@@ -122,24 +122,27 @@ std::map<Function*, FuncInfo> analyzeFuncs(Module& module,
122122}
123123
124124using CallGraphNode = std::variant<Function*, HeapType>;
125- using CallGraph =
126- std::unordered_map<CallGraphNode, std::unordered_set<CallGraphNode>>;
127125
128126/*
129- Build a call graph for indirect and direct calls.
127+ Call graph for indirect and direct calls.
130128
131129 key (caller) -> value (callee)
132- Name -> Name : direct call
133- Name -> HeapType : indirect call to the given HeapType
134- HeapType -> Name : The function `callee` has the type `caller`. The
135- HeapType may essentially 'call' any of its
136- potential implementations.
137- HeapType -> HeapType : `callee` is a subtype of `caller`. A call_ref
138- could target any subtype of the ref, so we need to
139- aggregate effects of subtypes of the target type.
140-
141- If we're running in an open world, we only include Name -> Name edges.
130+ Function -> Function : direct call
131+ Function -> HeapType : indirect call to the given HeapType
132+ HeapType -> Function : The function `callee` has the type `caller`. The
133+ HeapType may essentially 'call' any of its
134+ potential implementations.
135+ HeapType -> HeapType : `callee` is a subtype of `caller`. A call_ref
136+ could target any subtype of the ref, so we need to
137+ aggregate effects of subtypes of the target type.
138+
139+ If we're running in an open world, we only include Function -> Function edges,
140+ and don't compute effects for indirect calls, conservatively assuming the
141+ worst.
142142*/
143+ using CallGraph =
144+ std::unordered_map<CallGraphNode, std::unordered_set<CallGraphNode>>;
145+
143146CallGraph buildCallGraph (const Module& module ,
144147 const std::map<Function*, FuncInfo>& funcInfos,
145148 bool closedWorld) {
@@ -149,7 +152,7 @@ CallGraph buildCallGraph(const Module& module,
149152 for (const auto & [caller, callerInfo] : funcInfos) {
150153 auto & callees = callGraph[caller];
151154
152- // Name -> Name
155+ // Function -> Function
153156 for (Name calleeFunction : callerInfo.calledFunctions ) {
154157 callees.insert (module .getFunction (calleeFunction));
155158 }
@@ -158,24 +161,26 @@ CallGraph buildCallGraph(const Module& module,
158161 continue ;
159162 }
160163
161- // Name -> Type
164+ // Function -> Type
162165 allFunctionTypes.insert (caller->type .getHeapType ());
163166 for (HeapType calleeType : callerInfo.indirectCalledTypes ) {
164167 callees.insert (calleeType);
165168 allFunctionTypes.insert (calleeType);
166169 }
167170
168- // Type -> Name
171+ // Type -> Function
169172 callGraph[caller->type .getHeapType ()].insert (caller);
170173 }
171174
172175 // Type -> Type
173176 for (HeapType type : allFunctionTypes) {
174- // Not needed but during lookup we expect the key to exist.
177+ // Not needed except that during lookup we expect the key to exist.
175178 callGraph[type];
176179
177180 for (auto super = type.getDeclaredSuperType (); super;
178181 super = super->getDeclaredSuperType ()) {
182+ // Don't bother noting supertypes with no functions in it. There are no
183+ // effects to aggregate anyway.
179184 if (allFunctionTypes.contains (*super)) {
180185 callGraph[*super].insert (type);
181186 }
@@ -244,7 +249,7 @@ void propagateEffects(const Module& module,
244249 const PassOptions& passOptions,
245250 std::map<Function*, FuncInfo>& funcInfos,
246251 const CallGraph& callGraph) {
247- // We only care about Functions that are roots, not types
252+ // We only care about Functions that are roots, not types.
248253 // A type would be a root if a function exists with that type, but no-one
249254 // indirect calls the type.
250255 auto funcNodes = std::views::keys (callGraph) |
0 commit comments