Skip to content

Commit 2651ffb

Browse files
authored
[wasm-split] Add an --asyncify option (#4513)
Add an option for running the asyncify transformation on the primary module emitted by wasm-split. The idea is that the placeholder functions should be able to unwind the stack while the secondary module is asynchronously loaded, then once the placeholder functions have been patched out by the secondary module the stack should be rewound and end up in the correct secondary function.
1 parent 0f5f720 commit 2651ffb

File tree

5 files changed

+199
-0
lines changed

5 files changed

+199
-0
lines changed

src/tools/wasm-split/split-options.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,15 @@ WasmSplitOptions::WasmSplitOptions()
190190
[&](Options* o, const std::string& argument) {
191191
placeholderNamespace = argument;
192192
})
193+
.add(
194+
"--asyncify",
195+
"",
196+
"Transform the module to support unwinding the stack from placeholder "
197+
"functions and rewinding it once the secondary module has been loaded.",
198+
WasmSplitOption,
199+
{Mode::Split},
200+
Options::Arguments::Zero,
201+
[&](Options* o, const std::string& argument) { asyncify = true; })
193202
.add(
194203
"--export-prefix",
195204
"",

src/tools/wasm-split/split-options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct WasmSplitOptions : ToolOptions {
4343
bool emitBinary = true;
4444
bool symbolMap = false;
4545
bool placeholderMap = false;
46+
bool asyncify = false;
4647

4748
// TODO: Remove this. See the comment in wasm-binary.h.
4849
bool emitModuleNames = false;

src/tools/wasm-split/wasm-split.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,16 @@ void splitModule(const WasmSplitOptions& options) {
280280
adjustTableSize(wasm, options.initialTableSize);
281281
adjustTableSize(*secondary, options.initialTableSize);
282282

283+
// Run asyncify on the primary module
284+
if (options.asyncify) {
285+
PassOptions passOptions;
286+
passOptions.optimizeLevel = 1;
287+
passOptions.arguments.insert({"asyncify-ignore-imports", ""});
288+
PassRunner runner(&wasm, passOptions);
289+
runner.add("asyncify");
290+
runner.run();
291+
}
292+
283293
if (options.symbolMap) {
284294
writeSymbolMap(wasm, options.primaryOutput + ".symbols");
285295
writeSymbolMap(*secondary, options.secondaryOutput + ".symbols");

test/lit/help/wasm-split.test

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@
5959
;; CHECK-NEXT: import placeholder functions into the
6060
;; CHECK-NEXT: primary module.
6161
;; CHECK-NEXT:
62+
;; CHECK-NEXT: --asyncify [split] Transform the module to support
63+
;; CHECK-NEXT: unwinding the stack from placeholder
64+
;; CHECK-NEXT: functions and rewinding it once the
65+
;; CHECK-NEXT: secondary module has been loaded.
66+
;; CHECK-NEXT:
6267
;; CHECK-NEXT: --export-prefix [split] An identifying prefix to prepend
6368
;; CHECK-NEXT: to new export names created by module
6469
;; CHECK-NEXT: splitting.

test/lit/wasm-split/asyncify.wast

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
;; RUN: wasm-split %s --export-prefix='%' -g -o1 %t.1.wasm -o2 %t.2.wasm --keep-funcs=foo --asyncify
2+
;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY
3+
;; RUN: wasm-dis %t.2.wasm | filecheck %s --check-prefix SECONDARY
4+
5+
;; Check that the --asyncify option instruments the primary module but not the
6+
;; secondary module.
7+
8+
(module
9+
(func $foo (param i32) (result i32)
10+
(call $bar (i32.const 0))
11+
)
12+
(func $bar (param i32) (result i32)
13+
(call $foo (i32.const 1))
14+
)
15+
)
16+
17+
;; PRIMARY: (module
18+
;; PRIMARY-NEXT: (type $i32_=>_i32 (func (param i32) (result i32)))
19+
;; PRIMARY-NEXT: (type $i32_=>_none (func (param i32)))
20+
;; PRIMARY-NEXT: (type $none_=>_none (func))
21+
;; PRIMARY-NEXT: (type $none_=>_i32 (func (result i32)))
22+
;; PRIMARY-NEXT: (import "placeholder" "0" (func $placeholder_0 (param i32) (result i32)))
23+
;; PRIMARY-NEXT: (global $global$0 (mut i32) (i32.const 0))
24+
;; PRIMARY-NEXT: (global $global$1 (mut i32) (i32.const 0))
25+
;; PRIMARY-NEXT: (memory $0 1 1)
26+
;; PRIMARY-NEXT: (table $0 1 funcref)
27+
;; PRIMARY-NEXT: (elem (i32.const 0) $placeholder_0)
28+
;; PRIMARY-NEXT: (export "%foo" (func $foo))
29+
;; PRIMARY-NEXT: (export "%table" (table $0))
30+
;; PRIMARY-NEXT: (export "asyncify_start_unwind" (func $asyncify_start_unwind))
31+
;; PRIMARY-NEXT: (export "asyncify_stop_unwind" (func $asyncify_stop_unwind))
32+
;; PRIMARY-NEXT: (export "asyncify_start_rewind" (func $asyncify_start_rewind))
33+
;; PRIMARY-NEXT: (export "asyncify_stop_rewind" (func $asyncify_stop_rewind))
34+
;; PRIMARY-NEXT: (export "asyncify_get_state" (func $asyncify_get_state))
35+
;; PRIMARY-NEXT: (func $foo (param $0 i32) (result i32)
36+
;; PRIMARY-NEXT: (local $1 i32)
37+
;; PRIMARY-NEXT: (if
38+
;; PRIMARY-NEXT: (i32.eq
39+
;; PRIMARY-NEXT: (global.get $global$0)
40+
;; PRIMARY-NEXT: (i32.const 2)
41+
;; PRIMARY-NEXT: )
42+
;; PRIMARY-NEXT: (block
43+
;; PRIMARY-NEXT: (i32.store
44+
;; PRIMARY-NEXT: (global.get $global$1)
45+
;; PRIMARY-NEXT: (i32.sub
46+
;; PRIMARY-NEXT: (i32.load
47+
;; PRIMARY-NEXT: (global.get $global$1)
48+
;; PRIMARY-NEXT: )
49+
;; PRIMARY-NEXT: (i32.const 4)
50+
;; PRIMARY-NEXT: )
51+
;; PRIMARY-NEXT: )
52+
;; PRIMARY-NEXT: (local.set $0
53+
;; PRIMARY-NEXT: (i32.load
54+
;; PRIMARY-NEXT: (i32.load
55+
;; PRIMARY-NEXT: (global.get $global$1)
56+
;; PRIMARY-NEXT: )
57+
;; PRIMARY-NEXT: )
58+
;; PRIMARY-NEXT: )
59+
;; PRIMARY-NEXT: )
60+
;; PRIMARY-NEXT: )
61+
;; PRIMARY-NEXT: (local.set $1
62+
;; PRIMARY-NEXT: (block $label$2 (result i32)
63+
;; PRIMARY-NEXT: (if
64+
;; PRIMARY-NEXT: (i32.eqz
65+
;; PRIMARY-NEXT: (select
66+
;; PRIMARY-NEXT: (if (result i32)
67+
;; PRIMARY-NEXT: (i32.eq
68+
;; PRIMARY-NEXT: (global.get $global$0)
69+
;; PRIMARY-NEXT: (i32.const 2)
70+
;; PRIMARY-NEXT: )
71+
;; PRIMARY-NEXT: (block (result i32)
72+
;; PRIMARY-NEXT: (i32.store
73+
;; PRIMARY-NEXT: (global.get $global$1)
74+
;; PRIMARY-NEXT: (i32.sub
75+
;; PRIMARY-NEXT: (i32.load
76+
;; PRIMARY-NEXT: (global.get $global$1)
77+
;; PRIMARY-NEXT: )
78+
;; PRIMARY-NEXT: (i32.const 4)
79+
;; PRIMARY-NEXT: )
80+
;; PRIMARY-NEXT: )
81+
;; PRIMARY-NEXT: (i32.load
82+
;; PRIMARY-NEXT: (i32.load
83+
;; PRIMARY-NEXT: (global.get $global$1)
84+
;; PRIMARY-NEXT: )
85+
;; PRIMARY-NEXT: )
86+
;; PRIMARY-NEXT: )
87+
;; PRIMARY-NEXT: (local.get $1)
88+
;; PRIMARY-NEXT: )
89+
;; PRIMARY-NEXT: (i32.const 0)
90+
;; PRIMARY-NEXT: (global.get $global$0)
91+
;; PRIMARY-NEXT: )
92+
;; PRIMARY-NEXT: )
93+
;; PRIMARY-NEXT: (block
94+
;; PRIMARY-NEXT: (local.set $1
95+
;; PRIMARY-NEXT: (call_indirect (type $i32_=>_i32)
96+
;; PRIMARY-NEXT: (i32.const 0)
97+
;; PRIMARY-NEXT: (i32.const 0)
98+
;; PRIMARY-NEXT: )
99+
;; PRIMARY-NEXT: )
100+
;; PRIMARY-NEXT: (drop
101+
;; PRIMARY-NEXT: (br_if $label$2
102+
;; PRIMARY-NEXT: (i32.const 0)
103+
;; PRIMARY-NEXT: (i32.eq
104+
;; PRIMARY-NEXT: (global.get $global$0)
105+
;; PRIMARY-NEXT: (i32.const 1)
106+
;; PRIMARY-NEXT: )
107+
;; PRIMARY-NEXT: )
108+
;; PRIMARY-NEXT: )
109+
;; PRIMARY-NEXT: (local.set $0
110+
;; PRIMARY-NEXT: (local.get $1)
111+
;; PRIMARY-NEXT: )
112+
;; PRIMARY-NEXT: )
113+
;; PRIMARY-NEXT: )
114+
;; PRIMARY-NEXT: (if
115+
;; PRIMARY-NEXT: (i32.eqz
116+
;; PRIMARY-NEXT: (global.get $global$0)
117+
;; PRIMARY-NEXT: )
118+
;; PRIMARY-NEXT: (return
119+
;; PRIMARY-NEXT: (local.get $0)
120+
;; PRIMARY-NEXT: )
121+
;; PRIMARY-NEXT: )
122+
;; PRIMARY-NEXT: (unreachable)
123+
;; PRIMARY-NEXT: )
124+
;; PRIMARY-NEXT: )
125+
;; PRIMARY-NEXT: (i32.store
126+
;; PRIMARY-NEXT: (i32.load
127+
;; PRIMARY-NEXT: (global.get $global$1)
128+
;; PRIMARY-NEXT: )
129+
;; PRIMARY-NEXT: (local.get $1)
130+
;; PRIMARY-NEXT: )
131+
;; PRIMARY-NEXT: (i32.store
132+
;; PRIMARY-NEXT: (global.get $global$1)
133+
;; PRIMARY-NEXT: (i32.add
134+
;; PRIMARY-NEXT: (i32.load
135+
;; PRIMARY-NEXT: (global.get $global$1)
136+
;; PRIMARY-NEXT: )
137+
;; PRIMARY-NEXT: (i32.const 4)
138+
;; PRIMARY-NEXT: )
139+
;; PRIMARY-NEXT: )
140+
;; PRIMARY-NEXT: (i32.store
141+
;; PRIMARY-NEXT: (i32.load
142+
;; PRIMARY-NEXT: (global.get $global$1)
143+
;; PRIMARY-NEXT: )
144+
;; PRIMARY-NEXT: (local.get $0)
145+
;; PRIMARY-NEXT: )
146+
;; PRIMARY-NEXT: (i32.store
147+
;; PRIMARY-NEXT: (global.get $global$1)
148+
;; PRIMARY-NEXT: (i32.add
149+
;; PRIMARY-NEXT: (i32.load
150+
;; PRIMARY-NEXT: (global.get $global$1)
151+
;; PRIMARY-NEXT: )
152+
;; PRIMARY-NEXT: (i32.const 4)
153+
;; PRIMARY-NEXT: )
154+
;; PRIMARY-NEXT: )
155+
;; PRIMARY-NEXT: (i32.const 0)
156+
;; PRIMARY-NEXT: )
157+
;; PRIMARY: (func $asyncify_start_unwind (param $0 i32)
158+
;; PRIMARY: (func $asyncify_stop_unwind
159+
;; PRIMARY: (func $asyncify_start_rewind (param $0 i32)
160+
;; PRIMARY: (func $asyncify_stop_rewind
161+
;; PRIMARY: (func $asyncify_get_state (result i32)
162+
;; PRIMARY: )
163+
164+
;; SECONDARY: (module
165+
;; SECONDARY-NEXT: (type $i32_=>_i32 (func (param i32) (result i32)))
166+
;; SECONDARY-NEXT: (import "primary" "%table" (table $timport$0 1 funcref))
167+
;; SECONDARY-NEXT: (import "primary" "%foo" (func $foo (param i32) (result i32)))
168+
;; SECONDARY-NEXT: (elem (i32.const 0) $bar)
169+
;; SECONDARY-NEXT: (func $bar (param $0 i32) (result i32)
170+
;; SECONDARY-NEXT: (call $foo
171+
;; SECONDARY-NEXT: (i32.const 1)
172+
;; SECONDARY-NEXT: )
173+
;; SECONDARY-NEXT: )
174+
;; SECONDARY-NEXT: )

0 commit comments

Comments
 (0)