Skip to content

Commit ecaa818

Browse files
authored
Let GenerateDynCalls generate dynCalls for invokes (#3192)
This moves dynCall generating functionaity for invokes from `EmscriptenGlueGenerator` to `GenerateDynCalls` pass. So now `GenerateDynCalls` pass will take care of all cases we need dynCalls: functions in tables and invokes.
1 parent 42aec82 commit ecaa818

File tree

7 files changed

+129
-86
lines changed

7 files changed

+129
-86
lines changed

src/passes/GenerateDynCalls.cpp

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
#include "ir/import-utils.h"
2828
#include "pass.h"
2929
#include "support/debug.h"
30-
#include "wasm-emscripten.h"
30+
#include "wasm-builder.h"
3131

3232
#define DEBUG_TYPE "generate-dyncalls"
3333

@@ -36,21 +36,103 @@ namespace wasm {
3636
struct GenerateDynCalls : public WalkerPass<PostWalker<GenerateDynCalls>> {
3737
GenerateDynCalls(bool onlyI64) : onlyI64(onlyI64) {}
3838

39+
void doWalkModule(Module* wasm) {
40+
PostWalker<GenerateDynCalls>::doWalkModule(wasm);
41+
for (auto& sig : invokeSigs) {
42+
generateDynCallThunk(sig);
43+
}
44+
}
45+
3946
void visitTable(Table* table) {
47+
// Generate dynCalls for functions in the table
4048
if (table->segments.size() > 0) {
41-
EmscriptenGlueGenerator generator(*getModule());
42-
generator.onlyI64DynCalls = onlyI64;
4349
std::vector<Name> tableSegmentData;
4450
for (const auto& indirectFunc : table->segments[0].data) {
45-
generator.generateDynCallThunk(
46-
getModule()->getFunction(indirectFunc)->sig);
51+
generateDynCallThunk(getModule()->getFunction(indirectFunc)->sig);
4752
}
4853
}
4954
}
5055

56+
void visitFunction(Function* func) {
57+
// Generate dynCalls for invokes
58+
if (func->imported() && func->base.startsWith("invoke_")) {
59+
Signature sig = func->sig;
60+
// The first parameter is a pointer to the original function that's called
61+
// by the invoke, so skip it
62+
std::vector<Type> newParams(sig.params.begin() + 1, sig.params.end());
63+
invokeSigs.insert(Signature(Type(newParams), sig.results));
64+
}
65+
}
66+
67+
void generateDynCallThunk(Signature sig);
68+
5169
bool onlyI64;
70+
// The set of all invokes' signatures
71+
std::set<Signature> invokeSigs;
5272
};
5373

74+
static bool hasI64(Signature sig) {
75+
// We only generate dynCall functions for signatures that contain i64. This is
76+
// because any other function can be called directly from JavaScript using the
77+
// wasm table.
78+
for (auto t : sig.results) {
79+
if (t.getID() == Type::i64) {
80+
return true;
81+
}
82+
}
83+
for (auto t : sig.params) {
84+
if (t.getID() == Type::i64) {
85+
return true;
86+
}
87+
}
88+
return false;
89+
}
90+
91+
static void exportFunction(Module& wasm, Name name, bool must_export) {
92+
if (!wasm.getFunctionOrNull(name)) {
93+
assert(!must_export);
94+
return;
95+
}
96+
if (wasm.getExportOrNull(name)) {
97+
return; // Already exported
98+
}
99+
auto exp = new Export;
100+
exp->name = exp->value = name;
101+
exp->kind = ExternalKind::Function;
102+
wasm.addExport(exp);
103+
}
104+
105+
void GenerateDynCalls::generateDynCallThunk(Signature sig) {
106+
if (onlyI64 && !hasI64(sig)) {
107+
return;
108+
}
109+
110+
Module* wasm = getModule();
111+
Builder builder(*wasm);
112+
Name name = std::string("dynCall_") + getSig(sig.results, sig.params);
113+
if (wasm->getFunctionOrNull(name) || wasm->getExportOrNull(name)) {
114+
return; // module already contains this dyncall
115+
}
116+
std::vector<NameType> params;
117+
params.emplace_back("fptr", Type::i32); // function pointer param
118+
int p = 0;
119+
for (const auto& param : sig.params) {
120+
params.emplace_back(std::to_string(p++), param);
121+
}
122+
Function* f = builder.makeFunction(name, std::move(params), sig.results, {});
123+
Expression* fptr = builder.makeLocalGet(0, Type::i32);
124+
std::vector<Expression*> args;
125+
Index i = 0;
126+
for (const auto& param : sig.params) {
127+
args.push_back(builder.makeLocalGet(++i, param));
128+
}
129+
Expression* call = builder.makeCallIndirect(fptr, args, sig);
130+
f->body = call;
131+
132+
wasm->addFunction(f);
133+
exportFunction(*wasm, f->name, true);
134+
}
135+
54136
Pass* createGenerateDynCallsPass() { return new GenerateDynCalls(false); }
55137
Pass* createGenerateI64DynCallsPass() { return new GenerateDynCalls(true); }
56138

src/wasm-emscripten.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@ class EmscriptenGlueGenerator {
5858
// the file).
5959
void separateDataSegments(Output* outfile, Address base);
6060

61-
void generateDynCallThunk(Signature sig);
62-
6361
bool standalone = false;
6462
bool sideModule = false;
6563
bool minimizeWasmChanges = false;
@@ -71,9 +69,6 @@ class EmscriptenGlueGenerator {
7169
Builder builder;
7270
Address stackPointerOffset;
7371
bool useStackPointerGlobal;
74-
// Used by generateDynCallThunk to track all the dynCall functions created
75-
// so far.
76-
std::unordered_set<Signature> sigs;
7772
};
7873

7974
} // namespace wasm

src/wasm/wasm-emscripten.cpp

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -116,68 +116,6 @@ void EmscriptenGlueGenerator::generatePostInstantiateFunction() {
116116
wasm.addExport(ex);
117117
}
118118

119-
inline void exportFunction(Module& wasm, Name name, bool must_export) {
120-
if (!wasm.getFunctionOrNull(name)) {
121-
assert(!must_export);
122-
return;
123-
}
124-
if (wasm.getExportOrNull(name)) {
125-
return; // Already exported
126-
}
127-
auto exp = new Export;
128-
exp->name = exp->value = name;
129-
exp->kind = ExternalKind::Function;
130-
wasm.addExport(exp);
131-
}
132-
133-
static bool hasI64(Signature sig) {
134-
// We only generate dynCall functions for signatures that contain
135-
// i64. This is because any other function can be called directly
136-
// from JavaScript using the wasm table.
137-
for (auto t : sig.results) {
138-
if (t.getID() == Type::i64) {
139-
return true;
140-
}
141-
}
142-
for (auto t : sig.params) {
143-
if (t.getID() == Type::i64) {
144-
return true;
145-
}
146-
}
147-
return false;
148-
}
149-
150-
void EmscriptenGlueGenerator::generateDynCallThunk(Signature sig) {
151-
if (noDynCalls || (onlyI64DynCalls && !hasI64(sig))) {
152-
return;
153-
}
154-
if (!sigs.insert(sig).second) {
155-
return; // sig is already in the set
156-
}
157-
Name name = std::string("dynCall_") + getSig(sig.results, sig.params);
158-
if (wasm.getFunctionOrNull(name) || wasm.getExportOrNull(name)) {
159-
return; // module already contains this dyncall
160-
}
161-
std::vector<NameType> params;
162-
params.emplace_back("fptr", Type::i32); // function pointer param
163-
int p = 0;
164-
for (const auto& param : sig.params) {
165-
params.emplace_back(std::to_string(p++), param);
166-
}
167-
Function* f = builder.makeFunction(name, std::move(params), sig.results, {});
168-
Expression* fptr = builder.makeLocalGet(0, Type::i32);
169-
std::vector<Expression*> args;
170-
Index i = 0;
171-
for (const auto& param : sig.params) {
172-
args.push_back(builder.makeLocalGet(++i, param));
173-
}
174-
Expression* call = builder.makeCallIndirect(fptr, args, sig);
175-
f->body = call;
176-
177-
wasm.addFunction(f);
178-
exportFunction(wasm, f->name, true);
179-
}
180-
181119
// lld can sometimes produce a build with an imported mutable __stack_pointer
182120
// (i.e. when linking with -fpie). This method internalizes the
183121
// __stack_pointer and initializes it from an immutable global instead.
@@ -675,10 +613,6 @@ void EmscriptenGlueGenerator::fixInvokeFunctionNames() {
675613
BYN_TRACE("fixInvokeFunctionNames\n");
676614
FixInvokeFunctionNamesWalker walker(wasm);
677615
walker.walkModule(&wasm);
678-
BYN_TRACE("generating dyncall thunks\n");
679-
for (auto sig : walker.invokeSigs) {
680-
generateDynCallThunk(sig);
681-
}
682616
}
683617

684618
void printSignatures(std::ostream& o, const std::set<Signature>& c) {

test/lld/duplicate_imports.wat.out

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
(module
2+
(type $i32_f32_f64_=>_f32 (func (param i32 f32 f64) (result f32)))
3+
(type $i32_f64_f64_=>_f32 (func (param i32 f64 f64) (result f32)))
24
(type $none_=>_none (func))
35
(type $none_=>_i32 (func (result i32)))
46
(type $i32_=>_i32 (func (param i32) (result i32)))
57
(type $i32_i32_=>_i32 (func (param i32 i32) (result i32)))
68
(type $i64_=>_i32 (func (param i64) (result i32)))
7-
(type $i32_f32_f64_=>_f32 (func (param i32 f32 f64) (result f32)))
8-
(type $i32_f64_f64_=>_f32 (func (param i32 f64 f64) (result f32)))
9+
(type $f32_f64_=>_f32 (func (param f32 f64) (result f32)))
10+
(type $f64_f64_=>_f32 (func (param f64 f64) (result f32)))
911
(import "env" "puts" (func $puts1 (param i32) (result i32)))
1012
(import "env" "invoke_ffd" (func $invoke_ffd (param i32 f32 f64) (result f32)))
1113
(import "env" "invoke_ffd" (func $invoke_ffd2 (param i32 f64 f64) (result f32)))
@@ -21,6 +23,8 @@
2123
(export "main" (func $main))
2224
(export "__heap_base" (global $global$1))
2325
(export "__data_end" (global $global$2))
26+
(export "dynCall_ffd" (func $dynCall_ffd))
27+
(export "dynCall_fdd" (func $dynCall_fdd))
2428
(func $main (result i32)
2529
(drop
2630
(call $puts1
@@ -32,6 +36,20 @@
3236
(func $__wasm_call_ctors
3337
(nop)
3438
)
39+
(func $dynCall_ffd (param $fptr i32) (param $0 f32) (param $1 f64) (result f32)
40+
(call_indirect (type $f32_f64_=>_f32)
41+
(local.get $0)
42+
(local.get $1)
43+
(local.get $fptr)
44+
)
45+
)
46+
(func $dynCall_fdd (param $fptr i32) (param $0 f64) (param $1 f64) (result f32)
47+
(call_indirect (type $f64_f64_=>_f32)
48+
(local.get $0)
49+
(local.get $1)
50+
(local.get $fptr)
51+
)
52+
)
3553
(func $legalfunc$puts2 (param $0 i64) (result i32)
3654
(call $legalimport$puts2
3755
(i32.wrap_i64
@@ -61,7 +79,9 @@
6179
],
6280
"exports": [
6381
"__wasm_call_ctors",
64-
"main"
82+
"main",
83+
"dynCall_ffd",
84+
"dynCall_fdd"
6585
],
6686
"namedGlobals": {
6787
"__heap_base" : "66128",

test/lld/shared_longjmp.wat.out

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,6 @@
143143
)
144144
(unreachable)
145145
)
146-
(func $dynCall_vii (param $fptr i32) (param $0 i32) (param $1 i32)
147-
(call_indirect (type $i32_i32_=>_none)
148-
(local.get $0)
149-
(local.get $1)
150-
(local.get $fptr)
151-
)
152-
)
153146
(func $__assign_got_enties
154147
(global.set $gimport$13
155148
(call $g$__THREW__)
@@ -161,6 +154,13 @@
161154
(call $fp$emscripten_longjmp_jmpbuf$vii)
162155
)
163156
)
157+
(func $dynCall_vii (param $fptr i32) (param $0 i32) (param $1 i32)
158+
(call_indirect (type $i32_i32_=>_none)
159+
(local.get $0)
160+
(local.get $1)
161+
(local.get $fptr)
162+
)
163+
)
164164
(func $__post_instantiate
165165
(call $__assign_got_enties)
166166
(call $0)

test/passes/generate-dyncalls.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
(module
2+
(type $i32_i32_i32_=>_none (func (param i32 i32 i32)))
23
(type $none_=>_i32 (func (result i32)))
34
(type $i32_=>_i64 (func (param i32) (result i64)))
5+
(type $i32_i32_=>_none (func (param i32 i32)))
46
(type $i32_=>_i32 (func (param i32) (result i32)))
57
(type $i32_i32_=>_i64 (func (param i32 i32) (result i64)))
8+
(import "env" "invoke_vii" (func $invoke_vii (param i32 i32 i32)))
69
(table $0 2 2 funcref)
710
(elem (i32.const 0) $f1 $f2)
811
(export "dynCall_i" (func $dynCall_i))
912
(export "dynCall_ji" (func $dynCall_ji))
13+
(export "dynCall_vii" (func $dynCall_vii))
1014
(func $f1 (result i32)
1115
(i32.const 1024)
1216
)
@@ -24,4 +28,11 @@
2428
(local.get $fptr)
2529
)
2630
)
31+
(func $dynCall_vii (param $fptr i32) (param $0 i32) (param $1 i32)
32+
(call_indirect (type $i32_i32_=>_none)
33+
(local.get $0)
34+
(local.get $1)
35+
(local.get $fptr)
36+
)
37+
)
2738
)

test/passes/generate-dyncalls.wast

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
(module
2+
(import "env" "invoke_vii" (func $invoke_vii (param i32 i32 i32)))
23
(func $f1 (result i32)
34
(i32.const 1024)
45
)

0 commit comments

Comments
 (0)