diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp index 4827664a721..b17d6c772b9 100644 --- a/src/ir/module-utils.cpp +++ b/src/ir/module-utils.cpp @@ -18,12 +18,9 @@ #include "ir/intrinsics.h" #include "ir/manipulation.h" #include "ir/metadata.h" -#include "ir/subtypes.h" -#include "pass.h" +#include "ir/properties.h" #include "support/insert_ordered.h" #include "support/topological_sort.h" -#include "support/utilities.h" -#include "wasm-builder.h" namespace wasm::ModuleUtils { @@ -607,24 +604,55 @@ collectHeapTypeInfo(Module& wasm, namespace { -// Collects all defined heap types transitively reachable from a root set of -// types. +void classifyTypeVisibility(Module& wasm, + InsertOrderedMap& types, + WorldMode worldMode) { + for (auto type : getPublicHeapTypes(wasm, worldMode)) { + if (auto it = types.find(type); it != types.end()) { + it->second.visibility = Visibility::Public; + } + } + for (auto& [type, info] : types) { + if (info.visibility != Visibility::Public) { + info.visibility = Visibility::Private; + } + } +} + +// Collects all heap types transitively reachable from a root set of types. +// Options are provided to customize the traversal: +// - `includeSupertypes`: if true, declared supertypes are also traversed. +// - `includeRecGroups`: if true, all types in the same recursion group +// are also traversed. std::vector -getTransitivelyReachable(const std::vector& roots) { +getTransitivelyReachable(const std::vector& roots, + bool includeSupertypes, + bool includeRecGroups) { std::vector result; std::vector worklist; + std::unordered_set seen; std::unordered_set seenRecGroups; auto note = [&](HeapType type) { if (type.isBasic()) { + if (seen.insert(type).second) { + result.push_back(type); + } return; } - auto group = type.getRecGroup(); - if (seenRecGroups.insert(group).second) { - for (auto member : group) { - result.push_back(member); - worklist.push_back(member); + if (includeRecGroups) { + auto group = type.getRecGroup(); + if (seenRecGroups.insert(group).second) { + for (auto member : group) { + result.push_back(member); + worklist.push_back(member); + } + } + } else { + if (seen.insert(type).second) { + result.push_back(type); + worklist.push_back(type); } } }; @@ -636,7 +664,12 @@ getTransitivelyReachable(const std::vector& roots) { while (!worklist.empty()) { auto curr = worklist.back(); worklist.pop_back(); + std::optional super = + includeSupertypes ? std::nullopt : curr.getDeclaredSuperType(); for (auto t : curr.getReferencedHeapTypes()) { + if (super && t == *super) { + continue; + } note(t); } } @@ -644,158 +677,6 @@ getTransitivelyReachable(const std::vector& roots) { return result; } -// Computes the visibility of all types in the module. -// -// ## Closed World Mode -// Every type reachable from imports/exports and all of their rec group siblings -// are marked public. This preserves the structural type identity of the imports -// and exports. -// -// ## Open World Mode -// In an open world, the outside environment may cast a publicized type down -// to any of its subtypes. Thus, subtypes of exposed types must also remain -// public to preserve their structural identities. -void classifyTypeVisibility(Module& wasm, - InsertOrderedMap& types, - WorldMode worldMode) { - if (worldMode == WorldMode::Closed) { - // In closed world mode, the public types are simply the exposed types and - // all types reachable from their definitions. - for (auto type : getPublicHeapTypes(wasm, WorldMode::Closed)) { - if (auto it = types.find(type); it != types.end()) { - it->second.visibility = Visibility::Public; - } - } - for (auto& [_, info] : types) { - if (info.visibility != Visibility::Public) { - info.visibility = Visibility::Private; - } - } - return; - } - - // Open world public types have different levels of exposure that change - // whether their related types must be public or not. - enum Exposure { - // Types that never cross the module boundary (i.e. are "not exposed"), but - // must have stable structural identities so some other public type can have - // a stable identity. - NotExposed, - // Types that may cross the module boundary only via exact references. - ExposedExactly, - // Types that may cross the module boundary via inexact references, meaning - // their subtypes may cross the module boundary as well. - Exposed - }; - - std::unordered_map exposures; - std::vector worklist; - - // Insert or upgrade a type's exposure in the `visited` map. If a type's - // exposure is upgraded, we re-push it to the worklist to update the - // propagation to related types. - auto markPublic = [&](HeapType type, Exposure state) { - auto [it, inserted] = exposures.insert({type, state}); - if (inserted || state > it->second) { - it->second = state; - worklist.push_back(type); - } - }; - - // Build the subtype hierarchy. - std::vector heapTypes; - heapTypes.reserve(types.size()); - for (auto& [type, _] : types) { - heapTypes.push_back(type); - } - SubTypes subTypes(heapTypes); - - // Initialize with directly exposed types. - for (auto& [type, exact] : getExposedPublicHeapTypes(wasm)) { - markPublic(type, - exact == Exact ? Exposure::ExposedExactly : Exposure::Exposed); - } - - while (!worklist.empty()) { - auto curr = worklist.back(); - worklist.pop_back(); - - auto state = exposures.at(curr); - - // Propagate exposed status to subtypes. - if (state == Exposure::Exposed) { - if (curr.isBasic()) { - for (auto& [definedType, _] : types) { - if (HeapType::isSubType(definedType, curr)) { - markPublic(definedType, Exposure::Exposed); - } - } - } else { - for (auto sub : subTypes.getImmediateSubTypes(curr)) { - markPublic(sub, Exposure::Exposed); - } - } - } - - if (curr.isBasic()) { - continue; - } - - // Rec group members must also be public, but do not necessarily cross the - // module boundary. - for (auto member : curr.getRecGroup()) { - markPublic(member, Exposure::NotExposed); - } - - // Types reachable from this public type (e.g. params, results, fields) must - // be public. If the current type is not exposed, the other reachable types - // are not necessarily exposed either. If the current type is exposed - // (whether exactly or not), the reachable types are exposed with exactness - // depending on the reference type. - for (auto child : curr.getTypeChildren()) { - if (child.isRef()) { - auto exposure = state == NotExposed ? NotExposed - : child.isExact() ? ExposedExactly - : Exposed; - markPublic(child.getHeapType(), exposure); - } - } - - // Public continuation types require their function types to be public, but - // a continuation reference does not make any function reference available. - if (curr.isContinuation()) { - markPublic(curr.getContinuation().type, NotExposed); - } - - // Descriptor types are like type children, except that they are exposed - // exactly iff the current type is exposed exactly. - if (auto desc = curr.getDescriptorType()) { - markPublic(*desc, state); - } - - // Supertypes need to be public, but only to keep structural identity the - // same. Other types related to the supertypes are not necessarily exposed. - if (auto super = curr.getDeclaredSuperType()) { - markPublic(*super, Exposure::NotExposed); - } - - // Similarly, described types also need to be kept public, but they are not - // necessarily exposed just because their descriptor is exposed. - if (auto described = curr.getDescribedType()) { - markPublic(*described, Exposure::NotExposed); - } - } - - // Mark visibility for all defined types - for (auto& [type, typeInfo] : types) { - if (exposures.contains(type)) { - typeInfo.visibility = Visibility::Public; - } else { - typeInfo.visibility = Visibility::Private; - } - } -} - void setIndices(IndexedHeapTypes& indexedTypes) { for (Index i = 0; i < indexedTypes.types.size(); i++) { indexedTypes.indices[indexedTypes.types[i]] = i; @@ -814,59 +695,60 @@ std::vector collectHeapTypes(Module& wasm) { return types; } -std::vector> -getExposedPublicHeapTypes(Module& wasm) { - InsertOrderedMap seenTypes; +std::vector getExposedPublicHeapTypes(Module& wasm) { + // Look at the types of imports and exports to get an initial set of public + // types. + std::vector publicTypes; + std::unordered_set seenTypes; - auto notePublic = [&](HeapType type, Exactness exact) { - auto [it, inserted] = seenTypes.insert({type, exact}); - if (!inserted) { - if (it->second == Exact && exact == Inexact) { - it->second = Inexact; - } + auto notePublic = [&](HeapType type) { + if (seenTypes.insert(type).second) { + publicTypes.push_back(type); } }; - ModuleUtils::iterImportedTags( - wasm, [&](Tag* tag) { notePublic(tag->type, Inexact); }); + ModuleUtils::iterImportedTags(wasm, [&](Tag* tag) { notePublic(tag->type); }); ModuleUtils::iterImportedTables(wasm, [&](Table* table) { assert(table->type.isRef()); - notePublic(table->type.getHeapType(), table->type.getExactness()); + notePublic(table->type.getHeapType()); }); ModuleUtils::iterImportedGlobals(wasm, [&](Global* global) { if (global->type.isRef()) { - notePublic(global->type.getHeapType(), global->type.getExactness()); + notePublic(global->type.getHeapType()); } }); ModuleUtils::iterImportedFunctions(wasm, [&](Function* func) { + // We can ignore call.without.effects, which is implemented as an import but + // functionally is a call within the module. if (!Intrinsics(wasm).isCallWithoutEffects(func)) { - notePublic(func->type.getHeapType(), Inexact); + notePublic(func->type.getHeapType()); } }); for (auto& ex : wasm.exports) { switch (ex->kind) { case ExternalKind::Function: { auto* func = wasm.getFunction(*ex->getInternalName()); - notePublic(func->type.getHeapType(), Inexact); + notePublic(func->type.getHeapType()); continue; } case ExternalKind::Table: { auto* table = wasm.getTable(*ex->getInternalName()); assert(table->type.isRef()); - notePublic(table->type.getHeapType(), table->type.getExactness()); + notePublic(table->type.getHeapType()); continue; } case ExternalKind::Memory: + // Never a reference type. continue; case ExternalKind::Global: { auto* global = wasm.getGlobal(*ex->getInternalName()); if (global->type.isRef()) { - notePublic(global->type.getHeapType(), global->type.getExactness()); + notePublic(global->type.getHeapType()); } continue; } case ExternalKind::Tag: - notePublic(wasm.getTag(*ex->getInternalName())->type, Inexact); + notePublic(wasm.getTag(*ex->getInternalName())->type); continue; case ExternalKind::Invalid: break; @@ -874,36 +756,22 @@ getExposedPublicHeapTypes(Module& wasm) { WASM_UNREACHABLE("unexpected export kind"); } + // Ignorable public types are public. for (auto type : getIgnorablePublicTypes()) { - notePublic(type, Inexact); + notePublic(type); } - return std::vector>(seenTypes.begin(), - seenTypes.end()); + return publicTypes; } std::vector getPublicHeapTypes(Module& wasm, WorldMode worldMode) { - if (worldMode == WorldMode::Closed) { - // Find all the types reachable from the directly exposed types. There's no - // need to traverse the entire module to find all the subtypes, etc. - auto exposedPairs = getExposedPublicHeapTypes(wasm); - std::vector directlyExposed; - directlyExposed.reserve(exposedPairs.size()); - for (auto& [type, _] : exposedPairs) { - directlyExposed.push_back(type); - } - return getTransitivelyReachable(directlyExposed); - } - - // In open-world mode we need to find all the types so we can include - // subtypes. - auto typeInfo = collectHeapTypeInfo(wasm, - worldMode, - TypeInclusion::AllTypes, - VisibilityHandling::FindVisibility); + auto directlyExposed = getExposedPublicHeapTypes(wasm); + auto transitivelyExposed = getTransitivelyReachable( + directlyExposed, /*includeSupertypes=*/true, /*includeRecGroups=*/true); std::vector publicTypes; - for (auto& [type, info] : typeInfo) { - if (info.visibility == Visibility::Public) { + publicTypes.reserve(transitivelyExposed.size()); + for (auto type : transitivelyExposed) { + if (!type.isBasic()) { publicTypes.push_back(type); } } @@ -913,7 +781,7 @@ std::vector getPublicHeapTypes(Module& wasm, WorldMode worldMode) { std::vector getPrivateHeapTypes(Module& wasm, WorldMode worldMode) { auto info = collectHeapTypeInfo(wasm, worldMode, - TypeInclusion::AllTypes, + TypeInclusion::UsedIRTypes, VisibilityHandling::FindVisibility); std::vector types; types.reserve(info.size()); diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h index dde0a71e3ba..860672beef8 100644 --- a/src/ir/module-utils.h +++ b/src/ir/module-utils.h @@ -483,8 +483,7 @@ std::vector collectHeapTypes(Module& wasm); // Get the types directly made public by imported or exported module items. For // example, the types of imported or exported globals or functions, but not // other types reachable from those types. Includes abstract heap types. -std::vector> -getExposedPublicHeapTypes(Module& wasm); +std::vector getExposedPublicHeapTypes(Module& wasm); // Collect all the defined heap types visible on the module boundary that cannot // be changed, e.g. the defined types from getExposedPublicHeapTypes and those diff --git a/src/passes/Unsubtyping.cpp b/src/passes/Unsubtyping.cpp index 74b0850da0e..866e32ba58a 100644 --- a/src/passes/Unsubtyping.cpp +++ b/src/passes/Unsubtyping.cpp @@ -567,6 +567,9 @@ struct Unsubtyping : Pass, Noter { if (!wasm->features.hasGC()) { return; } + if (getPassOptions().worldMode == WorldMode::Open) { + Fatal() << "Unsubtyping requires --closed-world"; + } // Initialize the subtype relation based on what is immediately required to // keep the code and public types valid. diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index a28faacc3c0..8a5a63ca0af 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -250,7 +250,7 @@ void validateExactReferences(Module& module, ValidationInfo& info) { return; } - for (auto& [type, _] : ModuleUtils::getExposedPublicHeapTypes(module)) { + for (auto type : ModuleUtils::getExposedPublicHeapTypes(module)) { for (auto child : type.getTypeChildren()) { if (child.isExact()) { std::string typeName; diff --git a/test/lit/passes/gufa-tnh.wast b/test/lit/passes/gufa-tnh.wast index b4601d47661..a0dd9846191 100644 --- a/test/lit/passes/gufa-tnh.wast +++ b/test/lit/passes/gufa-tnh.wast @@ -169,6 +169,8 @@ ;; CHECK: (type $4 (func (param anyref))) + ;; CHECK: (export "out" (func $caller)) + ;; CHECK: (func $maker (type $2) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (struct.new $A @@ -211,8 +213,7 @@ ) ) - ;; CHECK: (@binaryen.js.called) - ;; CHECK-NEXT: (func $caller (type $4) (param $any anyref) + ;; CHECK: (func $caller (type $4) (param $any anyref) ;; CHECK-NEXT: (local $x (ref null $A)) ;; CHECK-NEXT: (call $called ;; CHECK-NEXT: (local.tee $x @@ -240,8 +241,7 @@ ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (@binaryen.js.called) - (func $caller (param $any anyref) + (func $caller (export "out") (param $any anyref) (local $x (ref null $A)) ;; The called function casts to $B. This lets us infer the value of the ;; fallthrough ref.cast, which will turn into $B. Furthermore, that then @@ -296,6 +296,8 @@ ;; CHECK: (type $3 (func)) + ;; CHECK: (export "out" (func $caller)) + ;; CHECK: (func $maker (type $3) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (struct.new $A @@ -338,8 +340,7 @@ ) ) - ;; CHECK: (@binaryen.js.called) - ;; CHECK-NEXT: (func $caller (type $2) (param $a (ref null $A)) + ;; CHECK: (func $caller (type $2) (param $a (ref null $A)) ;; CHECK-NEXT: (local $x (ref null $A)) ;; CHECK-NEXT: (call $called ;; CHECK-NEXT: (local.tee $x @@ -350,8 +351,7 @@ ;; CHECK-NEXT: (i32.const 20) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (@binaryen.js.called) - (func $caller (param $a (ref null $A)) + (func $caller (export "out") (param $a (ref null $A)) (local $x (ref null $A)) ;; The change compared to before is that we only have a local.tee here, and ;; no ref.cast. We can still infer the type of the tee's value, and @@ -370,96 +370,6 @@ ) ) -;; The same module as above, but now $caller is exported instead of marked -;; @binaryen.js.called. This makes $A public, and since we are in open-world -;; mode, that in turn makes its subtype $B public, so we cannot infer anything -;; about either type. -(module - ;; CHECK: (type $A (sub (struct (field (mut i32))))) - (type $A (sub (struct (field (mut i32))))) - - ;; CHECK: (type $B (sub $A (struct (field (mut i32))))) - (type $B (sub $A (struct (field (mut i32))))) - - ;; CHECK: (type $2 (func (param (ref null $A)))) - - ;; CHECK: (type $3 (func)) - - ;; CHECK: (export "out" (func $caller)) - - ;; CHECK: (func $maker (type $3) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (struct.new $A - ;; CHECK-NEXT: (i32.const 10) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (struct.new $B - ;; CHECK-NEXT: (i32.const 20) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $maker - ;; A always contains 10, and B always contains 20, except that they are both - ;; public, so they can come in from the outside with any field value. - (drop - (struct.new $A - (i32.const 10) - ) - ) - (drop - (struct.new $B - (i32.const 20) - ) - ) - ) - - ;; CHECK: (func $called (type $2) (param $x (ref null $A)) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.cast (ref $B) - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $called (param $x (ref null $A)) - ;; Cast the input to a $B, which will help the caller. - (drop - (ref.cast (ref $B) - (local.get $x) - ) - ) - ) - - ;; CHECK: (func $caller (type $2) (param $a (ref null $A)) - ;; CHECK-NEXT: (local $x (ref null $A)) - ;; CHECK-NEXT: (call $called - ;; CHECK-NEXT: (local.tee $x - ;; CHECK-NEXT: (local.get $a) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (struct.get $A 0 - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $caller (export "out") (param $a (ref null $A)) - (local $x (ref null $A)) - ;; Despite inferring that the argument must be a $B, we still cannot - ;; optimize. - (call $called - (local.tee $x - (local.get $a) - ) - ) - (drop - (struct.get $A 0 - (local.get $x) - ) - ) - ) -) - ;; As above, but add a local.tee etc. in the called function. (module ;; CHECK: (type $A (sub (struct (field (mut i32))))) @@ -1236,6 +1146,12 @@ ;; CHECK: (type $5 (func)) + ;; CHECK: (export "caller-C" (func $caller-C)) + + ;; CHECK: (export "caller-B" (func $caller-B)) + + ;; CHECK: (export "caller-A" (func $caller-A)) + ;; CHECK: (func $called (type $4) (param $x (ref null $A)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.cast (ref $B) @@ -1288,8 +1204,7 @@ ) ) - ;; CHECK: (@binaryen.js.called) - ;; CHECK-NEXT: (func $caller-C (type $3) (param $any anyref) + ;; CHECK: (func $caller-C (type $3) (param $any anyref) ;; CHECK-NEXT: (local $temp-C (ref $C)) ;; CHECK-NEXT: (local $temp-any anyref) ;; CHECK-NEXT: (call $called @@ -1319,8 +1234,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (@binaryen.js.called) - (func $caller-C (param $any anyref) + (func $caller-C (export "caller-C") (param $any anyref) (local $temp-C (ref $C)) (local $temp-any anyref) (call $called @@ -1362,8 +1276,7 @@ ) ) - ;; CHECK: (@binaryen.js.called) - ;; CHECK-NEXT: (func $caller-B (type $3) (param $any anyref) + ;; CHECK: (func $caller-B (type $3) (param $any anyref) ;; CHECK-NEXT: (local $temp (ref $A)) ;; CHECK-NEXT: (call $called ;; CHECK-NEXT: (local.tee $temp @@ -1378,8 +1291,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (@binaryen.js.called) - (func $caller-B (param $any anyref) + (func $caller-B (export "caller-B") (param $any anyref) (local $temp (ref $A)) (call $called (local.tee $temp @@ -1395,8 +1307,7 @@ ) ) - ;; CHECK: (@binaryen.js.called) - ;; CHECK-NEXT: (func $caller-A (type $3) (param $any anyref) + ;; CHECK: (func $caller-A (type $3) (param $any anyref) ;; CHECK-NEXT: (local $temp (ref $A)) ;; CHECK-NEXT: (call $called ;; CHECK-NEXT: (local.tee $temp @@ -1411,8 +1322,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (@binaryen.js.called) - (func $caller-A (param $any anyref) + (func $caller-A (export "caller-A") (param $any anyref) (local $temp (ref $A)) (call $called (local.tee $temp @@ -1821,6 +1731,8 @@ ;; CHECK: (elem $e func) (elem $e funcref) + ;; CHECK: (export "out" (func $caller)) + ;; CHECK: (func $called (type $3) (param $struct.get (ref null $A)) (param $struct.set (ref null $A)) (param $array.get (ref null $B)) (param $array.set (ref null $B)) (param $array.len (ref null $B)) (param $array.copy.src (ref null $B)) (param $array.copy.dest (ref null $B)) (param $array.fill (ref null $B)) (param $array.init_data (ref null $B)) (param $array.init_elem (ref null $C)) (param $ref.test (ref null $A)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 0) @@ -1947,8 +1859,7 @@ ) ) - ;; CHECK: (@binaryen.js.called) - ;; CHECK-NEXT: (func $caller (type $4) (param $any anyref) + ;; CHECK: (func $caller (type $4) (param $any anyref) ;; CHECK-NEXT: (call $called ;; CHECK-NEXT: (ref.cast (ref $A) ;; CHECK-NEXT: (local.get $any) @@ -1985,8 +1896,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (@binaryen.js.called) - (func $caller (param $any anyref) + (func $caller (export "out") (param $any anyref) ;; All these casts will be refined to non-nullable, aside from the last ;; param which is but a ref.test. (call $called diff --git a/test/lit/passes/gufa-vs-cfp.wast b/test/lit/passes/gufa-vs-cfp.wast index bbd834e62d6..bfeeac5fa22 100644 --- a/test/lit/passes/gufa-vs-cfp.wast +++ b/test/lit/passes/gufa-vs-cfp.wast @@ -1,6 +1,6 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --remove-unused-names --gufa --closed-world -all -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --remove-unused-names --gufa -all -S -o - | filecheck %s ;; (remove-unused-names is added to test fallthrough values without a block ;; name getting in the way) diff --git a/test/lit/passes/minimize-rec-groups.wast b/test/lit/passes/minimize-rec-groups.wast index f0805c79e26..963f655f20d 100644 --- a/test/lit/passes/minimize-rec-groups.wast +++ b/test/lit/passes/minimize-rec-groups.wast @@ -1,5 +1,5 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt -all --closed-world --minimize-rec-groups -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt -all --minimize-rec-groups -S -o - | filecheck %s ;; A module with no heap types at all should be ok. (module diff --git a/test/lit/passes/signature-refining-configureAll.wast b/test/lit/passes/signature-refining-configureAll.wast index 362f433c29c..2fd1b1d3eae 100644 --- a/test/lit/passes/signature-refining-configureAll.wast +++ b/test/lit/passes/signature-refining-configureAll.wast @@ -1,10 +1,10 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. ;; RUN: foreach %s %t wasm-opt --signature-refining --closed-world -all -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --signature-refining -all -S -o - | filecheck %s --check-prefix OPEN_WORLD -;; Test that configureAll is respected: referred functions are not refined. In -;; open-world, no function types would be refined because configureAll makes -;; `func`, and therefore all function types, public. +;; Test that configureAll is respected: referred functions are not refined. +;; This is so even in closed world. (module ;; CHECK: (rec @@ -19,15 +19,30 @@ ;; CHECK: (type $4 (func)) ;; CHECK: (type $externs (array (mut externref))) + ;; OPEN_WORLD: (rec + ;; OPEN_WORLD-NEXT: (type $func-2 (func (param (ref (exact $struct))) (result (ref (exact $struct))))) + + ;; OPEN_WORLD: (type $func-1 (func (param anyref) (result (ref (exact $struct))))) + + ;; OPEN_WORLD: (type $2 (func (result i32))) + + ;; OPEN_WORLD: (type $struct (struct)) + + ;; OPEN_WORLD: (type $4 (func)) + + ;; OPEN_WORLD: (type $externs (array (mut externref))) (type $externs (array (mut externref))) ;; CHECK: (type $funcs (array (mut funcref))) + ;; OPEN_WORLD: (type $funcs (array (mut funcref))) (type $funcs (array (mut funcref))) ;; CHECK: (type $bytes (array (mut i8))) + ;; OPEN_WORLD: (type $bytes (array (mut i8))) (type $bytes (array (mut i8))) ;; CHECK: (type $configureAll (func (param (ref null $externs) (ref null $funcs) (ref null $bytes) externref))) + ;; OPEN_WORLD: (type $configureAll (func (param (ref null $externs) (ref null $funcs) (ref null $bytes) externref))) (type $configureAll (func (param (ref null $externs)) (param (ref null $funcs)) (param (ref null $bytes)) (param externref))) (type $struct (struct)) @@ -47,16 +62,21 @@ ) ;; CHECK: (import "wasm:js-prototypes" "configureAll" (func $configureAll (type $configureAll) (param (ref null $externs) (ref null $funcs) (ref null $bytes) externref))) + ;; OPEN_WORLD: (import "wasm:js-prototypes" "configureAll" (func $configureAll (type $configureAll) (param (ref null $externs) (ref null $funcs) (ref null $bytes) externref))) (import "wasm:js-prototypes" "configureAll" (func $configureAll (type $configureAll))) ;; CHECK: (data $bytes "12345678") ;; CHECK: (elem $externs externref (item (ref.null noextern))) + ;; OPEN_WORLD: (data $bytes "12345678") + + ;; OPEN_WORLD: (elem $externs externref (item (ref.null noextern))) (elem $externs externref (ref.null extern) ) ;; CHECK: (elem $funcs func $foo $bar) + ;; OPEN_WORLD: (elem $funcs func $foo $bar) (elem $funcs funcref (ref.func $foo) (ref.func $bar) @@ -65,6 +85,7 @@ (data $bytes "12345678") ;; CHECK: (start $start) + ;; OPEN_WORLD: (start $start) (start $start) ;; CHECK: (func $start (type $4) @@ -84,6 +105,23 @@ ;; CHECK-NEXT: (ref.null noextern) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $start (type $4) + ;; OPEN_WORLD-NEXT: (call $configureAll + ;; OPEN_WORLD-NEXT: (array.new_elem $externs $externs + ;; OPEN_WORLD-NEXT: (i32.const 0) + ;; OPEN_WORLD-NEXT: (i32.const 1) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (array.new_elem $funcs $funcs + ;; OPEN_WORLD-NEXT: (i32.const 0) + ;; OPEN_WORLD-NEXT: (i32.const 2) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (array.new_data $bytes $bytes + ;; OPEN_WORLD-NEXT: (i32.const 0) + ;; OPEN_WORLD-NEXT: (i32.const 8) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (ref.null noextern) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) (func $start (call $configureAll (array.new_elem $externs $externs @@ -108,6 +146,18 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $calls (type $4) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (call $bar + ;; OPEN_WORLD-NEXT: (struct.new_default $struct) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (call $unconfigured + ;; OPEN_WORLD-NEXT: (struct.new_default $struct) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) (func $calls (drop (call $bar @@ -124,6 +174,9 @@ ;; CHECK: (func $foo (type $2) (result i32) ;; CHECK-NEXT: (i32.const 42) ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $foo (type $2) (result i32) + ;; OPEN_WORLD-NEXT: (i32.const 42) + ;; OPEN_WORLD-NEXT: ) (func $foo (result i32) ;; Nothing to do here anyhow, but do not error. (i32.const 42) @@ -132,6 +185,9 @@ ;; CHECK: (func $bar (type $func-1) (param $x anyref) (result (ref (exact $struct))) ;; CHECK-NEXT: (struct.new_default $struct) ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $bar (type $func-1) (param $x anyref) (result (ref (exact $struct))) + ;; OPEN_WORLD-NEXT: (struct.new_default $struct) + ;; OPEN_WORLD-NEXT: ) (func $bar (type $func-1) (param $x anyref) (result anyref) ;; The params will not be refined due to configureAll, but the result will. (struct.new $struct) @@ -140,6 +196,9 @@ ;; CHECK: (func $unconfigured (type $func-2) (param $x (ref (exact $struct))) (result (ref (exact $struct))) ;; CHECK-NEXT: (struct.new_default $struct) ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $unconfigured (type $func-2) (param $x (ref (exact $struct))) (result (ref (exact $struct))) + ;; OPEN_WORLD-NEXT: (struct.new_default $struct) + ;; OPEN_WORLD-NEXT: ) (func $unconfigured (type $func-2) (param $x anyref) (result anyref) ;; This is not referred to by configureAll, and can be refined in both ;; params and result. diff --git a/test/lit/passes/unsubtyping-open-world.wast b/test/lit/passes/unsubtyping-open-world.wast deleted file mode 100644 index 016b2534f9b..00000000000 --- a/test/lit/passes/unsubtyping-open-world.wast +++ /dev/null @@ -1,961 +0,0 @@ -;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt -all --preserve-type-order --unsubtyping -all -S -o - | filecheck %s - -;; No public types. We can still optimize. -(module - ;; CHECK: (rec - ;; CHECK-NEXT: (type $super (sub (struct))) - (type $super (sub (struct))) - ;; CHECK: (type $sub (sub (struct))) - (type $sub (sub $super (struct))) - - ;; CHECK: (global $use-super (ref null $super) (ref.null none)) - (global $use-super (ref null $super) (ref.null none)) - ;; CHECK: (global $use-sub (ref null $sub) (ref.null none)) - (global $use-sub (ref null $sub) (ref.null none)) -) - -;; func is public, so we cannot optimize any functions. We can still optimize -;; structs. -(module - ;; CHECK: (type $super-func (sub (func))) - (type $super-func (sub (func))) - ;; CHECK: (type $sub-func (sub $super-func (func))) - (type $sub-func (sub $super-func (func))) - ;; CHECK: (rec - ;; CHECK-NEXT: (type $super-struct (sub (struct))) - (type $super-struct (sub (struct))) - ;; CHECK: (type $sub-struct (sub (struct))) - (type $sub-struct (sub $super-struct (struct))) - - ;; CHECK: (global $g-func funcref (ref.null nofunc)) - (global $g-func funcref (ref.null nofunc)) - ;; CHECK: (global $use-super-func (ref null $super-func) (ref.null nofunc)) - (global $use-super-func (ref null $super-func) (ref.null nofunc)) - ;; CHECK: (global $use-sub-func (ref null $sub-func) (ref.null nofunc)) - (global $use-sub-func (ref null $sub-func) (ref.null nofunc)) - ;; CHECK: (global $use-super-struct (ref null $super-struct) (ref.null none)) - (global $use-super-struct (ref null $super-struct) (ref.null none)) - ;; CHECK: (global $use-sub-struct (ref null $sub-struct) (ref.null none)) - (global $use-sub-struct (ref null $sub-struct) (ref.null none)) - ;; CHECK: (export "g-func" (global $g-func)) - (export "g-func" (global $g-func)) -) - -;; any is public, so we cannot optimize any structs or arrays. We can still -;; optimize functions. -(module - ;; CHECK: (type $super-struct (sub (struct))) - (type $super-struct (sub (struct))) - ;; CHECK: (type $sub-struct (sub $super-struct (struct))) - (type $sub-struct (sub $super-struct (struct))) - ;; CHECK: (rec - ;; CHECK-NEXT: (type $super-func (sub (func))) - (type $super-func (sub (func))) - ;; CHECK: (type $sub-func (sub (func))) - (type $sub-func (sub $super-func (func))) - - ;; CHECK: (global $g-any anyref (ref.null none)) - (global $g-any anyref (ref.null none)) - ;; CHECK: (global $use-super-struct (ref null $super-struct) (ref.null none)) - (global $use-super-struct (ref null $super-struct) (ref.null none)) - ;; CHECK: (global $use-sub-struct (ref null $sub-struct) (ref.null none)) - (global $use-sub-struct (ref null $sub-struct) (ref.null none)) - ;; CHECK: (global $use-super-func (ref null $super-func) (ref.null nofunc)) - (global $use-super-func (ref null $super-func) (ref.null nofunc)) - ;; CHECK: (global $use-sub-func (ref null $sub-func) (ref.null nofunc)) - (global $use-sub-func (ref null $sub-func) (ref.null nofunc)) - ;; CHECK: (export "g-any" (global $g-any)) - (export "g-any" (global $g-any)) -) - -;; i31 is public. We can still optimize structs. -(module - ;; CHECK: (rec - ;; CHECK-NEXT: (type $super-struct (sub (struct))) - (type $super-struct (sub (struct))) - ;; CHECK: (type $sub-struct (sub (struct))) - (type $sub-struct (sub $super-struct (struct))) - - ;; CHECK: (global $g-i31 i31ref (ref.null none)) - (global $g-i31 i31ref (ref.null none)) - ;; CHECK: (global $use-super-struct (ref null $super-struct) (ref.null none)) - (global $use-super-struct (ref null $super-struct) (ref.null none)) - ;; CHECK: (global $use-sub-struct (ref null $sub-struct) (ref.null none)) - (global $use-sub-struct (ref null $sub-struct) (ref.null none)) - ;; CHECK: (export "g-i31" (global $g-i31)) - (export "g-i31" (global $g-i31)) -) - -;; eq is public. We cannot optimize structs. -(module - ;; CHECK: (type $super-struct (sub (struct))) - (type $super-struct (sub (struct))) - ;; CHECK: (type $sub-struct (sub $super-struct (struct))) - (type $sub-struct (sub $super-struct (struct))) - - ;; CHECK: (global $g-eq eqref (ref.null none)) - (global $g-eq eqref (ref.null none)) - ;; CHECK: (global $use-super-struct (ref null $super-struct) (ref.null none)) - (global $use-super-struct (ref null $super-struct) (ref.null none)) - ;; CHECK: (global $use-sub-struct (ref null $sub-struct) (ref.null none)) - (global $use-sub-struct (ref null $sub-struct) (ref.null none)) - ;; CHECK: (export "g-eq" (global $g-eq)) - (export "g-eq" (global $g-eq)) -) - -;; We cannot optimize subtypes of a publicly exposed defined type. We can still -;; optimize unrelated types. -(module - ;; CHECK: (type $public-parent (sub (struct))) - (type $public-parent (sub (struct))) - ;; CHECK: (type $public-child (sub $public-parent (struct))) - (type $public-child (sub $public-parent (struct))) - ;; CHECK: (type $public-grandchild (sub $public-child (struct))) - (type $public-grandchild (sub $public-child (struct))) - ;; CHECK: (rec - ;; CHECK-NEXT: (type $unrelated-super (sub (struct (field i32)))) - (type $unrelated-super (sub (struct (field i32)))) - ;; CHECK: (type $unrelated-sub (sub (struct (field i32) (field f32)))) - (type $unrelated-sub (sub $unrelated-super (struct i32 f32))) - - ;; CHECK: (global $g-public (ref null $public-parent) (ref.null none)) - (global $g-public (ref null $public-parent) (ref.null none)) - ;; CHECK: (global $use-public-child (ref null $public-child) (ref.null none)) - (global $use-public-child (ref null $public-child) (ref.null none)) - ;; CHECK: (global $use-public-grandchild (ref null $public-grandchild) (ref.null none)) - (global $use-public-grandchild (ref null $public-grandchild) (ref.null none)) - ;; CHECK: (global $use-unrelated-super (ref null $unrelated-super) (ref.null none)) - (global $use-unrelated-super (ref null $unrelated-super) (ref.null none)) - ;; CHECK: (global $use-unrelated-sub (ref null $unrelated-sub) (ref.null none)) - (global $use-unrelated-sub (ref null $unrelated-sub) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; When a type is exposed exactly, we can still optimize its subtypes. -(module - ;; CHECK: (type $public-parent (sub (struct))) - (type $public-parent (sub (struct))) - ;; CHECK: (rec - ;; CHECK-NEXT: (type $public-child (sub (struct))) - (type $public-child (sub $public-parent (struct))) - ;; CHECK: (type $public-grandchild (sub (struct))) - (type $public-grandchild (sub $public-child (struct))) - - ;; CHECK: (global $g-public (ref (exact $public-parent)) (struct.new_default $public-parent)) - (global $g-public (ref (exact $public-parent)) (struct.new_default $public-parent)) - ;; CHECK: (global $use-public-child (ref null $public-child) (ref.null none)) - (global $use-public-child (ref null $public-child) (ref.null none)) - ;; CHECK: (global $use-public-grandchild (ref null $public-grandchild) (ref.null none)) - (global $use-public-grandchild (ref null $public-grandchild) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; We cannot optimize a type referenced from a publicly exposed type, nor a -;; subtype of that type. -(module - ;; CHECK: (type $referenced-super (sub (struct))) - (type $referenced-super (sub (struct))) - ;; CHECK: (type $referenced (sub $referenced-super (struct))) - (type $referenced (sub $referenced-super (struct))) - ;; CHECK: (type $referenced-child (sub $referenced (struct))) - (type $referenced-child (sub $referenced (struct))) - ;; CHECK: (type $public (sub (struct (field (ref $referenced))))) - (type $public (sub (struct (field (ref $referenced))))) - - ;; CHECK: (global $g-public (ref null $public) (ref.null none)) - (global $g-public (ref null $public) (ref.null none)) - ;; CHECK: (global $use-referenced-super (ref null $referenced-super) (ref.null none)) - (global $use-referenced-super (ref null $referenced-super) (ref.null none)) - ;; CHECK: (global $use-referenced (ref null $referenced) (ref.null none)) - (global $use-referenced (ref null $referenced) (ref.null none)) - ;; CHECK: (global $use-referenced-child (ref null $referenced-child) (ref.null none)) - (global $use-referenced-child (ref null $referenced-child) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; Same, but with the public type exposed exactly. We still cannot optimize -;; subtypes of the reachable type. -(module - ;; CHECK: (type $referenced-super (sub (struct))) - (type $referenced-super (sub (struct))) - ;; CHECK: (type $referenced (sub $referenced-super (struct))) - (type $referenced (sub $referenced-super (struct))) - ;; CHECK: (type $referenced-child (sub $referenced (struct))) - (type $referenced-child (sub $referenced (struct))) - ;; CHECK: (type $public (sub (struct (field (ref null $referenced))))) - (type $public (sub (struct (field (ref null $referenced))))) - - ;; CHECK: (global $g-public (ref (exact $public)) (struct.new_default $public)) - (global $g-public (ref (exact $public)) (struct.new_default $public)) - ;; CHECK: (global $use-referenced-super (ref null $referenced-super) (ref.null none)) - (global $use-referenced-super (ref null $referenced-super) (ref.null none)) - ;; CHECK: (global $use-referenced (ref null $referenced) (ref.null none)) - (global $use-referenced (ref null $referenced) (ref.null none)) - ;; CHECK: (global $use-referenced-child (ref null $referenced-child) (ref.null none)) - (global $use-referenced-child (ref null $referenced-child) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; When the referenced type is referenced exactly, we still cannot optimize it, -;; but we can optimize its subtypes. -(module - ;; CHECK: (type $referenced-super (sub (struct))) - (type $referenced-super (sub (struct))) - ;; CHECK: (type $referenced (sub $referenced-super (struct))) - (type $referenced (sub $referenced-super (struct))) - ;; CHECK: (rec - ;; CHECK-NEXT: (type $referenced-child (sub (struct))) - (type $referenced-child (sub $referenced (struct))) - ;; CHECK: (type $3 (struct)) - - ;; CHECK: (type $public (sub (struct (field (ref (exact $referenced)))))) - (type $public (sub (struct (field (ref (exact $referenced)))))) - - ;; CHECK: (global $g-public (ref null $public) (ref.null none)) - (global $g-public (ref null $public) (ref.null none)) - ;; CHECK: (global $use-referenced-super (ref null $referenced-super) (ref.null none)) - (global $use-referenced-super (ref null $referenced-super) (ref.null none)) - ;; CHECK: (global $use-referenced (ref null $referenced) (ref.null none)) - (global $use-referenced (ref null $referenced) (ref.null none)) - ;; CHECK: (global $use-referenced-child (ref null $referenced-child) (ref.null none)) - (global $use-referenced-child (ref null $referenced-child) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; When func is exposed indirectly because it is referenced by a publicly -;; exposed type, we still cannot optimize any function types. -(module - ;; CHECK: (rec - ;; CHECK-NEXT: (type $super-struct (sub (struct))) - (type $super-struct (sub (struct))) - ;; CHECK: (type $sub-struct (sub (struct))) - (type $sub-struct (sub $super-struct (struct))) - ;; CHECK: (type $super-func (sub (func))) - (type $super-func (sub (func))) - ;; CHECK: (type $sub-func (sub $super-func (func))) - (type $sub-func (sub $super-func (func))) - ;; CHECK: (type $public (sub (struct (field funcref)))) - (type $public (sub (struct (field funcref)))) - - ;; CHECK: (global $g-public (ref null $public) (ref.null none)) - (global $g-public (ref null $public) (ref.null none)) - ;; CHECK: (global $use-super-struct (ref null $super-struct) (ref.null none)) - (global $use-super-struct (ref null $super-struct) (ref.null none)) - ;; CHECK: (global $use-sub-struct (ref null $sub-struct) (ref.null none)) - (global $use-sub-struct (ref null $sub-struct) (ref.null none)) - ;; CHECK: (global $use-super-func (ref null $super-func) (ref.null nofunc)) - (global $use-super-func (ref null $super-func) (ref.null nofunc)) - ;; CHECK: (global $use-sub-func (ref null $sub-func) (ref.null nofunc)) - (global $use-sub-func (ref null $sub-func) (ref.null nofunc)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; We cannot optimize the supertype of a publicly exposed type, but we can -;; optimize other subtypes of the supertype. -(module - ;; CHECK: (type $super-super (sub (struct))) - (type $super-super (sub (struct))) - ;; CHECK: (type $super (sub $super-super (struct))) - (type $super (sub $super-super (struct))) - ;; CHECK: (type $public (sub $super (struct))) - (type $public (sub $super (struct))) - ;; CHECK: (type $sibling-private (sub (struct (field i32)))) - (type $sibling-private (sub $super (struct (field i32)))) - - ;; CHECK: (global $g-public (ref null $public) (ref.null none)) - (global $g-public (ref null $public) (ref.null none)) - ;; CHECK: (global $use-super-super (ref null $super-super) (ref.null none)) - (global $use-super-super (ref null $super-super) (ref.null none)) - ;; CHECK: (global $use-super (ref null $super) (ref.null none)) - (global $use-super (ref null $super) (ref.null none)) - ;; CHECK: (global $use-sibling-private (ref null $sibling-private) (ref.null none)) - (global $use-sibling-private (ref null $sibling-private) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; We cannot optimize the descriptor of a publicly exposed type, nor subtypes of -;; that descriptor. -(module - (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $public-super (sub (descriptor $desc-super) (struct))) - (type $public-super (sub (descriptor $desc-super) (struct))) - ;; CHECK: (type $desc-super (sub (describes $public-super) (struct))) - (type $desc-super (sub (describes $public-super) (struct))) - ) - (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $public (sub $public-super (descriptor $desc) (struct))) - (type $public (sub $public-super (descriptor $desc) (struct))) - ;; CHECK: (type $desc (sub $desc-super (describes $public) (struct))) - (type $desc (sub $desc-super (describes $public) (struct))) - ) - (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $public-child (sub $public (descriptor $desc-child) (struct))) - (type $public-child (sub $public (descriptor $desc-child) (struct))) - ;; CHECK: (type $desc-child (sub $desc (describes $public-child) (struct))) - (type $desc-child (sub $desc (describes $public-child) (struct))) - ) - - ;; CHECK: (global $g-public (ref null $public) (ref.null none)) - (global $g-public (ref null $public) (ref.null none)) - ;; CHECK: (global $use-public-super (ref null $public-super) (ref.null none)) - (global $use-public-super (ref null $public-super) (ref.null none)) - ;; CHECK: (global $use-desc-super (ref null $desc-super) (ref.null none)) - (global $use-desc-super (ref null $desc-super) (ref.null none)) - ;; CHECK: (global $use-public-child (ref null $public-child) (ref.null none)) - (global $use-public-child (ref null $public-child) (ref.null none)) - ;; CHECK: (global $use-desc (ref null $desc) (ref.null none)) - (global $use-desc (ref null $desc) (ref.null none)) - ;; CHECK: (global $use-desc-child (ref null $desc-child) (ref.null none)) - (global $use-desc-child (ref null $desc-child) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; When the publicly exposed type is exact, we cannot optimize its descriptor, -;; but we can optimize subtypes of that descriptor. -(module - (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $public-super (sub (descriptor $desc-super) (struct))) - (type $public-super (sub (descriptor $desc-super) (struct))) - ;; CHECK: (type $desc-super (sub (describes $public-super) (struct))) - (type $desc-super (sub (describes $public-super) (struct))) - ) - (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $public (sub $public-super (descriptor $desc) (struct))) - (type $public (sub $public-super (descriptor $desc) (struct))) - ;; CHECK: (type $desc (sub $desc-super (describes $public) (struct))) - (type $desc (sub $desc-super (describes $public) (struct))) - ) - (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $public-child (sub (struct))) - (type $public-child (sub $public (descriptor $desc-child) (struct))) - ;; CHECK: (type $desc-child (sub (struct))) - (type $desc-child (sub $desc (describes $public-child) (struct))) - ) - - ;; CHECK: (global $g-public (ref (exact $public)) (struct.new_default_desc $public - ;; CHECK-NEXT: (struct.new_default $desc) - ;; CHECK-NEXT: )) - (global $g-public (ref (exact $public)) (struct.new_default_desc $public (struct.new_default $desc))) - ;; CHECK: (global $use-public-super (ref null $public-super) (ref.null none)) - (global $use-public-super (ref null $public-super) (ref.null none)) - ;; CHECK: (global $use-desc-super (ref null $desc-super) (ref.null none)) - (global $use-desc-super (ref null $desc-super) (ref.null none)) - ;; CHECK: (global $use-public-child (ref null $public-child) (ref.null none)) - (global $use-public-child (ref null $public-child) (ref.null none)) - ;; CHECK: (global $use-desc-child (ref null $desc-child) (ref.null none)) - (global $use-desc-child (ref null $desc-child) (ref.null none)) - ;; CHECK: (global $use-desc (ref null $desc) (ref.null none)) - (global $use-desc (ref null $desc) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; We cannot optimize the described type of a publicly exposed type. We could -;; optimize subtypes of that described type, except that they are necessarily in -;; the same rec group as exposed subtypes of the publicly exposed type. -(module - (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $described-super (sub (descriptor $public-desc-super) (struct))) - (type $described-super (sub (descriptor $public-desc-super) (struct))) - ;; CHECK: (type $public-desc-super (sub (describes $described-super) (struct))) - (type $public-desc-super (sub (describes $described-super) (struct))) - ) - (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $described (sub $described-super (descriptor $public-desc) (struct))) - (type $described (sub $described-super (descriptor $public-desc) (struct))) - ;; CHECK: (type $public-desc (sub $public-desc-super (describes $described) (struct))) - (type $public-desc (sub $public-desc-super (describes $described) (struct))) - ) - (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $described-child (sub $described (descriptor $desc-child) (struct))) - (type $described-child (sub $described (descriptor $desc-child) (struct))) - ;; CHECK: (type $desc-child (sub $public-desc (describes $described-child) (struct))) - (type $desc-child (sub $public-desc (describes $described-child) (struct))) - ) - - ;; CHECK: (global $g-public (ref null $public-desc) (ref.null none)) - (global $g-public (ref null $public-desc) (ref.null none)) - ;; CHECK: (global $use-described-super (ref null $described-super) (ref.null none)) - (global $use-described-super (ref null $described-super) (ref.null none)) - ;; CHECK: (global $use-public-desc-super (ref null $public-desc-super) (ref.null none)) - (global $use-public-desc-super (ref null $public-desc-super) (ref.null none)) - ;; CHECK: (global $use-described-child (ref null $described-child) (ref.null none)) - (global $use-described-child (ref null $described-child) (ref.null none)) - ;; CHECK: (global $use-desc-child (ref null $desc-child) (ref.null none)) - (global $use-desc-child (ref null $desc-child) (ref.null none)) - ;; CHECK: (global $use-described (ref null $described) (ref.null none)) - (global $use-described (ref null $described) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; We can still optimize subtypes of the described type when the publicly -;; exposed descriptor is exact. -(module - (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $described-super (sub (descriptor $public-desc-super) (struct))) - (type $described-super (sub (descriptor $public-desc-super) (struct))) - ;; CHECK: (type $public-desc-super (sub (describes $described-super) (struct))) - (type $public-desc-super (sub (describes $described-super) (struct))) - ) - (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $described (sub $described-super (descriptor $public-desc) (struct))) - (type $described (sub $described-super (descriptor $public-desc) (struct))) - ;; CHECK: (type $public-desc (sub $public-desc-super (describes $described) (struct))) - (type $public-desc (sub $public-desc-super (describes $described) (struct))) - ) - (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $described-child (sub (struct))) - (type $described-child (sub $described (descriptor $desc-child) (struct))) - ;; CHECK: (type $desc-child (sub (struct))) - (type $desc-child (sub $public-desc (describes $described-child) (struct))) - ) - - ;; CHECK: (global $g-public (ref (exact $public-desc)) (struct.new_default $public-desc)) - (global $g-public (ref (exact $public-desc)) (struct.new_default $public-desc)) - ;; CHECK: (global $use-described-super (ref null $described-super) (ref.null none)) - (global $use-described-super (ref null $described-super) (ref.null none)) - ;; CHECK: (global $use-public-desc-super (ref null $public-desc-super) (ref.null none)) - (global $use-public-desc-super (ref null $public-desc-super) (ref.null none)) - ;; CHECK: (global $use-described-child (ref null $described-child) (ref.null none)) - (global $use-described-child (ref null $described-child) (ref.null none)) - ;; CHECK: (global $use-desc-child (ref null $desc-child) (ref.null none)) - (global $use-desc-child (ref null $desc-child) (ref.null none)) - ;; CHECK: (global $use-described (ref null $described) (ref.null none)) - (global $use-described (ref null $described) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; We cannot optimize rec groups siblings of a public type, but we can optimize -;; subtypes of those siblings. -(module - ;; CHECK: (type $sibling-super (sub (struct))) - (type $sibling-super (sub (struct))) - (rec - ;; CHECK: (rec - ;; CHECK-NEXT: (type $public (sub (struct))) - (type $public (sub (struct))) - ;; CHECK: (type $sibling (sub $sibling-super (struct (field i32)))) - (type $sibling (sub $sibling-super (struct (field i32)))) - ) - ;; CHECK: (type $sibling-child (sub (struct (field i32) (field f32)))) - (type $sibling-child (sub $sibling (struct i32 f32))) - - ;; CHECK: (global $g-public (ref null $public) (ref.null none)) - (global $g-public (ref null $public) (ref.null none)) - ;; CHECK: (global $use-sibling-super (ref null $sibling-super) (ref.null none)) - (global $use-sibling-super (ref null $sibling-super) (ref.null none)) - ;; CHECK: (global $use-sibling (ref null $sibling) (ref.null none)) - (global $use-sibling (ref null $sibling) (ref.null none)) - ;; CHECK: (global $use-sibling-child (ref null $sibling-child) (ref.null none)) - (global $use-sibling-child (ref null $sibling-child) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; Continuation function type is not exposed. We can optimize its subtypes and -;; the subtypes of other types it references. -(module - ;; CHECK: (type $other-super (sub (struct))) - (type $other-super (sub (struct))) - ;; CHECK: (type $other (sub $other-super (struct))) - (type $other (sub $other-super (struct))) - ;; CHECK: (rec - ;; CHECK-NEXT: (type $other-child (sub (struct))) - (type $other-child (sub $other (struct))) - - ;; CHECK: (type $func-sub-child (sub (func (result (ref null $other))))) - - ;; CHECK: (type $func-super (sub (func (result (ref null $other-super))))) - (type $func-super (sub (func (result (ref null $other-super))))) - ;; CHECK: (type $func-sub (sub $func-super (func (result (ref null $other))))) - (type $func-sub (sub $func-super (func (result (ref null $other))))) - (type $func-sub-child (sub $func-sub (func (result (ref null $other))))) - ;; CHECK: (type $cont (sub (cont $func-sub))) - (type $cont (sub (cont $func-sub))) - - ;; CHECK: (global $g-cont (ref null $cont) (ref.null nocont)) - (global $g-cont (ref null $cont) (ref.null nocont)) - ;; CHECK: (global $use-func-super (ref null $func-super) (ref.null nofunc)) - (global $use-func-super (ref null $func-super) (ref.null nofunc)) - ;; CHECK: (global $use-func-sub (ref null $func-sub) (ref.null nofunc)) - (global $use-func-sub (ref null $func-sub) (ref.null nofunc)) - ;; CHECK: (global $use-func-sub-child (ref null $func-sub-child) (ref.null nofunc)) - (global $use-func-sub-child (ref null $func-sub-child) (ref.null nofunc)) - ;; CHECK: (global $use-other-super (ref null $other-super) (ref.null none)) - (global $use-other-super (ref null $other-super) (ref.null none)) - ;; CHECK: (global $use-other (ref null $other) (ref.null none)) - (global $use-other (ref null $other) (ref.null none)) - ;; CHECK: (global $use-other-child (ref null $other-child) (ref.null none)) - (global $use-other-child (ref null $other-child) (ref.null none)) - ;; CHECK: (export "g-cont" (global $g-cont)) - (export "g-cont" (global $g-cont)) -) - -;; structref is public. We can optimize arrays, functions, and continuations. -(module - ;; CHECK: (type $struct-super (sub (struct))) - (type $struct-super (sub (struct))) - ;; CHECK: (type $struct-sub (sub $struct-super (struct))) - (type $struct-sub (sub $struct-super (struct))) - ;; CHECK: (type $struct-sub-child (sub $struct-sub (struct))) - (type $struct-sub-child (sub $struct-sub (struct))) - - ;; CHECK: (rec - ;; CHECK-NEXT: (type $array-super (sub (array i32))) - (type $array-super (sub (array i32))) - ;; CHECK: (type $array-sub (sub (array i32))) - (type $array-sub (sub $array-super (array i32))) - - ;; CHECK: (type $func-super (sub (func))) - (type $func-super (sub (func))) - ;; CHECK: (type $func-sub (sub (func))) - (type $func-sub (sub $func-super (func))) - - ;; CHECK: (type $cont-super (sub (cont $func-super))) - (type $cont-super (sub (cont $func-super))) - ;; CHECK: (type $cont-sub (sub (cont $func-super))) - (type $cont-sub (sub $cont-super (cont $func-super))) - - ;; CHECK: (global $g (mut structref) (ref.null none)) - (global $g (mut structref) (ref.null none)) - ;; CHECK: (global $use-struct-super (ref null $struct-super) (ref.null none)) - (global $use-struct-super (ref null $struct-super) (ref.null none)) - ;; CHECK: (global $use-struct-sub (ref null $struct-sub) (ref.null none)) - (global $use-struct-sub (ref null $struct-sub) (ref.null none)) - ;; CHECK: (global $use-struct-sub-child (ref null $struct-sub-child) (ref.null none)) - (global $use-struct-sub-child (ref null $struct-sub-child) (ref.null none)) - ;; CHECK: (global $use-array-super (ref null $array-super) (ref.null none)) - (global $use-array-super (ref null $array-super) (ref.null none)) - ;; CHECK: (global $use-array-sub (ref null $array-sub) (ref.null none)) - (global $use-array-sub (ref null $array-sub) (ref.null none)) - ;; CHECK: (global $use-func-super (ref null $func-super) (ref.null nofunc)) - (global $use-func-super (ref null $func-super) (ref.null nofunc)) - ;; CHECK: (global $use-func-sub (ref null $func-sub) (ref.null nofunc)) - (global $use-func-sub (ref null $func-sub) (ref.null nofunc)) - ;; CHECK: (global $use-cont-super (ref null $cont-super) (ref.null nocont)) - (global $use-cont-super (ref null $cont-super) (ref.null nocont)) - ;; CHECK: (global $use-cont-sub (ref null $cont-sub) (ref.null nocont)) - (global $use-cont-sub (ref null $cont-sub) (ref.null nocont)) - ;; CHECK: (export "g" (global $g)) - (export "g" (global $g)) -) - -;; A is exposed and has an exact reference to B, which has an inexact reference -;; to C. Subtypes of B can be optimized, but not subtypes of A or C. -(module - ;; CHECK: (type $C-super (sub (struct))) - (type $C-super (sub (struct))) - ;; CHECK: (type $C (sub $C-super (struct))) - (type $C (sub $C-super (struct))) - ;; CHECK: (type $C-child (sub $C (struct))) - (type $C-child (sub $C (struct))) - - (type $B-super (sub (struct))) - ;; CHECK: (type $B (sub $C-super (struct (field (ref $C))))) - (type $B (sub $B-super (struct (field (ref $C))))) - ;; CHECK: (type $B-child (sub (struct (field (ref $C))))) - (type $B-child (sub $B (struct (field (ref $C))))) - - (type $A-super (sub (struct))) - ;; CHECK: (type $A (sub $C-super (struct (field (ref (exact $B)))))) - (type $A (sub $A-super (struct (field (ref (exact $B)))))) - ;; CHECK: (type $A-child (sub $A (struct (field (ref (exact $B)))))) - (type $A-child (sub $A (struct (field (ref (exact $B)))))) - - ;; CHECK: (global $g-public (ref null $A) (ref.null none)) - (global $g-public (ref null $A) (ref.null none)) - ;; CHECK: (global $use-A-super (ref null $C-super) (ref.null none)) - (global $use-A-super (ref null $A-super) (ref.null none)) - ;; CHECK: (global $use-A-child (ref null $A-child) (ref.null none)) - (global $use-A-child (ref null $A-child) (ref.null none)) - ;; CHECK: (global $use-B-super (ref null $C-super) (ref.null none)) - (global $use-B-super (ref null $B-super) (ref.null none)) - ;; CHECK: (global $use-B-child (ref null $B-child) (ref.null none)) - (global $use-B-child (ref null $B-child) (ref.null none)) - ;; CHECK: (global $use-C-super (ref null $C-super) (ref.null none)) - (global $use-C-super (ref null $C-super) (ref.null none)) - ;; CHECK: (global $use-C-child (ref null $C-child) (ref.null none)) - (global $use-C-child (ref null $C-child) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; Array type exposed inexactly. We cannot optimize its subtypes. -(module - ;; CHECK: (type $array-super (sub (array (mut i32)))) - (type $array-super (sub (array (mut i32)))) - ;; CHECK: (type $array-sub (sub $array-super (array (mut i32)))) - (type $array-sub (sub $array-super (array (mut i32)))) - ;; CHECK: (type $array-sub-child (sub $array-sub (array (mut i32)))) - (type $array-sub-child (sub $array-sub (array (mut i32)))) - - ;; CHECK: (global $g (ref null $array-sub) (ref.null none)) - (global $g (ref null $array-sub) (ref.null none)) - ;; CHECK: (global $use-super (ref null $array-super) (ref.null none)) - (global $use-super (ref null $array-super) (ref.null none)) - ;; CHECK: (global $use-child (ref null $array-sub-child) (ref.null none)) - (global $use-child (ref null $array-sub-child) (ref.null none)) - ;; CHECK: (export "g" (global $g)) - (export "g" (global $g)) -) - -;; Array type exposed exactly. We can optimize its subtypes. -(module - ;; CHECK: (type $array-super (sub (array (mut i32)))) - (type $array-super (sub (array (mut i32)))) - ;; CHECK: (type $array-sub (sub $array-super (array (mut i32)))) - (type $array-sub (sub $array-super (array (mut i32)))) - ;; CHECK: (rec - ;; CHECK-NEXT: (type $array-sub-child (sub (array (mut i32)))) - (type $array-sub-child (sub $array-sub (array (mut i32)))) - - ;; CHECK: (type $3 (struct)) - - ;; CHECK: (global $g (ref (exact $array-sub)) (array.new_default $array-sub - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: )) - (global $g (ref (exact $array-sub)) (array.new_default $array-sub (i32.const 1))) - ;; CHECK: (global $use-super (ref null $array-super) (ref.null none)) - (global $use-super (ref null $array-super) (ref.null none)) - ;; CHECK: (global $use-child (ref null $array-sub-child) (ref.null none)) - (global $use-child (ref null $array-sub-child) (ref.null none)) - ;; CHECK: (export "g" (global $g)) - (export "g" (global $g)) -) - -;; Array element type is inexact. We cannot optimize its subtypes. -(module - ;; CHECK: (type $element-super (sub (struct))) - (type $element-super (sub (struct))) - ;; CHECK: (type $element (sub $element-super (struct))) - (type $element (sub $element-super (struct))) - ;; CHECK: (type $element-child (sub $element (struct))) - (type $element-child (sub $element (struct))) - ;; CHECK: (type $array (sub (array (ref $element)))) - (type $array (sub (array (ref $element)))) - - ;; CHECK: (global $g (ref null $array) (ref.null none)) - (global $g (ref null $array) (ref.null none)) - ;; CHECK: (global $use-element-super (ref null $element-super) (ref.null none)) - (global $use-element-super (ref null $element-super) (ref.null none)) - ;; CHECK: (global $use-element (ref null $element) (ref.null none)) - (global $use-element (ref null $element) (ref.null none)) - ;; CHECK: (global $use-element-child (ref null $element-child) (ref.null none)) - (global $use-element-child (ref null $element-child) (ref.null none)) - ;; CHECK: (export "g" (global $g)) - (export "g" (global $g)) -) - -;; Array element type is exact. We can optimize its subtypes. -(module - ;; CHECK: (type $element-super (sub (struct))) - (type $element-super (sub (struct))) - ;; CHECK: (type $element (sub $element-super (struct))) - (type $element (sub $element-super (struct))) - ;; CHECK: (rec - ;; CHECK-NEXT: (type $element-child (sub (struct))) - (type $element-child (sub $element (struct))) - ;; CHECK: (type $3 (struct)) - - ;; CHECK: (type $array (sub (array (ref (exact $element))))) - (type $array (sub (array (ref (exact $element))))) - - ;; CHECK: (global $g (ref null $array) (ref.null none)) - (global $g (ref null $array) (ref.null none)) - ;; CHECK: (global $use-element-super (ref null $element-super) (ref.null none)) - (global $use-element-super (ref null $element-super) (ref.null none)) - ;; CHECK: (global $use-element (ref null $element) (ref.null none)) - (global $use-element (ref null $element) (ref.null none)) - ;; CHECK: (global $use-element-child (ref null $element-child) (ref.null none)) - (global $use-element-child (ref null $element-child) (ref.null none)) - ;; CHECK: (export "g" (global $g)) - (export "g" (global $g)) -) - -;; Table type exposed inexactly. We cannot optimize its subtypes. -(module - ;; CHECK: (type $T-super (sub (struct))) - (type $T-super (sub (struct))) - ;; CHECK: (type $T (sub $T-super (struct))) - (type $T (sub $T-super (struct))) - ;; CHECK: (type $T-child (sub $T (struct))) - (type $T-child (sub $T (struct))) - - (import "env" "table" (table 1 (ref null $T))) - ;; CHECK: (import "env" "table" (table $timport$0 1 (ref null $T))) - - ;; CHECK: (global $use-super (ref null $T-super) (ref.null none)) - (global $use-super (ref null $T-super) (ref.null none)) - ;; CHECK: (global $use-child (ref null $T-child) (ref.null none)) - (global $use-child (ref null $T-child) (ref.null none)) -) - -;; Table type exposed exactly. We can optimize its subtypes. -(module - ;; CHECK: (type $T-super (sub (struct))) - (type $T-super (sub (struct))) - ;; CHECK: (type $T (sub $T-super (struct))) - (type $T (sub $T-super (struct))) - ;; CHECK: (rec - ;; CHECK-NEXT: (type $T-child (sub (struct))) - (type $T-child (sub $T (struct))) - - (import "env" "table" (table 1 (ref (exact $T)))) - ;; CHECK: (type $3 (struct)) - - ;; CHECK: (import "env" "table" (table $timport$0 1 (ref (exact $T)))) - - ;; CHECK: (global $use-super (ref null $T-super) (ref.null none)) - (global $use-super (ref null $T-super) (ref.null none)) - ;; CHECK: (global $use-child (ref null $T-child) (ref.null none)) - (global $use-child (ref null $T-child) (ref.null none)) -) - -;; Tag type and its children are exposed inexactly. We cannot optimize their -;; subtypes. -(module - ;; CHECK: (type $param-super (sub (struct))) - (type $param-super (sub (struct))) - ;; CHECK: (type $param (sub $param-super (struct))) - (type $param (sub $param-super (struct))) - ;; CHECK: (type $param-child (sub $param (struct))) - (type $param-child (sub $param (struct))) - ;; CHECK: (type $sig (sub (func (param (ref $param))))) - (type $sig (sub (func (param (ref $param))))) - - ;; CHECK: (global $use-param-super (ref null $param-super) (ref.null none)) - - ;; CHECK: (global $use-param-child (ref null $param-child) (ref.null none)) - - ;; CHECK: (tag $t (type $sig) (param (ref $param))) - (tag $t (type $sig)) - (global $use-param-super (ref null $param-super) (ref.null none)) - (global $use-param-child (ref null $param-child) (ref.null none)) - ;; CHECK: (export "t" (tag $t)) - (export "t" (tag $t)) -) - -;; Function signature exact vs inexact params and results. None of the params or -;; results can be optimized, but the subtypes of the exactly referenced params -;; and results can. -(module - ;; CHECK: (type $param-exact-super (sub (struct (field i32)))) - (type $param-exact-super (sub (struct (field i32)))) - ;; CHECK: (type $param-exact (sub $param-exact-super (struct (field i32) (field i32)))) - (type $param-exact (sub $param-exact-super (struct (field i32 i32)))) - ;; CHECK: (rec - ;; CHECK-NEXT: (type $param-exact-child (sub (struct (field i32) (field i32) (field f32)))) - (type $param-exact-child (sub $param-exact (struct (field i32 i32 f32)))) - - ;; CHECK: (type $result-exact-child (sub (struct (field f32) (field f32) (field i32)))) - - ;; CHECK: (type $param-inexact-super (sub (struct (field i64)))) - (type $param-inexact-super (sub (struct (field i64)))) - ;; CHECK: (type $param-inexact (sub $param-inexact-super (struct (field i64) (field i64)))) - (type $param-inexact (sub $param-inexact-super (struct (field i64 i64)))) - ;; CHECK: (type $param-inexact-child (sub $param-inexact (struct (field i64) (field i64) (field f64)))) - (type $param-inexact-child (sub $param-inexact (struct (field i64 i64 f64)))) - - ;; CHECK: (type $result-exact-super (sub (struct (field f32)))) - (type $result-exact-super (sub (struct (field f32)))) - ;; CHECK: (type $result-exact (sub $result-exact-super (struct (field f32) (field f32)))) - (type $result-exact (sub $result-exact-super (struct (field f32 f32)))) - (type $result-exact-child (sub $result-exact (struct (field f32 f32 i32)))) - - ;; CHECK: (type $result-inexact-super (sub (struct (field f64)))) - (type $result-inexact-super (sub (struct (field f64)))) - ;; CHECK: (type $result-inexact (sub $result-inexact-super (struct (field f64) (field f64)))) - (type $result-inexact (sub $result-inexact-super (struct (field f64 f64)))) - ;; CHECK: (type $result-inexact-child (sub $result-inexact (struct (field f64) (field f64) (field i64)))) - (type $result-inexact-child (sub $result-inexact (struct (field f64 f64 i64)))) - - ;; CHECK: (type $sig (sub (func (param (ref (exact $param-exact)) (ref $param-inexact)) (result (ref (exact $result-exact)) (ref $result-inexact))))) - (type $sig (sub (func (param (ref (exact $param-exact)) (ref $param-inexact)) (result (ref (exact $result-exact)) (ref $result-inexact))))) - - - ;; CHECK: (global $use-param-exact-super (ref null $param-exact-super) (ref.null none)) - (global $use-param-exact-super (ref null $param-exact-super) (ref.null none)) - ;; CHECK: (global $use-param-exact-child (ref null $param-exact-child) (ref.null none)) - (global $use-param-exact-child (ref null $param-exact-child) (ref.null none)) - ;; CHECK: (global $use-param-inexact-super (ref null $param-inexact-super) (ref.null none)) - (global $use-param-inexact-super (ref null $param-inexact-super) (ref.null none)) - ;; CHECK: (global $use-param-inexact-child (ref null $param-inexact-child) (ref.null none)) - (global $use-param-inexact-child (ref null $param-inexact-child) (ref.null none)) - ;; CHECK: (global $use-result-exact-super (ref null $result-exact-super) (ref.null none)) - (global $use-result-exact-super (ref null $result-exact-super) (ref.null none)) - ;; CHECK: (global $use-result-exact-child (ref null $result-exact-child) (ref.null none)) - (global $use-result-exact-child (ref null $result-exact-child) (ref.null none)) - ;; CHECK: (global $use-result-inexact-super (ref null $result-inexact-super) (ref.null none)) - (global $use-result-inexact-super (ref null $result-inexact-super) (ref.null none)) - ;; CHECK: (global $use-result-inexact-child (ref null $result-inexact-child) (ref.null none)) - (global $use-result-inexact-child (ref null $result-inexact-child) (ref.null none)) - ;; CHECK: (export "f" (func $f)) - (export "f" (func $f)) - ;; CHECK: (func $f (type $sig) (param $0 (ref (exact $param-exact))) (param $1 (ref $param-inexact)) (result (ref (exact $result-exact)) (ref $result-inexact)) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - (func $f (type $sig) (param (ref (exact $param-exact)) (ref $param-inexact)) (result (ref (exact $result-exact)) (ref $result-inexact)) - (unreachable) - ) -) - -;; contref is public. We can optimize structs, arrays, and functions. -(module - ;; CHECK: (rec - ;; CHECK-NEXT: (type $struct-super (sub (struct))) - (type $struct-super (sub (struct))) - ;; CHECK: (type $struct-sub (sub (struct))) - (type $struct-sub (sub $struct-super (struct))) - - ;; CHECK: (type $array-super (sub (array i32))) - (type $array-super (sub (array i32))) - ;; CHECK: (type $array-sub (sub (array i32))) - (type $array-sub (sub $array-super (array i32))) - - ;; CHECK: (type $func-sub (sub (func))) - - ;; CHECK: (type $func-super (sub (func))) - (type $func-super (sub (func))) - (type $func-sub (sub $func-super (func))) - - ;; CHECK: (type $cont-super (sub (cont $func-super))) - (type $cont-super (sub (cont $func-super))) - ;; CHECK: (type $cont-sub (sub $cont-super (cont $func-super))) - (type $cont-sub (sub $cont-super (cont $func-super))) - ;; CHECK: (type $cont-sub-child (sub $cont-sub (cont $func-super))) - (type $cont-sub-child (sub $cont-sub (cont $func-super))) - - ;; CHECK: (global $g (mut contref) (ref.null nocont)) - (global $g (mut contref) (ref.null nocont)) - ;; CHECK: (global $use-struct-super (ref null $struct-super) (ref.null none)) - (global $use-struct-super (ref null $struct-super) (ref.null none)) - ;; CHECK: (global $use-struct-sub (ref null $struct-sub) (ref.null none)) - (global $use-struct-sub (ref null $struct-sub) (ref.null none)) - ;; CHECK: (global $use-array-super (ref null $array-super) (ref.null none)) - (global $use-array-super (ref null $array-super) (ref.null none)) - ;; CHECK: (global $use-array-sub (ref null $array-sub) (ref.null none)) - (global $use-array-sub (ref null $array-sub) (ref.null none)) - ;; CHECK: (global $use-func-super (ref null $func-super) (ref.null nofunc)) - (global $use-func-super (ref null $func-super) (ref.null nofunc)) - ;; CHECK: (global $use-func-sub (ref null $func-sub) (ref.null nofunc)) - (global $use-func-sub (ref null $func-sub) (ref.null nofunc)) - ;; CHECK: (global $use-cont-super (ref null $cont-super) (ref.null nocont)) - (global $use-cont-super (ref null $cont-super) (ref.null nocont)) - ;; CHECK: (global $use-cont-sub (ref null $cont-sub) (ref.null nocont)) - (global $use-cont-sub (ref null $cont-sub) (ref.null nocont)) - ;; CHECK: (global $use-cont-sub-child (ref null $cont-sub-child) (ref.null nocont)) - (global $use-cont-sub-child (ref null $cont-sub-child) (ref.null nocont)) - ;; CHECK: (export "g" (global $g)) - (export "g" (global $g)) -) - -;; A is exposed exactly and has an inexact reference to B, which has an exact -;; reference to C. Subtypes of A and C but not B can be optimized. -(module - ;; CHECK: (type $C-super (sub (struct))) - (type $C-super (sub (struct))) - ;; CHECK: (type $C (sub $C-super (struct))) - (type $C (sub $C-super (struct))) - ;; CHECK: (type $B (sub $C-super (struct (field (ref null (exact $C)))))) - - ;; CHECK: (rec - ;; CHECK-NEXT: (type $C-child (sub (struct))) - (type $C-child (sub $C (struct))) - - (type $B-super (sub (struct))) - (type $B (sub $B-super (struct (field (ref null (exact $C)))))) - ;; CHECK: (type $A-child (sub (struct (field (ref null $B))))) - - ;; CHECK: (type $B-child (sub $B (struct (field (ref null (exact $C)))))) - (type $B-child (sub $B (struct (field (ref null (exact $C)))))) - - (type $A-super (sub (struct))) - ;; CHECK: (type $A (sub $C-super (struct (field (ref null $B))))) - (type $A (sub $A-super (struct (field (ref null $B))))) - (type $A-child (sub $A (struct (field (ref null $B))))) - - ;; CHECK: (global $g-public (ref (exact $A)) (struct.new_default $A)) - (global $g-public (ref (exact $A)) (struct.new_default $A)) - ;; CHECK: (global $use-A-super (ref null $C-super) (ref.null none)) - (global $use-A-super (ref null $A-super) (ref.null none)) - ;; CHECK: (global $use-A-child (ref null $A-child) (ref.null none)) - (global $use-A-child (ref null $A-child) (ref.null none)) - ;; CHECK: (global $use-B-super (ref null $C-super) (ref.null none)) - (global $use-B-super (ref null $B-super) (ref.null none)) - ;; CHECK: (global $use-B-child (ref null $B-child) (ref.null none)) - (global $use-B-child (ref null $B-child) (ref.null none)) - ;; CHECK: (global $use-C-super (ref null $C-super) (ref.null none)) - (global $use-C-super (ref null $C-super) (ref.null none)) - ;; CHECK: (global $use-C-child (ref null $C-child) (ref.null none)) - (global $use-C-child (ref null $C-child) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -) - -;; A is exposed, making A-super and B public. The inexact reference from A-super -;; to B does not prevent B-child from being optimized, nor does it prevent the -;; subtypes of C (referenced inexactly from B) from being optimized. -(module - ;; CHECK: (type $C-super (sub (struct))) - (type $C-super (sub (struct))) - ;; CHECK: (type $C (sub $C-super (struct))) - (type $C (sub $C-super (struct))) - ;; CHECK: (rec - ;; CHECK-NEXT: (type $C-child (sub (struct))) - (type $C-child (sub $C (struct))) - - ;; CHECK: (type $B-child (sub (struct (field (ref null $C))))) - - ;; CHECK: (type $B-super (sub (struct (field (ref null $C))))) - (type $B-super (sub (struct (field (ref null $C))))) - ;; CHECK: (type $B (sub $B-super (struct (field (ref null $C))))) - (type $B (sub $B-super (struct (field (ref null $C))))) - (type $B-child (sub $B (struct (field (ref null $C))))) - - ;; CHECK: (type $A-super (sub (struct (field (ref null $B))))) - (type $A-super (sub (struct (field (ref null $B))))) - ;; CHECK: (type $A (sub $A-super (struct (field nullref)))) - (type $A (sub $A-super (struct (field (ref null none))))) - - ;; CHECK: (global $g-public (ref null $A) (ref.null none)) - (global $g-public (ref null $A) (ref.null none)) - ;; CHECK: (global $use-A-super (ref null $A-super) (ref.null none)) - (global $use-A-super (ref null $A-super) (ref.null none)) - ;; CHECK: (global $use-B-super (ref null $B-super) (ref.null none)) - (global $use-B-super (ref null $B-super) (ref.null none)) - ;; CHECK: (global $use-B (ref null $B) (ref.null none)) - (global $use-B (ref null $B) (ref.null none)) - ;; CHECK: (global $use-B-child (ref null $B-child) (ref.null none)) - (global $use-B-child (ref null $B-child) (ref.null none)) - ;; CHECK: (global $use-C-super (ref null $C-super) (ref.null none)) - (global $use-C-super (ref null $C-super) (ref.null none)) - ;; CHECK: (global $use-C (ref null $C) (ref.null none)) - (global $use-C (ref null $C) (ref.null none)) - ;; CHECK: (global $use-C-child (ref null $C-child) (ref.null none)) - (global $use-C-child (ref null $C-child) (ref.null none)) - ;; CHECK: (export "g-public" (global $g-public)) - (export "g-public" (global $g-public)) -)