diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 422b18b94ea..b557e774855 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -77,7 +77,6 @@ #include "ir/find_all.h" #include "ir/module-utils.h" #include "ir/names.h" -#include "support/insert_ordered.h" #include "support/unique_deferring_queue.h" #include "wasm-builder.h" #include "wasm.h" @@ -755,13 +754,6 @@ ModuleSplitter::PrimarySecondaryUsedNames ModuleSplitter::computeUsedNames() { } } - for (auto name : used.tables) { - if (auto* table = primary.getTableOrNull(name)) { - if (table->init) { - collector.walk(table->init); - } - } - } return used; }; @@ -821,32 +813,69 @@ ModuleSplitter::PrimarySecondaryUsedNames ModuleSplitter::computeUsedNames() { } } - // 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 - // as used. - auto computeTransitiveGlobals = [&](UsedNames& used) { - UniqueNonrepeatingDeferredQueue worklist; - for (auto global : used.globals) { - worklist.push(global); + // Given a name and a module item kind (field pointer), find which module + // "owns" it. If it is used by exactly one secondary module, that secondary + // module is the owner. If it is used by the primary module or multiple + // secondary modules, the primary module is the owner. If it is not used, + // returns nullptr. + auto getOwner = [&](Name name, auto UsedNames::* field) -> UsedNames* { + UsedNames* owner = nullptr; + if ((primaryUsed.*field).contains(name)) { + owner = &primaryUsed; } - while (!worklist.empty()) { - Name name = worklist.pop(); - // At this point all globals are still in the primary module, so this - // exists - auto* global = primary.getGlobal(name); - if (!global->imported() && global->init) { - for (auto* get : FindAll(global->init).list) { - worklist.push(get->name); - used.globals.insert(get->name); + for (auto& sec : secondaryUsed) { + if ((sec.*field).contains(name)) { + if (owner) { + owner = &primaryUsed; + break; } + owner = &sec; } } + return owner; }; - computeTransitiveGlobals(primaryUsed); - for (auto& used : secondaryUsed) { - computeTransitiveGlobals(used); + // Scan table initializers into their owning modules. If a table is used by a + // single secondary module, its initializer dependencies are marked as "used" + // in that secondary module. Otherwise, they are marked as used in the primary + // module. + if (primary.features.hasGC()) { + for (auto& table : primary.tables) { + if (!table->init) { + continue; + } + if (UsedNames* owner = getOwner(table->name, &UsedNames::tables)) { + NameCollector(*owner).walk(table->init); + } + } + } + + // Compute the transitive closure of globals referenced in other globals' + // initializers. A global's initializer is evaluated by the module that + // defines it. + UniqueDeferredQueue worklist; + for (auto name : primaryUsed.globals) { + worklist.push(name); + } + for (auto& sec : secondaryUsed) { + for (auto name : sec.globals) { + worklist.push(name); + } + } + + while (!worklist.empty()) { + Name name = worklist.pop(); + auto* global = primary.getGlobal(name); + if (!global->init) { + continue; + } + if (UsedNames* owner = getOwner(name, &UsedNames::globals)) { + for (auto* get : FindAll(global->init).list) { + if (owner->globals.insert(get->name).second) { + worklist.push(get->name); + } + } + } } return std::make_pair(primaryUsed, secondaryUsed); diff --git a/test/lit/wasm-split/table-init-deps.wast b/test/lit/wasm-split/table-init-deps.wast new file mode 100644 index 00000000000..7bd40a65a1b --- /dev/null +++ b/test/lit/wasm-split/table-init-deps.wast @@ -0,0 +1,53 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; 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 + +(module + ;; PRIMARY: (type $0 (func)) + + ;; PRIMARY: (import "env" "g" (global $g funcref)) + (import "env" "g" (global $g funcref)) + + ;; A table used by both $keep and $split. + ;; The table stays in PRIMARY. $g should stay in PRIMARY and NOT be + ;; exported/imported. + ;; PRIMARY: (table $t 1 1 funcref (global.get $g)) + (table $t 1 1 funcref (global.get $g)) + + ;; PRIMARY: (export "table" (table $t)) + + ;; PRIMARY: (func $keep + ;; PRIMARY-NEXT: (drop + ;; PRIMARY-NEXT: (table.get $t + ;; PRIMARY-NEXT: (i32.const 0) + ;; PRIMARY-NEXT: ) + ;; PRIMARY-NEXT: ) + ;; PRIMARY-NEXT: ) + (func $keep + (drop + (table.get $t + (i32.const 0) + ) + ) + ) + + ;; SECONDARY: (type $0 (func)) + + ;; SECONDARY: (import "primary" "table" (table $t 1 1 funcref)) + + ;; SECONDARY: (func $split + ;; SECONDARY-NEXT: (drop + ;; SECONDARY-NEXT: (table.get $t + ;; SECONDARY-NEXT: (i32.const 0) + ;; SECONDARY-NEXT: ) + ;; SECONDARY-NEXT: ) + ;; SECONDARY-NEXT: ) + (func $split + (drop + (table.get $t + (i32.const 0) + ) + ) + ) +) diff --git a/test/lit/wasm-split/transitive-globals-multi.wast b/test/lit/wasm-split/transitive-globals-multi.wast new file mode 100644 index 00000000000..f9eda9a7d59 --- /dev/null +++ b/test/lit/wasm-split/transitive-globals-multi.wast @@ -0,0 +1,52 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; RUN: wasm-split -all -g --multi-split %s --manifest %s.manifest --out-prefix=%t -o %t.wasm +;; RUN: wasm-dis %t.wasm | filecheck %s --check-prefix PRIMARY +;; RUN: wasm-dis %t1.wasm | filecheck %s --check-prefix SECONDARY1 +;; RUN: wasm-dis %t2.wasm | filecheck %s --check-prefix SECONDARY2 + +;; Because global $e is used in both module1 ($split1) and module2 ($split2), $e +;; will be exported / imported, but we don't need to export $f. + +(module + ;; PRIMARY: (type $0 (func)) + + ;; PRIMARY: (global $f i32 (i32.const 42)) + (global $f i32 (i32.const 42)) + ;; PRIMARY: (global $e i32 (global.get $f)) + (global $e i32 (global.get $f)) + + ;; PRIMARY: (export "global" (global $e)) + + ;; PRIMARY: (func $keep + ;; PRIMARY-NEXT: (nop) + ;; PRIMARY-NEXT: ) + (func $keep + (nop) + ) + + ;; SECONDARY1: (type $0 (func)) + + ;; SECONDARY1: (import "primary" "global" (global $e i32)) + + ;; SECONDARY1: (func $split1 + ;; SECONDARY1-NEXT: (drop + ;; SECONDARY1-NEXT: (global.get $e) + ;; SECONDARY1-NEXT: ) + ;; SECONDARY1-NEXT: ) + (func $split1 + (drop (global.get $e)) + ) + + ;; SECONDARY2: (type $0 (func)) + + ;; SECONDARY2: (import "primary" "global" (global $e i32)) + + ;; SECONDARY2: (func $split2 + ;; SECONDARY2-NEXT: (drop + ;; SECONDARY2-NEXT: (global.get $e) + ;; SECONDARY2-NEXT: ) + ;; SECONDARY2-NEXT: ) + (func $split2 + (drop (global.get $e)) + ) +) diff --git a/test/lit/wasm-split/transitive-globals-multi.wast.manifest b/test/lit/wasm-split/transitive-globals-multi.wast.manifest new file mode 100644 index 00000000000..56731c0a1f9 --- /dev/null +++ b/test/lit/wasm-split/transitive-globals-multi.wast.manifest @@ -0,0 +1,5 @@ +1: +split1 + +2: +split2 diff --git a/test/lit/wasm-split/transitive-globals.wast b/test/lit/wasm-split/transitive-globals.wast index a0bac6c3364..a277ec72605 100644 --- a/test/lit/wasm-split/transitive-globals.wast +++ b/test/lit/wasm-split/transitive-globals.wast @@ -22,11 +22,9 @@ ;; PRIMARY: (global $f i32 (i32.const 42)) ;; PRIMARY: (global $e i32 (global.get $f)) - ;; PRIMARY: (export "global" (global $f)) - ;; PRIMARY: (export "global_1" (global $e)) + ;; PRIMARY: (export "global" (global $e)) - ;; SECONDARY: (import "primary" "global" (global $f i32)) - ;; SECONDARY: (import "primary" "global_1" (global $e i32)) + ;; SECONDARY: (import "primary" "global" (global $e i32)) ;; SECONDARY: (global $c i32 (i32.const 42)) ;; SECONDARY: (global $b i32 (global.get $c))