Skip to content

Commit e7a9067

Browse files
Cherrypick SCC changes
1 parent fc43f0d commit e7a9067

File tree

1 file changed

+125
-52
lines changed

1 file changed

+125
-52
lines changed

src/passes/GlobalEffects.cpp

Lines changed: 125 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
#include "ir/effects.h"
2323
#include "ir/module-utils.h"
2424
#include "pass.h"
25-
#include "support/unique_deferring_queue.h"
25+
#include "support/strongly_connected_components.h"
2626
#include "wasm.h"
2727

2828
namespace wasm {
@@ -107,89 +107,162 @@ std::map<Function*, FuncInfo> analyzeFuncs(Module& module,
107107
return std::move(analysis.map);
108108
}
109109

110+
std::unordered_map<Function*, std::unordered_set<Function*>>
111+
buildCallGraph(const Module& module,
112+
const std::map<Function*, FuncInfo>& funcInfos) {
113+
std::unordered_map<Function*, std::unordered_set<Function*>> callGraph;
114+
for (const auto& [func, info] : funcInfos) {
115+
for (Name callee : info.calledFunctions) {
116+
callGraph[func].insert(module.getFunction(callee));
117+
}
118+
}
119+
120+
return callGraph;
121+
}
122+
110123
// Propagate effects from callees to callers transitively
111124
// e.g. if A -> B -> C (A calls B which calls C)
112125
// Then B inherits effects from C and A inherits effects from both B and C.
113126
void propagateEffects(
114127
const Module& module,
115-
const std::unordered_map<Name, std::unordered_set<Name>>& reverseCallGraph,
116-
std::map<Function*, FuncInfo>& funcInfos) {
117-
118-
UniqueNonrepeatingDeferredQueue<std::pair<Name, Name>> work;
128+
const PassOptions& passOptions,
129+
std::map<Function*, FuncInfo>& funcInfos,
130+
const std::unordered_map<Function*, std::unordered_set<Function*>>
131+
callGraph) {
132+
struct CallGraphSCCs
133+
: SCCs<std::vector<Function*>::const_iterator, CallGraphSCCs> {
134+
const std::map<Function*, FuncInfo>& funcInfos;
135+
const std::unordered_map<Function*, std::unordered_set<Function*>>&
136+
callGraph;
137+
const Module& module;
138+
139+
CallGraphSCCs(
140+
const std::vector<Function*>& funcs,
141+
const std::map<Function*, FuncInfo>& funcInfos,
142+
const std::unordered_map<Function*, std::unordered_set<Function*>>&
143+
callGraph,
144+
const Module& module)
145+
: SCCs<std::vector<Function*>::const_iterator, CallGraphSCCs>(
146+
funcs.begin(), funcs.end()),
147+
funcInfos(funcInfos), callGraph(callGraph), module(module) {}
148+
149+
void pushChildren(Function* f) {
150+
auto callees = callGraph.find(f);
151+
if (callees == callGraph.end()) {
152+
return;
153+
}
119154

120-
for (const auto& [callee, callers] : reverseCallGraph) {
121-
for (const auto& caller : callers) {
122-
work.push(std::pair(callee, caller));
155+
for (auto* callee : callees->second) {
156+
push(callee);
157+
}
123158
}
159+
};
160+
161+
std::vector<Function*> allFuncs;
162+
for (auto& [func, info] : funcInfos) {
163+
allFuncs.push_back(func);
124164
}
165+
CallGraphSCCs sccs(allFuncs, funcInfos, callGraph, module);
166+
167+
// Can we put this in the SCC object somehow?
168+
std::unordered_map<Function*, int> sccMembers;
169+
std::unordered_map<int, std::optional<EffectAnalyzer>> componentEffects;
170+
171+
int ccIndex = 0;
172+
for (auto ccIterator : sccs) {
173+
ccIndex++;
174+
std::optional<EffectAnalyzer>& ccEffects = componentEffects[ccIndex];
175+
std::vector<Function*> ccFuncs(ccIterator.begin(), ccIterator.end());
125176

126-
auto propagate = [&](Name callee, Name caller) {
127-
auto& callerEffects = funcInfos.at(module.getFunction(caller)).effects;
128-
const auto& calleeEffects =
129-
funcInfos.at(module.getFunction(callee)).effects;
130-
if (!callerEffects) {
131-
return;
177+
ccEffects.emplace(passOptions, module);
178+
179+
for (Function* f : ccFuncs) {
180+
sccMembers.emplace(f, ccIndex);
132181
}
133182

134-
if (!calleeEffects) {
135-
callerEffects = UnknownEffects;
136-
return;
183+
std::unordered_set<int> calleeSccs;
184+
for (Function* caller : ccFuncs) {
185+
auto callees = callGraph.find(caller);
186+
if (callees == callGraph.end()) {
187+
continue;
188+
}
189+
for (auto* callee : callees->second) {
190+
calleeSccs.insert(sccMembers.at(callee));
191+
}
137192
}
138193

139-
callerEffects->mergeIn(*calleeEffects);
140-
};
194+
// Merge in effects from callees
195+
for (int calleeScc : calleeSccs) {
196+
const auto& calleeComponentEffects = componentEffects.at(calleeScc);
197+
if (calleeComponentEffects == UnknownEffects) {
198+
ccEffects = UnknownEffects;
199+
break;
200+
}
141201

142-
while (!work.empty()) {
143-
auto [callee, caller] = work.pop();
202+
else if (ccEffects != UnknownEffects) {
203+
ccEffects->mergeIn(*calleeComponentEffects);
204+
}
205+
}
144206

145-
if (callee == caller) {
146-
auto& callerEffects = funcInfos.at(module.getFunction(caller)).effects;
147-
if (callerEffects) {
148-
callerEffects->trap = true;
207+
// Add trap effects for potential cycles.
208+
if (ccFuncs.size() > 1) {
209+
if (ccEffects != UnknownEffects) {
210+
ccEffects->trap = true;
211+
}
212+
} else {
213+
auto* func = ccFuncs[0];
214+
if (funcInfos.at(func).calledFunctions.contains(func->name)) {
215+
if (ccEffects != UnknownEffects) {
216+
ccEffects->trap = true;
217+
}
149218
}
150219
}
151220

152-
// Even if nothing changed, we still need to keep traversing the callers
153-
// to look for a potential cycle which adds a trap affect on the above
154-
// lines.
155-
propagate(callee, caller);
221+
// Aggregate effects within this CC
222+
if (ccEffects) {
223+
for (Function* f : ccFuncs) {
224+
const auto& effects = funcInfos.at(f).effects;
225+
if (effects == UnknownEffects) {
226+
ccEffects = UnknownEffects;
227+
break;
228+
}
156229

157-
const auto& callerCallers = reverseCallGraph.find(caller);
158-
if (callerCallers == reverseCallGraph.end()) {
159-
continue;
230+
ccEffects->mergeIn(*effects);
231+
}
160232
}
161233

162-
for (const Name& callerCaller : callerCallers->second) {
163-
work.push(std::pair(callee, callerCaller));
234+
// Assign each function's effects to its CC effects.
235+
for (Function* f : ccFuncs) {
236+
if (!ccEffects) {
237+
funcInfos.at(f).effects = UnknownEffects;
238+
} else {
239+
funcInfos.at(f).effects.emplace(*ccEffects);
240+
}
164241
}
165242
}
166243
}
167244

245+
void copyEffectsToFunctions(const std::map<Function*, FuncInfo> funcInfos) {
246+
for (auto& [func, info] : funcInfos) {
247+
func->effects.reset();
248+
if (!info.effects) {
249+
continue;
250+
}
251+
252+
func->effects = std::make_shared<EffectAnalyzer>(*info.effects);
253+
}
254+
}
255+
168256
struct GenerateGlobalEffects : public Pass {
169257
void run(Module* module) override {
170258
std::map<Function*, FuncInfo> funcInfos =
171259
analyzeFuncs(*module, getPassOptions());
172260

173-
// callee : caller
174-
std::unordered_map<Name, std::unordered_set<Name>> callers;
175-
for (const auto& [func, info] : funcInfos) {
176-
for (const auto& callee : info.calledFunctions) {
177-
callers[callee].insert(func->name);
178-
}
179-
}
261+
auto callGraph = buildCallGraph(*module, funcInfos);
180262

181-
propagateEffects(*module, callers, funcInfos);
263+
propagateEffects(*module, getPassOptions(), funcInfos, callGraph);
182264

183-
// Generate the final data, starting from a blank slate where nothing is
184-
// known.
185-
for (auto& [func, info] : funcInfos) {
186-
func->effects.reset();
187-
if (!info.effects) {
188-
continue;
189-
}
190-
191-
func->effects = std::make_shared<EffectAnalyzer>(*info.effects);
192-
}
265+
copyEffectsToFunctions(funcInfos);
193266
}
194267
};
195268

0 commit comments

Comments
 (0)