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{
40
47 !std::getenv("JLM_DISABLE_OPERATION_SIZE_BLOCKING");
48
54 !std::getenv("JLM_DISABLE_CONSTANT_MEMORY_BLOCKING");
55
60static const bool ENABLE_READ_ONLY_DETECTION = !std::getenv("JLM_DISABLE_READ_ONLY_DETECTION");
61
67 !std::getenv("JLM_DISABLE_FUNCTION_SIMPLE_ALLOCA_ALLOWLIST");
68
74 !std::getenv("JLM_DISABLE_CALL_SIMPLE_ALLOCA_ALLOWLIST");
75
82 !std::getenv("JLM_DISABLE_EXTERN_SIMPLE_ALLOCA_ALLOWLIST");
83
91{
92 static constexpr auto NumRvsdgRegionsLabel_ = "#RvsdgRegions";
93 static constexpr auto NumSimpleAllocas_ = "#SimpleAllocas";
94 static constexpr auto NumFunctions_ = "#Functions";
95 static constexpr auto NumFunctionsCallingSetjmp_ = "#FunctionsCallingSetjmp";
96 static constexpr auto NumReadOnlyMemoryNodesDetectedLabel_ = "#ReadOnlyMemoryNodesDetected";
97
98 static constexpr auto NumModRefSetsMaterializedLabel_ = "#ModRefSetsMaterialized";
100 "ModRefSetSizeBeforeMaterialization";
101 static constexpr auto ModRefSetSizeAfterFilteringLabel_ = "ModRefSetSizeAfterFiltering";
102 static constexpr auto NumModRefSetsWithEffectOnExternalLabel_ = "#ModRefSetsWithEffectOnExternal";
104 "#ModRefSetsCallingExternalFunction";
106 "ModRefSetSizeAfterMaterialization";
107
108 static constexpr auto SimpleAllocasSetTimer_ = "SimpleAllocasSetTimer";
109 static constexpr auto AnnotationTimer_ = "AnnotationTimer";
110 static constexpr auto SolvingTimer_ = "SolvingTimer";
111 static constexpr auto ReadOnlyDetectionTimer_ = "ReadOnlyDetectionTimer";
112 static constexpr auto ModRefSetMaterializationTimer_ = "ModRefSetMaterializationTimer";
113
114 static constexpr auto memoryStateDistributionLabel_ = "MemoryStateDistribution";
115
116public:
117 ~Statistics() override = default;
118
119 explicit Statistics(const rvsdg::RvsdgModule & rvsdgModule, const PointsToGraph & pointsToGraph)
120 : util::Statistics(Id::RegionAwareModRefSummarizer, rvsdgModule.SourceFilePath().value())
121 {
122 AddMeasurement(Label::NumRvsdgNodes, rvsdg::nnodes(&rvsdgModule.Rvsdg().GetRootRegion()));
126 AddMeasurement(Label::NumPointsToGraphMemoryNodes, pointsToGraph.numMemoryNodes());
127 }
128
129 void
134
135 void
136 StopCreateSimpleAllocasSetStatistics(uint64_t numSimpleAllocas)
137 {
139 AddMeasurement(NumSimpleAllocas_, numSimpleAllocas);
140 }
141
142 void
147
148 void
149 StopAnnotationStatistics(size_t numFunctions, size_t numFunctionsCallingSetjmp)
150 {
152 AddMeasurement(NumFunctions_, numFunctions);
153 AddMeasurement(NumFunctionsCallingSetjmp_, numFunctionsCallingSetjmp);
154 }
155
156 void
161
162 void
167
168 void
173
174 void
175 stopReadOnlyMemoryDetectionStatistics(size_t numReadOnlyMemoryNodesDetected)
176 {
178 AddMeasurement(NumReadOnlyMemoryNodesDetectedLabel_, numReadOnlyMemoryNodesDetected);
179 }
180
181 void
186
187 void
189 size_t numModRefSetsMaterialized,
190 size_t modRefSetSizeBeforeMaterialization,
191 size_t modRefSetSizeAfterFiltering,
192 size_t numModRefSetsWithEffectOnExternal,
193 size_t numModRefSetsCallingExternalFunction,
194 size_t modRefSetSizeAfterMaterialization)
195 {
197 AddMeasurement(NumModRefSetsMaterializedLabel_, numModRefSetsMaterialized);
198 AddMeasurement(ModRefSetSizeBeforeMaterializationLabel_, modRefSetSizeBeforeMaterialization);
199 AddMeasurement(ModRefSetSizeAfterFilteringLabel_, modRefSetSizeAfterFiltering);
200 AddMeasurement(NumModRefSetsWithEffectOnExternalLabel_, numModRefSetsWithEffectOnExternal);
203 numModRefSetsCallingExternalFunction);
204 AddMeasurement(ModRefSetSizeAfterMaterializationLabel_, modRefSetSizeAfterMaterialization);
205 }
206
207 void
208 addMemoryStateDistribution(const std::vector<MemoryStateSummary> & memoryStateDistribution)
209 {
210 AddMeasurement(memoryStateDistributionLabel_, toString(memoryStateDistribution));
211 }
212
213 static std::unique_ptr<Statistics>
214 Create(const rvsdg::RvsdgModule & rvsdgModule, const PointsToGraph & pointsToGraph)
215 {
216 return std::make_unique<Statistics>(rvsdgModule, pointsToGraph);
217 }
218};
219
234class RegionAwareModRefSet final : public ModRefSet
235{
236public:
237 // The byte size used when no access to external is made
238 static constexpr uint32_t NoneSize = std::numeric_limits<uint32_t>::max();
239
241
251 [[nodiscard]] std::optional<size_t>
253 {
255 return std::nullopt;
256 return refExternalOfSize_;
257 }
258
268 [[nodiscard]] std::optional<size_t>
270 {
272 return std::nullopt;
273 return modExternalOfSize_;
274 }
275
281 bool
283 {
284 // Make sure we do not overflow or collide with the sentinel NoneSize
285 minSize = std::min<size_t>(minSize, NoneSize - 1);
286
287 if (minSize < refExternalOfSize_)
288 {
289 refExternalOfSize_ = minSize;
290 return true;
291 }
292 return false;
293 }
294
300 bool
302 {
303 // Make sure we do not overflow or collide with the sentinel NoneSize
304 minSize = std::min<size_t>(minSize, NoneSize - 1);
305
306 if (minSize < modExternalOfSize_)
307 {
308 modExternalOfSize_ = minSize;
309 return true;
310 }
311 return false;
312 }
313
320 [[nodiscard]] ModRefEffect
321 getImplicitModRefEffectForExternal(std::optional<size_t> size) const noexcept
322 {
325 {
326 if (size.value_or(NoneSize) >= refExternalOfSize_)
327 result |= ModRefEffect::RefOnly;
328 }
330 {
331 if (size.value_or(NoneSize) >= modExternalOfSize_)
332 result |= ModRefEffect::ModOnly;
333 }
334 return result;
335 }
336
340 [[nodiscard]] bool
342 {
344 }
345
350 bool
352 {
354 return false;
355
357
358 // calls to external functions may reference and modify any externally available memory
361 return true;
362 }
363
372 bool
374 {
375 JLM_ASSERT(modRefEffect != ModRefEffect::NoEffect);
376
377 const auto [it, inserted] = modRefNodes_.insert({ memoryNode, modRefEffect });
378 if (inserted)
379 return true;
380
381 // The memory node was already present, but we may add more effects
382 auto oldEffects = it->second;
383 it->second = oldEffects | modRefEffect;
384 return it->second != oldEffects;
385 }
386
397 bool
399 PointsToGraph::NodeIndex memoryNode,
400 bool isExternallyAvailable,
401 std::optional<size_t> memoryNodeSize,
402 ModRefEffect modRefEffect)
403 {
404 JLM_ASSERT(modRefEffect != ModRefEffect::NoEffect);
405
406 // If the effects on the node are already encoded implicitly, skip adding them explicitly
407 if (isExternallyAvailable)
408 {
409 const auto implicitEffect = getImplicitModRefEffectForExternal(memoryNodeSize);
410 if (isEffectSubset(modRefEffect, implicitEffect))
411 return false;
412 }
413
414 return addExplicitMemoryNode(memoryNode, modRefEffect);
415 }
416
420 void
422 {
423 auto it = modRefNodes_.begin();
424 while (it != modRefNodes_.end())
425 {
426 if (filter.Contains(it->first))
427 ++it;
428 else
429 it = modRefNodes_.erase(it);
430 }
431 }
432
437 bool
439 {
440 // Check the most restrictive flag first
441 if (other.callsExternalFunction_)
442 {
444 return false;
445
447 JLM_ASSERT(other.refExternalOfSize_ == 0);
448 JLM_ASSERT(other.modExternalOfSize_ == 0);
451 return true;
452 }
453
455 {
458 return true;
459 }
460
462 {
464 return true;
465 }
466
467 return false;
468 }
469
470private:
471 // If not equal to NoneSize, the ModRefSet potentially reads all externally available memory
472 // of size >= the given number of bytes.
474 // If not equal to NoneSize, the ModRefSet potentially modifies all externally available memory
475 // of size >= the given number of bytes.
477 // If true, the ModRefSet represents possibly calling externally defined functions.
478 // In this case, \ref refExternalOfSize and \ref modExternalOfSize must both be 0
480};
481
485{
486public:
487 explicit RegionAwareModRefSummary(const PointsToGraph & pointsToGraph)
488 : pointsToGraph_(pointsToGraph)
489 {
490 // Create the ModRefSet representing eveything that can be referenced and modified,
491 // directly or indirectly, from external functions.
493 // The ModRefSet representing external functions can call external functions
495 }
496
500
501 [[nodiscard]] const PointsToGraph &
502 GetPointsToGraph() const noexcept override
503 {
504 return pointsToGraph_;
505 }
506
507 [[nodiscard]] size_t
508 NumModRefSets() const noexcept
509 {
510 return modRefSets_.size();
511 }
512
513 [[nodiscard]] RegionAwareModRefSet &
515 {
516 JLM_ASSERT(index < modRefSets_.size());
517 return modRefSets_[index];
518 }
519
520 [[nodiscard]] const RegionAwareModRefSet &
522 {
523 JLM_ASSERT(index < modRefSets_.size());
524 return modRefSets_[index];
525 }
526
527 bool
529 {
530 return getModRefSet(index).markAsReferencingExternal(minSize);
531 }
532
533 bool
535 {
536 return getModRefSet(index).markAsModifyingExternal(minSize);
537 }
538
539 bool
544
554 bool
556 ModRefSetIndex index,
558 ModRefEffect modRefEffect)
559 {
560 const bool isExternallyAvailable = pointsToGraph_.isExternallyAvailable(ptgNode);
561 const std::optional<size_t> memoryNodeSize = pointsToGraph_.tryGetNodeSize(ptgNode);
562 return getModRefSet(index)
563 .addMemoryNode(ptgNode, isExternallyAvailable, memoryNodeSize, modRefEffect);
564 }
565
575 bool
577 ModRefSetIndex index,
579 ModRefEffect modRefEffect)
580 {
581 return getModRefSet(index).addExplicitMemoryNode(ptgNode, modRefEffect);
582 }
583
595 [[nodiscard]] ModRefSetIndex
596 getExternModRefSet() const noexcept
597 {
598 return externModRefSet_;
599 }
600
601 [[nodiscard]] bool
602 hasSetForNode(const rvsdg::Node & node) const
603 {
604 return nodeMap_.find(&node) != nodeMap_.end();
605 }
606
607 [[nodiscard]] ModRefSetIndex
608 getSetForNode(const rvsdg::Node & node) const
609 {
610 const auto it = nodeMap_.find(&node);
611 JLM_ASSERT(it != nodeMap_.end());
612 return it->second;
613 }
614
621 [[nodiscard]] ModRefSetIndex
622 getOrCreateSetForNode(const rvsdg::Node & node, const rvsdg::LambdaNode & lambdaNode)
623 {
624 auto [it, inserted] = nodeMap_.insert({ &node, 0 });
625 if (inserted)
626 {
627 const auto created = createModRefSet();
628 it->second = created;
629 modRefSetsInFunction_[&lambdaNode].push_back(created);
630 return created;
631 }
632
633 return it->second;
634 }
635
636 [[nodiscard]] const std::vector<ModRefSetIndex> &
638 {
639 return modRefSetsInFunction_[&lambdaNode];
640 }
641
642 [[nodiscard]] std::string
644 {
645 const auto & modRefSet = getModRefSet(index);
646 std::stringstream ss;
647 ss << "{MRS#" << index << "; ";
648 const auto mayRefMinSize = modRefSet.getRefExternalMinSize();
649 if (mayRefMinSize.has_value())
650 ss << "RefExt>=" << *mayRefMinSize << " bytes; ";
651 const auto mayModMinSize = modRefSet.getModExternalMinSize();
652 if (mayModMinSize.has_value())
653 ss << "ModExt>=" << *mayModMinSize << " bytes; ";
654 if (modRefSet.mayCallExternalFunction())
655 ss << "MayCallExt; ";
656
657 bool first = true;
658 for (auto [memoryNode, modRefEffect] : modRefSet.getModRefNodes())
659 {
660 if (first)
661 first = false;
662 else
663 ss << ", ";
664
665 ss << pointsToGraph_.getNodeDebugString(memoryNode);
666 switch (modRefEffect)
667 {
669 JLM_UNREACHABLE("Memory nodes should never be added with NoEffect");
671 ss << "[R]";
672 break;
674 ss << "[M]";
675 break;
677 ss << "[MR]";
678 break;
679 }
680 }
681 ss << "}";
682 return ss.str();
683 }
684
685 const ModRefSet &
686 GetSimpleNodeModRef(const rvsdg::SimpleNode & node) const override
687 {
688 return modRefSets_[getSetForNode(node)];
689 }
690
691 const ModRefSet &
692 GetGammaEntryModRef(const rvsdg::GammaNode & gamma) const override
693 {
694 return modRefSets_[getSetForNode(gamma)];
695 }
696
697 const ModRefSet &
698 GetGammaExitModRef(const rvsdg::GammaNode & gamma) const override
699 {
700 return GetGammaEntryModRef(gamma);
701 }
702
703 const ModRefSet &
704 GetThetaModRef(const rvsdg::ThetaNode & theta) const override
705 {
706 return modRefSets_[getSetForNode(theta)];
707 }
708
709 const ModRefSet &
710 GetLambdaEntryModRef(const rvsdg::LambdaNode & lambda) const override
711 {
712 return modRefSets_[getSetForNode(lambda)];
713 }
714
715 const ModRefSet &
716 GetLambdaExitModRef(const rvsdg::LambdaNode & lambda) const override
717 {
718 return GetLambdaEntryModRef(lambda);
719 }
720
721 [[nodiscard]] static std::unique_ptr<RegionAwareModRefSummary>
722 Create(const PointsToGraph & pointsToGraph)
723 {
724 return std::make_unique<RegionAwareModRefSummary>(pointsToGraph);
725 }
726
727private:
728 [[nodiscard]] ModRefSetIndex
730 {
731 modRefSets_.emplace_back();
732 return modRefSets_.size() - 1;
733 }
734
736
740 std::vector<RegionAwareModRefSet> modRefSets_;
741
748
752 std::unordered_map<const rvsdg::LambdaNode *, std::vector<ModRefSetIndex>> modRefSetsInFunction_;
753
759 std::unordered_map<const rvsdg::Node *, ModRefSetIndex> nodeMap_;
760};
761
853
855
857
858std::unique_ptr<ModRefSummary>
860 const rvsdg::RvsdgModule & rvsdgModule,
861 const PointsToGraph & pointsToGraph,
862 util::StatisticsCollector & statisticsCollector)
863{
864 ModRefSummary_ = RegionAwareModRefSummary::Create(pointsToGraph);
865 Context_ = std::make_unique<Context>(pointsToGraph);
866 auto statistics = Statistics::Create(rvsdgModule, pointsToGraph);
867
868 statistics->StartCreateSimpleAllocasSetStatistics();
869 Context_->SimpleAllocas = CreateSimpleAllocaSet(pointsToGraph);
870 statistics->StopCreateSimpleAllocasSetStatistics(Context_->SimpleAllocas.Size());
871
873 {
874 // Use an empty allowlist, since all simple allocas should be blocked
875 addModRefSetSimpleAllocaAllowlist(ModRefSummary_->getExternModRefSet(), {});
876 }
877
878 statistics->StartAnnotationStatistics();
879 // Go through and recursively annotate all functions, regions and nodes
880 annotateInterproceduralRegion(rvsdgModule.Rvsdg().GetRootRegion());
881 statistics->StopAnnotationStatistics(
882 Context_->Functions.size(),
883 Context_->FunctionsCallingSetjmp.Size());
884
885 removeSimpleAllocasAroundSetjmp();
886
887 statistics->StartSolvingStatistics();
888 SolveModRefSetConstraintGraph();
889 statistics->StopSolvingStatistics();
890
892 {
893 statistics->startReadOnlyMemoryDetectionStatistics();
894 determineReadOnlyMemory();
895 statistics->stopReadOnlyMemoryDetectionStatistics(Context_->ReadOnlyMemoryNodes.Size());
896 }
897
898 statistics->startModRefSetMaterializationStatistics();
899 materializeSets();
900 statistics->stopModRefSetMaterializationStatistics(
901 Context_->numModRefSetsMaterialized,
902 Context_->modRefSetSizeBeforeMaterialization,
903 Context_->modRefSetSizeAfterFiltering,
904 Context_->numModRefSetsWithEffectOnExternal,
905 Context_->numModRefSetsCallingExternalFunction,
906 Context_->modRefSetSizeAfterMaterialization);
907
908 // Perform the collection of the memory state distribution AFTER we invoked stopped collecting
909 // everything such that it does not count into the timing measurements
910 if (statisticsCollector.IsDemanded(statistics->GetId()))
911 {
912 const auto distribution = collectMemoryStateDistribution(rvsdgModule.Rvsdg(), *ModRefSummary_);
913 statistics->addMemoryStateDistribution(distribution);
914 }
915
916 statisticsCollector.CollectDemandedStatistics(std::move(statistics));
917 Context_.reset();
918 return std::move(ModRefSummary_);
919}
920
923{
924 // The set of allocas that are simple. Starts off as an over-approximation
926 // A queue used to visit all PtG memory nodes that are not simple allocas
927 std::queue<PointsToGraph::NodeIndex> notSimple;
928
929 for (PointsToGraph::NodeIndex ptgNode = 0; ptgNode < pointsToGraph.numNodes(); ptgNode++)
930 {
931 // Only memory nodes are relevant
932 if (!pointsToGraph.isMemoryNode(ptgNode))
933 continue;
934
935 // Allocas that are not externally available start of as presumed simple
936 if (pointsToGraph.getNodeKind(ptgNode) == PointsToGraph::NodeKind::AllocaNode
937 && !pointsToGraph.isExternallyAvailable(ptgNode))
938 simpleAllocas.insert(ptgNode);
939 else
940 notSimple.push(ptgNode);
941 }
942
943 // Process the queue to visit all memory nodes that may disqualify allocas from being simple
944 while (!notSimple.empty())
945 {
946 const auto ptgNode = notSimple.front();
947 notSimple.pop();
948
949 // Any node targeted by the not-simple memory node can themselves not be simple
950 for (const auto targetPtgNode : pointsToGraph.getExplicitTargets(ptgNode).Items())
951 {
952 // If the target is currently in the simple allocas candiate set, move it to the queue
953 if (simpleAllocas.Remove(targetPtgNode))
954 notSimple.push(targetPtgNode);
955 }
956 }
957
958 return simpleAllocas;
959}
960
963{
964 const auto & pointsToGraph = Context_->pointsToGraph;
965
966 util::HashSet<PointsToGraph::NodeIndex> reachableSimpleAllocas;
967 // Traverse along PointsToGraph edges to find all reachable simple allocas
968 while (!nodes.empty())
969 {
970 const auto ptgNode = nodes.front();
971 nodes.pop();
972
973 for (const auto targetPtgNode : pointsToGraph.getExplicitTargets(ptgNode).Items())
974 {
975 // We only are about following simple allocas, as simple allocas are only reachable from them.
976 if (!Context_->SimpleAllocas.Contains(targetPtgNode))
977 continue;
978
979 if (reachableSimpleAllocas.insert(targetPtgNode))
980 nodes.push(targetPtgNode);
981 }
982 }
983
984 return reachableSimpleAllocas;
985}
986
989 const rvsdg::Region & region)
990{
991 const auto & pointsToGraph = Context_->pointsToGraph;
992
993 // Start by finding initial register nodes
994 std::queue<PointsToGraph::NodeIndex> nodes;
995 for (auto argument : region.Arguments())
996 {
997 if (!IsPointerCompatible(*argument))
998 continue;
999 const auto ptgNode = pointsToGraph.getNodeForRegister(*argument);
1000 nodes.push(ptgNode);
1001 }
1002
1004}
1005
1008 const rvsdg::SimpleNode & call)
1009{
1010 const auto & pointsToGraph = Context_->pointsToGraph;
1011
1012 // Use a queue and a set to traverse the PointsToGraph
1013 std::queue<PointsToGraph::NodeIndex> nodes;
1014 auto numArguments = CallOperation::NumArguments(call);
1015 for (size_t i = 0; i < numArguments; i++)
1016 {
1017 const auto & argument = *CallOperation::Argument(call, i)->origin();
1018
1019 if (!IsPointerCompatible(argument))
1020 continue;
1021 const auto ptgNode = pointsToGraph.getNodeForRegister(argument);
1022 nodes.push(ptgNode);
1023 }
1024
1026}
1027
1028void
1030{
1031 for (auto setjmpCaller : Context_->FunctionsCallingSetjmp.Items())
1032 {
1033 const auto reachableFromArguments =
1034 getSimpleAllocasReachableFromRegionArguments(*setjmpCaller->subregion());
1035 Context_->SimpleAllocas.DifferenceWith(reachableFromArguments);
1036 }
1037}
1038
1039void
1041{
1042 // We should never add outgoing edges from the set representing all external functions
1043 JLM_ASSERT(from != ModRefSummary_->getExternModRefSet());
1044 // Ensure the constraint vector is large enough
1045 Context_->ModRefSetSimpleConstraints.resize(ModRefSummary_->NumModRefSets());
1046 Context_->ModRefSetSimpleConstraints[from].push_back(to);
1047}
1048
1049void
1051 ModRefSetIndex index,
1053{
1054 auto [_, inserted] =
1055 Context_->ModRefSetSimpleAllocaAllowlist.emplace(std::make_pair(index, std::move(blocklist)));
1056 JLM_ASSERT(inserted);
1057}
1058
1059void
1061{
1062 for (auto & node : region.Nodes())
1063 {
1065 node,
1066 [&](const rvsdg::PhiNode & phi)
1067 {
1069 },
1070 [&](const rvsdg::LambdaNode & lambda)
1071 {
1072 AnnotateFunction(lambda);
1073 });
1074 }
1075}
1076
1077void
1079{
1080 Context_->Functions.push_back(&lambda);
1081
1082 const auto modRefSet = AnnotateStructuralNode(lambda, lambda);
1083
1084 // Prevent memory nodes from being added to the ModRefSet of the lambda,
1085 // if the memory node represents a simple alloca that is not reachable from arguments
1087 {
1088 auto allowlist = getSimpleAllocasReachableFromRegionArguments(*lambda.subregion());
1089 addModRefSetSimpleAllocaAllowlist(modRefSet, std::move(allowlist));
1090 }
1091
1092 // If the function is externally available, it can be called by external functions,
1093 // so add a simple edge to the ModRefSet representing all external functions.
1094 const auto lambdaPtgNode = Context_->pointsToGraph.getNodeForLambda(lambda);
1095 if (Context_->pointsToGraph.isExternallyAvailable(lambdaPtgNode))
1096 {
1097 AddModRefSimpleConstraint(modRefSet, ModRefSummary_->getExternModRefSet());
1098 }
1099}
1100
1101void
1103 const rvsdg::Region & region,
1104 ModRefSetIndex modRefSet,
1105 const rvsdg::LambdaNode & lambda)
1106{
1107 for (auto & node : region.Nodes())
1108 {
1110 node,
1111 [&](const rvsdg::StructuralNode & structuralNode)
1112 {
1113 const auto nodeModRefSet = AnnotateStructuralNode(structuralNode, lambda);
1114 AddModRefSimpleConstraint(nodeModRefSet, modRefSet);
1115 },
1116 [&](const rvsdg::SimpleNode & simpleNode)
1117 {
1118 if (const auto nodeModRefSet = AnnotateSimpleNode(simpleNode, lambda))
1119 AddModRefSimpleConstraint(*nodeModRefSet, modRefSet);
1120 });
1121 }
1122}
1123
1126 const rvsdg::StructuralNode & structuralNode,
1127 const rvsdg::LambdaNode & lambda)
1128{
1129 // The ModRefSet of a structural node is the same as that of its subregion(s)
1130 const auto modRefSet = ModRefSummary_->getOrCreateSetForNode(structuralNode, lambda);
1131
1132 for (auto & subregion : structuralNode.Subregions())
1133 {
1134 AnnotateRegion(subregion, modRefSet, lambda);
1135 }
1136
1137 return modRefSet;
1138}
1139
1140std::optional<ModRefSetIndex>
1142 const rvsdg::SimpleNode & simpleNode,
1143 const rvsdg::LambdaNode & lambda)
1144{
1145 return MatchTypeWithDefault(
1146 simpleNode.GetOperation(),
1147 [&](const LoadOperation &) -> std::optional<ModRefSetIndex>
1148 {
1149 return AnnotateLoad(simpleNode, lambda);
1150 },
1151 [&](const StoreOperation &) -> std::optional<ModRefSetIndex>
1152 {
1153 return AnnotateStore(simpleNode, lambda);
1154 },
1155 [&](const AllocaOperation &) -> std::optional<ModRefSetIndex>
1156 {
1157 return AnnotateAlloca(simpleNode, lambda);
1158 },
1159 [&](const MallocOperation &) -> std::optional<ModRefSetIndex>
1160 {
1161 return AnnotateMalloc(simpleNode, lambda);
1162 },
1163 [&](const FreeOperation &) -> std::optional<ModRefSetIndex>
1164 {
1165 return AnnotateFree(simpleNode, lambda);
1166 },
1167 [&](const MemCpyOperation &) -> std::optional<ModRefSetIndex>
1168 {
1169 return AnnotateMemcpy(simpleNode, lambda);
1170 },
1171 [&](const MemSetOperation &) -> std::optional<ModRefSetIndex>
1172 {
1173 return AnnotateMemset(simpleNode, lambda);
1174 },
1175 [&](const MemMoveOperation &) -> std::optional<ModRefSetIndex>
1176 {
1177 return AnnotateMemmove(simpleNode, lambda);
1178 },
1179 [&](const CallOperation &) -> std::optional<ModRefSetIndex>
1180 {
1181 return AnnotateCall(simpleNode, lambda);
1182 },
1183 [&](const MemoryStateOperation &) -> std::optional<ModRefSetIndex>
1184 {
1185 // MemoryStateOperations are only used to route memory states, and can be ignored
1186 return std::nullopt;
1187 },
1188 [&]() -> std::optional<ModRefSetIndex>
1189 {
1190 // Any remaining type of node should not involve any memory states
1191 JLM_ASSERT(!hasMemoryState(simpleNode));
1192 return std::nullopt;
1193 });
1194}
1195
1196void
1198 ModRefSetIndex modRefSetIndex,
1199 const rvsdg::Output & origin,
1200 std::optional<size_t> minTargetSize,
1201 ModRefEffect modRefEffect)
1202{
1203 const auto & pointsToGraph = Context_->pointsToGraph;
1204
1205 const auto registerPtgNode = pointsToGraph.getNodeForRegister(origin);
1206
1207 const auto tryAddToModRefSet = [&](PointsToGraph::NodeIndex targetPtgNode)
1208 {
1209 if (ENABLE_CONSTANT_MEMORY_BLOCKING && pointsToGraph.isNodeConstant(targetPtgNode))
1210 return;
1211 if (ENABLE_OPERATION_SIZE_BLOCKING && minTargetSize)
1212 {
1213 const auto targetSize = pointsToGraph.tryGetNodeSize(targetPtgNode);
1214 if (targetSize.has_value() && *targetSize < minTargetSize)
1215 return;
1216 }
1217 ModRefSummary_->addExplicitMemoryNodeToSet(modRefSetIndex, targetPtgNode, modRefEffect);
1218 };
1219
1220 // If the pointer is targeting everything external, flag the ModRefSet
1221 if (pointsToGraph.isTargetingAllExternallyAvailable(registerPtgNode))
1222 {
1223 if (mayEffectReference(modRefEffect))
1224 {
1225 ModRefSummary_->markSetAsReferencingExternal(modRefSetIndex, minTargetSize.value_or(0));
1226 }
1227 if (mayEffectModify(modRefEffect))
1228 {
1229 ModRefSummary_->markSetAsModifyingExternal(modRefSetIndex, minTargetSize.value_or(0));
1230 }
1231 }
1232
1233 for (const auto targetPtgNode : pointsToGraph.getExplicitTargets(registerPtgNode).Items())
1234 {
1235 // Assert that the PointsToGraph contains no doubled-up pointees
1236 JLM_ASSERT(
1237 !pointsToGraph.isTargetingAllExternallyAvailable(registerPtgNode)
1238 || !pointsToGraph.isExternallyAvailable(targetPtgNode));
1239 tryAddToModRefSet(targetPtgNode);
1240 }
1241}
1242
1245 const rvsdg::SimpleNode & loadNode,
1246 const rvsdg::LambdaNode & lambda)
1247{
1248 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(loadNode, lambda);
1249 const auto origin = LoadOperation::AddressInput(loadNode).origin();
1250 const auto loadOperation = util::assertedCast<const LoadOperation>(&loadNode.GetOperation());
1251 const auto loadSize = GetTypeStoreSize(*loadOperation->GetLoadedType());
1252
1253 addPointerOriginTargets(nodeModRef, *origin, loadSize, ModRefEffect::RefOnly);
1254 return nodeModRef;
1255}
1256
1259 const rvsdg::SimpleNode & storeNode,
1260 const rvsdg::LambdaNode & lambda)
1261{
1262 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(storeNode, lambda);
1263 const auto origin = StoreOperation::AddressInput(storeNode).origin();
1264 const auto storeOperation = util::assertedCast<const StoreOperation>(&storeNode.GetOperation());
1265 const auto storeSize = GetTypeStoreSize(storeOperation->GetStoredType());
1266
1267 addPointerOriginTargets(nodeModRef, *origin, storeSize, ModRefEffect::ModOnly);
1268 return nodeModRef;
1269}
1270
1273 const rvsdg::SimpleNode & allocaNode,
1274 const rvsdg::LambdaNode & lambda)
1275{
1276 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(allocaNode, lambda);
1277 const auto allocaMemoryNode = Context_->pointsToGraph.getNodeForAlloca(allocaNode);
1278 // The alloca itself is only considered to be a ref, since its value is indeterminite,
1279 // and any users of the alloca will depend on its address output
1280 ModRefSummary_->addExplicitMemoryNodeToSet(nodeModRef, allocaMemoryNode, ModRefEffect::RefOnly);
1281 return nodeModRef;
1282}
1283
1286 const rvsdg::SimpleNode & mallocNode,
1287 const rvsdg::LambdaNode & lambda)
1288{
1289 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(mallocNode, lambda);
1290 const auto mallocMemoryNode = Context_->pointsToGraph.getNodeForMalloc(mallocNode);
1291 // The malloc itself is only considered to be a ref, since its value is indeterminite,
1292 // and any users of the malloc will depend on its address output
1293 ModRefSummary_->addExplicitMemoryNodeToSet(nodeModRef, mallocMemoryNode, ModRefEffect::RefOnly);
1294 return nodeModRef;
1295}
1296
1299 const rvsdg::SimpleNode & freeNode,
1300 const rvsdg::LambdaNode & lambda)
1301{
1302 JLM_ASSERT(is<FreeOperation>(freeNode.GetOperation()));
1303
1304 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(freeNode, lambda);
1305 const auto origin = FreeOperation::getAddressInput(freeNode).origin();
1306
1307 // TODO: Filter so we only free MallocMemoryNodes
1308 addPointerOriginTargets(nodeModRef, *origin, std::nullopt, ModRefEffect::ModOnly);
1309 return nodeModRef;
1310}
1311
1314 const rvsdg::SimpleNode & memcpyNode,
1315 const rvsdg::LambdaNode & lambda)
1316{
1317 JLM_ASSERT(is<MemCpyOperation>(memcpyNode.GetOperation()));
1318
1319 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(memcpyNode, lambda);
1320 const auto dstOrigin = MemCpyOperation::destinationInput(memcpyNode).origin();
1321 const auto srcOrigin = MemCpyOperation::sourceInput(memcpyNode).origin();
1322 const auto countOrigin = MemCpyOperation::countInput(memcpyNode).origin();
1323 const auto count = tryGetConstantSignedInteger(*countOrigin);
1324 addPointerOriginTargets(nodeModRef, *dstOrigin, count, ModRefEffect::ModOnly);
1325 addPointerOriginTargets(nodeModRef, *srcOrigin, count, ModRefEffect::RefOnly);
1326 return nodeModRef;
1327}
1328
1331 const rvsdg::SimpleNode & memmoveNode,
1332 const rvsdg::LambdaNode & lambda)
1333{
1334 JLM_ASSERT(is<MemMoveOperation>(memmoveNode.GetOperation()));
1335
1336 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(memmoveNode, lambda);
1337 const auto dstOrigin = MemMoveOperation::destinationInput(memmoveNode).origin();
1338 const auto srcOrigin = MemMoveOperation::sourceInput(memmoveNode).origin();
1339 const auto lengthOrigin = MemMoveOperation::lengthInput(memmoveNode).origin();
1340 const auto count = tryGetConstantSignedInteger(*lengthOrigin);
1341 addPointerOriginTargets(nodeModRef, *dstOrigin, count, ModRefEffect::ModOnly);
1342 addPointerOriginTargets(nodeModRef, *srcOrigin, count, ModRefEffect::RefOnly);
1343 return nodeModRef;
1344}
1345
1348 const rvsdg::SimpleNode & memsetNode,
1349 const rvsdg::LambdaNode & lambda)
1350{
1351 JLM_ASSERT(is<MemSetOperation>(memsetNode.GetOperation()));
1352
1353 const auto nodeModRef = ModRefSummary_->getOrCreateSetForNode(memsetNode, lambda);
1354 const auto dstOrigin = MemSetOperation::destinationInput(memsetNode).origin();
1355 const auto lengthOrigin = MemSetOperation::lengthInput(memsetNode).origin();
1356 const auto numBytes = tryGetConstantSignedInteger(*lengthOrigin);
1357 addPointerOriginTargets(nodeModRef, *dstOrigin, numBytes, ModRefEffect::ModOnly);
1358
1359 return nodeModRef;
1360}
1361
1364 const rvsdg::SimpleNode & callNode,
1365 const rvsdg::LambdaNode & lambda)
1366{
1367 JLM_ASSERT(is<CallOperation>(callNode.GetOperation()));
1368
1369 const auto & pointsToGraph = Context_->pointsToGraph;
1370
1371 // This ModRefSet represents everything the call may affect
1372 const auto callModRef = ModRefSummary_->getOrCreateSetForNode(callNode, lambda);
1373
1374 // Go over all possible targets of the call and add them to the call summary
1375 const auto targetPtr = callNode.input(0)->origin();
1376 const auto targetPtgNode = Context_->pointsToGraph.getNodeForRegister(*targetPtr);
1377
1378 // Go through all locations the called function pointer may target
1379 for (const auto calleePtgNode : pointsToGraph.getExplicitTargets(targetPtgNode).Items())
1380 {
1381 const auto kind = pointsToGraph.getNodeKind(calleePtgNode);
1383 {
1384 const auto & calleeLambda = pointsToGraph.getLambdaForNode(calleePtgNode);
1385 const auto targetModRefSet =
1386 ModRefSummary_->getOrCreateSetForNode(calleeLambda, calleeLambda);
1387 AddModRefSimpleConstraint(targetModRefSet, callModRef);
1388 }
1389 else if (kind == PointsToGraph::NodeKind::ImportNode)
1390 {
1391 ModRefSummary_->markSetAsCallingExternalFunction(callModRef);
1392 }
1393 }
1394 if (pointsToGraph.isTargetingAllExternallyAvailable(targetPtgNode))
1395 {
1396 ModRefSummary_->markSetAsCallingExternalFunction(callModRef);
1397 }
1398
1399 // Skip adding memory nodes to the ModRefSet of the call operation if they represent
1400 // simple allocas, and they are not reachable from any of the call arguments
1402 {
1403 const auto reachableSimpleAllocas = getSimpleAllocasReachableFromCallArguments(callNode);
1404 addModRefSetSimpleAllocaAllowlist(callModRef, reachableSimpleAllocas);
1405 }
1406
1407 // If the call targets setjmp, mark the function as possibly calling setjmp
1408 const auto classification = CallOperation::ClassifyCall(callNode);
1409 if (classification->isSetjmpCall())
1410 {
1411 Context_->FunctionsCallingSetjmp.insert(&lambda);
1412 }
1413
1414 return callModRef;
1415}
1416
1417void
1419{
1420 Context_->ModRefSetSimpleConstraints.resize(ModRefSummary_->NumModRefSets());
1422
1423 // Start by pushing everything to the worklist
1424 for (ModRefSetIndex i = 0; i < ModRefSummary_->NumModRefSets(); i++)
1425 worklist.PushWorkItem(i);
1426
1427 while (worklist.HasMoreWorkItems())
1428 {
1429 const auto workItem = worklist.PopWorkItem();
1430
1431 const RegionAwareModRefSet & fromSet = ModRefSummary_->getModRefSet(workItem);
1432
1433 // Handle all simple constraints workItem -> target
1434 for (auto target : Context_->ModRefSetSimpleConstraints[workItem])
1435 {
1436 RegionAwareModRefSet & targetSet = ModRefSummary_->getModRefSet(target);
1437
1438 // Propagate flags first, to enable skipping of doubled-up memory nodes
1439 bool changed = targetSet.propagateFlags(fromSet);
1440
1441 if (auto allowlist = Context_->ModRefSetSimpleAllocaAllowlist.find(target);
1442 allowlist != Context_->ModRefSetSimpleAllocaAllowlist.end())
1443 {
1444 // The target has a simple alloca allowlist, avoid propagating all other simple alloca nodes
1445 for (auto [memoryNode, mayMod] : fromSet.getModRefNodes())
1446 {
1447 if (Context_->SimpleAllocas.Contains(memoryNode))
1448 {
1449 if (!allowlist->second.Contains(memoryNode))
1450 continue;
1451 }
1452
1453 changed |= ModRefSummary_->addMemoryNodeToSet(target, memoryNode, mayMod);
1454 }
1455 }
1456 else
1457 {
1458 // The target does not have an allowlist, so propagate everything
1459 for (auto [memoryNode, mayMod] : fromSet.getModRefNodes())
1460 {
1461 changed |= ModRefSummary_->addMemoryNodeToSet(target, memoryNode, mayMod);
1462 }
1463 }
1464
1465 if (changed)
1466 worklist.PushWorkItem(target);
1467 }
1468 }
1469
1471}
1472
1473bool
1475{
1476 // For all ModRefSets where an allowlist has been defined,
1477 // check that no other simple allocas are included in the ModRefSet
1478 for (auto & [index, allowlist] : Context_->ModRefSetSimpleAllocaAllowlist)
1479 {
1480 for (auto [memoryNode, _] : ModRefSummary_->getModRefSet(index).getModRefNodes())
1481 {
1482 if (Context_->SimpleAllocas.Contains(memoryNode) && !allowlist.Contains(memoryNode))
1483 return false;
1484 }
1485 }
1486 return true;
1487}
1488
1489void
1491{
1492 const auto externModRefSet = ModRefSummary_->getExternModRefSet();
1493 for (auto [memoryNode, modRefEffect] :
1494 ModRefSummary_->getModRefSet(externModRefSet).getModRefNodes())
1495 {
1496 // Memory marked as const in the PointsToGraph should not even be in any ModRefSets
1498 JLM_ASSERT(!ModRefSummary_->GetPointsToGraph().isNodeConstant(memoryNode));
1499
1500 // If the memory is modified in this module, it is not read-only
1501 if (mayEffectModify(modRefEffect))
1502 continue;
1503 // If the memory is externally accessible, it is also not read-only
1504 if (ModRefSummary_->GetPointsToGraph().isExternallyAvailable(memoryNode))
1505 continue;
1506 // If the memory node is of type Alloca, it might not actually be read-only,
1507 // as non-reentrant allocas can be partially hidden from reaching the extern \ref ModRefSet.
1508 // This is handled by never treating allocas as "effectively read-only"
1509 if (ModRefSummary_->GetPointsToGraph().getNodeKind(memoryNode)
1511 continue;
1512
1513 Context_->ReadOnlyMemoryNodes.insert(memoryNode);
1514 }
1515}
1516
1517void
1519{
1520 for (auto function : Context_->Functions)
1521 {
1522 materializeSetsInFunction(*function);
1523 }
1524}
1525
1526void
1528{
1529 const auto & pointsToGraph = ModRefSummary_->GetPointsToGraph();
1530 // The ModRefSet representing everying that can be modified from external functions
1531 const auto & externModRefNodes =
1532 ModRefSummary_->getModRefSet(ModRefSummary_->getExternModRefSet()).getModRefNodes();
1533
1534 // Only memory nodes that appear in ModRefSets without the external memory node should be kept
1536
1537 // Among memory nodes that should be kept, the ones flagged externally available are added here.
1538 // When materializing sets, the flags are turned into explicit targets using this list
1539 std::vector<PointsToGraph::NodeIndex> materializeExternallyAvailable;
1540 // When a memory node we need to keep is not externally available, yet is in the
1541 // ModRefSet representing extern functions, the memory node is added to this list.
1542 // It gets materialized in all ModRefSets flagged as possibly calling external functions.
1543 std::vector<std::pair<PointsToGraph::NodeIndex, ModRefEffect>> materializeFromCallToExtern;
1544
1545 const auto markToKeep = [&](PointsToGraph::NodeIndex memoryNode)
1546 {
1547 // Memory nodes discovered to be read-only should not be kept
1548 if (Context_->ReadOnlyMemoryNodes.Contains(memoryNode))
1549 return;
1550
1551 bool inserted = keepMemoryNodes.insert(memoryNode);
1552 if (!inserted)
1553 return;
1554
1555 if (pointsToGraph.isExternallyAvailable(memoryNode))
1556 materializeExternallyAvailable.push_back(memoryNode);
1557 else if (auto it = externModRefNodes.find(memoryNode); it != externModRefNodes.end())
1558 materializeFromCallToExtern.push_back(*it);
1559 };
1560
1562
1563 // Go over all ModRefSets in the function twice
1564 // The first pass determines which memory nodes to keep
1565 const auto & allModRefSets = ModRefSummary_->getAllSetsInFunction(lambda);
1566 for (auto modRefSetIndex : allModRefSets)
1567 {
1568 const auto & modRefSet = ModRefSummary_->getModRefSet(modRefSetIndex);
1569 Context_->numModRefSetsMaterialized++;
1570 Context_->modRefSetSizeBeforeMaterialization += modRefSet.getModRefNodes().size();
1571
1572 auto effectOnExternalNode = modRefSet.getImplicitModRefEffectForExternal(std::nullopt);
1573
1574 // If this ModRefSet may both reference and modify the external memory node,
1575 // it will not disqualify any other memory nodes from compression
1576 if (effectOnExternalNode == ModRefEffect::ModRef)
1577 continue;
1578
1579 for (auto [memoryNode, modRefEffect] : modRefSet.getModRefNodes())
1580 {
1581 JLM_ASSERT(modRefEffect != ModRefEffect::NoEffect);
1582
1583 // If the set has an effect on the memory node that it does not have on the external node,
1584 // the memory node is disqualified from compression
1585 if (!isEffectSubset(modRefEffect, effectOnExternalNode))
1586 markToKeep(memoryNode);
1587 }
1588 }
1589
1590 // Now go over all sets again, removing all memory nodes that are not on the keep list
1591 // and materializing memory nodes that were previously only implicit
1592 for (auto modRefSetIndex : allModRefSets)
1593 {
1594 auto & modRefSet = ModRefSummary_->getModRefSet(modRefSetIndex);
1595 modRefSet.keepSubsetOfExplicitMemoryNodes(keepMemoryNodes);
1596 Context_->modRefSetSizeAfterFiltering += modRefSet.getModRefNodes().size();
1597
1598 // Check what effects are implicitly encoded for externally available memory nodes.
1599 // The flags have a minimum size, so large memory nodes may have more effects than small ones.
1600 // Setting an unknown size gives the largest possible effect set.
1601 auto effectOnAllExternalNodes = modRefSet.getImplicitModRefEffectForExternal(1);
1602 auto effectOnLargeExternalNodes = modRefSet.getImplicitModRefEffectForExternal(std::nullopt);
1603 JLM_ASSERT(isEffectSubset(effectOnAllExternalNodes, effectOnLargeExternalNodes));
1604
1605 if (effectOnLargeExternalNodes == ModRefEffect::NoEffect)
1606 {
1607 // For nodes that do not have any effect on externally available memory,
1608 // we do not need to do any materialization.
1609 Context_->modRefSetSizeAfterMaterialization += modRefSet.getModRefNodes().size();
1610 continue;
1611 }
1612
1613 // If small and large external memory nodes have the same effects,
1614 // we can materialize all external memory nodes with that effect.
1615 if (effectOnLargeExternalNodes == effectOnAllExternalNodes)
1616 {
1617 for (auto memoryNode : materializeExternallyAvailable)
1618 {
1619 modRefSet.addExplicitMemoryNode(memoryNode, effectOnLargeExternalNodes);
1620 }
1621 }
1622 else
1623 {
1624 // Materialize each memory node with the appropriate effects based on its size
1625 for (auto memoryNode : materializeExternallyAvailable)
1626 {
1627 const auto memoryNodeSize = pointsToGraph.tryGetNodeSize(memoryNode);
1628 const auto modRefEffect = modRefSet.getImplicitModRefEffectForExternal(memoryNodeSize);
1629 if (modRefEffect != ModRefEffect::NoEffect)
1630 modRefSet.addExplicitMemoryNode(memoryNode, modRefEffect);
1631 }
1632 }
1633
1634 // If this ModRefSet is not only accessing everything that is externally available,
1635 // but also possibly calling external functions, materialize from the call to external set
1636 if (modRefSet.mayCallExternalFunction())
1637 {
1638 Context_->numModRefSetsCallingExternalFunction++;
1639 for (auto [memoryNode, modRefEffect] : materializeFromCallToExtern)
1640 {
1641 modRefSet.addExplicitMemoryNode(memoryNode, modRefEffect);
1642 }
1643 }
1644
1645 Context_->numModRefSetsWithEffectOnExternal++;
1646 Context_->modRefSetSizeAfterMaterialization += modRefSet.getModRefNodes().size();
1647 }
1648}
1649
1650std::string
1652 const rvsdg::Graph & rvsdg,
1653 const RegionAwareModRefSummary & modRefSummary)
1654{
1655 std::ostringstream ss;
1656
1657 ss << "ExternModRefSet: "
1658 << modRefSummary.getModRefSetDebugString(modRefSummary.getExternModRefSet()) << std::endl;
1659
1660 auto indent = [&](size_t depth, char c = '-')
1661 {
1662 for (size_t i = 0; i < depth; i++)
1663 ss << c;
1664 };
1665
1666 std::function<void(const rvsdg::Node &, size_t)> toRegionTree =
1667 [&](const rvsdg::Node & node, size_t depth)
1668 {
1669 // Simple nodes with no ModRefSet can be ignored
1670 if (dynamic_cast<const rvsdg::SimpleNode *>(&node) && !modRefSummary.hasSetForNode(node))
1671 return;
1672
1673 indent(depth, '-');
1674 ss << "node " << node.DebugString() << " NodeID: " << node.GetNodeId() << ": ";
1675 if (modRefSummary.hasSetForNode(node))
1676 {
1677 auto modRefIndex = modRefSummary.getSetForNode(node);
1678 ss << modRefSummary.getModRefSetDebugString(modRefIndex) << std::endl;
1679 }
1680
1681 if (auto structuralNode = dynamic_cast<const rvsdg::StructuralNode *>(&node))
1682 {
1683 for (auto & region : structuralNode->Subregions())
1684 {
1685 indent(depth + 1, '-');
1686 ss << "RegionID: " << region.getRegionId() << std::endl;
1687 for (auto & n : region.Nodes())
1688 toRegionTree(n, depth + 2);
1689 }
1690 }
1691 };
1692
1693 ss << "RootRegion:" << std::endl;
1694 for (auto & node : rvsdg.GetRootRegion().Nodes())
1695 toRegionTree(node, 0);
1696
1697 return ss.str();
1698}
1699
1700std::unique_ptr<ModRefSummary>
1702 const rvsdg::RvsdgModule & rvsdgModule,
1703 const PointsToGraph & pointsToGraph,
1705{
1706 RegionAwareModRefSummarizer summarizer;
1707 return summarizer.SummarizeModRefs(rvsdgModule, pointsToGraph, statisticsCollector);
1708}
1709
1710std::unique_ptr<ModRefSummary>
1712 const rvsdg::RvsdgModule & rvsdgModule,
1713 const PointsToGraph & pointsToGraph)
1714{
1716 return Create(rvsdgModule, pointsToGraph, statisticsCollector);
1717}
1718}
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 & getAddressInput(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:90
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)
void StopAnnotationStatistics(size_t numFunctions, size_t numFunctionsCallingSetjmp)
static std::unique_ptr< Statistics > Create(const rvsdg::RvsdgModule &rvsdgModule, const PointsToGraph &pointsToGraph)
Statistics(const rvsdg::RvsdgModule &rvsdgModule, const PointsToGraph &pointsToGraph)
void addMemoryStateDistribution(const std::vector< MemoryStateSummary > &memoryStateDistribution)
ModRefSetIndex AnnotateStore(const rvsdg::SimpleNode &storeNode, const rvsdg::LambdaNode &lambda)
ModRefSetIndex AnnotateMalloc(const rvsdg::SimpleNode &mallocNode, const rvsdg::LambdaNode &lambda)
void annotateInterproceduralRegion(const rvsdg::Region &region)
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)
~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)
std::unique_ptr< ModRefSummary > SummarizeModRefs(const rvsdg::RvsdgModule &rvsdgModule, const PointsToGraph &pointsToGraph, util::StatisticsCollector &statisticsCollector) override
util::HashSet< PointsToGraph::NodeIndex > getSimpleAllocasReachableFromRegionArguments(const rvsdg::Region &region)
void addModRefSetSimpleAllocaAllowlist(ModRefSetIndex index, util::HashSet< PointsToGraph::NodeIndex > allowlist)
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
rvsdg::Region * subregion() const noexcept
Definition lambda.hpp:138
Id GetNodeId() const noexcept
Definition node.hpp:600
virtual std::string DebugString() const =0
A phi node represents the fixpoint of mutually recursive definitions.
Definition Phi.hpp:46
rvsdg::Region * subregion() const noexcept
Definition Phi.hpp:320
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
bool IsDemanded(Statistics::Id id) const noexcept
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
bool IsPointerCompatible(const rvsdg::Output &value)
bool isEffectSubset(ModRefEffect subset, ModRefEffect superset)
std::string toString(const std::vector< MemoryStateSummary > &memoryStateDistribution)
bool mayEffectModify(ModRefEffect effect)
static const bool ENABLE_EXTERN_SIMPLE_ALLOCA_ALLOWLIST
bool mayEffectReference(ModRefEffect effect)
static const bool ENABLE_CONSTANT_MEMORY_BLOCKING
static const bool ENABLE_OPERATION_SIZE_BLOCKING
static const bool ENABLE_CALL_SIMPLE_ALLOCA_ALLOWLIST
static const bool ENABLE_FUNCTION_SIMPLE_ALLOCA_ALLOWLIST
std::vector< MemoryStateSummary > collectMemoryStateDistribution(const rvsdg::Graph &rvsdg, const ModRefSummary &modRefSummary)
static util::StatisticsCollector statisticsCollector
size_t GetTypeStoreSize(const rvsdg::Type &type)
Definition types.cpp:386
std::optional< int64_t > tryGetConstantSignedInteger(const rvsdg::Output &output)
Definition Trace.cpp:97
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
std::unordered_map< ModRefSetIndex, util::HashSet< PointsToGraph::NodeIndex > > ModRefSetSimpleAllocaAllowlist
util::HashSet< const rvsdg::LambdaNode * > FunctionsCallingSetjmp
util::HashSet< PointsToGraph::NodeIndex > ReadOnlyMemoryNodes
std::vector< std::vector< ModRefSetIndex > > ModRefSetSimpleConstraints