From cf7ca5c3a476063839a278fd2dd0df48db3f4750 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Tue, 5 Aug 2025 05:40:04 +0000 Subject: [PATCH 1/6] [wasm-split] Support --placeholdermap for --multi-split This adds support for `--placeholdermap` for `--multi-split`. In the normal two-way split mode, `--placeholdermap` emits a single placeholdermap file for the primary module, but semantically a placeholdermap should exist per split module. I guess we should switch to make the placeholder file name follow the secondary module for the two-way split for consistency? --- CHANGELOG.md | 10 +-- src/tools/wasm-split/split-options.cpp | 2 +- src/tools/wasm-split/wasm-split.cpp | 6 +- test/lit/help/wasm-split.test | 5 +- .../placeholdermap-multi-split.wast | 73 +++++++++++++++++++ 5 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 test/lit/wasm-split/placeholdermap-multi-split.wast diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f96d6fb770..e6d7e63a37d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,11 +16,11 @@ Current Trunk ------------- - `wasm-split`'s `--multi-split` mode now supports more options: - `--no-placeholders`, `--import-namespace`, `--emit-module-names`, and - `--emit-text`. Because `--no-placeholders` is false by default and until now - `--multi-split` didn't use placeholders at all, this is a breaking change. If - you want to continue to do multi-split without placeholders, you need to - explicitly specify `--no-placeholders`. + `--no-placeholders`, `--import-namespace`, `--emit-module-names`, + `--emit-text`, and `--placeholdermap`. Because `--no-placeholders` is false + by default and until now `--multi-split` didn't use placeholders at all, + this is a breaking change. If you want to continue to do multi-split + without placeholders, you need to explicitly specify `--no-placeholders`. - Add a `--string-lifting` pass that raises imported string operations and constants into stringref in Binaryen IR (which can then be fully optimized, and typically lowered back down with `--string-lowering`). diff --git a/src/tools/wasm-split/split-options.cpp b/src/tools/wasm-split/split-options.cpp index 3a087e91971..eab6426564c 100644 --- a/src/tools/wasm-split/split-options.cpp +++ b/src/tools/wasm-split/split-options.cpp @@ -221,7 +221,7 @@ WasmSplitOptions::WasmSplitOptions() "", "Write a file mapping placeholder indices to the function names.", WasmSplitOption, - {Mode::Split}, + {Mode::Split, Mode::MultiSplit}, Options::Arguments::Zero, [&](Options* o, const std::string& argument) { placeholderMap = true; }) .add("--import-namespace", diff --git a/src/tools/wasm-split/wasm-split.cpp b/src/tools/wasm-split/wasm-split.cpp index 5dac884a799..d35d6e485c2 100644 --- a/src/tools/wasm-split/wasm-split.cpp +++ b/src/tools/wasm-split/wasm-split.cpp @@ -431,9 +431,13 @@ void multiSplitModule(const WasmSplitOptions& options) { } config.secondaryFuncs = std::set(funcs.begin(), funcs.end()); auto splitResults = ModuleSplitting::splitFunctions(wasm, config); - // TODO: symbolMap, placeholderMap + // TODO: symbolMap auto moduleName = options.outPrefix + mod + (options.emitBinary ? ".wasm" : ".wast"); + if (options.placeholderMap) { + writePlaceholderMap(splitResults.placeholderMap, + moduleName + ".placeholders"); + } if (options.emitModuleNames) { splitResults.secondary->name = Path::getBaseName(moduleName); } diff --git a/test/lit/help/wasm-split.test b/test/lit/help/wasm-split.test index ab15bec7a50..dab64e30226 100644 --- a/test/lit/help/wasm-split.test +++ b/test/lit/help/wasm-split.test @@ -72,8 +72,9 @@ ;; CHECK-NEXT: functions will fail before the secondary ;; CHECK-NEXT: module has been instantiated. ;; CHECK-NEXT: -;; CHECK-NEXT: --placeholdermap [split] Write a file mapping placeholder -;; CHECK-NEXT: indices to the function names. +;; CHECK-NEXT: --placeholdermap [split, multi-split] Write a file mapping +;; CHECK-NEXT: placeholder indices to the function +;; CHECK-NEXT: names. ;; CHECK-NEXT: ;; CHECK-NEXT: --import-namespace [split, instrument, multi-split] When ;; CHECK-NEXT: provided as an option for module diff --git a/test/lit/wasm-split/placeholdermap-multi-split.wast b/test/lit/wasm-split/placeholdermap-multi-split.wast new file mode 100644 index 00000000000..a1368a7db4a --- /dev/null +++ b/test/lit/wasm-split/placeholdermap-multi-split.wast @@ -0,0 +1,73 @@ +;; RUN: wasm-split -all --multi-split %s --manifest %S/multi-split.wast.manifest --out-prefix=%t --placeholdermap -o %t.wasm +;; RUN: filecheck %s --check-prefix MOD1-MAP < %t1.wasm.placeholders +;; RUN: filecheck %s --check-prefix MOD2-MAP < %t2.wasm.placeholders +;; RUN: filecheck %s --check-prefix MOD3-MAP < %t3.wasm.placeholders + +;; MOD1-MAP: 0:A + +;; MOD2-MAP: 0:B + +;; MOD3-MAP: 0:C + +(module + (type $ret-i32 (func (result i32))) + (type $ret-i64 (func (result i64))) + (type $ret-f32 (func (result f32))) + + (func $A (type $ret-i32) (result i32) + (drop + (call_ref $ret-i32 + (ref.func $A) + ) + ) + (drop + (call_ref $ret-i64 + (ref.func $B) + ) + ) + (drop + (call_ref $ret-f32 + (ref.func $C) + ) + ) + (i32.const 0) + ) + + (func $B (type $ret-i64) (result i64) + (drop + (call_ref $ret-i32 + (ref.func $A) + ) + ) + (drop + (call_ref $ret-i64 + (ref.func $B) + ) + ) + (drop + (call_ref $ret-f32 + (ref.func $C) + ) + ) + (i64.const 0) + ) + + (func $C (type $ret-f32) (result f32) + (drop + (call_ref $ret-i32 + (ref.func $A) + ) + ) + (drop + (call_ref $ret-i64 + (ref.func $B) + ) + ) + (drop + (call_ref $ret-f32 + (ref.func $C) + ) + ) + (f32.const 0) + ) +) From a278ec2aa7cc7688d388adde39a676bacff2454a Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Sat, 9 Aug 2025 04:36:32 +0000 Subject: [PATCH 2/6] Print multiple tables for primary module --- src/ir/module-splitting.cpp | 7 ++-- src/ir/module-splitting.h | 3 +- src/tools/wasm-split/wasm-split.cpp | 33 +++++++++++++------ .../placeholdermap-multi-split.wast | 17 +++++----- test/lit/wasm-split/placeholdermap.wast | 3 +- 5 files changed, 40 insertions(+), 23 deletions(-) diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 99bf4cb3005..48bbd2001c8 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -302,8 +302,9 @@ struct ModuleSplitter { // names. std::map exportedPrimaryFuncs; - // Map placeholder indices to the names of the functions they replace. - std::map placeholderMap; + // For each table, map placeholder indices to the names of the functions they + // replace. + std::unordered_map> placeholderMap; // Internal name of the LOAD_SECONDARY_MODULE function. Name internalLoadSecondaryModule; @@ -721,7 +722,7 @@ void ModuleSplitter::setupTablePatching() { } assert(table == tableManager.activeTable->name); - placeholderMap[index] = ref->func; + placeholderMap[table][index] = ref->func; auto* secondaryFunc = secondary.getFunction(ref->func); replacedElems[index] = secondaryFunc; if (!config.usePlaceholders) { diff --git a/src/ir/module-splitting.h b/src/ir/module-splitting.h index 89e4dd2bb14..ecf79148235 100644 --- a/src/ir/module-splitting.h +++ b/src/ir/module-splitting.h @@ -41,6 +41,7 @@ #define wasm_ir_module_splitting_h #include "wasm.h" +#include namespace wasm::ModuleSplitting { @@ -77,7 +78,7 @@ struct Config { struct Results { std::unique_ptr secondary; - std::map placeholderMap; + std::unordered_map> placeholderMap; }; // Returns the new secondary module and modifies the `primary` module in place. diff --git a/src/tools/wasm-split/wasm-split.cpp b/src/tools/wasm-split/wasm-split.cpp index cca63c24f8a..34b6f3a40f9 100644 --- a/src/tools/wasm-split/wasm-split.cpp +++ b/src/tools/wasm-split/wasm-split.cpp @@ -20,13 +20,11 @@ #include #include "ir/module-splitting.h" -#include "ir/names.h" #include "support/file.h" #include "support/name.h" #include "support/path.h" #include "support/utilities.h" #include "wasm-binary.h" -#include "wasm-builder.h" #include "wasm-io.h" #include "wasm-validator.h" @@ -203,12 +201,24 @@ void writeSymbolMap(Module& wasm, std::string filename) { runner.run(); } -void writePlaceholderMap(const std::map placeholderMap, - std::string filename) { +void writePlaceholderMap( + Module& wasm, + const std::unordered_map> placeholderMap, + std::string filename) { Output output(filename, Flags::Text); auto& o = output.getStream(); - for (auto& [index, func] : placeholderMap) { - o << index << ':' << func << '\n'; + for (Index i = 0; i < wasm.tables.size(); i++) { + const auto& table = wasm.tables[i]; + auto it = placeholderMap.find(table->name); + if (it != placeholderMap.end()) { + o << "table " << i << " (" << table->name << ")" << "\n"; + for (auto& [index, func] : it->second) { + o << index << ':' << func << '\n'; + } + } + if (i < wasm.tables.size() - 1) { + o << "\n"; + } } } @@ -344,7 +354,8 @@ void splitModule(const WasmSplitOptions& options) { } if (options.placeholderMap) { - writePlaceholderMap(splitResults.placeholderMap, + writePlaceholderMap(wasm, + splitResults.placeholderMap, options.primaryOutput + ".placeholders"); } @@ -422,6 +433,7 @@ void multiSplitModule(const WasmSplitOptions& options) { wasm.name = Path::getBaseName(options.output); } + std::unordered_map> placeholderMap; for (auto& [mod, funcs] : moduleFuncs) { if (options.verbose) { std::cerr << "Splitting module " << mod << '\n'; @@ -437,8 +449,7 @@ void multiSplitModule(const WasmSplitOptions& options) { writeSymbolMap(*splitResults.secondary, moduleName + ".symbols"); } if (options.placeholderMap) { - writePlaceholderMap(splitResults.placeholderMap, - moduleName + ".placeholders"); + placeholderMap.merge(splitResults.placeholderMap); } if (options.emitModuleNames) { splitResults.secondary->name = Path::getBaseName(moduleName); @@ -448,7 +459,9 @@ void multiSplitModule(const WasmSplitOptions& options) { if (options.symbolMap) { writeSymbolMap(wasm, options.output + ".symbols"); } - + if (options.placeholderMap) { + writePlaceholderMap(wasm, placeholderMap, options.output + ".placeholders"); + } writeModule(wasm, options.output, options); } diff --git a/test/lit/wasm-split/placeholdermap-multi-split.wast b/test/lit/wasm-split/placeholdermap-multi-split.wast index a1368a7db4a..e748fb5f48d 100644 --- a/test/lit/wasm-split/placeholdermap-multi-split.wast +++ b/test/lit/wasm-split/placeholdermap-multi-split.wast @@ -1,13 +1,14 @@ ;; RUN: wasm-split -all --multi-split %s --manifest %S/multi-split.wast.manifest --out-prefix=%t --placeholdermap -o %t.wasm -;; RUN: filecheck %s --check-prefix MOD1-MAP < %t1.wasm.placeholders -;; RUN: filecheck %s --check-prefix MOD2-MAP < %t2.wasm.placeholders -;; RUN: filecheck %s --check-prefix MOD3-MAP < %t3.wasm.placeholders +;; RUN: filecheck %s --check-prefix MAP < %t.wasm.placeholders -;; MOD1-MAP: 0:A - -;; MOD2-MAP: 0:B - -;; MOD3-MAP: 0:C +;; MAP: table 0 (0) +;; MAP-NEXT: 0:A +;; MAP-NEXT: +;; MAP-NEXT: table 1 (0_1) +;; MAP-NEXT: 0:B +;; MAP-NEXT: +;; MAP-NEXT: table 2 (0_2) +;; MAP-NEXT: 0:C (module (type $ret-i32 (func (result i32))) diff --git a/test/lit/wasm-split/placeholdermap.wast b/test/lit/wasm-split/placeholdermap.wast index dd8598b0b95..ce1fe195f01 100644 --- a/test/lit/wasm-split/placeholdermap.wast +++ b/test/lit/wasm-split/placeholdermap.wast @@ -2,7 +2,8 @@ ;; RUN: filecheck %s --check-prefix MAP < %t.1.wasm.placeholders ;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY -;; MAP: 0:foo +;; MAP: table 0 (table) +;; MAP-NEXT: 0:foo ;; MAP-NEXT: 2:baz ;; MAP-NOT: bar From 11d1a57c1e652112e5b439831e619bde17278ae6 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Sat, 9 Aug 2025 05:12:57 +0000 Subject: [PATCH 3/6] Remove include --- src/ir/module-splitting.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ir/module-splitting.h b/src/ir/module-splitting.h index ecf79148235..b21a4f2bc8f 100644 --- a/src/ir/module-splitting.h +++ b/src/ir/module-splitting.h @@ -41,7 +41,6 @@ #define wasm_ir_module_splitting_h #include "wasm.h" -#include namespace wasm::ModuleSplitting { From ae2142d84032554626b60ccf874e1dfd595e99ed Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Sat, 9 Aug 2025 05:13:59 +0000 Subject: [PATCH 4/6] fix --- src/tools/wasm-split/wasm-split.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/wasm-split/wasm-split.cpp b/src/tools/wasm-split/wasm-split.cpp index 34b6f3a40f9..451380110a2 100644 --- a/src/tools/wasm-split/wasm-split.cpp +++ b/src/tools/wasm-split/wasm-split.cpp @@ -215,9 +215,9 @@ void writePlaceholderMap( for (auto& [index, func] : it->second) { o << index << ':' << func << '\n'; } - } - if (i < wasm.tables.size() - 1) { - o << "\n"; + if (i < wasm.tables.size() - 1) { + o << "\n"; + } } } } From b7ad1e1a2dd848fc31620e199b2f6d21ef5c7b2c Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Mon, 11 Aug 2025 15:55:46 -0700 Subject: [PATCH 5/6] Update src/tools/wasm-split/wasm-split.cpp Co-authored-by: Thomas Lively --- src/tools/wasm-split/wasm-split.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/wasm-split/wasm-split.cpp b/src/tools/wasm-split/wasm-split.cpp index 451380110a2..05b9f929c37 100644 --- a/src/tools/wasm-split/wasm-split.cpp +++ b/src/tools/wasm-split/wasm-split.cpp @@ -211,7 +211,7 @@ void writePlaceholderMap( const auto& table = wasm.tables[i]; auto it = placeholderMap.find(table->name); if (it != placeholderMap.end()) { - o << "table " << i << " (" << table->name << ")" << "\n"; + o << "table " << i << " ($" << table->name << ")" << "\n"; for (auto& [index, func] : it->second) { o << index << ':' << func << '\n'; } From cc6978a8df23131ece47596becb227432420b53f Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Mon, 11 Aug 2025 23:38:44 +0000 Subject: [PATCH 6/6] Only print indices --- src/tools/wasm-split/wasm-split.cpp | 4 ++-- test/lit/wasm-split/placeholdermap-multi-split.wast | 6 +++--- test/lit/wasm-split/placeholdermap.wast | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/wasm-split/wasm-split.cpp b/src/tools/wasm-split/wasm-split.cpp index 05b9f929c37..c520c04a1cf 100644 --- a/src/tools/wasm-split/wasm-split.cpp +++ b/src/tools/wasm-split/wasm-split.cpp @@ -203,7 +203,7 @@ void writeSymbolMap(Module& wasm, std::string filename) { void writePlaceholderMap( Module& wasm, - const std::unordered_map> placeholderMap, + const std::unordered_map>& placeholderMap, std::string filename) { Output output(filename, Flags::Text); auto& o = output.getStream(); @@ -211,7 +211,7 @@ void writePlaceholderMap( const auto& table = wasm.tables[i]; auto it = placeholderMap.find(table->name); if (it != placeholderMap.end()) { - o << "table " << i << " ($" << table->name << ")" << "\n"; + o << "table " << i << "\n"; for (auto& [index, func] : it->second) { o << index << ':' << func << '\n'; } diff --git a/test/lit/wasm-split/placeholdermap-multi-split.wast b/test/lit/wasm-split/placeholdermap-multi-split.wast index e748fb5f48d..1af96452c41 100644 --- a/test/lit/wasm-split/placeholdermap-multi-split.wast +++ b/test/lit/wasm-split/placeholdermap-multi-split.wast @@ -1,13 +1,13 @@ ;; RUN: wasm-split -all --multi-split %s --manifest %S/multi-split.wast.manifest --out-prefix=%t --placeholdermap -o %t.wasm ;; RUN: filecheck %s --check-prefix MAP < %t.wasm.placeholders -;; MAP: table 0 (0) +;; MAP: table 0 ;; MAP-NEXT: 0:A ;; MAP-NEXT: -;; MAP-NEXT: table 1 (0_1) +;; MAP-NEXT: table 1 ;; MAP-NEXT: 0:B ;; MAP-NEXT: -;; MAP-NEXT: table 2 (0_2) +;; MAP-NEXT: table 2 ;; MAP-NEXT: 0:C (module diff --git a/test/lit/wasm-split/placeholdermap.wast b/test/lit/wasm-split/placeholdermap.wast index ce1fe195f01..772676d1b42 100644 --- a/test/lit/wasm-split/placeholdermap.wast +++ b/test/lit/wasm-split/placeholdermap.wast @@ -2,7 +2,7 @@ ;; RUN: filecheck %s --check-prefix MAP < %t.1.wasm.placeholders ;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY -;; MAP: table 0 (table) +;; MAP: table 0 ;; MAP-NEXT: 0:foo ;; MAP-NEXT: 2:baz ;; MAP-NOT: bar