From 9abfe7844bf2fc7196fe5f6355dec1a1a40bfcd8 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Fri, 15 May 2026 07:21:20 +0000 Subject: [PATCH 1/6] [wasm-split] Fix table naming conflicts After #8688, we split module elements, including tables, earlier than `indirectCallsToSecondaryFunctions`, which calls `getSlot`, which calls `makeTable`. But when making a table, we only try to get a valid name within the primary module: https://github.com/WebAssembly/binaryen/blob/2f1f55aef6d9adfa6fdc2c25e46d202232dbf6e2/src/ir/module-splitting.cpp#L235-L238 If an existing table's name was `0` and it was moved to a secondary module in `shareImportable` already, this will happily create an active table with the name `0` again. And in `setupTablePatching`, because the secondary module already has `0`, the active table will not be exported / imported there: https://github.com/WebAssembly/binaryen/blob/2f1f55aef6d9adfa6fdc2c25e46d202232dbf6e2/src/ir/module-splitting.cpp#L1103-L1117 But this existing table is NOT the active table, and this table's type may not even be `funcref`. This fixes `makeTable` so that it makes a table name that does not collide with any table names not only in the primary module but all secondary modules. --- src/ir/module-splitting.cpp | 24 ++++++++--- test/lit/wasm-split/table-name-conflict.wast | 45 ++++++++++++++++++++ 2 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 test/lit/wasm-split/table-name-conflict.wast diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 6ae18645128..e30b61bf5a0 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -112,13 +112,15 @@ struct TableSlotManager { Expression* makeExpr(Module& module); }; Module& module; + const std::vector>& secondaries; Table* activeTable = nullptr; ElementSegment* activeSegment = nullptr; Slot activeBase; std::map funcIndices; std::vector activeTableSegments; - TableSlotManager(Module& module); + TableSlotManager(Module& module, + const std::vector>& secondaries); Table* makeTable(); ElementSegment* makeElementSegment(); @@ -149,7 +151,9 @@ void TableSlotManager::addSlot(Name func, Slot slot) { funcIndices.insert({func, slot}); } -TableSlotManager::TableSlotManager(Module& module) : module(module) { +TableSlotManager::TableSlotManager( + Module& module, const std::vector>& secondaries) + : module(module), secondaries(secondaries) { // If possible, just create a new table to manage all primary-to-secondary // calls lazily. Do not re-use slots for functions that will already be in // existing tables, since that is not correct in the face of table mutations. @@ -233,8 +237,18 @@ TableSlotManager::TableSlotManager(Module& module) : module(module) { } Table* TableSlotManager::makeTable() { - return module.addTable( - Builder::makeTable(Names::getValidTableName(module, Name::fromInt(0)))); + Name name = Names::getValidName("0", [&](Name test) { + if (module.getTableOrNull(test)) { + return false; + } + for (auto& secondary : secondaries) { + if (secondary->getTableOrNull(test)) { + return false; + } + } + return true; + }); + return module.addTable(Builder::makeTable(name)); } ElementSegment* TableSlotManager::makeElementSegment() { @@ -347,7 +361,7 @@ struct ModuleSplitter { void setupTablePatching(); ModuleSplitter(Module& primary, const Config& config) - : config(config), primary(primary), tableManager(primary), + : config(config), primary(primary), tableManager(primary, secondaries), exportedPrimaryFuncs(initExportedPrimaryFuncs(primary)), exportedPrimaryItems(initExportedPrimaryItems(primary)) { classifyFunctions(); diff --git a/test/lit/wasm-split/table-name-conflict.wast b/test/lit/wasm-split/table-name-conflict.wast new file mode 100644 index 00000000000..539eb046204 --- /dev/null +++ b/test/lit/wasm-split/table-name-conflict.wast @@ -0,0 +1,45 @@ +;; RUN: wasm-split %s -all -g -o1 %t.1.wasm -o2 %t.2.wasm --split-funcs=split +;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY +;; RUN: wasm-dis %t.2.wasm | filecheck %s --check-prefix SECONDARY + +;; Regression test for a bug when an existing table, which is to be split to the +;; secondary module, has the name '0'. The newly created active table should +;; have a different name. + +(module + (table $0 0 externref) + (export "split" (func $split)) + (func $split + (table.set $0 + (i32.const 0) + (ref.null extern) + ) + ) +) + +;; PRIMARY: (module +;; PRIMARY-NEXT: (type $0 (func)) +;; PRIMARY-NEXT: (import "placeholder.deferred" "0" (func $placeholder_0)) +;; PRIMARY-NEXT: (table $0 1 funcref) +;; PRIMARY-NEXT: (elem $0 (i32.const 0) $placeholder_0) +;; PRIMARY-NEXT: (export "split" (func $trampoline_split)) +;; PRIMARY-NEXT: (export "table" (table $0)) +;; PRIMARY-NEXT: (func $trampoline_split +;; PRIMARY-NEXT: (call_indirect (type $0) +;; PRIMARY-NEXT: (i32.const 0) +;; PRIMARY-NEXT: ) +;; PRIMARY-NEXT: ) +;; PRIMARY-NEXT: ) + +;; SECONDARY: (module +;; SECONDARY-NEXT: (type $0 (func)) +;; SECONDARY-NEXT: (import "primary" "table" (table $timport$0 1 funcref)) +;; SECONDARY-NEXT: (table $0 0 externref) +;; SECONDARY-NEXT: (elem $0 (table $timport$0) (i32.const 0) func $split) +;; SECONDARY-NEXT: (func $split +;; SECONDARY-NEXT: (table.set $0 +;; SECONDARY-NEXT: (i32.const 0) +;; SECONDARY-NEXT: (ref.null noextern) +;; SECONDARY-NEXT: ) +;; SECONDARY-NEXT: ) +;; SECONDARY-NEXT: ) From d38d9fa43c64b6ef7b22ad6f711323ffb2e61be8 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Fri, 15 May 2026 07:50:45 +0000 Subject: [PATCH 2/6] Disable Split fuzzer --- scripts/fuzz_opt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index b089ec7f1f6..4173058abf8 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -2525,7 +2525,7 @@ def handle(self, wasm): TrapsNeverHappen(), CtorEval(), Merge(), - Split(), +# Split(), # Will reenable after stabilized RoundtripText(), ClusterFuzz(), Two(), From 57d63fc3f437a328d20f1b9cebcf297e0869b55b Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Fri, 15 May 2026 19:07:29 +0000 Subject: [PATCH 3/6] Make a set of secondary names first --- src/ir/module-splitting.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index e30b61bf5a0..048354cea16 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -237,14 +237,18 @@ TableSlotManager::TableSlotManager( } Table* TableSlotManager::makeTable() { + std::unordered_set secondaryTableNames; + for (auto& secondary : secondaries) { + for (auto& table : secondary->tables) { + secondaryTableNames.insert(table->name); + } + } Name name = Names::getValidName("0", [&](Name test) { if (module.getTableOrNull(test)) { return false; } - for (auto& secondary : secondaries) { - if (secondary->getTableOrNull(test)) { - return false; - } + if (secondaryTableNames.count(test)) { + return false; } return true; }); From 24a4d9142f8473d48f16abee931caa70a13baa5b Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Fri, 15 May 2026 21:21:23 +0000 Subject: [PATCH 4/6] Use -S and cat --- test/lit/wasm-split/table-name-conflict.wast | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/lit/wasm-split/table-name-conflict.wast b/test/lit/wasm-split/table-name-conflict.wast index 539eb046204..983234b8854 100644 --- a/test/lit/wasm-split/table-name-conflict.wast +++ b/test/lit/wasm-split/table-name-conflict.wast @@ -1,6 +1,6 @@ -;; RUN: wasm-split %s -all -g -o1 %t.1.wasm -o2 %t.2.wasm --split-funcs=split -;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY -;; RUN: wasm-dis %t.2.wasm | filecheck %s --check-prefix SECONDARY +;; RUN: wasm-split %s -all -g -S -o1 %t.1.wast -o2 %t.2.wast --split-funcs=split +;; RUN: cat %t.1.wast | filecheck %s --check-prefix PRIMARY +;; RUN: cat %t.2.wast | filecheck %s --check-prefix SECONDARY ;; Regression test for a bug when an existing table, which is to be split to the ;; secondary module, has the name '0'. The newly created active table should @@ -19,13 +19,13 @@ ;; PRIMARY: (module ;; PRIMARY-NEXT: (type $0 (func)) -;; PRIMARY-NEXT: (import "placeholder.deferred" "0" (func $placeholder_0)) -;; PRIMARY-NEXT: (table $0 1 funcref) +;; PRIMARY-NEXT: (import "placeholder.deferred" "0" (func $placeholder_0 (type $0))) +;; PRIMARY-NEXT: (table $0_0 1 funcref) ;; PRIMARY-NEXT: (elem $0 (i32.const 0) $placeholder_0) ;; PRIMARY-NEXT: (export "split" (func $trampoline_split)) -;; PRIMARY-NEXT: (export "table" (table $0)) -;; PRIMARY-NEXT: (func $trampoline_split -;; PRIMARY-NEXT: (call_indirect (type $0) +;; PRIMARY-NEXT: (export "table" (table $0_0)) +;; PRIMARY-NEXT: (func $trampoline_split (type $0) +;; PRIMARY-NEXT: (call_indirect $0_0 (type $0) ;; PRIMARY-NEXT: (i32.const 0) ;; PRIMARY-NEXT: ) ;; PRIMARY-NEXT: ) @@ -33,10 +33,10 @@ ;; SECONDARY: (module ;; SECONDARY-NEXT: (type $0 (func)) -;; SECONDARY-NEXT: (import "primary" "table" (table $timport$0 1 funcref)) +;; SECONDARY-NEXT: (import "primary" "table" (table $0_0 1 funcref)) ;; SECONDARY-NEXT: (table $0 0 externref) -;; SECONDARY-NEXT: (elem $0 (table $timport$0) (i32.const 0) func $split) -;; SECONDARY-NEXT: (func $split +;; SECONDARY-NEXT: (elem $0 (table $0_0) (i32.const 0) func $split) +;; SECONDARY-NEXT: (func $split (type $0) ;; SECONDARY-NEXT: (table.set $0 ;; SECONDARY-NEXT: (i32.const 0) ;; SECONDARY-NEXT: (ref.null noextern) From cf68bab7f14e6e3b6174f29ef9406c875ce9fa46 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Fri, 15 May 2026 21:31:21 +0000 Subject: [PATCH 5/6] Comment --- src/ir/module-splitting.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 761787b6bce..da097103279 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -237,6 +237,9 @@ TableSlotManager::TableSlotManager( } Table* TableSlotManager::makeTable() { + // Because the active table will be imported in secondary modules, its name + // should not collide with any existing tables in primary and secondary + // modules. std::unordered_set secondaryTableNames; for (auto& secondary : secondaries) { for (auto& table : secondary->tables) { From 227315907669965433181cef4677e5a32967bc2b Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Fri, 15 May 2026 14:50:05 -0700 Subject: [PATCH 6/6] Update src/ir/module-splitting.cpp Co-authored-by: Thomas Lively --- src/ir/module-splitting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index da097103279..30f6b9b563c 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -250,7 +250,7 @@ Table* TableSlotManager::makeTable() { if (module.getTableOrNull(test)) { return false; } - if (secondaryTableNames.count(test)) { + if (secondaryTableNames.contains(test)) { return false; } return true;