Skip to content

[wasm-split] Fix table naming conflicts#8708

Open
aheejin wants to merge 2 commits into
WebAssembly:mainfrom
aheejin:wasm_split_fuzz_fix
Open

[wasm-split] Fix table naming conflicts#8708
aheejin wants to merge 2 commits into
WebAssembly:mainfrom
aheejin:wasm_split_fuzz_fix

Conversation

@aheejin
Copy link
Copy Markdown
Member

@aheejin aheejin commented May 15, 2026

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:

Table* TableSlotManager::makeTable() {
return module.addTable(
Builder::makeTable(Names::getValidTableName(module, Name::fromInt(0))));
}

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:

// Import and export the active table if necessary. Unless we use an
// existing table as an active table (e.g. because reference-types is
// disabled) and that table was already being used by an existing indirect
// call, shareImportableItems wasn't able to mark it as used in secondaries,
// so we should export and import the active table here.
auto secondaryTable =
secondary.getTableOrNull(tableManager.activeTable->name);
if (!secondaryTable) {
secondaryTable =
ModuleUtils::copyTable(tableManager.activeTable, secondary);
makeImportExport(*tableManager.activeTable,
*secondaryTable,
"table",
ExternalKind::Table);
}

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.

After WebAssembly#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.
@aheejin aheejin requested a review from tlively May 15, 2026 07:38
@aheejin aheejin requested a review from a team as a code owner May 15, 2026 07:38
;; PRIMARY: (module
;; PRIMARY-NEXT: (type $0 (func))
;; PRIMARY-NEXT: (import "placeholder.deferred" "0" (func $placeholder_0))
;; PRIMARY-NEXT: (table $0 1 funcref)
Copy link
Copy Markdown
Member Author

@aheejin aheejin May 15, 2026

Choose a reason for hiding this comment

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

Table names are not actually shown in the output because its hasExternalName is false. But without this patch this test crashes because it tries to reuse the existing externref table in the secondary module as the active table.

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.

Are you saying that $0 is not the internal name of the table? Why are we emitting a name at all, then?

Comment on lines +244 to +248
for (auto& secondary : secondaries) {
if (secondary->getTableOrNull(test)) {
return false;
}
}
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.

We don't want to iterate through all the secondaries multiple times in case it takes several iterations to find a good name. Let's iterate through them just once and create a set of all the table names we have to avoid.

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.

But we only create an active table once, no?

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.

Right. I mean that we should create this set just once at the beginning of makeTable. getValidName() can call the predicate an arbitrary number of times until it finds a valid name, so we want to avoid that being pathologically expensive.

;; PRIMARY: (module
;; PRIMARY-NEXT: (type $0 (func))
;; PRIMARY-NEXT: (import "placeholder.deferred" "0" (func $placeholder_0))
;; PRIMARY-NEXT: (table $0 1 funcref)
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.

It looks like the new table is still called $0?

Copy link
Copy Markdown
Member Author

@aheejin aheejin May 15, 2026

Choose a reason for hiding this comment

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

#8708 (comment)
We can set hasExternalName true depending on the debug information. Given that there will be only one active table, I'm not sure if it's worth it, but we can try, like even naming it "active_table" and make it shown in debug mode. (But this would be better as a follow-up, if we do it)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants