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