Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/tools/wasm-merge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,12 @@ int main(int argc, const char* argv[]) {
std::string outputSourceMapFilename;
std::string outputSourceMapUrl;

// We can write wasm-split manifests that can later be fed to wasm-split to
// split the merged module back up along the lines of the original modules.
// Map modules to their functions so we can write the manifest.
std::string manifestFile;
std::unordered_map<Name, std::vector<Name>> moduleFuncs;

const std::string WasmMergeOption = "wasm-merge options";

ToolOptions options("wasm-merge",
Expand Down Expand Up @@ -687,6 +693,16 @@ Input source maps can be specified by adding an -ism option right after the modu
[&outputSourceMapUrl](Options* o, const std::string& argument) {
outputSourceMapUrl = argument;
})
.add("--output-manifest",
"",
"Write a wasm-split manifest to the specified file. This manifest can "
"be given to wasm-split to split the merged module along the lines of "
"the original modules.",
WasmMergeOption,
Options::Arguments::One,
[&manifestFile](Options* o, const std::string& argument) {
manifestFile = argument;
})
.add("--rename-export-conflicts",
"-rec",
"Rename exports to avoid conflicts (rather than error)",
Expand Down Expand Up @@ -780,6 +796,16 @@ Input source maps can be specified by adding an -ism option right after the modu
// This is a later module: do a full merge.
mergeInto(*currModule, inputFileName);

// The functions in the module have been renamed and copied rather than
// moved, so we can get their final names directly. (We don't need this
// for the first module because it does not appear in the manifest.)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we don't print this for the first module, but we are still collecting it for the first module, given that we start from 0 in the for loop above, no?

  for (Index i = 0; i < inputFiles.size(); i++) {

If not excluding the first module here was intentional, the comment is confusing. Also we can probably exclude the first module here, given that it won't be used.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because we are in the else branch of the if (!laterInput), so we are only collecting this info for the first module. The comment is explaining why we don't need this in the other arm as well.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we are only collecting this info for the first module.

You mean secondary modules, right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, sorry. We are only collecting info for secondary modules.

auto& funcs = moduleFuncs[inputFileName];
for (auto& func : currModule->functions) {
if (!func->imported()) {
funcs.push_back(func->name);
}
}

// Validate after each merged module, when we are in pass-debug mode
// (this can be quadratic time).
if (PassRunner::getPassDebug()) {
Expand Down Expand Up @@ -822,6 +848,25 @@ Input source maps can be specified by adding an -ism option right after the modu
}

// Output.
if (!manifestFile.empty()) {
std::ofstream manifest(manifestFile);
// Skip module 0 because it will be the primary module for the split and
// does not need to appear in the manifest.
for (size_t i = 1; i < inputFileNames.size(); i++) {
auto moduleName = inputFileNames[i];
const auto& funcs = moduleFuncs[moduleName];
if (funcs.empty()) {
continue;
}

manifest << moduleName << "\n";
for (auto func : funcs) {
manifest << func << "\n";
}
manifest << "\n";
}
}

if (options.extra.count("output") > 0) {
ModuleWriter writer(options.passOptions);
writer.setBinary(emitBinary);
Expand Down
6 changes: 6 additions & 0 deletions test/lit/help/wasm-merge.test
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@
;; CHECK-NEXT:
;; CHECK-NEXT: --output-source-map-url,-osu Emit specified string as source map URL
;; CHECK-NEXT:
;; CHECK-NEXT: --output-manifest Write a wasm-split manifest to the
;; CHECK-NEXT: specified file. This manifest can be
;; CHECK-NEXT: given to wasm-split to split the merged
;; CHECK-NEXT: module along the lines of the original
;; CHECK-NEXT: modules.
;; CHECK-NEXT:
;; CHECK-NEXT: --rename-export-conflicts,-rec Rename exports to avoid conflicts (rather
;; CHECK-NEXT: than error)
;; CHECK-NEXT:
Expand Down
23 changes: 23 additions & 0 deletions test/lit/merge/manifest.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
;; RUN: wasm-merge %s first %s.second second %s.third third --output-manifest %t.manifest -S -o %t.wasm
;; RUN: cat %t.manifest | filecheck %s

;; The first module is the primary module and does not appear in the manifest.
;; CHECK-NOT: first
;; CHECK-NOT: foo
;; CHECK-NOT: bar

;; CHECK: second
;; CHECK-NEXT: baz
;; CHECK-NEXT:
;; CHECK-NEXT: third
;; CHECK-NEXT: qux

(module
(import "env" "imported_first" (func $imported_first))
(func $foo (export "foo")
(call $imported_first)
)
(func $bar (export "bar")
nop
)
)
6 changes: 6 additions & 0 deletions test/lit/merge/manifest.wat.second
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(module
(import "env" "imported_second" (func $imported_second))
(func $baz (export "baz")
(call $imported_second)
)
)
6 changes: 6 additions & 0 deletions test/lit/merge/manifest.wat.third
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(module
(import "env" "imported_third" (func $imported_third))
(func $qux (export "qux")
(call $imported_third)
)
)
62 changes: 62 additions & 0 deletions test/lit/wasm-split/merge-split.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
;; RUN: wasm-merge %s first %s.second second %s.third third --output-manifest %t.manifest -S -o %t.wasm
;; RUN: wasm-split %t.wasm --multi-split --manifest %t.manifest -g -o %t.primary.wasm --out-prefix %t.
;; RUN: wasm-dis %t.primary.wasm | filecheck %s --check-prefix PRIMARY
;; RUN: wasm-dis %t.second.wasm | filecheck %s --check-prefix SECOND
;; RUN: wasm-dis %t.third.wasm | filecheck %s --check-prefix THIRD

;; PRIMARY: (module
;; PRIMARY-NEXT: (type $0 (func))
;; PRIMARY-NEXT: (import "env" "imported_first" (func $imported_first))
;; PRIMARY-NEXT: (import "env" "imported_second" (func $imported_second))
;; PRIMARY-NEXT: (import "env" "imported_third" (func $imported_third))
;; PRIMARY-NEXT: (import "placeholder.second" "0" (func $placeholder_0))
;; PRIMARY-NEXT: (import "placeholder.third" "1" (func $placeholder_1))
;; PRIMARY-NEXT: (table $0 2 funcref)
;; PRIMARY-NEXT: (elem $0 (i32.const 0) $placeholder_0 $placeholder_1)
;; PRIMARY-NEXT: (export "first_func" (func $first_func))
;; PRIMARY-NEXT: (export "second_func" (func $trampoline_second_func))
;; PRIMARY-NEXT: (export "third_func" (func $trampoline_third_func))
;; PRIMARY-NEXT: (export "imported_second" (func $imported_second))
;; PRIMARY-NEXT: (export "imported_third" (func $imported_third))
;; PRIMARY-NEXT: (export "table" (table $0))
;; PRIMARY-NEXT: (func $first_func
;; PRIMARY-NEXT: (call $imported_first)
;; PRIMARY-NEXT: )
;; PRIMARY-NEXT: (func $trampoline_second_func
;; PRIMARY-NEXT: (call_indirect (type $0)
;; PRIMARY-NEXT: (i32.const 0)
;; PRIMARY-NEXT: )
;; PRIMARY-NEXT: )
;; PRIMARY-NEXT: (func $trampoline_third_func
;; PRIMARY-NEXT: (call_indirect (type $0)
;; PRIMARY-NEXT: (i32.const 1)
;; PRIMARY-NEXT: )
;; PRIMARY-NEXT: )
;; PRIMARY-NEXT: )

;; SECOND: (module
;; SECOND-NEXT: (type $0 (func))
;; SECOND-NEXT: (import "primary" "table" (table $timport$0 2 funcref))
;; SECOND-NEXT: (import "primary" "imported_second" (func $imported_second))
;; SECOND-NEXT: (elem $0 (i32.const 0) $second_func)
;; SECOND-NEXT: (func $second_func
;; SECOND-NEXT: (call $imported_second)
;; SECOND-NEXT: )
;; SECOND-NEXT: )

;; THIRD: (module
;; THIRD-NEXT: (type $0 (func))
;; THIRD-NEXT: (import "primary" "table" (table $timport$0 2 funcref))
;; THIRD-NEXT: (import "primary" "imported_third" (func $imported_third))
;; THIRD-NEXT: (elem $0 (i32.const 1) $third_func)
;; THIRD-NEXT: (func $third_func
;; THIRD-NEXT: (call $imported_third)
;; THIRD-NEXT: )
;; THIRD-NEXT: )

(module
(import "env" "imported_first" (func $imported_first))
(func $first_func (export "first_func")
(call $imported_first)
)
)
6 changes: 6 additions & 0 deletions test/lit/wasm-split/merge-split.wast.second
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(module
(import "env" "imported_second" (func $imported_second))
(func $second_func (export "second_func")
(call $imported_second)
)
)
6 changes: 6 additions & 0 deletions test/lit/wasm-split/merge-split.wast.third
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(module
(import "env" "imported_third" (func $imported_third))
(func $third_func (export "third_func")
(call $imported_third)
)
)
Loading