@@ -47,10 +47,10 @@ struct FuncInfo {
4747 std::unordered_set<HeapType> indirectCalledTypes;
4848};
4949
50- std::map <Function*, FuncInfo> analyzeFuncs (Module& module ,
51- const PassOptions& passOptions) {
52- ModuleUtils::ParallelFunctionAnalysis<FuncInfo> analysis (
53- module , [&](Function* func, FuncInfo& funcInfo) {
50+ std::unordered_map <Function*, FuncInfo>
51+ analyzeFuncs (Module& module , const PassOptions& passOptions) {
52+ ModuleUtils::ParallelFunctionAnalysis<FuncInfo, Immutable, std::unordered_map>
53+ analysis ( module , [&](Function* func, FuncInfo& funcInfo) {
5454 if (func->imported ()) {
5555 // Imports can do anything, so we need to assume the worst anyhow,
5656 // which is the same as not specifying any effects for them in the
@@ -101,7 +101,7 @@ std::map<Function*, FuncInfo> analyzeFuncs(Module& module,
101101 } else if (auto * callIndirect = curr->dynCast <CallIndirect>()) {
102102 type = callIndirect->heapType ;
103103 } else {
104- assert (false && " Unexpected type of call" );
104+ assert (" Unexpected type of call" );
105105 }
106106
107107 funcInfo.indirectCalledTypes .insert (type);
@@ -123,58 +123,34 @@ std::map<Function*, FuncInfo> analyzeFuncs(Module& module,
123123 return std::move (analysis.map );
124124}
125125
126- // Funcs that can be the target of a virtual call
127- // These are either:
128- // - Part of an (elem declare ...) or (elem ...) directive
129- // - Exported, since they may flow back to us from the host
130- std::unordered_set<Name> getFuncsWithAddress (Module& module ) {
131- std::unordered_set<Name> funcsWithAddress;
132- for (const auto & fun : module .functions ) {
133- funcsWithAddress.insert (fun->name );
134- }
135- return funcsWithAddress;
136-
137- // {
138- // auto refFuncs = TableUtils::getFunctionsNeedingElemDeclare(module);
139- // funcsWithAddress.insert(refFuncs.begin(), refFuncs.end());
140- // }
141-
142- // ElementUtils::iterAllElementFunctionNames(
143- // &module,
144- // [&funcsWithAddress](Name name) { funcsWithAddress.insert(name); });
145- // for (const auto& export_ : module.exports) {
146- // if (export_->kind == ExternalKind::Function) {
147- // // This exported function might flow back to us even in a closed world,
148- // // so it's essentially addressed.
149- // funcsWithAddress.insert(export_->name);
150- // }
151- // }
152-
153- // return funcsWithAddress;
154- }
155-
156126using CallGraphNode = std::variant<Name, HeapType>;
157127
158128// Build a call graph for indirect and direct calls.
159129// key (callee) -> value (caller)
160130// Name -> Name : callee is called directly by caller
161- // Name -> HeapType : callee is a potential target of a virtual call with this HeapType
162- // HeapType -> Name : callee is indirectly called by caller
163- // HeapType -> HeapType : callee is a subtype of caller
164-
165- // TODO: only track indirect calls in closed world
166- std::unordered_map<CallGraphNode, std::unordered_set<CallGraphNode>> buildReverseCallGraph (Module& module , const std::map<Function*, FuncInfo> funcInfos) {
131+ // Name -> HeapType : callee is a potential target of a virtual call
132+ // with this HeapType HeapType -> Name : callee is indirectly called by
133+ // caller HeapType -> HeapType : callee is a subtype of caller If we're
134+ // running in an open world, we only include Name -> Name edges.
135+ std::unordered_map<CallGraphNode, std::unordered_set<CallGraphNode>>
136+ buildReverseCallGraph (Module& module ,
137+ const std::unordered_map<Function*, FuncInfo>& funcInfos,
138+ bool closedWorld) {
167139 // callee : caller
168- std::unordered_map<CallGraphNode, std::unordered_set<CallGraphNode>>
169- callers;
140+ std::unordered_map<CallGraphNode, std::unordered_set<CallGraphNode>> callers;
170141
171- std::unordered_set<HeapType> allIndirectCalledTypes;
142+ if (!closedWorld) {
143+ for (const auto & [func, info] : funcInfos) {
144+ // Name -> Name for direct calls
145+ for (const auto & callee : info.calledFunctions ) {
146+ callers[callee].insert (func->name );
147+ }
148+ }
149+
150+ return callers;
151+ }
172152
173- // Funcs that can be the target of a virtual call
174- // These are either:
175- // - Part of an (elem declare ...) or (elem ...) directive
176- // - Exported, since they may flow back to us from the host
177- std::unordered_set<Name> funcsWithAddress = getFuncsWithAddress (module );
153+ std::unordered_set<HeapType> allIndirectCalledTypes;
178154
179155 for (const auto & [func, info] : funcInfos) {
180156 // Name -> Name for direct calls
@@ -188,16 +164,16 @@ std::unordered_map<CallGraphNode, std::unordered_set<CallGraphNode>> buildRevers
188164 }
189165
190166 // Name -> HeapType for function types
191- if (funcsWithAddress. contains (func-> name )) {
192- callers[func-> name ]. insert (func-> type . getHeapType ());
193- }
167+ // TODO: only look at functions that are addressable
168+ // i.e. appear in a (ref.func) or are exported
169+ callers[func-> name ]. insert (func-> type . getHeapType ());
194170
195171 allIndirectCalledTypes.insert (func->type .getHeapType ());
196172 }
197173
198174 SubTypes subtypes (module );
199175 for (auto type : allIndirectCalledTypes) {
200- subtypes.iterSubTypes (type, [&callers, type](HeapType sub, int _) {
176+ subtypes.iterSubTypes (type, [&callers, type](HeapType sub, Index _) {
201177 // HeapType -> HeapType
202178 // A subtype is a 'callee' of its supertype.
203179 // Supertypes need to inherit effects from their subtypes since they may
@@ -217,19 +193,27 @@ void propagateEffects(
217193 const Module& module ,
218194 const std::unordered_map<CallGraphNode, std::unordered_set<CallGraphNode>>&
219195 reverseCallGraph,
220- std::map <Function*, FuncInfo>& funcInfos) {
196+ std::unordered_map <Function*, FuncInfo>& funcInfos) {
221197
222198 using CallGraphEdge = std::pair<CallGraphNode, CallGraphNode>;
223199 UniqueNonrepeatingDeferredQueue<CallGraphEdge> work;
224200
225201 for (const auto & [callee, callers] : reverseCallGraph) {
202+ // We only care about roots that will lead to a Name -> Name connection
203+ // If there's a HeapType with no Name callee, we don't need to process it
204+ // anyway.
205+ if (!std::holds_alternative<Name>(callee)) {
206+ continue ;
207+ }
226208 for (const auto & caller : callers) {
227209 work.push (std::pair (callee, caller));
228210 }
229211 }
230212
231- auto propagate = [&](const CallGraphNode& calleeNode, const CallGraphNode& callerNode) {
232- if (!std::get_if<Name>(&calleeNode) || !std::get_if<Name>(&callerNode)) {
213+ auto propagate = [&](const CallGraphNode& calleeNode,
214+ const CallGraphNode& callerNode) {
215+ if (!std::holds_alternative<Name>(calleeNode) ||
216+ !std::holds_alternative<Name>(callerNode)) {
233217 return ;
234218 }
235219
@@ -257,15 +241,6 @@ void propagateEffects(
257241 while (!work.empty ()) {
258242 auto [callee, caller] = work.pop ();
259243
260- if (std::get_if<Name>(&callee) == std::get_if<Name>(&caller) &&
261- std::holds_alternative<Name>(callee)) {
262- auto & callerEffects =
263- funcInfos.at (module .getFunction (std::get<Name>(caller))).effects ;
264- if (callerEffects) {
265- callerEffects->trap = true ;
266- }
267- }
268-
269244 // Even if nothing changed, we still need to keep traversing the callers
270245 // to look for a potential cycle which adds a trap affect on the above
271246 // lines.
@@ -276,6 +251,7 @@ void propagateEffects(
276251 continue ;
277252 }
278253
254+ // TODO: handle exact refs here
279255 for (const CallGraphNode& callerCaller : callerCallers->second ) {
280256 work.push (std::pair (callee, callerCaller));
281257 }
@@ -284,21 +260,13 @@ void propagateEffects(
284260
285261struct GenerateGlobalEffects : public Pass {
286262 void run (Module* module ) override {
287- std::map <Function*, FuncInfo> funcInfos =
263+ std::unordered_map <Function*, FuncInfo> funcInfos =
288264 analyzeFuncs (*module , getPassOptions ());
289265
290266 // callee : caller
291267 std::unordered_map<CallGraphNode, std::unordered_set<CallGraphNode>>
292- callers = buildReverseCallGraph (*module , funcInfos);
293-
294- // for (const auto& [callee, callers] : callers) {
295- // for (const auto& caller : callers) {
296- // const auto* calleeName = std::get_if<Name>(&callee);
297- // const auto* callerName = std::get_if<Name>(&caller);
298- // if (!calleeName || !callerName) continue;
299- // std::cout<<*calleeName<<"\t\t->\t\t"<<*callerName<<"\n";
300- // }
301- // }
268+ callers =
269+ buildReverseCallGraph (*module , funcInfos, getPassOptions ().closedWorld );
302270
303271 propagateEffects (*module , callers, funcInfos);
304272
0 commit comments