@@ -120,6 +120,68 @@ std::map<Function*, FuncInfo> analyzeFuncs(Module& module,
120120
121121using CallGraphNode = std::variant<Name, HeapType>;
122122
123+ // Build a call graph for indirect and direct calls.
124+ // key (callee) -> value (caller)
125+ // Name -> Name : callee is called directly by caller
126+ // Name -> HeapType : callee is a potential target of a virtual call with this HeapType
127+ // HeapType -> Name : callee is indirectly called by caller
128+ // HeapType -> HeapType : callee is a subtype of caller
129+ std::unordered_map<CallGraphNode, std::unordered_set<CallGraphNode>> buildReverseCallGraph (Module& module , const std::map<Function*, FuncInfo> funcInfos) {
130+ // callee : caller
131+ std::unordered_map<CallGraphNode, std::unordered_set<CallGraphNode>>
132+ callers;
133+
134+ std::unordered_set<HeapType> allIndirectCalledTypes;
135+
136+ std::unordered_set<Name> funcsWithAddress;
137+
138+ auto refFuncs = TableUtils::getFunctionsNeedingElemDeclare (module );
139+ funcsWithAddress.insert (refFuncs.begin (), refFuncs.end ());
140+ ElementUtils::iterAllElementFunctionNames (
141+ &module ,
142+ [&funcsWithAddress](Name name) { funcsWithAddress.insert (name); });
143+ for (const auto & export_ : module .exports ) {
144+ if (export_->kind == ExternalKind::Function) {
145+ // This exported function might flow back to us even in a closed world,
146+ // so it's essentially addressed.
147+ funcsWithAddress.insert (export_->name );
148+ }
149+ }
150+
151+ for (const auto & [func, info] : funcInfos) {
152+ // Name -> Name for direct calls
153+ for (const auto & callee : info.calledFunctions ) {
154+ callers[callee].insert (func->name );
155+ }
156+
157+ // HeapType -> Name for indirect calls
158+ for (const auto & calleeType : info.indirectCalledTypes ) {
159+ callers[calleeType].insert (func->name );
160+ }
161+
162+ // Name -> HeapType for function types
163+ if (funcsWithAddress.contains (func->name )) {
164+ callers[func->name ].insert (func->type .getHeapType ());
165+ }
166+
167+ allIndirectCalledTypes.insert (func->type .getHeapType ());
168+ }
169+
170+ SubTypes subtypes (module );
171+ for (auto type : allIndirectCalledTypes) {
172+ subtypes.iterSubTypes (type, [&callers, type](HeapType sub, int _) {
173+ // HeapType -> HeapType
174+ // A subtype is a 'callee' of its supertype.
175+ // Supertypes need to inherit effects from their subtypes since they may
176+ // be called via a ref to the subtype.
177+ callers[sub].insert (type);
178+ return true ;
179+ });
180+ }
181+
182+ return callers;
183+ }
184+
123185// Propagate effects from callees to callers transitively
124186// e.g. if A -> B -> C (A calls B which calls C)
125187// Then B inherits effects from C and A inherits effects from both B and C.
@@ -138,14 +200,16 @@ void propagateEffects(
138200 }
139201 }
140202
141- auto propagate = [&](Name* callee, Name* caller ) {
142- if (callee == nullptr || caller == nullptr ) {
203+ auto propagate = [&](const CallGraphNode& calleeNode, const CallGraphNode& callerNode ) {
204+ if (!std::get_if<Name>(&calleeNode) || !std::get_if<Name>(&callerNode) ) {
143205 return ;
144206 }
145207
146- auto & callerEffects = funcInfos.at (module .getFunction (*caller)).effects ;
208+ Name callee = std::get<Name>(calleeNode);
209+ Name caller = std::get<Name>(callerNode);
210+ auto & callerEffects = funcInfos.at (module .getFunction (caller)).effects ;
147211 const auto & calleeEffects =
148- funcInfos.at (module .getFunction (* callee)).effects ;
212+ funcInfos.at (module .getFunction (callee)).effects ;
149213 if (callerEffects == UnknownEffects) {
150214 return ;
151215 }
@@ -155,7 +219,7 @@ void propagateEffects(
155219 return ;
156220 }
157221
158- if (* callee == * caller) {
222+ if (callee == caller) {
159223 callerEffects->trap = true ;
160224 } else {
161225 callerEffects->mergeIn (*calleeEffects);
@@ -177,7 +241,7 @@ void propagateEffects(
177241 // Even if nothing changed, we still need to keep traversing the callers
178242 // to look for a potential cycle which adds a trap affect on the above
179243 // lines.
180- propagate (std::get_if<Name>(& callee), std::get_if<Name>(& caller) );
244+ propagate (callee, caller);
181245
182246 const auto & callerCallers = reverseCallGraph.find (caller);
183247 if (callerCallers == reverseCallGraph.end ()) {
@@ -197,55 +261,7 @@ struct GenerateGlobalEffects : public Pass {
197261
198262 // callee : caller
199263 std::unordered_map<CallGraphNode, std::unordered_set<CallGraphNode>>
200- callers;
201-
202- std::unordered_set<HeapType> allIndirectCalledTypes;
203-
204- std::unordered_set<Name> funcsWithAddress;
205-
206- auto refFuncs = TableUtils::getFunctionsNeedingElemDeclare (*module );
207- funcsWithAddress.insert (refFuncs.begin (), refFuncs.end ());
208- ElementUtils::iterAllElementFunctionNames (
209- module ,
210- [&funcsWithAddress](Name name) { funcsWithAddress.insert (name); });
211- for (const auto & export_ : module ->exports ) {
212- if (export_->kind == ExternalKind::Function) {
213- // This exported function might flow back to us even in a closed world,
214- // so it's essentially addressed.
215- funcsWithAddress.insert (export_->name );
216- }
217- }
218-
219- for (const auto & [func, info] : funcInfos) {
220- // Name -> Name for direct calls
221- for (const auto & callee : info.calledFunctions ) {
222- callers[callee].insert (func->name );
223- }
224-
225- // HeapType -> Name for indirect calls
226- for (const auto & calleeType : info.indirectCalledTypes ) {
227- callers[calleeType].insert (func->name );
228- }
229-
230- // Name -> HeapType for function types
231- if (funcsWithAddress.contains (func->name )) {
232- callers[func->name ].insert (func->type .getHeapType ());
233- }
234-
235- allIndirectCalledTypes.insert (func->type .getHeapType ());
236- }
237-
238- SubTypes subtypes (*module );
239- for (auto type : allIndirectCalledTypes) {
240- subtypes.iterSubTypes (type, [&callers, type](HeapType sub, int _) {
241- // HeapType -> HeapType
242- // A subtype is a 'callee' of its supertype.
243- // Supertypes need to inherit effects from their subtypes since they may
244- // be called via a ref to the subtype.
245- callers[sub].insert (type);
246- return true ;
247- });
248- }
264+ callers = buildReverseCallGraph (*module , funcInfos);
249265
250266 propagateEffects (*module , callers, funcInfos);
251267
0 commit comments