diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 67b361fb946..d0bf2e1206c 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -77,6 +77,7 @@ #include "ir/find_all.h" #include "ir/module-utils.h" #include "ir/names.h" +#include "ir/properties.h" #include "support/small_vector.h" #include "support/stdckdint.h" #include "wasm-builder.h" @@ -336,6 +337,7 @@ struct OwnershipTracker { struct ItemInfo { UsedNames* owner = nullptr; SmallVector usingSecondaries; + bool usedByPrimary = false; }; std::unordered_map tables; @@ -376,6 +378,9 @@ struct OwnershipTracker { // used by the primary module or multiple secondary modules, the primary // module is the owner. auto [it, inserted] = (this->*mapField).insert({name, ItemInfo{owner, {}}}); + if (owner == &primaryUsed) { + it->second.usedByPrimary = true; + } Module* secondary = nullptr; if (owner != &primaryUsed) { size_t index = owner - secondaryUsed.data(); @@ -420,13 +425,30 @@ struct OwnershipTracker { } bool isUnused(Name name, const std::unordered_map& map) { - return getOwner(name, map) == nullptr; + auto it = map.find(name); + if (it != map.end()) { + return !it->second.usedByPrimary && it->second.usingSecondaries.empty(); + } + return true; + } + + bool isUsedByPrimary(Name name, + const std::unordered_map& map) { + auto it = map.find(name); + if (it != map.end()) { + return it->second.usedByPrimary; + } + return false; } bool usedBySingleSecondary(Name name, const std::unordered_map& map) { - auto* owner = getOwner(name, map); - return owner != nullptr && owner != &primaryUsed; + auto it = map.find(name); + if (it != map.end()) { + return !it->second.usedByPrimary && + it->second.usingSecondaries.size() == 1; + } + return false; } }; @@ -758,6 +780,18 @@ void ModuleSplitter::thunkExportedSecondaryFunctions() { } } +static bool canCopyGlobal(Global* global, Module& wasm) { + // Mutable globals cannot be copied to multiple modules because mutations in + // one module would not be visible in others. Immutable globals with + // generative initializers (e.g. struct.new) also cannot be copied because + // there would be an observable difference (e.g. struct identity tested with + // ref.eq) between the values in the copies. Note that imported globals can be + // copied / moved. + return !global->mutable_ && + (!global->init || + !Properties::isGenerative(global->init, nullptr, wasm)); +} + void ModuleSplitter::computeUsedNames() { tracker.secondaries = &secondaries; tracker.secondaryUsed.resize(secondaries.size()); @@ -1047,9 +1081,41 @@ void ModuleSplitter::computeUsedNames() { if (!global->init) { continue; } - if (UsedNames* owner = tracker.getOwner(global->name, tracker.globals)) { - for (auto* get : FindAll(global->init).list) { - tracker.insert(get->name, owner); + auto gets = FindAll(global->init).list; + if (gets.empty()) { + continue; + } + + // In case of mutable globals or immutable globals with generative + // initializers (e.g. struct.new), we cannot have multiple copies. Compute + // the 'owner' of the global and insert its dependent globals there. + if (!canCopyGlobal(global.get(), primary)) { + if (UsedNames* owner = tracker.getOwner(global->name, tracker.globals)) { + for (auto* get : gets) { + tracker.insert(get->name, owner); + } + } + continue; + } + + // In case of copyable immutable globals, we can have multiple copies of + // them. To reduce the primary module size, we just copy the globals to all + // secondary modules using them. So here we insert the dependent globals in + // all using modules. + if (tracker.isUsedByPrimary(global->name, tracker.globals)) { + for (auto* get : gets) { + tracker.insert(get->name, &primaryUsed); + } + } + auto& usingSecs = + tracker.getUsingSecondaries(global->name, tracker.globals); + for (size_t i = 0; i < secondaries.size(); ++i) { + Module* sec = secondaries[i].get(); + if (std::find(usingSecs.begin(), usingSecs.end(), sec) != + usingSecs.end()) { + for (auto* get : gets) { + tracker.insert(get->name, &secondaryUsed[i]); + } } } } @@ -1063,8 +1129,8 @@ void ModuleSplitter::shareImportableItems() { // 2. If an item is used by only a single secondary module, move the item to // that secondary module. If an item is used by multiple modules (including // the primary and secondary modules), export the item from the primary and - // import it from the using secondary modules. - + // import it from the using secondary modules. (except for non-generative + // immutable globals, which we just copy to using secondary modules) auto shareElements = [&](auto& elements, auto& trackerElements, auto copyElement, @@ -1073,18 +1139,41 @@ void ModuleSplitter::shareImportableItems() { ExternalKind kind = ExternalKind::Invalid) { std::unordered_set elementsToRemove; for (auto& element : elements) { + using T = std::remove_pointer_t; + bool isCopyableGlobal = false; + if constexpr (std::is_same_v) { + isCopyableGlobal = canCopyGlobal(element.get(), primary); + } + if (tracker.isUnused(element->name, trackerElements)) { + // If this element is not used anywhere, just remove it elementsToRemove.insert(element->name); } else if (tracker.usedBySingleSecondary(element->name, trackerElements)) { + // If this element is used in a single secondary module, move it to the + // secondary module auto* secondary = tracker.getUsingSecondaries(element->name, trackerElements)[0]; copyElement(element.get(), *secondary); elementsToRemove.insert(element->name); + } else if (isCopyableGlobal) { + // If this element is a copyable immutable global, copy it to all using + // secondary modules to reduce the primary module size, because we can + // have multiple copies. If it is not used in the primary module, remove + // it from there. + if (!tracker.isUsedByPrimary(element->name, trackerElements)) { + elementsToRemove.insert(element->name); + } + for (auto* secondary : + tracker.getUsingSecondaries(element->name, trackerElements)) { + copyElement(element.get(), *secondary); + } } else { + // If this element is used by multiple modules (primary + multiple + // secondaries or just multiple secondaries), export them from the + // primary module and import them from using secondary modules. // We only import and export Importables, i.e., we don't do this for // segments. - using T = std::remove_pointer_t; if constexpr (std::is_base_of_v) { for (auto* secondary : tracker.getUsingSecondaries(element->name, trackerElements)) { diff --git a/test/lit/wasm-split/dispatch-table-base-global-used-elsewhere.wast b/test/lit/wasm-split/dispatch-table-base-global-used-elsewhere.wast index b860fb144a5..a19bf77b939 100644 --- a/test/lit/wasm-split/dispatch-table-base-global-used-elsewhere.wast +++ b/test/lit/wasm-split/dispatch-table-base-global-used-elsewhere.wast @@ -25,8 +25,6 @@ ;; PRIMARY: (export "table" (table $table)) - ;; PRIMARY: (export "global" (global $base)) - ;; PRIMARY: (export "keep" (func $keep)) ;; PRIMARY: (func $keep @@ -42,7 +40,7 @@ ) ;; SECONDARY: (import "primary" "table" (table $table 2 2 funcref)) - ;; SECONDARY: (import "primary" "global" (global $base i32)) + ;; SECONDARY: (import "env" "base" (global $base i32)) ;; SECONDARY: (import "primary" "keep" (func $keep)) diff --git a/test/lit/wasm-split/passive-deps.wast b/test/lit/wasm-split/passive-deps.wast index 9c14bb09e98..5f16ebc596c 100644 --- a/test/lit/wasm-split/passive-deps.wast +++ b/test/lit/wasm-split/passive-deps.wast @@ -22,7 +22,6 @@ ;; PRIMARY-NEXT: (type $0 (func)) ;; PRIMARY-NEXT: (global $g funcref (ref.null nofunc)) ;; PRIMARY-NEXT: (elem $passive-elem funcref (item (global.get $g))) -;; PRIMARY-NEXT: (export "global" (global $g)) ;; PRIMARY-NEXT: (func $keep (type $0) ;; PRIMARY-NEXT: (elem.drop $passive-elem) ;; PRIMARY-NEXT: ) @@ -30,7 +29,7 @@ ;; SECONDARY: (module ;; SECONDARY-NEXT: (type $0 (func)) -;; SECONDARY-NEXT: (import "primary" "global" (global $g funcref)) +;; SECONDARY-NEXT: (global $g funcref (ref.null nofunc)) ;; SECONDARY-NEXT: (func $split (type $0) ;; SECONDARY-NEXT: (drop ;; SECONDARY-NEXT: (global.get $g) diff --git a/test/lit/wasm-split/split-module-items.wast b/test/lit/wasm-split/split-module-items.wast index b4a8b3db881..2cf526d1f36 100644 --- a/test/lit/wasm-split/split-module-items.wast +++ b/test/lit/wasm-split/split-module-items.wast @@ -5,14 +5,19 @@ ;; Check that ;; 1. Items only used in the primary module stay in the primary module ;; 2. Items only used in the secondary module are moved to the secondary module -;; 3. Items used in both modules are exported from the primary and imported from -;; the secondary module +;; 3. Items (that are not copyable immutable globals) used in both modules are +;; exported from the primary and imported from the secondary module +;; 3-1. Non-generative immutable globals used in both modules stay in the +;; primary module and also are copied to the secondary module +;; 3-2. Generative immutable globals (e.g. struct.new) used in both modules are +;; exported from the primary and imported from the secondary module (module (rec (type $struct (descriptor $desc) (struct)) (type $desc (describes $struct) (struct)) ) + (type $simple-struct (struct)) (global $null-desc (ref null none) (ref.null none)) (memory $keep-memory 1 1) @@ -20,6 +25,7 @@ ;; so it is pinned to the primary, so this will be too. (memory $keep-memory2 1 1) (global $keep-immutable-global i32 (i32.const 20)) + (global $keep-immutable-generative-global (ref $simple-struct) (struct.new_default $simple-struct)) (global $keep-mutable-global (mut i32) (i32.const 20)) (table $keep-table 1 1 funcref) ;; This is only used in the secondary module, but segments $keep-elem2 and @@ -37,6 +43,7 @@ (memory $split-memory 1 1) (global $split-immutable-global i32 (i32.const 20)) + (global $split-immutable-generative-global (ref $simple-struct) (struct.new_default $simple-struct)) (global $split-mutable-global (mut i32) (i32.const 20)) (table $split-table 1 1 funcref) (tag $split-tag (param i32)) @@ -45,6 +52,7 @@ (memory $shared-memory 1 1) (global $shared-immutable-global i32 (i32.const 20)) + (global $shared-immutable-generative-global (ref $simple-struct) (struct.new_default $simple-struct)) (global $shared-mutable-global (mut i32) (i32.const 20)) (table $shared-table 1 1 funcref) (tag $shared-tag (param i32)) @@ -52,8 +60,10 @@ (data $shared-data (memory $shared-memory) (i32.const 0) "a") ;; PRIMARY: (global $keep-immutable-global i32 (i32.const 20)) + ;; PRIMARY-NEXT: (global $keep-immutable-generative-global (ref $simple-struct) (struct.new_default $simple-struct)) ;; PRIMARY-NEXT: (global $keep-mutable-global (mut i32) (i32.const 20)) ;; PRIMARY-NEXT: (global $shared-immutable-global i32 (i32.const 20)) + ;; PRIMARY-NEXT: (global $shared-immutable-generative-global (ref $simple-struct) (struct.new_default $simple-struct)) ;; PRIMARY-NEXT: (global $shared-mutable-global (mut i32) (i32.const 20)) ;; PRIMARY-NEXT: (memory $keep-memory 1 1) ;; PRIMARY-NEXT: (memory $keep-memory2 1 1) @@ -71,14 +81,14 @@ ;; PRIMARY-NEXT: (global.get $null-desc) ;; PRIMARY-NEXT: ))) ;; PRIMARY-NEXT: (elem $shared-elem (table $shared-table) (i32.const 0) funcref (item (ref.null nofunc))) - ;; PRIMARY: (tag $keep-tag (type $3) (param i32)) - ;; PRIMARY-NEXT: (tag $shared-tag (type $3) (param i32)) + ;; PRIMARY: (tag $keep-tag (type $4) (param i32)) + ;; PRIMARY-NEXT: (tag $shared-tag (type $4) (param i32)) ;; PRIMARY: (export "memory" (memory $keep-memory2)) ;; PRIMARY-NEXT: (export "memory_1" (memory $shared-memory)) ;; PRIMARY-NEXT: (export "table" (table $keep-table2)) ;; PRIMARY-NEXT: (export "table_3" (table $shared-table)) - ;; PRIMARY-NEXT: (export "global" (global $shared-immutable-global)) + ;; PRIMARY-NEXT: (export "global" (global $shared-immutable-generative-global)) ;; PRIMARY-NEXT: (export "global_5" (global $shared-mutable-global)) ;; PRIMARY-NEXT: (export "tag" (tag $shared-tag)) ;; PRIMARY-NEXT: (export "keep" (func $keep)) @@ -86,21 +96,23 @@ ;; SECONDARY: (import "primary" "memory" (memory $keep-memory2 1 1)) ;; SECONDARY-NEXT: (import "primary" "memory_1" (memory $shared-memory 1 1)) - ;; SECONDARY-NEXT: (import "primary" "table" (table $keep-table2 1 1 (ref null $2))) + ;; SECONDARY-NEXT: (import "primary" "table" (table $keep-table2 1 1 (ref null $3))) ;; SECONDARY-NEXT: (import "primary" "table_3" (table $shared-table 1 1 funcref)) ;; SECONDARY-NEXT: (import "primary" "table_8" (table $timport$2 1 funcref)) - ;; SECONDARY-NEXT: (import "primary" "global" (global $shared-immutable-global i32)) + ;; SECONDARY-NEXT: (import "primary" "global" (global $shared-immutable-generative-global (ref $0))) ;; SECONDARY-NEXT: (import "primary" "global_5" (global $shared-mutable-global (mut 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))) + ;; SECONDARY-NEXT: (import "primary" "tag" (tag $shared-tag (type $2) (param i32))) ;; SECONDARY: (global $split-immutable-global i32 (i32.const 20)) + ;; SECONDARY-NEXT: (global $split-immutable-generative-global (ref $0) (struct.new_default $0)) ;; SECONDARY-NEXT: (global $split-mutable-global (mut i32) (i32.const 20)) + ;; SECONDARY-NEXT: (global $shared-immutable-global i32 (i32.const 20)) ;; SECONDARY-NEXT: (memory $split-memory 1 1) ;; SECONDARY-NEXT: (data $split-data (memory $split-memory) (i32.const 0) "a") ;; SECONDARY-NEXT: (table $split-table 1 1 funcref) ;; SECONDARY-NEXT: (elem $split-elem (table $split-table) (i32.const 0) funcref (item (ref.null nofunc))) - ;; SECONDARY: (tag $split-tag (type $1) (param i32)) + ;; SECONDARY: (tag $split-tag (type $2) (param i32)) (func $keep (param i32) (result i32) (call $split (i32.const 0)) @@ -117,10 +129,14 @@ (i32.const 0) ) ) - ;; Uses $keep-immutable-global and $keep-mutable-global + ;; Uses $keep-immutable-global, $keep-immutable-generative-global, and + ;; $keep-mutable-global (drop (global.get $keep-immutable-global) ) + (drop + (global.get $keep-immutable-generative-global) + ) (drop (global.get $keep-mutable-global) ) @@ -141,10 +157,14 @@ (i32.const 0) ) ) - ;; Uses $shared-immutable-global and $shared-mutable-global + ;; Uses $shared-immutable-global, $shared-immutable-generative-global, and + ;; $shared-mutable-global (drop (global.get $shared-immutable-global) ) + (drop + (global.get $shared-immutable-generative-global) + ) (drop (global.get $shared-mutable-global) ) @@ -180,10 +200,14 @@ (drop (table.get $keep-table2 (i32.const 0)) ) - ;; Uses $split-immutable-global and $shared-mutable-global + ;; Uses $split-immutable-global, $split-immutable-generative-global, and + ;; $split-mutable-global (drop (global.get $split-immutable-global) ) + (drop + (global.get $split-immutable-generative-global) + ) (drop (global.get $split-mutable-global) ) @@ -204,10 +228,14 @@ (i32.const 0) ) ) - ;; Uses $shared-immutable-global and $shared-mutable-global + ;; Uses $shared-immutable-global, $shared-immutable-generative-global, and + ;; $shared-mutable-global (drop (global.get $shared-immutable-global) ) + (drop + (global.get $shared-immutable-generative-global) + ) (drop (global.get $shared-mutable-global) ) diff --git a/test/lit/wasm-split/transitive-globals.wast b/test/lit/wasm-split/transitive-globals.wast index ad702a4363b..00fc2d3bd3e 100644 --- a/test/lit/wasm-split/transitive-globals.wast +++ b/test/lit/wasm-split/transitive-globals.wast @@ -3,13 +3,14 @@ ;; RUN: wasm-dis %t.2.wasm | filecheck %s --check-prefix SECONDARY ;; Check that transitive dependencies in global initializers are correctly -;; analyzed and moved to the secondary module. +;; analyzed and copied to the secondary module. (module - ;; There are two dependency chains: $a->$b->$c and $d->$e->$f. While all of - ;; $a, $b, and $c can be moved to the secondary module because all f them are - ;; used only there, $e is used in the primary module, preventing $e and $f - ;; from being moved to the secondary module. + ;; There are two dependency chains: $a->$b->$c and $d->$e->$f. Because these + ;; are immutable globals, globals are copied to whichever module they are + ;; used. The secondary module uses $a and $d, so it will have all globals + ;; copied to it. The primary module only uses $e, so it will have $e and its + ;; dependency $f. (global $c i32 (i32.const 42)) (global $b i32 (global.get $c)) @@ -29,7 +30,6 @@ ) ) - ;; Exclusively uses $a and $d, causing them to move to the secondary module (func $split (drop (global.get $a) @@ -44,7 +44,6 @@ ;; PRIMARY-NEXT: (type $0 (func)) ;; PRIMARY-NEXT: (global $f i32 (i32.const 42)) ;; PRIMARY-NEXT: (global $e i32 (global.get $f)) -;; PRIMARY-NEXT: (export "global" (global $e)) ;; PRIMARY-NEXT: (func $keep ;; PRIMARY-NEXT: (drop ;; PRIMARY-NEXT: (global.get $e) @@ -54,10 +53,11 @@ ;; SECONDARY: (module ;; SECONDARY-NEXT: (type $0 (func)) -;; SECONDARY-NEXT: (import "primary" "global" (global $e i32)) ;; SECONDARY-NEXT: (global $c i32 (i32.const 42)) ;; SECONDARY-NEXT: (global $b i32 (global.get $c)) ;; SECONDARY-NEXT: (global $a i32 (global.get $b)) +;; SECONDARY-NEXT: (global $f i32 (i32.const 42)) +;; SECONDARY-NEXT: (global $e i32 (global.get $f)) ;; SECONDARY-NEXT: (global $d i32 (global.get $e)) ;; SECONDARY-NEXT: (func $split ;; SECONDARY-NEXT: (drop diff --git a/test/lit/wasm-split/transitive-globals2.wast b/test/lit/wasm-split/transitive-globals2.wast index 56e96807aae..04944971521 100644 --- a/test/lit/wasm-split/transitive-globals2.wast +++ b/test/lit/wasm-split/transitive-globals2.wast @@ -3,8 +3,9 @@ ;; RUN: wasm-dis -all %t.2.wasm | filecheck %s --check-prefix SECONDARY ;; The dependence chain is $g4->$g3->$g2->$g1, and because $g4 is used in the -;; primary module, all four globals should end up in the primary module. Only -;; $g2 needs to be exported to the secondary module, not $g1. +;; primary module, all four globals should end up in the primary module. The +;; secondary module uses $g2, so it will have $g2 and $g1 it depends on. +;; (Immmutable globals are copied to whichever modules they are used.) (module (global $g1 i32 (i32.const 42)) @@ -27,7 +28,6 @@ ;; PRIMARY-NEXT: (global $g2 i32 (global.get $g1)) ;; PRIMARY-NEXT: (global $g3 i32 (global.get $g2)) ;; PRIMARY-NEXT: (global $g4 i32 (global.get $g3)) -;; PRIMARY-NEXT: (export "global" (global $g2)) ;; PRIMARY-NEXT: (func $keep (type $0) ;; PRIMARY-NEXT: (drop ;; PRIMARY-NEXT: (global.get $g4) @@ -37,7 +37,8 @@ ;; SECONDARY: (module ;; SECONDARY-NEXT: (type $0 (func)) -;; SECONDARY-NEXT: (import "primary" "global" (global $g2 i32)) +;; SECONDARY-NEXT: (global $g1 i32 (i32.const 42)) +;; SECONDARY-NEXT: (global $g2 i32 (global.get $g1)) ;; SECONDARY-NEXT: (func $split (type $0) ;; SECONDARY-NEXT: (drop ;; SECONDARY-NEXT: (global.get $g2) diff --git a/test/lit/wasm-split/transitive-immutable-globals-multi.wast b/test/lit/wasm-split/transitive-immutable-globals-multi.wast index 3f2cb97c392..3825f08dff2 100644 --- a/test/lit/wasm-split/transitive-immutable-globals-multi.wast +++ b/test/lit/wasm-split/transitive-immutable-globals-multi.wast @@ -3,52 +3,78 @@ ;; RUN: wasm-dis -all %t1.wasm | filecheck %s --check-prefix SECONDARY1 ;; RUN: wasm-dis -all %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. +;; In case of non-generative immutable globals, they are copied to whichever +;; modules they are used. $e will be copied to both module1 ($split1) and +;; module2 ($split2), and this will also cause $f to be copied to both modules. +;; However, generative immutable globals ($gen) cannot be copied, so $gen and +;; its dependency $gen-dep stay in the primary module, and only $gen is +;; exported/imported. (module + (type $struct (struct (field i32))) + (global $f i32 (i32.const 42)) (global $e i32 (global.get $f)) + (global $gen-dep i32 (i32.const 10)) + (global $gen (ref $struct) (struct.new $struct (global.get $gen-dep))) + (func $keep (nop) ) (func $split1 (drop (global.get $e)) + (drop (global.get $gen)) ) (func $split2 (drop (global.get $e)) + (drop (global.get $gen)) ) ) ;; PRIMARY: (module -;; PRIMARY-NEXT: (type $0 (func)) -;; PRIMARY-NEXT: (global $f i32 (i32.const 42)) -;; PRIMARY-NEXT: (global $e i32 (global.get $f)) -;; PRIMARY-NEXT: (export "global" (global $e)) -;; PRIMARY-NEXT: (func $keep (type $0) +;; PRIMARY-NEXT: (type $struct (struct (field i32))) +;; PRIMARY-NEXT: (type $1 (func)) +;; PRIMARY-NEXT: (global $gen-dep i32 (i32.const 10)) +;; PRIMARY-NEXT: (global $gen (ref $struct) (struct.new $struct +;; PRIMARY-NEXT: (global.get $gen-dep) +;; PRIMARY-NEXT: )) +;; PRIMARY-NEXT: (export "global" (global $gen)) +;; PRIMARY-NEXT: (func $keep (type $1) ;; PRIMARY-NEXT: (nop) ;; PRIMARY-NEXT: ) ;; PRIMARY-NEXT: ) ;; SECONDARY1: (module -;; SECONDARY1-NEXT: (type $0 (func)) -;; SECONDARY1-NEXT: (import "primary" "global" (global $e i32)) -;; SECONDARY1-NEXT: (func $split1 (type $0) +;; SECONDARY1-NEXT: (type $0 (struct (field i32))) +;; SECONDARY1-NEXT: (type $1 (func)) +;; SECONDARY1-NEXT: (import "primary" "global" (global $gen (ref $0))) +;; SECONDARY1-NEXT: (global $f i32 (i32.const 42)) +;; SECONDARY1-NEXT: (global $e i32 (global.get $f)) +;; SECONDARY1-NEXT: (func $split1 (type $1) ;; SECONDARY1-NEXT: (drop ;; SECONDARY1-NEXT: (global.get $e) ;; SECONDARY1-NEXT: ) +;; SECONDARY1-NEXT: (drop +;; SECONDARY1-NEXT: (global.get $gen) +;; SECONDARY1-NEXT: ) ;; SECONDARY1-NEXT: ) ;; SECONDARY1-NEXT: ) ;; SECONDARY2: (module -;; SECONDARY2-NEXT: (type $0 (func)) -;; SECONDARY2-NEXT: (import "primary" "global" (global $e i32)) -;; SECONDARY2-NEXT: (func $split2 (type $0) +;; SECONDARY2-NEXT: (type $0 (struct (field i32))) +;; SECONDARY2-NEXT: (type $1 (func)) +;; SECONDARY2-NEXT: (import "primary" "global" (global $gen (ref $0))) +;; SECONDARY2-NEXT: (global $f i32 (i32.const 42)) +;; SECONDARY2-NEXT: (global $e i32 (global.get $f)) +;; SECONDARY2-NEXT: (func $split2 (type $1) ;; SECONDARY2-NEXT: (drop ;; SECONDARY2-NEXT: (global.get $e) ;; SECONDARY2-NEXT: ) +;; SECONDARY2-NEXT: (drop +;; SECONDARY2-NEXT: (global.get $gen) +;; SECONDARY2-NEXT: ) ;; SECONDARY2-NEXT: ) ;; SECONDARY2-NEXT: )