@@ -94,7 +94,7 @@ std::map<Function*, FuncInfo> analyzeFuncs(Module& module,
9494 } else if (auto * callIndirect = curr->dynCast <CallIndirect>()) {
9595 type = callIndirect->heapType ;
9696 } else {
97- assert ( false && " Unexpected type of call" ) ;
97+ Fatal () << " Unexpected call type " ;
9898 }
9999
100100 funcInfo.indirectCalledTypes .insert (type);
@@ -123,34 +123,41 @@ using CallGraphNode = std::variant<Function*, HeapType>;
123123using CallGraph =
124124 std::unordered_map<CallGraphNode, std::unordered_set<CallGraphNode>>;
125125
126+ /* Build a call graph for indirect and direct calls.
127+
128+ key (caller) -> value (callee)
129+ Name -> Name : direct call
130+ Name -> HeapType : indirect call to the given HeapType
131+ HeapType -> Name : The function `callee` has the type `caller`. The
132+ HeapType may essentially 'call' any of its
133+ potential implementations.
134+ HeapType -> HeapType : `callee` is a subtype of `caller`. A call_ref
135+ could target any subtype of the ref, so we need to
136+ aggregate effects of subtypes of the target type.
137+
138+ If we're running in an open world, we only include Name -> Name edges.
139+ */
126140CallGraph buildCallGraph (Module& module ,
127141 const std::map<Function*, FuncInfo>& funcInfos,
128142 bool closedWorld) {
129143 CallGraph callGraph;
130144
131- if (!closedWorld) {
132- for (const auto & [func, info] : funcInfos) {
133- if (info.calledFunctions .empty ()) {
134- continue ;
135- }
136-
137- auto & callees = callGraph[func];
138- for (Name calleeFunction : info.calledFunctions ) {
139- callees.insert (module .getFunction (calleeFunction));
140- }
141- }
142- return callGraph;
143- }
144-
145145 std::unordered_set<HeapType> allFunctionTypes;
146146 for (const auto & [caller, callerInfo] : funcInfos) {
147+ auto & callees = callGraph[caller];
147148 for (Name calleeFunction : callerInfo.calledFunctions ) {
148- callGraph[caller].insert (module .getFunction (calleeFunction));
149+ callees.insert (module .getFunction (calleeFunction));
150+ }
151+
152+ // In open world, just connect functions. Indirect calls are already handled
153+ // by giving such functions unknown effects.
154+ if (!closedWorld) {
155+ continue ;
149156 }
150157
151158 allFunctionTypes.insert (caller->type .getHeapType ());
152159 for (HeapType calleeType : callerInfo.indirectCalledTypes ) {
153- callGraph[caller] .insert (calleeType);
160+ callees .insert (calleeType);
154161 allFunctionTypes.insert (calleeType);
155162 }
156163 callGraph[caller->type .getHeapType ()].insert (caller);
@@ -210,11 +217,7 @@ void propagateEffects(const Module& module,
210217 funcInfos (funcInfos), callGraph(callGraph), module (module ) {}
211218
212219 void pushChildren (CallGraphNode node) {
213- auto callees = callGraph.find (node);
214- if (callees == callGraph.end ()) {
215- return ;
216- }
217- for (CallGraphNode callee : callees->second ) {
220+ for (CallGraphNode callee : callGraph.at (node)) {
218221 push (callee);
219222 }
220223 }
@@ -249,12 +252,7 @@ void propagateEffects(const Module& module,
249252
250253 std::unordered_set<int > calleeSccs;
251254 for (CallGraphNode caller : cc) {
252- auto callees = callGraph.find (caller);
253- if (callees == callGraph.end ()) {
254- continue ;
255- }
256-
257- for (CallGraphNode callee : callees->second ) {
255+ for (CallGraphNode callee : callGraph.at (caller)) {
258256 calleeSccs.insert (funcComponents.at (callee));
259257 }
260258 }
0 commit comments