Skip to content

Commit 7fd7418

Browse files
Done but not working
1 parent 513362c commit 7fd7418

File tree

3 files changed

+133
-55
lines changed

3 files changed

+133
-55
lines changed

src/passes/GlobalEffects.cpp

Lines changed: 133 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
// PassOptions structure; see more details there.
2020
//
2121

22+
#include <ranges>
23+
2224
#include "ir/effects.h"
2325
#include "ir/module-utils.h"
2426
#include "pass.h"
@@ -92,7 +94,7 @@ std::map<Function*, FuncInfo> analyzeFuncs(Module& module,
9294
}
9395

9496
funcInfo.indirectCalledTypes.insert(type);
95-
funcInfo.effects.reset();
97+
// funcInfo.effects.reset();
9698
} else {
9799
// No call here, but update throwing if we see it. (Only do so,
98100
// however, if we have effects; if we cleared it - see before -
@@ -111,59 +113,131 @@ std::map<Function*, FuncInfo> analyzeFuncs(Module& module,
111113
return analysis.map;
112114
}
113115

116+
std::unordered_map<HeapType, std::unordered_set<Name>>
117+
typeToFunctionNames(const Module& module) {
118+
std::unordered_map<HeapType, std::unordered_set<Name>> ret;
119+
120+
for (const auto& func : module.functions) {
121+
ret[func->type.getHeapType()].insert(func->name);
122+
}
123+
124+
return ret;
125+
}
126+
127+
std::unordered_map<Name, std::unordered_set<Name>>
128+
transitiveClosure(const Module& module,
129+
const std::map<Function*, FuncInfo>& funcInfos) {
130+
// Compute the transitive closure of effects. To do so, first construct for
131+
// each function a list of the functions that it is called by (so we need to
132+
// propogate its effects to them), and then we'll construct the closure of
133+
// that.
134+
//
135+
// callers[foo] = [func that calls foo, another func that calls foo, ..]
136+
//
137+
std::unordered_map<Name, std::unordered_set<Name>> callers;
138+
139+
// Our work queue contains info about a new call pair: a call from a caller
140+
// to a called function, that is information we then apply and propagate.
141+
using CallPair = std::pair<Name, Name>; // { caller, called }
142+
UniqueDeferredQueue<CallPair> work;
143+
for (auto& [func, info] : funcInfos) {
144+
for (auto& called : info.calledFunctions) {
145+
work.push({func->name, called});
146+
}
147+
}
148+
149+
// Compute the transitive closure of the call graph, that is, fill out
150+
// |callers| so that it contains the list of all callers - even through a
151+
// chain - of each function.
152+
while (!work.empty()) {
153+
auto [caller, called] = work.pop();
154+
155+
// We must not already have an entry for this call (that would imply we
156+
// are doing wasted work).
157+
assert(!callers[called].contains(caller));
158+
159+
// Apply the new call information.
160+
callers[called].insert(caller);
161+
162+
// We just learned that |caller| calls |called|. It also calls
163+
// transitively, which we need to propagate to all places unaware of that
164+
// information yet.
165+
//
166+
// caller => called => called by called
167+
//
168+
auto& calledInfo = funcInfos.at(module.getFunction(called));
169+
for (auto calledByCalled : calledInfo.calledFunctions) {
170+
if (!callers[calledByCalled].contains(caller)) {
171+
work.push({caller, calledByCalled});
172+
}
173+
}
174+
}
175+
176+
return callers;
177+
}
178+
179+
std::unordered_map<Name, std::unordered_set<Name>> transitiveClosure(
180+
const Module& module,
181+
const std::unordered_map<Name, std::unordered_set<Name>>& funcInfos) {
182+
std::map<Function*, FuncInfo> other;
183+
auto _ =
184+
funcInfos | std::views::transform(
185+
[&](const auto& pair) -> std::pair<Function*, FuncInfo> {
186+
auto& [k, v] = pair;
187+
188+
auto& func = module.getFunction(k);
189+
FuncInfo info;
190+
info.calledFunctions = v;
191+
return {func->name, info};
192+
});
193+
194+
return transitiveClosure(module, other);
195+
}
196+
114197
struct GenerateGlobalEffects : public Pass {
115198
void run(Module* module) override {
116199
// First, we do a scan of each function to see what effects they have,
117200
// including which functions they call directly (so that we can compute
118201
// transitive effects later).
119202
auto funcInfos = analyzeFuncs(*module, getPassOptions());
120203

121-
// Our work queue contains info about a new call pair: a call from a caller
122-
// to a called function, that is information we then apply and propagate.
123-
using CallPair = std::pair<Name, Name>; // { caller, called }
124-
UniqueDeferredQueue<CallPair> work;
125-
for (auto& [func, info] : funcInfos) {
126-
for (auto& called : info.calledFunctions) {
127-
work.push({func->name, called});
128-
}
129-
}
130-
131204
// Compute the transitive closure of effects. To do so, first construct for
132205
// each function a list of the functions that it is called by (so we need to
133206
// propagate its effects to them), and then we'll construct the closure of
134207
// that.
135208
//
136209
// callers[foo] = [func that calls foo, another func that calls foo, ..]
137210
//
138-
std::unordered_map<Name, std::unordered_set<Name>> callers;
139-
140-
// Compute the transitive closure of the call graph, that is, fill out
141-
// |callers| so that it contains the list of all callers - even through a
142-
// chain - of each function.
143-
while (!work.empty()) {
144-
auto [caller, called] = work.pop();
145-
146-
// We must not already have an entry for this call (that would imply we
147-
// are doing wasted work).
148-
assert(!callers[called].contains(caller));
149-
150-
// Apply the new call information.
151-
callers[called].insert(caller);
152-
153-
// We just learned that |caller| calls |called|. It also calls
154-
// transitively, which we need to propagate to all places unaware of that
155-
// information yet.
156-
//
157-
// caller => called => called by called
158-
//
159-
auto& calledInfo = funcInfos[module->getFunction(called)];
160-
for (auto calledByCalled : calledInfo.calledFunctions) {
161-
if (!callers[calledByCalled].contains(caller)) {
162-
work.push({caller, calledByCalled});
211+
std::unordered_map<Name, std::unordered_set<Name>> callers =
212+
transitiveClosure(*module, funcInfos);
213+
214+
const auto functionsWithType = typeToFunctionNames(*module);
215+
std::unordered_map<Name, std::unordered_set<Name>>
216+
indirectCallersNonTransitive;
217+
for (auto& [func, info] : funcInfos) {
218+
for (auto& calledType : info.indirectCalledTypes) {
219+
// auto asdf = functionsWithType.at(calledType);
220+
// auto foo = indirectCallersNonTransitive[func->name];
221+
// asdf.merge(foo);
222+
// foo.merge(asdf);
223+
224+
if (auto it = functionsWithType.find(calledType);
225+
it != functionsWithType.end()) {
226+
indirectCallersNonTransitive[func->name].insert(it->second.begin(),
227+
it->second.end());
163228
}
229+
// indirectCallersNonTransitive[func->name].merge(functionsWithType.at(calledType));
164230
}
231+
// for (const auto& name : functionsWitType[])
232+
// for ()
233+
// info.indirectCalledTypes[func->name]
165234
}
166235

236+
// indirectCallers[foo] = [func that indirect calls something with the same
237+
// type as foo, ..]
238+
const std::unordered_map<Name, std::unordered_set<Name>> indirectCallers =
239+
transitiveClosure(*module, indirectCallersNonTransitive);
240+
167241
// Now that we have transitively propagated all static calls, apply that
168242
// information. First, apply infinite recursion: if a function can call
169243
// itself then it might recurse infinitely, which we consider an effect (a
@@ -180,7 +254,29 @@ struct GenerateGlobalEffects : public Pass {
180254
for (auto& [func, info] : funcInfos) {
181255
auto& funcEffects = info.effects;
182256

183-
for (auto& caller : callers[func->name]) {
257+
for (const auto& caller : callers[func->name]) {
258+
auto& callerEffects = funcInfos[module->getFunction(caller)].effects;
259+
if (!callerEffects) {
260+
// Nothing is known for the caller, which is already the worst case.
261+
continue;
262+
}
263+
264+
if (!funcEffects) {
265+
// Nothing is known for the called function, which means nothing is
266+
// known for the caller either.
267+
callerEffects.reset();
268+
continue;
269+
}
270+
271+
// Add func's effects to the caller.
272+
callerEffects->mergeIn(*funcEffects);
273+
}
274+
275+
auto indirectCallersOfThisFunction = indirectCallers.find(func->name);
276+
if (indirectCallersOfThisFunction == indirectCallers.end()) {
277+
continue;
278+
}
279+
for (Name caller : indirectCallersOfThisFunction->second) {
184280
auto& callerEffects = funcInfos[module->getFunction(caller)].effects;
185281
if (!callerEffects) {
186282
// Nothing is known for the caller, which is already the worst case.

test/lit/passes/global-effects-closed-world.wast

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,6 @@
4848
)
4949

5050
;; CHECK: (func $f (type $3) (param $ref (ref $nopType)) (result i32)
51-
;; CHECK-NEXT: (call $calls-nop-via-ref
52-
;; CHECK-NEXT: (local.get $ref)
53-
;; CHECK-NEXT: )
5451
;; CHECK-NEXT: (i32.const 1)
5552
;; CHECK-NEXT: )
5653
(func $f (param $ref (ref $nopType)) (result i32)
@@ -72,9 +69,6 @@
7269
)
7370

7471
;; CHECK: (func $g (type $5) (param $ref (ref $maybe-has-effects)) (result i32)
75-
;; CHECK-NEXT: (call $calls-effectful-function-via-ref
76-
;; CHECK-NEXT: (local.get $ref)
77-
;; CHECK-NEXT: )
7872
;; CHECK-NEXT: (i32.const 1)
7973
;; CHECK-NEXT: )
8074
(func $g (param $ref (ref $maybe-has-effects)) (result i32)

test/lit/passes/global-effects.wast

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -315,19 +315,7 @@
315315
;; INCLUDE-NEXT: (call $return-call-throw-and-catch)
316316
;; INCLUDE-NEXT: )
317317
;; INCLUDE-NEXT: )
318-
;; INCLUDE-NEXT: (block $tryend0
319-
;; INCLUDE-NEXT: (try_table (catch_all $tryend0)
320-
;; INCLUDE-NEXT: (call $return-call-indirect-throw-and-catch)
321-
;; INCLUDE-NEXT: )
322-
;; INCLUDE-NEXT: )
323-
;; INCLUDE-NEXT: (block $tryend1
324-
;; INCLUDE-NEXT: (try_table (catch_all $tryend1)
325-
;; INCLUDE-NEXT: (call $return-call-ref-throw-and-catch)
326-
;; INCLUDE-NEXT: )
327-
;; INCLUDE-NEXT: )
328318
;; INCLUDE-NEXT: (call $return-call-throw-and-catch)
329-
;; INCLUDE-NEXT: (call $return-call-indirect-throw-and-catch)
330-
;; INCLUDE-NEXT: (call $return-call-ref-throw-and-catch)
331319
;; INCLUDE-NEXT: )
332320
(func $call-return-call-throw-and-catch
333321
(block $tryend

0 commit comments

Comments
 (0)