Skip to content

Commit 3318f8f

Browse files
Cherrypick SCC changes
1 parent fc43f0d commit 3318f8f

File tree

1 file changed

+124
-52
lines changed

1 file changed

+124
-52
lines changed

src/passes/GlobalEffects.cpp

Lines changed: 124 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,161 @@ 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+
std::unordered_map<Function*, int> sccMembers;
168+
std::unordered_map<int, std::optional<EffectAnalyzer>> componentEffects;
169+
170+
int ccIndex = 0;
171+
for (auto ccIterator : sccs) {
172+
ccIndex++;
173+
std::optional<EffectAnalyzer>& ccEffects = componentEffects[ccIndex];
174+
std::vector<Function*> ccFuncs(ccIterator.begin(), ccIterator.end());
125175

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;
176+
ccEffects.emplace(passOptions, module);
177+
178+
for (Function* f : ccFuncs) {
179+
sccMembers.emplace(f, ccIndex);
132180
}
133181

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

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

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

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

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);
220+
// Aggregate effects within this CC
221+
if (ccEffects) {
222+
for (Function* f : ccFuncs) {
223+
const auto& effects = funcInfos.at(f).effects;
224+
if (effects == UnknownEffects) {
225+
ccEffects = UnknownEffects;
226+
break;
227+
}
156228

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

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

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

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-
}
260+
auto callGraph = buildCallGraph(*module, funcInfos);
180261

181-
propagateEffects(*module, callers, funcInfos);
262+
propagateEffects(*module, getPassOptions(), funcInfos, callGraph);
182263

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-
}
264+
copyEffectsToFunctions(funcInfos);
193265
}
194266
};
195267

0 commit comments

Comments
 (0)