Jlm
RegionAwareModRefSummarizer.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2022 Nico Reißmann <nico.reissmann@gmail.com>
3  * Copyright 2025 Håvard Krogstie <krogstie.havard@gmail.com>
4  * See COPYING for terms of redistribution.
5  */
6 
12 #include <jlm/llvm/ir/Trace.hpp>
17 #include <jlm/rvsdg/MatchType.hpp>
18 #include <jlm/rvsdg/traverser.hpp>
19 #include <jlm/util/Statistics.hpp>
20 #include <jlm/util/TarjanScc.hpp>
21 #include <jlm/util/Worklist.hpp>
22 
23 #include <queue>
24 
25 namespace jlm::llvm::aa
26 {
27 
32 static const bool ENABLE_DEAD_ALLOCA_BLOCKLIST = !std::getenv("JLM_DISABLE_DEAD_ALLOCA_BLOCKLIST");
33 
40  !std::getenv("JLM_DISABLE_NON_REENTRANT_ALLOCA_BLOCKLIST");
41 
47 static const bool ENABLE_OPERATION_SIZE_BLOCKING =
48  !std::getenv("JLM_DISABLE_OPERATION_SIZE_BLOCKING");
49 
55  !std::getenv("JLM_DISABLE_CONSTANT_MEMORY_BLOCKING");
56 
64 {
65  static constexpr auto NumRvsdgRegionsLabel_ = "#RvsdgRegions";
66  static constexpr auto NumSimpleAllocas_ = "#SimpleAllocas";
67  static constexpr auto NumNonReentrantAllocas_ = "#NonReentrantAllocas";
68  static constexpr auto NumCallGraphSccs_ = "#CallGraphSccs";
69  static constexpr auto NumFunctionsCallingSetjmp_ = "#FunctionsCallingSetjmp";
70  static constexpr auto NumCallGraphSccsCanCallExternal_ = "#CallGraphSccsCanCallExternal";
71 
72  static constexpr auto CallGraphTimer_ = "CallGraphTimer";
73  static constexpr auto AllocasDeadInSccsTimer_ = "AllocasDeadInSccsTimer";
74  static constexpr auto SimpleAllocasSetTimer_ = "SimpleAllocasSetTimer";
75  static constexpr auto NonReentrantAllocaSetsTimer_ = "NonReentrantAllocaSetsTimer";
76  static constexpr auto CreateExternalModRefSetTimer_ = "CreateExternalModRefSetTimer";
77  static constexpr auto AnnotationTimer_ = "AnnotationTimer";
78  static constexpr auto SolvingTimer_ = "SolvingTimer";
79 
80 public:
81  ~Statistics() override = default;
82 
83  explicit Statistics(const rvsdg::RvsdgModule & rvsdgModule, const PointsToGraph & pointsToGraph)
84  : util::Statistics(Id::RegionAwareModRefSummarizer, rvsdgModule.SourceFilePath().value())
85  {
91  }
92 
93  void
95  {
97  }
98 
99  void
100  stopCallGraphStatistics(size_t numSccs, size_t numFunctionsCallingSetjmp)
101  {
104  AddMeasurement(NumFunctionsCallingSetjmp_, numFunctionsCallingSetjmp);
105  }
106 
107  void
109  {
111  }
112 
113  void
115  {
117  }
118 
119  void
121  {
123  }
124 
125  void
126  StopCreateSimpleAllocasSetStatistics(uint64_t numSimpleAllocas)
127  {
129  AddMeasurement(NumSimpleAllocas_, numSimpleAllocas);
130  }
131 
132  void
134  {
136  }
137 
138  void
139  StopCreateNonReentrantAllocaSetsStatistics(size_t numNonReentrantAllocas)
140  {
141  AddMeasurement(NumNonReentrantAllocas_, numNonReentrantAllocas);
143  }
144 
145  void
147  {
149  }
150 
151  void
153  {
155  }
156 
157  void
159  {
161  }
162 
163  void
165  {
167  }
168 
169  void
171  {
173  }
174 
175  void
177  {
179  }
180 
181  static std::unique_ptr<Statistics>
182  Create(const rvsdg::RvsdgModule & rvsdgModule, const PointsToGraph & pointsToGraph)
183  {
184  return std::make_unique<Statistics>(rvsdgModule, pointsToGraph);
185  }
186 };
187 
192 class ModRefSet final
193 {
194 public:
195  ModRefSet() = default;
196 
199  {
200  return MemoryNodes_;
201  }
202 
203  [[nodiscard]] const util::HashSet<PointsToGraph::NodeIndex> &
205  {
206  return MemoryNodes_;
207  }
208 
209 private:
211 };
212 
216 {
217 public:
218  explicit RegionAwareModRefSummary(const PointsToGraph & pointsToGraph)
219  : PointsToGraph_(pointsToGraph)
220  {}
221 
225 
226  [[nodiscard]] const PointsToGraph &
227  GetPointsToGraph() const noexcept override
228  {
229  return PointsToGraph_;
230  }
231 
232  [[nodiscard]] size_t
233  NumModRefSets() const noexcept
234  {
235  return ModRefSets_.size();
236  }
237 
238  [[nodiscard]] const util::HashSet<PointsToGraph::NodeIndex> &
240  {
241  JLM_ASSERT(index < ModRefSets_.size());
242  return ModRefSets_[index].GetMemoryNodes();
243  }
244 
245  bool
247  {
248  JLM_ASSERT(index < ModRefSets_.size());
249  return ModRefSets_[index].GetMemoryNodes().insert(ptgNode);
250  }
251 
252  bool
254  {
255  JLM_ASSERT(from < ModRefSets_.size());
256  JLM_ASSERT(to < ModRefSets_.size());
257  return ModRefSets_[to].GetMemoryNodes().UnionWith(ModRefSets_[from].GetMemoryNodes());
258  }
259 
264  [[nodiscard]] ModRefSetIndex
266  {
267  ModRefSets_.emplace_back();
268  return ModRefSets_.size() - 1;
269  }
270 
271  [[nodiscard]] bool
272  HasSetForNode(const rvsdg::Node & node) const
273  {
274  return NodeMap_.find(&node) != NodeMap_.end();
275  }
276 
277  [[nodiscard]] ModRefSetIndex
278  GetSetForNode(const rvsdg::Node & node) const
279  {
280  const auto it = NodeMap_.find(&node);
281  JLM_ASSERT(it != NodeMap_.end());
282  return it->second;
283  }
284 
285  [[nodiscard]] ModRefSetIndex
287  {
288  if (const auto it = NodeMap_.find(&node); it != NodeMap_.end())
289  return it->second;
290 
291  return NodeMap_[&node] = CreateModRefSet();
292  }
293 
294  void
296  {
297  JLM_ASSERT(!HasSetForNode(node));
298  NodeMap_[&node] = index;
299  }
300 
302  GetSimpleNodeModRef(const rvsdg::SimpleNode & node) const override
303  {
304  return ModRefSets_[GetSetForNode(node)].GetMemoryNodes();
305  }
306 
308  GetGammaEntryModRef(const rvsdg::GammaNode & gamma) const override
309  {
310  return ModRefSets_[GetSetForNode(gamma)].GetMemoryNodes();
311  }
312 
314  GetGammaExitModRef(const rvsdg::GammaNode & gamma) const override
315  {
316  return GetGammaEntryModRef(gamma);
317  }
318 
320  GetThetaModRef(const rvsdg::ThetaNode & theta) const override
321  {
322  return ModRefSets_[GetSetForNode(theta)].GetMemoryNodes();
323  }
324 
326  GetLambdaEntryModRef(const rvsdg::LambdaNode & lambda) const override
327  {
328  return ModRefSets_[GetSetForNode(lambda)].GetMemoryNodes();
329  }
330 
332  GetLambdaExitModRef(const rvsdg::LambdaNode & lambda) const override
333  {
334  return GetLambdaEntryModRef(lambda);
335  }
336 
337  [[nodiscard]] static std::unique_ptr<RegionAwareModRefSummary>
338  Create(const PointsToGraph & pointsToGraph)
339  {
340  return std::make_unique<RegionAwareModRefSummary>(pointsToGraph);
341  }
342 
343 private:
345 
349  std::vector<ModRefSet> ModRefSets_;
350 
356  std::unordered_map<const rvsdg::Node *, ModRefSetIndex> NodeMap_;
357 };
358 
363 {
364  explicit Context(const PointsToGraph & ptg)
365  : pointsToGraph(ptg)
366  {}
367 
372 
383  std::vector<util::HashSet<const rvsdg::LambdaNode *>> SccFunctions;
384 
391 
399  std::vector<util::HashSet<size_t>> SccCallTargets;
400 
406  std::unordered_map<const rvsdg::LambdaNode *, size_t> FunctionToSccIndex;
407 
413 
420  std::vector<util::HashSet<PointsToGraph::NodeIndex>> AllocasDeadInScc;
421 
428 
435  std::unordered_map<const rvsdg::Region *, util::HashSet<PointsToGraph::NodeIndex>>
437 
444 
450  std::vector<util::HashSet<ModRefSetIndex>> ModRefSetSimpleConstraints;
451 
458  std::unordered_map<ModRefSetIndex, const util::HashSet<PointsToGraph::NodeIndex> *>
460 };
461 
463 
465 
466 std::unique_ptr<ModRefSummary>
467 RegionAwareModRefSummarizer::SummarizeModRefs(
468  const rvsdg::RvsdgModule & rvsdgModule,
469  const PointsToGraph & pointsToGraph,
470  util::StatisticsCollector & statisticsCollector)
471 {
472  ModRefSummary_ = RegionAwareModRefSummary::Create(pointsToGraph);
473  Context_ = std::make_unique<Context>(pointsToGraph);
474  auto statistics = Statistics::Create(rvsdgModule, pointsToGraph);
475 
476  statistics->startCallGraphStatistics();
477  createCallGraph(rvsdgModule);
478  statistics->stopCallGraphStatistics(
479  Context_->SccFunctions.size(),
480  Context_->FunctionsCallingSetjmp.Size());
481 
482  statistics->StartAllocasDeadInSccStatistics();
483  FindAllocasDeadInSccs();
484  statistics->StopAllocasDeadInSccStatistics();
485 
486  statistics->StartCreateSimpleAllocasSetStatistics();
487  Context_->SimpleAllocas = CreateSimpleAllocaSet(pointsToGraph);
488  statistics->StopCreateSimpleAllocasSetStatistics(Context_->SimpleAllocas.Size());
489 
490  statistics->StartCreateNonReentrantAllocaSetsStatistics();
491  auto numNonReentrantAllocas = CreateNonReentrantAllocaSets();
492  statistics->StopCreateNonReentrantAllocaSetsStatistics(numNonReentrantAllocas);
493 
494  statistics->StartCreateExternalModRefSet();
495  CreateExternalModRefSet();
496  statistics->StopCreateExternalModRefSet();
497 
498  statistics->StartAnnotationStatistics();
499  // Go through and recursively annotate all functions, regions and nodes
500  for (const auto & scc : Context_->SccFunctions)
501  {
502  for (const auto lambda : scc.Items())
503  {
504  AnnotateFunction(*lambda);
505  }
506  }
507  statistics->StopAnnotationStatistics();
508 
509  statistics->StartSolvingStatistics();
510  SolveModRefSetConstraintGraph();
511  statistics->StopSolvingStatistics();
512 
513  // Print debug output
514  // std::cerr << PointsToGraph::dumpDot(pointsToGraph) << std::endl;
515  // std::cerr << "numSimpleAllocas: " << Context_->SimpleAllocas.Size() << std::endl;
516  // std::cerr << "numNonReentrantAllocas: " << numNonReentrantAllocas << std::endl;
517  // std::cerr << "Call Graph SCCs:" << std::endl << CallGraphSCCsToString(*this) << std::endl;
518  // std::cerr << "RegionTree:" << std::endl
519  // << ToRegionTree(rvsdgModule.Rvsdg(), *ModRefSummary_) << std::endl;
520 
521  statisticsCollector.CollectDemandedStatistics(std::move(statistics));
522  Context_.reset();
523  return std::move(ModRefSummary_);
524 }
525 
531 static std::vector<const rvsdg::LambdaNode *>
533 {
534  std::vector<const rvsdg::LambdaNode *> result;
535 
536  // Recursively traverses all structural nodes, but does not enter into lambdas
537  const std::function<void(rvsdg::Region &)> CollectLambdasInRegion =
538  [&](rvsdg::Region & region) -> void
539  {
540  for (auto & node : region.Nodes())
541  {
542  if (auto lambda = dynamic_cast<rvsdg::LambdaNode *>(&node))
543  {
544  result.push_back(lambda);
545  }
546  else if (auto structural = dynamic_cast<rvsdg::StructuralNode *>(&node))
547  {
548  for (size_t i = 0; i < structural->nsubregions(); i++)
549  {
550  CollectLambdasInRegion(*structural->subregion(i));
551  }
552  }
553  }
554  };
555 
556  CollectLambdasInRegion(rvsdgModule.Rvsdg().GetRootRegion());
557 
558  return result;
559 }
560 
561 void
563 {
564  const auto & pointsToGraph = Context_->pointsToGraph;
565 
566  // The list of lambdas becomes the list of nodes in the call graph
567  auto lambdaNodes = CollectLambdaNodes(rvsdgModule);
568 
569  // Mapping from LambdaNode* to its index in lambdaNodes
570  std::unordered_map<const rvsdg::LambdaNode *, size_t> callGraphNodeIndex;
571  callGraphNodeIndex.reserve(lambdaNodes.size());
572  for (size_t i = 0; i < lambdaNodes.size(); i++)
573  {
574  callGraphNodeIndex.insert({ lambdaNodes[i], i });
575  }
576 
577  // Add a dummy node representing all external functions, with no associated LambdaNode
578  const auto externalNodeIndex = lambdaNodes.size();
579  const auto numCallGraphNodes = externalNodeIndex + 1;
580 
581  // Outgoing edges for each node in the call graph, indexed by position in lambdaNodes
582  std::vector<util::HashSet<size_t>> callGraphSuccessors(numCallGraphNodes);
583 
584  // Add outgoing edges from the given caller to any function the call may target
585  const auto handleCall = [&](const rvsdg::SimpleNode & callNode, size_t callerIndex) -> void
586  {
587  const auto classification = CallOperation::ClassifyCall(callNode);
588  if (classification->isSetjmpCall())
589  {
590  Context_->FunctionsCallingSetjmp.insert(lambdaNodes[callerIndex]);
591  return;
592  }
593 
594  const auto target = callNode.input(0)->origin();
595  const auto targetPtgNode = pointsToGraph.getNodeForRegister(*target);
596 
597  // Go through all locations the called function pointer may target
598  for (const auto calleePtgNode : pointsToGraph.getExplicitTargets(targetPtgNode).Items())
599  {
600  const auto kind = pointsToGraph.getNodeKind(calleePtgNode);
602  {
603  const auto & lambdaNode = pointsToGraph.getLambdaForNode(calleePtgNode);
604 
605  // Look up which call graph node represents the target lambda
606  JLM_ASSERT(callGraphNodeIndex.find(&lambdaNode) != callGraphNodeIndex.end());
607  const auto calleeCallGraphNode = callGraphNodeIndex[&lambdaNode];
608 
609  // Add the edge caller -> callee to the call graph
610  callGraphSuccessors[callerIndex].insert(calleeCallGraphNode);
611  }
612  else if (kind == PointsToGraph::NodeKind::ImportNode)
613  {
614  // Add the edge caller -> node representing external functions
615  callGraphSuccessors[callerIndex].insert(externalNodeIndex);
616  }
617  }
618 
619  if (pointsToGraph.isTargetingAllExternallyAvailable(targetPtgNode))
620  {
621  // If the call target pointer is flagged, add an edge to external functions
622  callGraphSuccessors[callerIndex].insert(externalNodeIndex);
623  }
624  };
625 
626  // Recursive function finding all call operations, adding edges to the call graph
627  const std::function<void(const rvsdg::Region &, size_t)> handleCalls =
628  [&](const rvsdg::Region & region, size_t callerIndex) -> void
629  {
630  for (auto & node : region.Nodes())
631  {
632  if (const auto [callNode, callOp] = rvsdg::TryGetSimpleNodeAndOptionalOp<CallOperation>(node);
633  callOp)
634  {
635  handleCall(*callNode, callerIndex);
636  }
637 
639  node,
640  [&](const rvsdg::StructuralNode & structural)
641  {
642  for (auto & subregion : structural.Subregions())
643  {
644  handleCalls(subregion, callerIndex);
645  }
646  });
647  }
648  };
649 
650  // For all functions, visit all their calls and add outgoing edges in the call graph
651  for (size_t i = 0; i < lambdaNodes.size(); i++)
652  {
653  handleCalls(*lambdaNodes[i]->subregion(), i);
654 
655  // If the function has escaped, add an edge from the node representing all external functions
656  if (pointsToGraph.isExternallyAvailable(pointsToGraph.getNodeForLambda(*lambdaNodes[i])))
657  {
658  callGraphSuccessors[externalNodeIndex].insert(i);
659  }
660  }
661 
662  // Finally, add the fact that the external node may call itself
663  callGraphSuccessors[externalNodeIndex].insert(externalNodeIndex);
664 
665  // Used by the implementation of Tarjan's SCC algorithm
666  const auto getSuccessors = [&](size_t nodeIndex)
667  {
668  return callGraphSuccessors[nodeIndex].Items();
669  };
670 
671  // Find SCCs in the call graph
672  std::vector<size_t> sccIndex;
673  std::vector<size_t> reverseTopologicalOrder;
674  auto numSCCs = util::FindStronglyConnectedComponents<size_t>(
675  numCallGraphNodes,
676  getSuccessors,
677  sccIndex,
678  reverseTopologicalOrder);
679 
680  // sccIndex are distributed in a reverse topological order, so the sccIndex is used
681  // when creating the list of SCCs and the functions they contain
682  Context_->SccFunctions.resize(numSCCs);
683  for (size_t i = 0; i < lambdaNodes.size(); i++)
684  {
685  Context_->SccFunctions[sccIndex[i]].insert(lambdaNodes[i]);
686  Context_->FunctionToSccIndex[lambdaNodes[i]] = sccIndex[i];
687  }
688 
689  // Add edges between the SCCs for all calls
690  Context_->SccCallTargets.resize(numSCCs);
691  for (size_t i = 0; i < numCallGraphNodes; i++)
692  {
693  for (auto target : callGraphSuccessors[i].Items())
694  {
695  Context_->SccCallTargets[sccIndex[i]].insert(sccIndex[target]);
696  }
697  }
698 
699  // Also note which SCC contains all external functions
700  Context_->ExternalNodeSccIndex = sccIndex[externalNodeIndex];
701 }
702 
703 void
705 {
706  const auto & pointsToGraph = Context_->pointsToGraph;
707 
708  // First find which allocas may be live in each SCC
709  std::vector<util::HashSet<PointsToGraph::NodeIndex>> liveAllocas(Context_->SccFunctions.size());
710 
712 
713  // Add all Allocas to the SCC of the function they are defined in
714  for (auto allocaPtgNode : Context_->pointsToGraph.allocaNodes())
715  {
716  allAllocas.insert(allocaPtgNode);
717  const auto & allocaNode = pointsToGraph.getAllocaForNode(allocaPtgNode);
718  const auto & lambdaNode = rvsdg::getSurroundingLambdaNode(allocaNode);
719  JLM_ASSERT(Context_->FunctionToSccIndex.count(&lambdaNode));
720  const auto sccIndex = Context_->FunctionToSccIndex[&lambdaNode];
721  liveAllocas[sccIndex].insert(allocaPtgNode);
722  }
723 
724  // Propagate live allocas to targets of function calls.
725  // I.e., if a() -> b(), then any alloca that is live in a() may also be live in b()
726  // Start at the topologically earliest SCC, which has the largest SCC index
727  // The topologically latest SCC can't have any targets
728  for (size_t sccIndex = Context_->SccFunctions.size() - 1; sccIndex > 0; sccIndex--)
729  {
730  for (auto targetScc : Context_->SccCallTargets[sccIndex].Items())
731  {
732  JLM_ASSERT(targetScc <= sccIndex);
733  if (targetScc != sccIndex)
734  liveAllocas[targetScc].UnionWith(liveAllocas[sccIndex]);
735  }
736  }
737 
738  // Any alloca that is not live in the SCC gets added to the set of allocas that are dead
739  Context_->AllocasDeadInScc.resize(Context_->SccFunctions.size());
740  for (size_t sccIndex = 0; sccIndex < Context_->SccFunctions.size(); sccIndex++)
741  {
742  Context_->AllocasDeadInScc[sccIndex].UnionWith(allAllocas);
743  Context_->AllocasDeadInScc[sccIndex].DifferenceWith(liveAllocas[sccIndex]);
744  }
745 }
746 
749 {
750  // The set of allocas that are simple. Starts off as an over-approximation
752  // A queue used to visit all PtG memory nodes that are not simple allocas
753  std::queue<PointsToGraph::NodeIndex> notSimple;
754 
755  for (PointsToGraph::NodeIndex ptgNode = 0; ptgNode < pointsToGraph.numNodes(); ptgNode++)
756  {
757  // Only memory nodes are relevant
758  if (!pointsToGraph.isMemoryNode(ptgNode))
759  continue;
760 
761  // Allocas that are not externally available start of as presumed simple
762  if (pointsToGraph.getNodeKind(ptgNode) == PointsToGraph::NodeKind::AllocaNode
763  && !pointsToGraph.isExternallyAvailable(ptgNode))
764  simpleAllocas.insert(ptgNode);
765  else
766  notSimple.push(ptgNode);
767  }
768 
769  // Process the queue to visit all memory nodes that may disqualify allocas from being simple
770  while (!notSimple.empty())
771  {
772  const auto ptgNode = notSimple.front();
773  notSimple.pop();
774 
775  // Any node targeted by the not-simple memory node can themselves not be simple
776  for (const auto targetPtgNode : pointsToGraph.getExplicitTargets(ptgNode).Items())
777  {
778  // If the target is currently in the simple allocas candiate set, move it to the queue
779  if (simpleAllocas.Remove(targetPtgNode))
780  notSimple.push(targetPtgNode);
781  }
782  }
783 
784  return simpleAllocas;
785 }
786 
789  const rvsdg::Region & region)
790 {
791  const auto & pointsToGraph = Context_->pointsToGraph;
792 
793  // Use a queue and a set to traverse the PointsToGraph
794  util::HashSet<PointsToGraph::NodeIndex> reachableSimpleAllocas;
795  std::queue<PointsToGraph::NodeIndex> nodes;
796  for (auto argument : region.Arguments())
797  {
798  if (!IsPointerCompatible(*argument))
799  continue;
800  const auto ptgNode = pointsToGraph.getNodeForRegister(*argument);
801  nodes.push(ptgNode);
802  }
803 
804  // Traverse along PointsToGraph edges to find all reachable simple allocas
805  while (!nodes.empty())
806  {
807  const auto ptgNode = nodes.front();
808  nodes.pop();
809 
810  for (const auto targetPtgNode : pointsToGraph.getExplicitTargets(ptgNode).Items())
811  {
812  // We only are about following simple allocas, as simple allocas are only reachable from them.
813  if (!Context_->SimpleAllocas.Contains(targetPtgNode))
814  continue;
815 
816  if (reachableSimpleAllocas.insert(targetPtgNode))
817  nodes.push(targetPtgNode);
818  }
819  }
820 
821  return reachableSimpleAllocas;
822 }
823 
824 bool
826 {
827  const auto scc = Context_->FunctionToSccIndex[&lambda];
828  return Context_->SccCallTargets[scc].Contains(scc);
829 }
830 
831 size_t
833 {
834  const auto & pointsToGraph = Context_->pointsToGraph;
835 
836  // Caching the sets of simple allocas reachable from region arguments
837  std::unordered_map<const rvsdg::Region *, util::HashSet<PointsToGraph::NodeIndex>>
838  reachableSimpleAllocas;
839 
840  // Returns the set of simple allocas reachable from the region's arguments
841  const auto getReachableSimpleAllocas =
842  [&](const rvsdg::Region & region) -> const util::HashSet<PointsToGraph::NodeIndex> &
843  {
844  if (const auto it = reachableSimpleAllocas.find(&region); it != reachableSimpleAllocas.end())
845  {
846  return it->second;
847  }
848  return reachableSimpleAllocas[&region] = GetSimpleAllocasReachableFromRegionArguments(region);
849  };
850 
851  // Checks if the simple alloca represented by the given points-to graph node is non-reentrant
852  const auto isNonReentrant = [&](PointsToGraph::NodeIndex simpleAllocaPtgNode) -> bool
853  {
854  auto & allocaNode = pointsToGraph.getAllocaForNode(simpleAllocaPtgNode);
855  const auto & region = *allocaNode.region();
856 
857  // If the alloca's function is never involved in any recursion,
858  // the alloca is trivially non-reentrant.
859  const auto & lambda = getSurroundingLambdaNode(allocaNode);
860  if (!IsRecursionPossible(lambda))
861  return true;
862 
863  // In lambdas where recursion is possible, simple allocas that are reachable from
864  // region arguments via edges in the points-to graph must be considered reentrant.
865  if (getReachableSimpleAllocas(region).Contains(simpleAllocaPtgNode))
866  return false;
867 
868  // Otherwise the simple alloca is non-reentrant
869  return true;
870  };
871 
872  size_t numNonReentrantAllocas = 0;
873 
874  // Only simple allocas are candidates for being non-reentrant
875  for (auto simpleAllocaPtgNode : Context_->SimpleAllocas.Items())
876  {
877  if (isNonReentrant(simpleAllocaPtgNode))
878  {
879  const auto region = pointsToGraph.getAllocaForNode(simpleAllocaPtgNode).region();
880  // Creates a set for the region if it does not already have one, and add the alloca
881  Context_->NonReentrantAllocas[region].insert(simpleAllocaPtgNode);
882  numNonReentrantAllocas++;
883  }
884  }
885 
886  return numNonReentrantAllocas;
887 }
888 
889 void
891 {
892  const auto & pointsToGraph = Context_->pointsToGraph;
893 
894  Context_->ExternalModRefIndex = ModRefSummary_->CreateModRefSet();
895 
896  // Go through all types of memory node and add them to the external ModRefSet if escaping
897  for (PointsToGraph::NodeIndex ptgNode = 0; ptgNode < pointsToGraph.numNodes(); ptgNode++)
898  {
899  // Must be a memory node
900  if (!pointsToGraph.isMemoryNode(ptgNode))
901  continue;
902 
903  // Must be externally available
904  if (!pointsToGraph.isExternallyAvailable(ptgNode))
905  continue;
906 
907  // Must be non-constant
908  if (ENABLE_CONSTANT_MEMORY_BLOCKING && pointsToGraph.isNodeConstant(ptgNode))
909  continue;
910 
911  ModRefSummary_->AddToModRefSet(Context_->ExternalModRefIndex, ptgNode);
912  }
913 }
914 
915 void
917 {
918  // Ensure the constraint vector is large enough
919  Context_->ModRefSetSimpleConstraints.resize(ModRefSummary_->NumModRefSets());
920  Context_->ModRefSetSimpleConstraints[from].insert(to);
921 }
922 
923 void
925  ModRefSetIndex index,
926  const util::HashSet<PointsToGraph::NodeIndex> & blocklist)
927 {
928  JLM_ASSERT(Context_->ModRefSetBlocklists.find(index) == Context_->ModRefSetBlocklists.end());
929  Context_->ModRefSetBlocklists[index] = &blocklist;
930 }
931 
932 void
934 {
935  const auto & region = *lambda.subregion();
936  const auto regionModRefSet = AnnotateRegion(region, lambda);
937  const auto lambdaModRefSet = ModRefSummary_->GetOrCreateSetForNode(lambda);
938  AddModRefSimpleConstraint(regionModRefSet, lambdaModRefSet);
939 
940  if (Context_->FunctionsCallingSetjmp.Contains(&lambda))
941  {
942  // If this function can be jumped into, store operations on memory in its Mod/Ref set must be
943  // sequentialized with calls to external functions, in case the trigger jumps
944  // TODO: When we separate loads and stores, this edge should only propagate stores,
945  // and turn them into loads
946  AddModRefSimpleConstraint(lambdaModRefSet, Context_->ExternalModRefIndex);
947  }
948 }
949 
952  const rvsdg::Region & region,
953  const rvsdg::LambdaNode & lambda)
954 {
955  const auto regionModRefSet = ModRefSummary_->CreateModRefSet();
956 
957  for (auto & node : region.Nodes())
958  {
960  node,
961  [&](const rvsdg::StructuralNode & structuralNode)
962  {
963  const auto nodeModRefSet = AnnotateStructuralNode(structuralNode, lambda);
964  AddModRefSimpleConstraint(nodeModRefSet, regionModRefSet);
965  },
966  [&](const rvsdg::SimpleNode & simpleNode)
967  {
968  if (const auto nodeModRefSet = AnnotateSimpleNode(simpleNode, lambda))
969  AddModRefSimpleConstraint(*nodeModRefSet, regionModRefSet);
970  });
971  }
972 
973  // Check if this region has any non-reentrant allocas. If so, block them
974  if (const auto it = Context_->NonReentrantAllocas.find(&region);
975  it != Context_->NonReentrantAllocas.end() && ENABLE_NON_REENTRANT_ALLOCA_BLOCKLIST)
976  {
977  JLM_ASSERT(ModRefSummary_->GetModRefSet(regionModRefSet).IsEmpty());
978  AddModRefSetBlocklist(regionModRefSet, it->second);
979  }
980 
981  return regionModRefSet;
982 }
983 
986  const rvsdg::StructuralNode & structuralNode,
987  const rvsdg::LambdaNode & lambda)
988 {
989  const auto nodeModRefSet = ModRefSummary_->GetOrCreateSetForNode(structuralNode);
990 
991  for (auto & subregion : structuralNode.Subregions())
992  {
993  const auto subregionModRefSef = AnnotateRegion(subregion, lambda);
994  AddModRefSimpleConstraint(subregionModRefSef, nodeModRefSet);
995  }
996 
997  return nodeModRefSet;
998 }
999 
1000 std::optional<ModRefSetIndex>
1002  const rvsdg::SimpleNode & simpleNode,
1003  const rvsdg::LambdaNode & lambda)
1004 {
1005  return MatchTypeWithDefault(
1006  simpleNode.GetOperation(),
1007  [&](const LoadOperation &) -> std::optional<ModRefSetIndex>
1008  {
1009  return AnnotateLoad(simpleNode, lambda);
1010  },
1011  [&](const StoreOperation &) -> std::optional<ModRefSetIndex>
1012  {
1013  return AnnotateStore(simpleNode, lambda);
1014  },
1015  [&](const AllocaOperation &) -> std::optional<ModRefSetIndex>
1016  {
1017  return AnnotateAlloca(simpleNode);
1018  },
1019  [&](const MallocOperation &) -> std::optional<ModRefSetIndex>
1020  {
1021  return AnnotateMalloc(simpleNode);
1022  },
1023  [&](const FreeOperation &) -> std::optional<ModRefSetIndex>
1024  {
1025  return AnnotateFree(simpleNode, lambda);
1026  },
1027  [&](const MemCpyOperation &) -> std::optional<ModRefSetIndex>
1028  {
1029  return AnnotateMemcpy(simpleNode, lambda);
1030  },
1031  [&](const CallOperation &) -> std::optional<ModRefSetIndex>
1032  {
1033  return AnnotateCall(simpleNode, lambda);
1034  },
1035  [&](const MemoryStateOperation &) -> std::optional<ModRefSetIndex>
1036  {
1037  // MemoryStateOperations are only used to route memory states, and can be ignored
1038  return std::nullopt;
1039  },
1040  [&]() -> std::optional<ModRefSetIndex>
1041  {
1042  // Any remaining type of node should not involve any memory states
1043  JLM_ASSERT(!hasMemoryState(simpleNode));
1044  return std::nullopt;
1045  });
1046 }
1047 
1048 void
1050  ModRefSetIndex modRefSetIndex,
1051  const rvsdg::Output & origin,
1052  std::optional<size_t> minTargetSize,
1053  const rvsdg::LambdaNode & lambda)
1054 {
1055  const auto & pointsToGraph = Context_->pointsToGraph;
1056  const auto & allocasDead = Context_->AllocasDeadInScc[Context_->FunctionToSccIndex[&lambda]];
1057 
1058  // TODO: Re-use ModRefSets for all uses of the registerNode in this function
1059  const auto registerPtgNode = pointsToGraph.getNodeForRegister(origin);
1060 
1061  const auto tryAddToModRefSet = [&](PointsToGraph::NodeIndex targetPtgNode)
1062  {
1063  if (ENABLE_CONSTANT_MEMORY_BLOCKING && pointsToGraph.isNodeConstant(targetPtgNode))
1064  return;
1065  if (ENABLE_OPERATION_SIZE_BLOCKING && minTargetSize)
1066  {
1067  const auto targetSize = pointsToGraph.tryGetNodeSize(targetPtgNode);
1068  if (targetSize && *targetSize < minTargetSize)
1069  return;
1070  }
1071  if (ENABLE_DEAD_ALLOCA_BLOCKLIST && allocasDead.Contains(targetPtgNode))
1072  return;
1073  ModRefSummary_->AddToModRefSet(modRefSetIndex, targetPtgNode);
1074  };
1075 
1076  // If the pointer is targeting everything external, add it all to the Mod/Ref set
1077  if (pointsToGraph.isTargetingAllExternallyAvailable(registerPtgNode))
1078  {
1079  for (const auto targetPtgNode : pointsToGraph.getExternallyAvailableNodes())
1080  {
1081  tryAddToModRefSet(targetPtgNode);
1082  }
1083  }
1084 
1085  for (const auto targetPtgNode : pointsToGraph.getExplicitTargets(registerPtgNode).Items())
1086  {
1087  tryAddToModRefSet(targetPtgNode);
1088  }
1089 }
1090 
1093  const rvsdg::SimpleNode & loadNode,
1094  const rvsdg::LambdaNode & lambda)
1095 {
1096  const auto nodeModRef = ModRefSummary_->GetOrCreateSetForNode(loadNode);
1097  const auto origin = LoadOperation::AddressInput(loadNode).origin();
1098  const auto loadOperation = util::assertedCast<const LoadOperation>(&loadNode.GetOperation());
1099  const auto loadSize = GetTypeStoreSize(*loadOperation->GetLoadedType());
1100 
1101  AddPointerOriginTargets(nodeModRef, *origin, loadSize, lambda);
1102  return nodeModRef;
1103 }
1104 
1107  const rvsdg::SimpleNode & storeNode,
1108  const rvsdg::LambdaNode & lambda)
1109 {
1110  const auto nodeModRef = ModRefSummary_->GetOrCreateSetForNode(storeNode);
1111  const auto origin = StoreOperation::AddressInput(storeNode).origin();
1112  const auto storeOperation = util::assertedCast<const StoreOperation>(&storeNode.GetOperation());
1113  const auto storeSize = GetTypeStoreSize(storeOperation->GetStoredType());
1114 
1115  AddPointerOriginTargets(nodeModRef, *origin, storeSize, lambda);
1116  return nodeModRef;
1117 }
1118 
1121 {
1122  const auto nodeModRef = ModRefSummary_->GetOrCreateSetForNode(allocaNode);
1123  const auto allocaMemoryNode = Context_->pointsToGraph.getNodeForAlloca(allocaNode);
1124  ModRefSummary_->AddToModRefSet(nodeModRef, allocaMemoryNode);
1125  return nodeModRef;
1126 }
1127 
1130 {
1131  const auto nodeModRef = ModRefSummary_->GetOrCreateSetForNode(mallocNode);
1132  const auto mallocMemoryNode = Context_->pointsToGraph.getNodeForMalloc(mallocNode);
1133  ModRefSummary_->AddToModRefSet(nodeModRef, mallocMemoryNode);
1134  return nodeModRef;
1135 }
1136 
1139  const rvsdg::SimpleNode & freeNode,
1140  const rvsdg::LambdaNode & lambda)
1141 {
1142  JLM_ASSERT(is<FreeOperation>(freeNode.GetOperation()));
1143 
1144  const auto nodeModRef = ModRefSummary_->GetOrCreateSetForNode(freeNode);
1145  const auto origin = FreeOperation::addressInput(freeNode).origin();
1146 
1147  // TODO: Only free MallocMemoryNodes
1148  AddPointerOriginTargets(nodeModRef, *origin, std::nullopt, lambda);
1149  return nodeModRef;
1150 }
1151 
1154  const rvsdg::SimpleNode & memcpyNode,
1155  const rvsdg::LambdaNode & lambda)
1156 {
1157  JLM_ASSERT(is<MemCpyOperation>(memcpyNode.GetOperation()));
1158 
1159  const auto nodeModRef = ModRefSummary_->GetOrCreateSetForNode(memcpyNode);
1160  const auto dstOrigin = MemCpyOperation::destinationInput(memcpyNode).origin();
1161  const auto srcOrigin = MemCpyOperation::sourceInput(memcpyNode).origin();
1162  const auto countOrigin = MemCpyOperation::countInput(memcpyNode).origin();
1163  const auto count = tryGetConstantSignedInteger(*countOrigin);
1164  AddPointerOriginTargets(nodeModRef, *dstOrigin, count, lambda);
1165  AddPointerOriginTargets(nodeModRef, *srcOrigin, count, lambda);
1166  return nodeModRef;
1167 }
1168 
1171  const rvsdg::SimpleNode & callNode,
1172  const rvsdg::LambdaNode & lambda)
1173 {
1174  JLM_ASSERT(is<CallOperation>(callNode.GetOperation()));
1175 
1176  const auto & pointsToGraph = Context_->pointsToGraph;
1177 
1178  // This ModRefSet represents everything the call may affect
1179  const auto callModRef = ModRefSummary_->GetOrCreateSetForNode(callNode);
1180 
1181  // Go over all possible targets of the call and add them to the call summary
1182  const auto targetPtr = callNode.input(0)->origin();
1183  const auto targetPtgNode = Context_->pointsToGraph.getNodeForRegister(*targetPtr);
1184 
1185  // Go through all locations the called function pointer may target
1186  for (const auto calleePtgNode : pointsToGraph.getExplicitTargets(targetPtgNode).Items())
1187  {
1188  const auto kind = pointsToGraph.getNodeKind(calleePtgNode);
1190  {
1191  const auto & calleeLambda = pointsToGraph.getLambdaForNode(calleePtgNode);
1192  const auto targetModRefSet = ModRefSummary_->GetOrCreateSetForNode(calleeLambda);
1193  AddModRefSimpleConstraint(targetModRefSet, callModRef);
1194  }
1195  else if (kind == PointsToGraph::NodeKind::ImportNode)
1196  {
1197  AddModRefSimpleConstraint(Context_->ExternalModRefIndex, callModRef);
1198  }
1199  }
1200  if (pointsToGraph.isTargetingAllExternallyAvailable(targetPtgNode))
1201  {
1202  AddModRefSimpleConstraint(Context_->ExternalModRefIndex, callModRef);
1203  }
1204 
1205  // Allocas that are live within the call, might no longer be live from the call site
1207  {
1208  JLM_ASSERT(ModRefSummary_->GetModRefSet(callModRef).IsEmpty());
1210  callModRef,
1211  Context_->AllocasDeadInScc[Context_->FunctionToSccIndex[&lambda]]);
1212  }
1213 
1214  return callModRef;
1215 }
1216 
1217 void
1219 {
1220  Context_->ModRefSetSimpleConstraints.resize(ModRefSummary_->NumModRefSets());
1222 
1223  // Start by pushing everything to the worklist
1224  for (ModRefSetIndex i = 0; i < ModRefSummary_->NumModRefSets(); i++)
1225  worklist.PushWorkItem(i);
1226 
1227  while (worklist.HasMoreWorkItems())
1228  {
1229  const auto workItem = worklist.PopWorkItem();
1230 
1231  // Handle all simple constraints workItem -> target
1232  for (auto target : Context_->ModRefSetSimpleConstraints[workItem].Items())
1233  {
1234  bool changed = false;
1235  if (auto blocklist = Context_->ModRefSetBlocklists.find(target);
1236  blocklist != Context_->ModRefSetBlocklists.end())
1237  {
1238  // The target has a blocklist, avoid propagating blocked MemoryNodes
1239  for (auto memoryNode : ModRefSummary_->GetModRefSet(workItem).Items())
1240  {
1241  if (blocklist->second->Contains(memoryNode))
1242  continue;
1243  changed |= ModRefSummary_->AddToModRefSet(target, memoryNode);
1244  }
1245  }
1246  else
1247  {
1248  // The target has no blocklist, propagate everything
1249  changed |= ModRefSummary_->PropagateModRefSet(workItem, target);
1250  }
1251 
1252  if (changed)
1253  worklist.PushWorkItem(target);
1254  }
1255  }
1256 
1258 }
1259 
1260 bool
1262 {
1263  // For all ModRefSets where a blocklist has been defined,
1264  // check that none of its MemoryNodes are on the blocklist
1265  for (auto [index, blocklist] : Context_->ModRefSetBlocklists)
1266  {
1267  for (auto memoryNode : ModRefSummary_->GetModRefSet(index).Items())
1268  {
1269  if (blocklist->Contains(memoryNode))
1270  return false;
1271  }
1272  }
1273  return true;
1274 }
1275 
1276 std::string
1278 {
1279  std::ostringstream ss;
1280  for (size_t i = 0; i < summarizer.Context_->SccFunctions.size(); i++)
1281  {
1282  if (i != 0)
1283  ss << " <- ";
1284  ss << "[" << std::endl;
1285  if (i == summarizer.Context_->ExternalNodeSccIndex)
1286  {
1287  ss << " " << "<external>" << std::endl;
1288  }
1289  for (auto function : summarizer.Context_->SccFunctions[i].Items())
1290  {
1291  ss << " " << function->DebugString() << std::endl;
1292  }
1293  ss << "]";
1294  }
1295  return ss.str();
1296 }
1297 
1298 std::string
1300  const rvsdg::Graph & rvsdg,
1301  const RegionAwareModRefSummary & modRefSummary)
1302 {
1303  const auto & pointsToGraph = modRefSummary.GetPointsToGraph();
1304 
1305  std::ostringstream ss;
1306 
1307  auto toString = [&](const util::HashSet<PointsToGraph::NodeIndex> & memoryNodes)
1308  {
1309  ss << "MemoryNodes: {";
1310  for (auto & memoryNode : memoryNodes.Items())
1311  {
1312  ss << pointsToGraph.getNodeDebugString(memoryNode);
1313  ss << ", ";
1314  }
1315  ss << "}" << std::endl;
1316  };
1317 
1318  auto indent = [&](size_t depth, char c = '-')
1319  {
1320  for (size_t i = 0; i < depth; i++)
1321  ss << c;
1322  };
1323 
1324  std::function<void(const rvsdg::Node &, size_t)> toRegionTree =
1325  [&](const rvsdg::Node & node, size_t depth)
1326  {
1327  if (!modRefSummary.HasSetForNode(node))
1328  return;
1329 
1330  auto modRefIndex = modRefSummary.GetSetForNode(node);
1331  auto & memoryNodes = modRefSummary.GetModRefSet(modRefIndex);
1332  ss << " ";
1333  toString(memoryNodes);
1334 
1335  if (auto structuralNode = dynamic_cast<const rvsdg::StructuralNode *>(&node))
1336  {
1337  for (auto & region : structuralNode->Subregions())
1338  {
1339  indent(depth + 1, '-');
1340  ss << "region" << std::endl;
1341  for (auto & n : region.Nodes())
1342  toRegionTree(n, depth + 2);
1343  }
1344  }
1345  };
1346 
1347  for (auto & node : rvsdg.GetRootRegion().Nodes())
1348  toRegionTree(node, 0);
1349 
1350  return ss.str();
1351 }
1352 
1353 std::unique_ptr<ModRefSummary>
1355  const rvsdg::RvsdgModule & rvsdgModule,
1356  const PointsToGraph & pointsToGraph,
1357  util::StatisticsCollector & statisticsCollector)
1358 {
1359  RegionAwareModRefSummarizer summarizer;
1360  return summarizer.SummarizeModRefs(rvsdgModule, pointsToGraph, statisticsCollector);
1361 }
1362 
1363 std::unique_ptr<ModRefSummary>
1365  const rvsdg::RvsdgModule & rvsdgModule,
1366  const PointsToGraph & pointsToGraph)
1367 {
1368  util::StatisticsCollector statisticsCollector;
1369  return Create(rvsdgModule, pointsToGraph, statisticsCollector);
1370 }
1371 }
Call operation class.
Definition: call.hpp:249
static std::unique_ptr< CallTypeClassifier > ClassifyCall(const rvsdg::SimpleNode &callNode)
Classifies a call node.
Definition: call.cpp:47
static rvsdg::Input & addressInput(const rvsdg::Node &node) noexcept
Definition: operators.hpp:2534
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition: Load.hpp:75
static rvsdg::Input & destinationInput(const rvsdg::Node &node) noexcept
Definition: MemCpy.hpp:69
static rvsdg::Input & countInput(const rvsdg::Node &node) noexcept
Definition: MemCpy.hpp:95
static rvsdg::Input & sourceInput(const rvsdg::Node &node) noexcept
Definition: MemCpy.hpp:82
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition: Store.hpp:75
const util::HashSet< PointsToGraph::NodeIndex > & GetMemoryNodes() const
util::HashSet< PointsToGraph::NodeIndex > MemoryNodes_
util::HashSet< PointsToGraph::NodeIndex > & GetMemoryNodes()
size_t numNodes() const noexcept
size_t numMemoryNodes() const noexcept
const util::HashSet< NodeIndex > & getExplicitTargets(NodeIndex index) const
bool isExternallyAvailable(NodeIndex index) const
std::string getNodeDebugString(NodeIndex index, char separator=' ') const
NodeKind getNodeKind(NodeIndex index) const
bool isMemoryNode(NodeIndex index) const
Statistics(const rvsdg::RvsdgModule &rvsdgModule, const PointsToGraph &pointsToGraph)
static std::unique_ptr< Statistics > Create(const rvsdg::RvsdgModule &rvsdgModule, const PointsToGraph &pointsToGraph)
void StopCreateNonReentrantAllocaSetsStatistics(size_t numNonReentrantAllocas)
void stopCallGraphStatistics(size_t numSccs, size_t numFunctionsCallingSetjmp)
ModRefSetIndex AnnotateStore(const rvsdg::SimpleNode &storeNode, const rvsdg::LambdaNode &lambda)
static util::HashSet< PointsToGraph::NodeIndex > CreateSimpleAllocaSet(const PointsToGraph &pointsToGraph)
ModRefSetIndex AnnotateLoad(const rvsdg::SimpleNode &loadNode, const rvsdg::LambdaNode &lambda)
void AddPointerOriginTargets(ModRefSetIndex modRefSetIndex, const rvsdg::Output &origin, std::optional< size_t > minTargetSize, const rvsdg::LambdaNode &lambda)
ModRefSetIndex AnnotateMemcpy(const rvsdg::SimpleNode &memcpyNode, const rvsdg::LambdaNode &lambda)
static std::unique_ptr< ModRefSummary > Create(const rvsdg::RvsdgModule &rvsdgModule, const PointsToGraph &pointsToGraph, util::StatisticsCollector &statisticsCollector)
void AddModRefSetBlocklist(ModRefSetIndex index, const util::HashSet< PointsToGraph::NodeIndex > &blocklist)
~RegionAwareModRefSummarizer() noexcept override
std::optional< ModRefSetIndex > AnnotateSimpleNode(const rvsdg::SimpleNode &simpleNode, const rvsdg::LambdaNode &lambda)
std::unique_ptr< RegionAwareModRefSummary > ModRefSummary_
ModRefSetIndex AnnotateMalloc(const rvsdg::SimpleNode &mallocNode)
void AddModRefSimpleConstraint(ModRefSetIndex from, ModRefSetIndex to)
bool IsRecursionPossible(const rvsdg::LambdaNode &lambda) const
static std::string CallGraphSCCsToString(const RegionAwareModRefSummarizer &summarizer)
std::unique_ptr< ModRefSummary > SummarizeModRefs(const rvsdg::RvsdgModule &rvsdgModule, const PointsToGraph &pointsToGraph, util::StatisticsCollector &statisticsCollector) override
void createCallGraph(const rvsdg::RvsdgModule &rvsdgModule)
static std::string ToRegionTree(const rvsdg::Graph &rvsdg, const RegionAwareModRefSummary &modRefSummary)
ModRefSetIndex AnnotateRegion(const rvsdg::Region &region, const rvsdg::LambdaNode &lambda)
util::HashSet< PointsToGraph::NodeIndex > GetSimpleAllocasReachableFromRegionArguments(const rvsdg::Region &region)
void AnnotateFunction(const rvsdg::LambdaNode &lambda)
ModRefSetIndex AnnotateFree(const rvsdg::SimpleNode &freeNode, const rvsdg::LambdaNode &lambda)
ModRefSetIndex AnnotateStructuralNode(const rvsdg::StructuralNode &structuralNode, const rvsdg::LambdaNode &lambda)
ModRefSetIndex AnnotateCall(const rvsdg::SimpleNode &callNode, const rvsdg::LambdaNode &lambda)
ModRefSetIndex AnnotateAlloca(const rvsdg::SimpleNode &allocaNode)
Mod/Ref summary of region-aware mod/ref summarizer.
void MapNodeToSet(const rvsdg::Node &node, ModRefSetIndex index)
bool PropagateModRefSet(ModRefSetIndex from, ModRefSetIndex to)
const util::HashSet< PointsToGraph::NodeIndex > & GetModRefSet(ModRefSetIndex index) const
bool AddToModRefSet(ModRefSetIndex index, PointsToGraph::NodeIndex ptgNode)
ModRefSetIndex GetOrCreateSetForNode(const rvsdg::Node &node)
const util::HashSet< PointsToGraph::NodeIndex > & GetSimpleNodeModRef(const rvsdg::SimpleNode &node) const override
const util::HashSet< PointsToGraph::NodeIndex > & GetGammaEntryModRef(const rvsdg::GammaNode &gamma) const override
RegionAwareModRefSummary(const PointsToGraph &pointsToGraph)
std::unordered_map< const rvsdg::Node *, ModRefSetIndex > NodeMap_
RegionAwareModRefSummary(const RegionAwareModRefSummary &)=delete
const util::HashSet< PointsToGraph::NodeIndex > & GetLambdaExitModRef(const rvsdg::LambdaNode &lambda) const override
const util::HashSet< PointsToGraph::NodeIndex > & GetLambdaEntryModRef(const rvsdg::LambdaNode &lambda) const override
RegionAwareModRefSummary & operator=(const RegionAwareModRefSummary &)=delete
const PointsToGraph & GetPointsToGraph() const noexcept override
ModRefSetIndex GetSetForNode(const rvsdg::Node &node) const
const util::HashSet< PointsToGraph::NodeIndex > & GetThetaModRef(const rvsdg::ThetaNode &theta) const override
bool HasSetForNode(const rvsdg::Node &node) const
static std::unique_ptr< RegionAwareModRefSummary > Create(const PointsToGraph &pointsToGraph)
const util::HashSet< PointsToGraph::NodeIndex > & GetGammaExitModRef(const rvsdg::GammaNode &gamma) const override
Conditional operator / pattern matching.
Definition: gamma.hpp:99
Region & GetRootRegion() const noexcept
Definition: graph.hpp:99
Output * origin() const noexcept
Definition: node.hpp:58
Lambda node.
Definition: lambda.hpp:83
rvsdg::Region * subregion() const noexcept
Definition: lambda.hpp:138
Represent acyclic RVSDG subgraphs.
Definition: region.hpp:213
RegionArgumentRange Arguments() noexcept
Definition: region.hpp:272
static size_t NumRegions(const rvsdg::Region &region) noexcept
Definition: region.cpp:453
NodeRange Nodes() noexcept
Definition: region.hpp:328
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
SubregionIteratorRange Subregions()
bool insert(ItemType item)
Definition: HashSet.hpp:210
bool Remove(ItemType item)
Definition: HashSet.hpp:332
Statistics Interface.
Definition: Statistics.hpp:31
util::Timer & GetTimer(const std::string &name)
Definition: Statistics.cpp:134
util::Timer & AddTimer(std::string name)
Definition: Statistics.cpp:155
void AddMeasurement(std::string name, T value)
Definition: Statistics.hpp:174
void start() noexcept
Definition: time.hpp:54
void stop() noexcept
Definition: time.hpp:67
void PushWorkItem(T item) override
Definition: Worklist.hpp:267
bool HasMoreWorkItems() const noexcept override
Definition: Worklist.hpp:244
#define JLM_ASSERT(x)
Definition: common.hpp:16
static const bool ENABLE_DEAD_ALLOCA_BLOCKLIST
bool IsPointerCompatible(const rvsdg::Output &value)
static const bool ENABLE_CONSTANT_MEMORY_BLOCKING
static std::vector< const rvsdg::LambdaNode * > CollectLambdaNodes(const rvsdg::RvsdgModule &rvsdgModule)
static const bool ENABLE_OPERATION_SIZE_BLOCKING
static const bool ENABLE_NON_REENTRANT_ALLOCA_BLOCKLIST
size_t GetTypeStoreSize(const rvsdg::Type &type)
Definition: types.cpp:386
std::optional< int64_t > tryGetConstantSignedInteger(const rvsdg::Output &output)
Definition: Trace.cpp:46
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 indent(size_t depth)
Definition: view.cpp:22
void MatchType(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:629
rvsdg::LambdaNode & getSurroundingLambdaNode(rvsdg::Node &node)
Definition: lambda.cpp:272
std::vector< util::HashSet< const rvsdg::LambdaNode * > > SccFunctions
std::vector< util::HashSet< PointsToGraph::NodeIndex > > AllocasDeadInScc
util::HashSet< const rvsdg::LambdaNode * > FunctionsCallingSetjmp
std::unordered_map< const rvsdg::LambdaNode *, size_t > FunctionToSccIndex
std::unordered_map< const rvsdg::Region *, util::HashSet< PointsToGraph::NodeIndex > > NonReentrantAllocas
std::vector< util::HashSet< ModRefSetIndex > > ModRefSetSimpleConstraints
std::unordered_map< ModRefSetIndex, const util::HashSet< PointsToGraph::NodeIndex > * > ModRefSetBlocklists
static const char * NumPointsToGraphMemoryNodes
Definition: Statistics.hpp:223
static const char * NumRvsdgNodes
Definition: Statistics.hpp:210