diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index c1a4d02c6d5..b089ec7f1f6 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -2525,7 +2525,7 @@ def handle(self, wasm): TrapsNeverHappen(), CtorEval(), Merge(), - # Split(), # https://github.com/WebAssembly/binaryen/issues/8510 + Split(), RoundtripText(), ClusterFuzz(), Two(), diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 21d82d013a4..f4ccaacbc96 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -26,28 +26,28 @@ // placeholder function (and eventually to the original secondary // function), allocating a new table slot for the placeholder if necessary. // -// 4. Replace all references to each secondary module's functions in the +// 4. Export globals, tags, tables, and memories from the primary module and +// import them in the secondary modules. If possible, move those module +// items instead to the secondary modules. +// +// 5. Replace all references to each secondary module's functions in the // primary module's and each other secondary module's table segments with // references to imported placeholder functions. // -// 5. Rewrite direct calls from primary functions to secondary functions to be +// 6. Rewrite direct calls from primary functions to secondary functions to be // indirect calls to their placeholder functions (and eventually to their // original secondary functions), allocating new table slots for the // placeholders if necessary. // -// 6. For each primary function directly called from a secondary function, +// 7. For each primary function directly called from a secondary function, // export the primary function if it is not already exported and import it // into each secondary module using it. // -// 7. For each secondary module, create new active table segments in the +// 8. For each secondary module, create new active table segments in the // module that will replace all the placeholder function references in the // table with references to their corresponding secondary functions upon // instantiation. // -// 8. Export globals, tags, tables, and memories from the primary module and -// import them in the secondary modules. If possible, move those module -// items instead to the secondary modules. -// // Functions can be used or referenced three ways in a WebAssembly module: they // can be exported, called, or referenced with ref.func. The above procedure // introduces a layer of indirection to each of those mechanisms that removes @@ -73,7 +73,6 @@ // from the IR before splitting. // #include "ir/module-splitting.h" -#include "ir/export-utils.h" #include "ir/find_all.h" #include "ir/module-utils.h" #include "ir/names.h" @@ -311,6 +310,10 @@ struct ModuleSplitter { // names. std::unordered_map exportedPrimaryFuncs; + // Map from to their corresponding export names for + // non-function items. + std::unordered_map, Name> exportedPrimaryItems; + // For each table, map placeholder indices to the names of the functions they // replace. std::unordered_map> placeholderMap; @@ -322,32 +325,39 @@ struct ModuleSplitter { static std::unique_ptr initSecondary(const Module& primary); static std::unordered_map initExportedPrimaryFuncs(const Module& primary); + static std::unordered_map, Name> + initExportedPrimaryItems(const Module& primary); // Other helpers void exportImportFunction(Name func, const std::set& modules); + void makeImportExport(Importable& primaryItem, + Importable& secondaryItem, + const std::string& genericExportName, + ExternalKind kind); Name getTrampoline(Name funcName); // Main splitting steps void classifyFunctions(); void moveSecondaryFunctions(); void thunkExportedSecondaryFunctions(); + void shareImportableItems(); void indirectReferencesToSecondaryFunctions(); void indirectCallsToSecondaryFunctions(); void exportImportCalledPrimaryFunctions(); void setupTablePatching(); - void shareImportableItems(); ModuleSplitter(Module& primary, const Config& config) : config(config), primary(primary), tableManager(primary), - exportedPrimaryFuncs(initExportedPrimaryFuncs(primary)) { + exportedPrimaryFuncs(initExportedPrimaryFuncs(primary)), + exportedPrimaryItems(initExportedPrimaryItems(primary)) { classifyFunctions(); moveSecondaryFunctions(); thunkExportedSecondaryFunctions(); + shareImportableItems(); indirectReferencesToSecondaryFunctions(); indirectCallsToSecondaryFunctions(); exportImportCalledPrimaryFunctions(); setupTablePatching(); - shareImportableItems(); } }; @@ -443,6 +453,41 @@ ModuleSplitter::initExportedPrimaryFuncs(const Module& primary) { return functionExportNames; } +std::unordered_map, Name> +ModuleSplitter::initExportedPrimaryItems(const Module& primary) { + std::unordered_map, Name> exports; + for (auto& ex : primary.exports) { + if (ex->kind != ExternalKind::Function) { + if (auto* name = ex->getInternalName()) { + exports[std::make_pair(ex->kind, *name)] = ex->name; + } + } + } + return exports; +} + +void ModuleSplitter::makeImportExport(Importable& primaryItem, + Importable& secondaryItem, + const std::string& genericExportName, + ExternalKind kind) { + secondaryItem.name = primaryItem.name; + secondaryItem.hasExplicitName = primaryItem.hasExplicitName; + secondaryItem.module = config.importNamespace; + auto exportIt = + exportedPrimaryItems.find(std::make_pair(kind, primaryItem.name)); + if (exportIt != exportedPrimaryItems.end()) { + secondaryItem.base = exportIt->second; + } else { + std::string baseName = + config.newExportPrefix + + (config.minimizeNewExportNames ? minified.getName() : genericExportName); + Name exportName = Names::getValidExportName(primary, baseName); + primary.addExport(new Export(exportName, kind, primaryItem.name)); + secondaryItem.base = exportName; + exportedPrimaryItems[std::make_pair(kind, primaryItem.name)] = exportName; + } +} + void ModuleSplitter::exportImportFunction(Name funcName, const std::set& modules) { Name exportName; @@ -508,7 +553,9 @@ Name ModuleSplitter::getTrampoline(Name funcName) { primary, std::string("trampoline_") + funcName.toString()); it->second = trampoline; - // Generate the call and the function. + // Generate the call and the function. We generate a direct call here, but + // this will be converted to a call_indirect in + // indirectCallsToSecondaryFunctions. std::vector args; for (Index i = 0; i < oldFunc->getNumParams(); i++) { args.push_back(builder.makeLocalGet(i, oldFunc->getLocalType(i))); @@ -559,39 +606,6 @@ static void walkSegments(Walker& walker, Module* module) { } void ModuleSplitter::shareImportableItems() { - // Map internal names to (one of) their corresponding export names. Don't - // consider functions because they have already been imported and exported as - // necessary. - std::unordered_map, Name> exports; - for (auto& ex : primary.exports) { - if (ex->kind != ExternalKind::Function) { - if (auto* name = ex->getInternalName()) { - exports[std::make_pair(ex->kind, *name)] = ex->name; - } - } - } - - auto makeImportExport = [&](Importable& primaryItem, - Importable& secondaryItem, - const std::string& genericExportName, - ExternalKind kind) { - secondaryItem.name = primaryItem.name; - secondaryItem.hasExplicitName = primaryItem.hasExplicitName; - secondaryItem.module = config.importNamespace; - auto exportIt = exports.find(std::make_pair(kind, primaryItem.name)); - if (exportIt != exports.end()) { - secondaryItem.base = exportIt->second; - } else { - std::string baseName = - config.newExportPrefix + (config.minimizeNewExportNames - ? minified.getName() - : genericExportName); - Name exportName = Names::getValidExportName(primary, baseName); - primary.addExport(new Export(exportName, kind, primaryItem.name)); - secondaryItem.base = exportName; - exports[std::make_pair(kind, primaryItem.name)] = exportName; - } - }; struct UsedNames { std::unordered_set globals; @@ -718,6 +732,15 @@ void ModuleSplitter::shareImportableItems() { secondaryUsed.push_back(getUsedNames(*secondaryPtr)); } + // We need to assume the active table and its base global are used in the + // primary module, because we will create segments there later + if (tableManager.activeTable) { + primaryUsed.tables.insert(tableManager.activeTable->name); + } + if (tableManager.activeBase.global.size()) { + primaryUsed.globals.insert(tableManager.activeBase.global); + } + // Compute the transitive closure of globals referenced in other globals' // initializers. Since globals can reference other globals, we must ensure // that if a global is used in a module, all its dependencies are also marked @@ -796,24 +819,12 @@ void ModuleSplitter::shareImportableItems() { if (!usedInPrimary && usingSecondaries.size() == 1) { auto* secondary = usingSecondaries[0]; - // In case we copied this table to this secondary module in - // setupTablePatching(), !usedInPrimary can't be satisfied, because the - // primary module should have an element segment that refers to this - // table. assert(!secondary->getTableOrNull(table->name)); ModuleUtils::copyTable(table.get(), *secondary); tablesToRemove.push_back(table->name); } else { for (auto* secondary : usingSecondaries) { - // 1. In case we copied this table to this secondary module in - // setupTablePatching(), secondary.getTableOrNull(table->name) is not - // null, and we need to import it. - // 2. As in the case with other module elements, if the table is used in - // the secondary module's instructions, we need to export it. - auto secondaryTable = secondary->getTableOrNull(table->name); - if (!secondaryTable) { - secondaryTable = ModuleUtils::copyTable(table.get(), *secondary); - } + auto* secondaryTable = ModuleUtils::copyTable(table.get(), *secondary); makeImportExport(*table, *secondaryTable, "table", ExternalKind::Table); } } @@ -841,50 +852,11 @@ void ModuleSplitter::shareImportableItems() { // global that will be moved to a secondary module, like // (global $unused i32 (global.get $a)) // $a is moved to a secondary globalsToRemove.push_back(global->name); - } else if (!inPrimary && usingSecondaries.size() == 1) { - // We are moving this global to this secondary module auto* secondary = usingSecondaries[0]; - auto* secondaryGlobal = ModuleUtils::copyGlobal(global.get(), *secondary); + ModuleUtils::copyGlobal(global.get(), *secondary); globalsToRemove.push_back(global->name); - - if (secondaryGlobal->init) { - // When a global's initializer contains ref.func - for (auto* ref : FindAll(secondaryGlobal->init).list) { - // If ref.func's function is in a different secondary module, we - // create a trampoline here. - if (auto targetIndexIt = funcToSecondaryIndex.find(ref->func); - targetIndexIt != funcToSecondaryIndex.end()) { - if (secondaries[targetIndexIt->second].get() != secondary) { - ref->func = getTrampoline(ref->func); - } - } - // 1. If ref.func's function is in the primary module, we export it - // here. - // 2. If ref.func's function is in a different secondary module and we - // just created a trampoline for it in the primary module above, we - // export the trampoline here. - if (primary.getFunctionOrNull(ref->func)) { - exportImportFunction(ref->func, {secondary}); - } - // If ref.func's function is in the same secondary module, we don't - // need to do anything. The ref.func can directly reference the - // function. - } - } - - } else { // We are NOT moving this global to the secondary module - if (global->init) { - for (auto* ref : FindAll(global->init).list) { - // If we are exporting this global from the primary module, we should - // create a trampoline here, because we skipped doing it for global - // initializers in indirectReferencesToSecondaryFunctions. - if (allSecondaryFuncs.contains(ref->func)) { - ref->func = getTrampoline(ref->func); - } - } - } - + } else { for (auto* secondary : usingSecondaries) { auto* secondaryGlobal = ModuleUtils::copyGlobal(global.get(), *secondary); @@ -946,25 +918,7 @@ void ModuleSplitter::indirectReferencesToSecondaryFunctions() { } } } gatherer(*this); - // We shouldn't use collector.walkModuleCode here, because we don't want to - // walk global initializers. At this point, all globals are still in the - // primary module, so if we walk global initializers here, it will create - // unnecessary trampolines. - // - // For example, we have (global $a funcref (ref.func $foo)), and $foo was - // split into a secondary module. Because $a is at this point still in the - // primary module, $foo will be considered to exist in a different module, so - // this will create a trampoline for $foo. But it is possible that later we - // find out $a is exclusively used by that secondary module and move $a there. - // In that case, $a can just reference $foo locally, but if we scan global - // initializers here, we would have created an unnecessary trampoline for - // $foo. - walkSegments(gatherer, &primary); - for (auto& curr : primary.functions) { - if (!curr->imported()) { - gatherer.walkFunction(curr.get()); - } - } + gatherer.walkModule(&primary); for (auto& secondaryPtr : secondaries) { gatherer.walkModule(secondaryPtr.get()); } @@ -1052,29 +1006,30 @@ void ModuleSplitter::indirectCallsToSecondaryFunctions() { void ModuleSplitter::exportImportCalledPrimaryFunctions() { // Find primary functions called/referred to from the secondary modules. using CalledPrimaryToModules = std::map>; + struct CallCollector : PostWalker { + const std::unordered_set& primaryFuncs; + CalledPrimaryToModules& calledPrimaryToModules; + CallCollector(const std::unordered_set& primaryFuncs, + CalledPrimaryToModules& calledPrimaryToModules) + : primaryFuncs(primaryFuncs), + calledPrimaryToModules(calledPrimaryToModules) {} + void visitCall(Call* curr) { + if (primaryFuncs.contains(curr->target)) { + calledPrimaryToModules[curr->target].insert(getModule()); + } + } + void visitRefFunc(RefFunc* curr) { + if (primaryFuncs.contains(curr->func)) { + calledPrimaryToModules[curr->func].insert(getModule()); + } + } + }; + for (auto& secondaryPtr : secondaries) { Module* secondary = secondaryPtr.get(); ModuleUtils::ParallelFunctionAnalysis callCollector( *secondary, [&](Function* func, CalledPrimaryToModules& calledPrimaryToModules) { - struct CallCollector : PostWalker { - const std::unordered_set& primaryFuncs; - CalledPrimaryToModules& calledPrimaryToModules; - CallCollector(const std::unordered_set& primaryFuncs, - CalledPrimaryToModules& calledPrimaryToModules) - : primaryFuncs(primaryFuncs), - calledPrimaryToModules(calledPrimaryToModules) {} - void visitCall(Call* curr) { - if (primaryFuncs.contains(curr->target)) { - calledPrimaryToModules[curr->target].insert(getModule()); - } - } - void visitRefFunc(RefFunc* curr) { - if (primaryFuncs.contains(curr->func)) { - calledPrimaryToModules[curr->func].insert(getModule()); - } - } - }; CallCollector(primaryFuncs, calledPrimaryToModules) .walkFunctionInModule(func, secondary); }); @@ -1084,6 +1039,9 @@ void ModuleSplitter::exportImportCalledPrimaryFunctions() { calledPrimaryToModules.merge(map); } + CallCollector collector(primaryFuncs, calledPrimaryToModules); + collector.walkModuleCode(secondary); + // Ensure each called primary function is exported and imported for (auto& [func, modules] : calledPrimaryToModules) { exportImportFunction(func, modules); @@ -1142,10 +1100,40 @@ void ModuleSplitter::setupTablePatching() { for (auto& [secondaryPtr, replacedElems] : moduleToReplacedElems) { Module& secondary = *secondaryPtr; + // Import and export the active table. This was not done previously in + // shareImportableItems because the active table was not being used by + // secondaries yet. auto secondaryTable = - ModuleUtils::copyTable(tableManager.activeTable, secondary); + secondary.getTableOrNull(tableManager.activeTable->name); + // If we use an existing table as the active table (e.g. because + // reference-types is disabled) and that table was already being used by an + // existing indirect call (not created by wasm-split), the active table may + // already exist, so we need to check. + if (!secondaryTable) { + secondaryTable = + ModuleUtils::copyTable(tableManager.activeTable, secondary); + makeImportExport(*tableManager.activeTable, + *secondaryTable, + "table", + ExternalKind::Table); + } if (tableManager.activeBase.global.size()) { + // Import and export the active table's base global. This was not done + // previously in shareImportableItems because the active table and its + // base global were not being used by secondaries yet. + auto* primaryGlobal = primary.getGlobal(tableManager.activeBase.global); + auto* secondaryGlobal = + secondary.getGlobalOrNull(tableManager.activeBase.global); + // If the base global is also used elsewhere in the secondary module, like + // in functions, this global may already have moved to the secondary, so + // we need to check. + if (!secondaryGlobal) { + secondaryGlobal = ModuleUtils::copyGlobal(primaryGlobal, secondary); + } + makeImportExport( + *primaryGlobal, *secondaryGlobal, "global", ExternalKind::Global); + assert(tableManager.activeTableSegments.size() == 1 && "Unexpected number of segments with non-const base"); assert(secondary.tables.size() == 1 && secondary.elementSegments.empty()); diff --git a/test/lit/wasm-split/active-table-base-global-used-elsewhere.wast b/test/lit/wasm-split/active-table-base-global-used-elsewhere.wast new file mode 100644 index 00000000000..6c68499e732 --- /dev/null +++ b/test/lit/wasm-split/active-table-base-global-used-elsewhere.wast @@ -0,0 +1,63 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; We need to disable reference-types to reuse the existing table as the active +;; table +;; RUN: wasm-split %s --disable-reference-types --split-funcs=split -g -o1 %t.1.wasm -o2 %t.2.wasm +;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY +;; RUN: wasm-dis %t.2.wasm | filecheck %s --check-prefix SECONDARY + +;; This tests the case when an existing table is used as the active table, and +;; the active table and its base global already has existing uses in the +;; secondary module. + +(module + ;; PRIMARY: (type $0 (func)) + ;; SECONDARY: (type $0 (func)) + (type $0 (func)) + (global $base (import "env" "base") i32) + ;; PRIMARY: (import "env" "base" (global $base i32)) + + ;; PRIMARY: (import "placeholder.deferred" "1" (func $placeholder_1)) + + ;; PRIMARY: (table $table 2 funcref) + (table $table 1 funcref) + (elem (global.get $base) $keep) + ;; PRIMARY: (elem $0 (global.get $base) $keep $placeholder_1) + + ;; PRIMARY: (export "table" (table $table)) + + ;; PRIMARY: (export "global" (global $base)) + + ;; PRIMARY: (export "keep" (func $keep)) + + ;; PRIMARY: (func $keep + ;; PRIMARY-NEXT: (call_indirect (type $0) + ;; PRIMARY-NEXT: (i32.add + ;; PRIMARY-NEXT: (global.get $base) + ;; PRIMARY-NEXT: (i32.const 1) + ;; PRIMARY-NEXT: ) + ;; PRIMARY-NEXT: ) + ;; PRIMARY-NEXT: ) + (func $keep + (call $split) + ) + ;; SECONDARY: (import "primary" "table" (table $table 1 funcref)) + + ;; SECONDARY: (import "primary" "global" (global $base i32)) + + ;; SECONDARY: (import "primary" "keep" (func $keep)) + + ;; SECONDARY: (elem $0 (global.get $base) $keep $split) + + ;; SECONDARY: (func $split + ;; SECONDARY-NEXT: (drop + ;; SECONDARY-NEXT: (global.get $base) + ;; SECONDARY-NEXT: ) + ;; SECONDARY-NEXT: (call_indirect (type $0) + ;; SECONDARY-NEXT: (i32.const 0) + ;; SECONDARY-NEXT: ) + ;; SECONDARY-NEXT: ) + (func $split + (drop (global.get $base)) + (call_indirect (type $0) (i32.const 0)) + ) +) diff --git a/test/lit/wasm-split/global-funcref.wast b/test/lit/wasm-split/global-funcref.wast deleted file mode 100644 index 3faf339546c..00000000000 --- a/test/lit/wasm-split/global-funcref.wast +++ /dev/null @@ -1,43 +0,0 @@ -;; RUN: wasm-split %s -all -g -o1 %t.1.wasm -o2 %t.2.wasm --keep-funcs=keep -;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY -;; RUN: wasm-dis %t.2.wasm | filecheck %s --check-prefix SECONDARY - -;; When a split global ($a here)'s initializer contains a ref.func of a split -;; function, we should NOT create any trampolines, and the split global should -;; direclty refer to the function. - -(module - (global $a funcref (ref.func $split)) - (global $b funcref (ref.func $keep)) - - ;; PRIMARY: (export "keep" (func $keep)) - - ;; PRIMARY-NOT: (export "trampoline_split" - ;; PRIMARY-NOT: (func $trampoline_split - - ;; SECONDARY: (import "primary" "keep" (func $keep (exact))) - - ;; SECONDARY: (global $a funcref (ref.func $split)) - ;; SECONDARY: (global $b funcref (ref.func $keep)) - - ;; PRIMARY: (func $keep - ;; PRIMARY-NEXT: ) - (func $keep) - - ;; SECONDARY: (func $split - ;; SECONDARY-NEXT: (drop - ;; SECONDARY-NEXT: (global.get $a) - ;; SECONDARY-NEXT: ) - ;; SECONDARY-NEXT: (drop - ;; SECONDARY-NEXT: (global.get $b) - ;; SECONDARY-NEXT: ) - ;; SECONDARY-NEXT: ) - (func $split - (drop - (global.get $a) - ) - (drop - (global.get $b) - ) - ) -) diff --git a/test/lit/wasm-split/global-reffunc.wast b/test/lit/wasm-split/global-reffunc.wast new file mode 100644 index 00000000000..a5afa465408 --- /dev/null +++ b/test/lit/wasm-split/global-reffunc.wast @@ -0,0 +1,45 @@ +;; RUN: wasm-split %s -all -g -o1 %t.1.wasm -o2 %t.2.wasm --keep-funcs=keep +;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY +;; RUN: wasm-dis %t.2.wasm | filecheck %s --check-prefix SECONDARY + +;; When a split global ($a here)'s initializer contains a ref.func of a split +;; function, we should NOT create any trampolines, and the split global should +;; direclty refer to the function. + +(module + (global $a funcref (ref.func $split)) + (global $b funcref (ref.func $keep)) + + (func $keep) + + (func $split + (drop + (global.get $a) + ) + (drop + (global.get $b) + ) + ) +) + +;; PRIMARY: (module +;; PRIMARY-NEXT: (type $0 (func)) +;; PRIMARY-NEXT: (export "keep" (func $keep)) +;; PRIMARY-NEXT: (func $keep +;; PRIMARY-NEXT: ) +;; PRIMARY-NEXT: ) + +;; SECONDARY: (module +;; SECONDARY-NEXT: (type $0 (func)) +;; SECONDARY-NEXT: (import "primary" "keep" (func $keep (exact))) +;; SECONDARY-NEXT: (global $a funcref (ref.func $split)) +;; SECONDARY-NEXT: (global $b funcref (ref.func $keep)) +;; SECONDARY-NEXT: (func $split +;; SECONDARY-NEXT: (drop +;; SECONDARY-NEXT: (global.get $a) +;; SECONDARY-NEXT: ) +;; SECONDARY-NEXT: (drop +;; SECONDARY-NEXT: (global.get $b) +;; SECONDARY-NEXT: ) +;; SECONDARY-NEXT: ) +;; SECONDARY-NEXT: ) diff --git a/test/lit/wasm-split/global-reffunc2.wast b/test/lit/wasm-split/global-reffunc2.wast new file mode 100644 index 00000000000..80ce2275b25 --- /dev/null +++ b/test/lit/wasm-split/global-reffunc2.wast @@ -0,0 +1,52 @@ +;; RUN: wasm-split %s -all -g -o1 %t.1.wasm -o2 %t.2.wasm --split-funcs=split1,split2 +;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY +;; RUN: wasm-dis %t.2.wasm | filecheck %s --check-prefix SECONDARY + +;; Global $g1 is used (exported) in the primary module so it can't move, and +;; global $g2 is only used in the secondary module so it will move there. + +(module + (global $g1 funcref (ref.func $split1)) + (global $g2 funcref (ref.func $split2)) + (export "g1" (global $g1)) + + (func $split1 + (unreachable) + ) + + (func $split2 + (drop + (global.get $g2) + ) + ) +) + +;; PRIMARY: (module +;; PRIMARY-NEXT: (type $0 (func)) +;; PRIMARY-NEXT: (import "placeholder.deferred" "0" (func $placeholder_0)) +;; PRIMARY-NEXT: (global $g1 funcref (ref.func $trampoline_split1)) +;; PRIMARY-NEXT: (table $0 1 funcref) +;; PRIMARY-NEXT: (elem $0 (i32.const 0) $placeholder_0) +;; PRIMARY-NEXT: (export "g1" (global $g1)) +;; PRIMARY-NEXT: (export "table" (table $0)) +;; PRIMARY-NEXT: (func $trampoline_split1 +;; 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: (global $g2 funcref (ref.func $split2)) +;; SECONDARY-NEXT: (elem $0 (i32.const 0) $split1) +;; SECONDARY-NEXT: (func $split1 +;; SECONDARY-NEXT: (unreachable) +;; SECONDARY-NEXT: ) +;; SECONDARY-NEXT: (func $split2 +;; SECONDARY-NEXT: (drop +;; SECONDARY-NEXT: (global.get $g2) +;; SECONDARY-NEXT: ) +;; SECONDARY-NEXT: ) +;; SECONDARY-NEXT: ) diff --git a/test/lit/wasm-split/ref.func.wast b/test/lit/wasm-split/ref.func.wast index 38eea498d72..5cc261dc4cd 100644 --- a/test/lit/wasm-split/ref.func.wast +++ b/test/lit/wasm-split/ref.func.wast @@ -73,7 +73,7 @@ ;; SECONDARY: (import "primary" "prime" (func $prime (exact (type $0)))) - ;; SECONDARY: (elem $0 (i32.const 0) $second-in-table $second) + ;; SECONDARY: (elem $0 (i32.const 0) $second $second-in-table) ;; SECONDARY: (elem declare func $prime) @@ -109,13 +109,13 @@ ;; (but we will get a placeholder, as all split-out functions do). ) ) -;; PRIMARY: (func $trampoline_second-in-table (type $0) +;; PRIMARY: (func $trampoline_second (type $0) ;; PRIMARY-NEXT: (call_indirect $1 (type $0) ;; PRIMARY-NEXT: (i32.const 0) ;; PRIMARY-NEXT: ) ;; PRIMARY-NEXT: ) -;; PRIMARY: (func $trampoline_second (type $0) +;; PRIMARY: (func $trampoline_second-in-table (type $0) ;; PRIMARY-NEXT: (call_indirect $1 (type $0) ;; PRIMARY-NEXT: (i32.const 1) ;; PRIMARY-NEXT: ) diff --git a/test/lit/wasm-split/split-module-items.wast b/test/lit/wasm-split/split-module-items.wast index 340fe27dac8..fdca4737a28 100644 --- a/test/lit/wasm-split/split-module-items.wast +++ b/test/lit/wasm-split/split-module-items.wast @@ -34,16 +34,16 @@ ;; PRIMARY: (tag $keep-tag (type $1) (param i32)) ;; PRIMARY-NEXT: (tag $shared-tag (type $1) (param i32)) - ;; PRIMARY: (export "keep" (func $keep)) - ;; PRIMARY-NEXT: (export "memory" (memory $shared-memory)) + ;; PRIMARY: (export "memory" (memory $shared-memory)) ;; PRIMARY-NEXT: (export "table" (table $shared-table)) - ;; PRIMARY-NEXT: (export "table_3" (table $2)) ;; PRIMARY-NEXT: (export "global" (global $shared-global)) ;; PRIMARY-NEXT: (export "tag" (tag $shared-tag)) + ;; PRIMARY-NEXT: (export "keep" (func $keep)) + ;; PRIMARY-NEXT: (export "table_5" (table $2)) ;; SECONDARY: (import "primary" "memory" (memory $shared-memory 1 1)) - ;; SECONDARY-NEXT: (import "primary" "table_3" (table $timport$0 1 funcref)) ;; SECONDARY-NEXT: (import "primary" "table" (table $shared-table 1 1 funcref)) + ;; SECONDARY-NEXT: (import "primary" "table_5" (table $timport$1 1 funcref)) ;; SECONDARY-NEXT: (import "primary" "global" (global $shared-global i32)) ;; SECONDARY-NEXT: (import "primary" "keep" (func $keep (exact (param i32) (result i32)))) ;; SECONDARY-NEXT: (import "primary" "tag" (tag $shared-tag (type $1) (param i32)))