Skip to content

Commit 6d47684

Browse files
authored
Add an option for wasm-merge to write a split manifest (#8534)
This manifest can later be given to wasm-split to split the merged module back up such that each function appears in its originating module. This can help simplify a merge-optimize-split workflow.
1 parent a7330d7 commit 6d47684

8 files changed

Lines changed: 160 additions & 0 deletions

File tree

src/tools/wasm-merge.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,12 @@ int main(int argc, const char* argv[]) {
620620
std::string outputSourceMapFilename;
621621
std::string outputSourceMapUrl;
622622

623+
// We can write wasm-split manifests that can later be fed to wasm-split to
624+
// split the merged module back up along the lines of the original modules.
625+
// Map modules to their functions so we can write the manifest.
626+
std::string manifestFile;
627+
std::unordered_map<Name, std::vector<Name>> moduleFuncs;
628+
623629
const std::string WasmMergeOption = "wasm-merge options";
624630

625631
ToolOptions options("wasm-merge",
@@ -687,6 +693,16 @@ Input source maps can be specified by adding an -ism option right after the modu
687693
[&outputSourceMapUrl](Options* o, const std::string& argument) {
688694
outputSourceMapUrl = argument;
689695
})
696+
.add("--output-manifest",
697+
"",
698+
"Write a wasm-split manifest to the specified file. This manifest can "
699+
"be given to wasm-split to split the merged module along the lines of "
700+
"the original modules.",
701+
WasmMergeOption,
702+
Options::Arguments::One,
703+
[&manifestFile](Options* o, const std::string& argument) {
704+
manifestFile = argument;
705+
})
690706
.add("--rename-export-conflicts",
691707
"-rec",
692708
"Rename exports to avoid conflicts (rather than error)",
@@ -780,6 +796,16 @@ Input source maps can be specified by adding an -ism option right after the modu
780796
// This is a later module: do a full merge.
781797
mergeInto(*currModule, inputFileName);
782798

799+
// The functions in the module have been renamed and copied rather than
800+
// moved, so we can get their final names directly. (We don't need this
801+
// for the first module because it does not appear in the manifest.)
802+
auto& funcs = moduleFuncs[inputFileName];
803+
for (auto& func : currModule->functions) {
804+
if (!func->imported()) {
805+
funcs.push_back(func->name);
806+
}
807+
}
808+
783809
// Validate after each merged module, when we are in pass-debug mode
784810
// (this can be quadratic time).
785811
if (PassRunner::getPassDebug()) {
@@ -822,6 +848,25 @@ Input source maps can be specified by adding an -ism option right after the modu
822848
}
823849

824850
// Output.
851+
if (!manifestFile.empty()) {
852+
std::ofstream manifest(manifestFile);
853+
// Skip module 0 because it will be the primary module for the split and
854+
// does not need to appear in the manifest.
855+
for (size_t i = 1; i < inputFileNames.size(); i++) {
856+
auto moduleName = inputFileNames[i];
857+
const auto& funcs = moduleFuncs[moduleName];
858+
if (funcs.empty()) {
859+
continue;
860+
}
861+
862+
manifest << moduleName << "\n";
863+
for (auto func : funcs) {
864+
manifest << func << "\n";
865+
}
866+
manifest << "\n";
867+
}
868+
}
869+
825870
if (options.extra.count("output") > 0) {
826871
ModuleWriter writer(options.passOptions);
827872
writer.setBinary(emitBinary);

test/lit/help/wasm-merge.test

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434
;; CHECK-NEXT:
3535
;; CHECK-NEXT: --output-source-map-url,-osu Emit specified string as source map URL
3636
;; CHECK-NEXT:
37+
;; CHECK-NEXT: --output-manifest Write a wasm-split manifest to the
38+
;; CHECK-NEXT: specified file. This manifest can be
39+
;; CHECK-NEXT: given to wasm-split to split the merged
40+
;; CHECK-NEXT: module along the lines of the original
41+
;; CHECK-NEXT: modules.
42+
;; CHECK-NEXT:
3743
;; CHECK-NEXT: --rename-export-conflicts,-rec Rename exports to avoid conflicts (rather
3844
;; CHECK-NEXT: than error)
3945
;; CHECK-NEXT:

test/lit/merge/manifest.wat

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
;; RUN: wasm-merge %s first %s.second second %s.third third --output-manifest %t.manifest -S -o %t.wasm
2+
;; RUN: cat %t.manifest | filecheck %s
3+
4+
;; The first module is the primary module and does not appear in the manifest.
5+
;; CHECK-NOT: first
6+
;; CHECK-NOT: foo
7+
;; CHECK-NOT: bar
8+
9+
;; CHECK: second
10+
;; CHECK-NEXT: baz
11+
;; CHECK-NEXT:
12+
;; CHECK-NEXT: third
13+
;; CHECK-NEXT: qux
14+
15+
(module
16+
(import "env" "imported_first" (func $imported_first))
17+
(func $foo (export "foo")
18+
(call $imported_first)
19+
)
20+
(func $bar (export "bar")
21+
nop
22+
)
23+
)

test/lit/merge/manifest.wat.second

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(module
2+
(import "env" "imported_second" (func $imported_second))
3+
(func $baz (export "baz")
4+
(call $imported_second)
5+
)
6+
)

test/lit/merge/manifest.wat.third

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(module
2+
(import "env" "imported_third" (func $imported_third))
3+
(func $qux (export "qux")
4+
(call $imported_third)
5+
)
6+
)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
;; RUN: wasm-merge %s first %s.second second %s.third third --output-manifest %t.manifest -S -o %t.wasm
2+
;; RUN: wasm-split %t.wasm --multi-split --manifest %t.manifest -g -o %t.primary.wasm --out-prefix %t.
3+
;; RUN: wasm-dis %t.primary.wasm | filecheck %s --check-prefix PRIMARY
4+
;; RUN: wasm-dis %t.second.wasm | filecheck %s --check-prefix SECOND
5+
;; RUN: wasm-dis %t.third.wasm | filecheck %s --check-prefix THIRD
6+
7+
;; PRIMARY: (module
8+
;; PRIMARY-NEXT: (type $0 (func))
9+
;; PRIMARY-NEXT: (import "env" "imported_first" (func $imported_first))
10+
;; PRIMARY-NEXT: (import "env" "imported_second" (func $imported_second))
11+
;; PRIMARY-NEXT: (import "env" "imported_third" (func $imported_third))
12+
;; PRIMARY-NEXT: (import "placeholder.second" "0" (func $placeholder_0))
13+
;; PRIMARY-NEXT: (import "placeholder.third" "1" (func $placeholder_1))
14+
;; PRIMARY-NEXT: (table $0 2 funcref)
15+
;; PRIMARY-NEXT: (elem $0 (i32.const 0) $placeholder_0 $placeholder_1)
16+
;; PRIMARY-NEXT: (export "first_func" (func $first_func))
17+
;; PRIMARY-NEXT: (export "second_func" (func $trampoline_second_func))
18+
;; PRIMARY-NEXT: (export "third_func" (func $trampoline_third_func))
19+
;; PRIMARY-NEXT: (export "imported_second" (func $imported_second))
20+
;; PRIMARY-NEXT: (export "imported_third" (func $imported_third))
21+
;; PRIMARY-NEXT: (export "table" (table $0))
22+
;; PRIMARY-NEXT: (func $first_func
23+
;; PRIMARY-NEXT: (call $imported_first)
24+
;; PRIMARY-NEXT: )
25+
;; PRIMARY-NEXT: (func $trampoline_second_func
26+
;; PRIMARY-NEXT: (call_indirect (type $0)
27+
;; PRIMARY-NEXT: (i32.const 0)
28+
;; PRIMARY-NEXT: )
29+
;; PRIMARY-NEXT: )
30+
;; PRIMARY-NEXT: (func $trampoline_third_func
31+
;; PRIMARY-NEXT: (call_indirect (type $0)
32+
;; PRIMARY-NEXT: (i32.const 1)
33+
;; PRIMARY-NEXT: )
34+
;; PRIMARY-NEXT: )
35+
;; PRIMARY-NEXT: )
36+
37+
;; SECOND: (module
38+
;; SECOND-NEXT: (type $0 (func))
39+
;; SECOND-NEXT: (import "primary" "table" (table $timport$0 2 funcref))
40+
;; SECOND-NEXT: (import "primary" "imported_second" (func $imported_second))
41+
;; SECOND-NEXT: (elem $0 (i32.const 0) $second_func)
42+
;; SECOND-NEXT: (func $second_func
43+
;; SECOND-NEXT: (call $imported_second)
44+
;; SECOND-NEXT: )
45+
;; SECOND-NEXT: )
46+
47+
;; THIRD: (module
48+
;; THIRD-NEXT: (type $0 (func))
49+
;; THIRD-NEXT: (import "primary" "table" (table $timport$0 2 funcref))
50+
;; THIRD-NEXT: (import "primary" "imported_third" (func $imported_third))
51+
;; THIRD-NEXT: (elem $0 (i32.const 1) $third_func)
52+
;; THIRD-NEXT: (func $third_func
53+
;; THIRD-NEXT: (call $imported_third)
54+
;; THIRD-NEXT: )
55+
;; THIRD-NEXT: )
56+
57+
(module
58+
(import "env" "imported_first" (func $imported_first))
59+
(func $first_func (export "first_func")
60+
(call $imported_first)
61+
)
62+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(module
2+
(import "env" "imported_second" (func $imported_second))
3+
(func $second_func (export "second_func")
4+
(call $imported_second)
5+
)
6+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(module
2+
(import "env" "imported_third" (func $imported_third))
3+
(func $third_func (export "third_func")
4+
(call $imported_third)
5+
)
6+
)

0 commit comments

Comments
 (0)