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 
17 #include <jlm/rvsdg/MatchType.hpp>
18 #include <jlm/rvsdg/traverser.hpp>
19 #include <jlm/util/Statistics.hpp>
20 
21 namespace jlm::llvm::aa
22 {
23 
29 bool
31 {
32  return IsOrContains<PointerType>(type) || is<rvsdg::FunctionType>(type);
33 }
34 
35 std::string
37 {
38  std::ostringstream str;
40  str << "OVS_";
42  str << "NORM_";
43  if (Solver_ == Solver::Naive)
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 
75 std::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 
277 public:
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_,
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 
620 Andersen::Andersen() = default;
621 
622 Andersen::~Andersen() noexcept = default;
623 
624 void
625 Andersen::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 BitCastOperation &)
654  {
655  AnalyzeBitcast(node);
656  },
657  [&](const IntegerToPointerOperation &)
658  {
659  AnalyzeBits2ptr(node);
660  },
661  [&](const PtrToIntOperation &)
662  {
663  AnalyzePtrToInt(node);
664  },
665  [&](const ConstantPointerNullOperation &)
666  {
667  AnalyzeConstantPointerNull(node);
668  },
669  [&](const UndefValueOperation &)
670  {
671  AnalyzeUndef(node);
672  },
673  [&](const PoisonValueOperation &)
674  {
675  AnalyzePoison(node);
676  },
677  [&](const FreezeOperation &)
678  {
679  AnalyzeFreeze(node);
680  },
681  [&](const MemCpyOperation &)
682  {
683  AnalyzeMemcpy(node);
684  },
685  [&](const MemSetOperation &)
686  {
687  AnalyzeMemset(node);
688  },
689  [&](const ConstantArrayOperation &)
690  {
691  AnalyzeConstantArray(node);
692  },
693  [&](const ConstantStruct &)
694  {
695  AnalyzeConstantStruct(node);
696  },
697  [&](const ConstantAggregateZeroOperation &)
698  {
699  AnalyzeConstantAggregateZero(node);
700  },
701  [&](const InsertValueOperation &)
702  {
703  AnalyzeInsertValue(node);
704  },
705  [&](const ExtractValueOperation &)
706  {
707  AnalyzeExtractValue(node);
708  },
709  [&](const VariadicArgumentListOperation &)
710  {
711  AnalyzeValist(node);
712  },
713  [&](const PointerToFunctionOperation &)
714  {
715  AnalyzePointerToFunction(node);
716  },
717  [&](const FunctionToPointerOperation &)
718  {
719  AnalyzeFunctionToPointer(node);
720  },
721  [&](const IOBarrierOperation &)
722  {
723  AnalyzeIOBarrier(node);
724  },
725  [&](const FreeOperation &)
726  {
727  // Takes pointers as input, but does not affect any points-to sets
728  },
729  [&](const PtrCmpOperation &)
730  {
731  // Takes pointers as input, but does not affect any points-to sets
732  },
733  [&]()
734  {
735  // This node operation is unknown, make sure it doesn't consume any pointers
736  for (size_t n = 0; n < node.ninputs(); n++)
737  JLM_ASSERT(!IsOrContainsPointerType(*node.input(n)->Type()));
738  });
739 }
740 
741 void
743 {
744  const auto allocaOp = util::assertedCast<const AllocaOperation>(&node.GetOperation());
745 
746  const auto & outputRegister = *node.output(0);
747  const auto outputRegisterPO = Set_->CreateRegisterPointerObject(outputRegister);
748 
749  const bool canPoint = IsOrContainsPointerType(*allocaOp->allocatedType());
750  const auto allocaPO = Set_->CreateAllocaMemoryObject(node, canPoint);
751  Constraints_->AddPointerPointeeConstraint(outputRegisterPO, allocaPO);
752 }
753 
754 void
756 {
757  JLM_ASSERT(is<MallocOperation>(node.GetOperation()));
758 
759  const auto & outputRegister = MallocOperation::addressOutput(node);
760  const auto outputRegisterPO = Set_->CreateRegisterPointerObject(outputRegister);
761 
762  // We do not know what types will be stored in the malloc, so let it track pointers
763  const auto mallocPO = Set_->CreateMallocMemoryObject(node, true);
764  Constraints_->AddPointerPointeeConstraint(outputRegisterPO, mallocPO);
765 }
766 
767 void
769 {
770  JLM_ASSERT(is<LoadOperation>(node.GetOperation()));
771 
772  const auto & addressRegister = *LoadOperation::AddressInput(node).origin();
773  const auto & outputRegister = LoadOperation::LoadedValueOutput(node);
774 
775  const auto addressRegisterPO = Set_->GetRegisterPointerObject(addressRegister);
776 
777  if (IsOrContainsPointerType(*outputRegister.Type()))
778  {
779  const auto outputRegisterPO = Set_->CreateRegisterPointerObject(outputRegister);
780  Constraints_->AddConstraint(LoadConstraint(outputRegisterPO, addressRegisterPO));
781  }
782  else
783  {
784  Set_->MarkAsLoadingAsScalar(addressRegisterPO);
785  }
786 }
787 
788 void
790 {
791  const auto & addressRegister = *StoreOperation::AddressInput(node).origin();
792  const auto & valueRegister = *StoreOperation::StoredValueInput(node).origin();
793 
794  const auto addressRegisterPO = Set_->GetRegisterPointerObject(addressRegister);
795 
796  // If the written value is not a pointer, be conservative and mark the address
797  if (IsOrContainsPointerType(*valueRegister.Type()))
798  {
799  const auto valueRegisterPO = Set_->GetRegisterPointerObject(valueRegister);
800  Constraints_->AddConstraint(StoreConstraint(addressRegisterPO, valueRegisterPO));
801  }
802  else
803  {
804  Set_->MarkAsStoringAsScalar(addressRegisterPO);
805  }
806 }
807 
808 void
810 {
811  JLM_ASSERT(is<CallOperation>(callNode.GetOperation()));
812 
813  // The address being called by the call node
814  const auto & callTarget = *CallOperation::GetFunctionInput(callNode).origin();
815  const auto callTargetPO = Set_->GetRegisterPointerObject(callTarget);
816 
817  // Create PointerObjects for all output values of pointer type
818  for (size_t n = 0; n < callNode.noutputs(); n++)
819  {
820  const auto & outputRegister = *callNode.output(n);
821  if (IsOrContainsPointerType(*outputRegister.Type()))
822  (void)Set_->CreateRegisterPointerObject(outputRegister);
823  }
824 
825  // We make no attempt at detecting what type of call this is here.
826  // The logic handling external and indirect calls is done by the FunctionCallConstraint.
827  // Passing points-to-sets from call-site to function bodies is done fully by this constraint.
828  Constraints_->AddConstraint(FunctionCallConstraint(callTargetPO, callNode));
829 }
830 
831 void
833 {
834  JLM_ASSERT(is<GetElementPtrOperation>(node.GetOperation()));
835 
836  // The analysis is field insensitive, so ignoring the offset and mapping the output
837  // to the same PointerObject as the input is sufficient.
838  const auto & baseRegister = *node.input(0)->origin();
839  JLM_ASSERT(is<PointerType>(baseRegister.Type()));
840 
841  const auto baseRegisterPO = Set_->GetRegisterPointerObject(baseRegister);
842  const auto & outputRegister = *node.output(0);
843  Set_->MapRegisterToExistingPointerObject(outputRegister, baseRegisterPO);
844 }
845 
846 void
848 {
849  JLM_ASSERT(is<BitCastOperation>(node.GetOperation()));
850 
851  const auto & inputRegister = *node.input(0)->origin();
852  const auto & outputRegister = *node.output(0);
853 
854  JLM_ASSERT(!IsAggregateType(*inputRegister.Type()) && !IsAggregateType(*outputRegister.Type()));
855  if (!IsOrContainsPointerType(*inputRegister.Type()))
856  return;
857 
858  // If the input is a pointer type, the output must also be a pointer type
859  JLM_ASSERT(IsOrContainsPointerType(*outputRegister.Type()));
860 
861  const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
862  Set_->MapRegisterToExistingPointerObject(outputRegister, inputRegisterPO);
863 }
864 
865 void
867 {
868  JLM_ASSERT(is<IntegerToPointerOperation>(node.GetOperation()));
869  const auto & output = *node.output(0);
870  JLM_ASSERT(is<PointerType>(output.Type()));
871 
872  // This operation synthesizes a pointer from bytes.
873  // Since no points-to information is tracked through integers, the resulting pointer must
874  // be assumed to possibly point to any external or escaped memory object.
875  const auto outputPO = Set_->CreateRegisterPointerObject(output);
876  Constraints_->AddPointsToExternalConstraint(outputPO);
877 }
878 
879 void
881 {
882  JLM_ASSERT(is<PtrToIntOperation>(node.GetOperation()));
883  const auto & inputRegister = *node.input(0)->origin();
884  JLM_ASSERT(is<PointerType>(inputRegister.Type()));
885 
886  // This operation converts a pointer to bytes, exposing it as an integer, which we can't track.
887  const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
888  Constraints_->AddRegisterContentEscapedConstraint(inputRegisterPO);
889 }
890 
891 void
893 {
894  JLM_ASSERT(is<ConstantPointerNullOperation>(node.GetOperation()));
895  const auto & output = *node.output(0);
896  JLM_ASSERT(is<PointerType>(output.Type()));
897 
898  // ConstantPointerNull cannot point to any memory location. We therefore only insert a register
899  // node for it, but let this node not point to anything.
900  (void)Set_->CreateRegisterPointerObject(output);
901 }
902 
903 void
905 {
906  JLM_ASSERT(is<UndefValueOperation>(node.GetOperation()));
907  const auto & output = *node.output(0);
908 
909  if (!IsOrContainsPointerType(*output.Type()))
910  return;
911 
912  // UndefValue cannot point to any memory location. We therefore only insert a register node for
913  // it, but let this node not point to anything.
914  (void)Set_->CreateRegisterPointerObject(output);
915 }
916 
917 void
919 {
920  JLM_ASSERT(is<PoisonValueOperation>(node.GetOperation()));
921  const auto & output = *node.output(0);
922 
923  if (!IsOrContainsPointerType(*output.Type()))
924  return;
925 
926  // PoisonValue cannot point to any memory location. We therefore only insert a register node for
927  // it, but let this node not point to anything.
928  (void)Set_->CreateRegisterPointerObject(output);
929 }
930 
931 void
933 {
934  JLM_ASSERT(is<FreezeOperation>(node.GetOperation()));
935  const auto & output = *node.output(0);
936 
937  if (!IsOrContainsPointerType(*output.Type()))
938  return;
939 
940  // Freeze will either be a copy of its argument (when defined),
941  // or be an illegal pointer, so just re-use its argument
942 
943  auto & operand = *node.input(0)->origin();
944  auto operandPO = Set_->GetRegisterPointerObject(operand);
945  Set_->MapRegisterToExistingPointerObject(output, operandPO);
946 }
947 
948 void
950 {
951  JLM_ASSERT(is<MemCpyOperation>(node.GetOperation()));
952 
953  auto & dstAddressRegister = *node.input(0)->origin();
954  auto & srcAddressRegister = *node.input(1)->origin();
955  JLM_ASSERT(is<PointerType>(dstAddressRegister.Type()));
956  JLM_ASSERT(is<PointerType>(srcAddressRegister.Type()));
957 
958  const auto dstAddressRegisterPO = Set_->GetRegisterPointerObject(dstAddressRegister);
959  const auto srcAddressRegisterPO = Set_->GetRegisterPointerObject(srcAddressRegister);
960 
961  // Create an intermediate PointerObject representing the moved values
962  const auto dummyPO = Set_->CreateDummyRegisterPointerObject();
963 
964  // Add a "load" constraint from the source into the dummy register
965  Constraints_->AddConstraint(LoadConstraint(dummyPO, srcAddressRegisterPO));
966  // Add a "store" constraint from the dummy register into the destination
967  Constraints_->AddConstraint(StoreConstraint(dstAddressRegisterPO, dummyPO));
968 }
969 
970 void
972 {
973  JLM_ASSERT(is<MemSetOperation>(node.GetOperation()));
974 
975  const auto & dstAddressRegister = *MemSetOperation::destinationInput(node).origin();
976  JLM_ASSERT(is<PointerType>(dstAddressRegister.Type()));
977 
978  const auto dstAddressRegisterPO = Set_->GetRegisterPointerObject(dstAddressRegister);
979 
980  Set_->MarkAsStoringAsScalar(dstAddressRegisterPO);
981 }
982 
983 void
985 {
986  JLM_ASSERT(is<ConstantArrayOperation>(node.GetOperation()));
987 
988  if (!IsOrContainsPointerType(*node.output(0)->Type()))
989  return;
990 
991  // Make the resulting array point to everything its members are pointing to
992  auto & outputRegister = *node.output(0);
993  const auto outputRegisterPO = Set_->CreateRegisterPointerObject(outputRegister);
994 
995  for (size_t n = 0; n < node.ninputs(); n++)
996  {
997  const auto & inputRegister = *node.input(n)->origin();
998  JLM_ASSERT(IsOrContainsPointerType(*inputRegister.Type()));
999 
1000  const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1001  Constraints_->AddConstraint(SupersetConstraint(outputRegisterPO, inputRegisterPO));
1002  }
1003 }
1004 
1005 void
1007 {
1008  JLM_ASSERT(is<ConstantStruct>(node.GetOperation()));
1009 
1010  if (!IsOrContainsPointerType(*node.output(0)->Type()))
1011  return;
1012 
1013  // Make the resulting struct point to everything its members are pointing to
1014  auto & outputRegister = *node.output(0);
1015  const auto outputRegisterPO = Set_->CreateRegisterPointerObject(outputRegister);
1016 
1017  for (size_t n = 0; n < node.ninputs(); n++)
1018  {
1019  const auto & inputRegister = *node.input(n)->origin();
1020  if (!IsOrContainsPointerType(*inputRegister.Type()))
1021  continue;
1022 
1023  const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1024  Constraints_->AddConstraint(SupersetConstraint(outputRegisterPO, inputRegisterPO));
1025  }
1026 }
1027 
1028 void
1030 {
1031  JLM_ASSERT(is<ConstantAggregateZeroOperation>(node.GetOperation()));
1032  auto & output = *node.output(0);
1033 
1034  if (!IsOrContainsPointerType(*output.Type()))
1035  return;
1036 
1037  // ConstantAggregateZero cannot point to any memory location.
1038  // We therefore only insert a register node for it, but let this node not point to anything.
1039  (void)Set_->CreateRegisterPointerObject(output);
1040 }
1041 
1042 void
1044 {
1045  JLM_ASSERT(is<InsertValueOperation>(node.GetOperation()));
1046 
1047  // The InsertValue instruction looks like
1048  // result = insertvalue(input, element, index, [additional indices...])
1049  // where result and input are values of some aggregate type (e.g. a struct).
1050  // The element is inserted into the result aggregate at the specified index.
1051  // Additional indicies can be given to index into nested aggregate types.
1052 
1053  // We only care about aggregate types that contain pointers
1054  const auto & result = *node.output(0);
1055  if (!IsOrContainsPointerType(*result.Type()))
1056  return;
1057 
1058  const auto & inputRegister = *node.input(0)->origin();
1059  const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1060 
1061  // Check if the element type also contains a pointer
1062  const auto & element = *node.input(1)->origin();
1063  if (IsOrContainsPointerType(*element.Type()))
1064  {
1065  // Both the original aggregate and the inserted element contain pointers
1066  // Create a new PointerObject for the result containing their union
1067  const auto elementPO = Set_->GetRegisterPointerObject(element);
1068  const auto resultPO = Set_->CreateRegisterPointerObject(result);
1069  Constraints_->AddConstraint(SupersetConstraint(resultPO, inputRegisterPO));
1070  Constraints_->AddConstraint(SupersetConstraint(resultPO, elementPO));
1071  }
1072  else
1073  {
1074  // The operation does not add any additional pointees, so map the result directly to the input
1075  Set_->MapRegisterToExistingPointerObject(result, inputRegisterPO);
1076  }
1077 }
1078 
1079 void
1081 {
1082  JLM_ASSERT(is<ExtractValueOperation>(node.GetOperation()));
1083 
1084  const auto & result = *node.output(0);
1085  if (!IsOrContainsPointerType(*result.Type()))
1086  return;
1087 
1088  const auto & inputRegister = *node.input(0)->origin();
1089  const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1090  // The resulting element can point to anything the aggregate type points to
1091  Set_->MapRegisterToExistingPointerObject(result, inputRegisterPO);
1092 }
1093 
1094 void
1096 {
1097  JLM_ASSERT(is<VariadicArgumentListOperation>(node.GetOperation()));
1098 
1099  // Members of the valist are extracted using the va_arg macro, which loads from the va_list struct
1100  // on the stack. This struct will be marked as escaped from the call to va_start, and thus point
1101  // to external. All we need to do is mark all pointees of pointer varargs as escaping. When the
1102  // pointers are re-created inside the function, they will be marked as pointing to external.
1103 
1104  for (size_t i = 0; i < node.ninputs(); i++)
1105  {
1106  if (!IsOrContainsPointerType(*node.input(i)->Type()))
1107  continue;
1108 
1109  const auto & inputRegister = *node.input(i)->origin();
1110  const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1111  Constraints_->AddRegisterContentEscapedConstraint(inputRegisterPO);
1112  }
1113 }
1114 
1115 void
1117 {
1118  JLM_ASSERT(is<PointerToFunctionOperation>(node.GetOperation()));
1119 
1120  // For pointer analysis purposes, function objects and pointers
1121  // to functions are treated as being the same.
1122  const auto & baseRegister = *node.input(0)->origin();
1123  JLM_ASSERT(is<PointerType>(baseRegister.Type()));
1124 
1125  const auto baseRegisterPO = Set_->GetRegisterPointerObject(baseRegister);
1126  const auto & outputRegister = *node.output(0);
1127  Set_->MapRegisterToExistingPointerObject(outputRegister, baseRegisterPO);
1128 }
1129 
1130 void
1132 {
1133  JLM_ASSERT(is<FunctionToPointerOperation>(node.GetOperation()));
1134 
1135  // For pointer analysis purposes, function objects and pointers
1136  // to functions are treated as being the same.
1137  const auto & baseRegister = *node.input(0)->origin();
1138  JLM_ASSERT(is<rvsdg::FunctionType>(baseRegister.Type()));
1139 
1140  const auto baseRegisterPO = Set_->GetRegisterPointerObject(baseRegister);
1141  const auto & outputRegister = *node.output(0);
1142  Set_->MapRegisterToExistingPointerObject(outputRegister, baseRegisterPO);
1143 }
1144 
1145 void
1147 {
1148  JLM_ASSERT(is<IOBarrierOperation>(node.GetOperation()));
1149 
1150  const auto operation = util::assertedCast<const IOBarrierOperation>(&node.GetOperation());
1151  if (!IsOrContainsPointerType(*operation->Type()))
1152  return;
1153 
1154  const auto & inputRegister = *node.input(0)->origin();
1155  const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1156  const auto & outputRegister = *node.output(0);
1157  Set_->MapRegisterToExistingPointerObject(outputRegister, inputRegisterPO);
1158 }
1159 
1160 void
1162 {
1164  node,
1165  [this](const rvsdg::LambdaNode & lambdaNode)
1166  {
1167  AnalyzeLambda(lambdaNode);
1168  },
1169  [this](const rvsdg::DeltaNode & deltaNode)
1170  {
1171  AnalyzeDelta(deltaNode);
1172  },
1173  [this](const rvsdg::PhiNode & phiNode)
1174  {
1175  AnalyzePhi(phiNode);
1176  },
1177  [this](const rvsdg::GammaNode & gammaNode)
1178  {
1179  AnalyzeGamma(gammaNode);
1180  },
1181  [this](const rvsdg::ThetaNode & thetaNode)
1182  {
1183  AnalyzeTheta(thetaNode);
1184  });
1185 }
1186 
1187 void
1189 {
1190  // Handle context variables
1191  for (const auto & cv : lambda.GetContextVars())
1192  {
1193  if (!IsOrContainsPointerType(*cv.input->Type()))
1194  continue;
1195 
1196  auto & inputRegister = *cv.input->origin();
1197  auto & argumentRegister = *cv.inner;
1198  const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1199  Set_->MapRegisterToExistingPointerObject(argumentRegister, inputRegisterPO);
1200  }
1201 
1202  // Create Register PointerObjects for each argument of pointing type in the function
1203  for (auto argument : lambda.GetFunctionArguments())
1204  {
1205  if (IsOrContainsPointerType(*argument->Type()))
1206  (void)Set_->CreateRegisterPointerObject(*argument);
1207  }
1208 
1209  AnalyzeRegion(*lambda.subregion());
1210 
1211  // Create a lambda PointerObject for the lambda itself
1212  const auto lambdaPO = Set_->CreateFunctionMemoryObject(lambda);
1213 
1214  // Make the lambda node's output point to the lambda PointerObject
1215  const auto & lambdaOutput = *lambda.output();
1216  const auto lambdaOutputPO = Set_->CreateRegisterPointerObject(lambdaOutput);
1217  Constraints_->AddPointerPointeeConstraint(lambdaOutputPO, lambdaPO);
1218 }
1219 
1220 void
1222 {
1223  // Handle context variables
1224  for (auto & cv : delta.GetContextVars())
1225  {
1226  if (!IsOrContainsPointerType(*cv.input->Type()))
1227  continue;
1228 
1229  auto & inputRegister = *cv.input->origin();
1230  auto & argumentRegister = *cv.inner;
1231  const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1232  Set_->MapRegisterToExistingPointerObject(argumentRegister, inputRegisterPO);
1233  }
1234 
1235  AnalyzeRegion(*delta.subregion());
1236 
1237  // Get the result register from the subregion
1238  auto & resultRegister = *delta.result().origin();
1239 
1240  // If the type of the delta can point, the analysis should track its set of possible pointees
1241  bool canPoint = IsOrContainsPointerType(*delta.Type());
1242 
1243  // Create a global memory object representing the global variable
1244  const auto globalPO = Set_->CreateGlobalMemoryObject(delta, canPoint);
1245 
1246  // If the initializer subregion result is a pointer, make the global point to what it points to
1247  if (canPoint)
1248  {
1249  const auto resultRegisterPO = Set_->GetRegisterPointerObject(resultRegister);
1250  Constraints_->AddConstraint(SupersetConstraint(globalPO, resultRegisterPO));
1251  }
1252 
1253  // Finally create a Register PointerObject for the delta's output, pointing to the memory object
1254  auto & outputRegister = delta.output();
1255  const auto outputRegisterPO = Set_->CreateRegisterPointerObject(outputRegister);
1256  Constraints_->AddPointerPointeeConstraint(outputRegisterPO, globalPO);
1257 }
1258 
1259 void
1261 {
1262  // Handle context variables
1263  for (auto var : phi.GetContextVars())
1264  {
1265  if (!IsOrContainsPointerType(*var.inner->Type()))
1266  continue;
1267 
1268  auto & inputRegister = *var.input->origin();
1269  auto & argumentRegister = *var.inner;
1270  const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1271  Set_->MapRegisterToExistingPointerObject(argumentRegister, inputRegisterPO);
1272  }
1273 
1274  // Create Register PointerObjects for each fixpoint variable argument
1275  for (auto var : phi.GetFixVars())
1276  {
1277  if (!IsOrContainsPointerType(*var.output->Type()))
1278  continue;
1279 
1280  auto & argumentRegister = *var.recref;
1281  (void)Set_->CreateRegisterPointerObject(argumentRegister);
1282  }
1283 
1284  AnalyzeRegion(*phi.subregion());
1285 
1286  // Handle recursive definition results
1287  for (auto var : phi.GetFixVars())
1288  {
1289  if (!IsOrContainsPointerType(*var.output->Type()))
1290  continue;
1291 
1292  // Make the recursion variable argument point to what the result register points to
1293  auto & argumentRegister = *var.recref;
1294  auto & resultRegister = *var.result->origin();
1295  const auto argumentRegisterPO = Set_->GetRegisterPointerObject(argumentRegister);
1296  const auto resultRegisterPO = Set_->GetRegisterPointerObject(resultRegister);
1297  Constraints_->AddConstraint(SupersetConstraint(argumentRegisterPO, resultRegisterPO));
1298 
1299  // Map the output register to the recursion result's pointer object
1300  auto & outputRegister = *var.output;
1301  Set_->MapRegisterToExistingPointerObject(outputRegister, resultRegisterPO);
1302  }
1303 }
1304 
1305 void
1307 {
1308  // Handle input variables
1309  for (const auto & ev : gamma.GetEntryVars())
1310  {
1311  if (!IsOrContainsPointerType(*ev.input->Type()))
1312  continue;
1313 
1314  auto & inputRegister = *ev.input->origin();
1315  const auto inputRegisterPO = Set_->GetRegisterPointerObject(inputRegister);
1316 
1317  for (auto & argument : ev.branchArgument)
1318  Set_->MapRegisterToExistingPointerObject(*argument, inputRegisterPO);
1319  }
1320 
1321  // Handle subregions
1322  for (size_t n = 0; n < gamma.nsubregions(); n++)
1323  AnalyzeRegion(*gamma.subregion(n));
1324 
1325  // Handle exit variables
1326  for (const auto & ex : gamma.GetExitVars())
1327  {
1328  if (!IsOrContainsPointerType(*ex.output->Type()))
1329  continue;
1330 
1331  auto & outputRegister = ex.output;
1332  const auto outputRegisterPO = Set_->CreateRegisterPointerObject(*outputRegister);
1333 
1334  for (auto result : ex.branchResult)
1335  {
1336  const auto resultRegisterPO = Set_->GetRegisterPointerObject(*result->origin());
1337  Constraints_->AddConstraint(SupersetConstraint(outputRegisterPO, resultRegisterPO));
1338  }
1339  }
1340 }
1341 
1342 void
1344 {
1345  // Create a PointerObject for each argument in the inner region
1346  // And make it point to a superset of the corresponding input register
1347  for (const auto & loopVar : theta.GetLoopVars())
1348  {
1349  if (!IsOrContainsPointerType(*loopVar.input->Type()))
1350  continue;
1351 
1352  auto & inputReg = *loopVar.input->origin();
1353  auto & innerArgumentReg = *loopVar.pre;
1354  const auto inputRegPO = Set_->GetRegisterPointerObject(inputReg);
1355  const auto innerArgumentRegPO = Set_->CreateRegisterPointerObject(innerArgumentReg);
1356 
1357  // The inner argument can point to anything the input did
1358  Constraints_->AddConstraint(SupersetConstraint(innerArgumentRegPO, inputRegPO));
1359  }
1360 
1361  AnalyzeRegion(*theta.subregion());
1362 
1363  // Iterate over loop variables again, making the inner arguments point to a superset
1364  // of what the corresponding result registers point to
1365  for (const auto & loopVar : theta.GetLoopVars())
1366  {
1367  if (!IsOrContainsPointerType(*loopVar.input->Type()))
1368  continue;
1369 
1370  auto & innerArgumentReg = *loopVar.pre;
1371  auto & innerResultReg = *loopVar.post->origin();
1372  auto & outputReg = *loopVar.output;
1373 
1374  const auto innerArgumentRegPO = Set_->GetRegisterPointerObject(innerArgumentReg);
1375  const auto innerResultRegPO = Set_->GetRegisterPointerObject(innerResultReg);
1376 
1377  // The inner argument can point to anything the result of last iteration did
1378  Constraints_->AddConstraint(SupersetConstraint(innerArgumentRegPO, innerResultRegPO));
1379 
1380  // Due to theta nodes running at least once, the output always comes from the inner results
1381  Set_->MapRegisterToExistingPointerObject(outputReg, innerResultRegPO);
1382  }
1383 }
1384 
1385 void
1387 {
1388  // Check that all region arguments of pointing types have PointerObjects
1389  for (size_t i = 0; i < region.narguments(); i++)
1390  {
1391  if (IsOrContainsPointerType(*region.argument(i)->Type()))
1392  JLM_ASSERT(Set_->GetRegisterMap().count(region.argument(i)));
1393  }
1394 
1395  // The use of the top-down traverser is vital, as it ensures all input origins
1396  // of pointer type are mapped to PointerObjects by the time a node is processed.
1397  rvsdg::TopDownTraverser traverser(&region);
1398 
1399  // While visiting the node we have the responsibility of creating
1400  // PointerObjects for any of the node's outputs of pointer type
1401  for (const auto node : traverser)
1402  {
1403  if (auto simpleNode = dynamic_cast<const rvsdg::SimpleNode *>(node))
1404  AnalyzeSimpleNode(*simpleNode);
1405  else if (auto structuralNode = dynamic_cast<const rvsdg::StructuralNode *>(node))
1406  AnalyzeStructuralNode(*structuralNode);
1407  else
1408  JLM_UNREACHABLE("Unknown node type");
1409 
1410  // Check that all outputs with pointing types have PointerObjects created
1411  for (size_t i = 0; i < node->noutputs(); i++)
1412  {
1413  if (IsOrContainsPointerType(*node->output(i)->Type()))
1414  JLM_ASSERT(Set_->GetRegisterMap().count(node->output(i)));
1415  }
1416  }
1417 }
1418 
1419 void
1421 {
1422  auto & rootRegion = graph.GetRootRegion();
1423 
1424  // Iterate over all arguments to the root region - symbols imported from other modules
1425  // These symbols can either be global variables or functions
1426  for (size_t n = 0; n < rootRegion.narguments(); n++)
1427  {
1428  auto & argument = *util::assertedCast<LlvmGraphImport>(rootRegion.argument(n));
1429 
1430  // Only care about imported pointer values
1431  if (!IsOrContainsPointerType(*argument.Type()))
1432  continue;
1433 
1434  // Imported symbols are always externally available, so all pointees can be implicit.
1435  // The value of CanPoint thus has no effect on the solver, only the final PointsToGraph node.
1436  const bool canPoint = IsOrContainsPointerType(*argument.ValueType());
1437 
1438  // Create a memory PointerObject representing the target of the external symbol
1439  // We can assume that two external symbols don't alias. This is the assumption clang makes.
1440  const auto importObjectPO = Set_->CreateImportMemoryObject(argument, canPoint);
1441 
1442  // Create a register PointerObject representing the address value itself
1443  const auto importRegisterPO = Set_->CreateRegisterPointerObject(argument);
1444  Constraints_->AddPointerPointeeConstraint(importRegisterPO, importObjectPO);
1445  }
1446 
1447  AnalyzeRegion(rootRegion);
1448 
1449  // Mark all results escaping the root module as escaped
1450  for (size_t n = 0; n < rootRegion.nresults(); n++)
1451  {
1452  auto & escapedRegister = *rootRegion.result(n)->origin();
1453  if (!IsOrContainsPointerType(*escapedRegister.Type()))
1454  continue;
1455 
1456  const auto escapedRegisterPO = Set_->GetRegisterPointerObject(escapedRegister);
1457  Constraints_->AddRegisterContentEscapedConstraint(escapedRegisterPO);
1458  }
1459 }
1460 
1461 void
1463 {
1464  Config_ = std::move(config);
1465 }
1466 
1469 {
1470  return Config_;
1471 }
1472 
1473 void
1475 {
1476  Set_ = std::make_unique<PointerObjectSet>();
1477  Constraints_ = std::make_unique<PointerObjectConstraintSet>(*Set_);
1478 
1480  AnalyzeRvsdg(module.Rvsdg());
1482 }
1483 
1484 void
1486  PointerObjectConstraintSet & constraints,
1487  const Configuration & config,
1488  Statistics & statistics)
1489 {
1490  statistics.AddStatisticFromConfiguration(config);
1491 
1493  {
1494  statistics.StartOfflineVariableSubstitution();
1495  // If the solver uses hybrid cycle detection, tell OVS to store info about ref node cycles
1496  bool hasHCD = config.IsHybridCycleDetectionEnabled();
1497  auto numUnifications = constraints.PerformOfflineVariableSubstitution(hasHCD);
1498  statistics.StopOfflineVariableSubstitution(numUnifications);
1499  }
1500 
1502  {
1504  auto numConstraintsRemoved = constraints.NormalizeConstraints();
1505  statistics.StopOfflineConstraintNormalization(numConstraintsRemoved);
1506  }
1507 
1508  if (config.GetSolver() == Configuration::Solver::Naive)
1509  {
1511  size_t numIterations = constraints.SolveNaively();
1512  statistics.StopConstraintSolvingNaiveStatistics(numIterations);
1513  }
1514  else if (config.GetSolver() == Configuration::Solver::Worklist)
1515  {
1517  auto worklistStatistics = constraints.SolveUsingWorklist(
1518  config.GetWorklistSoliverPolicy(),
1521  config.IsLazyCycleDetectionEnabled(),
1524  statistics.StopConstraintSolvingWorklistStatistics(worklistStatistics);
1525  }
1526  else
1527  JLM_UNREACHABLE("Unknown solver");
1528 }
1529 
1530 std::unique_ptr<PointsToGraph>
1532  const rvsdg::RvsdgModule & module,
1534 {
1535  auto statistics = Statistics::Create(module.SourceFilePath().value());
1536  statistics->StartAndersenStatistics(module.Rvsdg());
1537 
1538  // Check environment variables for debugging flags
1539  size_t testAllConfigsIterations = 0;
1540  if (auto testAllConfigsString = std::getenv(ENV_TEST_ALL_CONFIGS))
1541  testAllConfigsIterations = std::stoi(testAllConfigsString);
1542  std::optional<size_t> useExactConfig;
1543  if (auto useExactConfigString = std::getenv(ENV_USE_EXACT_CONFIG))
1544  useExactConfig = std::stoi(useExactConfigString);
1545  const bool doubleCheck = std::getenv(ENV_DOUBLE_CHECK);
1546 
1547  const bool dumpGraphs = std::getenv(ENV_DUMP_SUBSET_GRAPH);
1548  util::graph::Writer writer;
1549 
1550  AnalyzeModule(module, *statistics);
1551 
1552  // If solving multiple times, make a copy of the original constraint set
1553  std::pair<std::unique_ptr<PointerObjectSet>, std::unique_ptr<PointerObjectConstraintSet>> copy;
1554  if (testAllConfigsIterations || doubleCheck)
1555  copy = Constraints_->Clone();
1556 
1557  // Draw subset graph both before and after solving
1558  if (dumpGraphs)
1559  Constraints_->DrawSubsetGraph(writer);
1560 
1561  auto config = Config_;
1562  if (useExactConfig.has_value())
1563  {
1564  auto allConfigs = Configuration::GetAllConfigurations();
1565  config = allConfigs.at(*useExactConfig);
1566  }
1567 
1568  SolveConstraints(*Constraints_, config, *statistics);
1569  statistics->AddStatisticsFromSolution(*Set_);
1570 
1571  if (dumpGraphs)
1572  {
1573  auto & graph = Constraints_->DrawSubsetGraph(writer);
1574  graph.AppendToLabel("After Solving with " + config.ToString());
1576  }
1577 
1578  auto result = ConstructPointsToGraphFromPointerObjectSet(*Set_, *statistics);
1579 
1580  statistics->StopAndersenStatistics();
1581  statisticsCollector.CollectDemandedStatistics(std::move(statistics));
1582 
1583  // Solve again if double-checking against naive is enabled
1584  if (testAllConfigsIterations || doubleCheck)
1585  {
1586  if (doubleCheck)
1587  std::cerr << "Double checking Andersen analysis using naive solving" << std::endl;
1588 
1589  // If double-checking, only use the naive configuration. Otherwise, try all configurations
1590  std::vector<Configuration> configs;
1591  if (testAllConfigsIterations)
1593  else
1594  configs.push_back(Configuration::NaiveSolverConfiguration());
1595 
1596  // If testing all configurations, do it as many times as requested.
1597  // Otherwise, do it at least once
1598  const auto iterations = std::max<size_t>(testAllConfigsIterations, 1);
1599 
1600  for (size_t i = 0; i < iterations; i++)
1601  {
1602  for (const auto & config : configs)
1603  {
1604  // Create a clone of the unsolved pointer object set and constraint set
1605  auto workingCopy = copy.second->Clone();
1606  // These statistics will only contain solving data
1607  auto solvingStats = Statistics::Create(module.SourceFilePath().value());
1608  SolveConstraints(*workingCopy.second, config, *solvingStats);
1609  solvingStats->AddStatisticsFromSolution(*workingCopy.first);
1610  statisticsCollector.CollectDemandedStatistics(std::move(solvingStats));
1611 
1612  // Only double check on the first iteration
1613  if (doubleCheck && i == 0)
1614  {
1615  if (workingCopy.first->HasIdenticalSolAs(*Set_))
1616  continue;
1617  std::cerr << "Solving with original config: " << Config_.ToString()
1618  << " did not produce the same solution as the config " << config.ToString()
1619  << std::endl;
1620  JLM_UNREACHABLE("Andersen solver double checking uncovered differences!");
1621  }
1622  }
1623  }
1624  }
1625 
1626  // Cleanup
1627  Constraints_.reset();
1628  Set_.reset();
1629  return result;
1630 }
1631 
1632 std::unique_ptr<PointsToGraph>
1634 {
1636  return Analyze(module, statisticsCollector);
1637 }
1638 
1639 std::unique_ptr<PointsToGraph>
1641  const PointerObjectSet & set,
1642  Statistics & statistics)
1643 {
1645 
1646  auto pointsToGraph = PointsToGraph::create();
1647 
1648  // Mapping from index in PointerObjectSet to the equivalent memory node in the PointsToGraph
1649  std::unordered_map<PointerObjectIndex, PointsToGraph::NodeIndex> memoryNodeMapping;
1650 
1651  for (auto [allocaNode, pointerObjectIndex] : set.GetAllocaMap())
1652  {
1653  memoryNodeMapping[pointerObjectIndex] =
1654  pointsToGraph->addNodeForAlloca(*allocaNode, set.HasEscaped(pointerObjectIndex));
1655  }
1656 
1657  for (auto [deltaNode, pointerObjectIndex] : set.GetGlobalMap())
1658  {
1659  memoryNodeMapping[pointerObjectIndex] =
1660  pointsToGraph->addNodeForDelta(*deltaNode, set.HasEscaped(pointerObjectIndex));
1661  }
1662 
1663  for (auto [import, pointerObjectIndex] : set.GetImportMap())
1664  {
1665  memoryNodeMapping[pointerObjectIndex] =
1666  pointsToGraph->addNodeForImport(*import, set.HasEscaped(pointerObjectIndex));
1667  }
1668 
1669  for (auto [lambdaNode, pointerObjectIndex] : set.GetFunctionMap())
1670  {
1671  memoryNodeMapping[pointerObjectIndex] =
1672  pointsToGraph->addNodeForLambda(*lambdaNode, set.HasEscaped(pointerObjectIndex));
1673  }
1674 
1675  for (auto [mallocNode, pointerObjectIndex] : set.GetMallocMap())
1676  {
1677  memoryNodeMapping[pointerObjectIndex] =
1678  pointsToGraph->addNodeForMalloc(*mallocNode, set.HasEscaped(pointerObjectIndex));
1679  }
1680 
1681  // Helper function for attaching PointsToGraph nodes to their pointees, based on the
1682  // PointerObject's points-to set.
1683  auto applyPointsToSet = [&](PointsToGraph::NodeIndex ptgNode, PointerObjectIndex index)
1684  {
1685  // PointerObjectSets that are marked as not containing pointers can be added to the
1686  // PointsToGraph without any explicit or implicit pointees.
1687  if (!set.CanPoint(index))
1688  {
1689  return;
1690  }
1691 
1692  // Mark nodes that target everything that is externally available
1693  if (set.IsPointingToExternal(index))
1694  {
1695  pointsToGraph->markAsTargetsAllExternallyAvailable(ptgNode);
1696  }
1697 
1698  // Add all explicit pointees. Doubled up pointees are ignored by the PtG
1699  for (const auto targetIdx : set.GetPointsToSet(index).Items())
1700  {
1701  pointsToGraph->addTarget(ptgNode, memoryNodeMapping[targetIdx]);
1702  }
1703  };
1704 
1705  // First group RVSDG registers by the PointerObject they are mapped to.
1706  // If the PointerObject is part of a unification, all Register PointerObjects in the unification
1707  // share points-to set, so they can all become one RegisterNode in the PointsToGraph.
1708  std::unordered_map<PointerObjectIndex, util::HashSet<const rvsdg::Output *>> outputsInRegister;
1709  for (auto [outputNode, registerIdx] : set.GetRegisterMap())
1710  {
1711  auto root = set.GetUnificationRoot(registerIdx);
1712  outputsInRegister[root].insert(outputNode);
1713  }
1714 
1715  // Create PointsToGraph::RegisterNodes for each PointerObject of register kind, and add edges
1716  for (auto & [registerIdx, outputNodes] : outputsInRegister)
1717  {
1718  const auto ptgNode = pointsToGraph->addNodeForRegisters();
1719  for (auto outputNode : outputNodes.Items())
1720  pointsToGraph->mapRegisterToNode(*outputNode, ptgNode);
1721  applyPointsToSet(ptgNode, registerIdx);
1722  }
1723 
1724  // Now add all edges from memory node to memory node.
1725  for (auto [pointerObjectSetIndex, ptgNode] : memoryNodeMapping)
1726  {
1727  applyPointsToSet(ptgNode, pointerObjectSetIndex);
1728  }
1729 
1730  statistics.StopPointsToGraphConstructionStatistics(*pointsToGraph);
1731  return pointsToGraph;
1732 }
1733 
1734 std::unique_ptr<PointsToGraph>
1736 {
1737  // Create a throwaway instance of statistics
1738  Statistics statistics(util::FilePath(""));
1739  return ConstructPointsToGraphFromPointerObjectSet(set, statistics);
1740 }
1741 
1742 }
static jlm::util::StatisticsCollector statisticsCollector
Call operation class.
Definition: call.hpp:251
static rvsdg::Input & GetFunctionInput(const rvsdg::Node &node) noexcept
Definition: call.hpp:321
ConstantPointerNullOperation class.
Definition: operators.hpp:432
FreezeOperation class.
Definition: operators.hpp:1160
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:2458
static rvsdg::Input & destinationInput(const rvsdg::Node &node) noexcept
Interpret pointer as callable function.
PoisonValueOperation class.
Definition: operators.hpp:1092
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:1023
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 StopAndersenStatistics() noexcept
Definition: Andersen.cpp:608
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
static std::unique_ptr< Statistics > Create(const util::FilePath &sourceFile)
Definition: Andersen.cpp:614
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
void AddStatisticFromConfiguration(const Configuration &config)
Definition: Andersen.cpp:435
void StopConstraintSolvingWorklistStatistics(PointerObjectConstraintSet::WorklistStatistics &statistics) noexcept
Definition: Andersen.cpp:395
void AnalyzeIOBarrier(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:1146
const Configuration & GetConfiguration() const
Definition: Andersen.cpp:1468
static void SolveConstraints(PointerObjectConstraintSet &constraints, const Configuration &config, Statistics &statistics)
Definition: Andersen.cpp:1485
void AnalyzePoison(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:918
void AnalyzeConstantPointerNull(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:892
void SetConfiguration(Configuration config)
Definition: Andersen.cpp:1462
void AnalyzeValist(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:1095
void AnalyzePointerToFunction(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:1116
void AnalyzeStore(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:789
static const char *const ENV_USE_EXACT_CONFIG
Definition: Andersen.hpp:41
Configuration Config_
Definition: Andersen.hpp:466
std::unique_ptr< PointerObjectSet > Set_
Definition: Andersen.hpp:468
void AnalyzeUndef(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:904
static std::unique_ptr< PointsToGraph > ConstructPointsToGraphFromPointerObjectSet(const PointerObjectSet &set, Statistics &statistics)
Definition: Andersen.cpp:1640
static const char *const ENV_DOUBLE_CHECK
Definition: Andersen.hpp:48
void AnalyzeRegion(rvsdg::Region &region)
Definition: Andersen.cpp:1386
void AnalyzeGep(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:832
~Andersen() noexcept override
void AnalyzeRvsdg(const rvsdg::Graph &graph)
Definition: Andersen.cpp:1420
void AnalyzeAlloca(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:742
void AnalyzeModule(const rvsdg::RvsdgModule &module, Statistics &statistics)
Definition: Andersen.cpp:1474
void AnalyzePtrToInt(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:880
void AnalyzeMemset(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:971
std::unique_ptr< PointsToGraph > Analyze(const rvsdg::RvsdgModule &module, util::StatisticsCollector &statisticsCollector) override
Definition: Andersen.cpp:1531
void AnalyzeExtractValue(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:1080
void AnalyzeConstantArray(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:984
void AnalyzeInsertValue(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:1043
void AnalyzeConstantAggregateZero(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:1029
void AnalyzeBitcast(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:847
void AnalyzePhi(const rvsdg::PhiNode &node)
Definition: Andersen.cpp:1260
static const char *const ENV_DUMP_SUBSET_GRAPH
Definition: Andersen.hpp:53
void AnalyzeMemcpy(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:949
void AnalyzeCall(const rvsdg::SimpleNode &callNode)
Definition: Andersen.cpp:809
void AnalyzeConstantStruct(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:1006
std::unique_ptr< PointerObjectConstraintSet > Constraints_
Definition: Andersen.hpp:469
void AnalyzeTheta(const rvsdg::ThetaNode &node)
Definition: Andersen.cpp:1343
void AnalyzeFreeze(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:932
void AnalyzeMalloc(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:755
void AnalyzeLambda(const rvsdg::LambdaNode &node)
Definition: Andersen.cpp:1188
void AnalyzeBits2ptr(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:866
void AnalyzeLoad(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:768
void AnalyzeSimpleNode(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:625
void AnalyzeFunctionToPointer(const rvsdg::SimpleNode &node)
Definition: Andersen.cpp:1131
void AnalyzeGamma(const rvsdg::GammaNode &node)
Definition: Andersen.cpp:1306
void AnalyzeStructuralNode(const rvsdg::StructuralNode &node)
Definition: Andersen.cpp:1161
static const char *const ENV_TEST_ALL_CONFIGS
Definition: Andersen.hpp:34
void AnalyzeDelta(const rvsdg::DeltaNode &node)
Definition: Andersen.cpp:1221
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: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
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:574
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:30
uint32_t PointerObjectIndex
bool IsAggregateType(const jlm::rvsdg::Type &type)
Definition: types.hpp:531
void MatchTypeWithDefault(T &obj, const Fns &... fns)
Pattern match over subclass type of given object with default handler.
void MatchTypeOrFail(T &obj, const Fns &... fns)
Pattern match over subclass type of given object.
size_t nnodes(const jlm::rvsdg::Region *region) noexcept
Definition: region.cpp:785