@@ -113,8 +113,9 @@ std::map<Function*, FuncInfo> analyzeFuncs(Module& module,
113113 return analysis.map ;
114114}
115115
116- template <typename T>
117- std::unordered_map<T, std::unordered_set<T>> transitiveClosure2 (const std::unordered_map<T, std::unordered_set<T>>& in) {
116+ template <typename T>
117+ std::unordered_map<T, std::unordered_set<T>>
118+ transitiveClosure2 (const std::unordered_map<T, std::unordered_set<T>>& in) {
118119 UniqueNonrepeatingDeferredQueue<std::pair<T, T>> work;
119120
120121 for (const auto & [curr, neighbors] : in) {
@@ -130,7 +131,8 @@ std::unordered_map<T, std::unordered_set<T>> transitiveClosure2(const std::unord
130131 closure[curr].insert (neighbor);
131132
132133 auto neighborNeighbors = in.find (neighbor);
133- if (neighborNeighbors == in.end ()) continue ;
134+ if (neighborNeighbors == in.end ())
135+ continue ;
134136 for (const auto & neighborNeighbor : neighborNeighbors->second ) {
135137 work.push ({curr, neighborNeighbor});
136138 }
@@ -193,25 +195,9 @@ transitiveClosure(const Module& module,
193195 return callers;
194196}
195197
196- // std::unordered_map<Name, std::unordered_set<Name>> transitiveClosure(
197- // const Module& module,
198- // const std::unordered_map<Name, std::unordered_set<Name>>& funcInfos) {
199- // auto transformed =
200- // funcInfos | std::views::transform(
201- // [&](const auto& pair) -> std::pair<Function*, FuncInfo> {
202- // auto& [k, v] = pair;
203-
204- // auto* func = module.getFunction(k);
205- // FuncInfo info;
206- // info.calledFunctions = v;
207- // return {func, info};
208- // });
209- // std::map<Function*, FuncInfo> other(transformed.begin(), transformed.end());
210- // return transitiveClosure(module, other);
211- // }
212-
213- template <typename K, typename V>
214- std::unordered_map<V, std::unordered_set<K>> flip (const std::unordered_map<K, std::unordered_set<V>>& in) {
198+ template <typename K, typename V>
199+ std::unordered_map<V, std::unordered_set<K>>
200+ flip (const std::unordered_map<K, std::unordered_set<V>>& in) {
215201 std::unordered_map<V, std::unordered_set<K>> flipped;
216202 for (const auto & [k, vs] : in) {
217203 for (const auto & v : vs) {
@@ -221,75 +207,73 @@ std::unordered_map<V, std::unordered_set<K>> flip(const std::unordered_map<K, st
221207 return flipped;
222208}
223209
224- // std::unordered_map<HeapType, std::unordered_set<Name>>
225-
226- struct GenerateGlobalEffects : public Pass {
227- void run (Module* module ) override {
228- // First, we do a scan of each function to see what effects they have,
229- // including which functions they call directly (so that we can compute
230- // transitive effects later).
231- auto funcInfos = analyzeFuncs (*module , getPassOptions ());
232-
233- // Find the 'entry points' of indirect calls Name -> HeapType.
234- // At the same time, start recording connections for the transitive closure of indirect calls
235- // HeapType -> HeapType. This will be used to find the set of HeapTypes that are reachable via any sequence of indirect calls from a given function
236- // (Name -> HeapType)
237- std::unordered_map<Name, std::unordered_set<HeapType>>
238- indirectCallersNonTransitive;
239- std::unordered_map<HeapType, std::unordered_set<HeapType>> indirectCalls;
240- for (auto & [func, info] : funcInfos) {
241- auto & set = indirectCallersNonTransitive[func->name ];
242- auto & indirectCallsSet = indirectCalls[func->type .getHeapType ()];
243- for (auto & calledType : info.indirectCalledTypes ) {
244- set.insert (calledType);
245- indirectCallsSet.insert (calledType);
246- }
210+ std::unordered_map<HeapType, std::unordered_set<Name>>
211+ callersOfHeapType (std::map<Function*, FuncInfo> funcInfos,
212+ const SubTypes& subtypes) {
213+ // Find the 'entry points' of indirect calls Name -> HeapType.
214+ // At the same time, start recording connections for the transitive closure of
215+ // indirect calls HeapType -> HeapType. This will be used to find the set of
216+ // HeapTypes that are reachable via any sequence of indirect calls from a
217+ // given function (Name -> HeapType)
218+ std::unordered_map<Name, std::unordered_set<HeapType>>
219+ indirectCallersNonTransitive;
220+ std::unordered_map<HeapType, std::unordered_set<HeapType>> indirectCalls;
221+ for (auto & [func, info] : funcInfos) {
222+ auto & set = indirectCallersNonTransitive[func->name ];
223+ auto & indirectCallsSet = indirectCalls[func->type .getHeapType ()];
224+ for (auto & calledType : info.indirectCalledTypes ) {
225+ set.insert (calledType);
226+ indirectCallsSet.insert (calledType);
247227 }
228+ }
248229
249- // indirectCallers[foo] = [func that indirect calls something with the same
250- // type as foo, ..]
251- // const std::unordered_map<HeapType, std::unordered_set<Name>> indirectCallers =
252- // transitiveClosure(*module, indirectCallersNonTransitive);
230+ std::unordered_map<HeapType, std::unordered_set<HeapType>> subTypesToAdd;
253231
254- // TODO: need to take subtypes into account here
255- // we can pretend that each type 'indirect calls' its subtypes
256- // This is good enough because when querying the particular function Name that
257- // indirect calls someone we want to take its indirect calls into account anyway
258- // So just pretend that it indirect calls its subtypes.
232+ for (const auto & [type, _] : indirectCalls) {
233+ subtypes.iterSubTypes (type, [&subTypesToAdd, type](HeapType sub, int _) {
234+ subTypesToAdd[type].insert (sub);
235+ return true ;
236+ });
237+ }
259238
260- SubTypes subtypes (* module );
261- std::unordered_map<HeapType, std::unordered_set<HeapType>> subTypesToAdd ;
239+ for ( const auto & [k, v] : subTypesToAdd) {
240+ auto it = indirectCalls. find (k) ;
262241
263- for (const auto & [type, _] : indirectCalls) {
264- subtypes.iterSubTypes (type, [&subTypesToAdd, type](HeapType sub, int _) { subTypesToAdd[type].insert (sub); return true ; });
265- }
242+ // No need to add this. It wasn't in the map because no function has this
243+ // type, so there are no effects to aggregate and we can forget about it.
244+ if (it == indirectCalls.end ())
245+ continue ;
266246
267- for ( const auto & [k , v] : subTypesToAdd) {
268- auto it = indirectCalls. find (k);
247+ it-> second . insert (v. begin () , v. end ());
248+ }
269249
270- // No need to add this. It wasn't in the map because no function has this
271- // type, so there are no effects to aggregate and we can forget about it.
272- if (it == indirectCalls.end ()) continue ;
250+ auto a = transitiveClosure2<HeapType>(indirectCalls);
273251
274- it->second .insert (v.begin (), v.end ());
275- }
252+ // Pretend that each subtype is indirect called by its supertype.
253+ // This might not be the case but it's accurate enough since any caller that
254+ // may indirect call a given type may also indirect call its subtype.
255+ for (const auto & [k, v] : indirectCallersNonTransitive) {
256+ for (const auto & x : v) {
257+ auto y = a[x];
276258
277- auto a = transitiveClosure2<HeapType>(indirectCalls);
259+ // we're leaving what was already there but should be fine
260+ // since it's covered under transitive calls anyway
261+ indirectCallersNonTransitive[k].insert (y.begin (), y.end ());
262+ }
263+ }
278264
279- // Pretend that each subtype is indirect called by its supertype.
280- // This might not be the case but it's accurate enough since any caller that
281- // may indirect call a given type may also indirect call its subtype.
282- for (const auto & [k, v]: indirectCallersNonTransitive) {
283- for (const auto & x : v) {
284- auto y = a[x];
265+ return flip (indirectCallersNonTransitive);
266+ }
285267
286- // we're leaving what was already there but should be fine
287- // since it's covered under transitive calls anyway
288- indirectCallersNonTransitive[k].insert (y.begin (), y.end ());
289- }
290- }
268+ struct GenerateGlobalEffects : public Pass {
269+ void run (Module* module ) override {
270+ // First, we do a scan of each function to see what effects they have,
271+ // including which functions they call directly (so that we can compute
272+ // transitive effects later).
273+ auto funcInfos = analyzeFuncs (*module , getPassOptions ());
291274
292- std::unordered_map<HeapType, std::unordered_set<Name>> flipped = flip (indirectCallersNonTransitive);
275+ SubTypes subtypes (*module );
276+ auto indirectCallers = callersOfHeapType (funcInfos, subtypes);
293277
294278 // Compute the transitive closure of effects. To do so, first construct for
295279 // each function a list of the functions that it is called by (so we need to
@@ -301,7 +285,6 @@ struct GenerateGlobalEffects : public Pass {
301285 std::unordered_map<Name, std::unordered_set<Name>> callers =
302286 transitiveClosure (*module , funcInfos);
303287
304-
305288 // Now that we have transitively propagated all static calls, apply that
306289 // information. First, apply infinite recursion: if a function can call
307290 // itself then it might recurse infinitely, which we consider an effect (a
@@ -336,8 +319,9 @@ struct GenerateGlobalEffects : public Pass {
336319 callerEffects->mergeIn (*funcEffects);
337320 }
338321
339- auto indirectCallersOfThisFunction = flipped.find (func->type .getHeapType ());
340- if (indirectCallersOfThisFunction == flipped.end ()) {
322+ auto indirectCallersOfThisFunction =
323+ indirectCallers.find (func->type .getHeapType ());
324+ if (indirectCallersOfThisFunction == indirectCallers.end ()) {
341325 continue ;
342326 }
343327 for (Name caller : indirectCallersOfThisFunction->second ) {
0 commit comments