diff --git a/src/ir/possible-constant.h b/src/ir/possible-constant.h index 79a9973b530..a75dd76299a 100644 --- a/src/ir/possible-constant.h +++ b/src/ir/possible-constant.h @@ -20,6 +20,7 @@ #include #include "ir/properties.h" +#include "support/utilities.h" #include "wasm-builder.h" #include "wasm.h" @@ -85,6 +86,45 @@ struct PossibleConstantValues { // identify a constant value here. void noteUnknown() { value = Many(); } + // Modify the possible constant to account for being written to or read from a + // possibly-packed field. When used to model a read, `isSigned` controls + // whether the value will be sign-extended or not. + void packForField(const Field& field, bool isSigned = false) { + if (field.type != Type::i32 || !field.isPacked()) { + return; + } + if (!isConstant()) { + // Nothing to pack. + return; + } + if (isConstantGlobal()) { + // Cannot track global and bit masking simultaneously, so give up. + noteUnknown(); + return; + } + assert(isConstantLiteral()); + auto val = getConstantLiteral(); + assert(val.type == Type::i32); + switch (field.packedType) { + case Field::i8: + if (isSigned) { + value = val.extendS8(); + } else { + value = val.and_(Literal(uint32_t(0xff))); + } + break; + case Field::i16: + if (isSigned) { + value = val.extendS16(); + } else { + value = val.and_(Literal(uint32_t(0xffff))); + } + break; + case Field::not_packed: + WASM_UNREACHABLE("unexpected packed type"); + } + } + // Combine the information in a given PossibleConstantValues to this one. This // is the same as if we have called note*() on us with all the history of // calls to that other object. @@ -109,28 +149,6 @@ struct PossibleConstantValues { return true; } - // Nulls compare equal, and we could consider any of the input nulls as the - // combination of the two (as any of them would be valid to place in the - // location we are working to optimize). In order to have simple symmetric - // behavior here, which does not depend on the order of the inputs, use the - // LUB. - if (isNull() && other.isNull()) { - auto type = getConstantLiteral().type.getHeapType(); - auto otherType = other.getConstantLiteral().type.getHeapType(); - auto lub = HeapType::getLeastUpperBound(type, otherType); - if (!lub) { - // TODO: Remove this workaround once we have bottom types to assign to - // null literals. - value = Many(); - return true; - } - if (*lub != type) { - value = Literal::makeNull(*lub); - return true; - } - return false; - } - return false; } diff --git a/src/passes/ConstantFieldPropagation.cpp b/src/passes/ConstantFieldPropagation.cpp index 9984a6ca330..8bf893c42c0 100644 --- a/src/passes/ConstantFieldPropagation.cpp +++ b/src/passes/ConstantFieldPropagation.cpp @@ -51,13 +51,17 @@ // wasm GC programs we need to check for type escaping. // +#include + #include "ir/bits.h" #include "ir/gc-type-utils.h" #include "ir/possible-constant.h" #include "ir/struct-utils.h" #include "ir/utils.h" #include "pass.h" +#include "support/hash.h" #include "support/small_vector.h" +#include "support/unique_deferring_queue.h" #include "wasm-builder.h" #include "wasm-traversal.h" #include "wasm.h" @@ -66,6 +70,43 @@ namespace wasm { namespace { +// The destination reference type and field index of a copy, as well as whether +// the copy read is signed (in the case of packed fields). This will be used to +// propagate copied values from their sources to destinations in the analysis. +struct CopyInfo { + HeapType type; + Exactness exact; + Index index; + bool isSigned; + + bool operator==(const CopyInfo& other) const { + return type == other.type && exact == other.exact && index == other.index && + isSigned == other.isSigned; + } +}; + +} // anonymous namespace + +} // namespace wasm + +namespace std { + +template<> struct hash { + size_t operator()(const wasm::CopyInfo& copy) const { + auto digest = wasm::hash(copy.type); + wasm::rehash(digest, copy.exact); + wasm::rehash(digest, copy.index); + wasm::rehash(digest, copy.isSigned); + return digest; + } +}; + +} // namespace std + +namespace wasm { + +namespace { + using PCVStructValuesMap = StructUtils::StructValuesMap; using PCVFunctionStructValuesMap = StructUtils::FunctionStructValuesMap; @@ -75,6 +116,25 @@ using BoolStructValuesMap = using BoolFunctionStructValuesMap = StructUtils::FunctionStructValuesMap; +using StructFieldPairs = + std::unordered_set>; + +// TODO: Deduplicate with Lattice infrastructure. +template struct CombinableSet : std::unordered_set { + bool combine(const CombinableSet& other) { + auto originalSize = this->size(); + this->insert(other.begin(), other.end()); + return this->size() != originalSize; + } +}; + +// For each field, the set of fields it is copied to. +using CopiesStructValuesMap = + StructUtils::StructValuesMap>; + +using CopiesFunctionStructValuesMap = + StructUtils::FunctionStructValuesMap>; + // Optimize struct gets based on what we've learned about writes. // // TODO Aside from writes, we could use information like whether any struct of @@ -415,7 +475,7 @@ struct PCVScanner PCVScanner(PCVFunctionStructValuesMap& functionNewInfos, PCVFunctionStructValuesMap& functionSetInfos, - BoolFunctionStructValuesMap& functionCopyInfos) + CopiesFunctionStructValuesMap& functionCopyInfos) : StructUtils::StructScanner( functionNewInfos, functionSetInfos), functionCopyInfos(functionCopyInfos) {} @@ -435,20 +495,14 @@ struct PCVScanner } void noteCopy(StructGet* get, - Type type, - Index index, - PossibleConstantValues& info) { - // We currently only treat copies from a field to itself specially. See the - // comments on value propagation below. - // TODO: generalize this. - if (get->ref->type.getHeapType() == type.getHeapType() && - get->index == index) { - // TODO: Use exactness from `type`. - auto ht = std::make_pair(type.getHeapType(), Inexact); - functionCopyInfos[getFunction()][ht][index] = true; - } else { - info.noteUnknown(); - } + Type dstType, + Index dstIndex, + PossibleConstantValues& dstInfo) { + auto srcType = get->ref->type.getHeapType(); + auto srcExact = get->ref->type.getExactness(); + auto srcIndex = get->index; + functionCopyInfos[getFunction()][{srcType, srcExact}][srcIndex].insert( + {dstType.getHeapType(), dstType.getExactness(), dstIndex, get->signed_}); } void noteRead(HeapType type, Index index, PossibleConstantValues& info) { @@ -465,7 +519,7 @@ struct PCVScanner info.noteUnknown(); } - BoolFunctionStructValuesMap& functionCopyInfos; + CopiesFunctionStructValuesMap& functionCopyInfos; }; struct ConstantFieldPropagation : public Pass { @@ -485,7 +539,7 @@ struct ConstantFieldPropagation : public Pass { // Find and analyze all writes inside each function. PCVFunctionStructValuesMap functionNewInfos(*module), functionSetInfos(*module); - BoolFunctionStructValuesMap functionCopyInfos(*module); + CopiesFunctionStructValuesMap functionCopyInfos(*module); PCVScanner scanner(functionNewInfos, functionSetInfos, functionCopyInfos); auto* runner = getPassRunner(); scanner.run(runner, module); @@ -495,106 +549,142 @@ struct ConstantFieldPropagation : public Pass { PCVStructValuesMap combinedSetInfos; functionNewInfos.combineInto(combinedSetInfos); functionSetInfos.combineInto(combinedSetInfos); - BoolStructValuesMap combinedCopyInfos; + CopiesStructValuesMap combinedCopyInfos; functionCopyInfos.combineInto(combinedCopyInfos); - // Handle subtyping. |combinedSetInfos| so far contains data that represents - // each struct.new and struct.set's operation on the struct type used in - // that instruction. That is, if we do a struct.set to type T, the value was - // noted for type T. But our actual goal is to answer questions about - // struct.gets. Specifically, when later we see: + // Perform an analysis to compute the readable values for each triple of + // heap type, exactness, and field index. The readable values are + // determined by the written values and copies. // - // (struct.get $A x (REF-1)) + // Whenever we have a write like this: // - // Then we want to be aware of all the relevant struct.sets, that is, the - // sets that can write data that this get reads. Given a set + // (struct.set $super x (... ref ...) (... value ...)) // - // (struct.set $B x (REF-2) (..value..)) + // The dynamic type of the struct we are writing to may be any subtype of + // the type of the ref. For example, if the ref has type (ref $super), + // then the write may go to an object of type (ref $super) or (ref $sub). + // In contrast, if the ref has an exact type, then we know the write + // cannot go to an object of type (ref $sub), which is not a subtype of + // (ref (exact $super)). The set of values that may have been written to a + // field is therefore the join of all the values we observe being written + // to that field in all supertypes of the written reference. The written + // values are propagated down to subtypes. // - // then + // Similarly, whenever we have a read like this: // - // 1. If $B is a subtype of $A, it is relevant: the get might read from a - // struct of type $B (i.e., REF-1 and REF-2 might be identical, and both - // be a struct of type $B). - // 2. If $B is a supertype of $A that still has the field x then it may - // also be relevant: since $A is a subtype of $B, the set may write to a - // struct of type $A (and again, REF-1 and REF-2 may be identical). + // (struct.get $super x (... ref ...)) // - // Thus, if either $A <: $B or $B <: $A then we must consider the get and - // set to be relevant to each other. To make our later lookups for gets - // efficient, we therefore propagate information about the possible values - // in each field to both subtypes and supertypes. + // The dynamic type of the struct we are reading from may be any subtype + // of the type of the ref. The set of values that we might read from a + // field is therefore the join of all the values that may have been + // written to that field in all subtypes of the read reference. The read + // values are propagated up to supertypes. // - // Values written in struct.news are equivalent to values written to exact - // references. In both cases, the propagation to subtypes will not do - // anything because an exact reference has no non-trivial subtypes. This - // works out because a set of a field of an exact reference (or an - // allocation) cannot ever affect the value read out of a subtype's field. - // - // An exception to the above are copies. If a field is copied then even - // struct.new information cannot be assumed to be precise: - // - // // A :> B :> C - // .. - // new B(20); - // .. - // A1->f0 = A2->f0; // Either of these might refer to an A, B, or C. - // .. - // foo(A->f0); // These can contain 20, - // foo(C->f0); // if the copy read from B. - // - // The handling of copies is explained below. + // Copies are interesting because they invert the normal dependence of + // readable values on written values. A copy is a write of a read, so the + // writable values depends on the readable values. Because of this cyclic + // dependency, we must iteratively update our knowledge of the written and + // readable values until we reach a fixed point. SubTypes subTypes(*module); StructUtils::TypeHierarchyPropagator propagator( subTypes); - // Compute the values without accounting for copies. - PCVStructValuesMap noCopySetInfos = combinedSetInfos; - propagator.propagateToSubTypes(noCopySetInfos); - propagator.propagateToSuperTypes(noCopySetInfos); - - // Now account for copies. A copy takes a value from any subtype - // of the copy source to any subtype of the copy destination. Since we last - // propagated to supertypes, we know the propagated values increase - // monotonically as you go up the type hierarchy. The propagated value in a - // field therefore overapproximates the values in the corresponding field in - // all the subtypes. So for each copy, we can use the propagated value as - // the copied value. Then we will propagate set values again, this time - // including the copied values. We only need to repeat the propagation once; - // if the second propagation discovers greater values in the copied fields, - // it can only be because those greater values were propagated from a - // supertype. In that case, the greater value has also been propagated to - // all subtypes, so repeating the process will not further change anything. + PCVStructValuesMap written = std::move(combinedSetInfos); + propagator.propagateToSubTypes(written); + PCVStructValuesMap readable = written; + propagator.propagateToSuperTypes(readable); + + // Now apply copies and propagate the new information until we have a + // fixed point. We could just join the copied values into `written`, + // propagate all of `written` down again, and recompute `readable`, + // but that would do more work than necessary since most fields are not + // going to be involved in copies. We will handle the propagation manually + // instead. // - // TODO: Track separate sources and destinations of copies rather than - // special-casing copies to self. This would let propagation discover - // greater copied values from unrelated types or even different field - // indices, so we would have to repeatedly propagate taking into account the - // latest discovered copied values until reaching a fixed point. - for (auto& [type, copied] : combinedCopyInfos) { - for (Index i = 0; i < copied.size(); ++i) { - if (copied[i]) { - combinedSetInfos[type][i].combine(noCopySetInfos[type][i]); + // Since the analysis records untruncated values for packed fields, we must + // be careful to truncate and sign extend copy source values as necessary. + // We generally don't truncate values based on their destination because + // that would regress propagation of globals when they are not copied. + // TODO: Track truncations in the analysis itself to propagate them through + // copies, even of globals. + UniqueDeferredQueue work; + auto applyCopiesTo = [&](auto& dsts, const Field& src, const auto& val) { + for (auto& dst : dsts) { + auto packed = val; + packed.packForField(src, dst.isSigned); + if (written[{dst.type, dst.exact}][dst.index].combine(packed)) { + work.push(dst); } } + }; + auto applyCopiesFrom = + [&](HeapType src, Exactness exact, Index index, const auto& val) { + if (auto it = combinedCopyInfos.find({src, exact}); + it != combinedCopyInfos.end()) { + const auto& srcField = src.getStruct().fields[index]; + applyCopiesTo(it->second[index], srcField, val); + } + }; + // For each copy, take the readable values at its source and join them to + // the written values at its destination. Record the written values that + // change so we can propagate the new information afterward. + for (auto& [src, fields] : combinedCopyInfos) { + auto [srcType, srcExact] = src; + for (Index srcField = 0; srcField < fields.size(); ++srcField) { + const auto& field = srcType.getStruct().fields[srcField]; + applyCopiesTo(fields[srcField], field, readable[src][srcField]); + } } - - // Propagate the values again, now including values readable by copies. - // RefTest optimization manually checks the values in every subtype to - // make sure they match, so there's no need to propagate values up for that. - // Snapshot the info before propagating up for use in RefTest - // optimization. - PCVStructValuesMap refTestInfos; - propagator.propagateToSubTypes(combinedSetInfos); - if (refTest) { - refTestInfos = combinedSetInfos; + while (work.size()) { + // Propagate down from dst in both written and readable, then + // propagate up from dst in readable only. Whenever we make a change in + // readable, see if there are copies to apply. If there are copies and + // they make changes, then we have more propagation work to do later. + auto dst = work.pop(); + assert(dst.index != StructUtils::DescriptorIndex); + auto val = written[{dst.type, dst.exact}][dst.index]; + val.packForField(dst.type.getStruct().fields[dst.index]); + // Make the copied value readable. + if (readable[{dst.type, dst.exact}][dst.index].combine(val)) { + applyCopiesFrom(dst.type, dst.exact, dst.index, val); + } + if (dst.exact == Inexact) { + // Propagate down to subtypes. + written[{dst.type, Exact}][dst.index].combine(val); + subTypes.iterSubTypes(dst.type, [&](HeapType sub, Index depth) { + written[{sub, Inexact}][dst.index].combine(val); + written[{sub, Exact}][dst.index].combine(val); + if (readable[{sub, Inexact}][dst.index].combine(val)) { + applyCopiesFrom(sub, Inexact, dst.index, val); + } + if (readable[{sub, Exact}][dst.index].combine(val)) { + applyCopiesFrom(sub, Exact, dst.index, val); + } + }); + } else { + // The copy destination is exact, so there are no subtypes to + // propagate to, but we do need to propagate up to the inexact type. + if (readable[{dst.type, Inexact}][dst.index].combine(val)) { + applyCopiesFrom(dst.type, Inexact, dst.index, val); + } + } + // Propagate up to the supertypes. + for (auto super = dst.type.getDeclaredSuperType(); super; + super = super->getDeclaredSuperType()) { + auto& readableSuperFields = readable[{*super, Inexact}]; + if (dst.index >= readableSuperFields.size()) { + break; + } + if (readableSuperFields[dst.index].combine(val)) { + applyCopiesFrom(*super, Inexact, dst.index, val); + } + } } - propagator.propagateToSuperTypes(combinedSetInfos); // Optimize. - // TODO: Skip this if we cannot optimize anything - FunctionOptimizer(combinedSetInfos, refTestInfos, subTypes, refTest) - .run(runner, module); + // TODO: Skip this if we cannot optimize anything. + FunctionOptimizer(readable, written, subTypes, refTest).run(runner, module); + return; } }; diff --git a/test/lit/passes/cfp-copies.wast b/test/lit/passes/cfp-copies.wast new file mode 100644 index 00000000000..a5af3e26c82 --- /dev/null +++ b/test/lit/passes/cfp-copies.wast @@ -0,0 +1,8311 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: foreach %s %t wasm-opt --closed-world --cfp -all -S -o - | filecheck %s + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (local $A (ref $A)) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new_default $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $B (ref $B)) (result i32) + ;; Copy the default value from $A to $B. Note that we do not detect that $B + ;; is never allocated in this pass. + (local $A (ref $A)) + (local.set $A + (struct.new_default $A) + ) + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; This should be optimized. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (local $A (ref $A)) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $B (ref $B)) (result i32) + ;; Same, but copy a non-default value. + (local $A (ref $A)) + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; This should be optimized. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (local $A (ref $A)) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (block (result (ref $B)) + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $B (ref $B)) (result i32) + ;; Now the copy has to look through fallthroughs on both the source and + ;; destination. + (local $A (ref $A)) + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + (struct.set $B 0 + (block (result (ref null $B)) + (local.get $B) + ) + (block (result i32) + (struct.get $A 0 + (local.get $A) + ) + ) + ) + ;; This should be optimized. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (local $A (ref $A)) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $B + ;; CHECK-NEXT: (struct.new $B + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $B (ref $B)) (result i32) + ;; Now copy from $A to $B, but also allocate a $B with a matching value. We + ;; should still optimize. + (local $A (ref $A)) + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + ;; This is new. + (local.set $B + (struct.new $B + (i32.const 10) + ) + ) + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; This should be optimized. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (local $A (ref $A)) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $B (ref $B)) (result i32) + ;; Now instead of allocating $B with the same value, we set $B with the same + ;; value. We can still optimize. + (local $A (ref $A)) + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + ;; This is a set now. + (struct.set $B 0 + (local.get $B) + (i32.const 10) + ) + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; This should be optimized. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (local $A (ref $A)) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $B + ;; CHECK-NEXT: (struct.new $B + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.get $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $B (ref $B)) (result i32) + ;; Same, but now allocate $B with a conflicting value. We cannot optimize. + (local $A (ref $A)) + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + (local.set $B + (struct.new $B + ;; Does not match. + (i32.const 20) + ) + ) + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; This cannot be optimized. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (local $A (ref $A)) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.get $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $B (ref $B)) (result i32) + ;; Same, but now the conflicting value comes from a set rather than an + ;; allocation. + (local $A (ref $A)) + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + ;; This is a set now. + (struct.set $B 0 + (local.get $B) + (i32.const 20) + ) + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; This cannot be optimized. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; Now the copied value comes from a set. + (struct.set $A 0 + (local.get $A) + (i32.const 10) + ) + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; This should be optimized. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; The copied value comes from both a set and an allocation, but they match. + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + (struct.set $A 0 + (local.get $A) + (i32.const 10) + ) + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; This should be optimized. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (local.set $A + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.get $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; Now the source values don't match, so we cannot optimize. + (local.set $A + (struct.new $A + (i32.const 10) + ) + ) + (struct.set $A 0 + (local.get $A) + (i32.const 20) + ) + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; This should be optimized. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $struct (struct (field (mut i32))))) + (type $sub (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $other (struct (field (mut i32)))) + (type $other (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $other) (ref $struct)))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $other + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $other + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $other (ref $other)) (param $struct (ref $struct)) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $struct (ref $struct)) + ;; Copy from $other to $struct. + (struct.set $struct 0 + (local.get $struct) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub (ref null $sub)) + ;; We never wrote to $super, so the only value we can read from it is what + ;; was written to $struct (which also may have been a $sub at runtime). + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub (ref null (exact $sub))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub (ref null (exact $sub))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $struct (struct (field (mut i32))))) + (type $sub (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $other (struct (field (mut i32)))) + (type $other (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $other) (ref (exact $struct))))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $other + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $other + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $other (ref $other)) (param $exact-struct (ref (exact $struct))) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $exact-struct) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $exact-struct (ref (exact $struct))) + ;; Same as above, but now we copy to exact $struct. + (struct.set $struct 0 + (local.get $exact-struct) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub (ref null $sub)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub (ref null (exact $sub))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub (ref null (exact $sub))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub1 (sub $struct (struct (field (mut i32))))) + (type $sub1 (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $sub2 (sub $struct (struct (field (mut i32))))) + (type $sub2 (sub $struct (struct (field (mut i32))))) + + ;; CHECK: (type $other (struct (field (mut i32)))) + (type $other (struct (field (mut i32)))) + ) + + ;; CHECK: (type $5 (func)) + + ;; CHECK: (type $6 (func (param (ref $other) (ref $struct)))) + + ;; CHECK: (func $init (type $5) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $other + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $sub1 + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + ;; Same as above, but now with a different value in the subtype $sub1. We + ;; also add an additional subtype, $sub2, which can still be optimized. + (drop + (struct.new $other + (i32.const 10) + ) + ) + (drop + (struct.new $sub1 + (i32.const 20) + ) + ) + ) + + ;; CHECK: (func $copy (type $6) (param $other (ref $other)) (param $struct (ref $struct)) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $struct (ref $struct)) + ;; Copy from $other to $struct. + (struct.set $struct 0 + (local.get $struct) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $gets (type $5) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $sub1 0 + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + ;; The copy might have written to $sub1 or $sub2, but only $sub2 does not + ;; already have another conflicting value. We can optimize $sub2 but not + ;; $sub1. + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $5) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub1 (ref null (exact $sub1))) + ;; CHECK-NEXT: (local $sub2 (ref null (exact $sub2))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $sub1 0 + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub1 (ref null (exact $sub1))) + (local $sub2 (ref null (exact $sub2))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub1 (sub $struct (struct (field (mut i32))))) + (type $sub1 (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $sub2 (sub $struct (struct (field (mut i32))))) + (type $sub2 (sub $struct (struct (field (mut i32))))) + + ;; CHECK: (type $other (struct (field (mut i32)))) + (type $other (struct (field (mut i32)))) + ) + + ;; CHECK: (type $5 (func)) + + ;; CHECK: (type $6 (func (param (ref $other) (ref (exact $struct))))) + + ;; CHECK: (func $init (type $5) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $other + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $sub1 + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + ;; Same as above. + (drop + (struct.new $other + (i32.const 10) + ) + ) + (drop + (struct.new $sub1 + (i32.const 20) + ) + ) + ) + + ;; CHECK: (func $copy (type $6) (param $other (ref $other)) (param $exact-struct (ref (exact $struct))) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $exact-struct) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $exact-struct (ref (exact $struct))) + ;; Now the copy is to exact $struct. + (struct.set $struct 0 + (local.get $exact-struct) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $gets (type $5) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $5) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub1 (ref null (exact $sub1))) + ;; CHECK-NEXT: (local $sub2 (ref null (exact $sub2))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub1 (ref null (exact $sub1))) + (local $sub2 (ref null (exact $sub2))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub1 (sub $struct (struct (field (mut i32))))) + (type $sub1 (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $sub2 (sub $struct (struct (field (mut i32))))) + (type $sub2 (sub $struct (struct (field (mut i32))))) + + ;; CHECK: (type $other (struct (field (mut i32)))) + (type $other (struct (field (mut i32)))) + ) + + ;; CHECK: (type $5 (func)) + + ;; CHECK: (type $6 (func (param (ref $sub1)))) + + ;; CHECK: (type $7 (func (param (ref $other) (ref $struct)))) + + ;; CHECK: (func $init (type $6) (param $sub1 (ref $sub1)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $other + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $sub1 0 + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $sub1 (ref $sub1)) + ;; Now the value is set to an inexact reference to $sub1. This won't make a + ;; difference. + (drop + (struct.new $other + (i32.const 10) + ) + ) + (struct.set $sub1 0 + (local.get $sub1) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $7) (param $other (ref $other)) (param $struct (ref $struct)) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $struct (ref $struct)) + ;; Copy from $other to $struct. + (struct.set $struct 0 + (local.get $struct) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $gets (type $5) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $sub1 0 + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $5) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub1 (ref null (exact $sub1))) + ;; CHECK-NEXT: (local $sub2 (ref null (exact $sub2))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $sub1 0 + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub1 (ref null (exact $sub1))) + (local $sub2 (ref null (exact $sub2))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub1 (sub $struct (struct (field (mut i32))))) + (type $sub1 (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $sub2 (sub $struct (struct (field (mut i32))))) + (type $sub2 (sub $struct (struct (field (mut i32))))) + + ;; CHECK: (type $other (struct (field (mut i32)))) + (type $other (struct (field (mut i32)))) + ) + + ;; CHECK: (type $5 (func)) + + ;; CHECK: (type $6 (func (param (ref $sub1)))) + + ;; CHECK: (type $7 (func (param (ref $other) (ref (exact $struct))))) + + ;; CHECK: (func $init (type $6) (param $sub1 (ref $sub1)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $other + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $sub1 0 + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $sub1 (ref $sub1)) + ;; Same as above. + (drop + (struct.new $other + (i32.const 10) + ) + ) + (struct.set $sub1 0 + (local.get $sub1) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $7) (param $other (ref $other)) (param $exact-struct (ref (exact $struct))) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $exact-struct) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $exact-struct (ref (exact $struct))) + ;; Now the copy is to exact $struct. + (struct.set $struct 0 + (local.get $exact-struct) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $gets (type $5) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $5) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub1 (ref null (exact $sub1))) + ;; CHECK-NEXT: (local $sub2 (ref null (exact $sub2))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub1 (ref null (exact $sub1))) + (local $sub2 (ref null (exact $sub2))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $struct (struct (field (mut i32))))) + (type $sub (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $other (struct (field (mut i32)))) + (type $other (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $other) (ref $struct)))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $other + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $super + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + ;; Now the different value is in $super. Since it's set in an exact $super, + ;; it won't interfere with the copy. + (drop + (struct.new $other + (i32.const 10) + ) + ) + (drop + (struct.new $super + (i32.const 20) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $other (ref $other)) (param $struct (ref $struct)) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $struct (ref $struct)) + ;; Copy from $other to $struct. + (struct.set $struct 0 + (local.get $struct) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub (ref null $sub)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub (ref null (exact $sub))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub (ref null (exact $sub))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $struct (struct (field (mut i32))))) + (type $sub (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $other (struct (field (mut i32)))) + (type $other (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $other) (ref (exact $struct))))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $other + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $super + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + ;; Same as above. + (drop + (struct.new $other + (i32.const 10) + ) + ) + (drop + (struct.new $super + (i32.const 20) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $other (ref $other)) (param $exact-struct (ref (exact $struct))) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $exact-struct) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $exact-struct (ref (exact $struct))) + ;; Copy from $other to exact $struct. + (struct.set $struct 0 + (local.get $exact-struct) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub (ref null $sub)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub (ref null (exact $sub))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub (ref null (exact $sub))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $struct (struct (field (mut i32))))) + (type $sub (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $other (struct (field (mut i32)))) + (type $other (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $super)))) + + ;; CHECK: (type $6 (func (param (ref $other) (ref $struct)))) + + ;; CHECK: (func $init (type $5) (param $super (ref $super)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $other + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $super (ref $super)) + ;; Now the different value is in an inexact $super. + (drop + (struct.new $other + (i32.const 10) + ) + ) + (struct.set $super 0 + (local.get $super) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $6) (param $other (ref $other)) (param $struct (ref $struct)) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $struct (ref $struct)) + ;; Copy from $other to $struct. + (struct.set $struct 0 + (local.get $struct) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $sub 0 + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub (ref null $sub)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub (ref null (exact $sub))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $sub 0 + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub (ref null (exact $sub))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $struct (struct (field (mut i32))))) + (type $sub (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $other (struct (field (mut i32)))) + (type $other (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $super)))) + + ;; CHECK: (type $6 (func (param (ref $other) (ref (exact $struct))))) + + ;; CHECK: (func $init (type $5) (param $super (ref $super)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $other + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $super (ref $super)) + ;; Same as above. + (drop + (struct.new $other + (i32.const 10) + ) + ) + (struct.set $super 0 + (local.get $super) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $6) (param $other (ref $other)) (param $exact-struct (ref (exact $struct))) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $exact-struct) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $exact-struct (ref (exact $struct))) + ;; Copy from $other to exact $struct. + (struct.set $struct 0 + (local.get $exact-struct) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub (ref null $sub)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub (ref null (exact $sub))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub (ref null (exact $sub))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct))) + (type $super (sub (struct))) + ;; CHECK: (type $A (sub $super (struct (field (mut i32))))) + (type $A (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $B (sub $super (struct (field (mut i32))))) + (type $B (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $other (struct (field (mut i32)))) + (type $other (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $super)))) + + ;; CHECK: (type $6 (func (param (ref $other) (ref $A)))) + + ;; CHECK: (func $init (type $5) (param $super (ref $super)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $other + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $super (ref $super)) + (drop + (struct.new $other + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $6) (param $other (ref $other)) (param $A (ref $A)) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $A (ref $A)) + ;; Copy from $other to $A. The fact that the field is missing in $super + ;; should not cause problems. + (struct.set $A 0 + (local.get $A) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $B (ref null $B)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $A (ref null $A)) + (local $B (ref null $B)) + (drop + (struct.get $A 0 + (local.get $A) + ) + ) + (drop + (struct.get $B 0 + (local.get $B) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $A (ref null (exact $A))) + ;; CHECK-NEXT: (local $B (ref null (exact $B))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $A (ref null (exact $A))) + (local $B (ref null (exact $B))) + (drop + (struct.get $A 0 + (local.get $A) + ) + ) + (drop + (struct.get $B 0 + (local.get $B) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct))) + (type $super (sub (struct))) + ;; CHECK: (type $A (sub $super (struct (field (mut i32))))) + (type $A (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $B (sub $super (struct (field (mut i32))))) + (type $B (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $other (struct (field (mut i32)))) + (type $other (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $super)))) + + ;; CHECK: (type $6 (func (param (ref $other) (ref (exact $A))))) + + ;; CHECK: (func $init (type $5) (param $super (ref $super)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $other + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $super (ref $super)) + (drop + (struct.new $other + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $6) (param $other (ref $other)) (param $exact-A (ref (exact $A))) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $exact-A) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $exact-A (ref (exact $A))) + ;; Same as above, but now the copy is to exact $A. + (struct.set $A 0 + (local.get $exact-A) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $B (ref null $B)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $A (ref null $A)) + (local $B (ref null $B)) + (drop + (struct.get $A 0 + (local.get $A) + ) + ) + (drop + (struct.get $B 0 + (local.get $B) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $A (ref null (exact $A))) + ;; CHECK-NEXT: (local $B (ref null (exact $B))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $A (ref null (exact $A))) + (local $B (ref null (exact $B))) + (drop + (struct.get $A 0 + (local.get $A) + ) + ) + (drop + (struct.get $B 0 + (local.get $B) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub1 (sub $struct (struct (field (mut i32))))) + (type $sub1 (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $sub2 (sub $struct (struct (field (mut i32))))) + (type $sub2 (sub $struct (struct (field (mut i32))))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $struct)))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $sub1 + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + ;; Same as above. + (drop + (struct.new $sub1 + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $struct (ref $struct)) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $struct (ref $struct)) + ;; Copy from $struct to itself. This propagates the write from $sub1 to + ;; $sub2. + (struct.set $struct 0 + (local.get $struct) + (struct.get $struct 0 + (local.get $struct) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub1 (ref null (exact $sub1))) + ;; CHECK-NEXT: (local $sub2 (ref null (exact $sub2))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub1 (ref null (exact $sub1))) + (local $sub2 (ref null (exact $sub2))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub1 (sub $struct (struct (field (mut i32))))) + (type $sub1 (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $sub2 (sub $struct (struct (field (mut i32))))) + (type $sub2 (sub $struct (struct (field (mut i32))))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $struct) (ref (exact $struct))))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $sub1 + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + ;; Same as above. + (drop + (struct.new $sub1 + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $struct (ref $struct)) (param $exact-struct (ref (exact $struct))) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $exact-struct) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $struct (ref $struct)) (param $exact-struct (ref (exact $struct))) + ;; Now copy from $struct to exact $struct. This does not propagate to $sub2. + (struct.set $struct 0 + (local.get $exact-struct) + (struct.get $struct 0 + (local.get $struct) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub1 (ref null (exact $sub1))) + ;; CHECK-NEXT: (local $sub2 (ref null (exact $sub2))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub1 (ref null (exact $sub1))) + (local $sub2 (ref null (exact $sub2))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub1 (sub $struct (struct (field (mut i32))))) + (type $sub1 (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $sub2 (sub $struct (struct (field (mut i32))))) + (type $sub2 (sub $struct (struct (field (mut i32))))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $struct) (ref (exact $struct))))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $sub1 + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + ;; Same as above. + (drop + (struct.new $sub1 + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $struct (ref $struct)) (param $exact-struct (ref (exact $struct))) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $exact-struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $struct (ref $struct)) (param $exact-struct (ref (exact $struct))) + ;; Now copy from exact $struct to $struct. This does nothing, since exact + ;; $struct is not written to originally. + (struct.set $struct 0 + (local.get $struct) + (struct.get $struct 0 + (local.get $exact-struct) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub1 (ref null (exact $sub1))) + ;; CHECK-NEXT: (local $sub2 (ref null (exact $sub2))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub1 (ref null (exact $sub1))) + (local $sub2 (ref null (exact $sub2))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub1 (sub $struct (struct (field (mut i32))))) + (type $sub1 (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $sub2 (sub $struct (struct (field (mut i32))))) + (type $sub2 (sub $struct (struct (field (mut i32))))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref (exact $struct))))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $sub1 + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + ;; Same as above. + (drop + (struct.new $sub1 + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $exact-struct (ref (exact $struct))) + ;; CHECK-NEXT: (struct.set $struct 0 + ;; CHECK-NEXT: (local.get $exact-struct) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $exact-struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $exact-struct (ref (exact $struct))) + ;; Now copy from exact $struct to exact $struct. + (struct.set $struct 0 + (local.get $exact-struct) + (struct.get $struct 0 + (local.get $exact-struct) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $sub1 (ref null (exact $sub1))) + ;; CHECK-NEXT: (local $sub2 (ref null (exact $sub2))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $sub1 (ref null (exact $sub1))) + (local $sub2 (ref null (exact $sub2))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $struct2 (sub $super (struct (field (mut i32))))) + (type $struct2 (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $struct (struct (field (mut i32))))) + (type $sub (sub $struct (struct (field (mut i32))))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $struct) (ref $super)))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $sub + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $sub + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $struct (ref $struct)) (param $super (ref $super)) + ;; CHECK-NEXT: (struct.set $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $struct (ref $struct)) (param $super (ref $super)) + ;; Copy from $struct to $super. + (struct.set $super 0 + (local.get $super) + (struct.get $struct 0 + (local.get $struct) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $struct2 (ref null $struct2)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $struct2 (ref null $struct2)) + (local $sub (ref null $sub)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $struct2 0 + (local.get $struct2) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $struct2 (ref null (exact $struct2))) + ;; CHECK-NEXT: (local $sub (ref null (exact $sub))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $struct2 (ref null (exact $struct2))) + (local $sub (ref null (exact $sub))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $struct2 0 + (local.get $struct2) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $struct2 (sub $super (struct (field (mut i32))))) + (type $struct2 (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $struct (struct (field (mut i32))))) + (type $sub (sub $struct (struct (field (mut i32))))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $struct) (ref (exact $super))))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $sub + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $sub + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $struct (ref $struct)) (param $exact-super (ref (exact $super))) + ;; CHECK-NEXT: (struct.set $super 0 + ;; CHECK-NEXT: (local.get $exact-super) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $struct (ref $struct)) (param $exact-super (ref (exact $super))) + ;; Copy from $struct to exact $super. + (struct.set $super 0 + (local.get $exact-super) + (struct.get $struct 0 + (local.get $struct) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $struct2 (ref null $struct2)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $struct2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $struct2 (ref null $struct2)) + (local $sub (ref null $sub)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $struct2 0 + (local.get $struct2) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $struct2 (ref null (exact $struct2))) + ;; CHECK-NEXT: (local $sub (ref null (exact $sub))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $struct2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $struct2 (ref null (exact $struct2))) + (local $sub (ref null (exact $sub))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $struct2 0 + (local.get $struct2) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $struct2 (sub $super (struct (field (mut i32))))) + (type $struct2 (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $struct (struct (field (mut i32))))) + (type $sub (sub $struct (struct (field (mut i32))))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref (exact $struct)) (ref $super)))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $sub + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $sub + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $exact-struct (ref (exact $struct))) (param $super (ref $super)) + ;; CHECK-NEXT: (struct.set $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $exact-struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $exact-struct (ref (exact $struct))) (param $super (ref $super)) + ;; Copy from exact $struct to $super. + (struct.set $super 0 + (local.get $super) + (struct.get $struct 0 + (local.get $exact-struct) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $struct2 (ref null $struct2)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $struct2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $struct2 (ref null $struct2)) + (local $sub (ref null $sub)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $struct2 0 + (local.get $struct2) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $struct2 (ref null (exact $struct2))) + ;; CHECK-NEXT: (local $sub (ref null (exact $sub))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $struct2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $struct2 (ref null (exact $struct2))) + (local $sub (ref null (exact $sub))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $struct2 0 + (local.get $struct2) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $struct2 (sub $super (struct (field (mut i32))))) + (type $struct2 (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $struct (struct (field (mut i32))))) + (type $sub (sub $struct (struct (field (mut i32))))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref (exact $struct)) (ref (exact $super))))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $sub + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $sub + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $exact-struct (ref (exact $struct))) (param $exact-super (ref (exact $super))) + ;; CHECK-NEXT: (struct.set $super 0 + ;; CHECK-NEXT: (local.get $exact-super) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $exact-struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $exact-struct (ref (exact $struct))) (param $exact-super (ref (exact $super))) + ;; Copy from exact $struct to exact $super. + (struct.set $super 0 + (local.get $exact-super) + (struct.get $struct 0 + (local.get $exact-struct) + ) + ) + ) + + ;; CHECK: (func $gets (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $struct2 (ref null $struct2)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $struct2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $struct2 (ref null $struct2)) + (local $sub (ref null $sub)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $struct2 0 + (local.get $struct2) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $4) + ;; CHECK-NEXT: (local $super (ref null (exact $super))) + ;; CHECK-NEXT: (local $struct (ref null (exact $struct))) + ;; CHECK-NEXT: (local $struct2 (ref null (exact $struct2))) + ;; CHECK-NEXT: (local $sub (ref null (exact $sub))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $struct2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $super (ref null (exact $super))) + (local $struct (ref null (exact $struct))) + (local $struct2 (ref null (exact $struct2))) + (local $sub (ref null (exact $sub))) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $struct2 0 + (local.get $struct2) + ) + ) + (drop + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (sub (struct (field (mut i32))))) + (type $struct (sub (struct (field (mut i32))))) + ;; CHECK: (type $sub1 (sub $struct (struct (field (mut i32))))) + (type $sub1 (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $sub2 (sub $struct (struct (field (mut i32))))) + (type $sub2 (sub $struct (struct (field (mut i32))))) + ) + + ;; CHECK: (type $3 (func)) + + ;; CHECK: (type $4 (func (param (ref $struct) (ref $sub1)))) + + ;; CHECK: (func $init (type $3) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $struct + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $struct + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $4) (param $struct (ref $struct)) (param $sub1 (ref $sub1)) + ;; CHECK-NEXT: (struct.set $sub1 0 + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $struct (ref $struct)) (param $sub1 (ref $sub1)) + ;; Copy from $struct to $sub1. + (struct.set $sub1 0 + (local.get $sub1) + (struct.get $struct 0 + (local.get $struct) + ) + ) + ) + + ;; CHECK: (func $gets (type $3) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $3) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null (exact $sub1))) + ;; CHECK-NEXT: (local $sub2 (ref null (exact $sub2))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $struct (ref null $struct)) + (local $sub1 (ref null (exact $sub1))) + (local $sub2 (ref null (exact $sub2))) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (sub (struct (field (mut i32))))) + (type $struct (sub (struct (field (mut i32))))) + ;; CHECK: (type $sub1 (sub $struct (struct (field (mut i32))))) + (type $sub1 (sub $struct (struct (field (mut i32))))) + ;; CHECK: (type $sub2 (sub $struct (struct (field (mut i32))))) + (type $sub2 (sub $struct (struct (field (mut i32))))) + ) + + ;; CHECK: (type $3 (func)) + + ;; CHECK: (type $4 (func (param (ref $struct) (ref (exact $sub1))))) + + ;; CHECK: (func $init (type $3) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $struct + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $struct + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $4) (param $struct (ref $struct)) (param $exact-sub1 (ref (exact $sub1))) + ;; CHECK-NEXT: (struct.set $sub1 0 + ;; CHECK-NEXT: (local.get $exact-sub1) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $struct (ref $struct)) (param $exact-sub1 (ref (exact $sub1))) + ;; Copy from $struct to exact $sub1. + (struct.set $sub1 0 + (local.get $exact-sub1) + (struct.get $struct 0 + (local.get $struct) + ) + ) + ) + + ;; CHECK: (func $gets (type $3) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gets + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) + + ;; CHECK: (func $exact-gets (type $3) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null (exact $sub1))) + ;; CHECK-NEXT: (local $sub2 (ref null (exact $sub2))) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $exact-gets + (local $struct (ref null $struct)) + (local $sub1 (ref null (exact $sub1))) + (local $sub2 (ref null (exact $sub2))) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))) + (type $super (sub (struct (field (mut i32) (mut i32) (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32) (mut i32) (mut i32))))) + ;; CHECK: (type $sub1 (sub $struct (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))) + (type $sub1 (sub $struct (struct (field (mut i32) (mut i32) (mut i32))))) + ;; CHECK: (type $sub2 (sub $struct (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))) + (type $sub2 (sub $struct (struct (field (mut i32) (mut i32) (mut i32))))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $struct) (ref $struct)))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $sub1 + ;; CHECK-NEXT: (i32.const 666) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $sub1 + (i32.const 666) + (i32.const 10) + (i32.const 0) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $src (ref $struct)) (param $dst (ref $struct)) + ;; CHECK-NEXT: (struct.set $struct 2 + ;; CHECK-NEXT: (local.get $dst) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $src) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $src (ref $struct)) (param $dst (ref $struct)) + ;; Copy from index 1 to index 2 in the same type. The copied value will + ;; conflict with the initial value and inhibit optimization. + (struct.set $struct 2 + (local.get $dst) + (struct.get $struct 1 + (local.get $src) + ) + ) + ) + + ;; CHECK: (func $get-0 (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 666) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 666) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 666) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get-0 + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $super 0 + (local.get $super) + ) + ) + (drop + (struct.get $struct 0 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 0 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 0 + (local.get $sub2) + ) + ) + ) + + ;; CHECK: (func $get-1 (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get-1 + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $super 1 + (local.get $super) + ) + ) + (drop + (struct.get $struct 1 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 1 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 1 + (local.get $sub2) + ) + ) + ) + + ;; CHECK: (func $get-2 (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $super 2 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 2 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $sub1 2 + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get-2 + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $super 2 + (local.get $super) + ) + ) + (drop + (struct.get $struct 2 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 2 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 2 + (local.get $sub2) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))) + (type $super (sub (struct (field (mut i32) (mut i32) (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32) (mut i32) (mut i32))))) + ;; CHECK: (type $sub1 (sub $struct (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))) + (type $sub1 (sub $struct (struct (field (mut i32) (mut i32) (mut i32))))) + ;; CHECK: (type $sub2 (sub $struct (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))) + (type $sub2 (sub $struct (struct (field (mut i32) (mut i32) (mut i32))))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $struct) (ref $struct)))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $sub1 + ;; CHECK-NEXT: (i32.const 666) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + ;; Same as above, except now field 2 is initialized with the same value as + ;; field 1, so the copy will not do anything. + (drop + (struct.new $sub1 + (i32.const 666) + (i32.const 10) + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $src (ref $struct)) (param $dst (ref $struct)) + ;; CHECK-NEXT: (struct.set $struct 2 + ;; CHECK-NEXT: (local.get $dst) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $src) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $src (ref $struct)) (param $dst (ref $struct)) + ;; Copy from index 1 to index 2 in the same type. + (struct.set $struct 2 + (local.get $dst) + (struct.get $struct 1 + (local.get $src) + ) + ) + ) + + ;; CHECK: (func $get-2 (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get-2 + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $super 2 + (local.get $super) + ) + ) + (drop + (struct.get $struct 2 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 2 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 2 + (local.get $sub2) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))) + (type $super (sub (struct (field (mut i32) (mut i32) (mut i32))))) + ;; CHECK: (type $struct (sub $super (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))) + (type $struct (sub $super (struct (field (mut i32) (mut i32) (mut i32))))) + ;; CHECK: (type $sub1 (sub $struct (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))) + (type $sub1 (sub $struct (struct (field (mut i32) (mut i32) (mut i32))))) + ;; CHECK: (type $sub2 (sub $struct (struct (field (mut i32)) (field (mut i32)) (field (mut i32))))) + (type $sub2 (sub $struct (struct (field (mut i32) (mut i32) (mut i32))))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $sub1) (ref $sub1)))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $sub1 + ;; CHECK-NEXT: (i32.const 666) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + ;; We're back to initializing field 2 with a different value. + (drop + (struct.new $sub1 + (i32.const 666) + (i32.const 10) + (i32.const 0) + ) + ) + ) + + ;; CHECK: (func $copy (type $5) (param $src (ref $sub1)) (param $dst (ref $sub1)) + ;; CHECK-NEXT: (struct.set $sub1 2 + ;; CHECK-NEXT: (local.get $dst) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $src) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $src (ref $sub1)) (param $dst (ref $sub1)) + ;; Copy from index 1 to index 2, but now on $sub1, so we will be able to + ;; optimize $sub2. + (struct.set $sub1 2 + (local.get $dst) + (struct.get $sub1 1 + (local.get $src) + ) + ) + ) + + ;; CHECK: (func $get-2 (type $4) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $sub1 (ref null $sub1)) + ;; CHECK-NEXT: (local $sub2 (ref null $sub2)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $super 2 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 2 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $sub1 2 + ;; CHECK-NEXT: (local.get $sub1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get-2 + (local $super (ref null $super)) + (local $struct (ref null $struct)) + (local $sub1 (ref null $sub1)) + (local $sub2 (ref null $sub2)) + (drop + (struct.get $super 2 + (local.get $super) + ) + ) + (drop + (struct.get $struct 2 + (local.get $struct) + ) + ) + (drop + (struct.get $sub1 2 + (local.get $sub1) + ) + ) + (drop + (struct.get $sub2 2 + (local.get $sub2) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $3 (func)) + + ;; CHECK: (type $4 (func (param (ref $C)) (result i32))) + + ;; CHECK: (func $init (type $3) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $A + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $3) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $B (ref null $B)) + ;; CHECK-NEXT: (local $C (ref null $C)) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy + (local $A (ref null $A)) + (local $B (ref null $B)) + (local $C (ref null $C)) + ;; Copy $A to $B. + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy $B to $C. + (struct.set $C 0 + (local.get $C) + (struct.get $B 0 + (local.get $B) + ) + ) + ) + + ;; CHECK: (func $get (type $4) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + (func $get (param $C (ref $C)) (result i32) + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $3 (func)) + + ;; CHECK: (type $4 (func (param (ref $C)) (result i32))) + + ;; CHECK: (func $init (type $3) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $A + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $3) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $B (ref null $B)) + ;; CHECK-NEXT: (local $exact-B (ref null (exact $B))) + ;; CHECK-NEXT: (local $C (ref null $C)) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $exact-B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy + (local $A (ref null $A)) + (local $B (ref null $B)) + (local $exact-B (ref null (exact $B))) + (local $C (ref null $C)) + ;; Copy $A to $B. + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy exact $B to $C. + (struct.set $C 0 + (local.get $C) + (struct.get $B 0 + (local.get $exact-B) + ) + ) + ) + + ;; CHECK: (func $get (type $4) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + (func $get (param $C (ref $C)) (result i32) + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $3 (func)) + + ;; CHECK: (type $4 (func (param (ref $C)) (result i32))) + + ;; CHECK: (func $init (type $3) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $A + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $3) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $B (ref null $B)) + ;; CHECK-NEXT: (local $exact-B (ref null (exact $B))) + ;; CHECK-NEXT: (local $C (ref null $C)) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $exact-B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy + (local $A (ref null $A)) + (local $B (ref null $B)) + (local $exact-B (ref null (exact $B))) + (local $C (ref null $C)) + ;; Copy $A to exact $B. + (struct.set $B 0 + (local.get $exact-B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy $B to $C. + (struct.set $C 0 + (local.get $C) + (struct.get $B 0 + (local.get $B) + ) + ) + ) + + ;; CHECK: (func $get (type $4) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + (func $get (param $C (ref $C)) (result i32) + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $3 (func)) + + ;; CHECK: (type $4 (func (param (ref $C)) (result i32))) + + ;; CHECK: (func $init (type $3) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $A + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $3) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $exact-B (ref null (exact $B))) + ;; CHECK-NEXT: (local $C (ref null $C)) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $exact-B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $exact-B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy + (local $A (ref null $A)) + (local $exact-B (ref null (exact $B))) + (local $C (ref null $C)) + ;; Copy $A to exact $B. + (struct.set $B 0 + (local.get $exact-B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy $exact B to $C. + (struct.set $C 0 + (local.get $C) + (struct.get $B 0 + (local.get $exact-B) + ) + ) + ) + + ;; CHECK: (func $get (type $4) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + (func $get (param $C (ref $C)) (result i32) + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $super (struct (field (mut i32))))) + (type $sub (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $C)) (result i32))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $A + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $4) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $C (ref null $C)) + ;; CHECK-NEXT: (struct.set $sub 0 + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy + (local $A (ref null $A)) + (local $sub (ref null $sub)) + (local $super (ref null $super)) + (local $C (ref null $C)) + ;; Copy $A to $sub. + (struct.set $sub 0 + (local.get $sub) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy $super to $C. + (struct.set $C 0 + (local.get $C) + (struct.get $super 0 + (local.get $super) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + (func $get (param $C (ref $C)) (result i32) + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $super (struct (field (mut i32))))) + (type $sub (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $C)) (result i32))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $A + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $4) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (local $exact-super (ref null (exact $super))) + ;; CHECK-NEXT: (local $C (ref null $C)) + ;; CHECK-NEXT: (struct.set $sub 0 + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $exact-super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy + (local $A (ref null $A)) + (local $sub (ref null $sub)) + (local $exact-super (ref null (exact $super))) + (local $C (ref null $C)) + ;; Copy $A to $sub. + (struct.set $sub 0 + (local.get $sub) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy exact $super to $C. This does not propagate the value. + (struct.set $C 0 + (local.get $C) + (struct.get $super 0 + (local.get $exact-super) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $get (param $C (ref $C)) (result i32) + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $super (struct (field (mut i32))))) + (type $sub (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $C)) (result i32))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $A + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $4) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $exact-sub (ref null (exact $sub))) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $C (ref null $C)) + ;; CHECK-NEXT: (struct.set $sub 0 + ;; CHECK-NEXT: (local.get $exact-sub) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy + (local $A (ref null $A)) + (local $exact-sub (ref null (exact $sub))) + (local $super (ref null $super)) + (local $C (ref null $C)) + ;; Copy $A to exact $sub. + (struct.set $sub 0 + (local.get $exact-sub) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy $super to $C. + (struct.set $C 0 + (local.get $C) + (struct.get $super 0 + (local.get $super) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + (func $get (param $C (ref $C)) (result i32) + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $super (struct (field (mut i32))))) + (type $sub (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $C)) (result i32))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $A + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $4) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $exact-sub (ref null (exact $sub))) + ;; CHECK-NEXT: (local $exact-super (ref null (exact $super))) + ;; CHECK-NEXT: (local $C (ref null $C)) + ;; CHECK-NEXT: (struct.set $sub 0 + ;; CHECK-NEXT: (local.get $exact-sub) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $exact-super) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy + (local $A (ref null $A)) + (local $exact-sub (ref null (exact $sub))) + (local $exact-super (ref null (exact $super))) + (local $C (ref null $C)) + ;; Copy $A to exact $sub. + (struct.set $sub 0 + (local.get $exact-sub) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy exact $super to $C. This does not propagate the value. + (struct.set $C 0 + (local.get $C) + (struct.get $super 0 + (local.get $exact-super) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $get (param $C (ref $C)) (result i32) + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $super (struct (field (mut i32))))) + (type $sub (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $C)) (result i32))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $A + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $4) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $C (ref null $C)) + ;; CHECK-NEXT: (struct.set $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy + (local $A (ref null $A)) + (local $sub (ref null $sub)) + (local $super (ref null $super)) + (local $C (ref null $C)) + ;; Copy $A to $super. + (struct.set $super 0 + (local.get $super) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy $sub to $C. + (struct.set $C 0 + (local.get $C) + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + (func $get (param $C (ref $C)) (result i32) + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $super (struct (field (mut i32))))) + (type $sub (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $C)) (result i32))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $A + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $4) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $exact-sub (ref null (exact $sub))) + ;; CHECK-NEXT: (local $super (ref null $super)) + ;; CHECK-NEXT: (local $C (ref null $C)) + ;; CHECK-NEXT: (struct.set $super 0 + ;; CHECK-NEXT: (local.get $super) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $exact-sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy + (local $A (ref null $A)) + (local $exact-sub (ref null (exact $sub))) + (local $super (ref null $super)) + (local $C (ref null $C)) + ;; Copy $A to $super. + (struct.set $super 0 + (local.get $super) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy exact $sub to $C. + (struct.set $C 0 + (local.get $C) + (struct.get $sub 0 + (local.get $exact-sub) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + (func $get (param $C (ref $C)) (result i32) + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $super (struct (field (mut i32))))) + (type $sub (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $C)) (result i32))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $A + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $4) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $sub (ref null $sub)) + ;; CHECK-NEXT: (local $exact-super (ref null (exact $super))) + ;; CHECK-NEXT: (local $C (ref null $C)) + ;; CHECK-NEXT: (struct.set $super 0 + ;; CHECK-NEXT: (local.get $exact-super) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy + (local $A (ref null $A)) + (local $sub (ref null $sub)) + (local $exact-super (ref null (exact $super))) + (local $C (ref null $C)) + ;; Copy $A to exact $super. + (struct.set $super 0 + (local.get $exact-super) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy $sub to $C. This does not propagate the value. + (struct.set $C 0 + (local.get $C) + (struct.get $sub 0 + (local.get $sub) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $get (param $C (ref $C)) (result i32) + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $super (sub (struct (field (mut i32))))) + (type $super (sub (struct (field (mut i32))))) + ;; CHECK: (type $sub (sub $super (struct (field (mut i32))))) + (type $sub (sub $super (struct (field (mut i32))))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $4 (func)) + + ;; CHECK: (type $5 (func (param (ref $C)) (result i32))) + + ;; CHECK: (func $init (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init + (drop + (struct.new $A + (i32.const 10) + ) + ) + ) + + ;; CHECK: (func $copy (type $4) + ;; CHECK-NEXT: (local $A (ref null $A)) + ;; CHECK-NEXT: (local $exact-sub (ref null (exact $sub))) + ;; CHECK-NEXT: (local $exact-super (ref null (exact $super))) + ;; CHECK-NEXT: (local $C (ref null $C)) + ;; CHECK-NEXT: (struct.set $super 0 + ;; CHECK-NEXT: (local.get $exact-super) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $exact-sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy + (local $A (ref null $A)) + (local $exact-sub (ref null (exact $sub))) + (local $exact-super (ref null (exact $super))) + (local $C (ref null $C)) + ;; Copy $A to exact $super. + (struct.set $super 0 + (local.get $exact-super) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy exact $sub to $C. This does not propagate the value. + (struct.set $C 0 + (local.get $C) + (struct.get $sub 0 + (local.get $exact-sub) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $get (param $C (ref $C)) (result i32) + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i8)))) + (type $B (struct (field (mut i8)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x000001FF) + ) + ;; Copy i32 to i8. + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Read truncated i8. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i16)))) + (type $B (struct (field (mut i16)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 131071) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 131071) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 65535) + ;; CHECK-NEXT: (i32.const 65535) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x0001FFFF) + ) + ;; Copy i32 to i16. + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Read truncated i16. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x000001FF) + ) + ;; Copy i8 to i32. + (struct.set $B 0 + (local.get $B) + (struct.get_u $A 0 + (local.get $A) + ) + ) + ;; Read truncated i32. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.shr_s + ;; CHECK-NEXT: (i32.shl + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: (i32.const 24) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 24) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x000001FF) + ) + ;; Copy i8 to i32 with sign extension. + (struct.set $B 0 + (local.get $B) + (struct.get_s $A 0 + (local.get $A) + ) + ) + ;; Read truncated then sign extended i32. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ;; CHECK: (type $B (struct (field (mut i16)))) + (type $B (struct (field (mut i16)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: (i32.const 65535) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x000001FF) + ) + ;; Copy i8 to i16. + (struct.set $B 0 + (local.get $B) + (struct.get_u $A 0 + (local.get $A) + ) + ) + ;; Read truncated i16. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ;; CHECK: (type $B (struct (field (mut i16)))) + (type $B (struct (field (mut i16)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.shr_s + ;; CHECK-NEXT: (i32.shl + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: (i32.const 24) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 24) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 65535) + ;; CHECK-NEXT: (i32.const 65535) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x000001FF) + ) + ;; Copy i8 to i16 with sign extension. + (struct.set $B 0 + (local.get $B) + (struct.get_s $A 0 + (local.get $A) + ) + ) + ;; Read truncated then sign extended i16. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i16)))) + (type $A (struct (field (mut i16)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 131071) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 131071) + ;; CHECK-NEXT: (i32.const 65535) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 65535) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x0001FFFF) + ) + ;; Copy i16 to i32. + (struct.set $B 0 + (local.get $B) + (struct.get_u $A 0 + (local.get $A) + ) + ) + ;; Read truncated i32. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i16)))) + (type $A (struct (field (mut i16)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 131071) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.shr_s + ;; CHECK-NEXT: (i32.shl + ;; CHECK-NEXT: (i32.const 131071) + ;; CHECK-NEXT: (i32.const 16) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 16) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x0001FFFF) + ) + ;; Copy i16 to i32 with sign extension. + (struct.set $B 0 + (local.get $B) + (struct.get_s $A 0 + (local.get $A) + ) + ) + ;; Read truncated then sign extended i32. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i16)))) + (type $A (struct (field (mut i16)))) + ;; CHECK: (type $B (struct (field (mut i8)))) + (type $B (struct (field (mut i8)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 131071) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 131071) + ;; CHECK-NEXT: (i32.const 65535) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x0001FFFF) + ) + ;; Copy i16 to i8. + (struct.set $B 0 + (local.get $B) + (struct.get_u $A 0 + (local.get $A) + ) + ) + ;; Read truncated i8. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i16)))) + (type $A (struct (field (mut i16)))) + ;; CHECK: (type $B (struct (field (mut i8)))) + (type $B (struct (field (mut i8)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 131071) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.shr_s + ;; CHECK-NEXT: (i32.shl + ;; CHECK-NEXT: (i32.const 131071) + ;; CHECK-NEXT: (i32.const 16) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 16) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x0001FFFF) + ) + ;; Copy i16 to i8 with sign extension. This does not make a difference. + (struct.set $B 0 + (local.get $B) + (struct.get_s $A 0 + (local.get $A) + ) + ) + ;; Read truncated i8. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ) + + ;; CHECK: (type $1 (func (param (ref $A)) (result i32))) + + ;; CHECK: (func $test (type $1) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (struct.get_u $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.get_u $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0xFFFFFFFF) + ) + ;; Copy the i8 to itself. + (struct.set $A 0 + (local.get $A) + (struct.get_u $A 0 + (local.get $A) + ) + ) + ;; Read truncated i8. + ;; TODO: We do not optimize this because the truncated copy value conflicts + ;; with the untruncated value found by the initial analysis. Fix this by + ;; tracking truncations and sign extensions as part of the analysis. + (struct.get $A 0 + (local.get $A) + ) + ) +) + +(module + (rec + ;; CHECK: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ) + + ;; CHECK: (type $1 (func (param (ref $A)) (result i32))) + + ;; CHECK: (func $test (type $1) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.shr_s + ;; CHECK-NEXT: (i32.shl + ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: (i32.const 24) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 24) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0xFFFFFFFF) + ) + ;; Copy the i8 to itself with sign extension. + (struct.set $A 0 + (local.get $A) + (struct.get_s $A 0 + (local.get $A) + ) + ) + ;; Read truncated i8. Now we can optimize because the sign-extended value + ;; happens to match the original value. + (struct.get $A 0 + (local.get $A) + ) + ) +) + +(module + (rec + ;; CHECK: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ) + + ;; CHECK: (type $1 (func (param (ref $A)) (result i32))) + + ;; CHECK: (func $test (type $1) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (struct.get_s $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.get_u $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x000001FF) + ) + ;; Copy the i8 to itself with sign extension. + (struct.set $A 0 + (local.get $A) + (struct.get_s $A 0 + (local.get $A) + ) + ) + ;; Now the value does not match and we don't optimize, even though we could + ;; in principle. + (struct.get $A 0 + (local.get $A) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i8)))) + (type $B (struct (field (mut i8)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (global $g i32 (i32.const 0)) + (global $g i32 (i32.const 0)) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.get_u $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + (struct.set $A 0 + (local.get $A) + (global.get $g) + ) + ;; Copy the i32 with global to the i8. + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Read the i8. We do not optimize this because we cannot model the + ;; truncation of the global. TODO. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (global $g i32 (i32.const 0)) + (global $g i32 (i32.const 0)) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.get $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + (struct.set $A 0 + (local.get $A) + (global.get $g) + ) + ;; Copy the i8 with global to the i32. + (struct.set $B 0 + (local.get $B) + (struct.get_u $A 0 + (local.get $A) + ) + ) + ;; Read the i32. We still cannot optimize. TODO. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (global $g i32 (i32.const 0)) + (global $g i32 (i32.const 0)) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.shr_s + ;; CHECK-NEXT: (i32.shl + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: (i32.const 24) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 24) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.get $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + (struct.set $A 0 + (local.get $A) + (global.get $g) + ) + ;; Copy the i8 with global to the i32 with sign extension. + (struct.set $B 0 + (local.get $B) + (struct.get_s $A 0 + (local.get $A) + ) + ) + ;; Read the i32. We still cannot optimize. TODO. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ) + + ;; CHECK: (type $1 (func (param (ref $A)) (result i32))) + + ;; CHECK: (global $g i32 (i32.const 0)) + (global $g i32 (i32.const 0)) + + ;; CHECK: (func $test (type $1) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (struct.get_u $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.get_u $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (result i32) + (struct.set $A 0 + (local.get $A) + (global.get $g) + ) + ;; Copy the i8 holding the global to itself. + (struct.set $A 0 + (local.get $A) + (struct.get_u $A 0 + (local.get $A) + ) + ) + ;; Read the i8. We cannot model the truncated global. TODO. + (struct.get_u $A 0 + (local.get $A) + ) + ) +) + + +(module + (rec + ;; CHECK: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ) + + ;; CHECK: (type $1 (func (param (ref $A)) (result i32))) + + ;; CHECK: (global $g i32 (i32.const 0)) + (global $g i32 (i32.const 0)) + + ;; CHECK: (func $test (type $1) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (struct.get_s $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.get_u $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (result i32) + (struct.set $A 0 + (local.get $A) + (global.get $g) + ) + ;; Copy the i8 holding the global to itself with sign extension. + (struct.set $A 0 + (local.get $A) + (struct.get_s $A 0 + (local.get $A) + ) + ) + ;; Read the i8. We cannot model the truncated global. TODO. + (struct.get_u $A 0 + (local.get $A) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; Copy the uninitialized i8 to i32. + (struct.set $B 0 + (local.get $B) + (struct.get_u $A 0 + (local.get $A) + ) + ) + ;; Read the i32. It should still be uninitialized and optimized out. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; Copy the uninitialized i8 to i32 with sign extension. + (struct.set $B 0 + (local.get $B) + (struct.get_s $A 0 + (local.get $A) + ) + ) + ;; Read the i32. It should still be uninitialized and optimized out. + (struct.get $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i8)))) + (type $B (struct (field (mut i8)))) + ) + + ;; CHECK: (type $2 (func (param (ref $A) (ref $B)) (result i32))) + + ;; CHECK: (func $test (type $2) (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (result i32) + ;; Copy the uninitialized i32 to i8. + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Read the i8. It should still be uninitialized and optimized out. + (struct.get_u $B 0 + (local.get $B) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i8)))) + (type $B (struct (field (mut i8)))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $3 (func (param (ref $A) (ref $B) (ref $C)) (result i32))) + + ;; CHECK: (func $test (type $3) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x000001FF) + ) + ;; Copy i32 to i8. + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy the i8 to another i32. + (struct.set $C 0 + (local.get $C) + (struct.get_u $B 0 + (local.get $B) + ) + ) + ;; Read truncated i32. + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i8)))) + (type $B (struct (field (mut i8)))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $3 (func (param (ref $A) (ref $B) (ref $C)) (result i32))) + + ;; CHECK: (func $test (type $3) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.shr_s + ;; CHECK-NEXT: (i32.shl + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: (i32.const 24) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 24) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x000001FF) + ) + ;; Copy i32 to i8. + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy the i8 to another i32 with sign extension. + (struct.set $C 0 + (local.get $C) + (struct.get_s $B 0 + (local.get $B) + ) + ) + ;; Read truncated i32. + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ;; CHECK: (type $C (struct (field (mut i8)))) + (type $C (struct (field (mut i8)))) + ) + + ;; CHECK: (type $3 (func (param (ref $A) (ref $B) (ref $C)) (result i32))) + + ;; CHECK: (func $test (type $3) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x000001FF) + ) + ;; Copy i8 to i32. + (struct.set $B 0 + (local.get $B) + (struct.get_u $A 0 + (local.get $A) + ) + ) + ;; Copy the i32 to another i8. + (struct.set $C 0 + (local.get $C) + (struct.get $B 0 + (local.get $B) + ) + ) + ;; Read the i8. + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i8)))) + (type $A (struct (field (mut i8)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ;; CHECK: (type $C (struct (field (mut i8)))) + (type $C (struct (field (mut i8)))) + ) + + ;; CHECK: (type $3 (func (param (ref $A) (ref $B) (ref $C)) (result i32))) + + ;; CHECK: (func $test (type $3) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.shr_s + ;; CHECK-NEXT: (i32.shl + ;; CHECK-NEXT: (i32.const 511) + ;; CHECK-NEXT: (i32.const 24) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 24) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: (i32.const 255) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) (result i32) + (struct.set $A 0 + (local.get $A) + (i32.const 0x000001FF) + ) + ;; Copy i8 to i32 with sign extension. + (struct.set $B 0 + (local.get $B) + (struct.get_s $A 0 + (local.get $A) + ) + ) + ;; Copy the i32 to another i8. + (struct.set $C 0 + (local.get $C) + (struct.get $B 0 + (local.get $B) + ) + ) + ;; Read the i8. + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $3 (func (param (ref $A) (ref $B) (ref $C)) (result i32))) + + ;; CHECK: (func $test (type $3) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) (result i32) + ;; A and B separately copy matching values into C + (struct.set $A 0 + (local.get $A) + (i32.const 10) + ) + (struct.set $B 0 + (local.get $B) + (i32.const 10) + ) + ;; Copy A to C. + (struct.set $C 0 + (local.get $C) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy B to C. + (struct.set $C 0 + (local.get $C) + (struct.get $B 0 + (local.get $B) + ) + ) + ;; Read C. + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $3 (func (param (ref $A) (ref $B) (ref $C)) (result i32))) + + ;; CHECK: (func $test (type $3) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) (result i32) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.get $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) (result i32) + ;; Now A and B separately copy different values into C + (struct.set $A 0 + (local.get $A) + (i32.const 10) + ) + (struct.set $B 0 + (local.get $B) + (i32.const 20) + ) + ;; Copy A to C. + (struct.set $C 0 + (local.get $C) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy B to C. + (struct.set $C 0 + (local.get $C) + (struct.get $B 0 + (local.get $B) + ) + ) + ;; Read C. We cannot optimize because there are multiple values. + (struct.get $C 0 + (local.get $C) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $3 (func (param (ref $A) (ref $B) (ref $C)))) + + ;; CHECK: (func $test (type $3) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; Copy A to both B and C. + (struct.set $A 0 + (local.get $A) + (i32.const 10) + ) + ;; Copy A to B. + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy A to C. + (struct.set $C 0 + (local.get $C) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Read B. We can optimize. + (drop + (struct.get $B 0 + (local.get $B) + ) + ) + ;; Read C. We can optimize this, too. + (drop + (struct.get $C 0 + (local.get $C) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $3 (func (param (ref $A) (ref $B) (ref $C)))) + + ;; CHECK: (func $copy (type $3) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; Create a loop copying A -> B -> C -> A. + ;; Copy A to B. + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy B to C. + (struct.set $C 0 + (local.get $C) + (struct.get $B 0 + (local.get $B) + ) + ) + ;; Copy C to A. + (struct.set $A 0 + (local.get $A) + (struct.get $C 0 + (local.get $C) + ) + ) + ) + + ;; CHECK: (func $get (type $3) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; Since no value ever entered the loop, these all get optimized out. + (drop + (struct.get $A 0 + (local.get $A) + ) + ) + (drop + (struct.get $B 0 + (local.get $B) + ) + ) + (drop + (struct.get $C 0 + (local.get $C) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $3 (func (param (ref $A) (ref $B) (ref $C)))) + + ;; CHECK: (type $4 (func (param (ref $A)))) + + ;; CHECK: (func $init (type $4) (param $A (ref $A)) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $A (ref $A)) + ;; Now we inject a value into the loop. + (struct.set $A 0 + (local.get $A) + (i32.const 10) + ) + ) + + ;; CHECK: (func $copy (type $3) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; Create a loop copying A -> B -> C -> A. + ;; Copy A to B. + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy B to C. + (struct.set $C 0 + (local.get $C) + (struct.get $B 0 + (local.get $B) + ) + ) + ;; Copy C to A. + (struct.set $A 0 + (local.get $A) + (struct.get $C 0 + (local.get $C) + ) + ) + ) + + ;; CHECK: (func $get (type $3) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; These all get optimized to the one injected value. + (drop + (struct.get $A 0 + (local.get $A) + ) + ) + (drop + (struct.get $B 0 + (local.get $B) + ) + ) + (drop + (struct.get $C 0 + (local.get $C) + ) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $A (struct (field (mut i32)))) + (type $A (struct (field (mut i32)))) + ;; CHECK: (type $B (struct (field (mut i32)))) + (type $B (struct (field (mut i32)))) + ;; CHECK: (type $C (struct (field (mut i32)))) + (type $C (struct (field (mut i32)))) + ) + + ;; CHECK: (type $3 (func (param (ref $A) (ref $B) (ref $C)))) + + ;; CHECK: (type $4 (func (param (ref $A) (ref $C)))) + + ;; CHECK: (func $init (type $4) (param $A (ref $A)) (param $C (ref $C)) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $A (ref $A)) (param $C (ref $C)) + ;; Now we inject two different values into the loop in different places. We + ;; won't be able to optimize anything. + (struct.set $A 0 + (local.get $A) + (i32.const 10) + ) + (struct.set $C 0 + (local.get $C) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $3) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: (struct.get $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (struct.get $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; Create a loop copying A -> B -> C -> A. + ;; Copy A to B. + (struct.set $B 0 + (local.get $B) + (struct.get $A 0 + (local.get $A) + ) + ) + ;; Copy B to C. + (struct.set $C 0 + (local.get $C) + (struct.get $B 0 + (local.get $B) + ) + ) + ;; Copy C to A. + (struct.set $A 0 + (local.get $A) + (struct.get $C 0 + (local.get $C) + ) + ) + ) + + ;; CHECK: (func $get (type $3) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $C 0 + ;; CHECK-NEXT: (local.get $C) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C)) + ;; These cannot be optimized. + (drop + (struct.get $A 0 + (local.get $A) + ) + ) + (drop + (struct.get $B 0 + (local.get $B) + ) + ) + (drop + (struct.get $C 0 + (local.get $C) + ) + ) + ) +) diff --git a/test/lit/passes/cfp-reftest-copies.wast b/test/lit/passes/cfp-reftest-copies.wast new file mode 100644 index 00000000000..6422431617c --- /dev/null +++ b/test/lit/passes/cfp-reftest-copies.wast @@ -0,0 +1,744 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: foreach %s %t wasm-opt --closed-world --cfp-reftest -all -S -o - | filecheck %s + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (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 $other (sub (struct (field (mut i32))))) + (type $other (sub (struct (field (mut i32))))) + ) + + ;; CHECK: (type $3 (func (param (ref $other) (ref $A)))) + + ;; CHECK: (type $4 (func (param (ref $other) (ref (exact $A))))) + + ;; CHECK: (type $5 (func (param (ref $A)) (result i32))) + + ;; CHECK: (func $init (type $3) (param $other (ref $other)) (param $A (ref $A)) + ;; CHECK-NEXT: (struct.set $other 0 + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $other (ref $other)) (param $A (ref $A)) + (struct.set $other 0 + (local.get $other) + (i32.const 10) + ) + ;; Set inexact A. + (struct.set $A 0 + (local.get $A) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $4) (param $other (ref $other)) (param $A-exact (ref (exact $A))) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A-exact) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $A-exact (ref (exact $A))) + ;; Copy to exact A. + (struct.set $A 0 + (local.get $A-exact) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get (param $A (ref $A)) (result i32) + ;; We won't be able to optimize because the set of inexact A could go to + ;; anything. + (struct.get $A 0 + (local.get $A) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (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 $other (sub (struct (field (mut i32))))) + (type $other (sub (struct (field (mut i32))))) + ) + + ;; CHECK: (type $3 (func (param (ref $other) (ref $A)))) + + ;; CHECK: (type $4 (func (param (ref $other) (ref $B)))) + + ;; CHECK: (type $5 (func (param (ref $A)) (result i32))) + + ;; CHECK: (func $init (type $3) (param $other (ref $other)) (param $A (ref $A)) + ;; CHECK-NEXT: (struct.set $other 0 + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $other (ref $other)) (param $A (ref $A)) + (struct.set $other 0 + (local.get $other) + (i32.const 10) + ) + ;; Set inexact A. + (struct.set $A 0 + (local.get $A) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $4) (param $other (ref $other)) (param $B (ref $B)) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $B (ref $B)) + ;; Copy to inexact B. + (struct.set $B 0 + (local.get $B) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get (param $A (ref $A)) (result i32) + ;; We won't be able to optimize because the set of inexact A could go to + ;; anything. + (struct.get $A 0 + (local.get $A) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (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 $other (sub (struct (field (mut i32))))) + (type $other (sub (struct (field (mut i32))))) + ) + + ;; CHECK: (type $3 (func (param (ref $other) (ref $A)))) + + ;; CHECK: (type $4 (func (param (ref $other) (ref (exact $B))))) + + ;; CHECK: (type $5 (func (param (ref $A)) (result i32))) + + ;; CHECK: (func $init (type $3) (param $other (ref $other)) (param $A (ref $A)) + ;; CHECK-NEXT: (struct.set $other 0 + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $other (ref $other)) (param $A (ref $A)) + (struct.set $other 0 + (local.get $other) + (i32.const 10) + ) + ;; Set inexact A. + (struct.set $A 0 + (local.get $A) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $4) (param $other (ref $other)) (param $B-exact (ref (exact $B))) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B-exact) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $B-exact (ref (exact $B))) + ;; Copy to exact B. + (struct.set $B 0 + (local.get $B-exact) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get (param $A (ref $A)) (result i32) + ;; We won't be able to optimize because the set of inexact A could go to + ;; anything. + (struct.get $A 0 + (local.get $A) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (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 $other (sub (struct (field (mut i32))))) + (type $other (sub (struct (field (mut i32))))) + ) + + ;; CHECK: (type $3 (func (param (ref $other) (ref (exact $A))))) + + ;; CHECK: (type $4 (func (param (ref $other) (ref $A)))) + + ;; CHECK: (type $5 (func (param (ref $A)) (result i32))) + + ;; CHECK: (func $init (type $3) (param $other (ref $other)) (param $A-exact (ref (exact $A))) + ;; CHECK-NEXT: (struct.set $other 0 + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A-exact) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $other (ref $other)) (param $A-exact (ref (exact $A))) + (struct.set $other 0 + (local.get $other) + (i32.const 10) + ) + ;; Set exact A. + (struct.set $A 0 + (local.get $A-exact) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $4) (param $other (ref $other)) (param $A (ref $A)) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $A (ref $A)) + ;; Copy to inexact A. + (struct.set $A 0 + (local.get $A) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get (param $A (ref $A)) (result i32) + ;; We won't be able to optimize because the copy to inexact A could go to + ;; anything. + (struct.get $A 0 + (local.get $A) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (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 $other (sub (struct (field (mut i32))))) + (type $other (sub (struct (field (mut i32))))) + ) + + ;; CHECK: (type $3 (func (param (ref $other) (ref (exact $A))))) + + ;; CHECK: (type $4 (func (param (ref $other) (ref $B)))) + + ;; CHECK: (type $5 (func (param (ref $A)) (result i32))) + + ;; CHECK: (func $init (type $3) (param $other (ref $other)) (param $A-exact (ref (exact $A))) + ;; CHECK-NEXT: (struct.set $other 0 + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A-exact) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $other (ref $other)) (param $A-exact (ref (exact $A))) + (struct.set $other 0 + (local.get $other) + (i32.const 10) + ) + ;; Set exact A. + (struct.set $A 0 + (local.get $A-exact) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $4) (param $other (ref $other)) (param $B (ref $B)) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $B (ref $B)) + ;; Copy to inexact B. + (struct.set $B 0 + (local.get $B) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: (ref.test (ref $B) + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get (param $A (ref $A)) (result i32) + ;; We can optimize! Only B will have the copied value. + (struct.get $A 0 + (local.get $A) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (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 $other (sub (struct (field (mut i32))))) + (type $other (sub (struct (field (mut i32))))) + ) + + ;; CHECK: (type $3 (func (param (ref $other) (ref (exact $A))))) + + ;; CHECK: (type $4 (func (param (ref $other) (ref (exact $B))))) + + ;; CHECK: (type $5 (func (param (ref $A)) (result i32))) + + ;; CHECK: (func $init (type $3) (param $other (ref $other)) (param $A-exact (ref (exact $A))) + ;; CHECK-NEXT: (struct.set $other 0 + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A-exact) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $other (ref $other)) (param $A-exact (ref (exact $A))) + (struct.set $other 0 + (local.get $other) + (i32.const 10) + ) + ;; Set exact A. + (struct.set $A 0 + (local.get $A-exact) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $4) (param $other (ref $other)) (param $B-exact (ref (exact $B))) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B-exact) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $B-exact (ref (exact $B))) + ;; Copy to exact B. + (struct.set $B 0 + (local.get $B-exact) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: (ref.test (ref $B) + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get (param $A (ref $A)) (result i32) + ;; We can optimize! Only B will have the copied value. + (struct.get $A 0 + (local.get $A) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (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 $other (sub (struct (field (mut i32))))) + (type $other (sub (struct (field (mut i32))))) + ) + + ;; CHECK: (type $3 (func (param (ref $other) (ref (exact $B))))) + + ;; CHECK: (type $4 (func (param (ref $other) (ref (exact $A))))) + + ;; CHECK: (type $5 (func (param (ref $A)) (result i32))) + + ;; CHECK: (func $init (type $3) (param $other (ref $other)) (param $B-exact (ref (exact $B))) + ;; CHECK-NEXT: (struct.set $other 0 + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B-exact) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $other (ref $other)) (param $B-exact (ref (exact $B))) + (struct.set $other 0 + (local.get $other) + (i32.const 10) + ) + ;; Set exact B. + (struct.set $B 0 + (local.get $B-exact) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $4) (param $other (ref $other)) (param $A-exact (ref (exact $A))) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A-exact) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $A-exact (ref (exact $A))) + ;; Copy to exact A. + (struct.set $A 0 + (local.get $A-exact) + (struct.get $other 0 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (ref.test (ref $B) + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get (param $A (ref $A)) (result i32) + ;; Switch the copy and set destinations from the previous case. We can still + ;; optimize! + (struct.get $A 0 + (local.get $A) + ) + ) +) + + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (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 $other (sub (struct (field (mut i32)) (field (mut i32))))) + (type $other (sub (struct (field (mut i32)) (field (mut i32))))) + ) + + ;; CHECK: (type $3 (func (param (ref $other) (ref (exact $B))))) + + ;; CHECK: (type $4 (func (param (ref $other) (ref (exact $A))))) + + ;; CHECK: (type $5 (func (param (ref $A)) (result i32))) + + ;; CHECK: (func $init (type $3) (param $other (ref $other)) (param $B-exact (ref (exact $B))) + ;; CHECK-NEXT: (struct.set $other 0 + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B-exact) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $other (ref $other)) (param $B-exact (ref (exact $B))) + (struct.set $other 0 + (local.get $other) + (i32.const 10) + ) + ;; Set exact B. + (struct.set $B 0 + (local.get $B-exact) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $4) (param $other (ref $other)) (param $A-exact (ref (exact $A))) + ;; CHECK-NEXT: (struct.set $other 1 + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A-exact) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $A-exact (ref (exact $A))) + ;; Copy to another field and from there to exact A. + (struct.set $other 1 + (local.get $other) + (struct.get $other 0 + (local.get $other) + ) + ) + ;; Copy to exact A. + (struct.set $A 0 + (local.get $A-exact) + (struct.get $other 1 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (ref.test (ref $B) + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get (param $A (ref $A)) (result i32) + ;; We can still optimize after doing two copies. + (struct.get $A 0 + (local.get $A) + ) + ) +) + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (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 $other (sub (struct (field (mut i32)) (field (mut i32))))) + (type $other (sub (struct (field (mut i32)) (field (mut i32))))) + ) + + ;; CHECK: (type $3 (func (param (ref $other) (ref (exact $B))))) + + ;; CHECK: (type $4 (func (param (ref $other) (ref $A)))) + + ;; CHECK: (type $5 (func (param (ref $A)) (result i32))) + + ;; CHECK: (func $init (type $3) (param $other (ref $other)) (param $B-exact (ref (exact $B))) + ;; CHECK-NEXT: (struct.set $other 0 + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $B 0 + ;; CHECK-NEXT: (local.get $B-exact) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $init (param $other (ref $other)) (param $B-exact (ref (exact $B))) + (struct.set $other 0 + (local.get $other) + (i32.const 10) + ) + ;; Set exact B. + (struct.set $B 0 + (local.get $B-exact) + (i32.const 20) + ) + ) + + ;; CHECK: (func $copy (type $4) (param $other (ref $other)) (param $A (ref $A)) + ;; CHECK-NEXT: (struct.set $other 1 + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.set $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $copy (param $other (ref $other)) (param $A (ref $A)) + ;; Copy to another field and from there to inexact A. + (struct.set $other 1 + (local.get $other) + (struct.get $other 0 + (local.get $other) + ) + ) + ;; Copy to A. + (struct.set $A 0 + (local.get $A) + (struct.get $other 1 + (local.get $other) + ) + ) + ) + + ;; CHECK: (func $get (type $5) (param $A (ref $A)) (result i32) + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (local.get $A) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get (param $A (ref $A)) (result i32) + ;; We cannot optimize because the copy to inexact A could write to anything. + (struct.get $A 0 + (local.get $A) + ) + ) +) diff --git a/test/lit/passes/cfp.wast b/test/lit/passes/cfp.wast index 4478ced671f..de34fb7f4c2 100644 --- a/test/lit/passes/cfp.wast +++ b/test/lit/passes/cfp.wast @@ -1695,155 +1695,6 @@ ) ) -;; Copies of a field to itself can be ignored. As a result, we can optimize both -;; of the gets here. -(module - ;; CHECK: (type $struct (struct (field (mut i32)))) - (type $struct (struct (mut i32))) - - ;; CHECK: (type $1 (func (param (ref null $struct) (ref null $struct)))) - - ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) (param $other (ref null $struct)) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (struct.new_default $struct) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (struct.set $struct 0 - ;; CHECK-NEXT: (local.get $struct) - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.as_non_null - ;; CHECK-NEXT: (local.get $struct) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.as_non_null - ;; CHECK-NEXT: (local.get $other) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $test (param $struct (ref null $struct)) (param $other (ref null $struct)) - (drop - (struct.new_default $struct) - ) - ;; This copy does not actually introduce any new possible values, and so it - ;; remains true that the only possible value is the default. - (struct.set $struct 0 - (local.get $struct) - (struct.get $struct 0 - (local.get $struct) - ) - ) - (drop - (struct.get $struct 0 - (local.get $other) - ) - ) - ) -) - -;; Test of a near-copy, of a similar looking field (same index, and same field -;; type) but in a different struct. -(module - ;; CHECK: (type $struct (struct (field (mut f32)) (field (mut i32)))) - (type $struct (struct (mut f32) (mut i32))) - ;; CHECK: (type $other (struct (field (mut f64)) (field (mut i32)))) - (type $other (struct (mut f64) (mut i32))) - - ;; CHECK: (type $2 (func (param (ref null $struct) (ref null $other)))) - - ;; CHECK: (func $test (type $2) (param $struct (ref null $struct)) (param $other (ref null $other)) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (struct.new_default $struct) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (struct.set $struct 1 - ;; CHECK-NEXT: (local.get $struct) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $other) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (struct.get $struct 1 - ;; CHECK-NEXT: (local.get $struct) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $test (param $struct (ref null $struct)) (param $other (ref null $other)) - (drop - (struct.new_default $struct) - ) - ;; As this is not a copy, we cannot optimize struct.1's get lower down. - (struct.set $struct 1 - (local.get $struct) - (struct.get $other 1 - (local.get $other) - ) - ) - (drop - (struct.get $struct 1 - (local.get $struct) - ) - ) - ) -) - -;; Test of a near-copy, of a different index. -(module - ;; CHECK: (type $struct (struct (field (mut i32)) (field (mut i32)))) - (type $struct (struct (mut i32) (mut i32))) - - ;; CHECK: (type $1 (func (param (ref null $struct)))) - - ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (struct.new_default $struct) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (struct.set $struct 0 - ;; CHECK-NEXT: (local.get $struct) - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.as_non_null - ;; CHECK-NEXT: (local.get $struct) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (struct.get $struct 0 - ;; CHECK-NEXT: (local.get $struct) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - (func $test (param $struct (ref null $struct)) - (drop - (struct.new_default $struct) - ) - ;; As this is not a copy, we cannot optimize struct.0's get lower down. - (struct.set $struct 0 - (local.get $struct) - (struct.get $struct 1 - (local.get $struct) - ) - ) - (drop - (struct.get $struct 0 - (local.get $struct) - ) - ) - ) -) - (module ;; CHECK: (type $struct (struct (field i32))) (type $struct (struct i32))