diff --git a/src/tools/wasm-merge.cpp b/src/tools/wasm-merge.cpp index 3c354f745bf..295b0b2d8b0 100644 --- a/src/tools/wasm-merge.cpp +++ b/src/tools/wasm-merge.cpp @@ -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> moduleFuncs; + const std::string WasmMergeOption = "wasm-merge options"; ToolOptions options("wasm-merge", @@ -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)", @@ -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.) + 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()) { @@ -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); diff --git a/test/lit/help/wasm-merge.test b/test/lit/help/wasm-merge.test index 0386c8adf8e..c0c4ee726a1 100644 --- a/test/lit/help/wasm-merge.test +++ b/test/lit/help/wasm-merge.test @@ -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: diff --git a/test/lit/merge/manifest.wat b/test/lit/merge/manifest.wat new file mode 100644 index 00000000000..b0d2ef213ad --- /dev/null +++ b/test/lit/merge/manifest.wat @@ -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 + ) +) diff --git a/test/lit/merge/manifest.wat.second b/test/lit/merge/manifest.wat.second new file mode 100644 index 00000000000..afbd6cfefbd --- /dev/null +++ b/test/lit/merge/manifest.wat.second @@ -0,0 +1,6 @@ +(module + (import "env" "imported_second" (func $imported_second)) + (func $baz (export "baz") + (call $imported_second) + ) +) diff --git a/test/lit/merge/manifest.wat.third b/test/lit/merge/manifest.wat.third new file mode 100644 index 00000000000..9071db1c023 --- /dev/null +++ b/test/lit/merge/manifest.wat.third @@ -0,0 +1,6 @@ +(module + (import "env" "imported_third" (func $imported_third)) + (func $qux (export "qux") + (call $imported_third) + ) +) diff --git a/test/lit/wasm-split/merge-split.wast b/test/lit/wasm-split/merge-split.wast new file mode 100644 index 00000000000..7c62d201dcd --- /dev/null +++ b/test/lit/wasm-split/merge-split.wast @@ -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) + ) +) diff --git a/test/lit/wasm-split/merge-split.wast.second b/test/lit/wasm-split/merge-split.wast.second new file mode 100644 index 00000000000..e52597945c9 --- /dev/null +++ b/test/lit/wasm-split/merge-split.wast.second @@ -0,0 +1,6 @@ +(module + (import "env" "imported_second" (func $imported_second)) + (func $second_func (export "second_func") + (call $imported_second) + ) +) diff --git a/test/lit/wasm-split/merge-split.wast.third b/test/lit/wasm-split/merge-split.wast.third new file mode 100644 index 00000000000..001ffacd7db --- /dev/null +++ b/test/lit/wasm-split/merge-split.wast.third @@ -0,0 +1,6 @@ +(module + (import "env" "imported_third" (func $imported_third)) + (func $third_func (export "third_func") + (call $imported_third) + ) +)