Skip to content

Commit 2f1f55a

Browse files
authored
[wasm-split] Split module elements early (#8688)
Before #8443, we scanned `ref.func`s in global initializers early in `indirectReferencesToSecondaryFunctions` and created trampolines for them and replaced `ref.func $func`s with `ref.func $trampoline_func` if `func` was set to move to a secondary module. But in case the global containing `ref.func $trampoline_func` also ends up moving to the same secondary module, creating trampoline and using it was not necessary, because the global can simply use `ref.func $func` because `func` is in the same secondary module. To fix this, in #8443, we postponed creating trampolines for `ref.func`s in global initializers until `shareImportableItems`. This had a problem, because we end up creating new trampolines late in `shareImportableItems`. But trampolines were designed to go through `indirectCallsToSecondaryFunctions` and `setupTablePatching`, so those late trampolines were invalid, like ```wast (func $trampoline_foo (call $foo) ) ``` when `foo` was in a secondary module. This was supposed to be converted to a `call_indirect` in `indirectCallsToSecondaryFunctions` and the table elements were supposed to set up in `setupTablePatching`. --- This moves `shareImportableItems` before `indirectReferencesToSecondaryFunctions`. Turns out, except for the active table and its base global, we can do all splitting before we make most of the changes related to splitting. This also simplifies `shareImportableItems` because we can now delete code handling the consequences of various transformations. Because the active table and its base global may not be registered as "used" in `shareImportableItems` before `setupTablePatching`, we make sure they are correctly shared with secondary modules in `setupTablePatching`. `active-table-base-global-used-elsewhere.wast` has some edge cases that I feel easy to miss, because now we specially handle the active table and the base global in `setupTablePatching`. `global-reffunc.wast` is the same but just renamed expectation rewritten. `global-reffunc2.wast` is the failing case simplified from #8510. Other test changes are just the changes in the creation order of module elements and not meaningful. Replaces #8542 and fixes #8510.
1 parent 02f114b commit 2f1f55a

8 files changed

Lines changed: 290 additions & 189 deletions

scripts/fuzz_opt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2525,7 +2525,7 @@ def handle(self, wasm):
25252525
TrapsNeverHappen(),
25262526
CtorEval(),
25272527
Merge(),
2528-
# Split(), # https://github.com/WebAssembly/binaryen/issues/8510
2528+
Split(),
25292529
RoundtripText(),
25302530
ClusterFuzz(),
25312531
Two(),

src/ir/module-splitting.cpp

Lines changed: 122 additions & 138 deletions
Large diffs are not rendered by default.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
2+
;; We need to disable reference-types to reuse the existing table as the active
3+
;; table
4+
;; RUN: wasm-split %s --disable-reference-types --split-funcs=split -g -o1 %t.1.wasm -o2 %t.2.wasm
5+
;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY
6+
;; RUN: wasm-dis %t.2.wasm | filecheck %s --check-prefix SECONDARY
7+
8+
;; This tests the case when an existing table is used as the active table, and
9+
;; the active table and its base global already has existing uses in the
10+
;; secondary module.
11+
12+
(module
13+
;; PRIMARY: (type $0 (func))
14+
;; SECONDARY: (type $0 (func))
15+
(type $0 (func))
16+
(global $base (import "env" "base") i32)
17+
;; PRIMARY: (import "env" "base" (global $base i32))
18+
19+
;; PRIMARY: (import "placeholder.deferred" "1" (func $placeholder_1))
20+
21+
;; PRIMARY: (table $table 2 funcref)
22+
(table $table 1 funcref)
23+
(elem (global.get $base) $keep)
24+
;; PRIMARY: (elem $0 (global.get $base) $keep $placeholder_1)
25+
26+
;; PRIMARY: (export "table" (table $table))
27+
28+
;; PRIMARY: (export "global" (global $base))
29+
30+
;; PRIMARY: (export "keep" (func $keep))
31+
32+
;; PRIMARY: (func $keep
33+
;; PRIMARY-NEXT: (call_indirect (type $0)
34+
;; PRIMARY-NEXT: (i32.add
35+
;; PRIMARY-NEXT: (global.get $base)
36+
;; PRIMARY-NEXT: (i32.const 1)
37+
;; PRIMARY-NEXT: )
38+
;; PRIMARY-NEXT: )
39+
;; PRIMARY-NEXT: )
40+
(func $keep
41+
(call $split)
42+
)
43+
;; SECONDARY: (import "primary" "table" (table $table 1 funcref))
44+
45+
;; SECONDARY: (import "primary" "global" (global $base i32))
46+
47+
;; SECONDARY: (import "primary" "keep" (func $keep))
48+
49+
;; SECONDARY: (elem $0 (global.get $base) $keep $split)
50+
51+
;; SECONDARY: (func $split
52+
;; SECONDARY-NEXT: (drop
53+
;; SECONDARY-NEXT: (global.get $base)
54+
;; SECONDARY-NEXT: )
55+
;; SECONDARY-NEXT: (call_indirect (type $0)
56+
;; SECONDARY-NEXT: (i32.const 0)
57+
;; SECONDARY-NEXT: )
58+
;; SECONDARY-NEXT: )
59+
(func $split
60+
(drop (global.get $base))
61+
(call_indirect (type $0) (i32.const 0))
62+
)
63+
)

test/lit/wasm-split/global-funcref.wast

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
;; RUN: wasm-split %s -all -g -o1 %t.1.wasm -o2 %t.2.wasm --keep-funcs=keep
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+
;; When a split global ($a here)'s initializer contains a ref.func of a split
6+
;; function, we should NOT create any trampolines, and the split global should
7+
;; directly refer to the function.
8+
9+
(module
10+
(global $a funcref (ref.func $split))
11+
(global $b funcref (ref.func $keep))
12+
13+
(func $keep)
14+
15+
(func $split
16+
(drop
17+
(global.get $a)
18+
)
19+
(drop
20+
(global.get $b)
21+
)
22+
)
23+
)
24+
25+
;; PRIMARY: (module
26+
;; PRIMARY-NEXT: (type $0 (func))
27+
;; PRIMARY-NEXT: (export "keep" (func $keep))
28+
;; PRIMARY-NEXT: (func $keep
29+
;; PRIMARY-NEXT: )
30+
;; PRIMARY-NEXT: )
31+
32+
;; SECONDARY: (module
33+
;; SECONDARY-NEXT: (type $0 (func))
34+
;; SECONDARY-NEXT: (import "primary" "keep" (func $keep (exact)))
35+
;; SECONDARY-NEXT: (global $a funcref (ref.func $split))
36+
;; SECONDARY-NEXT: (global $b funcref (ref.func $keep))
37+
;; SECONDARY-NEXT: (func $split
38+
;; SECONDARY-NEXT: (drop
39+
;; SECONDARY-NEXT: (global.get $a)
40+
;; SECONDARY-NEXT: )
41+
;; SECONDARY-NEXT: (drop
42+
;; SECONDARY-NEXT: (global.get $b)
43+
;; SECONDARY-NEXT: )
44+
;; SECONDARY-NEXT: )
45+
;; SECONDARY-NEXT: )
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
;; RUN: wasm-split %s -all -g -o1 %t.1.wasm -o2 %t.2.wasm --split-funcs=split1,split2
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+
;; Global $g1 is used (exported) in the primary module so it can't move, and
6+
;; global $g2 is only used in the secondary module so it will move there.
7+
8+
(module
9+
(global $g1 funcref (ref.func $split1))
10+
(global $g2 funcref (ref.func $split2))
11+
(export "g1" (global $g1))
12+
13+
(func $split1
14+
(unreachable)
15+
)
16+
17+
(func $split2
18+
(drop
19+
(global.get $g2)
20+
)
21+
)
22+
)
23+
24+
;; PRIMARY: (module
25+
;; PRIMARY-NEXT: (type $0 (func))
26+
;; PRIMARY-NEXT: (import "placeholder.deferred" "0" (func $placeholder_0))
27+
;; PRIMARY-NEXT: (global $g1 funcref (ref.func $trampoline_split1))
28+
;; PRIMARY-NEXT: (table $0 1 funcref)
29+
;; PRIMARY-NEXT: (elem $0 (i32.const 0) $placeholder_0)
30+
;; PRIMARY-NEXT: (export "g1" (global $g1))
31+
;; PRIMARY-NEXT: (export "table" (table $0))
32+
;; PRIMARY-NEXT: (func $trampoline_split1
33+
;; PRIMARY-NEXT: (call_indirect (type $0)
34+
;; PRIMARY-NEXT: (i32.const 0)
35+
;; PRIMARY-NEXT: )
36+
;; PRIMARY-NEXT: )
37+
;; PRIMARY-NEXT: )
38+
39+
;; SECONDARY: (module
40+
;; SECONDARY-NEXT: (type $0 (func))
41+
;; SECONDARY-NEXT: (import "primary" "table" (table $timport$0 1 funcref))
42+
;; SECONDARY-NEXT: (global $g2 funcref (ref.func $split2))
43+
;; SECONDARY-NEXT: (elem $0 (i32.const 0) $split1)
44+
;; SECONDARY-NEXT: (func $split1
45+
;; SECONDARY-NEXT: (unreachable)
46+
;; SECONDARY-NEXT: )
47+
;; SECONDARY-NEXT: (func $split2
48+
;; SECONDARY-NEXT: (drop
49+
;; SECONDARY-NEXT: (global.get $g2)
50+
;; SECONDARY-NEXT: )
51+
;; SECONDARY-NEXT: )
52+
;; SECONDARY-NEXT: )

test/lit/wasm-split/ref.func.wast

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373

7474
;; SECONDARY: (import "primary" "prime" (func $prime (exact (type $0))))
7575

76-
;; SECONDARY: (elem $0 (i32.const 0) $second-in-table $second)
76+
;; SECONDARY: (elem $0 (i32.const 0) $second $second-in-table)
7777

7878
;; SECONDARY: (elem declare func $prime)
7979

@@ -109,13 +109,13 @@
109109
;; (but we will get a placeholder, as all split-out functions do).
110110
)
111111
)
112-
;; PRIMARY: (func $trampoline_second-in-table (type $0)
112+
;; PRIMARY: (func $trampoline_second (type $0)
113113
;; PRIMARY-NEXT: (call_indirect $1 (type $0)
114114
;; PRIMARY-NEXT: (i32.const 0)
115115
;; PRIMARY-NEXT: )
116116
;; PRIMARY-NEXT: )
117117

118-
;; PRIMARY: (func $trampoline_second (type $0)
118+
;; PRIMARY: (func $trampoline_second-in-table (type $0)
119119
;; PRIMARY-NEXT: (call_indirect $1 (type $0)
120120
;; PRIMARY-NEXT: (i32.const 1)
121121
;; PRIMARY-NEXT: )

test/lit/wasm-split/split-module-items.wast

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,16 @@
3434
;; PRIMARY: (tag $keep-tag (type $1) (param i32))
3535
;; PRIMARY-NEXT: (tag $shared-tag (type $1) (param i32))
3636

37-
;; PRIMARY: (export "keep" (func $keep))
38-
;; PRIMARY-NEXT: (export "memory" (memory $shared-memory))
37+
;; PRIMARY: (export "memory" (memory $shared-memory))
3938
;; PRIMARY-NEXT: (export "table" (table $shared-table))
40-
;; PRIMARY-NEXT: (export "table_3" (table $2))
4139
;; PRIMARY-NEXT: (export "global" (global $shared-global))
4240
;; PRIMARY-NEXT: (export "tag" (tag $shared-tag))
41+
;; PRIMARY-NEXT: (export "keep" (func $keep))
42+
;; PRIMARY-NEXT: (export "table_5" (table $2))
4343

4444
;; SECONDARY: (import "primary" "memory" (memory $shared-memory 1 1))
45-
;; SECONDARY-NEXT: (import "primary" "table_3" (table $timport$0 1 funcref))
4645
;; SECONDARY-NEXT: (import "primary" "table" (table $shared-table 1 1 funcref))
46+
;; SECONDARY-NEXT: (import "primary" "table_5" (table $timport$1 1 funcref))
4747
;; SECONDARY-NEXT: (import "primary" "global" (global $shared-global i32))
4848
;; SECONDARY-NEXT: (import "primary" "keep" (func $keep (exact (param i32) (result i32))))
4949
;; SECONDARY-NEXT: (import "primary" "tag" (tag $shared-tag (type $1) (param i32)))

0 commit comments

Comments
 (0)