Jlm
Loading...
Searching...
No Matches
Andersen.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023, 2024 HÃ¥vard Krogstie <krogstie.havard@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
20
21namespace jlm::llvm::aa
22{
23
29bool
31{
32 return IsOrContains<PointerType>(type) || is<rvsdg::FunctionType>(type);
33}
34
35std::string
37{
38 std::ostringstream str;
40 str << "OVS_";
42 str << "NORM_";
44 {
45 str << "Solver=Naive_";
46 }
47 else if (Solver_ == Solver::Worklist)
48 {
49 str << "Solver=Worklist_";
50 str << "Policy=";
52 str << "_";
53
55 str << "OnlineCD_";
57 str << "HybridCD_";
59 str << "LazyCD_";
61 str << "DP_";
63 str << "PIP_";
64 }
65 else
66 {
67 JLM_UNREACHABLE("Unknown solver type");
68 }
69
70 auto result = str.str();
71 result.erase(result.size() - 1, 1); // Remove trailing '_'
72 return result;
73}
74
75std::vector<Andersen::Configuration>
77{
78 std::vector<Configuration> configs;
79 auto PickPreferImplicitPointees = [&](Configuration config)
80 {
81 config.EnablePreferImplicitPointees(false);
82 configs.push_back(config);
83 config.EnablePreferImplicitPointees(true);
84 configs.push_back(config);
85 };
86 auto PickDifferencePropagation = [&](Configuration config)
87 {
88 config.EnableDifferencePropagation(false);
89 PickPreferImplicitPointees(config);
90 config.EnableDifferencePropagation(true);
91 PickPreferImplicitPointees(config);
92 };
93 auto PickLazyCycleDetection = [&](Configuration config)
94 {
95 config.EnableLazyCycleDetection(false);
96 PickDifferencePropagation(config);
97 config.EnableLazyCycleDetection(true);
98 PickDifferencePropagation(config);
99 };
100 auto PickHybridCycleDetection = [&](Configuration config)
101 {
102 config.EnableHybridCycleDetection(false);
103 PickLazyCycleDetection(config);
104 // Hybrid Cycle Detection can only be enabled when OVS is enabled
105 if (config.IsOfflineVariableSubstitutionEnabled())
106 {
107 config.EnableHybridCycleDetection(true);
108 PickLazyCycleDetection(config);
109 }
110 };
111 auto PickOnlineCycleDetection = [&](Configuration config)
112 {
113 config.EnableOnlineCycleDetection(false);
114 PickHybridCycleDetection(config);
115 config.EnableOnlineCycleDetection(true);
116 // OnlineCD can not be combined with HybridCD or LazyCD
117 PickDifferencePropagation(config);
118 };
119 auto PickWorklistPolicy = [&](Configuration config)
120 {
122 config.SetWorklistSolverPolicy(Policy::LeastRecentlyFired);
123 PickOnlineCycleDetection(config);
124 config.SetWorklistSolverPolicy(Policy::TwoPhaseLeastRecentlyFired);
125 PickOnlineCycleDetection(config);
126 config.SetWorklistSolverPolicy(Policy::LastInFirstOut);
127 PickOnlineCycleDetection(config);
128 config.SetWorklistSolverPolicy(Policy::FirstInFirstOut);
129 PickOnlineCycleDetection(config);
130 config.SetWorklistSolverPolicy(Policy::TopologicalSort);
131 PickDifferencePropagation(config); // With topo, skip all cycle detection
132 };
133 auto PickOfflineNormalization = [&](Configuration config)
134 {
135 config.EnableOfflineConstraintNormalization(false);
136 configs.push_back(config);
137 config.EnableOfflineConstraintNormalization(true);
138 configs.push_back(config);
139 };
140 auto PickSolver = [&](Configuration config)
141 {
142 config.SetSolver(Solver::Worklist);
143 PickWorklistPolicy(config);
144 config.SetSolver(Solver::Naive);
145 PickOfflineNormalization(config);
146 };
147 auto PickOfflineVariableSubstitution = [&](Configuration config)
148 {
149 config.EnableOfflineVariableSubstitution(false);
150 PickSolver(config);
151 config.EnableOfflineVariableSubstitution(true);
152 PickSolver(config);
153 };
154
155 // Adds one configuration for all valid combinations of features
156 PickOfflineVariableSubstitution(NaiveSolverConfiguration());
157
158 return configs;
159}
160
165{
166 static constexpr const char * NumPointerObjects_ = "#PointerObjects";
167 static constexpr const char * NumMemoryPointerObjects_ = "#MemoryPointerObjects";
168 static constexpr const char * NumMemoryPointerObjectsCanPoint_ = "#MemoryPointerObjectsCanPoint";
169 static constexpr const char * NumRegisterPointerObjects_ = "#RegisterPointerObjects";
170 // A PointerObject of Register kind can represent multiple outputs in RVSDG. Sum them up.
171 static constexpr const char * NumRegistersMappedToPointerObject_ =
172 "#RegistersMappedToPointerObject";
173 static constexpr const char * NumAllocaPointerObjects = "#AllocaPointerObjects";
174 static constexpr const char * NumMallocPointerObjects = "#MallocPointerObjects";
175 static constexpr const char * NumGlobalPointerObjects = "#GlobalPointerObjects";
176 static constexpr const char * NumFunctionPointerObjects = "#FunctionPointerObjects";
177 static constexpr const char * NumImportPointerObjects = "#ImportPointerObjects";
178
179 static constexpr const char * NumBaseConstraints_ = "#BaseConstraints";
180 static constexpr const char * NumSupersetConstraints_ = "#SupersetConstraints";
181 static constexpr const char * NumStoreConstraints_ = "#StoreConstraints";
182 static constexpr const char * NumLoadConstraints_ = "#LoadConstraints";
183 static constexpr const char * NumFunctionCallConstraints_ = "#FunctionCallConstraints";
184 static constexpr const char * NumScalarFlagConstraints_ = "#ScalarFlagConstraints";
185 static constexpr const char * NumOtherFlagConstraints_ = "#OtherFlagConstraints";
186
187 static constexpr const char * Configuration_ = "Configuration";
188
189 // ====== Offline technique statistics ======
190 static constexpr const char * NumUnificationsOvs_ = "#Unifications(OVS)";
191 static constexpr const char * NumConstraintsRemovedOfflineNorm_ =
192 "#ConstraintsRemoved(OfflineNorm)";
193
194 // ====== Solver statistics ======
195 static constexpr const char * NumNaiveSolverIterations_ = "#NaiveSolverIterations";
196
197 static constexpr const char * WorklistPolicy_ = "WorklistPolicy";
198 static constexpr const char * NumWorklistSolverWorkItemsPopped_ =
199 "#WorklistSolverWorkItemsPopped";
200 static constexpr const char * NumWorklistSolverWorkItemsNewPointees_ =
201 "#WorklistSolverWorkItemsNewPointees";
202 static constexpr const char * NumTopologicalWorklistSweeps_ = "#TopologicalWorklistSweeps";
203
204 // ====== Online technique statistics ======
205 static constexpr const char * NumOnlineCyclesDetected_ = "#OnlineCyclesDetected";
206 static constexpr const char * NumOnlineCycleUnifications_ = "#OnlineCycleUnifications";
207
208 static constexpr const char * NumHybridCycleUnifications_ = "#HybridCycleUnifications";
209
210 static constexpr const char * NumLazyCycleDetectionAttempts_ = "#LazyCycleDetectionAttempts";
211 static constexpr const char * NumLazyCyclesDetected_ = "#LazyCyclesDetected";
212 static constexpr const char * NumLazyCycleUnifications_ = "#LazyCycleUnifications";
213
214 static constexpr const char * NumPIPExplicitPointeesRemoved_ = "#PIPExplicitPointeesRemoved";
215
216 // ====== During solving points-to set statistics ======
217 // How many times a pointee has been attempted inserted into an explicit points-to set.
218 // If a set with 10 elements is unioned into another set, that counts as 10 insertion attempts.
219 static constexpr const char * NumSetInsertionAttempts_ = "#PointsToSetInsertionAttempts";
220 // How many explicit pointees have been removed from points-to sets during solving.
221 // Removal can only happen due to unification, or explicitly when using PIP
222 static constexpr const char * NumExplicitPointeesRemoved_ = "#ExplicitPointeesRemoved";
223
224 // ====== After solving statistics ======
225 // How many disjoint sets of PointerObjects exist
226 static constexpr const char * NumUnificationRoots_ = "#UnificationRoots";
227 // How many memory objects where CanPoint() == true have escaped
228 static constexpr const char * NumCanPointsEscaped_ = "#CanPointsEscaped";
229 // How many memory objects where CanPoint() == false have escaped
230 static constexpr const char * NumCantPointsEscaped_ = "#CantPointsEscaped";
231
232 // The number of explicit pointees, counting only unification roots
233 static constexpr const char * NumExplicitPointees_ = "#ExplicitPointees";
234 // Only unification roots may have explicit pointees, but all PointerObjects in the unification
235 // marked CanPoint effectively have those explicit pointees. Add up the number of such relations.
236 static constexpr const char * NumExplicitPointsToRelations_ = "#ExplicitPointsToRelations";
237
238 // The number of PointsToExternal flags, counting only unification roots
239 static constexpr const char * NumPointsToExternalFlags_ = "#PointsToExternalFlags";
240 // Among all PointerObjects marked CanPoint, how many are in a unification pointing to external
241 static constexpr const char * NumPointsToExternalRelations_ = "#PointsToExternalRelations";
242
243 // Among all PointerObjects marked CanPoint and NOT flagged as pointing to external,
244 // add up how many pointer-pointee relations they have.
245 static constexpr const char * NumExplicitPointsToRelationsAmongPrecise_ =
246 "#ExplicitPointsToRelationsAmongPrecise";
247
248 // The number of PointeesEscaping flags, counting only unification roots
249 static constexpr const char * NumPointeesEscapingFlags_ = "#PointeesEscapingFlags";
250 // Among all PointerObjects marked CanPoint, how many are in a unification where pointees escape.
251 static constexpr const char * NumPointeesEscapingRelations_ = "#PointeesEscapingRelations";
252
253 // The total number of pointer-pointee relations, counting both explicit and implicit.
254 // In the case of doubled up pointees, the same pointer-pointee relation is not counted twice.
255 static constexpr const char * NumPointsToRelations_ = "#PointsToRelations";
256
257 // The number of doubled up pointees, only counting unification roots
258 static constexpr const char * NumDoubledUpPointees_ = "#DoubledUpPointees";
259 // The number of doubled up pointees, counting all PointerObjects marked CanPoint()
260 static constexpr const char * NumDoubledUpPointsToRelations_ = "#DoubledUpPointsToRelations";
261
262 // Number of unifications where no members have the CanPoint flag
263 static constexpr const char * NumCantPointUnifications_ = "#CantPointUnifications";
264 // In unifications where no member CanPoint, add up their explicit pointees
265 static constexpr const char * NumCantPointExplicitPointees_ = "#CantPointExplicitPointees";
266
267 static constexpr const char * AnalysisTimer_ = "AnalysisTimer";
268 static constexpr const char * SetAndConstraintBuildingTimer_ = "SetAndConstraintBuildingTimer";
269 static constexpr const char * OfflineVariableSubstitutionTimer_ = "OVSTimer";
270 static constexpr const char * OfflineConstraintNormalizationTimer_ = "OfflineNormTimer";
271 static constexpr const char * ConstraintSolvingNaiveTimer_ = "ConstraintSolvingNaiveTimer";
272 static constexpr const char * ConstraintSolvingWorklistTimer_ = "ConstraintSolvingWorklistTimer";
273 static constexpr const char * PointsToGraphConstructionTimer_ = "PointsToGraphConstructionTimer";
274 static constexpr const char * PointsToGraphConstructionExternalToEscapedTimer_ =
275 "PointsToGraphConstructionExternalToEscapedTimer";
276
277public:
278 ~Statistics() override = default;
279
280 explicit Statistics(const util::FilePath & sourceFile)
281 : util::Statistics(Statistics::Id::AndersenAnalysis, sourceFile)
282 {}
283
284 void
285 StartAndersenStatistics(const rvsdg::Graph & graph) noexcept
286 {
287 AddMeasurement(Label::NumRvsdgNodes, rvsdg::nnodes(&graph.GetRootRegion()));
288 AddTimer(AnalysisTimer_).start();
289 }
290
291 void
293 {
294 AddTimer(SetAndConstraintBuildingTimer_).start();
295 }
296
297 void
299 const PointerObjectSet & set,
300 const PointerObjectConstraintSet & constraints) noexcept
301 {
302 GetTimer(SetAndConstraintBuildingTimer_).stop();
303
304 // Measure the number of pointer objects of different kinds
305 AddMeasurement(NumPointerObjects_, set.NumPointerObjects());
306 AddMeasurement(NumMemoryPointerObjects_, set.NumMemoryPointerObjects());
307 AddMeasurement(NumMemoryPointerObjectsCanPoint_, set.NumMemoryPointerObjectsCanPoint());
308 AddMeasurement(NumRegisterPointerObjects_, set.NumRegisterPointerObjects());
309 AddMeasurement(NumRegistersMappedToPointerObject_, set.GetRegisterMap().size());
310
311 AddMeasurement(
312 NumAllocaPointerObjects,
313 set.NumPointerObjectsOfKind(PointerObjectKind::AllocaMemoryObject));
314 AddMeasurement(
315 NumMallocPointerObjects,
316 set.NumPointerObjectsOfKind(PointerObjectKind::MallocMemoryObject));
317 AddMeasurement(
318 NumGlobalPointerObjects,
319 set.NumPointerObjectsOfKind(PointerObjectKind::GlobalMemoryObject));
320 AddMeasurement(
321 NumFunctionPointerObjects,
322 set.NumPointerObjectsOfKind(PointerObjectKind::FunctionMemoryObject));
323 AddMeasurement(
324 NumImportPointerObjects,
325 set.NumPointerObjectsOfKind(PointerObjectKind::ImportMemoryObject));
326
327 // Count the number of constraints of different kinds
328 size_t numSupersetConstraints = 0;
329 size_t numStoreConstraints = 0;
330 size_t numLoadConstraints = 0;
331 size_t numFunctionCallConstraints = 0;
332 for (const auto & constraint : constraints.GetConstraints())
333 {
334 numSupersetConstraints += std::holds_alternative<SupersetConstraint>(constraint);
335 numStoreConstraints += std::holds_alternative<StoreConstraint>(constraint);
336 numLoadConstraints += std::holds_alternative<LoadConstraint>(constraint);
337 numFunctionCallConstraints += std::holds_alternative<FunctionCallConstraint>(constraint);
338 }
339 AddMeasurement(NumBaseConstraints_, constraints.NumBaseConstraints());
340 AddMeasurement(NumSupersetConstraints_, numSupersetConstraints);
341 AddMeasurement(NumStoreConstraints_, numStoreConstraints);
342 AddMeasurement(NumLoadConstraints_, numLoadConstraints);
343 AddMeasurement(NumFunctionCallConstraints_, numFunctionCallConstraints);
344 const auto [scalarFlags, otherFlags] = constraints.NumFlagConstraints();
345 AddMeasurement(NumScalarFlagConstraints_, scalarFlags);
346 AddMeasurement(NumOtherFlagConstraints_, otherFlags);
347 }
348
349 void
351 {
352 AddTimer(OfflineVariableSubstitutionTimer_).start();
353 }
354
355 void
356 StopOfflineVariableSubstitution(size_t numUnifications) noexcept
357 {
358 GetTimer(OfflineVariableSubstitutionTimer_).stop();
359 AddMeasurement(NumUnificationsOvs_, numUnifications);
360 }
361
362 void
364 {
365 AddTimer(OfflineConstraintNormalizationTimer_).start();
366 }
367
368 void
369 StopOfflineConstraintNormalization(size_t numConstraintsRemoved) noexcept
370 {
371 GetTimer(OfflineConstraintNormalizationTimer_).stop();
372 AddMeasurement(NumConstraintsRemovedOfflineNorm_, numConstraintsRemoved);
373 }
374
375 void
377 {
378 AddTimer(ConstraintSolvingNaiveTimer_).start();
379 }
380
381 void
382 StopConstraintSolvingNaiveStatistics(size_t numIterations) noexcept
383 {
384 GetTimer(ConstraintSolvingNaiveTimer_).stop();
385 AddMeasurement(NumNaiveSolverIterations_, numIterations);
386 }
387
388 void
390 {
391 AddTimer(ConstraintSolvingWorklistTimer_).start();
392 }
393
394 void
397 {
398 GetTimer(ConstraintSolvingWorklistTimer_).stop();
399
400 // What worklist policy was used
401 AddMeasurement(
402 WorklistPolicy_,
403 PointerObjectConstraintSet::WorklistSolverPolicyToString(statistics.Policy));
404
405 // How many work items were popped from the worklist in total
406 AddMeasurement(NumWorklistSolverWorkItemsPopped_, statistics.NumWorkItemsPopped);
407 AddMeasurement(NumWorklistSolverWorkItemsNewPointees_, statistics.NumWorkItemNewPointees);
408
409 if (statistics.NumTopologicalWorklistSweeps)
410 AddMeasurement(NumTopologicalWorklistSweeps_, *statistics.NumTopologicalWorklistSweeps);
411
412 if (statistics.NumOnlineCyclesDetected)
413 AddMeasurement(NumOnlineCyclesDetected_, *statistics.NumOnlineCyclesDetected);
414
415 if (statistics.NumOnlineCycleUnifications)
416 AddMeasurement(NumOnlineCycleUnifications_, *statistics.NumOnlineCycleUnifications);
417
418 if (statistics.NumHybridCycleUnifications)
419 AddMeasurement(NumHybridCycleUnifications_, *statistics.NumHybridCycleUnifications);
420
421 if (statistics.NumLazyCyclesDetectionAttempts)
422 AddMeasurement(NumLazyCycleDetectionAttempts_, *statistics.NumLazyCyclesDetectionAttempts);
423
424 if (statistics.NumLazyCyclesDetected)
425 AddMeasurement(NumLazyCyclesDetected_, *statistics.NumLazyCyclesDetected);
426
427 if (statistics.NumLazyCycleUnifications)
428 AddMeasurement(NumLazyCycleUnifications_, *statistics.NumLazyCycleUnifications);
429
430 if (statistics.NumPipExplicitPointeesRemoved)
431 AddMeasurement(NumPIPExplicitPointeesRemoved_, *statistics.NumPipExplicitPointeesRemoved);
432 }
433
434 void
436 {
437 AddMeasurement(Configuration_, config.ToString());
438 }
439
440 void
442 {
443 AddMeasurement(NumSetInsertionAttempts_, set.GetNumSetInsertionAttempts());
444 AddMeasurement(NumExplicitPointeesRemoved_, set.GetNumExplicitPointeesRemoved());
445
446 size_t numUnificationRoots = 0;
447
448 size_t numCanPointEscaped = 0;
449 size_t numCantPointEscaped = 0;
450
451 size_t numExplicitPointees = 0;
452 size_t numExplicitPointsToRelations = 0;
453 size_t numExplicitPointeeRelationsAmongPrecise = 0;
454
455 size_t numPointsToExternalFlags = 0;
456 size_t numPointsToExternalRelations = 0;
457 size_t numPointeesEscapingFlags = 0;
458 size_t numPointeesEscapingRelations = 0;
459
460 size_t numDoubledUpPointees = 0;
461 size_t numDoubledUpPointsToRelations = 0;
462
463 std::vector<bool> unificationHasCanPoint(set.NumPointerObjects(), false);
464
465 for (PointerObjectIndex i = 0; i < set.NumPointerObjects(); i++)
466 {
467 if (set.HasEscaped(i))
468 {
469 if (set.CanPoint(i))
470 numCanPointEscaped++;
471 else
472 numCantPointEscaped++;
473 }
474
475 const auto & pointees = set.GetPointsToSet(i);
476
477 if (set.CanPoint(i))
478 {
479 numExplicitPointsToRelations += pointees.Size();
480 numPointeesEscapingRelations += set.HasPointeesEscaping(i);
481
482 if (set.IsPointingToExternal(i))
483 {
484 numPointsToExternalRelations++;
485 for (auto pointee : pointees.Items())
486 {
487 if (set.HasEscaped(pointee))
488 numDoubledUpPointsToRelations++;
489 }
490 }
491 else
492 {
493 // When comparing precision, the number of explicit pointees is more interesting among
494 // pointers that do not also point to external.
495 numExplicitPointeeRelationsAmongPrecise += pointees.Size();
496 }
497
498 // This unification has at least one CanPoint member
499 unificationHasCanPoint[set.GetUnificationRoot(i)] = true;
500 }
501
502 // The rest of this loop is only concerned with unification roots, as they are the only
503 // PointerObjects that actually have explicit pointees or flags
504 if (!set.IsUnificationRoot(i))
505 continue;
506
507 numUnificationRoots++;
508 if (set.IsPointingToExternal(i))
509 numPointsToExternalFlags++;
510 if (set.HasPointeesEscaping(i))
511 numPointeesEscapingFlags++;
512
513 numExplicitPointees += pointees.Size();
514
515 // If the PointsToExternal flag is set, any explicit pointee that has escaped is doubled up
516 if (set.IsPointingToExternal(i))
517 for (auto pointee : pointees.Items())
518 if (set.HasEscaped(pointee))
519 numDoubledUpPointees++;
520 }
521
522 // Now find unifications where no member is marked CanPoint, as any explicit pointee is a waste
523 size_t numCantPointUnifications = 0;
524 size_t numCantPointExplicitPointees = 0;
525 for (PointerObjectIndex i = 0; i < set.NumPointerObjects(); i++)
526 {
527 if (!set.IsUnificationRoot(i))
528 continue;
529 if (unificationHasCanPoint[i])
530 continue;
531 numCantPointUnifications++;
532 numCantPointExplicitPointees += set.GetPointsToSet(i).Size();
533 }
534
535 AddMeasurement(NumUnificationRoots_, numUnificationRoots);
536 AddMeasurement(NumCanPointsEscaped_, numCanPointEscaped);
537 AddMeasurement(NumCantPointsEscaped_, numCantPointEscaped);
538
539 AddMeasurement(NumExplicitPointees_, numExplicitPointees);
540 AddMeasurement(NumExplicitPointsToRelations_, numExplicitPointsToRelations);
541 AddMeasurement(
542 NumExplicitPointsToRelationsAmongPrecise_,
543 numExplicitPointeeRelationsAmongPrecise);
544
545 AddMeasurement(NumPointsToExternalFlags_, numPointsToExternalFlags);
546 AddMeasurement(NumPointsToExternalRelations_, numPointsToExternalRelations);
547 AddMeasurement(NumPointeesEscapingFlags_, numPointeesEscapingFlags);
548 AddMeasurement(NumPointeesEscapingRelations_, numPointeesEscapingRelations);
549
550 // Calculate the total number of pointer-pointee relations by adding up all explicit and
551 // implicit relations, and removing the doubled up relations.
552 size_t numPointsToRelations =
553 numExplicitPointsToRelations - numDoubledUpPointsToRelations
554 + numPointsToExternalRelations * (numCanPointEscaped + numCantPointEscaped);
555
556 AddMeasurement(NumPointsToRelations_, numPointsToRelations);
557
558 AddMeasurement(NumDoubledUpPointees_, numDoubledUpPointees);
559 AddMeasurement(NumDoubledUpPointsToRelations_, numDoubledUpPointsToRelations);
560
561 AddMeasurement(NumCantPointUnifications_, numCantPointUnifications);
562 AddMeasurement(NumCantPointExplicitPointees_, numCantPointExplicitPointees);
563 }
564
565 void
567 {
568 AddTimer(PointsToGraphConstructionTimer_).start();
569 }
570
571 void
573 {
574 AddTimer(PointsToGraphConstructionExternalToEscapedTimer_).start();
575 }
576
577 void
579 {
580 GetTimer(PointsToGraphConstructionExternalToEscapedTimer_).stop();
581 }
582
583 void
585 {
586 GetTimer(PointsToGraphConstructionTimer_).stop();
587 AddMeasurement(Label::NumPointsToGraphNodes, pointsToGraph.numNodes());
588 AddMeasurement(Label::NumPointsToGraphAllocaNodes, pointsToGraph.numAllocaNodes());
589 AddMeasurement(Label::NumPointsToGraphDeltaNodes, pointsToGraph.numDeltaNodes());
590 AddMeasurement(Label::NumPointsToGraphImportNodes, pointsToGraph.numImportNodes());
591 AddMeasurement(Label::NumPointsToGraphLambdaNodes, pointsToGraph.numLambdaNodes());
592 AddMeasurement(Label::NumPointsToGraphMallocNodes, pointsToGraph.numMallocNodes());
593 AddMeasurement(Label::NumPointsToGraphMemoryNodes, pointsToGraph.numMemoryNodes());
594 AddMeasurement(Label::NumPointsToGraphRegisterNodes, pointsToGraph.numRegisterNodes());
595 AddMeasurement(
596 Label::NumPointsToGraphExternallyAvailableNodes,
597 pointsToGraph.numExternallyAvailableNodes());
598 // The number of nodes pointing to external (and all nodes marked as escaped)
599 AddMeasurement(
600 Label::NumPointsToGraphNodesTargetsAllExternallyAvailable,
602 auto [numExplicitEdges, numEdges] = pointsToGraph.numEdges();
603 AddMeasurement(Label::NumPointsToGraphExplicitEdges, numExplicitEdges);
604 AddMeasurement(Label::NumPointsToGraphEdges, numEdges);
605 }
606
607 void
609 {
610 GetTimer(AnalysisTimer_).stop();
611 }
612
613 static std::unique_ptr<Statistics>
614 Create(const util::FilePath & sourceFile)
615 {
616 return std::make_unique<Statistics>(sourceFile);
617 }
618};
619
620Andersen::Andersen() = default;
621
622Andersen::~Andersen() noexcept = default;
623
624void
625Andersen::AnalyzeSimpleNode(const rvsdg::SimpleNode & node)
626{
628 node.GetOperation(),
629 [&](const AllocaOperation &)
630 {
631 AnalyzeAlloca(node);
632 },
633 [&](const MallocOperation &)
634 {
635 AnalyzeMalloc(node);
636 },
637 [&](const LoadOperation &)
638 {
639 AnalyzeLoad(node);
640 },
641 [&](const StoreOperation &)
642 {
643 AnalyzeStore(node);
644 },
645 [&](const CallOperation &)
646 {
647 AnalyzeCall(node);
648 },
649 [&](const GetElementPtrOperation &)
650 {
651 AnalyzeGep(node);
652 },
653 [&](const PtrMaskOperation &)
654 {
655 AnalyzePtrMask(node);
656 },
657 [&](const BitCastOperation &)
658 {
659 AnalyzeBitcast(node);
660 },
661 [&](const IntToPtrOperation &)
662 {
663 AnalyzeBits2ptr(node);
664 },
665 [&](const PtrToIntOperation &)
666 {
667 AnalyzePtrToInt(node);
668 },
670 {
671 AnalyzeConstantPointerNull(node);
672 },
673 [&](const UndefValueOperation &)
674 {
675 AnalyzeUndef(node);
676 },
677 [&](const PoisonValueOperation &)
678 {
679 AnalyzePoison(node);
680 },
681 [&](const FreezeOperation &)
682 {
683 AnalyzeFreeze(node);
684 },
685 [&](const MemCpyOperation &)
686 {
687 AnalyzeMemcpy(node);
688 },
689 [&](const MemSetOperation &)
690 {
691 AnalyzeMemset(node);
692 },
693 [&](const MemMoveOperation &)
694 {
695 AnalyzeMemmove(node);
696 },
697 [&](const ConstantArrayOperation &)
698 {
699 AnalyzeConstantArray(node);
700 },
701 [&](const ConstantStructOperation &)
702 {
703 AnalyzeConstantStruct(node);
704 },
706 {
707 AnalyzeConstantAggregateZero(node);
708 },
709 [&](const InsertValueOperation &)
710 {
711 AnalyzeInsertValue(node);
712 },
713 [&](const ExtractValueOperation &)
714 {
715 AnalyzeExtractValue(node);
716 },
718 {
719 AnalyzeValist(node);
720 },
721 [&](const PointerToFunctionOperation &)
722 {
723 AnalyzePointerToFunction(node);
724 },
725 [&](const FunctionToPointerOperation &)
726 {
727 AnalyzeFunctionToPointer(node);
728 },
729 [&](const IOBarrierOperation &)
730 {
731 AnalyzeIOBarrier(node);
732 },
733 [&](const FreeOperation &)
734 {
735 // Takes pointers as input, but does not affect any points-to sets
736 },
737 [&](const PtrCmpOperation &)
738 {
739 // Takes pointers as input, but does not affect any points-to sets
740 },
741 [&]()
742 {
743 // This node operation is unknown, make sure it doesn't consume any pointers
744 for (size_t n = 0; n < node.ninputs(); n++)
745 JLM_ASSERT(!IsOrContainsPointerType(*node.input(n)->Type()));
746 });
747}
748
749void
751{
752 const auto allocaOp = util::assertedCast<const AllocaOperation>(&node.GetOperation());
753
754 const auto & outputRegister = *node.output(0);
755 const auto outputRegisterPO = Set_->CreateRegisterPointerObject(outputRegister);
756
757 const bool canPoint = IsOrContainsPointerType(*allocaOp->allocatedType());
758 const auto allocaPO = Set_->CreateAllocaMemoryObject(node, canPoint);
759 Constraints_->AddPointerPointeeConstraint(outputRegisterPO, allocaPO);
760}
761
762void
764{
765 JLM_ASSERT(is<MallocOperation>(node.GetOperation()));
766
767 const auto & outputRegister = MallocOperation::addressOutput(node);
768 const auto outputRegisterPO = Set_->CreateRegisterPointerObject(outputRegister);
769
770 // We do not know what types will be stored in the malloc, so let it track pointers
771 const auto mallocPO = Set_->CreateMallocMemoryObject(node, true);
772 Constraints_->AddPointerPointeeConstraint(outputRegisterPO, mallocPO);
773}
774
775void
777{
778 JLM_ASSERT(is<LoadOperation>(node.GetOperation()));
779
780 const auto & addressRegister = *LoadOperation::AddressInput(node).origin();
781 const auto & outputRegister = LoadOperation::LoadedValueOutput(node);
782
783 const auto addressRegisterPO = Set_->GetRegisterPointerObject(addressRegister);
784
785 if (IsOrContainsPointerType(*outputRegister.Type()))
786 {
787 const auto outputRegisterPO = Set_->CreateRegisterPointerObject(outputRegister);
788 Constraints_->AddConstraint(LoadConstraint(outputRegisterPO, addressRegisterPO));
789 }
790 else
791 {
792 Set_->MarkAsLoadingAsScalar(addressRegisterPO);
793 }
794}
795
796void
798{
799 const auto & addressRegister = *StoreOperation::AddressInput(node).origin();
800 const auto & valueRegister = *StoreOperation::StoredValueInput(node).origin();
801
802 const auto addressRegisterPO = Set_->GetRegisterPointerObject(addressRegister);
803
804 // If the written value is not a pointer, be conservative and mark the address
805 if (IsOrContainsPointerType(*valueRegister.Type()))
806 {
807 const auto valueRegisterPO = Set_->GetRegisterPointerObject(valueRegister);
808 Constraints_->AddConstraint(StoreConstraint(addressRegisterPO, valueRegisterPO));
809 }
810 else
811 {
812 Set_->MarkAsStoringAsScalar(addressRegisterPO);
813 }
814}
815
816void
818{
819 JLM_ASSERT(is<CallOperation>(callNode.GetOperation()));
820
821 // The address being called by the call node
822 const auto & callTarget = *CallOperation::GetFunctionInput(callNode).origin();
823 const auto callTargetPO = Set_->GetRegisterPointerObject(callTarget);
824
825 // Create PointerObjects for all output values of pointer type
826 for (size_t n = 0; n < callNode.noutputs(); n++)
827 {
828 const auto & outputRegister = *callNode.output(n);
829 if (IsOrContainsPointerType(*outputRegister.Type()))
830 (void)Set_->CreateRegisterPointerObject(outputRegister);
831 }
832
833 // We make no attempt at detecting what type of call this is here.
834 // The logic handling external and indirect calls is done by the FunctionCallConstraint.
835 // Passing points-to-sets from call-site to function bodies is done fully by this constraint.
836 Constraints_->AddConstraint(FunctionCallConstraint(callTargetPO, callNode));
837}
838
839void
841{
842 JLM_ASSERT(is<GetElementPtrOperation>(node.GetOperation()));
843
844 // The analysis is field insensitive, so ignoring the offset and mapping the output
845 // to the same PointerObject as the input is sufficient.
846 const auto & baseRegister = *node.input(0)->origin();
847 JLM_ASSERT(is<PointerType>(baseRegister.Type()));
848
849 const auto baseRegisterPO = Set_->GetRegisterPointerObject(baseRegister);
850 const auto & outputRegister = *node.output(0);
851 Set_->MapRegisterToExistingPointerObject(outputRegister, baseRegisterPO);
852}
853
854void
856{
857 JLM_ASSERT(is<PtrMaskOperation>(node.GetOperation()));
858
859 // FIXME: What about vector of pointers?
860 const auto & baseRegister = *node.input(0)->origin();
861 JLM_ASSERT(is<PointerType>(baseRegister.Type()));
862
863 const auto baseRegisterPO = Set_->GetRegisterPointerObject(baseRegister);
864 const auto & outputRegister = *node.output(0);
865 Set_->MapRegisterToExistingPointerObject(outputRegister, baseRegisterPO);
866}
867
868void
870{
871 JLM_ASSERT(is<BitCastOperation>(node.GetOperation()));
872
873 const auto & inputRegister = *node.input(0)->origin();
874 const auto & outputRegister = *node.output(0);
875
876 JLM_ASSERT(!IsAggregateType(*inputRegister.Type()) && !IsAggregateType(*outputRegister.Type()));
877 if (!IsOrContainsPointerType(*inputRegister.Type()))
878 return;
879
880 // If the input is a pointer type, the output must also be a pointer type
881 JLM_ASSERT(IsOrContainsPointerType(*outputRegister.Type()));
882
883 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
884 Set_->MapRegisterToExistingPointerObject(outputRegister, inputRegisterPO);
885}
886
887void
889{
890 JLM_ASSERT(is<IntToPtrOperation>(node.GetOperation()));
891 const auto & output = *node.output(0);
892 JLM_ASSERT(is<PointerType>(output.Type()));
893
894 // This operation synthesizes a pointer from bytes.
895 // Since no points-to information is tracked through integers, the resulting pointer must
896 // be assumed to possibly point to any external or escaped memory object.
897 const auto outputPO = Set_->CreateRegisterPointerObject(output);
898 Constraints_->AddPointsToExternalConstraint(outputPO);
899}
900
901void
903{
904 JLM_ASSERT(is<PtrToIntOperation>(node.GetOperation()));
905 const auto & inputRegister = *node.input(0)->origin();
906 JLM_ASSERT(is<PointerType>(inputRegister.Type()));
907
908 // This operation converts a pointer to bytes, exposing it as an integer, which we can't track.
909 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
910 Constraints_->AddRegisterContentEscapedConstraint(inputRegisterPO);
911}
912
913void
915{
916 JLM_ASSERT(is<ConstantPointerNullOperation>(node.GetOperation()));
917 const auto & output = *node.output(0);
918 JLM_ASSERT(is<PointerType>(output.Type()));
919
920 // ConstantPointerNull cannot point to any memory location. We therefore only insert a register
921 // node for it, but let this node not point to anything.
922 (void)Set_->CreateRegisterPointerObject(output);
923}
924
925void
927{
928 JLM_ASSERT(is<UndefValueOperation>(node.GetOperation()));
929 const auto & output = *node.output(0);
930
931 if (!IsOrContainsPointerType(*output.Type()))
932 return;
933
934 // UndefValue cannot point to any memory location. We therefore only insert a register node for
935 // it, but let this node not point to anything.
936 (void)Set_->CreateRegisterPointerObject(output);
937}
938
939void
941{
942 JLM_ASSERT(is<PoisonValueOperation>(node.GetOperation()));
943 const auto & output = *node.output(0);
944
945 if (!IsOrContainsPointerType(*output.Type()))
946 return;
947
948 // PoisonValue cannot point to any memory location. We therefore only insert a register node for
949 // it, but let this node not point to anything.
950 (void)Set_->CreateRegisterPointerObject(output);
951}
952
953void
955{
956 JLM_ASSERT(is<FreezeOperation>(node.GetOperation()));
957 const auto & output = *node.output(0);
958
959 if (!IsOrContainsPointerType(*output.Type()))
960 return;
961
962 // Freeze will either be a copy of its argument (when defined),
963 // or be an illegal pointer, so just re-use its argument
964
965 auto & operand = *node.input(0)->origin();
966 auto operandPO = Set_->GetRegisterPointerObject(operand);
967 Set_->MapRegisterToExistingPointerObject(output, operandPO);
968}
969
970void
972{
973 JLM_ASSERT(is<MemCpyOperation>(node.GetOperation()));
974
975 auto & dstAddressRegister = *node.input(0)->origin();
976 auto & srcAddressRegister = *node.input(1)->origin();
977 JLM_ASSERT(is<PointerType>(dstAddressRegister.Type()));
978 JLM_ASSERT(is<PointerType>(srcAddressRegister.Type()));
979
980 const auto dstAddressRegisterPO = Set_->GetRegisterPointerObject(dstAddressRegister);
981 const auto srcAddressRegisterPO = Set_->GetRegisterPointerObject(srcAddressRegister);
982
983 // Create an intermediate PointerObject representing the moved values
984 const auto dummyPO = Set_->CreateDummyRegisterPointerObject();
985
986 // Add a "load" constraint from the source into the dummy register
987 Constraints_->AddConstraint(LoadConstraint(dummyPO, srcAddressRegisterPO));
988 // Add a "store" constraint from the dummy register into the destination
989 Constraints_->AddConstraint(StoreConstraint(dstAddressRegisterPO, dummyPO));
990}
991
992void
994{
995 JLM_ASSERT(is<MemMoveOperation>(node.GetOperation()));
996
997 auto & dstAddressRegister = *node.input(0)->origin();
998 auto & srcAddressRegister = *node.input(1)->origin();
999 JLM_ASSERT(is<PointerType>(dstAddressRegister.Type()));
1000 JLM_ASSERT(is<PointerType>(srcAddressRegister.Type()));
1001
1002 const auto dstAddressRegisterPO = Set_->GetRegisterPointerObject(dstAddressRegister);
1003 const auto srcAddressRegisterPO = Set_->GetRegisterPointerObject(srcAddressRegister);
1004
1005 // Create an intermediate PointerObject representing the moved values
1006 const auto dummyPO = Set_->CreateDummyRegisterPointerObject();
1007
1008 // Add a "load" constraint from the source into the dummy register
1009 Constraints_->AddConstraint(LoadConstraint(dummyPO, srcAddressRegisterPO));
1010 // Add a "store" constraint from the dummy register into the destination
1011 Constraints_->AddConstraint(StoreConstraint(dstAddressRegisterPO, dummyPO));
1012}
1013
1014void
1016{
1017 JLM_ASSERT(is<MemSetOperation>(node.GetOperation()));
1018
1019 const auto & dstAddressRegister = *MemSetOperation::destinationInput(node).origin();
1020 JLM_ASSERT(is<PointerType>(dstAddressRegister.Type()));
1021
1022 const auto dstAddressRegisterPO = Set_->GetRegisterPointerObject(dstAddressRegister);
1023
1024 Set_->MarkAsStoringAsScalar(dstAddressRegisterPO);
1025}
1026
1027void
1029{
1030 JLM_ASSERT(is<ConstantArrayOperation>(node.GetOperation()));
1031
1032 if (!IsOrContainsPointerType(*node.output(0)->Type()))
1033 return;
1034
1035 // Make the resulting array point to everything its members are pointing to
1036 auto & outputRegister = *node.output(0);
1037 const auto outputRegisterPO = Set_->CreateRegisterPointerObject(outputRegister);
1038
1039 for (size_t n = 0; n < node.ninputs(); n++)
1040 {
1041 const auto & inputRegister = *node.input(n)->origin();
1042 JLM_ASSERT(IsOrContainsPointerType(*inputRegister.Type()));
1043
1044 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1045 Constraints_->AddConstraint(SupersetConstraint(outputRegisterPO, inputRegisterPO));
1046 }
1047}
1048
1049void
1051{
1052 JLM_ASSERT(is<ConstantStructOperation>(node.GetOperation()));
1053
1054 if (!IsOrContainsPointerType(*node.output(0)->Type()))
1055 return;
1056
1057 // Make the resulting struct point to everything its members are pointing to
1058 auto & outputRegister = *node.output(0);
1059 const auto outputRegisterPO = Set_->CreateRegisterPointerObject(outputRegister);
1060
1061 for (size_t n = 0; n < node.ninputs(); n++)
1062 {
1063 const auto & inputRegister = *node.input(n)->origin();
1064 if (!IsOrContainsPointerType(*inputRegister.Type()))
1065 continue;
1066
1067 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1068 Constraints_->AddConstraint(SupersetConstraint(outputRegisterPO, inputRegisterPO));
1069 }
1070}
1071
1072void
1074{
1075 JLM_ASSERT(is<ConstantAggregateZeroOperation>(node.GetOperation()));
1076 auto & output = *node.output(0);
1077
1078 if (!IsOrContainsPointerType(*output.Type()))
1079 return;
1080
1081 // ConstantAggregateZero cannot point to any memory location.
1082 // We therefore only insert a register node for it, but let this node not point to anything.
1083 (void)Set_->CreateRegisterPointerObject(output);
1084}
1085
1086void
1088{
1089 JLM_ASSERT(is<InsertValueOperation>(node.GetOperation()));
1090
1091 // The InsertValue instruction looks like
1092 // result = insertvalue(input, element, index, [additional indices...])
1093 // where result and input are values of some aggregate type (e.g. a struct).
1094 // The element is inserted into the result aggregate at the specified index.
1095 // Additional indicies can be given to index into nested aggregate types.
1096
1097 // We only care about aggregate types that contain pointers
1098 const auto & result = *node.output(0);
1099 if (!IsOrContainsPointerType(*result.Type()))
1100 return;
1101
1102 const auto & inputRegister = *node.input(0)->origin();
1103 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1104
1105 // Check if the element type also contains a pointer
1106 const auto & element = *node.input(1)->origin();
1107 if (IsOrContainsPointerType(*element.Type()))
1108 {
1109 // Both the original aggregate and the inserted element contain pointers
1110 // Create a new PointerObject for the result containing their union
1111 const auto elementPO = Set_->GetRegisterPointerObject(element);
1112 const auto resultPO = Set_->CreateRegisterPointerObject(result);
1113 Constraints_->AddConstraint(SupersetConstraint(resultPO, inputRegisterPO));
1114 Constraints_->AddConstraint(SupersetConstraint(resultPO, elementPO));
1115 }
1116 else
1117 {
1118 // The operation does not add any additional pointees, so map the result directly to the input
1119 Set_->MapRegisterToExistingPointerObject(result, inputRegisterPO);
1120 }
1121}
1122
1123void
1125{
1126 JLM_ASSERT(is<ExtractValueOperation>(node.GetOperation()));
1127
1128 const auto & result = *node.output(0);
1129 if (!IsOrContainsPointerType(*result.Type()))
1130 return;
1131
1132 const auto & inputRegister = *node.input(0)->origin();
1133 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1134 // The resulting element can point to anything the aggregate type points to
1135 Set_->MapRegisterToExistingPointerObject(result, inputRegisterPO);
1136}
1137
1138void
1140{
1141 JLM_ASSERT(is<VariadicArgumentListOperation>(node.GetOperation()));
1142
1143 // Members of the valist are extracted using the va_arg macro, which loads from the va_list struct
1144 // on the stack. This struct will be marked as escaped from the call to va_start, and thus point
1145 // to external. All we need to do is mark all pointees of pointer varargs as escaping. When the
1146 // pointers are re-created inside the function, they will be marked as pointing to external.
1147
1148 for (size_t i = 0; i < node.ninputs(); i++)
1149 {
1150 if (!IsOrContainsPointerType(*node.input(i)->Type()))
1151 continue;
1152
1153 const auto & inputRegister = *node.input(i)->origin();
1154 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1155 Constraints_->AddRegisterContentEscapedConstraint(inputRegisterPO);
1156 }
1157}
1158
1159void
1161{
1162 JLM_ASSERT(is<PointerToFunctionOperation>(node.GetOperation()));
1163
1164 // For pointer analysis purposes, function objects and pointers
1165 // to functions are treated as being the same.
1166 const auto & baseRegister = *node.input(0)->origin();
1167 JLM_ASSERT(is<PointerType>(baseRegister.Type()));
1168
1169 const auto baseRegisterPO = Set_->GetRegisterPointerObject(baseRegister);
1170 const auto & outputRegister = *node.output(0);
1171 Set_->MapRegisterToExistingPointerObject(outputRegister, baseRegisterPO);
1172}
1173
1174void
1176{
1177 JLM_ASSERT(is<FunctionToPointerOperation>(node.GetOperation()));
1178
1179 // For pointer analysis purposes, function objects and pointers
1180 // to functions are treated as being the same.
1181 const auto & baseRegister = *node.input(0)->origin();
1182 JLM_ASSERT(is<rvsdg::FunctionType>(baseRegister.Type()));
1183
1184 const auto baseRegisterPO = Set_->GetRegisterPointerObject(baseRegister);
1185 const auto & outputRegister = *node.output(0);
1186 Set_->MapRegisterToExistingPointerObject(outputRegister, baseRegisterPO);
1187}
1188
1189void
1191{
1192 JLM_ASSERT(is<IOBarrierOperation>(node.GetOperation()));
1193
1194 const auto operation = util::assertedCast<const IOBarrierOperation>(&node.GetOperation());
1195 if (!IsOrContainsPointerType(*operation->Type()))
1196 return;
1197
1198 const auto & inputRegister = *node.input(0)->origin();
1199 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1200 const auto & outputRegister = *node.output(0);
1201 Set_->MapRegisterToExistingPointerObject(outputRegister, inputRegisterPO);
1202}
1203
1204void
1206{
1207 MatchTypeOrFail(
1208 node,
1209 [this](const rvsdg::LambdaNode & lambdaNode)
1210 {
1211 AnalyzeLambda(lambdaNode);
1212 },
1213 [this](const rvsdg::DeltaNode & deltaNode)
1214 {
1215 AnalyzeDelta(deltaNode);
1216 },
1217 [this](const rvsdg::PhiNode & phiNode)
1218 {
1219 AnalyzePhi(phiNode);
1220 },
1221 [this](const rvsdg::GammaNode & gammaNode)
1222 {
1223 AnalyzeGamma(gammaNode);
1224 },
1225 [this](const rvsdg::ThetaNode & thetaNode)
1226 {
1227 AnalyzeTheta(thetaNode);
1228 });
1229}
1230
1231void
1233{
1234 // Handle context variables
1235 for (const auto & cv : lambda.GetContextVars())
1236 {
1237 if (!IsOrContainsPointerType(*cv.input->Type()))
1238 continue;
1239
1240 auto & inputRegister = *cv.input->origin();
1241 auto & argumentRegister = *cv.inner;
1242 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1243 Set_->MapRegisterToExistingPointerObject(argumentRegister, inputRegisterPO);
1244 }
1245
1246 // Create Register PointerObjects for each argument of pointing type in the function
1247 for (auto argument : lambda.GetFunctionArguments())
1248 {
1249 if (IsOrContainsPointerType(*argument->Type()))
1250 (void)Set_->CreateRegisterPointerObject(*argument);
1251 }
1252
1253 AnalyzeRegion(*lambda.subregion());
1254
1255 // Create a lambda PointerObject for the lambda itself
1256 const auto lambdaPO = Set_->CreateFunctionMemoryObject(lambda);
1257
1258 // Make the lambda node's output point to the lambda PointerObject
1259 const auto & lambdaOutput = *lambda.output();
1260 const auto lambdaOutputPO = Set_->CreateRegisterPointerObject(lambdaOutput);
1261 Constraints_->AddPointerPointeeConstraint(lambdaOutputPO, lambdaPO);
1262}
1263
1264void
1266{
1267 // Handle context variables
1268 for (auto & cv : delta.GetContextVars())
1269 {
1270 if (!IsOrContainsPointerType(*cv.input->Type()))
1271 continue;
1272
1273 auto & inputRegister = *cv.input->origin();
1274 auto & argumentRegister = *cv.inner;
1275 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1276 Set_->MapRegisterToExistingPointerObject(argumentRegister, inputRegisterPO);
1277 }
1278
1279 AnalyzeRegion(*delta.subregion());
1280
1281 // Get the result register from the subregion
1282 auto & resultRegister = *delta.result().origin();
1283
1284 // If the type of the delta can point, the analysis should track its set of possible pointees
1285 bool canPoint = IsOrContainsPointerType(*delta.Type());
1286
1287 // Create a global memory object representing the global variable
1288 const auto globalPO = Set_->CreateGlobalMemoryObject(delta, canPoint);
1289
1290 // If the initializer subregion result is a pointer, make the global point to what it points to
1291 if (canPoint)
1292 {
1293 const auto resultRegisterPO = Set_->GetRegisterPointerObject(resultRegister);
1294 Constraints_->AddConstraint(SupersetConstraint(globalPO, resultRegisterPO));
1295 }
1296
1297 // Finally create a Register PointerObject for the delta's output, pointing to the memory object
1298 auto & outputRegister = delta.output();
1299 const auto outputRegisterPO = Set_->CreateRegisterPointerObject(outputRegister);
1300 Constraints_->AddPointerPointeeConstraint(outputRegisterPO, globalPO);
1301}
1302
1303void
1305{
1306 // Handle context variables
1307 for (auto var : phi.GetContextVars())
1308 {
1309 if (!IsOrContainsPointerType(*var.inner->Type()))
1310 continue;
1311
1312 auto & inputRegister = *var.input->origin();
1313 auto & argumentRegister = *var.inner;
1314 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1315 Set_->MapRegisterToExistingPointerObject(argumentRegister, inputRegisterPO);
1316 }
1317
1318 // Create Register PointerObjects for each fixpoint variable argument
1319 for (auto var : phi.GetFixVars())
1320 {
1321 if (!IsOrContainsPointerType(*var.output->Type()))
1322 continue;
1323
1324 auto & argumentRegister = *var.recref;
1325 (void)Set_->CreateRegisterPointerObject(argumentRegister);
1326 }
1327
1328 AnalyzeRegion(*phi.subregion());
1329
1330 // Handle recursive definition results
1331 for (auto var : phi.GetFixVars())
1332 {
1333 if (!IsOrContainsPointerType(*var.output->Type()))
1334 continue;
1335
1336 // Make the recursion variable argument point to what the result register points to
1337 auto & argumentRegister = *var.recref;
1338 auto & resultRegister = *var.result->origin();
1339 const auto argumentRegisterPO = Set_->GetRegisterPointerObject(argumentRegister);
1340 const auto resultRegisterPO = Set_->GetRegisterPointerObject(resultRegister);
1341 Constraints_->AddConstraint(SupersetConstraint(argumentRegisterPO, resultRegisterPO));
1342
1343 // Map the output register to the recursion result's pointer object
1344 auto & outputRegister = *var.output;
1345 Set_->MapRegisterToExistingPointerObject(outputRegister, resultRegisterPO);
1346 }
1347}
1348
1349void
1351{
1352 // Handle input variables
1353 for (const auto & ev : gamma.GetEntryVars())
1354 {
1355 if (!IsOrContainsPointerType(*ev.input->Type()))
1356 continue;
1357
1358 auto & inputRegister = *ev.input->origin();
1359 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1360
1361 for (auto & argument : ev.branchArgument)
1362 Set_->MapRegisterToExistingPointerObject(*argument, inputRegisterPO);
1363 }
1364
1365 // Handle subregions
1366 for (size_t n = 0; n < gamma.nsubregions(); n++)
1367 AnalyzeRegion(*gamma.subregion(n));
1368
1369 // Handle exit variables
1370 for (const auto & ex : gamma.GetExitVars())
1371 {
1372 if (!IsOrContainsPointerType(*ex.output->Type()))
1373 continue;
1374
1375 auto & outputRegister = ex.output;
1376 const auto outputRegisterPO = Set_->CreateRegisterPointerObject(*outputRegister);
1377
1378 for (auto result : ex.branchResult)
1379 {
1380 const auto resultRegisterPO = Set_->GetRegisterPointerObject(*result->origin());
1381 Constraints_->AddConstraint(SupersetConstraint(outputRegisterPO, resultRegisterPO));
1382 }
1383 }
1384}
1385
1386void
1388{
1389 // Create a PointerObject for each argument in the inner region
1390 // And make it point to a superset of the corresponding input register
1391 for (const auto & loopVar : theta.GetLoopVars())
1392 {
1393 if (!IsOrContainsPointerType(*loopVar.input->Type()))
1394 continue;
1395
1396 auto & inputReg = *loopVar.input->origin();
1397 auto & innerArgumentReg = *loopVar.pre;
1398 const auto inputRegPO = Set_->GetRegisterPointerObject(inputReg);
1399 const auto innerArgumentRegPO = Set_->CreateRegisterPointerObject(innerArgumentReg);
1400
1401 // The inner argument can point to anything the input did
1402 Constraints_->AddConstraint(SupersetConstraint(innerArgumentRegPO, inputRegPO));
1403 }
1404
1405 AnalyzeRegion(*theta.subregion());
1406
1407 // Iterate over loop variables again, making the inner arguments point to a superset
1408 // of what the corresponding result registers point to
1409 for (const auto & loopVar : theta.GetLoopVars())
1410 {
1411 if (!IsOrContainsPointerType(*loopVar.input->Type()))
1412 continue;
1413
1414 auto & innerArgumentReg = *loopVar.pre;
1415 auto & innerResultReg = *loopVar.post->origin();
1416 auto & outputReg = *loopVar.output;
1417
1418 const auto innerArgumentRegPO = Set_->GetRegisterPointerObject(innerArgumentReg);
1419 const auto innerResultRegPO = Set_->GetRegisterPointerObject(innerResultReg);
1420
1421 // The inner argument can point to anything the result of last iteration did
1422 Constraints_->AddConstraint(SupersetConstraint(innerArgumentRegPO, innerResultRegPO));
1423
1424 // Due to theta nodes running at least once, the output always comes from the inner results
1425 Set_->MapRegisterToExistingPointerObject(outputReg, innerResultRegPO);
1426 }
1427}
1428
1429void
1431{
1432 // Check that all region arguments of pointing types have PointerObjects
1433 for (size_t i = 0; i < region.narguments(); i++)
1434 {
1435 if (IsOrContainsPointerType(*region.argument(i)->Type()))
1436 JLM_ASSERT(Set_->GetRegisterMap().count(region.argument(i)));
1437 }
1438
1439 // The use of the top-down traverser is vital, as it ensures all input origins
1440 // of pointer type are mapped to PointerObjects by the time a node is processed.
1441 rvsdg::TopDownTraverser traverser(&region);
1442
1443 // While visiting the node we have the responsibility of creating
1444 // PointerObjects for any of the node's outputs of pointer type
1445 for (const auto node : traverser)
1446 {
1447 if (auto simpleNode = dynamic_cast<const rvsdg::SimpleNode *>(node))
1448 AnalyzeSimpleNode(*simpleNode);
1449 else if (auto structuralNode = dynamic_cast<const rvsdg::StructuralNode *>(node))
1450 AnalyzeStructuralNode(*structuralNode);
1451 else
1452 JLM_UNREACHABLE("Unknown node type");
1453
1454 // Check that all outputs with pointing types have PointerObjects created
1455 for (size_t i = 0; i < node->noutputs(); i++)
1456 {
1457 if (IsOrContainsPointerType(*node->output(i)->Type()))
1458 JLM_ASSERT(Set_->GetRegisterMap().count(node->output(i)));
1459 }
1460 }
1461}
1462
1463void
1465{
1466 auto & rootRegion = graph.GetRootRegion();
1467
1468 // Iterate over all arguments to the root region - symbols imported from other modules
1469 // These symbols can either be global variables or functions
1470 for (size_t n = 0; n < rootRegion.narguments(); n++)
1471 {
1472 auto & argument = *util::assertedCast<LlvmGraphImport>(rootRegion.argument(n));
1473
1474 // Only care about imported pointer values
1475 if (!IsOrContainsPointerType(*argument.Type()))
1476 continue;
1477
1478 // Imported symbols are always externally available, so all pointees can be implicit.
1479 // The value of CanPoint thus has no effect on the solver, only the final PointsToGraph node.
1480 const bool canPoint = IsOrContainsPointerType(*argument.ValueType());
1481
1482 // Create a memory PointerObject representing the target of the external symbol
1483 // We can assume that two external symbols don't alias. This is the assumption clang makes.
1484 const auto importObjectPO = Set_->CreateImportMemoryObject(argument, canPoint);
1485
1486 // Create a register PointerObject representing the address value itself
1487 const auto importRegisterPO = Set_->CreateRegisterPointerObject(argument);
1488 Constraints_->AddPointerPointeeConstraint(importRegisterPO, importObjectPO);
1489 }
1490
1491 AnalyzeRegion(rootRegion);
1492
1493 // Mark all results escaping the root module as escaped
1494 for (size_t n = 0; n < rootRegion.nresults(); n++)
1495 {
1496 auto & escapedRegister = *rootRegion.result(n)->origin();
1497 if (!IsOrContainsPointerType(*escapedRegister.Type()))
1498 continue;
1499
1500 const auto escapedRegisterPO = Set_->GetRegisterPointerObject(escapedRegister);
1501 Constraints_->AddRegisterContentEscapedConstraint(escapedRegisterPO);
1502 }
1503}
1504
1505void
1507{
1508 Config_ = std::move(config);
1509}
1510
1513{
1514 return Config_;
1515}
1516
1517void
1519{
1520 Set_ = std::make_unique<PointerObjectSet>();
1521 Constraints_ = std::make_unique<PointerObjectConstraintSet>(*Set_);
1522
1524 AnalyzeRvsdg(module.Rvsdg());
1526}
1527
1528void
1530 PointerObjectConstraintSet & constraints,
1531 const Configuration & config,
1532 Statistics & statistics)
1533{
1534 statistics.AddStatisticFromConfiguration(config);
1535
1537 {
1539 // If the solver uses hybrid cycle detection, tell OVS to store info about ref node cycles
1540 bool hasHCD = config.IsHybridCycleDetectionEnabled();
1541 auto numUnifications = constraints.PerformOfflineVariableSubstitution(hasHCD);
1542 statistics.StopOfflineVariableSubstitution(numUnifications);
1543 }
1544
1546 {
1548 auto numConstraintsRemoved = constraints.NormalizeConstraints();
1549 statistics.StopOfflineConstraintNormalization(numConstraintsRemoved);
1550 }
1551
1553 {
1555 size_t numIterations = constraints.SolveNaively();
1556 statistics.StopConstraintSolvingNaiveStatistics(numIterations);
1557 }
1558 else if (config.GetSolver() == Configuration::Solver::Worklist)
1559 {
1561 auto worklistStatistics = constraints.SolveUsingWorklist(
1562 config.GetWorklistSoliverPolicy(),
1568 statistics.StopConstraintSolvingWorklistStatistics(worklistStatistics);
1569 }
1570 else
1571 JLM_UNREACHABLE("Unknown solver");
1572}
1573
1574std::unique_ptr<PointsToGraph>
1576 const rvsdg::RvsdgModule & module,
1578{
1579 auto statistics = Statistics::Create(module.SourceFilePath().value());
1580 statistics->StartAndersenStatistics(module.Rvsdg());
1581
1582 // Check environment variables for debugging flags
1583 size_t testAllConfigsIterations = 0;
1584 if (auto testAllConfigsString = std::getenv(ENV_TEST_ALL_CONFIGS))
1585 testAllConfigsIterations = std::stoi(testAllConfigsString);
1586 std::optional<size_t> useExactConfig;
1587 if (auto useExactConfigString = std::getenv(ENV_USE_EXACT_CONFIG))
1588 useExactConfig = std::stoi(useExactConfigString);
1589 const bool doubleCheck = std::getenv(ENV_DOUBLE_CHECK);
1590
1591 const bool dumpGraphs = std::getenv(ENV_DUMP_SUBSET_GRAPH);
1592 util::graph::Writer writer;
1593
1594 AnalyzeModule(module, *statistics);
1595
1596 // If solving multiple times, make a copy of the original constraint set
1597 std::pair<std::unique_ptr<PointerObjectSet>, std::unique_ptr<PointerObjectConstraintSet>> copy;
1598 if (testAllConfigsIterations || doubleCheck)
1599 copy = Constraints_->Clone();
1600
1601 // Draw subset graph both before and after solving
1602 if (dumpGraphs)
1603 Constraints_->DrawSubsetGraph(writer);
1604
1605 auto config = Config_;
1606 if (useExactConfig.has_value())
1607 {
1608 auto allConfigs = Configuration::GetAllConfigurations();
1609 config = allConfigs.at(*useExactConfig);
1610 }
1611
1612 SolveConstraints(*Constraints_, config, *statistics);
1613 statistics->AddStatisticsFromSolution(*Set_);
1614
1615 if (dumpGraphs)
1616 {
1617 auto & graph = Constraints_->DrawSubsetGraph(writer);
1618 graph.AppendToLabel("After Solving with " + config.ToString());
1620 }
1621
1622 auto result = ConstructPointsToGraphFromPointerObjectSet(*Set_, *statistics);
1623
1624 statistics->StopAndersenStatistics();
1625 statisticsCollector.CollectDemandedStatistics(std::move(statistics));
1626
1627 // Solve again if double-checking against naive is enabled
1628 if (testAllConfigsIterations || doubleCheck)
1629 {
1630 if (doubleCheck)
1631 std::cerr << "Double checking Andersen analysis using naive solving" << std::endl;
1632
1633 // If double-checking, only use the naive configuration. Otherwise, try all configurations
1634 std::vector<Configuration> configs;
1635 if (testAllConfigsIterations)
1637 else
1638 configs.push_back(Configuration::NaiveSolverConfiguration());
1639
1640 // If testing all configurations, do it as many times as requested.
1641 // Otherwise, do it at least once
1642 const auto iterations = std::max<size_t>(testAllConfigsIterations, 1);
1643
1644 for (size_t i = 0; i < iterations; i++)
1645 {
1646 for (const auto & config : configs)
1647 {
1648 // Create a clone of the unsolved pointer object set and constraint set
1649 auto workingCopy = copy.second->Clone();
1650 // These statistics will only contain solving data
1651 auto solvingStats = Statistics::Create(module.SourceFilePath().value());
1652 SolveConstraints(*workingCopy.second, config, *solvingStats);
1653 solvingStats->AddStatisticsFromSolution(*workingCopy.first);
1654 statisticsCollector.CollectDemandedStatistics(std::move(solvingStats));
1655
1656 // Only double check on the first iteration
1657 if (doubleCheck && i == 0)
1658 {
1659 if (workingCopy.first->HasIdenticalSolAs(*Set_))
1660 continue;
1661 std::cerr << "Solving with original config: " << Config_.ToString()
1662 << " did not produce the same solution as the config " << config.ToString()
1663 << std::endl;
1664 JLM_UNREACHABLE("Andersen solver double checking uncovered differences!");
1665 }
1666 }
1667 }
1668 }
1669
1670 // Cleanup
1671 Constraints_.reset();
1672 Set_.reset();
1673 return result;
1674}
1675
1676std::unique_ptr<PointsToGraph>
1682
1683std::unique_ptr<PointsToGraph>
1685 const PointerObjectSet & set,
1686 Statistics & statistics)
1687{
1689
1690 auto pointsToGraph = PointsToGraph::create();
1691
1692 // Mapping from index in PointerObjectSet to the equivalent memory node in the PointsToGraph
1693 std::unordered_map<PointerObjectIndex, PointsToGraph::NodeIndex> memoryNodeMapping;
1694
1695 for (auto [allocaNode, pointerObjectIndex] : set.GetAllocaMap())
1696 {
1697 memoryNodeMapping[pointerObjectIndex] =
1698 pointsToGraph->addNodeForAlloca(*allocaNode, set.HasEscaped(pointerObjectIndex));
1699 }
1700
1701 for (auto [deltaNode, pointerObjectIndex] : set.GetGlobalMap())
1702 {
1703 memoryNodeMapping[pointerObjectIndex] =
1704 pointsToGraph->addNodeForDelta(*deltaNode, set.HasEscaped(pointerObjectIndex));
1705 }
1706
1707 for (auto [import, pointerObjectIndex] : set.GetImportMap())
1708 {
1709 memoryNodeMapping[pointerObjectIndex] =
1710 pointsToGraph->addNodeForImport(*import, set.HasEscaped(pointerObjectIndex));
1711 }
1712
1713 for (auto [lambdaNode, pointerObjectIndex] : set.GetFunctionMap())
1714 {
1715 memoryNodeMapping[pointerObjectIndex] =
1716 pointsToGraph->addNodeForLambda(*lambdaNode, set.HasEscaped(pointerObjectIndex));
1717 }
1718
1719 for (auto [mallocNode, pointerObjectIndex] : set.GetMallocMap())
1720 {
1721 memoryNodeMapping[pointerObjectIndex] =
1722 pointsToGraph->addNodeForMalloc(*mallocNode, set.HasEscaped(pointerObjectIndex));
1723 }
1724
1725 // Helper function for attaching PointsToGraph nodes to their pointees, based on the
1726 // PointerObject's points-to set.
1727 auto applyPointsToSet = [&](PointsToGraph::NodeIndex ptgNode, PointerObjectIndex index)
1728 {
1729 // PointerObjectSets that are marked as not containing pointers can be added to the
1730 // PointsToGraph without any explicit or implicit pointees.
1731 if (!set.CanPoint(index))
1732 {
1733 return;
1734 }
1735
1736 // Mark nodes that target everything that is externally available
1737 if (set.IsPointingToExternal(index))
1738 {
1739 pointsToGraph->markAsTargetsAllExternallyAvailable(ptgNode);
1740 }
1741
1742 // Add all explicit pointees. Doubled up pointees are ignored by the PtG
1743 for (const auto targetIdx : set.GetPointsToSet(index).Items())
1744 {
1745 pointsToGraph->addTarget(ptgNode, memoryNodeMapping[targetIdx]);
1746 }
1747 };
1748
1749 // First group RVSDG registers by the PointerObject they are mapped to.
1750 // If the PointerObject is part of a unification, all Register PointerObjects in the unification
1751 // share points-to set, so they can all become one RegisterNode in the PointsToGraph.
1752 std::unordered_map<PointerObjectIndex, util::HashSet<const rvsdg::Output *>> outputsInRegister;
1753 for (auto [outputNode, registerIdx] : set.GetRegisterMap())
1754 {
1755 auto root = set.GetUnificationRoot(registerIdx);
1756 outputsInRegister[root].insert(outputNode);
1757 }
1758
1759 // Create PointsToGraph::RegisterNodes for each PointerObject of register kind, and add edges
1760 for (auto & [registerIdx, outputNodes] : outputsInRegister)
1761 {
1762 const auto ptgNode = pointsToGraph->addNodeForRegisters();
1763 for (auto outputNode : outputNodes.Items())
1764 pointsToGraph->mapRegisterToNode(*outputNode, ptgNode);
1765 applyPointsToSet(ptgNode, registerIdx);
1766 }
1767
1768 // Now add all edges from memory node to memory node.
1769 for (auto [pointerObjectSetIndex, ptgNode] : memoryNodeMapping)
1770 {
1771 applyPointsToSet(ptgNode, pointerObjectSetIndex);
1772 }
1773
1774 statistics.StopPointsToGraphConstructionStatistics(*pointsToGraph);
1775 return pointsToGraph;
1776}
1777
1778std::unique_ptr<PointsToGraph>
1780{
1781 // Create a throwaway instance of statistics
1782 Statistics statistics(util::FilePath(""));
1783 return ConstructPointsToGraphFromPointerObjectSet(set, statistics);
1784}
1785
1786}
static jlm::util::StatisticsCollector statisticsCollector
Call operation class.
Definition call.hpp:251
static rvsdg::Input & GetFunctionInput(const rvsdg::Node &node) noexcept
Definition call.hpp:321
ConstantPointerNullOperation class.
FreezeOperation class.
Get address of compiled function object.
static rvsdg::Output & LoadedValueOutput(const rvsdg::Node &node)
Definition Load.hpp:84
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition Load.hpp:75
static rvsdg::Output & addressOutput(const rvsdg::Node &node)
static rvsdg::Input & destinationInput(const rvsdg::Node &node) noexcept
Interpret pointer as callable function.
PoisonValueOperation class.
static rvsdg::Input & StoredValueInput(const rvsdg::Node &node) noexcept
Definition Store.hpp:84
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition Store.hpp:75
UndefValueOperation class.
bool IsOfflineVariableSubstitutionEnabled() const noexcept
Definition Andersen.hpp:114
bool IsLazyCycleDetectionEnabled() const noexcept
Definition Andersen.hpp:182
PointerObjectConstraintSet::WorklistSolverPolicy GetWorklistSoliverPolicy() const noexcept
Definition Andersen.hpp:96
bool IsHybridCycleDetectionEnabled() const noexcept
Definition Andersen.hpp:165
bool IsPreferImplicitPointeesEnabled() const noexcept
Definition Andersen.hpp:214
bool IsDifferencePropagationEnabled() const noexcept
Definition Andersen.hpp:199
static Configuration NaiveSolverConfiguration() noexcept
Definition Andersen.hpp:249
PointerObjectConstraintSet::WorklistSolverPolicy WorklistSolverPolicy_
Definition Andersen.hpp:270
bool IsOnlineCycleDetectionEnabled() const noexcept
Definition Andersen.hpp:148
static std::vector< Configuration > GetAllConfigurations()
Definition Andersen.cpp:76
bool IsOfflineConstraintNormalizationEnabled() const noexcept
Definition Andersen.hpp:131
Solver GetSolver() const noexcept
Definition Andersen.hpp:80
void StartConstraintSolvingNaiveStatistics() noexcept
Definition Andersen.cpp:376
Statistics(const util::FilePath &sourceFile)
Definition Andersen.cpp:280
void StartSetAndConstraintBuildingStatistics() noexcept
Definition Andersen.cpp:292
void StartConstraintSolvingWorklistStatistics() noexcept
Definition Andersen.cpp:389
void StartOfflineConstraintNormalization() noexcept
Definition Andersen.cpp:363
void StopOfflineVariableSubstitution(size_t numUnifications) noexcept
Definition Andersen.cpp:356
void StartOfflineVariableSubstitution() noexcept
Definition Andersen.cpp:350
void AddStatisticsFromSolution(const PointerObjectSet &set)
Definition Andersen.cpp:441
void StopPointsToGraphConstructionStatistics(const PointsToGraph &pointsToGraph)
Definition Andersen.cpp:584
void StopSetAndConstraintBuildingStatistics(const PointerObjectSet &set, const PointerObjectConstraintSet &constraints) noexcept
Definition Andersen.cpp:298
void StopOfflineConstraintNormalization(size_t numConstraintsRemoved) noexcept
Definition Andersen.cpp:369
void StartAndersenStatistics(const rvsdg::Graph &graph) noexcept
Definition Andersen.cpp:285
void StopConstraintSolvingNaiveStatistics(size_t numIterations) noexcept
Definition Andersen.cpp:382
static std::unique_ptr< Statistics > Create(const util::FilePath &sourceFile)
Definition Andersen.cpp:614
void AddStatisticFromConfiguration(const Configuration &config)
Definition Andersen.cpp:435
void StopConstraintSolvingWorklistStatistics(PointerObjectConstraintSet::WorklistStatistics &statistics) noexcept
Definition Andersen.cpp:395
void AnalyzeIOBarrier(const rvsdg::SimpleNode &node)
const Configuration & GetConfiguration() const
void AnalyzeMemmove(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:993
static void SolveConstraints(PointerObjectConstraintSet &constraints, const Configuration &config, Statistics &statistics)
void AnalyzePoison(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:940
void AnalyzeConstantPointerNull(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:914
void SetConfiguration(Configuration config)
void AnalyzePtrMask(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:855
void AnalyzeValist(const rvsdg::SimpleNode &node)
void AnalyzePointerToFunction(const rvsdg::SimpleNode &node)
void AnalyzeStore(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:797
static const char *const ENV_USE_EXACT_CONFIG
Definition Andersen.hpp:41
Configuration Config_
Definition Andersen.hpp:472
std::unique_ptr< PointerObjectSet > Set_
Definition Andersen.hpp:474
void AnalyzeUndef(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:926
static std::unique_ptr< PointsToGraph > ConstructPointsToGraphFromPointerObjectSet(const PointerObjectSet &set, Statistics &statistics)
static const char *const ENV_DOUBLE_CHECK
Definition Andersen.hpp:48
void AnalyzeRegion(rvsdg::Region &region)
void AnalyzeGep(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:840
~Andersen() noexcept override
void AnalyzeRvsdg(const rvsdg::Graph &graph)
void AnalyzeAlloca(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:750
void AnalyzeModule(const rvsdg::RvsdgModule &module, Statistics &statistics)
void AnalyzePtrToInt(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:902
void AnalyzeMemset(const rvsdg::SimpleNode &node)
std::unique_ptr< PointsToGraph > Analyze(const rvsdg::RvsdgModule &module, util::StatisticsCollector &statisticsCollector) override
void AnalyzeExtractValue(const rvsdg::SimpleNode &node)
void AnalyzeConstantArray(const rvsdg::SimpleNode &node)
void AnalyzeInsertValue(const rvsdg::SimpleNode &node)
void AnalyzeConstantAggregateZero(const rvsdg::SimpleNode &node)
void AnalyzeBitcast(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:869
void AnalyzePhi(const rvsdg::PhiNode &node)
static const char *const ENV_DUMP_SUBSET_GRAPH
Definition Andersen.hpp:53
void AnalyzeMemcpy(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:971
void AnalyzeCall(const rvsdg::SimpleNode &callNode)
Definition Andersen.cpp:817
void AnalyzeConstantStruct(const rvsdg::SimpleNode &node)
std::unique_ptr< PointerObjectConstraintSet > Constraints_
Definition Andersen.hpp:475
void AnalyzeTheta(const rvsdg::ThetaNode &node)
void AnalyzeFreeze(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:954
void AnalyzeMalloc(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:763
void AnalyzeLambda(const rvsdg::LambdaNode &node)
void AnalyzeBits2ptr(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:888
void AnalyzeLoad(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:776
void AnalyzeSimpleNode(const rvsdg::SimpleNode &node)
Definition Andersen.cpp:625
void AnalyzeFunctionToPointer(const rvsdg::SimpleNode &node)
void AnalyzeGamma(const rvsdg::GammaNode &node)
void AnalyzeStructuralNode(const rvsdg::StructuralNode &node)
static const char *const ENV_TEST_ALL_CONFIGS
Definition Andersen.hpp:34
void AnalyzeDelta(const rvsdg::DeltaNode &node)
size_t PerformOfflineVariableSubstitution(bool storeRefCycleUnificationRoot)
WorklistStatistics SolveUsingWorklist(WorklistSolverPolicy policy, bool enableOnlineCycleDetection, bool enableHybridCycleDetection, bool enableLazyCycleDetection, bool enableDifferencePropagation, bool enablePreferImplicitPropation)
static const char * WorklistSolverPolicyToString(WorklistSolverPolicy policy)
const util::BijectiveMap< const rvsdg::LambdaNode *, PointerObjectIndex > & GetFunctionMap() const noexcept
size_t GetNumSetInsertionAttempts() const noexcept
const std::unordered_map< const rvsdg::SimpleNode *, PointerObjectIndex > & GetMallocMap() const noexcept
size_t GetNumExplicitPointeesRemoved() const noexcept
const util::HashSet< PointerObjectIndex > & GetPointsToSet(PointerObjectIndex index) const
bool HasPointeesEscaping(PointerObjectIndex index) const noexcept
bool CanPoint(PointerObjectIndex index) const noexcept
const std::unordered_map< const rvsdg::SimpleNode *, PointerObjectIndex > & GetAllocaMap() const noexcept
bool IsPointingToExternal(PointerObjectIndex index) const noexcept
bool HasEscaped(PointerObjectIndex index) const noexcept
const std::unordered_map< const LlvmGraphImport *, PointerObjectIndex > & GetImportMap() const noexcept
const std::unordered_map< const rvsdg::DeltaNode *, PointerObjectIndex > & GetGlobalMap() const noexcept
PointerObjectIndex GetUnificationRoot(PointerObjectIndex index) const noexcept
bool IsUnificationRoot(PointerObjectIndex index) const noexcept
size_t NumPointerObjects() const noexcept
const std::unordered_map< const rvsdg::Output *, PointerObjectIndex > & GetRegisterMap() const noexcept
size_t numLambdaNodes() const noexcept
size_t numNodesTargetingAllExternallyAvailable() const noexcept
size_t numNodes() const noexcept
size_t numMemoryNodes() const noexcept
size_t numRegisterNodes() const noexcept
size_t numMallocNodes() const noexcept
size_t numExternallyAvailableNodes() const noexcept
size_t numDeltaNodes() const noexcept
static std::unique_ptr< PointsToGraph > create()
size_t numAllocaNodes() const noexcept
size_t numImportNodes() const noexcept
std::pair< size_t, size_t > numEdges() const noexcept
rvsdg::Input & result() const noexcept
Definition delta.cpp:116
std::vector< ContextVar > GetContextVars() const noexcept
Gets all bound context variables.
Definition delta.cpp:39
rvsdg::Region * subregion() const noexcept
Definition delta.hpp:234
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition delta.hpp:243
rvsdg::Output & output() const noexcept
Definition delta.cpp:110
Conditional operator / pattern matching.
Definition gamma.hpp:99
std::vector< ExitVar > GetExitVars() const
Gets all exit variables for this gamma.
Definition gamma.cpp:381
std::vector< EntryVar > GetEntryVars() const
Gets all entry variables for this gamma.
Definition gamma.cpp:305
Region & GetRootRegion() const noexcept
Definition graph.hpp:99
Output * origin() const noexcept
Definition node.hpp:58
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition node.hpp:67
std::vector< rvsdg::Output * > GetFunctionArguments() const
Definition lambda.cpp:58
rvsdg::Output * output() const noexcept
Definition lambda.cpp:177
rvsdg::Region * subregion() const noexcept
Definition lambda.hpp:138
std::vector< ContextVar > GetContextVars() const noexcept
Gets all bound context variables.
Definition lambda.cpp:120
size_t ninputs() const noexcept
Definition node.hpp:609
size_t noutputs() const noexcept
Definition node.hpp:644
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition node.hpp:366
A phi node represents the fixpoint of mutually recursive definitions.
Definition Phi.hpp:46
std::vector< FixVar > GetFixVars() const noexcept
Gets all fixpoint variables.
Definition Phi.cpp:63
std::vector< ContextVar > GetContextVars() const noexcept
Gets all bound context variables.
Definition Phi.cpp:50
rvsdg::Region * subregion() const noexcept
Definition Phi.hpp:320
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
RegionArgument * argument(size_t index) const noexcept
Definition region.hpp:466
size_t narguments() const noexcept
Definition region.hpp:460
const std::optional< util::FilePath > & SourceFilePath() const noexcept
Graph & Rvsdg() noexcept
const SimpleOperation & GetOperation() const noexcept override
NodeInput * input(size_t index) const noexcept
NodeOutput * output(size_t index) const noexcept
rvsdg::Region * subregion(size_t index) const noexcept
size_t nsubregions() const noexcept
std::vector< LoopVar > GetLoopVars() const
Returns all loop variables.
Definition theta.cpp:176
rvsdg::Region * subregion() const noexcept
Definition theta.hpp:79
std::size_t Size() const noexcept
Definition HashSet.hpp:187
IteratorRange< ItemConstIterator > Items() const noexcept
Definition HashSet.hpp:223
void CollectDemandedStatistics(std::unique_ptr< Statistics > statistics)
Statistics Interface.
void outputAllGraphs(std::ostream &out, OutputFormat format)
#define JLM_ASSERT(x)
Definition common.hpp:16
#define JLM_UNREACHABLE(msg)
Definition common.hpp:43
bool IsOrContainsPointerType(const rvsdg::Type &type)
Definition Andersen.cpp:30
uint32_t PointerObjectIndex
bool IsAggregateType(const jlm::rvsdg::Type &type)
Definition types.hpp:531
void MatchTypeWithDefault(T &obj, const Fns &... fns)
Pattern match over subclass type of given object with default handler.