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 
15 #include <jlm/llvm/ir/Trace.hpp>
20 #include <jlm/rvsdg/MatchType.hpp>
21 #include <jlm/rvsdg/traverser.hpp>
22 #include <jlm/util/Statistics.hpp>
23 #include <jlm/util/TarjanScc.hpp>
24 #include <jlm/util/Worklist.hpp>
25 
26 #include <queue>
27 
28 namespace jlm::llvm::aa
29 {
30 
35 static const bool ENABLE_DEAD_ALLOCA_BLOCKLIST = !std::getenv("JLM_DISABLE_DEAD_ALLOCA_BLOCKLIST");
36 
43  !std::getenv("JLM_DISABLE_NON_REENTRANT_ALLOCA_BLOCKLIST");
44 
50 static const bool ENABLE_OPERATION_SIZE_BLOCKING =
51  !std::getenv("JLM_DISABLE_OPERATION_SIZE_BLOCKING");
52 
58  !std::getenv("JLM_DISABLE_CONSTANT_MEMORY_BLOCKING");
59 
64 static const bool ENABLE_EXTERNAL_COMPRESSION = !std::getenv("JLM_DISABLE_EXTERN_COMPRESSION");
65 
73 {
74  static constexpr auto NumRvsdgRegionsLabel_ = "#RvsdgRegions";
75  static constexpr auto NumSimpleAllocas_ = "#SimpleAllocas";
76  static constexpr auto NumNonReentrantAllocas_ = "#NonReentrantAllocas";
77  static constexpr auto NumCallGraphSccs_ = "#CallGraphSccs";
78  static constexpr auto NumFunctionsCallingSetjmp_ = "#FunctionsCallingSetjmp";
79  static constexpr auto NumCallGraphSccsCanCallExternal_ = "#CallGraphSccsCanCallExternal";
80 
81  // Statistics about compressing memory nodes into the external node
82  // Number of ModRefSets containing external
83  static constexpr auto NumExtModRefSets_ = "#ExtModRefSets";
84  // Number of memory nodes removed from compression in sets containing external
85  static constexpr auto NumExtModRefCompressed_ = "#ExtModRefCompressed";
86  // Number of memory nodes kept (not compressed) in sets containing external
87  static constexpr auto NumExtModRefKept_ = "#ExtModRefKept";
88  // Number of ModRefSets that do not contain external
89  static constexpr auto NumLocalModRefSets_ = "#LocalModRefSets";
90  // Number of memory nodes in sets that do not contain external
91  static constexpr auto NumLocalModRefKept_ = "#LocalModRefKept";
92 
93  static constexpr auto CallGraphTimer_ = "CallGraphTimer";
94  static constexpr auto AllocasDeadInSccsTimer_ = "AllocasDeadInSccsTimer";
95  static constexpr auto SimpleAllocasSetTimer_ = "SimpleAllocasSetTimer";
96  static constexpr auto NonReentrantAllocaSetsTimer_ = "NonReentrantAllocaSetsTimer";
97  static constexpr auto CreateExternalModRefSetTimer_ = "CreateExternalModRefSetTimer";
98  static constexpr auto AnnotationTimer_ = "AnnotationTimer";
99  static constexpr auto SolvingTimer_ = "SolvingTimer";
100  static constexpr auto ExternalCompressionTimer_ = "ExternalCompactionTimer";
101 
102 public:
103  ~Statistics() override = default;
104 
105  explicit Statistics(const rvsdg::RvsdgModule & rvsdgModule, const PointsToGraph & pointsToGraph)
106  : util::Statistics(Id::RegionAwareModRefSummarizer, rvsdgModule.SourceFilePath().value())
107  {
113  }
114 
115  void
117  {
119  }
120 
121  void
122  stopCallGraphStatistics(size_t numSccs, size_t numFunctionsCallingSetjmp)
123  {
126  AddMeasurement(NumFunctionsCallingSetjmp_, numFunctionsCallingSetjmp);
127  }
128 
129  void
131  {
133  }
134 
135  void
137  {
139  }
140 
141  void
143  {
145  }
146 
147  void
148  StopCreateSimpleAllocasSetStatistics(uint64_t numSimpleAllocas)
149  {
151  AddMeasurement(NumSimpleAllocas_, numSimpleAllocas);
152  }
153 
154  void
156  {
158  }
159 
160  void
161  StopCreateNonReentrantAllocaSetsStatistics(size_t numNonReentrantAllocas)
162  {
163  AddMeasurement(NumNonReentrantAllocas_, numNonReentrantAllocas);
165  }
166 
167  void
169  {
171  }
172 
173  void
175  {
177  }
178 
179  void
181  {
183  }
184 
185  void
187  {
189  }
190 
191  void
193  {
195  }
196 
197  void
199  {
201  }
202 
203  void
205  {
207  }
208 
209  void
211  size_t numExtModRefSets,
212  size_t numExtModRefCompressed,
213  size_t numExtModRefKept,
214  size_t numLocalModRefSets,
215  size_t numLocalModRefKept)
216  {
218  AddMeasurement(NumExtModRefSets_, numExtModRefSets);
219  AddMeasurement(NumExtModRefCompressed_, numExtModRefCompressed);
220  AddMeasurement(NumExtModRefKept_, numExtModRefKept);
221  AddMeasurement(NumLocalModRefSets_, numLocalModRefSets);
222  AddMeasurement(NumLocalModRefKept_, numLocalModRefKept);
223  }
224 
225  static std::unique_ptr<Statistics>
226  Create(const rvsdg::RvsdgModule & rvsdgModule, const PointsToGraph & pointsToGraph)
227  {
228  return std::make_unique<Statistics>(rvsdgModule, pointsToGraph);
229  }
230 };
231 
236 class ModRefSet final
237 {
238 public:
239  ModRefSet() = default;
240 
243  {
244  return MemoryNodes_;
245  }
246 
247  [[nodiscard]] const util::HashSet<PointsToGraph::NodeIndex> &
249  {
250  return MemoryNodes_;
251  }
252 
253 private:
255 };
256 
260 {
261 public:
262  explicit RegionAwareModRefSummary(const PointsToGraph & pointsToGraph)
263  : PointsToGraph_(pointsToGraph)
264  {}
265 
269 
270  [[nodiscard]] const PointsToGraph &
271  GetPointsToGraph() const noexcept override
272  {
273  return PointsToGraph_;
274  }
275 
276  [[nodiscard]] size_t
277  NumModRefSets() const noexcept
278  {
279  return ModRefSets_.size();
280  }
281 
284  {
285  JLM_ASSERT(index < ModRefSets_.size());
286  return ModRefSets_[index].GetMemoryNodes();
287  }
288 
289  [[nodiscard]] const util::HashSet<PointsToGraph::NodeIndex> &
291  {
292  JLM_ASSERT(index < ModRefSets_.size());
293  return ModRefSets_[index].GetMemoryNodes();
294  }
295 
296  bool
298  {
299  JLM_ASSERT(index < ModRefSets_.size());
300  return ModRefSets_[index].GetMemoryNodes().insert(ptgNode);
301  }
302 
303  bool
305  {
306  JLM_ASSERT(from < ModRefSets_.size());
307  JLM_ASSERT(to < ModRefSets_.size());
308  return ModRefSets_[to].GetMemoryNodes().UnionWith(ModRefSets_[from].GetMemoryNodes());
309  }
310 
315  [[nodiscard]] ModRefSetIndex
317  {
318  ModRefSets_.emplace_back();
319  return ModRefSets_.size() - 1;
320  }
321 
322  [[nodiscard]] bool
323  HasSetForNode(const rvsdg::Node & node) const
324  {
325  return NodeMap_.find(&node) != NodeMap_.end();
326  }
327 
328  [[nodiscard]] ModRefSetIndex
329  GetSetForNode(const rvsdg::Node & node) const
330  {
331  const auto it = NodeMap_.find(&node);
332  JLM_ASSERT(it != NodeMap_.end());
333  return it->second;
334  }
335 
336  [[nodiscard]] ModRefSetIndex
338  {
339  if (const auto it = NodeMap_.find(&node); it != NodeMap_.end())
340  return it->second;
341 
342  return NodeMap_[&node] = CreateModRefSet();
343  }
344 
345  void
347  {
348  JLM_ASSERT(!HasSetForNode(node));
349  NodeMap_[&node] = index;
350  }
351 
353  GetSimpleNodeModRef(const rvsdg::SimpleNode & node) const override
354  {
355  return ModRefSets_[GetSetForNode(node)].GetMemoryNodes();
356  }
357 
359  GetGammaEntryModRef(const rvsdg::GammaNode & gamma) const override
360  {
361  return ModRefSets_[GetSetForNode(gamma)].GetMemoryNodes();
362  }
363 
365  GetGammaExitModRef(const rvsdg::GammaNode & gamma) const override
366  {
367  return GetGammaEntryModRef(gamma);
368  }
369 
371  GetThetaModRef(const rvsdg::ThetaNode & theta) const override
372  {
373  return ModRefSets_[GetSetForNode(theta)].GetMemoryNodes();
374  }
375 
377  GetLambdaEntryModRef(const rvsdg::LambdaNode & lambda) const override
378  {
379  return ModRefSets_[GetSetForNode(lambda)].GetMemoryNodes();
380  }
381 
383  GetLambdaExitModRef(const rvsdg::LambdaNode & lambda) const override
384  {
385  return GetLambdaEntryModRef(lambda);
386  }
387 
388  [[nodiscard]] static std::unique_ptr<RegionAwareModRefSummary>
389  Create(const PointsToGraph & pointsToGraph)
390  {
391  return std::make_unique<RegionAwareModRefSummary>(pointsToGraph);
392  }
393 
394 private:
396 
400  std::vector<ModRefSet> ModRefSets_;
401 
407  std::unordered_map<const rvsdg::Node *, ModRefSetIndex> NodeMap_;
408 };
409 
414 {
415  explicit Context(const PointsToGraph & ptg)
416  : pointsToGraph(ptg)
417  {}
418 
423 
434  std::vector<util::HashSet<const rvsdg::LambdaNode *>> SccFunctions;
435 
442 
450  std::vector<util::HashSet<size_t>> SccCallTargets;
451 
457  std::unordered_map<const rvsdg::LambdaNode *, size_t> FunctionToSccIndex;
458 
464 
471  std::vector<util::HashSet<PointsToGraph::NodeIndex>> AllocasDeadInScc;
472 
479 
486  std::unordered_map<const rvsdg::Region *, util::HashSet<PointsToGraph::NodeIndex>>
488 
495 
501  std::vector<util::HashSet<ModRefSetIndex>> ModRefSetSimpleConstraints;
502 
509  std::unordered_map<ModRefSetIndex, const util::HashSet<PointsToGraph::NodeIndex> *>
511 
512  // Counters used for external compression statistics
513  size_t numExtModRefSets = 0;
515  size_t numExtModRefKept = 0;
516  size_t numLocalModRefSets = 0;
517  size_t numLocalModRefKept = 0;
518 };
519 
521 
523 
524 std::unique_ptr<ModRefSummary>
525 RegionAwareModRefSummarizer::SummarizeModRefs(
526  const rvsdg::RvsdgModule & rvsdgModule,
527  const PointsToGraph & pointsToGraph,
528  util::StatisticsCollector & statisticsCollector)
529 {
530  ModRefSummary_ = RegionAwareModRefSummary::Create(pointsToGraph);
531  Context_ = std::make_unique<Context>(pointsToGraph);
532  auto statistics = Statistics::Create(rvsdgModule, pointsToGraph);
533 
534  statistics->startCallGraphStatistics();
535  createCallGraph(rvsdgModule);
536  statistics->stopCallGraphStatistics(
537  Context_->SccFunctions.size(),
538  Context_->FunctionsCallingSetjmp.Size());
539 
540  statistics->StartAllocasDeadInSccStatistics();
541  FindAllocasDeadInSccs();
542  statistics->StopAllocasDeadInSccStatistics();
543 
544  statistics->StartCreateSimpleAllocasSetStatistics();
545  Context_->SimpleAllocas = CreateSimpleAllocaSet(pointsToGraph);
546  statistics->StopCreateSimpleAllocasSetStatistics(Context_->SimpleAllocas.Size());
547 
548  statistics->StartCreateNonReentrantAllocaSetsStatistics();
549  auto numNonReentrantAllocas = CreateNonReentrantAllocaSets();
550  statistics->StopCreateNonReentrantAllocaSetsStatistics(numNonReentrantAllocas);
551 
552  statistics->StartCreateExternalModRefSet();
553  CreateExternalModRefSet();
554  statistics->StopCreateExternalModRefSet();
555 
556  statistics->StartAnnotationStatistics();
557  // Go through and recursively annotate all functions, regions and nodes
558  for (const auto & scc : Context_->SccFunctions)
559  {
560  for (const auto lambda : scc.Items())
561  {
562  AnnotateFunction(*lambda);
563  }
564  }
565  statistics->StopAnnotationStatistics();
566 
567  statistics->StartSolvingStatistics();
568  SolveModRefSetConstraintGraph();
569  statistics->StopSolvingStatistics();
570 
572  {
573  statistics->StartExternalCompactionStatistics();
574  doExternalCompression();
575  statistics->StopExternalCompactionStatistics(
576  Context_->numExtModRefSets,
577  Context_->numExtModRefCompressed,
578  Context_->numExtModRefKept,
579  Context_->numLocalModRefSets,
580  Context_->numLocalModRefKept);
581  }
582 
583  // Print debug output
584  // std::cerr << PointsToGraph::dumpDot(pointsToGraph) << std::endl;
585  // std::cerr << "numSimpleAllocas: " << Context_->SimpleAllocas.Size() << std::endl;
586  // std::cerr << "numNonReentrantAllocas: " << numNonReentrantAllocas << std::endl;
587  // std::cerr << "Call Graph SCCs:" << std::endl << CallGraphSCCsToString(*this) << std::endl;
588  // std::cerr << "RegionTree:" << std::endl
589  // << ToRegionTree(rvsdgModule.Rvsdg(), *ModRefSummary_) << std::endl;
590 
591  statisticsCollector.CollectDemandedStatistics(std::move(statistics));
592  Context_.reset();
593  return std::move(ModRefSummary_);
594 }
595 
601 static std::vector<const rvsdg::LambdaNode *>
603 {
604  std::vector<const rvsdg::LambdaNode *> result;
605 
606  // Recursively traverses all structural nodes, but does not enter into lambdas
607  const std::function<void(rvsdg::Region &)> CollectLambdasInRegion =
608  [&](rvsdg::Region & region) -> void
609  {
610  for (auto & node : region.Nodes())
611  {
612  if (auto lambda = dynamic_cast<rvsdg::LambdaNode *>(&node))
613  {
614  result.push_back(lambda);
615  }
616  else if (auto structural = dynamic_cast<rvsdg::StructuralNode *>(&node))
617  {
618  for (size_t i = 0; i < structural->nsubregions(); i++)
619  {
620  CollectLambdasInRegion(*structural->subregion(i));
621  }
622  }
623  }
624  };
625 
626  CollectLambdasInRegion(rvsdgModule.Rvsdg().GetRootRegion());
627 
628  return result;
629 }
630 
631 void
633 {
634  const auto & pointsToGraph = Context_->pointsToGraph;
635 
636  // The list of lambdas becomes the list of nodes in the call graph
637  auto lambdaNodes = CollectLambdaNodes(rvsdgModule);
638 
639  // Mapping from LambdaNode* to its index in lambdaNodes
640  std::unordered_map<const rvsdg::LambdaNode *, size_t> callGraphNodeIndex;
641  callGraphNodeIndex.reserve(lambdaNodes.size());
642  for (size_t i = 0; i < lambdaNodes.size(); i++)
643  {
644  callGraphNodeIndex.insert({ lambdaNodes[i], i });
645  }
646 
647  // Add a dummy node representing all external functions, with no associated LambdaNode
648  const auto externalNodeIndex = lambdaNodes.size();
649  const auto numCallGraphNodes = externalNodeIndex + 1;
650 
651  // Outgoing edges for each node in the call graph, indexed by position in lambdaNodes
652  std::vector<util::HashSet<size_t>> callGraphSuccessors(numCallGraphNodes);
653 
654  // Add outgoing edges from the given caller to any function the call may target
655  const auto handleCall = [&](const rvsdg::SimpleNode & callNode, size_t callerIndex) -> void
656  {
657  const auto classification = CallOperation::ClassifyCall(callNode);
658  if (classification->isSetjmpCall())
659  {
660  Context_->FunctionsCallingSetjmp.insert(lambdaNodes[callerIndex]);
661  return;
662  }
663 
664  const auto target = callNode.input(0)->origin();
665  const auto targetPtgNode = pointsToGraph.getNodeForRegister(*target);
666 
667  // Go through all locations the called function pointer may target
668  for (const auto calleePtgNode : pointsToGraph.getExplicitTargets(targetPtgNode).Items())
669  {
670  const auto kind = pointsToGraph.getNodeKind(calleePtgNode);
672  {
673  const auto & lambdaNode = pointsToGraph.getLambdaForNode(calleePtgNode);
674 
675  // Look up which call graph node represents the target lambda
676  JLM_ASSERT(callGraphNodeIndex.find(&lambdaNode) != callGraphNodeIndex.end());
677  const auto calleeCallGraphNode = callGraphNodeIndex[&lambdaNode];
678 
679  // Add the edge caller -> callee to the call graph
680  callGraphSuccessors[callerIndex].insert(calleeCallGraphNode);
681  }
682  else if (kind == PointsToGraph::NodeKind::ImportNode)
683  {
684  // Add the edge caller -> node representing external functions
685  callGraphSuccessors[callerIndex].insert(externalNodeIndex);
686  }
687  }
688 
689  if (pointsToGraph.isTargetingAllExternallyAvailable(targetPtgNode))
690  {
691  // If the call target pointer is flagged, add an edge to external functions
692  callGraphSuccessors[callerIndex].insert(externalNodeIndex);
693  }
694  };
695 
696  // Recursive function finding all call operations, adding edges to the call graph
697  const std::function<void(const rvsdg::Region &, size_t)> handleCalls =
698  [&](const rvsdg::Region & region, size_t callerIndex) -> void
699  {
700  for (auto & node : region.Nodes())
701  {
702  if (const auto [callNode, callOp] = rvsdg::TryGetSimpleNodeAndOptionalOp<CallOperation>(node);
703  callOp)
704  {
705  handleCall(*callNode, callerIndex);
706  }
707 
709  node,
710  [&](const rvsdg::StructuralNode & structural)
711  {
712  for (auto & subregion : structural.Subregions())
713  {
714  handleCalls(subregion, callerIndex);
715  }
716  });
717  }
718  };
719 
720  // For all functions, visit all their calls and add outgoing edges in the call graph
721  for (size_t i = 0; i < lambdaNodes.size(); i++)
722  {
723  handleCalls(*lambdaNodes[i]->subregion(), i);
724 
725  // If the function has escaped, add an edge from the node representing all external functions
726  if (pointsToGraph.isExternallyAvailable(pointsToGraph.getNodeForLambda(*lambdaNodes[i])))
727  {
728  callGraphSuccessors[externalNodeIndex].insert(i);
729  }
730  }
731 
732  // Finally, add the fact that the external node may call itself
733  callGraphSuccessors[externalNodeIndex].insert(externalNodeIndex);
734 
735  // Used by the implementation of Tarjan's SCC algorithm
736  const auto getSuccessors = [&](size_t nodeIndex)
737  {
738  return callGraphSuccessors[nodeIndex].Items();
739  };
740 
741  // Find SCCs in the call graph
742  std::vector<size_t> sccIndex;
743  std::vector<size_t> reverseTopologicalOrder;
744  auto numSCCs = util::FindStronglyConnectedComponents<size_t>(
745  numCallGraphNodes,
746  getSuccessors,
747  sccIndex,
748  reverseTopologicalOrder);
749 
750  // sccIndex are distributed in a reverse topological order, so the sccIndex is used
751  // when creating the list of SCCs and the functions they contain
752  Context_->SccFunctions.resize(numSCCs);
753  for (size_t i = 0; i < lambdaNodes.size(); i++)
754  {
755  Context_->SccFunctions[sccIndex[i]].insert(lambdaNodes[i]);
756  Context_->FunctionToSccIndex[lambdaNodes[i]] = sccIndex[i];
757  }
758 
759  // Add edges between the SCCs for all calls
760  Context_->SccCallTargets.resize(numSCCs);
761  for (size_t i = 0; i < numCallGraphNodes; i++)
762  {
763  for (auto target : callGraphSuccessors[i].Items())
764  {
765  Context_->SccCallTargets[sccIndex[i]].insert(sccIndex[target]);
766  }
767  }
768 
769  // Also note which SCC contains all external functions
770  Context_->ExternalNodeSccIndex = sccIndex[externalNodeIndex];
771 }
772 
773 void
775 {
776  const auto & pointsToGraph = Context_->pointsToGraph;
777 
778  // First find which allocas may be live in each SCC
779  std::vector<util::HashSet<PointsToGraph::NodeIndex>> liveAllocas(Context_->SccFunctions.size());
780 
782 
783  // Add all Allocas to the SCC of the function they are defined in
784  for (auto allocaPtgNode : Context_->pointsToGraph.allocaNodes())
785  {
786  allAllocas.insert(allocaPtgNode);
787  const auto & allocaNode = pointsToGraph.getAllocaForNode(allocaPtgNode);
788  const auto & lambdaNode = rvsdg::getSurroundingLambdaNode(allocaNode);
789  JLM_ASSERT(Context_->FunctionToSccIndex.count(&lambdaNode));
790  const auto sccIndex = Context_->FunctionToSccIndex[&lambdaNode];
791  liveAllocas[sccIndex].insert(allocaPtgNode);
792  }
793 
794  // Propagate live allocas to targets of function calls.
795  // I.e., if a() -> b(), then any alloca that is live in a() may also be live in b()
796  // Start at the topologically earliest SCC, which has the largest SCC index
797  // The topologically latest SCC can't have any targets
798  for (size_t sccIndex = Context_->SccFunctions.size() - 1; sccIndex > 0; sccIndex--)
799  {
800  for (auto targetScc : Context_->SccCallTargets[sccIndex].Items())
801  {
802  JLM_ASSERT(targetScc <= sccIndex);
803  if (targetScc != sccIndex)
804  liveAllocas[targetScc].UnionWith(liveAllocas[sccIndex]);
805  }
806  }
807 
808  // Any alloca that is not live in the SCC gets added to the set of allocas that are dead
809  Context_->AllocasDeadInScc.resize(Context_->SccFunctions.size());
810  for (size_t sccIndex = 0; sccIndex < Context_->SccFunctions.size(); sccIndex++)
811  {
812  Context_->AllocasDeadInScc[sccIndex].UnionWith(allAllocas);
813  Context_->AllocasDeadInScc[sccIndex].DifferenceWith(liveAllocas[sccIndex]);
814  }
815 }
816 
819 {
820  // The set of allocas that are simple. Starts off as an over-approximation
822  // A queue used to visit all PtG memory nodes that are not simple allocas
823  std::queue<PointsToGraph::NodeIndex> notSimple;
824 
825  for (PointsToGraph::NodeIndex ptgNode = 0; ptgNode < pointsToGraph.numNodes(); ptgNode++)
826  {
827  // Only memory nodes are relevant
828  if (!pointsToGraph.isMemoryNode(ptgNode))
829  continue;
830 
831  // Allocas that are not externally available start of as presumed simple
832  if (pointsToGraph.getNodeKind(ptgNode) == PointsToGraph::NodeKind::AllocaNode
833  && !pointsToGraph.isExternallyAvailable(ptgNode))
834  simpleAllocas.insert(ptgNode);
835  else
836  notSimple.push(ptgNode);
837  }
838 
839  // Process the queue to visit all memory nodes that may disqualify allocas from being simple
840  while (!notSimple.empty())
841  {
842  const auto ptgNode = notSimple.front();
843  notSimple.pop();
844 
845  // Any node targeted by the not-simple memory node can themselves not be simple
846  for (const auto targetPtgNode : pointsToGraph.getExplicitTargets(ptgNode).Items())
847  {
848  // If the target is currently in the simple allocas candiate set, move it to the queue
849  if (simpleAllocas.Remove(targetPtgNode))
850  notSimple.push(targetPtgNode);
851  }
852  }
853 
854  return simpleAllocas;
855 }
856 
859  const rvsdg::Region & region)
860 {
861  const auto & pointsToGraph = Context_->pointsToGraph;
862 
863  // Use a queue and a set to traverse the PointsToGraph
864  util::HashSet<PointsToGraph::NodeIndex> reachableSimpleAllocas;
865  std::queue<PointsToGraph::NodeIndex> nodes;
866  for (auto argument : region.Arguments())
867  {
868  if (!IsPointerCompatible(*argument))
869  continue;
870  const auto ptgNode = pointsToGraph.getNodeForRegister(*argument);
871  nodes.push(ptgNode);
872  }
873 
874  // Traverse along PointsToGraph edges to find all reachable simple allocas
875  while (!nodes.empty())
876  {
877  const auto ptgNode = nodes.front();
878  nodes.pop();
879 
880  for (const auto targetPtgNode : pointsToGraph.getExplicitTargets(ptgNode).Items())
881  {
882  // We only are about following simple allocas, as simple allocas are only reachable from them.
883  if (!Context_->SimpleAllocas.Contains(targetPtgNode))
884  continue;
885 
886  if (reachableSimpleAllocas.insert(targetPtgNode))
887  nodes.push(targetPtgNode);
888  }
889  }
890 
891  return reachableSimpleAllocas;
892 }
893 
894 bool
896 {
897  const auto scc = Context_->FunctionToSccIndex[&lambda];
898  return Context_->SccCallTargets[scc].Contains(scc);
899 }
900 
901 size_t
903 {
904  const auto & pointsToGraph = Context_->pointsToGraph;
905 
906  // Caching the sets of simple allocas reachable from region arguments
907  std::unordered_map<const rvsdg::Region *, util::HashSet<PointsToGraph::NodeIndex>>
908  reachableSimpleAllocas;
909 
910  // Returns the set of simple allocas reachable from the region's arguments
911  const auto getReachableSimpleAllocas =
912  [&](const rvsdg::Region & region) -> const util::HashSet<PointsToGraph::NodeIndex> &
913  {
914  if (const auto it = reachableSimpleAllocas.find(&region); it != reachableSimpleAllocas.end())
915  {
916  return it->second;
917  }
918  return reachableSimpleAllocas[&region] = GetSimpleAllocasReachableFromRegionArguments(region);
919  };
920 
921  // Checks if the simple alloca represented by the given points-to graph node is non-reentrant
922  const auto isNonReentrant = [&](PointsToGraph::NodeIndex simpleAllocaPtgNode) -> bool
923  {
924  auto & allocaNode = pointsToGraph.getAllocaForNode(simpleAllocaPtgNode);
925  const auto & region = *allocaNode.region();
926 
927  // If the alloca's function is never involved in any recursion,
928  // the alloca is trivially non-reentrant.
929  const auto & lambda = getSurroundingLambdaNode(allocaNode);
930  if (!IsRecursionPossible(lambda))
931  return true;
932 
933  // In lambdas where recursion is possible, simple allocas that are reachable from
934  // region arguments via edges in the points-to graph must be considered reentrant.
935  if (getReachableSimpleAllocas(region).Contains(simpleAllocaPtgNode))
936  return false;
937 
938  // Otherwise the simple alloca is non-reentrant
939  return true;
940  };
941 
942  size_t numNonReentrantAllocas = 0;
943 
944  // Only simple allocas are candidates for being non-reentrant
945  for (auto simpleAllocaPtgNode : Context_->SimpleAllocas.Items())
946  {
947  if (isNonReentrant(simpleAllocaPtgNode))
948  {
949  const auto region = pointsToGraph.getAllocaForNode(simpleAllocaPtgNode).region();
950  // Creates a set for the region if it does not already have one, and add the alloca
951  Context_->NonReentrantAllocas[region].insert(simpleAllocaPtgNode);
952  numNonReentrantAllocas++;
953  }
954  }
955 
956  return numNonReentrantAllocas;
957 }
958 
959 void
961 {
962  const auto & pointsToGraph = Context_->pointsToGraph;
963 
964  Context_->ExternalModRefIndex = ModRefSummary_->CreateModRefSet();
965 
966  // Go through all types of memory node and add them to the external ModRefSet if escaping
967  for (PointsToGraph::NodeIndex ptgNode = 0; ptgNode < pointsToGraph.numNodes(); ptgNode++)
968  {
969  // Must be a memory node
970  if (!pointsToGraph.isMemoryNode(ptgNode))
971  continue;
972 
973  // Must be externally available
974  if (!pointsToGraph.isExternallyAvailable(ptgNode))
975  continue;
976 
977  // Must be non-constant
978  if (ENABLE_CONSTANT_MEMORY_BLOCKING && pointsToGraph.isNodeConstant(ptgNode))
979  continue;
980 
981  ModRefSummary_->AddToModRefSet(Context_->ExternalModRefIndex, ptgNode);
982  }
983 }
984 
985 void
987 {
988  // Ensure the constraint vector is large enough
989  Context_->ModRefSetSimpleConstraints.resize(ModRefSummary_->NumModRefSets());
990  Context_->ModRefSetSimpleConstraints[from].insert(to);
991 }
992 
993 void
995  ModRefSetIndex index,
996  const util::HashSet<PointsToGraph::NodeIndex> & blocklist)
997 {
998  JLM_ASSERT(Context_->ModRefSetBlocklists.find(index) == Context_->ModRefSetBlocklists.end());
999  Context_->ModRefSetBlocklists[index] = &blocklist;
1000 }
1001 
1002 void
1004 {
1005  const auto & region = *lambda.subregion();
1006  const auto regionModRefSet = AnnotateRegion(region, lambda);
1007  const auto lambdaModRefSet = ModRefSummary_->GetOrCreateSetForNode(lambda);
1008  AddModRefSimpleConstraint(regionModRefSet, lambdaModRefSet);
1009 
1010  if (Context_->FunctionsCallingSetjmp.Contains(&lambda))
1011  {
1012  // If this function can be jumped into, store operations on memory in its Mod/Ref set must be
1013  // sequentialized with calls to external functions, in case the trigger jumps
1014  // TODO: When we separate loads and stores, this edge should only propagate stores,
1015  // and turn them into loads
1016  AddModRefSimpleConstraint(lambdaModRefSet, Context_->ExternalModRefIndex);
1017  }
1018 
1019  // If the function is externally available, it can be called by external functions,
1020  // so add a simple edge to the ModRefSet representing all external functions.
1021  const auto lambdaPtgNode = Context_->pointsToGraph.getNodeForLambda(lambda);
1022  if (Context_->pointsToGraph.isExternallyAvailable(lambdaPtgNode))
1023  {
1024  AddModRefSimpleConstraint(lambdaModRefSet, Context_->ExternalModRefIndex);
1025  }
1026 }
1027 
1030  const rvsdg::Region & region,
1031  const rvsdg::LambdaNode & lambda)
1032 {
1033  const auto regionModRefSet = ModRefSummary_->CreateModRefSet();
1034 
1035  for (auto & node : region.Nodes())
1036  {
1038  node,
1039  [&](const rvsdg::StructuralNode & structuralNode)
1040  {
1041  const auto nodeModRefSet = AnnotateStructuralNode(structuralNode, lambda);
1042  AddModRefSimpleConstraint(nodeModRefSet, regionModRefSet);
1043  },
1044  [&](const rvsdg::SimpleNode & simpleNode)
1045  {
1046  if (const auto nodeModRefSet = AnnotateSimpleNode(simpleNode, lambda))
1047  AddModRefSimpleConstraint(*nodeModRefSet, regionModRefSet);
1048  });
1049  }
1050 
1051  // Check if this region has any non-reentrant allocas. If so, block them
1052  if (const auto it = Context_->NonReentrantAllocas.find(&region);
1053  it != Context_->NonReentrantAllocas.end() && ENABLE_NON_REENTRANT_ALLOCA_BLOCKLIST)
1054  {
1055  JLM_ASSERT(ModRefSummary_->GetModRefSet(regionModRefSet).IsEmpty());
1056  AddModRefSetBlocklist(regionModRefSet, it->second);
1057  }
1058 
1059  return regionModRefSet;
1060 }
1061 
1064  const rvsdg::StructuralNode & structuralNode,
1065  const rvsdg::LambdaNode & lambda)
1066 {
1067  const auto nodeModRefSet = ModRefSummary_->GetOrCreateSetForNode(structuralNode);
1068 
1069  for (auto & subregion : structuralNode.Subregions())
1070  {
1071  const auto subregionModRefSef = AnnotateRegion(subregion, lambda);
1072  AddModRefSimpleConstraint(subregionModRefSef, nodeModRefSet);
1073  }
1074 
1075  return nodeModRefSet;
1076 }
1077 
1078 std::optional<ModRefSetIndex>
1080  const rvsdg::SimpleNode & simpleNode,
1081  const rvsdg::LambdaNode & lambda)
1082 {
1083  return MatchTypeWithDefault(
1084  simpleNode.GetOperation(),
1085  [&](const LoadOperation &) -> std::optional<ModRefSetIndex>
1086  {
1087  return AnnotateLoad(simpleNode, lambda);
1088  },
1089  [&](const StoreOperation &) -> std::optional<ModRefSetIndex>
1090  {
1091  return AnnotateStore(simpleNode, lambda);
1092  },
1093  [&](const AllocaOperation &) -> std::optional<ModRefSetIndex>
1094  {
1095  return AnnotateAlloca(simpleNode);
1096  },
1097  [&](const MallocOperation &) -> std::optional<ModRefSetIndex>
1098  {
1099  return AnnotateMalloc(simpleNode);
1100  },
1101  [&](const FreeOperation &) -> std::optional<ModRefSetIndex>
1102  {
1103  return AnnotateFree(simpleNode, lambda);
1104  },
1105  [&](const MemCpyOperation &) -> std::optional<ModRefSetIndex>
1106  {
1107  return AnnotateMemcpy(simpleNode, lambda);
1108  },
1109  [&](const MemSetOperation &) -> std::optional<ModRefSetIndex>
1110  {
1111  return AnnotateMemset(simpleNode, lambda);
1112  },
1113  [&](const CallOperation &) -> std::optional<ModRefSetIndex>
1114  {
1115  return AnnotateCall(simpleNode, lambda);
1116  },
1117  [&](const MemoryStateOperation &) -> std::optional<ModRefSetIndex>
1118  {
1119  // MemoryStateOperations are only used to route memory states, and can be ignored
1120  return std::nullopt;
1121  },
1122  [&]() -> std::optional<ModRefSetIndex>
1123  {
1124  // Any remaining type of node should not involve any memory states
1125  JLM_ASSERT(!hasMemoryState(simpleNode));
1126  return std::nullopt;
1127  });
1128 }
1129 
1130 void
1132  ModRefSetIndex modRefSetIndex,
1133  const rvsdg::Output & origin,
1134  std::optional<size_t> minTargetSize,
1135  const rvsdg::LambdaNode & lambda)
1136 {
1137  const auto & pointsToGraph = Context_->pointsToGraph;
1138  const auto & allocasDead = Context_->AllocasDeadInScc[Context_->FunctionToSccIndex[&lambda]];
1139 
1140  // TODO: Re-use ModRefSets for all uses of the registerNode in this function
1141  const auto registerPtgNode = pointsToGraph.getNodeForRegister(origin);
1142 
1143  const auto tryAddToModRefSet = [&](PointsToGraph::NodeIndex targetPtgNode)
1144  {
1145  if (ENABLE_CONSTANT_MEMORY_BLOCKING && pointsToGraph.isNodeConstant(targetPtgNode))
1146  return;
1147  if (ENABLE_OPERATION_SIZE_BLOCKING && minTargetSize)
1148  {
1149  const auto targetSize = pointsToGraph.tryGetNodeSize(targetPtgNode);
1150  if (targetSize && *targetSize < minTargetSize)
1151  return;
1152  }
1153  if (ENABLE_DEAD_ALLOCA_BLOCKLIST && allocasDead.Contains(targetPtgNode))
1154  return;
1155  ModRefSummary_->AddToModRefSet(modRefSetIndex, targetPtgNode);
1156  };
1157 
1158  // If the pointer is targeting everything external, add it all to the Mod/Ref set
1159  if (pointsToGraph.isTargetingAllExternallyAvailable(registerPtgNode))
1160  {
1161  for (const auto targetPtgNode : pointsToGraph.getExternallyAvailableNodes())
1162  {
1163  tryAddToModRefSet(targetPtgNode);
1164  }
1165  }
1166 
1167  for (const auto targetPtgNode : pointsToGraph.getExplicitTargets(registerPtgNode).Items())
1168  {
1169  tryAddToModRefSet(targetPtgNode);
1170  }
1171 }
1172 
1175  const rvsdg::SimpleNode & loadNode,
1176  const rvsdg::LambdaNode & lambda)
1177 {
1178  const auto nodeModRef = ModRefSummary_->GetOrCreateSetForNode(loadNode);
1179  const auto origin = LoadOperation::AddressInput(loadNode).origin();
1180  const auto loadOperation = util::assertedCast<const LoadOperation>(&loadNode.GetOperation());
1181  const auto loadSize = GetTypeStoreSize(*loadOperation->GetLoadedType());
1182 
1183  AddPointerOriginTargets(nodeModRef, *origin, loadSize, lambda);
1184  return nodeModRef;
1185 }
1186 
1189  const rvsdg::SimpleNode & storeNode,
1190  const rvsdg::LambdaNode & lambda)
1191 {
1192  const auto nodeModRef = ModRefSummary_->GetOrCreateSetForNode(storeNode);
1193  const auto origin = StoreOperation::AddressInput(storeNode).origin();
1194  const auto storeOperation = util::assertedCast<const StoreOperation>(&storeNode.GetOperation());
1195  const auto storeSize = GetTypeStoreSize(storeOperation->GetStoredType());
1196 
1197  AddPointerOriginTargets(nodeModRef, *origin, storeSize, lambda);
1198  return nodeModRef;
1199 }
1200 
1203 {
1204  const auto nodeModRef = ModRefSummary_->GetOrCreateSetForNode(allocaNode);
1205  const auto allocaMemoryNode = Context_->pointsToGraph.getNodeForAlloca(allocaNode);
1206  ModRefSummary_->AddToModRefSet(nodeModRef, allocaMemoryNode);
1207  return nodeModRef;
1208 }
1209 
1212 {
1213  const auto nodeModRef = ModRefSummary_->GetOrCreateSetForNode(mallocNode);
1214  const auto mallocMemoryNode = Context_->pointsToGraph.getNodeForMalloc(mallocNode);
1215  ModRefSummary_->AddToModRefSet(nodeModRef, mallocMemoryNode);
1216  return nodeModRef;
1217 }
1218 
1221  const rvsdg::SimpleNode & freeNode,
1222  const rvsdg::LambdaNode & lambda)
1223 {
1224  JLM_ASSERT(is<FreeOperation>(freeNode.GetOperation()));
1225 
1226  const auto nodeModRef = ModRefSummary_->GetOrCreateSetForNode(freeNode);
1227  const auto origin = FreeOperation::addressInput(freeNode).origin();
1228 
1229  // TODO: Only free MallocMemoryNodes
1230  AddPointerOriginTargets(nodeModRef, *origin, std::nullopt, lambda);
1231  return nodeModRef;
1232 }
1233 
1236  const rvsdg::SimpleNode & memcpyNode,
1237  const rvsdg::LambdaNode & lambda)
1238 {
1239  JLM_ASSERT(is<MemCpyOperation>(memcpyNode.GetOperation()));
1240 
1241  const auto nodeModRef = ModRefSummary_->GetOrCreateSetForNode(memcpyNode);
1242  const auto dstOrigin = MemCpyOperation::destinationInput(memcpyNode).origin();
1243  const auto srcOrigin = MemCpyOperation::sourceInput(memcpyNode).origin();
1244  const auto countOrigin = MemCpyOperation::countInput(memcpyNode).origin();
1245  const auto count = tryGetConstantSignedInteger(*countOrigin);
1246  AddPointerOriginTargets(nodeModRef, *dstOrigin, count, lambda);
1247  AddPointerOriginTargets(nodeModRef, *srcOrigin, count, lambda);
1248  return nodeModRef;
1249 }
1250 
1253  const rvsdg::SimpleNode & memsetNode,
1254  const rvsdg::LambdaNode & lambda)
1255 {
1256  JLM_ASSERT(is<MemSetOperation>(memsetNode.GetOperation()));
1257 
1258  const auto nodeModRef = ModRefSummary_->GetOrCreateSetForNode(memsetNode);
1259  const auto dstOrigin = MemSetOperation::destinationInput(memsetNode).origin();
1260  const auto lengthOrigin = MemSetOperation::lengthInput(memsetNode).origin();
1261  const auto numBytes = tryGetConstantSignedInteger(*lengthOrigin);
1262  AddPointerOriginTargets(nodeModRef, *dstOrigin, numBytes, lambda);
1263 
1264  return nodeModRef;
1265 }
1266 
1269  const rvsdg::SimpleNode & callNode,
1270  const rvsdg::LambdaNode & lambda)
1271 {
1272  JLM_ASSERT(is<CallOperation>(callNode.GetOperation()));
1273 
1274  const auto & pointsToGraph = Context_->pointsToGraph;
1275 
1276  // This ModRefSet represents everything the call may affect
1277  const auto callModRef = ModRefSummary_->GetOrCreateSetForNode(callNode);
1278 
1279  // Go over all possible targets of the call and add them to the call summary
1280  const auto targetPtr = callNode.input(0)->origin();
1281  const auto targetPtgNode = Context_->pointsToGraph.getNodeForRegister(*targetPtr);
1282 
1283  // Go through all locations the called function pointer may target
1284  for (const auto calleePtgNode : pointsToGraph.getExplicitTargets(targetPtgNode).Items())
1285  {
1286  const auto kind = pointsToGraph.getNodeKind(calleePtgNode);
1288  {
1289  const auto & calleeLambda = pointsToGraph.getLambdaForNode(calleePtgNode);
1290  const auto targetModRefSet = ModRefSummary_->GetOrCreateSetForNode(calleeLambda);
1291  AddModRefSimpleConstraint(targetModRefSet, callModRef);
1292  }
1293  else if (kind == PointsToGraph::NodeKind::ImportNode)
1294  {
1295  AddModRefSimpleConstraint(Context_->ExternalModRefIndex, callModRef);
1296  }
1297  }
1298  if (pointsToGraph.isTargetingAllExternallyAvailable(targetPtgNode))
1299  {
1300  AddModRefSimpleConstraint(Context_->ExternalModRefIndex, callModRef);
1301  }
1302 
1303  // Allocas that are live within the call, might no longer be live from the call site
1305  {
1306  JLM_ASSERT(ModRefSummary_->GetModRefSet(callModRef).IsEmpty());
1308  callModRef,
1309  Context_->AllocasDeadInScc[Context_->FunctionToSccIndex[&lambda]]);
1310  }
1311 
1312  return callModRef;
1313 }
1314 
1315 void
1317 {
1318  Context_->ModRefSetSimpleConstraints.resize(ModRefSummary_->NumModRefSets());
1320 
1321  // Start by pushing everything to the worklist
1322  for (ModRefSetIndex i = 0; i < ModRefSummary_->NumModRefSets(); i++)
1323  worklist.PushWorkItem(i);
1324 
1325  while (worklist.HasMoreWorkItems())
1326  {
1327  const auto workItem = worklist.PopWorkItem();
1328 
1329  // Handle all simple constraints workItem -> target
1330  for (auto target : Context_->ModRefSetSimpleConstraints[workItem].Items())
1331  {
1332  bool changed = false;
1333  if (auto blocklist = Context_->ModRefSetBlocklists.find(target);
1334  blocklist != Context_->ModRefSetBlocklists.end())
1335  {
1336  // The target has a blocklist, avoid propagating blocked MemoryNodes
1337  for (auto memoryNode : ModRefSummary_->GetModRefSet(workItem).Items())
1338  {
1339  if (blocklist->second->Contains(memoryNode))
1340  continue;
1341  changed |= ModRefSummary_->AddToModRefSet(target, memoryNode);
1342  }
1343  }
1344  else
1345  {
1346  // The target has no blocklist, propagate everything
1347  changed |= ModRefSummary_->PropagateModRefSet(workItem, target);
1348  }
1349 
1350  if (changed)
1351  worklist.PushWorkItem(target);
1352  }
1353  }
1354 
1356 }
1357 
1358 bool
1360 {
1361  // For all ModRefSets where a blocklist has been defined,
1362  // check that none of its MemoryNodes are on the blocklist
1363  for (auto [index, blocklist] : Context_->ModRefSetBlocklists)
1364  {
1365  for (auto memoryNode : ModRefSummary_->GetModRefSet(index).Items())
1366  {
1367  if (blocklist->Contains(memoryNode))
1368  return false;
1369  }
1370  }
1371  return true;
1372 }
1373 
1374 void
1376 {
1377  for (auto & functions : Context_->SccFunctions)
1378  {
1379  for (auto function : functions.Items())
1380  {
1381  compressExternalInFunction(*function);
1382  }
1383  }
1384 }
1385 
1386 void
1388 {
1389  // The set of ModRefSets that belong to this function
1390  // TODO: They could be placed in a map when created to avoid needing to find them again
1391  util::HashSet<ModRefSetIndex> modRefSets;
1392  findAllModRefSets(lambda, modRefSets);
1393 
1394  // The node representing all memory locations that are only known in external modules
1395  const auto externalMemoryNode = aa::PointsToGraph::externalMemoryNode;
1396 
1397  // Memory nodes that appear in modRefSets without the external memory node
1398  util::HashSet<PointsToGraph::NodeIndex> memoryNodesToKeep;
1399  for (auto modRefSetIndex : modRefSets.Items())
1400  {
1401  auto & modRefSet = ModRefSummary_->GetModRefSet(modRefSetIndex);
1402  if (modRefSet.Contains(externalMemoryNode))
1403  continue;
1404 
1405  for (auto memoryNode : modRefSet.Items())
1406  memoryNodesToKeep.insert(memoryNode);
1407  }
1408 
1409  // All memory nodes that are compressed are instead represented by external, which we keep
1410  memoryNodesToKeep.insert(externalMemoryNode);
1411 
1412  // Now go over again, removing all memory nodes that are not on the keep list
1413  for (auto modRefSetIndex : modRefSets.Items())
1414  {
1415  auto & modRefSet = ModRefSummary_->GetModRefSet(modRefSetIndex);
1416  if (!modRefSet.Contains(externalMemoryNode))
1417  {
1418  Context_->numLocalModRefSets++;
1419  Context_->numLocalModRefKept += modRefSet.Size();
1420  continue;
1421  }
1422 
1423  Context_->numExtModRefSets++;
1424  size_t removed = modRefSet.RemoveWhere(
1425  [&](PointsToGraph::NodeIndex node)
1426  {
1427  return !memoryNodesToKeep.Contains(node);
1428  });
1429  Context_->numExtModRefCompressed += removed;
1430  Context_->numExtModRefKept += modRefSet.Size();
1431  }
1432 }
1433 
1434 void
1436  const rvsdg::Node & node,
1437  util::HashSet<ModRefSetIndex> & modRefSets)
1438 {
1439  if (ModRefSummary_->HasSetForNode(node))
1440  {
1441  modRefSets.insert(ModRefSummary_->GetSetForNode(node));
1442  }
1443 
1444  // If the node is a structural node, recurse into its subregion(s)
1446  node,
1447  [&](const rvsdg::StructuralNode & structuralNode)
1448  {
1449  for (auto & subregion : structuralNode.Subregions())
1450  {
1451  for (auto & node : subregion.Nodes())
1452  {
1453  findAllModRefSets(node, modRefSets);
1454  }
1455  }
1456  });
1457 }
1458 
1459 std::string
1460 RegionAwareModRefSummarizer::CallGraphSCCsToString(const RegionAwareModRefSummarizer & summarizer)
1461 {
1462  std::ostringstream ss;
1463  for (size_t i = 0; i < summarizer.Context_->SccFunctions.size(); i++)
1464  {
1465  if (i != 0)
1466  ss << " <- ";
1467  ss << "[" << std::endl;
1468  if (i == summarizer.Context_->ExternalNodeSccIndex)
1469  {
1470  ss << " " << "<external>" << std::endl;
1471  }
1472  for (auto function : summarizer.Context_->SccFunctions[i].Items())
1473  {
1474  ss << " " << function->DebugString() << std::endl;
1475  }
1476  ss << "]";
1477  }
1478  return ss.str();
1479 }
1480 
1481 std::string
1482 RegionAwareModRefSummarizer::ToRegionTree(
1483  const rvsdg::Graph & rvsdg,
1484  const RegionAwareModRefSummary & modRefSummary)
1485 {
1486  const auto & pointsToGraph = modRefSummary.GetPointsToGraph();
1487 
1488  std::ostringstream ss;
1489 
1490  auto toString = [&](const util::HashSet<PointsToGraph::NodeIndex> & memoryNodes)
1491  {
1492  ss << "MemoryNodes: {";
1493  for (auto & memoryNode : memoryNodes.Items())
1494  {
1495  ss << pointsToGraph.getNodeDebugString(memoryNode);
1496  ss << ", ";
1497  }
1498  ss << "}" << std::endl;
1499  };
1500 
1501  auto indent = [&](size_t depth, char c = '-')
1502  {
1503  for (size_t i = 0; i < depth; i++)
1504  ss << c;
1505  };
1506 
1507  std::function<void(const rvsdg::Node &, size_t)> toRegionTree =
1508  [&](const rvsdg::Node & node, size_t depth)
1509  {
1510  if (!modRefSummary.HasSetForNode(node))
1511  return;
1512 
1513  auto modRefIndex = modRefSummary.GetSetForNode(node);
1514  auto & memoryNodes = modRefSummary.GetModRefSet(modRefIndex);
1515  ss << " ";
1516  toString(memoryNodes);
1517 
1518  if (auto structuralNode = dynamic_cast<const rvsdg::StructuralNode *>(&node))
1519  {
1520  for (auto & region : structuralNode->Subregions())
1521  {
1522  indent(depth + 1, '-');
1523  ss << "region" << std::endl;
1524  for (auto & n : region.Nodes())
1525  toRegionTree(n, depth + 2);
1526  }
1527  }
1528  };
1529 
1530  for (auto & node : rvsdg.GetRootRegion().Nodes())
1531  toRegionTree(node, 0);
1532 
1533  return ss.str();
1534 }
1535 
1536 std::unique_ptr<ModRefSummary>
1537 RegionAwareModRefSummarizer::Create(
1538  const rvsdg::RvsdgModule & rvsdgModule,
1539  const PointsToGraph & pointsToGraph,
1541 {
1542  RegionAwareModRefSummarizer summarizer;
1543  return summarizer.SummarizeModRefs(rvsdgModule, pointsToGraph, statisticsCollector);
1544 }
1545 
1546 std::unique_ptr<ModRefSummary>
1547 RegionAwareModRefSummarizer::Create(
1548  const rvsdg::RvsdgModule & rvsdgModule,
1549  const PointsToGraph & pointsToGraph)
1550 {
1552  return Create(rvsdgModule, pointsToGraph, statisticsCollector);
1553 }
1554 }
static jlm::util::StatisticsCollector statisticsCollector
static bool Contains(const jlm::llvm::InterProceduralGraphModule &module, const std::string &)
Definition: FNegTests.cpp:19
Call operation class.
Definition: call.hpp:251
static std::unique_ptr< CallTypeClassifier > ClassifyCall(const rvsdg::SimpleNode &callNode)
Classifies a call node.
Definition: call.cpp:49
static rvsdg::Input & addressInput(const rvsdg::Node &node) noexcept
Definition: operators.hpp:2538
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition: Load.hpp:75
static rvsdg::Input & destinationInput(const rvsdg::Node &node) noexcept
static rvsdg::Input & countInput(const rvsdg::Node &node) noexcept
static rvsdg::Input & sourceInput(const rvsdg::Node &node) noexcept
static rvsdg::Input & destinationInput(const rvsdg::Node &node) noexcept
static rvsdg::Input & lengthInput(const rvsdg::Node &node) noexcept
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
static constexpr NodeIndex externalMemoryNode
Statistics(const rvsdg::RvsdgModule &rvsdgModule, const PointsToGraph &pointsToGraph)
static std::unique_ptr< Statistics > Create(const rvsdg::RvsdgModule &rvsdgModule, const PointsToGraph &pointsToGraph)
void StopExternalCompactionStatistics(size_t numExtModRefSets, size_t numExtModRefCompressed, size_t numExtModRefKept, size_t numLocalModRefSets, size_t numLocalModRefKept)
void StopCreateNonReentrantAllocaSetsStatistics(size_t numNonReentrantAllocas)
void stopCallGraphStatistics(size_t numSccs, size_t numFunctionsCallingSetjmp)
ModRefSetIndex AnnotateStore(const rvsdg::SimpleNode &storeNode, const rvsdg::LambdaNode &lambda)
void findAllModRefSets(const rvsdg::Node &node, util::HashSet< ModRefSetIndex > &modRefSets)
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)
void compressExternalInFunction(const rvsdg::LambdaNode &lambda)
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
std::unique_ptr< ModRefSummary > SummarizeModRefs(const rvsdg::RvsdgModule &rvsdgModule, const PointsToGraph &pointsToGraph, util::StatisticsCollector &statisticsCollector) override
void createCallGraph(const rvsdg::RvsdgModule &rvsdgModule)
ModRefSetIndex AnnotateMemset(const rvsdg::SimpleNode &memsetNode, const rvsdg::LambdaNode &lambda)
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)
util::HashSet< PointsToGraph::NodeIndex > & GetModRefSet(ModRefSetIndex index)
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 Contains(const ItemType &item) const noexcept
Definition: HashSet.hpp:150
bool Remove(ItemType item)
Definition: HashSet.hpp:332
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
util::Timer & GetTimer(const std::string &name)
Definition: Statistics.cpp:137
util::Timer & AddTimer(std::string name)
Definition: Statistics.cpp:158
void AddMeasurement(std::string name, T value)
Definition: Statistics.hpp:177
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
static const bool ENABLE_EXTERNAL_COMPRESSION
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:62
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:785
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:226
static const char * NumRvsdgNodes
Definition: Statistics.hpp:213