Skip to content

Commit 1a0530d

Browse files
authored
Handle indirect calls in CallGraphPropertyAnalysis (#2624)
We ignored them, which is a bad default, as typically they imply we can call anything in the table (and the table might change). Instead, notice indirect calls during traversal, and force the user to decide whether to ignore them or not. This was only an issue in PostEmscripten because the other user, Asyncify, already had indirect call analysis because it needed it for other things. Fixes a bug uncovered by #2619 and fixes the current binaryen roll.
1 parent 1ca6394 commit 1a0530d

File tree

5 files changed

+64
-5
lines changed

5 files changed

+64
-5
lines changed

src/ir/module-utils.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ template<typename T> struct CallGraphPropertyAnalysis {
341341
struct FunctionInfo {
342342
std::set<Function*> callsTo;
343343
std::set<Function*> calledBy;
344+
bool hasIndirectCall = false;
344345
};
345346

346347
typedef std::map<Function*, T> Map;
@@ -362,6 +363,10 @@ template<typename T> struct CallGraphPropertyAnalysis {
362363
info.callsTo.insert(module->getFunction(curr->target));
363364
}
364365

366+
void visitCallIndirect(CallIndirect* curr) {
367+
info.hasIndirectCall = true;
368+
}
369+
365370
private:
366371
Module* module;
367372
T& info;
@@ -382,14 +387,20 @@ template<typename T> struct CallGraphPropertyAnalysis {
382387
}
383388
}
384389

390+
enum IndirectCalls { IgnoreIndirectCalls, IndirectCallsHaveProperty };
391+
385392
// Propagate a property from a function to those that call it.
386393
void propagateBack(std::function<bool(const T&)> hasProperty,
387394
std::function<bool(const T&)> canHaveProperty,
388-
std::function<void(T&)> addProperty) {
395+
std::function<void(T&)> addProperty,
396+
IndirectCalls indirectCalls) {
389397
// The work queue contains items we just learned can change the state.
390398
UniqueDeferredQueue<Function*> work;
391399
for (auto& func : wasm.functions) {
392-
if (hasProperty(map[func.get()])) {
400+
if (hasProperty(map[func.get()]) ||
401+
(indirectCalls == IndirectCallsHaveProperty &&
402+
map[func.get()].hasIndirectCall)) {
403+
addProperty(map[func.get()]);
393404
work.push(func.get());
394405
}
395406
}

src/passes/Asyncify.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,8 @@ class ModuleAnalyzer {
549549
return !info.isBottomMostRuntime &&
550550
!info.inBlacklist;
551551
},
552-
[](Info& info) { info.canChangeState = true; });
552+
[](Info& info) { info.canChangeState = true; },
553+
scanner.IgnoreIndirectCalls);
553554

554555
map.swap(scanner.map);
555556

src/passes/PostEmscripten.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,11 @@ struct PostEmscripten : public Pass {
154154
}
155155
});
156156

157+
// Assume an indirect call might throw.
157158
analyzer.propagateBack([](const Info& info) { return info.canThrow; },
158159
[](const Info& info) { return true; },
159-
[](Info& info) { info.canThrow = true; });
160+
[](Info& info) { info.canThrow = true; },
161+
analyzer.IndirectCallsHaveProperty);
160162

161163
// Apply the information.
162164
struct OptimizeInvokes : public WalkerPass<PostWalker<OptimizeInvokes>> {

test/passes/post-emscripten.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,27 @@
152152
(nop)
153153
)
154154
)
155+
(module
156+
(type $none_=>_none (func))
157+
(type $i32_i32_f32_=>_none (func (param i32 i32 f32)))
158+
(type $i32_f32_=>_none (func (param i32 f32)))
159+
(type $f64_f64_=>_f64 (func (param f64 f64) (result f64)))
160+
(import "env" "glob" (global $glob i32))
161+
(import "global.Math" "pow" (func $Math_pow (param f64 f64) (result f64)))
162+
(import "env" "invoke_vif" (func $invoke_vif (param i32 i32 f32)))
163+
(memory $0 256 256)
164+
(table $0 7 7 funcref)
165+
(elem (i32.const 0) $other_safe)
166+
(func $exc (; 2 ;)
167+
(call $invoke_vif
168+
(i32.const 0)
169+
(i32.const 42)
170+
(f32.const 3.141590118408203)
171+
)
172+
)
173+
(func $other_safe (; 3 ;) (param $0 i32) (param $1 f32)
174+
(call_indirect (type $none_=>_none)
175+
(i32.const 0)
176+
)
177+
)
178+
)

test/passes/post-emscripten.wast

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
(call $call)
110110
)
111111
)
112-
(module
112+
(module ;; non-constant base for elem
113113
(type $0 (func (param i32)))
114114
(import "global.Math" "pow" (func $Math_pow (param f64 f64) (result f64)))
115115
(import "env" "invoke_vif" (func $invoke_vif (param i32 i32 f32)))
@@ -127,3 +127,24 @@
127127
(func $other_safe (param i32) (param f32)
128128
)
129129
)
130+
(module ;; indirect call in the invoke target, which we assume might throw
131+
(type $none_=>_none (func))
132+
(import "global.Math" "pow" (func $Math_pow (param f64 f64) (result f64)))
133+
(import "env" "invoke_vif" (func $invoke_vif (param i32 i32 f32)))
134+
(import "env" "glob" (global $glob i32)) ;; non-constant table offset
135+
(memory 256 256)
136+
(table 7 7 funcref)
137+
(elem (i32.const 0) $other_safe)
138+
(func $exc
139+
(call $invoke_vif
140+
(i32.const 0) ;; other_safe()
141+
(i32.const 42)
142+
(f32.const 3.14159)
143+
)
144+
)
145+
(func $other_safe (param i32) (param f32)
146+
(call_indirect (type $none_=>_none)
147+
(i32.const 0)
148+
)
149+
)
150+
)

0 commit comments

Comments
 (0)