Jlm
Loading...
Searching...
No Matches
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>
21#include <jlm/rvsdg/lambda.hpp>
25#include <jlm/util/common.hpp>
28#include <jlm/util/Worklist.hpp>
29
30#include <algorithm>
31#include <limits>
32#include <memory>
33#include <optional>
34#include <queue>
35#include <sstream>
36#include <unordered_map>
37
38namespace jlm::llvm::aa
39{
46 !std::getenv("JLM_DISABLE_NON_REENTRANT_ALLOCA_BLOCKLIST");
47
54 !std::getenv("JLM_DISABLE_OPERATION_SIZE_BLOCKING");
55
61 !std::getenv("JLM_DISABLE_CONSTANT_MEMORY_BLOCKING");
62
67static const bool ENABLE_READ_ONLY_DETECTION = !std::getenv("JLM_DISABLE_READ_ONLY_DETECTION");
68
74 !std::getenv("JLM_DISABLE_CALL_SIMPLE_ALLOCA_BLOCKING");
75
83{
84 static constexpr auto NumRvsdgRegionsLabel_ = "#RvsdgRegions";
85 static constexpr auto NumSimpleAllocas_ = "#SimpleAllocas";
86 static constexpr auto NumNonReentrantAllocas_ = "#NonReentrantAllocas";
87 static constexpr auto NumCallGraphSccs_ = "#CallGraphSccs";
88 static constexpr auto NumFunctionsCallingSetjmp_ = "#FunctionsCallingSetjmp";
89 static constexpr auto NumCallGraphSccsCanCallExternal_ = "#CallGraphSccsCanCallExternal";
90 static constexpr auto NumReadOnlyMemoryNodesDetectedLabel_ = "#ReadOnlyMemoryNodesDetected";
91
92 static constexpr auto NumModRefSetsMaterializedLabel_ = "#ModRefSetsMaterialized";
94 "ModRefSetSizeBeforeMaterialization";
95 static constexpr auto ModRefSetSizeAfterFilteringLabel_ = "ModRefSetSizeAfterFiltering";
96 static constexpr auto NumModRefSetsWithEffectOnExternalLabel_ = "#ModRefSetsWithEffectOnExternal";
98 "#ModRefSetsCallingExternalFunction";
100 "ModRefSetSizeAfterMaterialization";
101
102 static constexpr auto CallGraphTimer_ = "CallGraphTimer";
103 static constexpr auto AllocasDeadInSccsTimer_ = "AllocasDeadInSccsTimer";
104 static constexpr auto SimpleAllocasSetTimer_ = "SimpleAllocasSetTimer";
105 static constexpr auto NonReentrantAllocaSetsTimer_ = "NonReentrantAllocaSetsTimer";
106 static constexpr auto AnnotationTimer_ = "AnnotationTimer";
107 static constexpr auto SolvingTimer_ = "SolvingTimer";
108 static constexpr auto ReadOnlyDetectionTimer_ = "ReadOnlyDetectionTimer";
109 static constexpr auto ModRefSetMaterializationTimer_ = "ModRefSetMaterializationTimer";
110
111public:
112 ~Statistics() override = default;
113
114 explicit Statistics(const rvsdg::RvsdgModule & rvsdgModule, const PointsToGraph & pointsToGraph)
115 : util::Statistics(Id::RegionAwareModRefSummarizer, rvsdgModule.SourceFilePath().value())
116 {
117 AddMeasurement(Label::NumRvsdgNodes, rvsdg::nnodes(&rvsdgModule.Rvsdg().GetRootRegion()));
121 AddMeasurement(Label::NumPointsToGraphMemoryNodes, pointsToGraph.numMemoryNodes());
122 }
123
124 void
129
130 void
131 stopCallGraphStatistics(size_t numSccs, size_t numFunctionsCallingSetjmp)
132 {
135 AddMeasurement(NumFunctionsCallingSetjmp_, numFunctionsCallingSetjmp);
136 }
137
138 void
143
144 void
145 StopCreateSimpleAllocasSetStatistics(uint64_t numSimpleAllocas)
146 {
148 AddMeasurement(NumSimpleAllocas_, numSimpleAllocas);
149 }
150
151 void
156
157 void
158 StopCreateNonReentrantAllocaSetsStatistics(size_t numNonReentrantAllocas)
159 {
160 AddMeasurement(NumNonReentrantAllocas_, numNonReentrantAllocas);
162 }
163
164 void
169
170 void
175
176 void
181
182 void
187
188 void
193
194 void
195 stopReadOnlyMemoryDetectionStatistics(size_t numReadOnlyMemoryNodesDetected)
196 {
198 AddMeasurement(NumReadOnlyMemoryNodesDetectedLabel_, numReadOnlyMemoryNodesDetected);
199 }
200
201 void
206
207 void
209 size_t numModRefSetsMaterialized,
210 size_t modRefSetSizeBeforeMaterialization,
211 size_t modRefSetSizeAfterFiltering,
212 size_t numModRefSetsWithEffectOnExternal,
213 size_t numModRefSetsCallingExternalFunction,
214 size_t modRefSetSizeAfterMaterialization)
215 {
217 AddMeasurement(NumModRefSetsMaterializedLabel_, numModRefSetsMaterialized);
218 AddMeasurement(ModRefSetSizeBeforeMaterializationLabel_, modRefSetSizeBeforeMaterialization);
219 AddMeasurement(ModRefSetSizeAfterFilteringLabel_, modRefSetSizeAfterFiltering);
220 AddMeasurement(NumModRefSetsWithEffectOnExternalLabel_, numModRefSetsWithEffectOnExternal);
223 numModRefSetsCallingExternalFunction);
224 AddMeasurement(ModRefSetSizeAfterMaterializationLabel_, modRefSetSizeAfterMaterialization);
225 }
226
227 static std::unique_ptr<Statistics>
228 Create(const rvsdg::RvsdgModule & rvsdgModule, const PointsToGraph & pointsToGraph)
229 {
230 return std::make_unique<Statistics>(rvsdgModule, pointsToGraph);
231 }
232};
233
248class RegionAwareModRefSet final : public ModRefSet
249{
250public:
251 // The byte size used when no access to external is made
252 static constexpr uint32_t NoneSize = std::numeric_limits<uint32_t>::max();
253
255
265 [[nodiscard]] std::optional<size_t>
267 {
269 return std::nullopt;
270 return refExternalOfSize_;
271 }
272
282 [[nodiscard]] std::optional<size_t>
284 {
286 return std::nullopt;
287 return modExternalOfSize_;
288 }
289
295 bool
297 {
298 // Make sure we do not overflow or collide with the sentinel NoneSize
299 minSize = std::min<size_t>(minSize, NoneSize - 1);
300
301 if (minSize < refExternalOfSize_)
302 {
303 refExternalOfSize_ = minSize;
304 return true;
305 }
306 return false;
307 }
308
314 bool
316 {
317 // Make sure we do not overflow or collide with the sentinel NoneSize
318 minSize = std::min<size_t>(minSize, NoneSize - 1);
319
320 if (minSize < modExternalOfSize_)
321 {
322 modExternalOfSize_ = minSize;
323 return true;
324 }
325 return false;
326 }
327
334 [[nodiscard]] ModRefEffect
335 getImplicitModRefEffectForExternal(std::optional<size_t> size) const noexcept
336 {
339 {
340 if (size.value_or(NoneSize) >= refExternalOfSize_)
341 result |= ModRefEffect::RefOnly;
342 }
344 {
345 if (size.value_or(NoneSize) >= modExternalOfSize_)
346 result |= ModRefEffect::ModOnly;
347 }
348 return result;
349 }
350
354 [[nodiscard]] bool
356 {
358 }
359
364 bool
366 {
368 return false;
369
371
372 // calls to external functions may reference and modify any externally available memory
375 return true;
376 }
377
386 bool
388 {
389 JLM_ASSERT(modRefEffect != ModRefEffect::NoEffect);
390
391 const auto [it, inserted] = modRefNodes_.insert({ memoryNode, modRefEffect });
392 if (inserted)
393 return true;
394
395 // The memory node was already present, but we may add more effects
396 auto oldEffects = it->second;
397 it->second = oldEffects | modRefEffect;
398 return it->second != oldEffects;
399 }
400
411 bool
413 PointsToGraph::NodeIndex memoryNode,
414 bool isExternallyAvailable,
415 std::optional<size_t> memoryNodeSize,
416 ModRefEffect modRefEffect)
417 {
418 JLM_ASSERT(modRefEffect != ModRefEffect::NoEffect);
419
420 // If the effects on the node are already encoded implicitly, skip adding them explicitly
421 if (isExternallyAvailable)
422 {
423 const auto implicitEffect = getImplicitModRefEffectForExternal(memoryNodeSize);
424 if (isEffectSubset(modRefEffect, implicitEffect))
425 return false;
426 }
427
428 return addExplicitMemoryNode(memoryNode, modRefEffect);
429 }
430
434 void
436 {
437 auto it = modRefNodes_.begin();
438 while (it != modRefNodes_.end())
439 {
440 if (filter.Contains(it->first))
441 ++it;
442 else
443 it = modRefNodes_.erase(it);
444 }
445 }
446
451 bool
453 {
454 // Check the most restrictive flag first
455 if (other.callsExternalFunction_)
456 {
458 return false;
459
461 JLM_ASSERT(other.refExternalOfSize_ == 0);
462 JLM_ASSERT(other.modExternalOfSize_ == 0);
465 return true;
466 }
467
469 {
472 return true;
473 }
474
476 {
478 return true;
479 }
480
481 return false;
482 }
483
484private:
485 // If not equal to NoneSize, the ModRefSet potentially reads all externally available memory
486 // of size >= the given number of bytes.
488 // If not equal to NoneSize, the ModRefSet potentially modifies all externally available memory
489 // of size >= the given number of bytes.
491 // If true, the ModRefSet represents possibly calling externally defined functions.
492 // In this case, \ref refExternalOfSize and \ref modExternalOfSize must both be 0
494};
495
499{
500public:
501 explicit RegionAwareModRefSummary(const PointsToGraph & pointsToGraph)
502 : pointsToGraph_(pointsToGraph)
503 {
504 // Create the ModRefSet representing eveything that can be referenced and modified,
505 // directly or indirectly, from external functions.
507 // The ModRefSet representing external functions can call external functions
509 }
510
514
515 [[nodiscard]] const PointsToGraph &
516 GetPointsToGraph() const noexcept override
517 {
518 return pointsToGraph_;
519 }
520
521 [[nodiscard]] size_t
522 NumModRefSets() const noexcept
523 {
524 return modRefSets_.size();
525 }
526
527 [[nodiscard]] RegionAwareModRefSet &
529 {
530 JLM_ASSERT(index < modRefSets_.size());
531 return modRefSets_[index];
532 }
533
534 [[nodiscard]] const RegionAwareModRefSet &
536 {
537 JLM_ASSERT(index < modRefSets_.size());
538 return modRefSets_[index];
539 }
540
541 bool
543 {
544 return getModRefSet(index).markAsReferencingExternal(minSize);
545 }
546
547 bool
549 {
550 return getModRefSet(index).markAsModifyingExternal(minSize);
551 }
552
553 bool
558
568 bool
570 ModRefSetIndex index,
572 ModRefEffect modRefEffect)
573 {
574 const bool isExternallyAvailable = pointsToGraph_.isExternallyAvailable(ptgNode);
575 const std::optional<size_t> memoryNodeSize = pointsToGraph_.tryGetNodeSize(ptgNode);
576 return getModRefSet(index)
577 .addMemoryNode(ptgNode, isExternallyAvailable, memoryNodeSize, modRefEffect);
578 }
579
589 bool
591 ModRefSetIndex index,
593 ModRefEffect modRefEffect)
594 {
595 return getModRefSet(index).addExplicitMemoryNode(ptgNode, modRefEffect);
596 }
597
609 [[nodiscard]] ModRefSetIndex
610 getExternModRefSet() const noexcept
611 {
612 return externModRefSet_;
613 }
614
615 [[nodiscard]] bool
616 hasSetForNode(const rvsdg::Node & node) const
617 {
618 return nodeMap_.find(&node) != nodeMap_.end();
619 }
620
621 [[nodiscard]] ModRefSetIndex
622 getSetForNode(const rvsdg::Node & node) const
623 {
624 const auto it = nodeMap_.find(&node);
625 JLM_ASSERT(it != nodeMap_.end());
626 return it->second;
627 }
628
635 [[nodiscard]] ModRefSetIndex
636 getOrCreateSetForNode(const rvsdg::Node & node, const rvsdg::LambdaNode & lambdaNode)
637 {
638 auto [it, inserted] = nodeMap_.insert({ &node, 0 });
639 if (inserted)
640 {
641 const auto created = createModRefSet();
642 it->second = created;
643 modRefSetsInFunction_[&lambdaNode].push_back(created);
644 return created;
645 }
646
647 return it->second;
648 }
649
650 [[nodiscard]] const std::vector<ModRefSetIndex> &
652 {
653 return modRefSetsInFunction_[&lambdaNode];
654 }
655
656 [[nodiscard]] std::string
658 {
659 const auto & modRefSet = getModRefSet(index);
660 std::stringstream ss;
661 ss << "{MRS#" << index << "; ";
662 const auto mayRefMinSize = modRefSet.getRefExternalMinSize();
663 if (mayRefMinSize.has_value())
664 ss << "RefExt>=" << *mayRefMinSize << " bytes; ";
665 const auto mayModMinSize = modRefSet.getModExternalMinSize();
666 if (mayModMinSize.has_value())
667 ss << "ModExt>=" << *mayModMinSize << " bytes; ";
668 if (modRefSet.mayCallExternalFunction())
669 ss << "MayCallExt; ";
670
671 bool first = true;
672 for (auto [memoryNode, modRefEffect] : modRefSet.getModRefNodes())
673 {
674 if (first)
675 first = false;
676 else
677 ss << ", ";
678
679 ss << pointsToGraph_.getNodeDebugString(memoryNode);
680 switch (modRefEffect)
681 {
683 JLM_UNREACHABLE("Memory nodes should never be added with NoEffect");
685 ss << "[R]";
686 break;
688 ss << "[M]";
689 break;
691 ss << "[MR]";
692 break;
693 }
694 }
695 ss << "}";
696 return ss.str();
697 }
698
699 const ModRefSet &
700 GetSimpleNodeModRef(const rvsdg::SimpleNode & node) const override
701 {
702 return modRefSets_[getSetForNode(node)];
703 }
704
705 const ModRefSet &
706 GetGammaEntryModRef(const rvsdg::GammaNode & gamma) const override
707 {
708 return modRefSets_[getSetForNode(gamma)];
709 }
710
711 const ModRefSet &
712 GetGammaExitModRef(const rvsdg::GammaNode & gamma) const override
713 {
714 return GetGammaEntryModRef(gamma);
715 }
716
717 const ModRefSet &
718 GetThetaModRef(const rvsdg::ThetaNode & theta) const override
719 {
720 return modRefSets_[getSetForNode(theta)];
721 }
722
723 const ModRefSet &
724 GetLambdaEntryModRef(const rvsdg::LambdaNode & lambda) const override
725 {
726 return modRefSets_[getSetForNode(lambda)];
727 }
728
729 const ModRefSet &
730 GetLambdaExitModRef(const rvsdg::LambdaNode & lambda) const override
731 {
732 return GetLambdaEntryModRef(lambda);
733 }
734
735 [[nodiscard]] static std::unique_ptr<RegionAwareModRefSummary>
736 Create(const PointsToGraph & pointsToGraph)
737 {
738 return std::make_unique<RegionAwareModRefSummary>(pointsToGraph);
739 }
740
741private:
742 [[nodiscard]] ModRefSetIndex
744 {
745 modRefSets_.emplace_back();
746 return modRefSets_.size() - 1;
747 }
748
750
754 std::vector<RegionAwareModRefSet> modRefSets_;
755
762
766 std::unordered_map<const rvsdg::LambdaNode *, std::vector<ModRefSetIndex>> modRefSetsInFunction_;
767
773 std::unordered_map<const rvsdg::Node *, ModRefSetIndex> nodeMap_;
774};
775
780{
781 explicit Context(const PointsToGraph & ptg)
782 : pointsToGraph(ptg)
783 {}
784
789
800 std::vector<util::HashSet<const rvsdg::LambdaNode *>> SccFunctions;
801
808
816 std::vector<util::HashSet<size_t>> SccCallTargets;
817
823 std::unordered_map<const rvsdg::LambdaNode *, size_t> FunctionToSccIndex;
824
830
837
844 std::unordered_map<const rvsdg::Node *, util::HashSet<PointsToGraph::NodeIndex>>
846
852 std::deque<util::HashSet<PointsToGraph::NodeIndex>> CallBlocklists;
853
859 std::vector<util::HashSet<ModRefSetIndex>> ModRefSetSimpleConstraints;
860
867 std::unordered_map<ModRefSetIndex, const util::HashSet<PointsToGraph::NodeIndex> *>
869
877
907};
908
910
912
913std::unique_ptr<ModRefSummary>
915 const rvsdg::RvsdgModule & rvsdgModule,
916 const PointsToGraph & pointsToGraph,
917 util::StatisticsCollector & statisticsCollector)
918{
919 ModRefSummary_ = RegionAwareModRefSummary::Create(pointsToGraph);
920 Context_ = std::make_unique<Context>(pointsToGraph);
921 auto statistics = Statistics::Create(rvsdgModule, pointsToGraph);
922
923 statistics->startCallGraphStatistics();
924 createCallGraph(rvsdgModule);
925 statistics->stopCallGraphStatistics(
926 Context_->SccFunctions.size(),
927 Context_->FunctionsCallingSetjmp.Size());
928
929 statistics->StartCreateSimpleAllocasSetStatistics();
930 Context_->SimpleAllocas = CreateSimpleAllocaSet(pointsToGraph);
931 statistics->StopCreateSimpleAllocasSetStatistics(Context_->SimpleAllocas.Size());
932
933 statistics->StartCreateNonReentrantAllocaSetsStatistics();
934 auto numNonReentrantAllocas = CreateNonReentrantAllocaSets();
935 statistics->StopCreateNonReentrantAllocaSetsStatistics(numNonReentrantAllocas);
936
937 statistics->StartAnnotationStatistics();
938 // Go through and recursively annotate all functions, regions and nodes
939 for (const auto & scc : Context_->SccFunctions)
940 {
941 for (const auto lambda : scc.Items())
942 {
943 AnnotateFunction(*lambda);
944 }
945 }
946 statistics->StopAnnotationStatistics();
947
948 statistics->StartSolvingStatistics();
949 SolveModRefSetConstraintGraph();
950 statistics->StopSolvingStatistics();
951
952 // Print debug output
953 // std::cerr << PointsToGraph::dumpDot(pointsToGraph) << std::endl;
954 // std::cerr << "numSimpleAllocas: " << Context_->SimpleAllocas.Size() << std::endl;
955 // std::cerr << "numNonReentrantAllocas: " << numNonReentrantAllocas << std::endl;
956 // std::cerr << "Call Graph SCCs:" << std::endl << CallGraphSCCsToString(*this) << std::endl;
957 // std::cerr << "After solving, before materialization: " << std::endl;
958 // std::cerr << ToRegionTree(rvsdgModule.Rvsdg(), *ModRefSummary_) << std::endl;
959
961 {
962 statistics->startReadOnlyMemoryDetectionStatistics();
963 determineReadOnlyMemory();
964 statistics->stopReadOnlyMemoryDetectionStatistics(Context_->ReadOnlyMemoryNodes.Size());
965 }
966
967 statistics->startModRefSetMaterializationStatistics();
968 materializeSets();
969 statistics->stopModRefSetMaterializationStatistics(
970 Context_->numModRefSetsMaterialized,
971 Context_->modRefSetSizeBeforeMaterialization,
972 Context_->modRefSetSizeAfterFiltering,
973 Context_->numModRefSetsWithEffectOnExternal,
974 Context_->numModRefSetsCallingExternalFunction,
975 Context_->modRefSetSizeAfterMaterialization);
976
977 // More debug output
978 // std::cerr << "ReadOnlyMemoryNodes (PtGIndex): ";
979 // for (auto ptgNodeId : Context_->ReadOnlyMemoryNodes.Items())
980 // std::cerr << ptgNodeId << ", ";
981 // std::cerr << "After materialization: " << std::endl;
982 // std::cerr << ToRegionTree(rvsdgModule.Rvsdg(), *ModRefSummary_) << std::endl;
983
984 statisticsCollector.CollectDemandedStatistics(std::move(statistics));
985 Context_.reset();
986 return std::move(ModRefSummary_);
987}
988
994static std::vector<const rvsdg::LambdaNode *>
996{
997 std::vector<const rvsdg::LambdaNode *> result;
998
999 // Recursively traverses all structural nodes, but does not enter into lambdas
1000 const std::function<void(rvsdg::Region &)> CollectLambdasInRegion =
1001 [&](rvsdg::Region & region) -> void
1002 {
1003 for (auto & node : region.Nodes())
1004 {
1005 if (auto lambda = dynamic_cast<rvsdg::LambdaNode *>(&node))
1006 {
1007 result.push_back(lambda);
1008 }
1009 else if (auto structural = dynamic_cast<rvsdg::StructuralNode *>(&node))
1010 {
1011 for (size_t i = 0; i < structural->nsubregions(); i++)
1012 {
1013 CollectLambdasInRegion(*structural->subregion(i));
1014 }
1015 }
1016 }
1017 };
1018
1019 CollectLambdasInRegion(rvsdgModule.Rvsdg().GetRootRegion());
1020
1021 return result;
1022}
1023
1024void
1026{
1027 const auto & pointsToGraph = Context_->pointsToGraph;
1028
1029 // The list of lambdas becomes the list of nodes in the call graph
1030 auto lambdaNodes = CollectLambdaNodes(rvsdgModule);
1031
1032 // Mapping from LambdaNode* to its index in lambdaNodes
1033 std::unordered_map<const rvsdg::LambdaNode *, size_t> callGraphNodeIndex;
1034 callGraphNodeIndex.reserve(lambdaNodes.size());
1035 for (size_t i = 0; i < lambdaNodes.size(); i++)
1036 {
1037 callGraphNodeIndex.insert({ lambdaNodes[i], i });
1038 }
1039
1040 // Add a dummy node representing all external functions, with no associated LambdaNode
1041 const auto externalNodeIndex = lambdaNodes.size();
1042 const auto numCallGraphNodes = externalNodeIndex + 1;
1043
1044 // Outgoing edges for each node in the call graph, indexed by position in lambdaNodes
1045 std::vector<util::HashSet<size_t>> callGraphSuccessors(numCallGraphNodes);
1046
1047 // Add outgoing edges from the given caller to any function the call may target
1048 const auto handleCall = [&](const rvsdg::SimpleNode & callNode, size_t callerIndex) -> void
1049 {
1050 const auto classification = CallOperation::ClassifyCall(callNode);
1051 if (classification->isSetjmpCall())
1052 {
1053 Context_->FunctionsCallingSetjmp.insert(lambdaNodes[callerIndex]);
1054 return;
1055 }
1056
1057 const auto target = callNode.input(0)->origin();
1058 const auto targetPtgNode = pointsToGraph.getNodeForRegister(*target);
1059
1060 // Go through all locations the called function pointer may target
1061 for (const auto calleePtgNode : pointsToGraph.getExplicitTargets(targetPtgNode).Items())
1062 {
1063 const auto kind = pointsToGraph.getNodeKind(calleePtgNode);
1065 {
1066 const auto & lambdaNode = pointsToGraph.getLambdaForNode(calleePtgNode);
1067
1068 // Look up which call graph node represents the target lambda
1069 JLM_ASSERT(callGraphNodeIndex.find(&lambdaNode) != callGraphNodeIndex.end());
1070 const auto calleeCallGraphNode = callGraphNodeIndex[&lambdaNode];
1071
1072 // Add the edge caller -> callee to the call graph
1073 callGraphSuccessors[callerIndex].insert(calleeCallGraphNode);
1074 }
1075 else if (kind == PointsToGraph::NodeKind::ImportNode)
1076 {
1077 // Add the edge caller -> node representing external functions
1078 callGraphSuccessors[callerIndex].insert(externalNodeIndex);
1079 }
1080 }
1081
1082 if (pointsToGraph.isTargetingAllExternallyAvailable(targetPtgNode))
1083 {
1084 // If the call target pointer is flagged, add an edge to external functions
1085 callGraphSuccessors[callerIndex].insert(externalNodeIndex);
1086 }
1087 };
1088
1089 // Recursive function finding all call operations, adding edges to the call graph
1090 const std::function<void(const rvsdg::Region &, size_t)> handleCalls =
1091 [&](const rvsdg::Region & region, size_t callerIndex) -> void
1092 {
1093 for (auto & node : region.Nodes())
1094 {
1095 if (const auto [callNode, callOp] = rvsdg::TryGetSimpleNodeAndOptionalOp<CallOperation>(node);
1096 callOp)
1097 {
1098 handleCall(*callNode, callerIndex);
1099 }
1100
1102 node,
1103 [&](const rvsdg::StructuralNode & structural)
1104 {
1105 for (auto & subregion : structural.Subregions())
1106 {
1107 handleCalls(subregion, callerIndex);
1108 }
1109 });
1110 }
1111 };
1112
1113 // For all functions, visit all their calls and add outgoing edges in the call graph
1114 for (size_t i = 0; i < lambdaNodes.size(); i++)
1115 {
1116 handleCalls(*lambdaNodes[i]->subregion(), i);
1117
1118 // If the function has escaped, add an edge from the node representing all external functions
1119 if (pointsToGraph.isExternallyAvailable(pointsToGraph.getNodeForLambda(*lambdaNodes[i])))
1120 {
1121 callGraphSuccessors[externalNodeIndex].insert(i);
1122 }
1123 }
1124
1125 // Finally, add the fact that the external node may call itself
1126 callGraphSuccessors[externalNodeIndex].insert(externalNodeIndex);
1127
1128 // Used by the implementation of Tarjan's SCC algorithm
1129 const auto getSuccessors = [&](size_t nodeIndex)
1130 {
1131 return callGraphSuccessors[nodeIndex].Items();
1132 };
1133
1134 // Find SCCs in the call graph
1135 std::vector<size_t> sccIndex;
1136 std::vector<size_t> reverseTopologicalOrder;
1137 auto numSCCs = util::FindStronglyConnectedComponents<size_t>(
1138 numCallGraphNodes,
1139 getSuccessors,
1140 sccIndex,
1141 reverseTopologicalOrder);
1142
1143 // sccIndex are distributed in a reverse topological order, so the sccIndex is used
1144 // when creating the list of SCCs and the functions they contain
1145 Context_->SccFunctions.resize(numSCCs);
1146 for (size_t i = 0; i < lambdaNodes.size(); i++)
1147 {
1148 Context_->SccFunctions[sccIndex[i]].insert(lambdaNodes[i]);
1149 Context_->FunctionToSccIndex[lambdaNodes[i]] = sccIndex[i];
1150 }
1151
1152 // Add edges between the SCCs for all calls
1153 Context_->SccCallTargets.resize(numSCCs);
1154 for (size_t i = 0; i < numCallGraphNodes; i++)
1155 {
1156 for (auto target : callGraphSuccessors[i].Items())
1157 {
1158 Context_->SccCallTargets[sccIndex[i]].insert(sccIndex[target]);
1159 }
1160 }
1161
1162 // Also note which SCC contains all external functions
1163 Context_->ExternalNodeSccIndex = sccIndex[externalNodeIndex];
1164}
1165
1168{
1169 // The set of allocas that are simple. Starts off as an over-approximation
1171 // A queue used to visit all PtG memory nodes that are not simple allocas
1172 std::queue<PointsToGraph::NodeIndex> notSimple;
1173
1174 for (PointsToGraph::NodeIndex ptgNode = 0; ptgNode < pointsToGraph.numNodes(); ptgNode++)
1175 {
1176 // Only memory nodes are relevant
1177 if (!pointsToGraph.isMemoryNode(ptgNode))
1178 continue;
1179
1180 // Allocas that are not externally available start of as presumed simple
1181 if (pointsToGraph.getNodeKind(ptgNode) == PointsToGraph::NodeKind::AllocaNode
1182 && !pointsToGraph.isExternallyAvailable(ptgNode))
1183 simpleAllocas.insert(ptgNode);
1184 else
1185 notSimple.push(ptgNode);
1186 }
1187
1188 // Process the queue to visit all memory nodes that may disqualify allocas from being simple
1189 while (!notSimple.empty())
1190 {
1191 const auto ptgNode = notSimple.front();
1192 notSimple.pop();
1193
1194 // Any node targeted by the not-simple memory node can themselves not be simple
1195 for (const auto targetPtgNode : pointsToGraph.getExplicitTargets(ptgNode).Items())
1196 {
1197 // If the target is currently in the simple allocas candiate set, move it to the queue
1198 if (simpleAllocas.Remove(targetPtgNode))
1199 notSimple.push(targetPtgNode);
1200 }
1201 }
1202
1203 return simpleAllocas;
1204}
1205
1208{
1209 const auto & pointsToGraph = Context_->pointsToGraph;
1210
1211 util::HashSet<PointsToGraph::NodeIndex> reachableSimpleAllocas;
1212 // Traverse along PointsToGraph edges to find all reachable simple allocas
1213 while (!nodes.empty())
1214 {
1215 const auto ptgNode = nodes.front();
1216 nodes.pop();
1217
1218 for (const auto targetPtgNode : pointsToGraph.getExplicitTargets(ptgNode).Items())
1219 {
1220 // We only are about following simple allocas, as simple allocas are only reachable from them.
1221 if (!Context_->SimpleAllocas.Contains(targetPtgNode))
1222 continue;
1223
1224 if (reachableSimpleAllocas.insert(targetPtgNode))
1225 nodes.push(targetPtgNode);
1226 }
1227 }
1228
1229 return reachableSimpleAllocas;
1230}
1231
1234 const rvsdg::Region & region)
1235{
1236 const auto & pointsToGraph = Context_->pointsToGraph;
1237
1238 // Start by finding initial register nodes
1239 std::queue<PointsToGraph::NodeIndex> nodes;
1240 for (auto argument : region.Arguments())
1241 {
1242 if (!IsPointerCompatible(*argument))
1243 continue;
1244 const auto ptgNode = pointsToGraph.getNodeForRegister(*argument);
1245 nodes.push(ptgNode);
1246 }
1247
1249}
1250
1253 const rvsdg::SimpleNode & call)
1254{
1255 const auto & pointsToGraph = Context_->pointsToGraph;
1256
1257 // Use a queue and a set to traverse the PointsToGraph
1258 std::queue<PointsToGraph::NodeIndex> nodes;
1259 auto numArguments = CallOperation::NumArguments(call);
1260 for (size_t i = 0; i < numArguments; i++)
1261 {
1262 const auto & argument = *CallOperation::Argument(call, i)->origin();
1263
1264 if (!IsPointerCompatible(argument))
1265 continue;
1266 const auto ptgNode = pointsToGraph.getNodeForRegister(argument);
1267 nodes.push(ptgNode);
1268 }
1269
1271}
1272
1273bool
1275{
1276 const auto scc = Context_->FunctionToSccIndex[&lambda];
1277 return Context_->SccCallTargets[scc].Contains(scc);
1278}
1279
1280size_t
1282{
1283 const auto & pointsToGraph = Context_->pointsToGraph;
1284
1285 // Caching the sets of simple allocas reachable from region arguments
1286 std::unordered_map<const rvsdg::Region *, util::HashSet<PointsToGraph::NodeIndex>>
1287 reachableSimpleAllocas;
1288
1289 // Returns the set of simple allocas reachable from the region's arguments
1290 const auto getReachableSimpleAllocas =
1291 [&](const rvsdg::Region & region) -> const util::HashSet<PointsToGraph::NodeIndex> &
1292 {
1293 if (const auto it = reachableSimpleAllocas.find(&region); it != reachableSimpleAllocas.end())
1294 {
1295 return it->second;
1296 }
1297 return reachableSimpleAllocas[&region] = getSimpleAllocasReachableFromRegionArguments(region);
1298 };
1299
1300 // Checks if the simple alloca represented by the given points-to graph node is non-reentrant
1301 const auto isNonReentrant = [&](PointsToGraph::NodeIndex simpleAllocaPtgNode) -> bool
1302 {
1303 auto & allocaNode = pointsToGraph.getAllocaForNode(simpleAllocaPtgNode);
1304 const auto & region = *allocaNode.region();
1305
1306 // If the alloca's function is never involved in any recursion,
1307 // the alloca is trivially non-reentrant.
1308 const auto & lambda = getSurroundingLambdaNode(allocaNode);
1309 if (!IsRecursionPossible(lambda))
1310 return true;
1311
1312 // In lambdas where recursion is possible, simple allocas that are reachable from
1313 // region arguments via edges in the points-to graph must be considered reentrant.
1314 if (getReachableSimpleAllocas(region).Contains(simpleAllocaPtgNode))
1315 return false;
1316
1317 // Otherwise the simple alloca is non-reentrant
1318 return true;
1319 };
1320
1321 size_t numNonReentrantAllocas = 0;
1322
1323 // Only simple allocas are candidates for being non-reentrant
1324 for (auto simpleAllocaPtgNode : Context_->SimpleAllocas.Items())
1325 {
1326 if (!isNonReentrant(simpleAllocaPtgNode))
1327 continue;
1328
1329 const auto & region = *pointsToGraph.getAllocaForNode(simpleAllocaPtgNode).region();
1330 const auto structuralNode = region.node();
1331 // Creates a set for the structural node if it does not already have one, and add the alloca
1332 Context_->NonReentrantAllocas[structuralNode].insert(simpleAllocaPtgNode);
1333 numNonReentrantAllocas++;
1334 }
1335
1336 return numNonReentrantAllocas;
1337}
1338
1339void
1341{
1342 // We should never add outgoing edges from the set representing all external functions
1343 JLM_ASSERT(from != ModRefSummary_->getExternModRefSet());
1344 // Ensure the constraint vector is large enough
1345 Context_->ModRefSetSimpleConstraints.resize(ModRefSummary_->NumModRefSets());
1346 Context_->ModRefSetSimpleConstraints[from].insert(to);
1347}
1348
1349void
1351 ModRefSetIndex index,
1353{
1354 JLM_ASSERT(Context_->ModRefSetBlocklists.find(index) == Context_->ModRefSetBlocklists.end());
1355 Context_->ModRefSetBlocklists[index] = &blocklist;
1356}
1357
1358void
1360{
1361 const auto modRefSet = AnnotateStructuralNode(lambda, lambda);
1362
1363 if (Context_->FunctionsCallingSetjmp.Contains(&lambda))
1364 {
1365 // If this function can be jumped into, store operations on memory in its Mod/Ref set must be
1366 // sequentialized with calls to external functions, in case the trigger jumps
1367 // TODO: This edge could in theory only propagate Mod info, and turn it into Ref info,
1368 // since calls to longjmp only need to be sequentialized with stores
1369 AddModRefSimpleConstraint(modRefSet, ModRefSummary_->getExternModRefSet());
1370 }
1371
1372 // If the function is externally available, it can be called by external functions,
1373 // so add a simple edge to the ModRefSet representing all external functions.
1374 const auto lambdaPtgNode = Context_->pointsToGraph.getNodeForLambda(lambda);
1375 if (Context_->pointsToGraph.isExternallyAvailable(lambdaPtgNode))
1376 {
1377 AddModRefSimpleConstraint(modRefSet, ModRefSummary_->getExternModRefSet());
1378 }
1379}
1380
1381void
1383 const rvsdg::Region & region,
1384 ModRefSetIndex modRefSet,
1385 const rvsdg::LambdaNode & lambda)
1386{
1387 for (auto & node : region.Nodes())
1388 {
1390 node,
1391 [&](const rvsdg::StructuralNode & structuralNode)
1392 {
1393 const auto nodeModRefSet = AnnotateStructuralNode(structuralNode, lambda);
1394 AddModRefSimpleConstraint(nodeModRefSet, modRefSet);
1395 },
1396 [&](const rvsdg::SimpleNode & simpleNode)
1397 {
1398 if (const auto nodeModRefSet = AnnotateSimpleNode(simpleNode, lambda))
1399 AddModRefSimpleConstraint(*nodeModRefSet, modRefSet);
1400 });
1401 }
1402}
1403
1406 const rvsdg::StructuralNode & structuralNode,
1407 const rvsdg::LambdaNode & lambda)
1408{
1409 // The ModRefSet of a structural node is the same as that of its subregion(s)
1410 const auto modRefSet = ModRefSummary_->getOrCreateSetForNode(structuralNode, lambda);
1411
1412 for (auto & subregion : structuralNode.Subregions())
1413 {
1414 AnnotateRegion(subregion, modRefSet, lambda);
1415 }
1416
1417 // Check if this node has any non-reentrant allocas. If so, block them from leaving the node
1418 if (const auto it = Context_->NonReentrantAllocas.find(&structuralNode);
1419 it != Context_->NonReentrantAllocas.end() && ENABLE_NON_REENTRANT_ALLOCA_BLOCKLIST)
1420 {
1421 AddModRefSetBlocklist(modRefSet, it->second);
1422 }
1423
1424 return modRefSet;
1425}
1426
1427std::optional<ModRefSetIndex>
1429 const rvsdg::SimpleNode & simpleNode,
1430 const rvsdg::LambdaNode & lambda)
1431{
1432 return MatchTypeWithDefault(
1433 simpleNode.GetOperation(),
1434 [&](const LoadOperation &) -> std::optional<ModRefSetIndex>
1435 {
1436 return AnnotateLoad(simpleNode, lambda);
1437 },
1438 [&](const StoreOperation &) -> std::optional<ModRefSetIndex>
1439 {
1440 return AnnotateStore(simpleNode, lambda);
1441 },
1442 [&](const AllocaOperation &) -> std::optional<ModRefSetIndex>
1443 {
1444 return AnnotateAlloca(simpleNode, lambda);
1445 },
1446 [&](const MallocOperation &) -> std::optional<ModRefSetIndex>
1447 {
1448 return AnnotateMalloc(simpleNode, lambda);
1449 },
1450 [&](const FreeOperation &) -> std::optional<ModRefSetIndex>
1451 {
1452 return AnnotateFree(simpleNode, lambda);
1453 },
1454 [&](const MemCpyOperation &) -> std::optional<ModRefSetIndex>
1455 {
1456 return AnnotateMemcpy(simpleNode, lambda);
1457 },
1458 [&](const MemSetOperation &) -> std::optional<ModRefSetIndex>
1459 {
1460 return AnnotateMemset(simpleNode, lambda);
1461 },
1462 [&](const MemMoveOperation &) -> std::optional<ModRefSetIndex>
1463 {
1464 return AnnotateMemmove(simpleNode, lambda);
1465 },
1466 [&](const CallOperation &) -> std::optional<ModRefSetIndex>
1467 {
1468 return AnnotateCall(simpleNode, lambda);
1469 },
1470 [&](const MemoryStateOperation &) -> std::optional<ModRefSetIndex>
1471 {
1472 // MemoryStateOperations are only used to route memory states, and can be ignored
1473 return std::nullopt;
1474 },
1475 [&]() -> std::optional<ModRefSetIndex>
1476 {
1477 // Any remaining type of node should not involve any memory states
1478 JLM_ASSERT(!hasMemoryState(simpleNode));
1479 return std::nullopt;
1480 });
1481}
1482
1483void
1485 ModRefSetIndex modRefSetIndex,
1486 const rvsdg::Output & origin,
1487 std::optional<size_t> minTargetSize,
1488 ModRefEffect modRefEffect)
1489{
1490 const auto & pointsToGraph = Context_->pointsToGraph;
1491
1492 const auto registerPtgNode = pointsToGraph.getNodeForRegister(origin);
1493
1494 const auto tryAddToModRefSet = [&](PointsToGraph::NodeIndex targetPtgNode)
1495 {
1496 if (ENABLE_CONSTANT_MEMORY_BLOCKING && pointsToGraph.isNodeConstant(targetPtgNode))
1497 return;
1498 if (ENABLE_OPERATION_SIZE_BLOCKING && minTargetSize)
1499 {
1500 const auto targetSize = pointsToGraph.tryGetNodeSize(targetPtgNode);
1501 if (targetSize.has_value() && *targetSize < minTargetSize)
1502 return;
1503 }
1504 ModRefSummary_->addExplicitMemoryNodeToSet(modRefSetIndex, targetPtgNode, modRefEffect);
1505 };
1506
1507 // If the pointer is targeting everything external, flag the ModRefSet
1508 if (pointsToGraph.isTargetingAllExternallyAvailable(registerPtgNode))
1509 {
1510 if (mayEffectReference(modRefEffect))
1511 {
1512 ModRefSummary_->markSetAsReferencingExternal(modRefSetIndex, minTargetSize.value_or(0));
1513 }
1514 if (mayEffectModify(modRefEffect))
1515 {
1516 ModRefSummary_->markSetAsModifyingExternal(modRefSetIndex, minTargetSize.value_or(0));
1517 }
1518 }
1519
1520 for (const auto targetPtgNode : pointsToGraph.getExplicitTargets(registerPtgNode).Items())
1521 {
1522 // Assert that the PointsToGraph contains no doubled-up pointees
1523 JLM_ASSERT(
1524 !pointsToGraph.isTargetingAllExternallyAvailable(registerPtgNode)
1525 || !pointsToGraph.isExternallyAvailable(targetPtgNode));
1526 tryAddToModRefSet(targetPtgNode);
1527 }
1528}
1529
1532 const rvsdg::SimpleNode & loadNode,
1533 const rvsdg::LambdaNode & lambda)
1534{
1535 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(loadNode, lambda);
1536 const auto origin = LoadOperation::AddressInput(loadNode).origin();
1537 const auto loadOperation = util::assertedCast<const LoadOperation>(&loadNode.GetOperation());
1538 const auto loadSize = GetTypeStoreSize(*loadOperation->GetLoadedType());
1539
1540 addPointerOriginTargets(nodeModRef, *origin, loadSize, ModRefEffect::RefOnly);
1541 return nodeModRef;
1542}
1543
1546 const rvsdg::SimpleNode & storeNode,
1547 const rvsdg::LambdaNode & lambda)
1548{
1549 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(storeNode, lambda);
1550 const auto origin = StoreOperation::AddressInput(storeNode).origin();
1551 const auto storeOperation = util::assertedCast<const StoreOperation>(&storeNode.GetOperation());
1552 const auto storeSize = GetTypeStoreSize(storeOperation->GetStoredType());
1553
1554 addPointerOriginTargets(nodeModRef, *origin, storeSize, ModRefEffect::ModOnly);
1555 return nodeModRef;
1556}
1557
1560 const rvsdg::SimpleNode & allocaNode,
1561 const rvsdg::LambdaNode & lambda)
1562{
1563 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(allocaNode, lambda);
1564 const auto allocaMemoryNode = Context_->pointsToGraph.getNodeForAlloca(allocaNode);
1565 // The alloca itself is only considered to be a ref, since its value is indeterminite,
1566 // and any users of the alloca will depend on its address output
1567 ModRefSummary_->addExplicitMemoryNodeToSet(nodeModRef, allocaMemoryNode, ModRefEffect::RefOnly);
1568 return nodeModRef;
1569}
1570
1573 const rvsdg::SimpleNode & mallocNode,
1574 const rvsdg::LambdaNode & lambda)
1575{
1576 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(mallocNode, lambda);
1577 const auto mallocMemoryNode = Context_->pointsToGraph.getNodeForMalloc(mallocNode);
1578 // The malloc itself is only considered to be a ref, since its value is indeterminite,
1579 // and any users of the malloc will depend on its address output
1580 ModRefSummary_->addExplicitMemoryNodeToSet(nodeModRef, mallocMemoryNode, ModRefEffect::RefOnly);
1581 return nodeModRef;
1582}
1583
1586 const rvsdg::SimpleNode & freeNode,
1587 const rvsdg::LambdaNode & lambda)
1588{
1589 JLM_ASSERT(is<FreeOperation>(freeNode.GetOperation()));
1590
1591 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(freeNode, lambda);
1592 const auto origin = FreeOperation::addressInput(freeNode).origin();
1593
1594 // TODO: Filter so we only free MallocMemoryNodes
1595 addPointerOriginTargets(nodeModRef, *origin, std::nullopt, ModRefEffect::ModOnly);
1596 return nodeModRef;
1597}
1598
1601 const rvsdg::SimpleNode & memcpyNode,
1602 const rvsdg::LambdaNode & lambda)
1603{
1604 JLM_ASSERT(is<MemCpyOperation>(memcpyNode.GetOperation()));
1605
1606 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(memcpyNode, lambda);
1607 const auto dstOrigin = MemCpyOperation::destinationInput(memcpyNode).origin();
1608 const auto srcOrigin = MemCpyOperation::sourceInput(memcpyNode).origin();
1609 const auto countOrigin = MemCpyOperation::countInput(memcpyNode).origin();
1610 const auto count = tryGetConstantSignedInteger(*countOrigin);
1611 addPointerOriginTargets(nodeModRef, *dstOrigin, count, ModRefEffect::ModOnly);
1612 addPointerOriginTargets(nodeModRef, *srcOrigin, count, ModRefEffect::RefOnly);
1613 return nodeModRef;
1614}
1615
1618 const rvsdg::SimpleNode & memmoveNode,
1619 const rvsdg::LambdaNode & lambda)
1620{
1621 JLM_ASSERT(is<MemMoveOperation>(memmoveNode.GetOperation()));
1622
1623 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(memmoveNode, lambda);
1624 const auto dstOrigin = MemMoveOperation::destinationInput(memmoveNode).origin();
1625 const auto srcOrigin = MemMoveOperation::sourceInput(memmoveNode).origin();
1626 const auto lengthOrigin = MemMoveOperation::lengthInput(memmoveNode).origin();
1627 const auto count = tryGetConstantSignedInteger(*lengthOrigin);
1628 addPointerOriginTargets(nodeModRef, *dstOrigin, count, ModRefEffect::ModOnly);
1629 addPointerOriginTargets(nodeModRef, *srcOrigin, count, ModRefEffect::RefOnly);
1630 return nodeModRef;
1631}
1632
1635 const rvsdg::SimpleNode & memsetNode,
1636 const rvsdg::LambdaNode & lambda)
1637{
1638 JLM_ASSERT(is<MemSetOperation>(memsetNode.GetOperation()));
1639
1640 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(memsetNode, lambda);
1641 const auto dstOrigin = MemSetOperation::destinationInput(memsetNode).origin();
1642 const auto lengthOrigin = MemSetOperation::lengthInput(memsetNode).origin();
1643 const auto numBytes = tryGetConstantSignedInteger(*lengthOrigin);
1644 addPointerOriginTargets(nodeModRef, *dstOrigin, numBytes, ModRefEffect::ModOnly);
1645
1646 return nodeModRef;
1647}
1648
1651 const rvsdg::SimpleNode & callNode,
1652 const rvsdg::LambdaNode & lambda)
1653{
1654 JLM_ASSERT(is<CallOperation>(callNode.GetOperation()));
1655
1656 const auto & pointsToGraph = Context_->pointsToGraph;
1657
1658 // This ModRefSet represents everything the call may affect
1659 const auto callModRef = ModRefSummary_->getOrCreateSetForNode(callNode, lambda);
1660
1661 // Go over all possible targets of the call and add them to the call summary
1662 const auto targetPtr = callNode.input(0)->origin();
1663 const auto targetPtgNode = Context_->pointsToGraph.getNodeForRegister(*targetPtr);
1664
1665 // Go through all locations the called function pointer may target
1666 for (const auto calleePtgNode : pointsToGraph.getExplicitTargets(targetPtgNode).Items())
1667 {
1668 const auto kind = pointsToGraph.getNodeKind(calleePtgNode);
1670 {
1671 const auto & calleeLambda = pointsToGraph.getLambdaForNode(calleePtgNode);
1672 const auto targetModRefSet =
1673 ModRefSummary_->getOrCreateSetForNode(calleeLambda, calleeLambda);
1674 AddModRefSimpleConstraint(targetModRefSet, callModRef);
1675 }
1676 else if (kind == PointsToGraph::NodeKind::ImportNode)
1677 {
1678 ModRefSummary_->markSetAsCallingExternalFunction(callModRef);
1679 }
1680 }
1681 if (pointsToGraph.isTargetingAllExternallyAvailable(targetPtgNode))
1682 {
1683 ModRefSummary_->markSetAsCallingExternalFunction(callModRef);
1684 }
1685
1687 {
1688 const auto reachableSimpleAllocas = getSimpleAllocasReachableFromCallArguments(callNode);
1689 auto blocklist = Context_->SimpleAllocas;
1690 blocklist.DifferenceWith(reachableSimpleAllocas);
1691 // Move the blocklist to the deque to keep it alive during solving
1692 Context_->CallBlocklists.push_back(std::move(blocklist));
1693 AddModRefSetBlocklist(callModRef, Context_->CallBlocklists.back());
1694 }
1695
1696 return callModRef;
1697}
1698
1699void
1701{
1702 Context_->ModRefSetSimpleConstraints.resize(ModRefSummary_->NumModRefSets());
1704
1705 // Start by pushing everything to the worklist
1706 for (ModRefSetIndex i = 0; i < ModRefSummary_->NumModRefSets(); i++)
1707 worklist.PushWorkItem(i);
1708
1709 while (worklist.HasMoreWorkItems())
1710 {
1711 const auto workItem = worklist.PopWorkItem();
1712
1713 const RegionAwareModRefSet & fromSet = ModRefSummary_->getModRefSet(workItem);
1714
1715 // Handle all simple constraints workItem -> target
1716 for (auto target : Context_->ModRefSetSimpleConstraints[workItem].Items())
1717 {
1718 RegionAwareModRefSet & targetSet = ModRefSummary_->getModRefSet(target);
1719
1720 // Propagate flags first, to enable skipping of doubled-up memory nodes
1721 bool changed = targetSet.propagateFlags(fromSet);
1722
1723 if (auto blocklist = Context_->ModRefSetBlocklists.find(target);
1724 blocklist != Context_->ModRefSetBlocklists.end())
1725 {
1726 // The target has a blocklist, avoid propagating blocked memory nodes
1727 for (auto [memoryNode, mayMod] : fromSet.getModRefNodes())
1728 {
1729 if (blocklist->second->Contains(memoryNode))
1730 continue;
1731
1732 changed |= ModRefSummary_->addMemoryNodeToSet(target, memoryNode, mayMod);
1733 }
1734 }
1735 else
1736 {
1737 // The target does not have a blocklist, so propagate everything
1738 for (auto [memoryNode, mayMod] : fromSet.getModRefNodes())
1739 {
1740 changed |= ModRefSummary_->addMemoryNodeToSet(target, memoryNode, mayMod);
1741 }
1742 }
1743
1744 if (changed)
1745 worklist.PushWorkItem(target);
1746 }
1747 }
1748
1750}
1751
1752bool
1754{
1755 // For all ModRefSets where a blocklist has been defined,
1756 // check that none of its MemoryNodes are on the blocklist
1757 for (auto [index, blocklist] : Context_->ModRefSetBlocklists)
1758 {
1759 for (auto [memoryNode, _] : ModRefSummary_->getModRefSet(index).getModRefNodes())
1760 {
1761 if (blocklist->Contains(memoryNode))
1762 return false;
1763 }
1764 }
1765 return true;
1766}
1767
1768void
1770{
1771 const auto externModRefSet = ModRefSummary_->getExternModRefSet();
1772 for (auto [memoryNode, modRefEffect] :
1773 ModRefSummary_->getModRefSet(externModRefSet).getModRefNodes())
1774 {
1775 // Memory marked as const in the PointsToGraph should not even be in any ModRefSets
1777 JLM_ASSERT(!ModRefSummary_->GetPointsToGraph().isNodeConstant(memoryNode));
1778
1779 // If the memory is modified in this module, it is not read-only
1780 if (mayEffectModify(modRefEffect))
1781 continue;
1782 // If the memory is externally accessible, it is also not read-only
1783 if (ModRefSummary_->GetPointsToGraph().isExternallyAvailable(memoryNode))
1784 continue;
1785 // If the memory node is of type Alloca, it might not actually be read-only,
1786 // as non-reentrant allocas can be partially hidden from reaching the extern \ref ModRefSet.
1787 // This is handled by never treating allocas as "effectively read-only"
1788 if (ModRefSummary_->GetPointsToGraph().getNodeKind(memoryNode)
1790 continue;
1791
1792 Context_->ReadOnlyMemoryNodes.insert(memoryNode);
1793 }
1794}
1795
1796void
1798{
1799 for (auto & functions : Context_->SccFunctions)
1800 {
1801 for (auto function : functions.Items())
1802 {
1803 materializeSetsInFunction(*function);
1804 }
1805 }
1806}
1807
1808void
1810{
1811 const auto & pointsToGraph = ModRefSummary_->GetPointsToGraph();
1812 // The ModRefSet representing everying that can be modified from external functions
1813 const auto & externModRefNodes =
1814 ModRefSummary_->getModRefSet(ModRefSummary_->getExternModRefSet()).getModRefNodes();
1815
1816 // Only memory nodes that appear in ModRefSets without the external memory node should be kept
1818
1819 // Among memory nodes that should be kept, the ones flagged externally available are added here.
1820 // When materializing sets, the flags are turned into explicit targets using this list
1821 std::vector<PointsToGraph::NodeIndex> materializeExternallyAvailable;
1822 // When a memory node we need to keep is not externally available, yet is in the
1823 // ModRefSet representing extern functions, the memory node is added to this list.
1824 // It gets materialized in all ModRefSets flagged as possibly calling external functions.
1825 std::vector<std::pair<PointsToGraph::NodeIndex, ModRefEffect>> materializeFromCallToExtern;
1826
1827 const auto markToKeep = [&](PointsToGraph::NodeIndex memoryNode)
1828 {
1829 // Memory nodes discovered to be read-only should not be kept
1830 if (Context_->ReadOnlyMemoryNodes.Contains(memoryNode))
1831 return;
1832
1833 bool inserted = keepMemoryNodes.insert(memoryNode);
1834 if (!inserted)
1835 return;
1836
1837 if (pointsToGraph.isExternallyAvailable(memoryNode))
1838 materializeExternallyAvailable.push_back(memoryNode);
1839 else if (auto it = externModRefNodes.find(memoryNode); it != externModRefNodes.end())
1840 materializeFromCallToExtern.push_back(*it);
1841 };
1842
1844
1845 // Go over all ModRefSets in the function twice
1846 // The first pass determines which memory nodes to keep
1847 const auto & allModRefSets = ModRefSummary_->getAllSetsInFunction(lambda);
1848 for (auto modRefSetIndex : allModRefSets)
1849 {
1850 const auto & modRefSet = ModRefSummary_->getModRefSet(modRefSetIndex);
1851 Context_->numModRefSetsMaterialized++;
1852 Context_->modRefSetSizeBeforeMaterialization += modRefSet.getModRefNodes().size();
1853
1854 auto effectOnExternalNode = modRefSet.getImplicitModRefEffectForExternal(std::nullopt);
1855
1856 // If this ModRefSet may both reference and modify the external memory node,
1857 // it will not disqualify any other memory nodes from compression
1858 if (effectOnExternalNode == ModRefEffect::ModRef)
1859 continue;
1860
1861 for (auto [memoryNode, modRefEffect] : modRefSet.getModRefNodes())
1862 {
1863 JLM_ASSERT(modRefEffect != ModRefEffect::NoEffect);
1864
1865 // If the set has an effect on the memory node that it does not have on the external node,
1866 // the memory node is disqualified from compression
1867 if (!isEffectSubset(modRefEffect, effectOnExternalNode))
1868 markToKeep(memoryNode);
1869 }
1870 }
1871
1872 // Now go over all sets again, removing all memory nodes that are not on the keep list
1873 // and materializing memory nodes that were previously only implicit
1874 for (auto modRefSetIndex : allModRefSets)
1875 {
1876 auto & modRefSet = ModRefSummary_->getModRefSet(modRefSetIndex);
1877 modRefSet.keepSubsetOfExplicitMemoryNodes(keepMemoryNodes);
1878 Context_->modRefSetSizeAfterFiltering += modRefSet.getModRefNodes().size();
1879
1880 // Check what effects are implicitly encoded for externally available memory nodes.
1881 // The flags have a minimum size, so large memory nodes may have more effects than small ones.
1882 // Setting an unknown size gives the largest possible effect set.
1883 auto effectOnAllExternalNodes = modRefSet.getImplicitModRefEffectForExternal(1);
1884 auto effectOnLargeExternalNodes = modRefSet.getImplicitModRefEffectForExternal(std::nullopt);
1885 JLM_ASSERT(isEffectSubset(effectOnAllExternalNodes, effectOnLargeExternalNodes));
1886
1887 if (effectOnLargeExternalNodes == ModRefEffect::NoEffect)
1888 {
1889 // For nodes that do not have any effect on externally available memory,
1890 // we do not need to do any materialization.
1891 Context_->modRefSetSizeAfterMaterialization += modRefSet.getModRefNodes().size();
1892 continue;
1893 }
1894
1895 // If small and large external memory nodes have the same effects,
1896 // we can materialize all external memory nodes with that effect.
1897 if (effectOnLargeExternalNodes == effectOnAllExternalNodes)
1898 {
1899 for (auto memoryNode : materializeExternallyAvailable)
1900 {
1901 modRefSet.addExplicitMemoryNode(memoryNode, effectOnLargeExternalNodes);
1902 }
1903 }
1904 else
1905 {
1906 // Materialize each memory node with the appropriate effects based on its size
1907 for (auto memoryNode : materializeExternallyAvailable)
1908 {
1909 const auto memoryNodeSize = pointsToGraph.tryGetNodeSize(memoryNode);
1910 const auto modRefEffect = modRefSet.getImplicitModRefEffectForExternal(memoryNodeSize);
1911 if (modRefEffect != ModRefEffect::NoEffect)
1912 modRefSet.addExplicitMemoryNode(memoryNode, modRefEffect);
1913 }
1914 }
1915
1916 // If this ModRefSet is not only accessing everything that is externally available,
1917 // but also possibly calling external functions, materialize from the call to external set
1918 if (modRefSet.mayCallExternalFunction())
1919 {
1920 Context_->numModRefSetsCallingExternalFunction++;
1921 for (auto [memoryNode, modRefEffect] : materializeFromCallToExtern)
1922 {
1923 modRefSet.addExplicitMemoryNode(memoryNode, modRefEffect);
1924 }
1925 }
1926
1927 Context_->numModRefSetsWithEffectOnExternal++;
1928 Context_->modRefSetSizeAfterMaterialization += modRefSet.getModRefNodes().size();
1929 }
1930}
1931
1932std::string
1934{
1935 std::ostringstream ss;
1936 for (size_t i = 0; i < summarizer.Context_->SccFunctions.size(); i++)
1937 {
1938 if (i != 0)
1939 ss << " <- ";
1940 ss << "[" << std::endl;
1941 if (i == summarizer.Context_->ExternalNodeSccIndex)
1942 {
1943 ss << " " << "<external>" << std::endl;
1944 }
1945 for (auto function : summarizer.Context_->SccFunctions[i].Items())
1946 {
1947 ss << " " << function->DebugString() << std::endl;
1948 }
1949 ss << "]";
1950 }
1951 return ss.str();
1952}
1953
1954std::string
1956 const rvsdg::Graph & rvsdg,
1957 const RegionAwareModRefSummary & modRefSummary)
1958{
1959 std::ostringstream ss;
1960
1961 ss << "ExternModRefSet: "
1962 << modRefSummary.getModRefSetDebugString(modRefSummary.getExternModRefSet()) << std::endl;
1963
1964 auto indent = [&](size_t depth, char c = '-')
1965 {
1966 for (size_t i = 0; i < depth; i++)
1967 ss << c;
1968 };
1969
1970 std::function<void(const rvsdg::Node &, size_t)> toRegionTree =
1971 [&](const rvsdg::Node & node, size_t depth)
1972 {
1973 // Simple nodes with no ModRefSet can be ignored
1974 if (dynamic_cast<const rvsdg::SimpleNode *>(&node) && !modRefSummary.hasSetForNode(node))
1975 return;
1976
1977 indent(depth, '-');
1978 ss << "node " << node.DebugString() << " NodeID: " << node.GetNodeId() << ": ";
1979 if (modRefSummary.hasSetForNode(node))
1980 {
1981 auto modRefIndex = modRefSummary.getSetForNode(node);
1982 ss << modRefSummary.getModRefSetDebugString(modRefIndex) << std::endl;
1983 }
1984
1985 if (auto structuralNode = dynamic_cast<const rvsdg::StructuralNode *>(&node))
1986 {
1987 for (auto & region : structuralNode->Subregions())
1988 {
1989 indent(depth + 1, '-');
1990 ss << "RegionID: " << region.getRegionId() << std::endl;
1991 for (auto & n : region.Nodes())
1992 toRegionTree(n, depth + 2);
1993 }
1994 }
1995 };
1996
1997 ss << "RootRegion:" << std::endl;
1998 for (auto & node : rvsdg.GetRootRegion().Nodes())
1999 toRegionTree(node, 0);
2000
2001 return ss.str();
2002}
2003
2004std::unique_ptr<ModRefSummary>
2006 const rvsdg::RvsdgModule & rvsdgModule,
2007 const PointsToGraph & pointsToGraph,
2009{
2010 RegionAwareModRefSummarizer summarizer;
2011 return summarizer.SummarizeModRefs(rvsdgModule, pointsToGraph, statisticsCollector);
2012}
2013
2014std::unique_ptr<ModRefSummary>
2016 const rvsdg::RvsdgModule & rvsdgModule,
2017 const PointsToGraph & pointsToGraph)
2018{
2020 return Create(rvsdgModule, pointsToGraph, statisticsCollector);
2021}
2022}
static jlm::util::StatisticsCollector statisticsCollector
static bool Contains(const jlm::llvm::InterProceduralGraphModule &module, const std::string &)
Definition FNegTests.cpp:19
std::vector< rvsdg::Node * > nodes
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 * Argument(const rvsdg::Node &node, const size_t n)
Definition call.hpp:310
static size_t NumArguments(const rvsdg::Node &node) noexcept
Definition call.hpp:298
static rvsdg::Input & addressInput(const rvsdg::Node &node) noexcept
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition Load.hpp:75
static rvsdg::Input & sourceInput(const rvsdg::Node &node) noexcept
static rvsdg::Input & destinationInput(const rvsdg::Node &node) noexcept
static rvsdg::Input & countInput(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 & sourceInput(const rvsdg::Node &node) noexcept
static rvsdg::Input & lengthInput(const rvsdg::Node &node) noexcept
static rvsdg::Input & destinationInput(const rvsdg::Node &node) noexcept
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition Store.hpp:75
std::unordered_map< PointsToGraph::NodeIndex, ModRefEffect > modRefNodes_
const std::unordered_map< PointsToGraph::NodeIndex, ModRefEffect > & getModRefNodes() const
size_t numNodes() const noexcept
size_t numMemoryNodes() const noexcept
bool isExternallyAvailable(NodeIndex index) const
std::string getNodeDebugString(NodeIndex index, char separator=' ') const
std::optional< size_t > tryGetNodeSize(NodeIndex index) const noexcept
const util::HashSet< NodeIndex > & getExplicitTargets(NodeIndex index) const
NodeKind getNodeKind(NodeIndex index) const
bool isMemoryNode(NodeIndex index) const
static constexpr NodeIndex externalMemoryNode
ModRefEffect getImplicitModRefEffectForExternal(std::optional< size_t > size) const noexcept
std::optional< size_t > getRefExternalMinSize() const
bool propagateFlags(const RegionAwareModRefSet &other)
bool addExplicitMemoryNode(PointsToGraph::NodeIndex memoryNode, ModRefEffect modRefEffect)
void keepSubsetOfExplicitMemoryNodes(const util::HashSet< PointsToGraph::NodeIndex > &filter)
std::optional< size_t > getModExternalMinSize() const
bool addMemoryNode(PointsToGraph::NodeIndex memoryNode, bool isExternallyAvailable, std::optional< size_t > memoryNodeSize, ModRefEffect modRefEffect)
void stopReadOnlyMemoryDetectionStatistics(size_t numReadOnlyMemoryNodesDetected)
void stopModRefSetMaterializationStatistics(size_t numModRefSetsMaterialized, size_t modRefSetSizeBeforeMaterialization, size_t modRefSetSizeAfterFiltering, size_t numModRefSetsWithEffectOnExternal, size_t numModRefSetsCallingExternalFunction, size_t modRefSetSizeAfterMaterialization)
static std::unique_ptr< Statistics > Create(const rvsdg::RvsdgModule &rvsdgModule, const PointsToGraph &pointsToGraph)
Statistics(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)
ModRefSetIndex AnnotateMalloc(const rvsdg::SimpleNode &mallocNode, 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, ModRefEffect modRefEffect)
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 materializeSetsInFunction(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)
ModRefSetIndex AnnotateAlloca(const rvsdg::SimpleNode &allocaNode, const rvsdg::LambdaNode &lambda)
std::unique_ptr< RegionAwareModRefSummary > ModRefSummary_
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)
util::HashSet< PointsToGraph::NodeIndex > getSimpleAllocasReachableFromRegionArguments(const rvsdg::Region &region)
static std::string ToRegionTree(const rvsdg::Graph &rvsdg, const RegionAwareModRefSummary &modRefSummary)
util::HashSet< PointsToGraph::NodeIndex > getReachableSimpleAllocas(std::queue< PointsToGraph::NodeIndex > &nodes)
ModRefSetIndex AnnotateMemset(const rvsdg::SimpleNode &memsetNode, const rvsdg::LambdaNode &lambda)
util::HashSet< PointsToGraph::NodeIndex > getSimpleAllocasReachableFromCallArguments(const rvsdg::SimpleNode &call)
ModRefSetIndex AnnotateMemmove(const rvsdg::SimpleNode &memmoveNode, const rvsdg::LambdaNode &lambda)
void AnnotateRegion(const rvsdg::Region &region, ModRefSetIndex modRefSet, const rvsdg::LambdaNode &lambda)
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)
Mod/Ref summary of region-aware mod/ref summarizer.
const std::vector< ModRefSetIndex > & getAllSetsInFunction(const rvsdg::LambdaNode &lambdaNode)
std::string getModRefSetDebugString(ModRefSetIndex index) const
ModRefSetIndex getOrCreateSetForNode(const rvsdg::Node &node, const rvsdg::LambdaNode &lambdaNode)
static std::unique_ptr< RegionAwareModRefSummary > Create(const PointsToGraph &pointsToGraph)
RegionAwareModRefSummary & operator=(const RegionAwareModRefSummary &)=delete
bool markSetAsModifyingExternal(ModRefSetIndex index, size_t minSize)
const RegionAwareModRefSet & getModRefSet(ModRefSetIndex index) const
const ModRefSet & GetLambdaEntryModRef(const rvsdg::LambdaNode &lambda) const override
bool hasSetForNode(const rvsdg::Node &node) const
bool addMemoryNodeToSet(ModRefSetIndex index, PointsToGraph::NodeIndex ptgNode, ModRefEffect modRefEffect)
const ModRefSet & GetSimpleNodeModRef(const rvsdg::SimpleNode &node) const override
RegionAwareModRefSummary(const PointsToGraph &pointsToGraph)
bool markSetAsReferencingExternal(ModRefSetIndex index, size_t minSize)
RegionAwareModRefSummary(const RegionAwareModRefSummary &)=delete
std::vector< RegionAwareModRefSet > modRefSets_
std::unordered_map< const rvsdg::Node *, ModRefSetIndex > nodeMap_
RegionAwareModRefSet & getModRefSet(ModRefSetIndex index)
const ModRefSet & GetGammaExitModRef(const rvsdg::GammaNode &gamma) const override
const PointsToGraph & GetPointsToGraph() const noexcept override
std::unordered_map< const rvsdg::LambdaNode *, std::vector< ModRefSetIndex > > modRefSetsInFunction_
const ModRefSet & GetGammaEntryModRef(const rvsdg::GammaNode &gamma) const override
const ModRefSet & GetLambdaExitModRef(const rvsdg::LambdaNode &lambda) const override
ModRefSetIndex getSetForNode(const rvsdg::Node &node) const
const ModRefSet & GetThetaModRef(const rvsdg::ThetaNode &theta) const override
bool addExplicitMemoryNodeToSet(ModRefSetIndex index, PointsToGraph::NodeIndex ptgNode, ModRefEffect modRefEffect)
Conditional operator / pattern matching.
Definition gamma.hpp:99
Region & GetRootRegion() const noexcept
Definition graph.hpp:99
Output * origin() const noexcept
Definition node.hpp:58
Id GetNodeId() const noexcept
Definition node.hpp:600
virtual std::string DebugString() const =0
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
RegionArgumentRange Arguments() noexcept
Definition region.hpp:319
static size_t NumRegions(const rvsdg::Region &region) noexcept
Definition region.cpp:456
NodeRange Nodes() noexcept
Definition region.hpp:375
Graph & Rvsdg() noexcept
const SimpleOperation & GetOperation() const noexcept override
NodeInput * input(size_t index) const noexcept
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
void CollectDemandedStatistics(std::unique_ptr< Statistics > statistics)
Statistics Interface.
util::Timer & GetTimer(const std::string &name)
util::Timer & AddTimer(std::string name)
void AddMeasurement(std::string name, T value)
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
#define JLM_UNREACHABLE(msg)
Definition common.hpp:43
static const bool ENABLE_READ_ONLY_DETECTION
static const bool ENABLE_CALL_SIMPLE_ALLOCA_BLOCKING
static std::vector< const rvsdg::LambdaNode * > CollectLambdaNodes(const rvsdg::RvsdgModule &rvsdgModule)
bool IsPointerCompatible(const rvsdg::Output &value)
bool isEffectSubset(ModRefEffect subset, ModRefEffect superset)
bool mayEffectModify(ModRefEffect effect)
bool mayEffectReference(ModRefEffect effect)
static const bool ENABLE_CONSTANT_MEMORY_BLOCKING
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:70
void MatchTypeOrFail(T &obj, const Fns &... fns)
Pattern match over subclass type of given object.
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:808
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872
std::vector< util::HashSet< const rvsdg::LambdaNode * > > SccFunctions
std::unordered_map< const rvsdg::Node *, util::HashSet< PointsToGraph::NodeIndex > > NonReentrantAllocas
util::HashSet< const rvsdg::LambdaNode * > FunctionsCallingSetjmp
std::unordered_map< const rvsdg::LambdaNode *, size_t > FunctionToSccIndex
std::deque< util::HashSet< PointsToGraph::NodeIndex > > CallBlocklists
std::vector< util::HashSet< ModRefSetIndex > > ModRefSetSimpleConstraints
util::HashSet< PointsToGraph::NodeIndex > ReadOnlyMemoryNodes
std::unordered_map< ModRefSetIndex, const util::HashSet< PointsToGraph::NodeIndex > * > ModRefSetBlocklists