Skip to content

Commit 89fa118

Browse files
more cleanup
1 parent 3311a77 commit 89fa118

File tree

1 file changed

+66
-82
lines changed

1 file changed

+66
-82
lines changed

src/passes/GlobalEffects.cpp

Lines changed: 66 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)