Skip to content

Commit d3029d2

Browse files
authored
[wasm-split] Fix table naming conflicts (#8708)
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. This also disables `Split` fuzzer for now; I'm finding more bugs, so I'll reenable it after it is more stabilized.
1 parent b5b9ebd commit d3029d2

3 files changed

Lines changed: 72 additions & 6 deletions

File tree

scripts/fuzz_opt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2525,7 +2525,7 @@ def handle(self, wasm):
25252525
TrapsNeverHappen(),
25262526
CtorEval(),
25272527
Merge(),
2528-
Split(),
2528+
# Split(), # Will reenable after stabilized
25292529
RoundtripText(),
25302530
ClusterFuzz(),
25312531
Two(),

src/ir/module-splitting.cpp

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,15 @@ struct TableSlotManager {
112112
Expression* makeExpr(Module& module);
113113
};
114114
Module& module;
115+
const std::vector<std::unique_ptr<Module>>& secondaries;
115116
Table* activeTable = nullptr;
116117
ElementSegment* activeSegment = nullptr;
117118
Slot activeBase;
118119
std::map<Name, Slot> funcIndices;
119120
std::vector<ElementSegment*> activeTableSegments;
120121

121-
TableSlotManager(Module& module);
122+
TableSlotManager(Module& module,
123+
const std::vector<std::unique_ptr<Module>>& secondaries);
122124

123125
Table* makeTable();
124126
ElementSegment* makeElementSegment();
@@ -149,7 +151,9 @@ void TableSlotManager::addSlot(Name func, Slot slot) {
149151
funcIndices.insert({func, slot});
150152
}
151153

152-
TableSlotManager::TableSlotManager(Module& module) : module(module) {
154+
TableSlotManager::TableSlotManager(
155+
Module& module, const std::vector<std::unique_ptr<Module>>& secondaries)
156+
: module(module), secondaries(secondaries) {
153157
// If possible, just create a new table to manage all primary-to-secondary
154158
// calls lazily. Do not re-use slots for functions that will already be in
155159
// existing tables, since that is not correct in the face of table mutations.
@@ -233,8 +237,25 @@ TableSlotManager::TableSlotManager(Module& module) : module(module) {
233237
}
234238

235239
Table* TableSlotManager::makeTable() {
236-
return module.addTable(
237-
Builder::makeTable(Names::getValidTableName(module, Name::fromInt(0))));
240+
// Because the active table will be imported in secondary modules, its name
241+
// should not collide with any existing tables in primary and secondary
242+
// modules.
243+
std::unordered_set<Name> secondaryTableNames;
244+
for (auto& secondary : secondaries) {
245+
for (auto& table : secondary->tables) {
246+
secondaryTableNames.insert(table->name);
247+
}
248+
}
249+
Name name = Names::getValidName("0", [&](Name test) {
250+
if (module.getTableOrNull(test)) {
251+
return false;
252+
}
253+
if (secondaryTableNames.contains(test)) {
254+
return false;
255+
}
256+
return true;
257+
});
258+
return module.addTable(Builder::makeTable(name));
238259
}
239260

240261
ElementSegment* TableSlotManager::makeElementSegment() {
@@ -347,7 +368,7 @@ struct ModuleSplitter {
347368
void setupTablePatching();
348369

349370
ModuleSplitter(Module& primary, const Config& config)
350-
: config(config), primary(primary), tableManager(primary),
371+
: config(config), primary(primary), tableManager(primary, secondaries),
351372
exportedPrimaryFuncs(initExportedPrimaryFuncs(primary)),
352373
exportedPrimaryItems(initExportedPrimaryItems(primary)) {
353374
classifyFunctions();
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
;; RUN: wasm-split %s -all -g -S -o1 %t.1.wast -o2 %t.2.wast --split-funcs=split
2+
;; RUN: cat %t.1.wast | filecheck %s --check-prefix PRIMARY
3+
;; RUN: cat %t.2.wast | filecheck %s --check-prefix SECONDARY
4+
5+
;; Regression test for a bug when an existing table, which is to be split to the
6+
;; secondary module, has the name '0'. The newly created active table should
7+
;; have a different name.
8+
9+
(module
10+
(table $0 0 externref)
11+
(export "split" (func $split))
12+
(func $split
13+
(table.set $0
14+
(i32.const 0)
15+
(ref.null extern)
16+
)
17+
)
18+
)
19+
20+
;; PRIMARY: (module
21+
;; PRIMARY-NEXT: (type $0 (func))
22+
;; PRIMARY-NEXT: (import "placeholder.deferred" "0" (func $placeholder_0 (type $0)))
23+
;; PRIMARY-NEXT: (table $0_0 1 funcref)
24+
;; PRIMARY-NEXT: (elem $0 (i32.const 0) $placeholder_0)
25+
;; PRIMARY-NEXT: (export "split" (func $trampoline_split))
26+
;; PRIMARY-NEXT: (export "table" (table $0_0))
27+
;; PRIMARY-NEXT: (func $trampoline_split (type $0)
28+
;; PRIMARY-NEXT: (call_indirect $0_0 (type $0)
29+
;; PRIMARY-NEXT: (i32.const 0)
30+
;; PRIMARY-NEXT: )
31+
;; PRIMARY-NEXT: )
32+
;; PRIMARY-NEXT: )
33+
34+
;; SECONDARY: (module
35+
;; SECONDARY-NEXT: (type $0 (func))
36+
;; SECONDARY-NEXT: (import "primary" "table" (table $0_0 1 funcref))
37+
;; SECONDARY-NEXT: (table $0 0 externref)
38+
;; SECONDARY-NEXT: (elem $0 (table $0_0) (i32.const 0) func $split)
39+
;; SECONDARY-NEXT: (func $split (type $0)
40+
;; SECONDARY-NEXT: (table.set $0
41+
;; SECONDARY-NEXT: (i32.const 0)
42+
;; SECONDARY-NEXT: (ref.null noextern)
43+
;; SECONDARY-NEXT: )
44+
;; SECONDARY-NEXT: )
45+
;; SECONDARY-NEXT: )

0 commit comments

Comments
 (0)