Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/ir/module-splitting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,9 @@ void ModuleSplitter::setupTablePatching() {
assert(table == tableManager.activeTable->name);

placeholderMap[table][index] = ref->func;
Module& secondary = *secondaries.at(funcToSecondaryIndex.at(ref->func));
Index secondaryIndex = funcToSecondaryIndex.at(ref->func);
Module& secondary = *secondaries.at(secondaryIndex);
Name secondaryName = config.secondaryNames.at(secondaryIndex);
auto* secondaryFunc = secondary.getFunction(ref->func);
moduleToReplacedElems[&secondary][index] = secondaryFunc;
if (!config.usePlaceholders) {
Expand All @@ -782,7 +784,8 @@ void ModuleSplitter::setupTablePatching() {
return;
}
auto placeholder = std::make_unique<Function>();
placeholder->module = config.placeholderNamespace;
placeholder->module = config.placeholderNamespacePrefix.toString() + "." +
secondaryName.toString();
placeholder->base = std::to_string(index);
placeholder->name = Names::getValidFunctionName(
primary, std::string("placeholder_") + placeholder->base.toString());
Expand Down
8 changes: 5 additions & 3 deletions src/ir/module-splitting.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,18 @@ struct Config {
// may not include imported functions, which are always kept in the primary
// module regardless.
std::vector<std::set<Name>> secondaryFuncs;
// A vector of names of the secondary modules.
std::vector<Name> secondaryNames;
// Whether to import placeholder functions into the primary module that will
// be called when a secondary function is called before the secondary module
// has been loaded.
bool usePlaceholders = true;
// The namespace from which to import primary functions into the secondary
// module.
Name importNamespace = "primary";
// The namespace from which to import placeholder functions into the primary
// module. Ignored if `usePlaceholders` is false.
Name placeholderNamespace = "placeholder";
// The prefix of the namespaces from which to import placeholder functions
// into the primary module. Ignored if `usePlaceholders` is false.
Name placeholderNamespacePrefix = "placeholder";
// The prefix to attach to the name of any newly created exports. This can be
// used to differentiate between "real" exports of the module and exports that
// should only be consumed by the secondary module.
Expand Down
16 changes: 13 additions & 3 deletions src/tools/wasm-split/split-options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,15 +236,25 @@ WasmSplitOptions::WasmSplitOptions()
[&](Options* o, const std::string& argument) {
importNamespace = argument;
})
.add("--placeholder-namespace-prefix",
"",
"The prefix for the namespaces from which to import placeholder "
"functions into the primary module. The namespaces will be "
"concatenations of the prefix and the module name.",
WasmSplitOption,
{Mode::Split, Mode::MultiSplit},
Options::Arguments::One,
[&](Options* o, const std::string& argument) {
placeholderNamespacePrefix = argument;
})
.add("--placeholder-namespace",
"",
"The namespace from which to import placeholder functions into "
"the primary module.",
"The same as --placeholder-namespace-prefix.",
WasmSplitOption,
{Mode::Split, Mode::MultiSplit},
Options::Arguments::One,
[&](Options* o, const std::string& argument) {
placeholderNamespace = argument;
placeholderNamespacePrefix = argument;
})
.add("--jspi",
"",
Expand Down
2 changes: 1 addition & 1 deletion src/tools/wasm-split/split-options.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ struct WasmSplitOptions : ToolOptions {
std::string secondaryOutput;

std::string importNamespace;
std::string placeholderNamespace;
std::string placeholderNamespacePrefix;
std::string secondaryMemoryName;
std::string exportPrefix;

Expand Down
14 changes: 7 additions & 7 deletions src/tools/wasm-split/wasm-split.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ void setCommonSplitConfigs(ModuleSplitting::Config& config,
if (options.exportPrefix.size()) {
config.newExportPrefix = options.exportPrefix;
}
if (options.placeholderNamespace.size()) {
config.placeholderNamespace = options.placeholderNamespace;
if (options.placeholderNamespacePrefix.size()) {
config.placeholderNamespacePrefix = options.placeholderNamespacePrefix;
}
}

Expand Down Expand Up @@ -346,6 +346,7 @@ void splitModule(const WasmSplitOptions& options) {
ModuleSplitting::Config config;
setCommonSplitConfigs(config, options);
config.secondaryFuncs.push_back(std::move(splitFuncs));
config.secondaryNames.push_back("deferred");
Copy link
Member

Choose a reason for hiding this comment

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

Is the plan to remove this eventually?

Copy link
Member Author

@aheejin aheejin Oct 16, 2025

Choose a reason for hiding this comment

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

No, this is to keep the current emscripten behavior, that the secondary module is split with the name [moduleName].deferred.wasm. So in emscripten-core/emscripten#25571, it has

      let secondaryFile;
      if (moduleName == 'placeholder') { // old format
        secondaryFile = wasmBinaryFile.slice(0, -5) + '.deferred.wasm';
      } else { // new format
        let moduleID = moduleName.split('.')[1];
        secondaryFile = wasmBinaryFile.slice(0, -5) + '.' + moduleID + '.wasm';
}

The plan is to remove this old format part:

      if (moduleName == 'placeholder') { // old format
        secondaryFile = wasmBinaryFile.slice(0, -5) + '.deferred.wasm';
      }

And only do with this part:

        let moduleID = moduleName.split('.')[1];
        secondaryFile = wasmBinaryFile.slice(0, -5) + '.' + moduleID + '.wasm';

And with this, the placeholder has to be named placeholder.deferred to keep this behavior.

Copy link
Member

Choose a reason for hiding this comment

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

I would be happy to make breaking changes to the existing split workflow (e.g. the name of the secondary module) to minimize differences with the multi-split workflow, but this sounds good for now.

Copy link
Member Author

Choose a reason for hiding this comment

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

It is still hardcoding and users should make sure their -o2 option in wasm-split to be [moduleName].deferred.wasm, but this is the case now anyway, so it doesn't change from the user's perspective.

config.jspi = options.jspi;
auto splitResults = ModuleSplitting::splitFunctions(wasm, config);
auto& secondary = *splitResults.secondaries.begin();
Expand Down Expand Up @@ -407,7 +408,6 @@ void multiSplitModule(const WasmSplitOptions& options) {

std::string line;
bool newSection = true;
std::vector<Name> moduleNames;
std::unordered_set<Name> moduleNameSet;
while (std::getline(manifest, line)) {
if (line.empty()) {
Expand All @@ -424,7 +424,7 @@ void multiSplitModule(const WasmSplitOptions& options) {
}
currModule = name;
moduleNameSet.insert(currModule);
moduleNames.push_back(currModule);
config.secondaryNames.push_back(currModule);
config.secondaryFuncs.emplace_back(std::set<Name>());
currFuncs = &config.secondaryFuncs.back();
newSection = false;
Expand All @@ -448,10 +448,10 @@ void multiSplitModule(const WasmSplitOptions& options) {
}

auto splitResults = ModuleSplitting::splitFunctions(wasm, config);
assert(moduleNames.size() == splitResults.secondaries.size());
for (Index i = 0, n = moduleNames.size(); i < n; i++) {
assert(config.secondaryNames.size() == splitResults.secondaries.size());
for (Index i = 0, n = config.secondaryNames.size(); i < n; i++) {
auto& secondary = *splitResults.secondaries[i];
auto moduleName = options.outPrefix + moduleNames[i].toString() +
auto moduleName = options.outPrefix + config.secondaryNames[i].toString() +
(options.emitBinary ? ".wasm" : ".wast");
if (options.symbolMap) {
writeSymbolMap(secondary, moduleName + ".symbols");
Expand Down
2 changes: 2 additions & 0 deletions test/example/module-splitting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ void do_test(const std::set<Name>& keptFuncs, std::string&& module) {

ModuleSplitting::Config config;
config.secondaryFuncs.push_back(std::move(splitFuncs));
config.secondaryNames.push_back("deferred");
config.newExportPrefix = "%";
auto results = splitFunctions(*primary, config);
auto& secondary = *results.secondaries.begin();
Expand Down Expand Up @@ -477,6 +478,7 @@ void test_minimized_exports() {

ModuleSplitting::Config config;
config.secondaryFuncs.push_back({"call"});
config.secondaryNames.push_back("deferred");
config.newExportPrefix = "%";
config.minimizeNewExportNames = true;

Expand Down
44 changes: 22 additions & 22 deletions test/example/module-splitting.txt
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ Keeping: <none>
After:
(module
(type $0 (func (param i32) (result i32)))
(import "placeholder" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(table $0 1 funcref)
(elem $0 (i32.const 0) $placeholder_0)
(export "foo" (func $trampoline_foo))
Expand Down Expand Up @@ -369,7 +369,7 @@ Keeping: <none>
After:
(module
(type $0 (func (param i32) (result i32)))
(import "placeholder" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(table $table 1 funcref)
(table $0 1 funcref)
(elem $0 (table $table) (i32.const 0) func $trampoline_foo)
Expand Down Expand Up @@ -407,7 +407,7 @@ Keeping: <none>
After:
(module
(type $0 (func (param i32) (result i32)))
(import "placeholder" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(table $table 2 funcref)
(table $0 1 funcref)
(elem $0 (table $table) (i32.const 0) func $trampoline_foo $trampoline_foo)
Expand Down Expand Up @@ -446,7 +446,7 @@ Keeping: <none>
After:
(module
(type $0 (func (param i32) (result i32)))
(import "placeholder" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(table $table 1000 funcref)
(table $0 1 funcref)
(elem $0 (table $table) (i32.const 42) func $trampoline_foo)
Expand Down Expand Up @@ -488,7 +488,7 @@ After:
(module
(type $0 (func (param i32) (result i32)))
(import "env" "base" (global $base i32))
(import "placeholder" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(table $table 1000 funcref)
(table $0 1 funcref)
(elem $0 (table $table) (global.get $base) func $trampoline_foo)
Expand Down Expand Up @@ -531,7 +531,7 @@ After:
(module
(type $0 (func (param i32) (result i32)))
(import "env" "base" (global $base i32))
(import "placeholder" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(table $table 1000 funcref)
(table $0 1 funcref)
(elem $0 (table $table) (global.get $base) func $trampoline_foo $trampoline_foo)
Expand Down Expand Up @@ -578,7 +578,7 @@ After:
(type $0 (func))
(type $1 (func (param i32) (result i32)))
(import "env" "base" (global $base i32))
(import "placeholder" "0" (func $placeholder_0 (type $1) (param i32) (result i32)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $1) (param i32) (result i32)))
(table $table 1000 funcref)
(table $0 1 funcref)
(elem $0 (table $table) (global.get $base) func $null $trampoline_foo)
Expand Down Expand Up @@ -671,7 +671,7 @@ Keeping: foo
After:
(module
(type $0 (func))
(import "placeholder" "0" (func $placeholder_0 (type $0)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0)))
(table $0 1 funcref)
(elem $0 (i32.const 0) $placeholder_0)
(export "%table" (table $0))
Expand Down Expand Up @@ -726,7 +726,7 @@ Keeping: foo
After:
(module
(type $0 (func))
(import "placeholder" "0" (func $placeholder_0 (type $0)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0)))
(table $0 1 funcref)
(elem $0 (i32.const 0) $placeholder_0)
(export "%foo" (func $trampoline_bar))
Expand Down Expand Up @@ -775,8 +775,8 @@ Keeping: bar, quux
After:
(module
(type $0 (func))
(import "placeholder" "0" (func $placeholder_0 (type $0)))
(import "placeholder" "1" (func $placeholder_1 (type $0)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0)))
(import "placeholder.deferred" "1" (func $placeholder_1 (type $0)))
(table $table 4 funcref)
(table $0 2 funcref)
(elem $0 (table $table) (i32.const 0) func $trampoline_foo $bar $trampoline_baz $quux)
Expand Down Expand Up @@ -838,8 +838,8 @@ After:
(module
(type $0 (func))
(import "env" "base" (global $base i32))
(import "placeholder" "0" (func $placeholder_0 (type $0)))
(import "placeholder" "1" (func $placeholder_1 (type $0)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0)))
(import "placeholder.deferred" "1" (func $placeholder_1 (type $0)))
(table $table 4 funcref)
(table $0 2 funcref)
(elem $0 (table $table) (global.get $base) func $trampoline_foo $bar $trampoline_baz $quux)
Expand Down Expand Up @@ -900,9 +900,9 @@ Keeping: baz
After:
(module
(type $0 (func))
(import "placeholder" "0" (func $placeholder_0 (type $0)))
(import "placeholder" "1" (func $placeholder_1 (type $0)))
(import "placeholder" "2" (func $placeholder_2 (type $0)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0)))
(import "placeholder.deferred" "1" (func $placeholder_1 (type $0)))
(import "placeholder.deferred" "2" (func $placeholder_2 (type $0)))
(table $table 4 funcref)
(table $0 3 funcref)
(elem $0 (table $table) (i32.const 0) func $trampoline_foo $trampoline_bar $baz $trampoline_quux)
Expand Down Expand Up @@ -969,9 +969,9 @@ After:
(module
(type $0 (func))
(import "env" "base" (global $base i32))
(import "placeholder" "0" (func $placeholder_0 (type $0)))
(import "placeholder" "1" (func $placeholder_1 (type $0)))
(import "placeholder" "2" (func $placeholder_2 (type $0)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0)))
(import "placeholder.deferred" "1" (func $placeholder_1 (type $0)))
(import "placeholder.deferred" "2" (func $placeholder_2 (type $0)))
(table $table 4 funcref)
(table $0 3 funcref)
(elem $0 (table $table) (global.get $base) func $trampoline_foo $trampoline_bar $baz $trampoline_quux)
Expand Down Expand Up @@ -1033,7 +1033,7 @@ After:
(module
(type $0 (func))
(import "env" "base" (global $base i32))
(import "placeholder" "0" (func $placeholder_0 (type $0)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0)))
(table $table 2 funcref)
(table $0 1 funcref)
(elem $0 (table $table) (global.get $base) func $foo $trampoline_bar)
Expand Down Expand Up @@ -1083,7 +1083,7 @@ Keeping: foo
After:
(module
(type $0 (func (param i32) (result i32)))
(import "placeholder" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0) (param i32) (result i32)))
(table $table 1 1 funcref)
(table $0 1 funcref)
(elem $0 (table $table) (i32.const 0) func $foo)
Expand Down Expand Up @@ -1124,7 +1124,7 @@ Keeping: <none>
After:
(module
(type $0 (func))
(import "placeholder" "0" (func $placeholder_0 (type $0)))
(import "placeholder.deferred" "0" (func $placeholder_0 (type $0)))
(table $0 1 funcref)
(elem $0 (i32.const 0) $placeholder_0)
(export "foo1" (func $trampoline_foo))
Expand Down
12 changes: 9 additions & 3 deletions test/lit/help/wasm-split.test
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,15 @@
;; CHECK-NEXT: mode, refers to the namespace from which
;; CHECK-NEXT: to import the secondary memory, if any.
;; CHECK-NEXT:
;; CHECK-NEXT: --placeholder-namespace [split, multi-split] The namespace from
;; CHECK-NEXT: which to import placeholder functions
;; CHECK-NEXT: into the primary module.
;; CHECK-NEXT: --placeholder-namespace-prefix [split, multi-split] The prefix for the
;; CHECK-NEXT: namespaces from which to import
;; CHECK-NEXT: placeholder functions into the primary
;; CHECK-NEXT: module. The namespaces will be
;; CHECK-NEXT: concatenations of the prefix and the
;; CHECK-NEXT: module name.
;; CHECK-NEXT:
;; CHECK-NEXT: --placeholder-namespace [split, multi-split] The same as
;; CHECK-NEXT: --placeholder-namespace-prefix.
;; CHECK-NEXT:
;; CHECK-NEXT: --jspi [split] Transform the module to support
;; CHECK-NEXT: asynchronously loading the secondary
Expand Down
6 changes: 3 additions & 3 deletions test/lit/wasm-split/basic.wast
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@

;; KEEP-NONE-PRIMARY: (module
;; KEEP-NONE-PRIMARY-NEXT: (type $0 (func (param i32) (result i32)))
;; KEEP-NONE-PRIMARY-NEXT: (import "placeholder" "0" (func $placeholder_0 (param i32) (result i32)))
;; KEEP-NONE-PRIMARY-NEXT: (import "placeholder.deferred" "0" (func $placeholder_0 (param i32) (result i32)))
;; KEEP-NONE-PRIMARY-NEXT: (table $table 1 1 funcref)
;; KEEP-NONE-PRIMARY-NEXT: (elem $0 (i32.const 0) $placeholder_0)
;; KEEP-NONE-PRIMARY-NEXT: (export "%table" (table $table))
Expand All @@ -97,7 +97,7 @@

;; KEEP-FOO-PRIMARY: (module
;; KEEP-FOO-PRIMARY-NEXT: (type $0 (func (param i32) (result i32)))
;; KEEP-FOO-PRIMARY-NEXT: (import "placeholder" "1" (func $placeholder_1 (param i32) (result i32)))
;; KEEP-FOO-PRIMARY-NEXT: (import "placeholder.deferred" "1" (func $placeholder_1 (param i32) (result i32)))
;; KEEP-FOO-PRIMARY-NEXT: (table $table 2 2 funcref)
;; KEEP-FOO-PRIMARY-NEXT: (elem $0 (i32.const 0) $foo $placeholder_1)
;; KEEP-FOO-PRIMARY-NEXT: (export "%foo" (func $foo))
Expand Down Expand Up @@ -127,7 +127,7 @@

;; KEEP-BAR-PRIMARY: (module
;; KEEP-BAR-PRIMARY-NEXT: (type $0 (func (param i32) (result i32)))
;; KEEP-BAR-PRIMARY-NEXT: (import "placeholder" "0" (func $placeholder_0 (param i32) (result i32)))
;; KEEP-BAR-PRIMARY-NEXT: (import "placeholder.deferred" "0" (func $placeholder_0 (param i32) (result i32)))
;; KEEP-BAR-PRIMARY-NEXT: (table $table 1 1 funcref)
;; KEEP-BAR-PRIMARY-NEXT: (elem $0 (i32.const 0) $placeholder_0)
;; KEEP-BAR-PRIMARY-NEXT: (export "%bar" (func $bar))
Expand Down
2 changes: 1 addition & 1 deletion test/lit/wasm-split/jspi-secondary-export.wast
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

;; PRIMARY: (import "env" "__load_secondary_module" (func $fimport$0))

;; PRIMARY: (import "placeholder" "0" (func $placeholder_0 (param i32) (result i32)))
;; PRIMARY: (import "placeholder.deferred" "0" (func $placeholder_0 (param i32) (result i32)))

;; PRIMARY: (global $global$0 (mut i32) (i32.const 0))

Expand Down
2 changes: 1 addition & 1 deletion test/lit/wasm-split/jspi.wast
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

;; PRIMARY: (import "env" "__load_secondary_module" (func $fimport$0))

;; PRIMARY: (import "placeholder" "0" (func $placeholder_0 (param i32) (result i32)))
;; PRIMARY: (import "placeholder.deferred" "0" (func $placeholder_0 (param i32) (result i32)))

;; PRIMARY: (global $global$0 (mut i32) (i32.const 0))

Expand Down
6 changes: 3 additions & 3 deletions test/lit/wasm-split/multi-split-escape-names.wast
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,11 @@
(f32.const 0)
)
)
;; PRIMARY: (import "placeholder" "0" (func $placeholder_0 (result i64)))
;; PRIMARY: (import "placeholder.2" "0" (func $placeholder_0 (result i64)))

;; PRIMARY: (import "placeholder" "1" (func $placeholder_1 (result f32)))
;; PRIMARY: (import "placeholder.3" "1" (func $placeholder_1 (result f32)))

;; PRIMARY: (import "placeholder" "2" (func $placeholder_2 (result i32)))
;; PRIMARY: (import "placeholder.1" "2" (func $placeholder_2 (result i32)))

;; PRIMARY: (table $0 3 funcref)

Expand Down
Loading
Loading