[wasm-split] Share active table with caller modules#8728
Merged
Conversation
The way we currently share the active table with secondary modules https://github.com/WebAssembly/binaryen/blob/2c2509b5bfe399df400b2185caa370f56e563d32/src/ir/module-splitting.cpp#L1122-L1156 has missed an important case in multi-split. This is one of the unexpected consequences of WebAssembly#8688. Here the `secondary` module in this loop is a secondary module whose functions have been put in the active table, mostly by `getTrampoline` function. When we have a call from `$A` to `$B` and `$B` is split, `getTrampoline` puts `$B` on the active table, which will be converted to a placeholder in `setupTablePatcing`. So the code above share the active table (and its base global, if exists) with `$B`'s module. The problem is we didn't share the active global with `$A`'s module. In a two-way split this is not a problem because `$A` is in the primary module. But in multi-split, `$A`'s module itself is another secondary module, and this module needs to access the active table because it should call the placeholder for `$B`. This does it in `indirectCallsToSecondaryFunctions`. --- I wish we can do the active table sharing for callee modules in `indirectCallsToSecondaryFunctions` too and get done with it, but we still need to share it in `shareImportableItems` for secondary modules that have functions to be replaced in the active table, because there is a case of existing secondary function names in the active table. See `KEEP-NONE` RUN line in the test below: https://github.com/WebAssembly/binaryen/blob/main/test/lit/wasm-split/basic.wast In this test, we reuse the existing table as the active table, which already has `$foo` in its element section. And that `foo` will be split. Because both `bar` and `foo` are split there is no cross-module call between them to process in `indirectCallsToSecondaryFunctions`. This won't be converted to trampolines because https://github.com/WebAssembly/binaryen/blob/5fcc1af12ae19032339ee5e56889e08b6912e8cb/src/ir/module-splitting.cpp#L947-L958 and because there is no trampoline, this doesn't get to have a call instruction from primary->secondary, but we still need to share the active table with the secondary module, and we can't handle it in `indirectCallsToSecondaryFunctions` --- This fixes the error discussed in WebAssembly#8711 (comment).
aheejin
commented
May 19, 2026
| return trampoline; | ||
| } | ||
|
|
||
| void ModuleSplitter::shareActiveTable(Module* secondary) { |
Member
Author
There was a problem hiding this comment.
This was just extracted from the existing code in setupTablePatching.
tlively
approved these changes
May 19, 2026
| void ModuleSplitter::indirectCallsToSecondaryFunctions() { | ||
| // Update direct calls of secondary functions to be indirect calls of their | ||
| // corresponding table indices instead. | ||
| std::set<Module*> activeTableUsingSecondaries; |
Member
There was a problem hiding this comment.
Can we use an unordered set here?
Member
Author
There was a problem hiding this comment.
Hmm, yeah I think we can. I used std::set thinking we had to iterate on it below and the order mattered, but because we are iterating on modules and adding one export per module, the order won't really matter. Done.
| struct CallIndirector : public PostWalker<CallIndirector> { | ||
| ModuleSplitter& parent; | ||
| CallIndirector(ModuleSplitter& parent) : parent(parent) {} | ||
| std::set<Module*>& activeTableUsingSecondaries; |
Member
There was a problem hiding this comment.
It is slightly less code if CallIndirector owns activeTableUsingSecondaries. The downside is that have to access it with callIndirector.activeTableUsingSecondaries below. Historically I've more commonly used the pattern you have here though, so either way is fine with me.
tlively
approved these changes
May 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The way we currently share the active table with secondary modules
binaryen/src/ir/module-splitting.cpp
Lines 1122 to 1156 in 2c2509b
Here the
secondarymodule in this loop is a secondary module whose functions have been put in the active table, mostly bygetTrampolinefunction. When we have a call from$Ato$Band$Bis split,getTrampolineputs$Bon the active table, which will be converted to a placeholder insetupTablePatcing. So the code above share the active table (and its base global, if exists) with$B's module.The problem is we didn't share the active global with
$A's module. In a two-way split this is not a problem because$Ais in the primary module. But in multi-split,$A's module itself is another secondary module, and this module needs to access the active table because it should call the placeholder for$B. This does it inindirectCallsToSecondaryFunctions.I wish we can do the active table sharing for callee modules in
indirectCallsToSecondaryFunctionstoo and get done with it, but we still need to share it inshareImportableItemsfor secondary modules that have functions to be replaced in the active table, because there is a case of existing secondary function names in the active table. SeeKEEP-NONERUN line in the test below:https://github.com/WebAssembly/binaryen/blob/main/test/lit/wasm-split/basic.wast
In this test, we reuse the existing table as the active table, which already has
$fooin its element section. And thatfoowill be split. Because bothbarandfooare split there is no cross-module call between them to process inindirectCallsToSecondaryFunctions. This won't be converted to trampolines becausebinaryen/src/ir/module-splitting.cpp
Lines 947 to 958 in 5fcc1af
indirectCallsToSecondaryFunctions.This fixes the error discussed in #8711 (comment).