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 MemoryHoistBarrierOperation &)
730 {
731 AnalyzeMemoryHoistBarrier(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<MemoryHoistBarrierOperation>(node.GetOperation()));
1193
1194 const auto & inputRegister = *MemoryHoistBarrierOperation::getAddressInput(node).origin();
1195 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1196 const auto & outputRegister = MemoryHoistBarrierOperation::getAddressOutput(node);
1197 Set_->MapRegisterToExistingPointerObject(outputRegister, inputRegisterPO);
1198}
1199
1200void
1202{
1203 MatchTypeOrFail(
1204 node,
1205 [this](const rvsdg::LambdaNode & lambdaNode)
1206 {
1207 AnalyzeLambda(lambdaNode);
1208 },
1209 [this](const rvsdg::DeltaNode & deltaNode)
1210 {
1211 AnalyzeDelta(deltaNode);
1212 },
1213 [this](const rvsdg::PhiNode & phiNode)
1214 {
1215 AnalyzePhi(phiNode);
1216 },
1217 [this](const rvsdg::GammaNode & gammaNode)
1218 {
1219 AnalyzeGamma(gammaNode);
1220 },
1221 [this](const rvsdg::ThetaNode & thetaNode)
1222 {
1223 AnalyzeTheta(thetaNode);
1224 });
1225}
1226
1227void
1229{
1230 // Handle context variables
1231 for (const auto & cv : lambda.GetContextVars())
1232 {
1233 if (!IsOrContainsPointerType(*cv.input->Type()))
1234 continue;
1235
1236 auto & inputRegister = *cv.input->origin();
1237 auto & argumentRegister = *cv.inner;
1238 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1239 Set_->MapRegisterToExistingPointerObject(argumentRegister, inputRegisterPO);
1240 }
1241
1242 // Create Register PointerObjects for each argument of pointing type in the function
1243 for (auto argument : lambda.GetFunctionArguments())
1244 {
1245 if (IsOrContainsPointerType(*argument->Type()))
1246 (void)Set_->CreateRegisterPointerObject(*argument);
1247 }
1248
1249 AnalyzeRegion(*lambda.subregion());
1250
1251 // Create a lambda PointerObject for the lambda itself
1252 const auto lambdaPO = Set_->CreateFunctionMemoryObject(lambda);
1253
1254 // Make the lambda node's output point to the lambda PointerObject
1255 const auto & lambdaOutput = *lambda.output();
1256 const auto lambdaOutputPO = Set_->CreateRegisterPointerObject(lambdaOutput);
1257 Constraints_->AddPointerPointeeConstraint(lambdaOutputPO, lambdaPO);
1258}
1259
1260void
1262{
1263 // Handle context variables
1264 for (auto & cv : delta.GetContextVars())
1265 {
1266 if (!IsOrContainsPointerType(*cv.input->Type()))
1267 continue;
1268
1269 auto & inputRegister = *cv.input->origin();
1270 auto & argumentRegister = *cv.inner;
1271 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1272 Set_->MapRegisterToExistingPointerObject(argumentRegister, inputRegisterPO);
1273 }
1274
1275 AnalyzeRegion(*delta.subregion());
1276
1277 // Get the result register from the subregion
1278 auto & resultRegister = *delta.result().origin();
1279
1280 // If the type of the delta can point, the analysis should track its set of possible pointees
1281 bool canPoint = IsOrContainsPointerType(*delta.Type());
1282
1283 // Create a global memory object representing the global variable
1284 const auto globalPO = Set_->CreateGlobalMemoryObject(delta, canPoint);
1285
1286 // If the initializer subregion result is a pointer, make the global point to what it points to
1287 if (canPoint)
1288 {
1289 const auto resultRegisterPO = Set_->GetRegisterPointerObject(resultRegister);
1290 Constraints_->AddConstraint(SupersetConstraint(globalPO, resultRegisterPO));
1291 }
1292
1293 // Finally create a Register PointerObject for the delta's output, pointing to the memory object
1294 auto & outputRegister = delta.output();
1295 const auto outputRegisterPO = Set_->CreateRegisterPointerObject(outputRegister);
1296 Constraints_->AddPointerPointeeConstraint(outputRegisterPO, globalPO);
1297}
1298
1299void
1301{
1302 // Handle context variables
1303 for (auto var : phi.GetContextVars())
1304 {
1305 if (!IsOrContainsPointerType(*var.inner->Type()))
1306 continue;
1307
1308 auto & inputRegister = *var.input->origin();
1309 auto & argumentRegister = *var.inner;
1310 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1311 Set_->MapRegisterToExistingPointerObject(argumentRegister, inputRegisterPO);
1312 }
1313
1314 // Create Register PointerObjects for each fixpoint variable argument
1315 for (auto var : phi.GetFixVars())
1316 {
1317 if (!IsOrContainsPointerType(*var.output->Type()))
1318 continue;
1319
1320 auto & argumentRegister = *var.recref;
1321 (void)Set_->CreateRegisterPointerObject(argumentRegister);
1322 }
1323
1324 AnalyzeRegion(*phi.subregion());
1325
1326 // Handle recursive definition results
1327 for (auto var : phi.GetFixVars())
1328 {
1329 if (!IsOrContainsPointerType(*var.output->Type()))
1330 continue;
1331
1332 // Make the recursion variable argument point to what the result register points to
1333 auto & argumentRegister = *var.recref;
1334 auto & resultRegister = *var.result->origin();
1335 const auto argumentRegisterPO = Set_->GetRegisterPointerObject(argumentRegister);
1336 const auto resultRegisterPO = Set_->GetRegisterPointerObject(resultRegister);
1337 Constraints_->AddConstraint(SupersetConstraint(argumentRegisterPO, resultRegisterPO));
1338
1339 // Map the output register to the recursion result's pointer object
1340 auto & outputRegister = *var.output;
1341 Set_->MapRegisterToExistingPointerObject(outputRegister, resultRegisterPO);
1342 }
1343}
1344
1345void
1347{
1348 // Handle input variables
1349 for (const auto & ev : gamma.GetEntryVars())
1350 {
1351 if (!IsOrContainsPointerType(*ev.input->Type()))
1352 continue;
1353
1354 auto & inputRegister = *ev.input->origin();
1355 const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1356
1357 for (auto & argument : ev.branchArgument)
1358 Set_->MapRegisterToExistingPointerObject(*argument, inputRegisterPO);
1359 }
1360
1361 // Handle subregions
1362 for (size_t n = 0; n < gamma.nsubregions(); n++)
1363 AnalyzeRegion(*gamma.subregion(n));
1364
1365 // Handle exit variables
1366 for (const auto & ex : gamma.GetExitVars())
1367 {
1368 if (!IsOrContainsPointerType(*ex.output->Type()))
1369 continue;
1370
1371 auto & outputRegister = ex.output;
1372 const auto outputRegisterPO = Set_->CreateRegisterPointerObject(*outputRegister);
1373
1374 for (auto result : ex.branchResult)
1375 {
1376 const auto resultRegisterPO = Set_->GetRegisterPointerObject(*result->origin());
1377 Constraints_->AddConstraint(SupersetConstraint(outputRegisterPO, resultRegisterPO));
1378 }
1379 }
1380}
1381
1382void
1384{
1385 // Create a PointerObject for each argument in the inner region
1386 // And make it point to a superset of the corresponding input register
1387 for (const auto & loopVar : theta.GetLoopVars())
1388 {
1389 if (!IsOrContainsPointerType(*loopVar.input->Type()))
1390 continue;
1391
1392 auto & inputReg = *loopVar.input->origin();
1393 auto & innerArgumentReg = *loopVar.pre;
1394 const auto inputRegPO = Set_->GetRegisterPointerObject(inputReg);
1395 const auto innerArgumentRegPO = Set_->CreateRegisterPointerObject(innerArgumentReg);
1396
1397 // The inner argument can point to anything the input did
1398 Constraints_->AddConstraint(SupersetConstraint(innerArgumentRegPO, inputRegPO));
1399 }
1400
1401 AnalyzeRegion(*theta.subregion());
1402
1403 // Iterate over loop variables again, making the inner arguments point to a superset
1404 // of what the corresponding result registers point to
1405 for (const auto & loopVar : theta.GetLoopVars())
1406 {
1407 if (!IsOrContainsPointerType(*loopVar.input->Type()))
1408 continue;
1409
1410 auto & innerArgumentReg = *loopVar.pre;
1411 auto & innerResultReg = *loopVar.post->origin();
1412 auto & outputReg = *loopVar.output;
1413
1414 const auto innerArgumentRegPO = Set_->GetRegisterPointerObject(innerArgumentReg);
1415 const auto innerResultRegPO = Set_->GetRegisterPointerObject(innerResultReg);
1416
1417 // The inner argument can point to anything the result of last iteration did
1418 Constraints_->AddConstraint(SupersetConstraint(innerArgumentRegPO, innerResultRegPO));
1419
1420 // Due to theta nodes running at least once, the output always comes from the inner results
1421 Set_->MapRegisterToExistingPointerObject(outputReg, innerResultRegPO);
1422 }
1423}
1424
1425void
1427{
1428 // Check that all region arguments of pointing types have PointerObjects
1429 for (size_t i = 0; i < region.narguments(); i++)
1430 {
1431 if (IsOrContainsPointerType(*region.argument(i)->Type()))
1432 JLM_ASSERT(Set_->GetRegisterMap().count(region.argument(i)));
1433 }
1434
1435 // The use of the top-down traverser is vital, as it ensures all input origins
1436 // of pointer type are mapped to PointerObjects by the time a node is processed.
1437 rvsdg::TopDownTraverser traverser(&region);
1438
1439 // While visiting the node we have the responsibility of creating
1440 // PointerObjects for any of the node's outputs of pointer type
1441 for (const auto node : traverser)
1442 {
1443 if (auto simpleNode = dynamic_cast<const rvsdg::SimpleNode *>(node))
1444 AnalyzeSimpleNode(*simpleNode);
1445 else if (auto structuralNode = dynamic_cast<const rvsdg::StructuralNode *>(node))
1446 AnalyzeStructuralNode(*structuralNode);
1447 else
1448 JLM_UNREACHABLE("Unknown node type");
1449
1450 // Check that all outputs with pointing types have PointerObjects created
1451 for (size_t i = 0; i < node->noutputs(); i++)
1452 {
1453 if (IsOrContainsPointerType(*node->output(i)->Type()))
1454 JLM_ASSERT(Set_->GetRegisterMap().count(node->output(i)));
1455 }
1456 }
1457}
1458
1459void
1461{
1462 auto & rootRegion = graph.GetRootRegion();
1463
1464 // Iterate over all arguments to the root region - symbols imported from other modules
1465 // These symbols can either be global variables or functions
1466 for (size_t n = 0; n < rootRegion.narguments(); n++)
1467 {
1468 auto & argument = *util::assertedCast<LlvmGraphImport>(rootRegion.argument(n));
1469
1470 // Only care about imported pointer values
1471 if (!IsOrContainsPointerType(*argument.Type()))
1472 continue;
1473
1474 // Imported symbols are always externally available, so all pointees can be implicit.
1475 // The value of CanPoint thus has no effect on the solver, only the final PointsToGraph node.
1476 const bool canPoint = IsOrContainsPointerType(*argument.ValueType());
1477
1478 // Create a memory PointerObject representing the target of the external symbol
1479 // We can assume that two external symbols don't alias. This is the assumption clang makes.
1480 const auto importObjectPO = Set_->CreateImportMemoryObject(argument, canPoint);
1481
1482 // Create a register PointerObject representing the address value itself
1483 const auto importRegisterPO = Set_->CreateRegisterPointerObject(argument);
1484 Constraints_->AddPointerPointeeConstraint(importRegisterPO, importObjectPO);
1485 }
1486
1487 AnalyzeRegion(rootRegion);
1488
1489 // Mark all results escaping the root module as escaped
1490 for (size_t n = 0; n < rootRegion.nresults(); n++)
1491 {
1492 auto & escapedRegister = *rootRegion.result(n)->origin();
1493 if (!IsOrContainsPointerType(*escapedRegister.Type()))
1494 continue;
1495
1496 const auto escapedRegisterPO = Set_->GetRegisterPointerObject(escapedRegister);
1497 Constraints_->AddRegisterContentEscapedConstraint(escapedRegisterPO);
1498 }
1499}
1500
1501void
1503{
1504 Config_ = std::move(config);
1505}
1506
1509{
1510 return Config_;
1511}
1512
1513void
1515{
1516 Set_ = std::make_unique<PointerObjectSet>();
1517 Constraints_ = std::make_unique<PointerObjectConstraintSet>(*Set_);
1518
1520 AnalyzeRvsdg(module.Rvsdg());
1522}
1523
1524void
1526 PointerObjectConstraintSet & constraints,
1527 const Configuration & config,
1528 Statistics & statistics)
1529{
1530 statistics.AddStatisticFromConfiguration(config);
1531
1533 {
1535 // If the solver uses hybrid cycle detection, tell OVS to store info about ref node cycles
1536 bool hasHCD = config.IsHybridCycleDetectionEnabled();
1537 auto numUnifications = constraints.PerformOfflineVariableSubstitution(hasHCD);
1538 statistics.StopOfflineVariableSubstitution(numUnifications);
1539 }
1540
1542 {
1544 auto numConstraintsRemoved = constraints.NormalizeConstraints();
1545 statistics.StopOfflineConstraintNormalization(numConstraintsRemoved);
1546 }
1547
1549 {
1551 size_t numIterations = constraints.SolveNaively();
1552 statistics.StopConstraintSolvingNaiveStatistics(numIterations);
1553 }
1554 else if (config.GetSolver() == Configuration::Solver::Worklist)
1555 {
1557 auto worklistStatistics = constraints.SolveUsingWorklist(
1558 config.GetWorklistSoliverPolicy(),
1564 statistics.StopConstraintSolvingWorklistStatistics(worklistStatistics);
1565 }
1566 else
1567 JLM_UNREACHABLE("Unknown solver");
1568}
1569
1570std::unique_ptr<PointsToGraph>
1572 const rvsdg::RvsdgModule & module,
1574{
1575 auto statistics = Statistics::Create(module.SourceFilePath().value());
1576 statistics->StartAndersenStatistics(module.Rvsdg());
1577
1578 // Check environment variables for debugging flags
1579 size_t testAllConfigsIterations = 0;
1580 if (auto testAllConfigsString = std::getenv(ENV_TEST_ALL_CONFIGS))
1581 testAllConfigsIterations = std::stoi(testAllConfigsString);
1582 std::optional<size_t> useExactConfig;
1583 if (auto useExactConfigString = std::getenv(ENV_USE_EXACT_CONFIG))
1584 useExactConfig = std::stoi(useExactConfigString);
1585 const bool doubleCheck = std::getenv(ENV_DOUBLE_CHECK);
1586
1587 const bool dumpGraphs = std::getenv(ENV_DUMP_SUBSET_GRAPH);
1588 util::graph::Writer writer;
1589
1590 AnalyzeModule(module, *statistics);
1591
1592 // If solving multiple times, make a copy of the original constraint set
1593 std::pair<std::unique_ptr<PointerObjectSet>, std::unique_ptr<PointerObjectConstraintSet>> copy;
1594 if (testAllConfigsIterations || doubleCheck)
1595 copy = Constraints_->Clone();
1596
1597 // Draw subset graph both before and after solving
1598 if (dumpGraphs)
1599 Constraints_->DrawSubsetGraph(writer);
1600
1601 auto config = Config_;
1602 if (useExactConfig.has_value())
1603 {
1604 auto allConfigs = Configuration::GetAllConfigurations();
1605 config = allConfigs.at(*useExactConfig);
1606 }
1607
1608 SolveConstraints(*Constraints_, config, *statistics);
1609 statistics->AddStatisticsFromSolution(*Set_);
1610
1611 if (dumpGraphs)
1612 {
1613 auto & graph = Constraints_->DrawSubsetGraph(writer);
1614 graph.AppendToLabel("After Solving with " + config.ToString());
1616 }
1617
1618 auto result = ConstructPointsToGraphFromPointerObjectSet(*Set_, *statistics);
1619
1620 statistics->StopAndersenStatistics();
1621 statisticsCollector.CollectDemandedStatistics(std::move(statistics));
1622
1623 // Solve again if double-checking against naive is enabled
1624 if (testAllConfigsIterations || doubleCheck)
1625 {
1626 if (doubleCheck)
1627 std::cerr << "Double checking Andersen analysis using naive solving" << std::endl;
1628
1629 // If double-checking, only use the naive configuration. Otherwise, try all configurations
1630 std::vector<Configuration> configs;
1631 if (testAllConfigsIterations)
1633 else
1634 configs.push_back(Configuration::NaiveSolverConfiguration());
1635
1636 // If testing all configurations, do it as many times as requested.
1637 // Otherwise, do it at least once
1638 const auto iterations = std::max<size_t>(testAllConfigsIterations, 1);
1639
1640 for (size_t i = 0; i < iterations; i++)
1641 {
1642 for (const auto & config : configs)
1643 {
1644 // Create a clone of the unsolved pointer object set and constraint set
1645 auto workingCopy = copy.second->Clone();
1646 // These statistics will only contain solving data
1647 auto solvingStats = Statistics::Create(module.SourceFilePath().value());
1648 SolveConstraints(*workingCopy.second, config, *solvingStats);
1649 solvingStats->AddStatisticsFromSolution(*workingCopy.first);
1650 statisticsCollector.CollectDemandedStatistics(std::move(solvingStats));
1651
1652 // Only double check on the first iteration
1653 if (doubleCheck && i == 0)
1654 {
1655 if (workingCopy.first->HasIdenticalSolAs(*Set_))
1656 continue;
1657 std::cerr << "Solving with original config: " << Config_.ToString()
1658 << " did not produce the same solution as the config " << config.ToString()
1659 << std::endl;
1660 JLM_UNREACHABLE("Andersen solver double checking uncovered differences!");
1661 }
1662 }
1663 }
1664 }
1665
1666 // Cleanup
1667 Constraints_.reset();
1668 Set_.reset();
1669 return result;
1670}
1671
1672std::unique_ptr<PointsToGraph>
1678
1679std::unique_ptr<PointsToGraph>
1681 const PointerObjectSet & set,
1682 Statistics & statistics)
1683{
1685
1686 auto pointsToGraph = PointsToGraph::create();
1687
1688 // Mapping from index in PointerObjectSet to the equivalent memory node in the PointsToGraph
1689 std::unordered_map<PointerObjectIndex, PointsToGraph::NodeIndex> memoryNodeMapping;
1690
1691 for (auto [allocaNode, pointerObjectIndex] : set.GetAllocaMap())
1692 {
1693 memoryNodeMapping[pointerObjectIndex] =
1694 pointsToGraph->addNodeForAlloca(*allocaNode, set.HasEscaped(pointerObjectIndex));
1695 }
1696
1697 for (auto [deltaNode, pointerObjectIndex] : set.GetGlobalMap())
1698 {
1699 memoryNodeMapping[pointerObjectIndex] =
1700 pointsToGraph->addNodeForDelta(*deltaNode, set.HasEscaped(pointerObjectIndex));
1701 }
1702
1703 for (auto [import, pointerObjectIndex] : set.GetImportMap())
1704 {
1705 memoryNodeMapping[pointerObjectIndex] =
1706 pointsToGraph->addNodeForImport(*import, set.HasEscaped(pointerObjectIndex));
1707 }
1708
1709 for (auto [lambdaNode, pointerObjectIndex] : set.GetFunctionMap())
1710 {
1711 memoryNodeMapping[pointerObjectIndex] =
1712 pointsToGraph->addNodeForLambda(*lambdaNode, set.HasEscaped(pointerObjectIndex));
1713 }
1714
1715 for (auto [mallocNode, pointerObjectIndex] : set.GetMallocMap())
1716 {
1717 memoryNodeMapping[pointerObjectIndex] =
1718 pointsToGraph->addNodeForMalloc(*mallocNode, set.HasEscaped(pointerObjectIndex));
1719 }
1720
1721 // Helper function for attaching PointsToGraph nodes to their pointees, based on the
1722 // PointerObject's points-to set.
1723 auto applyPointsToSet = [&](PointsToGraph::NodeIndex ptgNode, PointerObjectIndex index)
1724 {
1725 // PointerObjectSets that are marked as not containing pointers can be added to the
1726 // PointsToGraph without any explicit or implicit pointees.
1727 if (!set.CanPoint(index))
1728 {
1729 return;
1730 }
1731
1732 // Mark nodes that target everything that is externally available
1733 if (set.IsPointingToExternal(index))
1734 {
1735 pointsToGraph->markAsTargetsAllExternallyAvailable(ptgNode);
1736 }
1737
1738 // Add all explicit pointees. Doubled up pointees are ignored by the PtG
1739 for (const auto targetIdx : set.GetPointsToSet(index).Items())
1740 {
1741 pointsToGraph->addTarget(ptgNode, memoryNodeMapping[targetIdx]);
1742 }
1743 };
1744
1745 // First group RVSDG registers by the PointerObject they are mapped to.
1746 // If the PointerObject is part of a unification, all Register PointerObjects in the unification
1747 // share points-to set, so they can all become one RegisterNode in the PointsToGraph.
1748 std::unordered_map<PointerObjectIndex, util::HashSet<const rvsdg::Output *>> outputsInRegister;
1749 for (auto [outputNode, registerIdx] : set.GetRegisterMap())
1750 {
1751 auto root = set.GetUnificationRoot(registerIdx);
1752 outputsInRegister[root].insert(outputNode);
1753 }
1754
1755 // Create PointsToGraph::RegisterNodes for each PointerObject of register kind, and add edges
1756 for (auto & [registerIdx, outputNodes] : outputsInRegister)
1757 {
1758 const auto ptgNode = pointsToGraph->addNodeForRegisters();
1759 for (auto outputNode : outputNodes.Items())
1760 pointsToGraph->mapRegisterToNode(*outputNode, ptgNode);
1761 applyPointsToSet(ptgNode, registerIdx);
1762 }
1763
1764 // Now add all edges from memory node to memory node.
1765 for (auto [pointerObjectSetIndex, ptgNode] : memoryNodeMapping)
1766 {
1767 applyPointsToSet(ptgNode, pointerObjectSetIndex);
1768 }
1769
1770 statistics.StopPointsToGraphConstructionStatistics(*pointsToGraph);
1771 return pointsToGraph;
1772}
1773
1774std::unique_ptr<PointsToGraph>
1776{
1777 // Create a throwaway instance of statistics
1778 Statistics statistics(util::FilePath(""));
1779 return ConstructPointsToGraphFromPointerObjectSet(set, statistics);
1780}
1781
1782}
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
static rvsdg::Output & getAddressOutput(const rvsdg::Node &node) noexcept
static rvsdg::Input & getAddressInput(const rvsdg::Node &node) noexcept
Interpret pointer as callable function.
PoisonValueOperation class.
static rvsdg::Input & StoredValueInput(const rvsdg::Node &node) noexcept
Definition Store.hpp:99
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition Store.hpp:90
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
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 AnalyzeMemoryHoistBarrier(const rvsdg::SimpleNode &node)
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:193
rvsdg::Region * subregion() const noexcept
Definition theta.hpp:90
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
static util::StatisticsCollector statisticsCollector
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.