diff --git a/scripts/test/fuzzing.py b/scripts/test/fuzzing.py index 50ab56683fa..a65d3a92a21 100644 --- a/scripts/test/fuzzing.py +++ b/scripts/test/fuzzing.py @@ -111,6 +111,14 @@ 'dce-stack-switching.wast', 'precompute-stack-switching.wast', 'vacuum-stack-switching.wast', + # TODO: fuzzer support for exact references + 'exact-references.wast', + 'optimize-instructions-exact.wast', + 'local-subtyping-exact.wast', + 'remove-unused-types-exact.wast', + 'coalesce-locals-exact.wast', + 'remove-unused-brs-exact.wast', + 'exact.wast', # TODO: fuzzer support for custom descriptors 'custom-descriptors.wast', ] diff --git a/src/ir/manipulation.h b/src/ir/manipulation.h index e7816af9fce..c766fb8719e 100644 --- a/src/ir/manipulation.h +++ b/src/ir/manipulation.h @@ -40,10 +40,9 @@ template inline Nop* nop(InputType* target) { } template -inline RefNull* refNull(InputType* target, Type type) { - assert(type.isNullable() && type.getHeapType().isBottom()); +inline RefNull* refNull(InputType* target, HeapType type) { auto* ret = convert(target); - ret->finalize(type); + ret->finalize(Type(type.getBottom(), Nullable, Exact)); return ret; } diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp index 6ea5b0d87cd..59286734720 100644 --- a/src/ir/type-updating.cpp +++ b/src/ir/type-updating.cpp @@ -346,6 +346,7 @@ Type GlobalTypeRewriter::getTempType(Type type) { if (type.isRef()) { auto heapType = type.getHeapType(); if (auto it = typeIndices.find(heapType); it != typeIndices.end()) { + // TODO: Handle exactness. return typeBuilder.getTempRefType(typeBuilder[it->second], type.getNullability()); } diff --git a/src/literal.h b/src/literal.h index c3cf2c15f70..4f9e3d535ad 100644 --- a/src/literal.h +++ b/src/literal.h @@ -246,7 +246,7 @@ class Literal { } } static Literal makeNull(HeapType type) { - return Literal(Type(type.getBottom(), Nullable)); + return Literal(Type(type.getBottom(), Nullable, Exact)); } static Literal makeFunc(Name func, HeapType type) { return Literal(func, type); diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 4c56780323b..fa96c270670 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -127,7 +127,9 @@ struct NullTypeParserCtx { TypeT makeF64() { return Ok{}; } TypeT makeV128() { return Ok{}; } - TypeT makeRefType(HeapTypeT, Nullability) { return Ok{}; } + TypeT makeRefType(HeapTypeT, Nullability, Exactness) { return Ok{}; } + + HeapTypeT getHeapTypeFromRefType(TypeT) { return Ok{}; } TupleElemListT makeTupleElemList() { return Ok{}; } void appendTupleElem(TupleElemListT&, TypeT) {} @@ -257,10 +259,13 @@ template struct TypeParserCtx { TypeT makeF64() { return Type::f64; } TypeT makeV128() { return Type::v128; } - TypeT makeRefType(HeapTypeT ht, Nullability nullability) { - return Type(ht, nullability); + TypeT + makeRefType(HeapTypeT ht, Nullability nullability, Exactness exactness) { + return Type(ht, nullability, exactness); } + HeapTypeT getHeapTypeFromRefType(TypeT t) { return t.getHeapType(); } + std::vector makeTupleElemList() { return {}; } void appendTupleElem(std::vector& elems, Type elem) { elems.push_back(elem); @@ -1117,10 +1122,13 @@ struct ParseTypeDefsCtx : TypeParserCtx { : TypeParserCtx(typeIndices), in(in), builder(builder), names(builder.size()) {} - TypeT makeRefType(HeapTypeT ht, Nullability nullability) { - return builder.getTempRefType(ht, nullability); + TypeT + makeRefType(HeapTypeT ht, Nullability nullability, Exactness exactness) { + return builder.getTempRefType(ht, nullability, exactness); } + HeapTypeT getHeapTypeFromRefType(TypeT t) { return t.getHeapType(); } + TypeT makeTupleType(const std::vector types) { return builder.getTempTupleType(types); } diff --git a/src/parser/parsers.h b/src/parser/parsers.h index 33e9d20fdc9..26c20767938 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -30,6 +30,8 @@ using namespace std::string_view_literals; template Result absheaptype(Ctx&, Shareability); template Result heaptype(Ctx&); +template +MaybeResult maybeReftypeAbbrev(Ctx&); template MaybeResult maybeReftype(Ctx&); template Result reftype(Ctx&); template MaybeResult tupletype(Ctx&); @@ -458,68 +460,85 @@ template Result heaptype(Ctx& ctx) { // | 'i31ref' => i31ref // | 'structref' => structref // | 'arrayref' => arrayref -// | '(' ref null? t:heaptype ')' => ref null? t -template MaybeResult maybeReftype(Ctx& ctx) { +// | ... +template +MaybeResult maybeReftypeAbbrev(Ctx& ctx) { if (ctx.in.takeKeyword("funcref"sv)) { - return ctx.makeRefType(ctx.makeFuncType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeFuncType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("externref"sv)) { - return ctx.makeRefType(ctx.makeExternType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeExternType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("anyref"sv)) { - return ctx.makeRefType(ctx.makeAnyType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeAnyType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("eqref"sv)) { - return ctx.makeRefType(ctx.makeEqType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeEqType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("i31ref"sv)) { - return ctx.makeRefType(ctx.makeI31Type(Unshared), Nullable); + return ctx.makeRefType(ctx.makeI31Type(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("structref"sv)) { - return ctx.makeRefType(ctx.makeStructType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeStructType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("arrayref"sv)) { - return ctx.makeRefType(ctx.makeArrayType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeArrayType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("exnref"sv)) { - return ctx.makeRefType(ctx.makeExnType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeExnType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("stringref"sv)) { - return ctx.makeRefType(ctx.makeStringType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeStringType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("contref"sv)) { - return ctx.makeRefType(ctx.makeContType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeContType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("nullref"sv)) { - return ctx.makeRefType(ctx.makeNoneType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeNoneType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("nullexternref"sv)) { - return ctx.makeRefType(ctx.makeNoextType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeNoextType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("nullfuncref"sv)) { - return ctx.makeRefType(ctx.makeNofuncType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeNofuncType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("nullexnref"sv)) { - return ctx.makeRefType(ctx.makeNoexnType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeNoexnType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("nullcontref"sv)) { - return ctx.makeRefType(ctx.makeNocontType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeNocontType(Unshared), Nullable, Inexact); } + return {}; +} - if (!ctx.in.takeSExprStart("ref"sv)) { - return {}; +// reftype ::= ... +// | '(' 'exact' (ref null ht):shorthand ')' => ref null exact ht +// | '(' ref null? exact? ht:heaptype ')' => ref null? t +template MaybeResult maybeReftype(Ctx& ctx) { + if (ctx.in.takeSExprStart("exact"sv)) { + auto rt = maybeReftypeAbbrev(ctx); + CHECK_ERR(rt); + if (!rt) { + return ctx.in.err("expected reftype shorthand"); + } + if (!ctx.in.takeRParen()) { + return ctx.in.err("expected end of reftype"); + } + return ctx.makeRefType(ctx.getHeapTypeFromRefType(*rt), Nullable, Exact); } - auto nullability = ctx.in.takeKeyword("null"sv) ? Nullable : NonNullable; - - auto type = heaptype(ctx); - CHECK_ERR(type); - - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of reftype"); + if (ctx.in.takeSExprStart("ref"sv)) { + auto nullability = ctx.in.takeKeyword("null"sv) ? Nullable : NonNullable; + auto exactness = ctx.in.takeKeyword("exact"sv) ? Exact : Inexact; + auto type = heaptype(ctx); + CHECK_ERR(type); + if (!ctx.in.takeRParen()) { + return ctx.in.err("expected end of reftype"); + } + return ctx.makeRefType(*type, nullability, exactness); } - return ctx.makeRefType(*type, nullability); + return maybeReftypeAbbrev(ctx); } template Result reftype(Ctx& ctx) { diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 7ade7ac3261..8c13663deba 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2291,10 +2291,14 @@ struct OptimizeInstructions // emit a null check. bool needsNullCheck = ref->type.getNullability() == Nullable && curr->type.getNullability() == NonNullable; + // Same with exactness. + bool needsExactCast = ref->type.getExactness() == Inexact && + curr->type.getExactness() == Exact; // If the best value to propagate is the argument to the cast, we can // simply remove the cast (or downgrade it to a null check if - // necessary). - if (ref == curr->ref) { + // necessary). This does not work if we need a cast to prove + // exactness. + if (ref == curr->ref && !needsExactCast) { if (needsNullCheck) { replaceCurrent(builder.makeRefAs(RefAsNonNull, curr->ref)); } else { @@ -2303,9 +2307,9 @@ struct OptimizeInstructions return; } // Otherwise we can't just remove the cast and replace it with `ref` - // because the intermediate expressions might have had side effects. - // We can replace the cast with a drop followed by a direct return of - // the value, though. + // because the intermediate expressions might have had side effects or + // we need to check exactness. We can replace the cast with a drop + // followed by a direct return of the value, though. if (ref->type.isNull()) { // We can materialize the resulting null value directly. // @@ -2313,7 +2317,7 @@ struct OptimizeInstructions // would be, aside from the interesting corner case of // uninhabitable types: // - // (ref.cast func + // (ref.cast (ref func) // (block (result (ref nofunc)) // (unreachable) // ) @@ -2360,18 +2364,20 @@ struct OptimizeInstructions } [[fallthrough]]; case GCTypeUtils::SuccessOnlyIfNull: { - auto nullType = Type(curr->type.getHeapType().getBottom(), Nullable); // The cast either returns null or traps. In trapsNeverHappen mode // we know the result, since by assumption it will not trap. if (getPassOptions().trapsNeverHappen) { - replaceCurrent(builder.makeBlock( - {builder.makeDrop(curr->ref), builder.makeRefNull(nullType)}, - curr->type)); + replaceCurrent( + builder.makeBlock({builder.makeDrop(curr->ref), + builder.makeRefNull(curr->type.getHeapType())}, + curr->type)); return; } // Otherwise, we should have already refined the cast type to cast - // directly to null. - assert(curr->type == nullType); + // directly to null. We do not further refine the cast type to exact + // null because the extra precision is not useful and doing so would + // increase the size of the instruction encoding. + assert(curr->type.isNull()); break; } case GCTypeUtils::Unreachable: diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index 6bf9114d604..6fa69e70cd7 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -859,8 +859,8 @@ struct RemoveUnusedBrs : public WalkerPass> { if (Type::isSubType(expr->type, type)) { return expr; } - if (HeapType::isSubType(expr->type.getHeapType(), - type.getHeapType())) { + if (type.isNonNullable() && expr->type.isNullable() && + Type::isSubType(expr->type.with(NonNullable), type)) { return builder.makeRefAs(RefAsNonNull, expr); } return builder.makeRefCast(expr, type); diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 383e5af70c1..bb43fa39fa2 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -360,6 +360,10 @@ class TranslateToFuzzReader { // instruction for EH is supposed to exist only at the beginning of a 'catch' // block, so it shouldn't be moved around or deleted freely. bool canBeArbitrarilyReplaced(Expression* curr) { + // TODO: Remove this once we better support exact references. + if (curr->type.isExact()) { + return false; + } return curr->type.isDefaultable() && !EHUtils::containsValidDanglingPop(curr); } @@ -521,7 +525,9 @@ class TranslateToFuzzReader { Type getLoggableType(); bool isLoggableType(Type type); Nullability getNullability(); + Exactness getExactness(); Nullability getSubType(Nullability nullability); + Exactness getSubType(Exactness exactness); HeapType getSubType(HeapType type); Type getSubType(Type type); Nullability getSuperType(Nullability nullability); diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 683ec6849bc..864705cea83 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -1566,20 +1566,22 @@ void TranslateToFuzzReader::recombine(Function* func) { } std::vector ret; - auto heapType = type.getHeapType(); - auto nullability = type.getNullability(); + ret.push_back(type); - if (nullability == NonNullable) { - ret = getRelevantTypes(Type(heapType, Nullable)); + if (type.isNonNullable()) { + auto nullable = getRelevantTypes(type.with(Nullable)); + ret.insert(ret.end(), nullable.begin(), nullable.end()); + } + if (type.isExact()) { + auto inexact = getRelevantTypes(type.with(Inexact)); + ret.insert(ret.end(), inexact.begin(), inexact.end()); + // Do not consider exact references to supertypes. + return ret; } - while (1) { - ret.push_back(Type(heapType, nullability)); - auto super = heapType.getSuperType(); - if (!super) { - break; - } - heapType = *super; + for (auto heapType = type.getHeapType().getSuperType(); heapType; + heapType = heapType->getSuperType()) { + ret.push_back(type.with(*heapType)); } return ret; @@ -4968,9 +4970,17 @@ static auto makeArrayBoundsCheck(Expression* ref, Function* func, Builder& builder, Expression* length = nullptr) { - auto tempRef = builder.addVar(func, ref->type); + // The reference might be a RefNull, in which case its type is exact. But we + // want to avoid creating exact-typed locals until we support them more widely + // in the fuzzer, so adjust the type. TODO: remove this once exact references + // are better supported. + Type refType = ref->type; + if (refType.isExact()) { + refType = refType.with(Inexact); + } + auto tempRef = builder.addVar(func, refType); auto tempIndex = builder.addVar(func, index->type); - auto* teeRef = builder.makeLocalTee(tempRef, ref, ref->type); + auto* teeRef = builder.makeLocalTee(tempRef, ref, refType); auto* teeIndex = builder.makeLocalTee(tempIndex, index, index->type); auto* getSize = builder.makeArrayLen(teeRef); @@ -4997,7 +5007,7 @@ static auto makeArrayBoundsCheck(Expression* ref, // An additional use of the length, if it was provided. Expression* getLength = nullptr; } result = {builder.makeBinary(LtUInt32, effectiveIndex, getSize), - builder.makeLocalGet(tempRef, ref->type), + builder.makeLocalGet(tempRef, refType), builder.makeLocalGet(tempIndex, index->type), getLength}; return result; @@ -5386,6 +5396,23 @@ Nullability TranslateToFuzzReader::getNullability() { return Nullable; } +Exactness TranslateToFuzzReader::getExactness() { + // Without GC, the only heap types are func and extern, neither of which is + // exactly inhabitable. To avoid introducing uninhabitable types, only + // generate exact references when GC is enabled. We don't need custom + // descriptors to be enabled even though that is the feature that introduces + // exact references because the binary writer can always generalize the exact + // reference types away. + // + // if (wasm.features.hasGC() && oneIn(8)) { + // return Exact; + // } + // + // However, we cannot yet handle creating exact references in general, so for + // now we always generate inexact references when given the choice. TODO. + return Inexact; +} + Nullability TranslateToFuzzReader::getSubType(Nullability nullability) { if (nullability == NonNullable) { return NonNullable; @@ -5393,6 +5420,13 @@ Nullability TranslateToFuzzReader::getSubType(Nullability nullability) { return getNullability(); } +Exactness TranslateToFuzzReader::getSubType(Exactness exactness) { + if (exactness == Exact) { + return Exact; + } + return getExactness(); +} + HeapType TranslateToFuzzReader::getSubType(HeapType type) { if (oneIn(3)) { return type; @@ -5486,9 +5520,18 @@ Type TranslateToFuzzReader::getSubType(Type type) { if (!funcContext && heapType.isMaybeShared(HeapType::exn)) { return type; } - heapType = getSubType(heapType); + if (type.isExact()) { + // The only other possible heap type is bottom, but we don't want to + // generate too many bottom types. + if (!heapType.isBottom() && oneIn(20)) { + heapType = heapType.getBottom(); + } + } else { + heapType = getSubType(heapType); + } auto nullability = getSubType(type.getNullability()); - auto subType = Type(heapType, nullability); + auto exactness = getSubType(type.getExactness()); + auto subType = Type(heapType, nullability, exactness); // We don't want to emit lots of uninhabitable types like (ref none), so // avoid them with high probability. Specifically, if the original type was // inhabitable then return that; avoid adding more uninhabitability. diff --git a/src/wasm-binary.h b/src/wasm-binary.h index e83645837fc..211b15183d8 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -304,6 +304,13 @@ enum SegmentFlag { UsesExpressions = 1 << 2 }; +enum BrOnCastFlag { + InputNullable = 1 << 0, + OutputNullable = 1 << 1, + InputExact = 1 << 2, + OutputExact = 1 << 3, +}; + enum EncodedType { // value types i32 = -0x1, // 0x7f @@ -327,6 +334,7 @@ enum EncodedType { eqref = -0x13, // 0x6d nonnullable = -0x1c, // 0x64 nullable = -0x1d, // 0x63 + exact = -0x1e, // 0x62 contref = -0x18, // 0x68 nullcontref = -0x0b, // 0x75 // exception handling @@ -1127,6 +1135,8 @@ enum ASTNodes { I31GetS = 0x1d, I31GetU = 0x1e, RefI31Shared = 0x1f, + RefTestRT = 0x20, + RefCastRT = 0x21, // Shared GC Opcodes @@ -1499,6 +1509,7 @@ class WasmBinaryReader { Type getType(); // Get a type given the initial S32LEB has already been read, and is provided. Type getType(int code); + Type getTypeNoExact(int code); HeapType getHeapType(); HeapType getIndexedHeapType(); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index a8a76f4740c..04b9cb2a1ad 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -670,11 +670,11 @@ class Builder { } RefNull* makeRefNull(HeapType type) { auto* ret = wasm.allocator.alloc(); - ret->finalize(Type(type.getBottom(), Nullable)); + ret->finalize(Type(type.getBottom(), Nullable, Exact)); return ret; } RefNull* makeRefNull(Type type) { - assert(type.isNullable() && type.isNull()); + assert(type.isNullable() && type.isNull() && type.isExact()); auto* ret = wasm.allocator.alloc(); ret->finalize(type); return ret; @@ -1270,7 +1270,7 @@ class Builder { return makeConst(value); } if (value.isNull()) { - return makeRefNull(type); + return makeRefNull(type.getHeapType()); } if (type.isFunction()) { return makeRefFunc(value.getFunc(), type.getHeapType()); @@ -1435,8 +1435,8 @@ class Builder { return maybeWrap(makeConstantExpression(Literal::makeZeros(curr->type))); } if (curr->type.isNullable()) { - return maybeWrap(ExpressionManipulator::refNull( - curr, Type(curr->type.getHeapType().getBottom(), Nullable))); + return maybeWrap( + ExpressionManipulator::refNull(curr, curr->type.getHeapType())); } if (curr->type.isRef() && curr->type.getHeapType().isMaybeShared(HeapType::i31)) { diff --git a/src/wasm-type.h b/src/wasm-type.h index a72ba9d2cfa..b250e38c9c8 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -62,6 +62,7 @@ using Tuple = TypeList; enum Nullability { NonNullable, Nullable }; enum Mutability { Immutable, Mutable }; +enum Exactness { Inexact, Exact }; // HeapType name information used for printing. struct TypeNames { @@ -95,13 +96,13 @@ class HeapType { // should also be passed by value. uintptr_t id; - static constexpr int TypeBits = 2; + static constexpr int TypeBits = 3; static constexpr int UsedBits = TypeBits + 1; static constexpr int SharedMask = 1 << TypeBits; public: - // Bits 0-1 are used by the Type representation, so need to be left free. - // Bit 2 determines whether the basic heap type is shared (1) or unshared (0). + // Bits 0-2 are used by the Type representation, so need to be left free. + // Bit 3 determines whether the basic heap type is shared (1) or unshared (0). enum BasicHeapType : uint32_t { ext = 1 << UsedBits, func = 2 << UsedBits, @@ -278,7 +279,7 @@ class Type { // bit 0 set. When that bit is masked off, they are pointers to the underlying // vectors of types. Otherwise, the type is a reference type, and is // represented as a heap type with bit 1 set iff the reference type is - // nullable. + // nullable and bit 2 set iff the reference type is exact. // // Since `Type` is really just a single integer, it should be passed by value. // This is a uintptr_t rather than a TypeID (uint64_t) to save memory on @@ -287,6 +288,7 @@ class Type { static constexpr int TupleMask = 1 << 0; static constexpr int NullMask = 1 << 1; + static constexpr int ExactMask = 1 << 2; public: enum BasicType : uint32_t { @@ -317,9 +319,10 @@ class Type { // Construct from a heap type description. Also covers construction from // Signature, Struct or Array via implicit conversion to HeapType. - Type(HeapType heapType, Nullability nullable) - : Type(heapType.getID() | (nullable == Nullable ? NullMask : 0)) { - assert(heapType.isBasic() || !(heapType.getID() & (TupleMask | NullMask))); + Type(HeapType heapType, Nullability nullable, Exactness exact = Inexact) + : Type(heapType.getID() | (nullable == Nullable ? NullMask : 0) | + (exact == Exact ? ExactMask : 0)) { + assert(!(heapType.getID() & (TupleMask | NullMask | ExactMask))); } // Predicates @@ -368,9 +371,11 @@ class Type { bool isRef() const { return !isBasic() && !(id & TupleMask); } bool isNullable() const { return isRef() && (id & NullMask); } bool isNonNullable() const { return isRef() && !(id & NullMask); } + bool isExact() const { return isRef() && (id & ExactMask); } + bool isInexact() const { return isRef() && !(id & ExactMask); } HeapType getHeapType() const { assert(isRef()); - return HeapType(id & ~NullMask); + return HeapType(id & ~(NullMask | ExactMask)); } bool isFunction() const { return isRef() && getHeapType().isFunction(); } @@ -392,11 +397,20 @@ class Type { Nullability getNullability() const { return isNullable() ? Nullable : NonNullable; } + Exactness getExactness() const { + assert(isRef()); + return isExact() ? Exact : Inexact; + } // Return a new reference type with some part updated to the specified value. - Type with(HeapType heapType) { return Type(heapType, getNullability()); } + Type with(HeapType heapType) { + return Type(heapType, getNullability(), getExactness()); + } Type with(Nullability nullability) { - return Type(getHeapType(), nullability); + return Type(getHeapType(), nullability, getExactness()); + } + Type with(Exactness exactness) { + return Type(getHeapType(), getNullability(), exactness); } private: @@ -716,7 +730,8 @@ struct TypeBuilder { return t; } assert(t.isRef()); - return getTempRefType(map(t.getHeapType()), t.getNullability()); + return getTempRefType( + map(t.getHeapType()), t.getNullability(), t.getExactness()); }; auto copyType = [&](Type t) -> Type { if (t.isTuple()) { @@ -769,7 +784,9 @@ struct TypeBuilder { // TypeBuilder's HeapTypes. For Ref types, the HeapType may be a temporary // HeapType owned by this builder or a canonical HeapType. Type getTempTupleType(const Tuple&); - Type getTempRefType(HeapType heapType, Nullability nullable); + Type getTempRefType(HeapType heapType, + Nullability nullable, + Exactness exact = Inexact); // Declare the HeapType being built at index `i` to be an immediate subtype of // the given HeapType. diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp index 9e015501a0a..de95fe565bf 100644 --- a/src/wasm/literal.cpp +++ b/src/wasm/literal.cpp @@ -72,7 +72,9 @@ Literal::Literal(const uint8_t init[16]) : type(Type::v128) { } Literal::Literal(std::shared_ptr gcData, HeapType type) - : gcData(gcData), type(type, gcData ? NonNullable : Nullable) { + : gcData(gcData), + type(type, gcData ? NonNullable : Nullable, gcData ? Inexact : Exact) { + // TODO: Use exact types for more than just nulls. // The type must be a proper type for GC data: either a struct, array, or // string; or an externalized version of the same; or a null; or an // internalized string (which appears as an anyref). diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index a841d44638d..5f113a93811 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1574,6 +1574,12 @@ void WasmBinaryWriter::writeInlineBuffer(const char* data, size_t size) { void WasmBinaryWriter::writeType(Type type) { if (type.isRef()) { + // Exact references are introduced by the custom descriptors feature, but + // can be used internally even when it is not enabled. In that case, we have + // to generalize the types to be inexact before writing them. + if (!wasm->features.hasCustomDescriptors()) { + type = Type(type.getHeapType(), type.getNullability(), Inexact); + } // The only reference types allowed without GC are funcref, externref, and // exnref. We internally use more refined versions of those types, but we // cannot emit those without GC. @@ -1590,6 +1596,12 @@ void WasmBinaryWriter::writeType(Type type) { type = Type(type.getHeapType().getTop(), Nullable); } } + // If the type is exact, emit the exact prefix and continue on without + // considering exactness. + if (type.isExact()) { + o << S32LEB(BinaryConsts::EncodedType::exact); + type = Type(type.getHeapType(), type.getNullability(), Inexact); + } auto heapType = type.getHeapType(); if (type.isNullable() && heapType.isBasic() && !heapType.isShared()) { switch (heapType.getBasic(Unshared)) { @@ -2178,7 +2190,7 @@ Signature WasmBinaryReader::getBlockType() { return Signature(Type::none, getType(code)); } -Type WasmBinaryReader::getType(int code) { +Type WasmBinaryReader::getTypeNoExact(int code) { Type type; if (getBasicType(code, type)) { return type; @@ -2194,6 +2206,17 @@ Type WasmBinaryReader::getType(int code) { WASM_UNREACHABLE("unexpected type"); } +Type WasmBinaryReader::getType(int code) { + if (code == BinaryConsts::EncodedType::exact) { + auto type = getTypeNoExact(getS32LEB()); + if (!type.isRef()) { + throwError("invalid exact prefix on non-reference type"); + } + return Type(type.getHeapType(), type.getNullability(), Exact); + } + return getTypeNoExact(code); +} + Type WasmBinaryReader::getType() { return getType(getS32LEB()); } HeapType WasmBinaryReader::getHeapType() { @@ -2354,7 +2377,7 @@ void WasmBinaryReader::readTypes() { } return builder.getTempHeapType(size_t(htCode)); }; - auto makeType = [&](int32_t typeCode) { + auto makeTypeNoExact = [&](int32_t typeCode) { Type type; if (getBasicType(typeCode, type)) { return type; @@ -2379,6 +2402,17 @@ void WasmBinaryReader::readTypes() { } WASM_UNREACHABLE("unexpected type"); }; + auto makeType = [&](int32_t typeCode) { + if (typeCode == BinaryConsts::EncodedType::exact) { + auto type = makeTypeNoExact(getS32LEB()); + if (!type.isRef()) { + throwError("unexpected exact prefix on non-reference type"); + } + return builder.getTempRefType( + type.getHeapType(), type.getNullability(), Exact); + } + return makeTypeNoExact(typeCode); + }; auto readType = [&]() { return makeType(getS32LEB()); }; auto readSignatureDef = [&]() { @@ -4252,16 +4286,30 @@ Result<> WasmBinaryReader::readInst() { return builder.makeRefTest(Type(getHeapType(), NonNullable)); case BinaryConsts::RefTestNull: return builder.makeRefTest(Type(getHeapType(), Nullable)); + case BinaryConsts::RefTestRT: + return builder.makeRefTest(getType()); case BinaryConsts::RefCast: return builder.makeRefCast(Type(getHeapType(), NonNullable)); case BinaryConsts::RefCastNull: return builder.makeRefCast(Type(getHeapType(), Nullable)); + case BinaryConsts::RefCastRT: + return builder.makeRefCast(getType()); case BinaryConsts::BrOnCast: case BinaryConsts::BrOnCastFail: { auto flags = getInt8(); auto label = getU32LEB(); - auto in = Type(getHeapType(), (flags & 1) ? Nullable : NonNullable); - auto cast = Type(getHeapType(), (flags & 2) ? Nullable : NonNullable); + auto srcNull = (flags & BinaryConsts::BrOnCastFlag::InputNullable) + ? Nullable + : NonNullable; + auto dstNull = (flags & BinaryConsts::BrOnCastFlag::OutputNullable) + ? Nullable + : NonNullable; + auto srcExact = + (flags & BinaryConsts::BrOnCastFlag::InputExact) ? Exact : Inexact; + auto dstExact = + (flags & BinaryConsts::BrOnCastFlag::OutputExact) ? Exact : Inexact; + auto in = Type(getHeapType(), srcNull, srcExact); + auto cast = Type(getHeapType(), dstNull, dstExact); auto kind = op == BinaryConsts::BrOnCast ? BrOnCast : BrOnCastFail; return builder.makeBrOn(label, kind, in, cast); } diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 060b01b04ee..506b11a085e 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2260,22 +2260,38 @@ void BinaryInstWriter::visitCallRef(CallRef* curr) { void BinaryInstWriter::visitRefTest(RefTest* curr) { o << int8_t(BinaryConsts::GCPrefix); - if (curr->castType.isNullable()) { - o << U32LEB(BinaryConsts::RefTestNull); + if (curr->castType.isExact() && + parent.getModule()->features.hasCustomDescriptors()) { + // Fall back to the general form with a reftype immediate. + o << U32LEB(BinaryConsts::RefTestRT); + parent.writeType(curr->castType); } else { - o << U32LEB(BinaryConsts::RefTest); + // Use the special-case form with heap type immediate. + if (curr->castType.isNullable()) { + o << U32LEB(BinaryConsts::RefTestNull); + } else { + o << U32LEB(BinaryConsts::RefTest); + } + parent.writeHeapType(curr->castType.getHeapType()); } - parent.writeHeapType(curr->castType.getHeapType()); } void BinaryInstWriter::visitRefCast(RefCast* curr) { o << int8_t(BinaryConsts::GCPrefix); - if (curr->type.isNullable()) { - o << U32LEB(BinaryConsts::RefCastNull); + if (curr->type.isExact() && + parent.getModule()->features.hasCustomDescriptors()) { + // Fall back to the general form with a reftype immediate. + o << U32LEB(BinaryConsts::RefCastRT); + parent.writeType(curr->type); } else { - o << U32LEB(BinaryConsts::RefCast); + // Use the special-case form with heap type immediate. + if (curr->type.isNullable()) { + o << U32LEB(BinaryConsts::RefCastNull); + } else { + o << U32LEB(BinaryConsts::RefCast); + } + parent.writeHeapType(curr->type.getHeapType()); } - parent.writeHeapType(curr->type.getHeapType()); } void BinaryInstWriter::visitBrOn(BrOn* curr) { @@ -2298,8 +2314,24 @@ void BinaryInstWriter::visitBrOn(BrOn* curr) { } assert(curr->ref->type.isRef()); assert(Type::isSubType(curr->castType, curr->ref->type)); - uint8_t flags = (curr->ref->type.isNullable() ? 1 : 0) | - (curr->castType.isNullable() ? 2 : 0); + uint8_t flags = 0; + if (curr->ref->type.isNullable()) { + flags |= BinaryConsts::BrOnCastFlag::InputNullable; + } + if (curr->castType.isNullable()) { + flags |= BinaryConsts::BrOnCastFlag::OutputNullable; + } + if (parent.getModule()->features.hasCustomDescriptors()) { + // If custom descriptors (and therefore exact references) are not + // enabled, then these flags wouldn't be recognized, and we will be + // generalizing all exact references to be non-exact anyway. + if (curr->ref->type.isExact()) { + flags |= BinaryConsts::BrOnCastFlag::InputExact; + } + if (curr->castType.isExact()) { + flags |= BinaryConsts::BrOnCastFlag::OutputExact; + } + } o << flags; o << U32LEB(getBreakIndex(curr->name)); parent.writeHeapType(curr->ref->type.getHeapType()); diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 5cdb76c19dd..ada53eb150b 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -790,11 +790,19 @@ Type Type::getLeastUpperBound(Type a, Type b) { return Type(elems); } if (a.isRef() && b.isRef()) { - if (auto heapType = - HeapType::getLeastUpperBound(a.getHeapType(), b.getHeapType())) { + auto heapTypeA = a.getHeapType(); + auto heapTypeB = b.getHeapType(); + if (auto heapType = HeapType::getLeastUpperBound(heapTypeA, heapTypeB)) { auto nullability = (a.isNullable() || b.isNullable()) ? Nullable : NonNullable; - return Type(*heapType, nullability); + auto exactness = (a.isInexact() || b.isInexact()) ? Inexact : Exact; + // The LUB can only be exact if the heap types are the same or one of them + // is bottom. + if (heapTypeA != heapTypeB && !heapTypeA.isBottom() && + !heapTypeB.isBottom()) { + exactness = Inexact; + } + return Type(*heapType, nullability, exactness); } } return Type::none; @@ -828,6 +836,7 @@ Type Type::getGreatestLowerBound(Type a, Type b) { } auto nullability = (a.isNonNullable() || b.isNonNullable()) ? NonNullable : Nullable; + auto exactness = (a.isExact() || b.isExact()) ? Exact : Inexact; HeapType heapType; if (HeapType::isSubType(heapA, heapB)) { heapType = heapA; @@ -836,7 +845,13 @@ Type Type::getGreatestLowerBound(Type a, Type b) { } else { heapType = heapA.getBottom(); } - return Type(heapType, nullability); + // If one of the types is exact, but the GLB heap type is different than its + // heap type, then we must make the GLB heap type bottom. + if ((a.isExact() && heapType != heapA) || + (b.isExact() && heapType != heapB)) { + heapType = heapA.getBottom(); + } + return Type(heapType, nullability, exactness); } const Type& Type::Iterator::operator*() const { @@ -1491,14 +1506,24 @@ bool SubTyper::isSubType(Type a, Type b) { if (a == Type::unreachable) { return true; } - if (a.isRef() && b.isRef()) { - return (a.isNullable() == b.isNullable() || !a.isNullable()) && - isSubType(a.getHeapType(), b.getHeapType()); - } if (a.isTuple() && b.isTuple()) { return isSubType(a.getTuple(), b.getTuple()); } - return false; + if (!a.isRef() || !b.isRef()) { + return false; + } + if (a.isNullable() && !b.isNullable()) { + return false; + } + if (a.isInexact() && !b.isInexact()) { + return false; + } + auto heapTypeA = a.getHeapType(); + auto heapTypeB = b.getHeapType(); + if (b.isExact() && !heapTypeA.isBottom()) { + return heapTypeA == heapTypeB; + } + return isSubType(heapTypeA, heapTypeB); } bool SubTyper::isSubType(HeapType a, HeapType b) { @@ -1646,44 +1671,69 @@ std::ostream& TypePrinter::print(Type type) { } else if (type.isRef()) { auto heapType = type.getHeapType(); if (type.isNullable() && heapType.isBasic() && !heapType.isShared()) { + if (type.isExact()) { + os << "(exact "; + } // Print shorthands for certain basic heap types. switch (heapType.getBasic(Unshared)) { case HeapType::ext: - return os << "externref"; + os << "externref"; + break; case HeapType::func: - return os << "funcref"; + os << "funcref"; + break; case HeapType::cont: - return os << "contref"; + os << "contref"; + break; case HeapType::any: - return os << "anyref"; + os << "anyref"; + break; case HeapType::eq: - return os << "eqref"; + os << "eqref"; + break; case HeapType::i31: - return os << "i31ref"; + os << "i31ref"; + break; case HeapType::struct_: - return os << "structref"; + os << "structref"; + break; case HeapType::array: - return os << "arrayref"; + os << "arrayref"; + break; case HeapType::exn: - return os << "exnref"; + os << "exnref"; + break; case HeapType::string: - return os << "stringref"; + os << "stringref"; + break; case HeapType::none: - return os << "nullref"; + os << "nullref"; + break; case HeapType::noext: - return os << "nullexternref"; + os << "nullexternref"; + break; case HeapType::nofunc: - return os << "nullfuncref"; + os << "nullfuncref"; + break; case HeapType::nocont: - return os << "nullcontref"; + os << "nullcontref"; + break; case HeapType::noexn: - return os << "nullexnref"; + os << "nullexnref"; + break; } + if (type.isExact()) { + os << ')'; + } + return os; } os << "(ref "; if (type.isNullable()) { os << "null "; } + if (type.isExact()) { + os << "exact "; + } printHeapTypeName(heapType); os << ')'; } else { @@ -1927,8 +1977,9 @@ size_t RecGroupHasher::hash(Type type) const { return digest; } assert(type.isRef()); - rehash(digest, type.getNullability()); - rehash(digest, hash(type.getHeapType())); + wasm::rehash(digest, type.getNullability()); + wasm::rehash(digest, type.getExactness()); + hash_combine(digest, hash(type.getHeapType())); return digest; } @@ -2059,6 +2110,7 @@ bool RecGroupEquator::eq(Type a, Type b) const { } if (a.isRef() && b.isRef()) { return a.getNullability() == b.getNullability() && + a.getExactness() == b.getExactness() && eq(a.getHeapType(), b.getHeapType()); } return false; @@ -2269,8 +2321,10 @@ Type TypeBuilder::getTempTupleType(const Tuple& tuple) { return impl->tupleStore.insert(tuple); } -Type TypeBuilder::getTempRefType(HeapType type, Nullability nullable) { - return Type(type, nullable); +Type TypeBuilder::getTempRefType(HeapType type, + Nullability nullable, + Exactness exact) { + return Type(type, nullable, exact); } void TypeBuilder::setSubType(size_t i, std::optional super) { diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index e33d5c3ef0d..3b7e33390f3 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2288,6 +2288,10 @@ void FunctionValidator::visitRefNull(RefNull* curr) { curr->type.isNullable(), curr, "ref.null types must be nullable")) { return; } + if (!shouldBeTrue( + curr->type.isExact(), curr, "ref.null types must be exact")) { + return; + } shouldBeTrue( curr->type.isNull(), curr, "ref.null must have a bottom heap type"); } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 0bc888646cc..6c5694c6848 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -800,7 +800,7 @@ void MemoryGrow::finalize() { void RefNull::finalize(HeapType heapType) { assert(heapType.isBottom()); - type = Type(heapType, Nullable); + type = Type(heapType, Nullable, Exact); } void RefNull::finalize(Type type_) { type = type_; } @@ -925,6 +925,7 @@ static void populateTryTableSentTypes(TryTable* curr, Module* wasm) { // wasm spec defines when GC is enabled (=== non-nullable types are allowed). // If GC is not enabled then we emit a nullable type in the binary format in // WasmBinaryWriter::writeType. + // TODO: Make this exact. Type exnref = Type(HeapType::exn, NonNullable); for (Index i = 0; i < curr->catchTags.size(); i++) { auto tagName = curr->catchTags[i]; @@ -979,6 +980,7 @@ void RefI31::finalize() { if (value->type == Type::unreachable) { type = Type::unreachable; } else { + // TODO: Make this exact. assert(type.isRef() && type.getHeapType().isMaybeShared(HeapType::i31)); } } @@ -1014,10 +1016,12 @@ void CallRef::finalize() { // unreachable instead (and similar in other GC accessors), although this // would currently cause the parser to admit more invalid modules. if (type.isRef()) { + // TODO: Make this exact. type = Type(type.getHeapType().getBottom(), NonNullable); } else if (type.isTuple()) { Tuple elems; for (auto t : type) { + // TODO: Make this exact. elems.push_back( t.isRef() ? Type(t.getHeapType().getBottom(), NonNullable) : t); } @@ -1153,6 +1157,7 @@ void StructGet::finalize() { } else if (ref->type.isNull()) { // See comment on CallRef for explanation. if (type.isRef()) { + // TODO: Make this exact. type = Type(type.getHeapType().getBottom(), NonNullable); } } else { @@ -1228,6 +1233,7 @@ void ArrayGet::finalize() { } else if (ref->type.isNull()) { // See comment on CallRef for explanation. if (type.isRef()) { + // TODO: Make this exact. type = Type(type.getHeapType().getBottom(), NonNullable); } } else { @@ -1309,11 +1315,13 @@ void RefAs::finalize() { break; case AnyConvertExtern: type = Type(HeapTypes::any.getBasic(valHeapType.getShared()), - value->type.getNullability()); + value->type.getNullability(), + Inexact); break; case ExternConvertAny: type = Type(HeapTypes::ext.getBasic(valHeapType.getShared()), - value->type.getNullability()); + value->type.getNullability(), + Inexact); break; default: WASM_UNREACHABLE("invalid ref.as_*"); @@ -1326,11 +1334,15 @@ void StringNew::finalize() { (end && end->type == Type::unreachable)) { type = Type::unreachable; } else { + // TODO: Make this exact. type = Type(HeapType::string, NonNullable); } } -void StringConst::finalize() { type = Type(HeapType::string, NonNullable); } +void StringConst::finalize() { + // TODO: Make this exact. + type = Type(HeapType::string, NonNullable); +} void StringMeasure::finalize() { if (ref->type == Type::unreachable) { @@ -1353,6 +1365,7 @@ void StringConcat::finalize() { if (left->type == Type::unreachable || right->type == Type::unreachable) { type = Type::unreachable; } else { + // TODO: Make this exact. type = Type(HeapType::string, NonNullable); } } @@ -1378,6 +1391,7 @@ void StringSliceWTF::finalize() { end->type == Type::unreachable) { type = Type::unreachable; } else { + // TODO: Make this exact. type = Type(HeapType::string, NonNullable); } } diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index 78f1c73552f..9bfc45ddf72 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -20,17 +20,17 @@ BinaryenTypeAuto: -1 BinaryenPackedTypeNotPacked: 0 BinaryenPackedTypeInt8: 1 BinaryenPackedTypeInt16: 2 -BinaryenHeapTypeExt: 8 -BinaryenHeapTypeFunc: 16 -BinaryenHeapTypeAny: 32 -BinaryenHeapTypeEq: 40 -BinaryenHeapTypeI31: 48 -BinaryenHeapTypeStruct: 56 -BinaryenHeapTypeArray: 64 -BinaryenHeapTypeString: 80 -BinaryenHeapTypeNone: 88 -BinaryenHeapTypeNoext: 96 -BinaryenHeapTypeNofunc: 104 +BinaryenHeapTypeExt: 16 +BinaryenHeapTypeFunc: 32 +BinaryenHeapTypeAny: 64 +BinaryenHeapTypeEq: 80 +BinaryenHeapTypeI31: 96 +BinaryenHeapTypeStruct: 112 +BinaryenHeapTypeArray: 128 +BinaryenHeapTypeString: 160 +BinaryenHeapTypeNone: 176 +BinaryenHeapTypeNoext: 192 +BinaryenHeapTypeNofunc: 208 BinaryenFeatureMVP: 0 BinaryenFeatureAtomics: 1 BinaryenFeatureBulkMemory: 16 diff --git a/test/gtest/type-builder.cpp b/test/gtest/type-builder.cpp index 1e676b7194a..0f8463d55da 100644 --- a/test/gtest/type-builder.cpp +++ b/test/gtest/type-builder.cpp @@ -428,6 +428,32 @@ TEST_F(TypeTest, CanonicalizeUses) { EXPECT_NE(built[4], built[6]); } +TEST_F(TypeTest, CanonicalizeExactRefs) { + TypeBuilder builder(4); + + // Types that vary in exactness or nullability of references are different. + Type a = builder.getTempRefType(builder[0], Nullable, Inexact); + Type b = builder.getTempRefType(builder[1], NonNullable, Inexact); + Type c = builder.getTempRefType(builder[2], Nullable, Exact); + Type d = builder.getTempRefType(builder[3], NonNullable, Exact); + + builder[0] = Struct({Field(a, Mutable)}); + builder[1] = Struct({Field(b, Mutable)}); + builder[2] = Struct({Field(c, Mutable)}); + builder[3] = Struct({Field(d, Mutable)}); + + auto result = builder.build(); + ASSERT_TRUE(result); + auto built = *result; + + EXPECT_NE(built[0], built[1]); + EXPECT_NE(built[0], built[2]); + EXPECT_NE(built[0], built[3]); + EXPECT_NE(built[1], built[2]); + EXPECT_NE(built[1], built[3]); + EXPECT_NE(built[2], built[3]); +} + TEST_F(TypeTest, CanonicalizeSelfReferences) { TypeBuilder builder(5); // Single self-reference @@ -1147,18 +1173,26 @@ FUZZ_TEST(TypeFuzzTest, TestHeapTypeRelationsFuzz) #endif // FUZZTEST TEST_F(TypeTest, TestTypeRelations) { - Type any = Type(HeapType::any, NonNullable); - Type nullAny = Type(HeapType::any, Nullable); + Type any = Type(HeapType::any, NonNullable, Inexact); + Type nullAny = Type(HeapType::any, Nullable, Inexact); + Type exactAny = Type(HeapType::any, NonNullable, Exact); + Type nullExactAny = Type(HeapType::any, Nullable, Exact); HeapType defined = Struct(); - Type def = Type(defined, NonNullable); - Type nullDef = Type(defined, Nullable); + Type def = Type(defined, NonNullable, Inexact); + Type nullDef = Type(defined, Nullable, Inexact); + Type exactDef = Type(defined, NonNullable, Exact); + Type nullExactDef = Type(defined, Nullable, Exact); - Type none = Type(HeapType::none, NonNullable); - Type nullNone = Type(HeapType::none, Nullable); + Type none = Type(HeapType::none, NonNullable, Inexact); + Type nullNone = Type(HeapType::none, Nullable, Inexact); + Type exactNone = Type(HeapType::none, NonNullable, Exact); + Type nullExactNone = Type(HeapType::none, Nullable, Exact); - Type func = Type(HeapType::func, NonNullable); - Type nullFunc = Type(HeapType::func, Nullable); + Type func = Type(HeapType::func, NonNullable, Inexact); + Type nullFunc = Type(HeapType::func, Nullable, Inexact); + Type exactFunc = Type(HeapType::func, NonNullable, Exact); + Type nullExactFunc = Type(HeapType::func, Nullable, Exact); Type i32 = Type::i32; Type unreachable = Type::unreachable; @@ -1217,54 +1251,165 @@ TEST_F(TypeTest, TestTypeRelations) { assertLUB(any, any, any, any); assertLUB(any, nullAny, nullAny, any); + assertLUB(any, exactAny, any, exactAny); + assertLUB(any, nullExactAny, nullAny, exactAny); assertLUB(any, def, any, def); assertLUB(any, nullDef, nullAny, def); + assertLUB(any, exactDef, any, exactDef); + assertLUB(any, nullExactDef, nullAny, exactDef); assertLUB(any, none, any, none); assertLUB(any, nullNone, nullAny, none); + assertLUB(any, exactNone, any, exactNone); + assertLUB(any, nullExactNone, nullAny, exactNone); assertLUB(any, func, Type(Type::none), unreachable); assertLUB(any, nullFunc, Type(Type::none), unreachable); + assertLUB(any, exactFunc, Type(Type::none), unreachable); + assertLUB(any, nullExactFunc, Type(Type::none), unreachable); assertLUB(any, i32, Type(Type::none), unreachable); assertLUB(any, unreachable, any, unreachable); assertLUB(nullAny, nullAny, nullAny, nullAny); + assertLUB(nullAny, exactAny, nullAny, exactAny); + assertLUB(nullAny, nullExactAny, nullAny, nullExactAny); assertLUB(nullAny, def, nullAny, def); assertLUB(nullAny, nullDef, nullAny, nullDef); + assertLUB(nullAny, exactDef, nullAny, exactDef); + assertLUB(nullAny, nullExactDef, nullAny, nullExactDef); assertLUB(nullAny, none, nullAny, none); assertLUB(nullAny, nullNone, nullAny, nullNone); + assertLUB(nullAny, exactNone, nullAny, exactNone); + assertLUB(nullAny, nullExactNone, nullAny, nullExactNone); assertLUB(nullAny, func, Type(Type::none), unreachable); assertLUB(nullAny, nullFunc, Type(Type::none), unreachable); + assertLUB(nullAny, exactFunc, Type(Type::none), unreachable); + assertLUB(nullAny, nullExactFunc, Type(Type::none), unreachable); assertLUB(nullAny, i32, Type(Type::none), unreachable); assertLUB(nullAny, unreachable, nullAny, unreachable); + assertLUB(exactAny, exactAny, exactAny, exactAny); + assertLUB(exactAny, nullExactAny, nullExactAny, exactAny); + assertLUB(exactAny, def, any, exactNone); + assertLUB(exactAny, nullDef, nullAny, exactNone); + assertLUB(exactAny, exactDef, any, exactNone); + assertLUB(exactAny, nullExactDef, nullAny, exactNone); + assertLUB(exactAny, none, any, exactNone); + assertLUB(exactAny, nullNone, nullAny, exactNone); + assertLUB(exactAny, exactNone, exactAny, exactNone); + assertLUB(exactAny, nullExactNone, nullExactAny, exactNone); + assertLUB(exactAny, func, Type(Type::none), unreachable); + assertLUB(exactAny, nullFunc, Type(Type::none), unreachable); + assertLUB(exactAny, exactFunc, Type(Type::none), unreachable); + assertLUB(exactAny, nullExactFunc, Type(Type::none), unreachable); + assertLUB(exactAny, i32, Type(Type::none), unreachable); + assertLUB(exactAny, unreachable, exactAny, unreachable); + + assertLUB(nullExactAny, nullExactAny, nullExactAny, nullExactAny); + assertLUB(nullExactAny, def, nullAny, exactNone); + assertLUB(nullExactAny, nullDef, nullAny, nullExactNone); + assertLUB(nullExactAny, exactDef, nullAny, exactNone); + assertLUB(nullExactAny, nullExactDef, nullAny, nullExactNone); + assertLUB(nullExactAny, none, nullAny, exactNone); + assertLUB(nullExactAny, nullNone, nullAny, nullExactNone); + assertLUB(nullExactAny, exactNone, nullExactAny, exactNone); + assertLUB(nullExactAny, nullExactNone, nullExactAny, nullExactNone); + assertLUB(nullExactAny, func, Type(Type::none), unreachable); + assertLUB(nullExactAny, nullFunc, Type(Type::none), unreachable); + assertLUB(nullExactAny, exactFunc, Type(Type::none), unreachable); + assertLUB(nullExactAny, nullExactFunc, Type(Type::none), unreachable); + assertLUB(nullExactAny, i32, Type(Type::none), unreachable); + assertLUB(nullExactAny, unreachable, nullExactAny, unreachable); + assertLUB(def, def, def, def); assertLUB(def, nullDef, nullDef, def); + assertLUB(def, exactDef, def, exactDef); + assertLUB(def, nullExactDef, nullDef, exactDef); assertLUB(def, none, def, none); assertLUB(def, nullNone, nullDef, none); + assertLUB(def, exactNone, def, exactNone); + assertLUB(def, nullExactNone, nullDef, exactNone); assertLUB(def, func, Type(Type::none), unreachable); assertLUB(def, nullFunc, Type(Type::none), unreachable); + assertLUB(def, exactFunc, Type(Type::none), unreachable); + assertLUB(def, nullExactFunc, Type(Type::none), unreachable); assertLUB(def, i32, Type(Type::none), unreachable); assertLUB(def, unreachable, def, unreachable); assertLUB(nullDef, nullDef, nullDef, nullDef); + assertLUB(nullDef, exactDef, nullDef, exactDef); + assertLUB(nullDef, nullExactDef, nullDef, nullExactDef); assertLUB(nullDef, none, nullDef, none); assertLUB(nullDef, nullNone, nullDef, nullNone); + assertLUB(nullDef, exactNone, nullDef, exactNone); + assertLUB(nullDef, nullExactNone, nullDef, nullExactNone); assertLUB(nullDef, func, Type(Type::none), unreachable); assertLUB(nullDef, nullFunc, Type(Type::none), unreachable); + assertLUB(nullDef, exactFunc, Type(Type::none), unreachable); + assertLUB(nullDef, nullExactFunc, Type(Type::none), unreachable); assertLUB(nullDef, i32, Type(Type::none), unreachable); assertLUB(nullDef, unreachable, nullDef, unreachable); + assertLUB(exactDef, exactDef, exactDef, exactDef); + assertLUB(exactDef, nullExactDef, nullExactDef, exactDef); + assertLUB(exactDef, none, def, exactNone); + assertLUB(exactDef, nullNone, nullDef, exactNone); + assertLUB(exactDef, exactNone, exactDef, exactNone); + assertLUB(exactDef, nullExactNone, nullExactDef, exactNone); + assertLUB(exactDef, func, Type(Type::none), unreachable); + assertLUB(exactDef, nullFunc, Type(Type::none), unreachable); + assertLUB(exactDef, exactFunc, Type(Type::none), unreachable); + assertLUB(exactDef, nullExactFunc, Type(Type::none), unreachable); + assertLUB(exactDef, i32, Type(Type::none), unreachable); + assertLUB(exactDef, unreachable, exactDef, unreachable); + + assertLUB(nullExactDef, nullExactDef, nullExactDef, nullExactDef); + assertLUB(nullExactDef, none, nullDef, exactNone); + assertLUB(nullExactDef, nullNone, nullDef, nullExactNone); + assertLUB(nullExactDef, exactNone, nullExactDef, exactNone); + assertLUB(nullExactDef, nullExactNone, nullExactDef, nullExactNone); + assertLUB(nullExactDef, func, Type(Type::none), unreachable); + assertLUB(nullExactDef, nullFunc, Type(Type::none), unreachable); + assertLUB(nullExactDef, exactFunc, Type(Type::none), unreachable); + assertLUB(nullExactDef, nullExactFunc, Type(Type::none), unreachable); + assertLUB(nullExactDef, i32, Type(Type::none), unreachable); + assertLUB(nullExactDef, unreachable, nullExactDef, unreachable); + assertLUB(none, none, none, none); assertLUB(none, nullNone, nullNone, none); + assertLUB(none, exactNone, none, exactNone); + assertLUB(none, nullExactNone, nullNone, exactNone); assertLUB(none, func, Type(Type::none), unreachable); assertLUB(none, nullFunc, Type(Type::none), unreachable); + assertLUB(none, exactFunc, Type(Type::none), unreachable); + assertLUB(none, nullExactFunc, Type(Type::none), unreachable); assertLUB(none, i32, Type(Type::none), unreachable); assertLUB(none, unreachable, none, unreachable); assertLUB(nullNone, nullNone, nullNone, nullNone); + assertLUB(nullNone, exactNone, nullNone, exactNone); + assertLUB(nullNone, nullExactNone, nullNone, nullExactNone); assertLUB(nullNone, func, Type(Type::none), unreachable); assertLUB(nullNone, nullFunc, Type(Type::none), unreachable); + assertLUB(nullNone, exactFunc, Type(Type::none), unreachable); + assertLUB(nullNone, nullExactFunc, Type(Type::none), unreachable); assertLUB(nullNone, i32, Type(Type::none), unreachable); assertLUB(nullNone, unreachable, nullNone, unreachable); + + assertLUB(exactNone, exactNone, exactNone, exactNone); + assertLUB(exactNone, nullExactNone, nullExactNone, exactNone); + assertLUB(exactNone, func, Type(Type::none), unreachable); + assertLUB(exactNone, nullFunc, Type(Type::none), unreachable); + assertLUB(exactNone, exactFunc, Type(Type::none), unreachable); + assertLUB(exactNone, nullExactFunc, Type(Type::none), unreachable); + assertLUB(exactNone, i32, Type(Type::none), unreachable); + assertLUB(exactNone, unreachable, exactNone, unreachable); + + assertLUB(nullExactNone, nullExactNone, nullExactNone, nullExactNone); + assertLUB(nullExactNone, func, Type(Type::none), unreachable); + assertLUB(nullExactNone, nullFunc, Type(Type::none), unreachable); + assertLUB(nullExactNone, exactFunc, Type(Type::none), unreachable); + assertLUB(nullExactNone, nullExactFunc, Type(Type::none), unreachable); + assertLUB(nullExactNone, i32, Type(Type::none), unreachable); + assertLUB(nullExactNone, unreachable, nullExactNone, unreachable); } TEST_F(TypeTest, TestSubtypeErrors) { diff --git a/test/lit/basic/exact-references.wast b/test/lit/basic/exact-references.wast new file mode 100644 index 00000000000..e6c1599ea48 --- /dev/null +++ b/test/lit/basic/exact-references.wast @@ -0,0 +1,672 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: wasm-opt %s -all -o %t.text.wast -g -S +;; RUN: wasm-as %s -all -g -o %t.wasm +;; RUN: wasm-dis %t.wasm -all -o %t.bin.wast +;; RUN: wasm-as %s -all -o %t.nodebug.wasm +;; RUN: wasm-dis %t.nodebug.wasm -all -o %t.bin.nodebug.wast +;; RUN: cat %t.text.wast | filecheck %s --check-prefix=CHECK-TEXT +;; RUN: cat %t.bin.wast | filecheck %s --check-prefix=CHECK-BIN +;; RUN: cat %t.bin.nodebug.wast | filecheck %s --check-prefix=CHECK-BIN-NODEBUG + +;; Also check that if we emit a binary without custom descriptors enabled, the +;; types are generalized to be inexact. + +;; RUN: wasm-opt %s -all --disable-custom-descriptors -g -o %t.noexact.wasm +;; RUN: wasm-opt %t.noexact.wasm -all -S -o - | filecheck %s --check-prefix=NO-EXACT + +(module + ;; CHECK-TEXT: (type $foo (struct (field (exact anyref)) (field (ref exact any)) (field (ref null exact $foo)) (field (ref exact $foo)))) + ;; CHECK-BIN: (type $foo (struct (field (exact anyref)) (field (ref exact any)) (field (ref null exact $foo)) (field (ref exact $foo)))) + ;; NO-EXACT: (type $foo (struct (field anyref) (field (ref any)) (field (ref null $foo)) (field (ref $foo)))) + (type $foo (struct (field (exact anyref) (ref exact any) (ref null exact $foo) (ref exact $foo)))) + + + ;; CHECK-TEXT: (type $1 (func (param (exact anyref)) (result (ref exact any)))) + + ;; CHECK-TEXT: (type $2 (func (param anyref) (result anyref))) + + ;; CHECK-TEXT: (type $3 (func (param (exact i31ref)))) + + ;; CHECK-TEXT: (type $4 (func (param (exact eqref)))) + + ;; CHECK-TEXT: (type $5 (func (param (exact anyref)))) + + ;; CHECK-TEXT: (type $6 (func (param (exact anyref)) (result (exact anyref)))) + + ;; CHECK-TEXT: (import "" "g1" (global $g1 (exact anyref))) + ;; CHECK-BIN: (type $1 (func (param (exact anyref)) (result (ref exact any)))) + + ;; CHECK-BIN: (type $2 (func (param anyref) (result anyref))) + + ;; CHECK-BIN: (type $3 (func (param (exact i31ref)))) + + ;; CHECK-BIN: (type $4 (func (param (exact eqref)))) + + ;; CHECK-BIN: (type $5 (func (param (exact anyref)))) + + ;; CHECK-BIN: (type $6 (func (param (exact anyref)) (result (exact anyref)))) + + ;; CHECK-BIN: (import "" "g1" (global $g1 (exact anyref))) + ;; NO-EXACT: (type $1 (func (param anyref) (result anyref))) + + ;; NO-EXACT: (type $2 (func (param anyref) (result (ref any)))) + + ;; NO-EXACT: (type $3 (func (param i31ref))) + + ;; NO-EXACT: (type $4 (func (param eqref))) + + ;; NO-EXACT: (type $5 (func (param anyref))) + + ;; NO-EXACT: (import "" "g1" (global $g1 anyref)) + (import "" "g1" (global $g1 (exact anyref))) + + ;; CHECK-TEXT: (import "" "g2" (global $g2 (ref exact any))) + ;; CHECK-BIN: (import "" "g2" (global $g2 (ref exact any))) + ;; NO-EXACT: (import "" "g2" (global $g2 (ref any))) + (import "" "g2" (global $g2 (ref exact any))) + + ;; CHECK-TEXT: (import "" "g3" (global $g3 (ref null exact $foo))) + ;; CHECK-BIN: (import "" "g3" (global $g3 (ref null exact $foo))) + ;; NO-EXACT: (import "" "g3" (global $g3 (ref null $foo))) + (import "" "g3" (global $g3 (ref null exact $foo))) + + ;; CHECK-TEXT: (import "" "g4" (global $g4 (ref exact $foo))) + ;; CHECK-BIN: (import "" "g4" (global $g4 (ref exact $foo))) + ;; NO-EXACT: (import "" "g4" (global $g4 (ref $foo))) + (import "" "g4" (global $g4 (ref exact $foo))) + + ;; CHECK-TEXT: (func $ref-test (type $3) (param $0 (exact i31ref)) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (ref.test (ref exact i31) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (ref.test (exact i31ref) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $ref-test (type $3) (param $0 (exact i31ref)) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (ref.test (ref exact i31) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (ref.test (exact i31ref) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; NO-EXACT: (func $ref-test (type $3) (param $0 i31ref) + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (ref.test (ref i31) + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (ref.test i31ref + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + (func $ref-test (param (ref null exact i31)) + (drop + (ref.test (ref exact i31) + (local.get 0) + ) + ) + (drop + (ref.test (ref null exact i31) + (local.get 0) + ) + ) + ) + + ;; CHECK-TEXT: (func $ref-cast (type $4) (param $0 (exact eqref)) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (ref.cast (ref exact eq) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (ref.cast (exact eqref) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (ref.cast (ref exact none) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $ref-cast (type $4) (param $0 (exact eqref)) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (ref.cast (ref exact eq) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (ref.cast (exact eqref) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (ref.cast (ref exact none) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; NO-EXACT: (func $ref-cast (type $4) (param $0 eqref) + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (ref.cast (ref eq) + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (ref.cast eqref + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (ref.cast (ref none) + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + (func $ref-cast (param (ref null exact eq)) + (drop + (ref.cast (ref exact eq) + (local.get 0) + ) + ) + (drop + (ref.cast (ref null exact eq) + (local.get 0) + ) + ) + (drop + (ref.cast (ref exact i31) + (local.get 0) + ) + ) + ) + + ;; CHECK-TEXT: (func $br-on-cast (type $2) (param $0 anyref) (result anyref) + ;; CHECK-TEXT-NEXT: (block $label (result anyref) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (br_on_cast $label anyref (exact eqref) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (br_on_cast $label anyref (ref exact eq) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (br_on_cast $label anyref (exact i31ref) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $br-on-cast (type $2) (param $0 anyref) (result anyref) + ;; CHECK-BIN-NEXT: (block $block (result anyref) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (br_on_cast $block anyref (exact eqref) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (br_on_cast $block anyref (ref exact eq) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (br_on_cast $block anyref (exact i31ref) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; NO-EXACT: (func $br-on-cast (type $1) (param $0 anyref) (result anyref) + ;; NO-EXACT-NEXT: (block $block (result anyref) + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (br_on_cast $block anyref eqref + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (br_on_cast $block anyref (ref eq) + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (br_on_cast $block anyref i31ref + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + (func $br-on-cast (param anyref) (result anyref) + (drop + (br_on_cast 0 anyref (ref null exact eq) + (local.get 0) + ) + ) + (drop + (br_on_cast 0 anyref (ref exact eq) + (local.get 0) + ) + ) + (drop + (br_on_cast 0 anyref (ref null exact i31) + (local.get 0) + ) + ) + (local.get 0) + ) + + ;; CHECK-TEXT: (func $br-on-cast-fail (type $2) (param $0 anyref) (result anyref) + ;; CHECK-TEXT-NEXT: (block $label (result anyref) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (br_on_cast_fail $label anyref (exact eqref) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (br_on_cast_fail $label anyref (ref exact eq) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (br_on_cast_fail $label anyref (exact i31ref) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $br-on-cast-fail (type $2) (param $0 anyref) (result anyref) + ;; CHECK-BIN-NEXT: (block $block (result anyref) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (br_on_cast_fail $block anyref (exact eqref) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (br_on_cast_fail $block anyref (ref exact eq) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (br_on_cast_fail $block anyref (exact i31ref) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; NO-EXACT: (func $br-on-cast-fail (type $1) (param $0 anyref) (result anyref) + ;; NO-EXACT-NEXT: (block $block (result anyref) + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (br_on_cast_fail $block anyref eqref + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (br_on_cast_fail $block anyref (ref eq) + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (br_on_cast_fail $block anyref i31ref + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + (func $br-on-cast-fail (param anyref) (result anyref) + (drop + (br_on_cast_fail 0 anyref (ref null exact eq) + (local.get 0) + ) + ) + (drop + (br_on_cast_fail 0 anyref (ref exact eq) + (local.get 0) + ) + ) + (drop + (br_on_cast_fail 0 anyref (ref null exact i31) + (local.get 0) + ) + ) + (local.get 0) + ) + + ;; CHECK-TEXT: (func $valid-ref-as-non-null (type $1) (param $0 (exact anyref)) (result (ref exact any)) + ;; CHECK-TEXT-NEXT: (ref.as_non_null + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $valid-ref-as-non-null (type $1) (param $0 (exact anyref)) (result (ref exact any)) + ;; CHECK-BIN-NEXT: (ref.as_non_null + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; NO-EXACT: (func $valid-ref-as-non-null (type $2) (param $0 anyref) (result (ref any)) + ;; NO-EXACT-NEXT: (ref.as_non_null + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + (func $valid-ref-as-non-null (param (ref null exact any)) (result (ref exact any)) + (ref.as_non_null + (local.get 0) + ) + ) + + ;; CHECK-TEXT: (func $valid-br-on-null (type $5) (param $0 (exact anyref)) + ;; CHECK-TEXT-NEXT: (block $label + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (block (result (ref exact any)) + ;; CHECK-TEXT-NEXT: (br_on_null $label + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $valid-br-on-null (type $5) (param $0 (exact anyref)) + ;; CHECK-BIN-NEXT: (block $block + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (br_on_null $block + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; NO-EXACT: (func $valid-br-on-null (type $5) (param $0 anyref) + ;; NO-EXACT-NEXT: (block $block + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (br_on_null $block + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + (func $valid-br-on-null (param (ref null exact any)) + (drop + (block (result (ref exact any)) + (br_on_null 1 + (local.get 0) + ) + ) + ) + ) + + ;; CHECK-TEXT: (func $valid-br-on-non-null (type $1) (param $0 (exact anyref)) (result (ref exact any)) + ;; CHECK-TEXT-NEXT: (block $label (result (ref exact any)) + ;; CHECK-TEXT-NEXT: (br_on_non_null $label + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $valid-br-on-non-null (type $1) (param $0 (exact anyref)) (result (ref exact any)) + ;; CHECK-BIN-NEXT: (block $block (result (ref exact any)) + ;; CHECK-BIN-NEXT: (br_on_non_null $block + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; NO-EXACT: (func $valid-br-on-non-null (type $2) (param $0 anyref) (result (ref any)) + ;; NO-EXACT-NEXT: (block $block (result (ref any)) + ;; NO-EXACT-NEXT: (br_on_non_null $block + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: (unreachable) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + (func $valid-br-on-non-null (param (ref null exact any)) (result (ref exact any)) + (br_on_non_null 0 + (local.get 0) + ) + (unreachable) + ) + + ;; CHECK-TEXT: (func $valid-br-on-cast (type $6) (param $0 (exact anyref)) (result (exact anyref)) + ;; CHECK-TEXT-NEXT: (block $label (result (exact anyref)) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (block (result (ref exact any)) + ;; CHECK-TEXT-NEXT: (br_on_cast $label (exact anyref) (exact anyref) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $valid-br-on-cast (type $6) (param $0 (exact anyref)) (result (exact anyref)) + ;; CHECK-BIN-NEXT: (block $block (result (exact anyref)) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (br_on_cast $block (exact anyref) (exact anyref) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; NO-EXACT: (func $valid-br-on-cast (type $1) (param $0 anyref) (result anyref) + ;; NO-EXACT-NEXT: (block $block (result anyref) + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (br_on_cast $block anyref anyref + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: (unreachable) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + (func $valid-br-on-cast (param (ref null exact any)) (result (ref null exact any)) + (drop + (block (result (ref exact any)) + (br_on_cast 1 (ref null exact any) (ref null exact any) + (local.get 0) + ) + ) + ) + (unreachable) + ) + + ;; CHECK-TEXT: (func $valid-br-on-cast-fail (type $1) (param $0 (exact anyref)) (result (ref exact any)) + ;; CHECK-TEXT-NEXT: (block $label (result (ref exact any)) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (block (result (exact anyref)) + ;; CHECK-TEXT-NEXT: (br_on_cast_fail $label (exact anyref) (exact anyref) + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $valid-br-on-cast-fail (type $1) (param $0 (exact anyref)) (result (ref exact any)) + ;; CHECK-BIN-NEXT: (block $block (result (ref exact any)) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (br_on_cast_fail $block (exact anyref) (exact anyref) + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; NO-EXACT: (func $valid-br-on-cast-fail (type $2) (param $0 anyref) (result (ref any)) + ;; NO-EXACT-NEXT: (block $block (result (ref any)) + ;; NO-EXACT-NEXT: (drop + ;; NO-EXACT-NEXT: (br_on_cast_fail $block anyref anyref + ;; NO-EXACT-NEXT: (local.get $0) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: (unreachable) + ;; NO-EXACT-NEXT: ) + ;; NO-EXACT-NEXT: ) + (func $valid-br-on-cast-fail (param (ref null exact any)) (result (ref exact any)) + (drop + (block (result (ref null exact any)) + (br_on_cast_fail 1 (ref null exact any) (ref null exact any) + (local.get 0) + ) + ) + ) + (unreachable) + ) +) +;; CHECK-BIN-NODEBUG: (type $0 (struct (field (exact anyref)) (field (ref exact any)) (field (ref null exact $0)) (field (ref exact $0)))) + +;; CHECK-BIN-NODEBUG: (type $1 (func (param (exact anyref)) (result (ref exact any)))) + +;; CHECK-BIN-NODEBUG: (type $2 (func (param anyref) (result anyref))) + +;; CHECK-BIN-NODEBUG: (type $3 (func (param (exact i31ref)))) + +;; CHECK-BIN-NODEBUG: (type $4 (func (param (exact eqref)))) + +;; CHECK-BIN-NODEBUG: (type $5 (func (param (exact anyref)))) + +;; CHECK-BIN-NODEBUG: (type $6 (func (param (exact anyref)) (result (exact anyref)))) + +;; CHECK-BIN-NODEBUG: (import "" "g1" (global $gimport$0 (exact anyref))) + +;; CHECK-BIN-NODEBUG: (import "" "g2" (global $gimport$1 (ref exact any))) + +;; CHECK-BIN-NODEBUG: (import "" "g3" (global $gimport$2 (ref null exact $0))) + +;; CHECK-BIN-NODEBUG: (import "" "g4" (global $gimport$3 (ref exact $0))) + +;; CHECK-BIN-NODEBUG: (func $0 (type $3) (param $0 (exact i31ref)) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (ref.test (ref exact i31) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (ref.test (exact i31ref) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $1 (type $4) (param $0 (exact eqref)) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (ref.cast (ref exact eq) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (ref.cast (exact eqref) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (ref.cast (ref exact none) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $2 (type $2) (param $0 anyref) (result anyref) +;; CHECK-BIN-NODEBUG-NEXT: (block $block (result anyref) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (br_on_cast $block anyref (exact eqref) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (br_on_cast $block anyref (ref exact eq) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (br_on_cast $block anyref (exact i31ref) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $3 (type $2) (param $0 anyref) (result anyref) +;; CHECK-BIN-NODEBUG-NEXT: (block $block (result anyref) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (br_on_cast_fail $block anyref (exact eqref) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (br_on_cast_fail $block anyref (ref exact eq) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (br_on_cast_fail $block anyref (exact i31ref) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $4 (type $1) (param $0 (exact anyref)) (result (ref exact any)) +;; CHECK-BIN-NODEBUG-NEXT: (ref.as_non_null +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $5 (type $5) (param $0 (exact anyref)) +;; CHECK-BIN-NODEBUG-NEXT: (block $block +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (br_on_null $block +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $6 (type $1) (param $0 (exact anyref)) (result (ref exact any)) +;; CHECK-BIN-NODEBUG-NEXT: (block $block (result (ref exact any)) +;; CHECK-BIN-NODEBUG-NEXT: (br_on_non_null $block +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $7 (type $6) (param $0 (exact anyref)) (result (exact anyref)) +;; CHECK-BIN-NODEBUG-NEXT: (block $block (result (exact anyref)) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (br_on_cast $block (exact anyref) (exact anyref) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $8 (type $1) (param $0 (exact anyref)) (result (ref exact any)) +;; CHECK-BIN-NODEBUG-NEXT: (block $block (result (ref exact any)) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (br_on_cast_fail $block (exact anyref) (exact anyref) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) diff --git a/test/lit/basic/reference-types.wast b/test/lit/basic/reference-types.wast index 82b1f8da4e3..7c4f2a23e76 100644 --- a/test/lit/basic/reference-types.wast +++ b/test/lit/basic/reference-types.wast @@ -961,7 +961,7 @@ ;; CHECK-BIN-NEXT: ) ;; CHECK-BIN-NEXT: (drop ;; CHECK-BIN-NEXT: (block $block2 (result eqref) - ;; CHECK-BIN-NEXT: (ref.cast nullref + ;; CHECK-BIN-NEXT: (ref.cast (exact nullref) ;; CHECK-BIN-NEXT: (br_if $block2 ;; CHECK-BIN-NEXT: (ref.null none) ;; CHECK-BIN-NEXT: (i32.const 1) @@ -987,7 +987,7 @@ ;; CHECK-BIN-NEXT: ) ;; CHECK-BIN-NEXT: (drop ;; CHECK-BIN-NEXT: (block $block5 (result funcref) - ;; CHECK-BIN-NEXT: (ref.cast nullfuncref + ;; CHECK-BIN-NEXT: (ref.cast (exact nullfuncref) ;; CHECK-BIN-NEXT: (br_if $block5 ;; CHECK-BIN-NEXT: (ref.null nofunc) ;; CHECK-BIN-NEXT: (i32.const 1) @@ -1023,7 +1023,7 @@ ;; CHECK-BIN-NEXT: ) ;; CHECK-BIN-NEXT: (drop ;; CHECK-BIN-NEXT: (block $block9 (result anyref) - ;; CHECK-BIN-NEXT: (ref.cast nullref + ;; CHECK-BIN-NEXT: (ref.cast (exact nullref) ;; CHECK-BIN-NEXT: (br_if $block9 ;; CHECK-BIN-NEXT: (ref.null none) ;; CHECK-BIN-NEXT: (i32.const 1) @@ -1043,7 +1043,7 @@ ;; CHECK-BIN-NEXT: ) ;; CHECK-BIN-NEXT: (drop ;; CHECK-BIN-NEXT: (block $block11 (result anyref) - ;; CHECK-BIN-NEXT: (ref.cast nullref + ;; CHECK-BIN-NEXT: (ref.cast (exact nullref) ;; CHECK-BIN-NEXT: (br_if $block11 ;; CHECK-BIN-NEXT: (ref.null none) ;; CHECK-BIN-NEXT: (i32.const 1) @@ -2298,7 +2298,7 @@ ;; CHECK-BIN-NODEBUG-NEXT: ) ;; CHECK-BIN-NODEBUG-NEXT: (drop ;; CHECK-BIN-NODEBUG-NEXT: (block $block2 (result eqref) -;; CHECK-BIN-NODEBUG-NEXT: (ref.cast nullref +;; CHECK-BIN-NODEBUG-NEXT: (ref.cast (exact nullref) ;; CHECK-BIN-NODEBUG-NEXT: (br_if $block2 ;; CHECK-BIN-NODEBUG-NEXT: (ref.null none) ;; CHECK-BIN-NODEBUG-NEXT: (i32.const 1) @@ -2324,7 +2324,7 @@ ;; CHECK-BIN-NODEBUG-NEXT: ) ;; CHECK-BIN-NODEBUG-NEXT: (drop ;; CHECK-BIN-NODEBUG-NEXT: (block $block5 (result funcref) -;; CHECK-BIN-NODEBUG-NEXT: (ref.cast nullfuncref +;; CHECK-BIN-NODEBUG-NEXT: (ref.cast (exact nullfuncref) ;; CHECK-BIN-NODEBUG-NEXT: (br_if $block5 ;; CHECK-BIN-NODEBUG-NEXT: (ref.null nofunc) ;; CHECK-BIN-NODEBUG-NEXT: (i32.const 1) @@ -2360,7 +2360,7 @@ ;; CHECK-BIN-NODEBUG-NEXT: ) ;; CHECK-BIN-NODEBUG-NEXT: (drop ;; CHECK-BIN-NODEBUG-NEXT: (block $block9 (result anyref) -;; CHECK-BIN-NODEBUG-NEXT: (ref.cast nullref +;; CHECK-BIN-NODEBUG-NEXT: (ref.cast (exact nullref) ;; CHECK-BIN-NODEBUG-NEXT: (br_if $block9 ;; CHECK-BIN-NODEBUG-NEXT: (ref.null none) ;; CHECK-BIN-NODEBUG-NEXT: (i32.const 1) @@ -2380,7 +2380,7 @@ ;; CHECK-BIN-NODEBUG-NEXT: ) ;; CHECK-BIN-NODEBUG-NEXT: (drop ;; CHECK-BIN-NODEBUG-NEXT: (block $block11 (result anyref) -;; CHECK-BIN-NODEBUG-NEXT: (ref.cast nullref +;; CHECK-BIN-NODEBUG-NEXT: (ref.cast (exact nullref) ;; CHECK-BIN-NODEBUG-NEXT: (br_if $block11 ;; CHECK-BIN-NODEBUG-NEXT: (ref.null none) ;; CHECK-BIN-NODEBUG-NEXT: (i32.const 1) diff --git a/test/lit/ctor-eval/materialize-null-local.wast b/test/lit/ctor-eval/materialize-null-local.wast new file mode 100644 index 00000000000..1f880f6816b --- /dev/null +++ b/test/lit/ctor-eval/materialize-null-local.wast @@ -0,0 +1,26 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: wasm-ctor-eval %s -all --ctors=test --kept-exports=test --ignore-external-input -S -o - \ +;; RUN: | filecheck %s + +;; Check that materializing a non-data null local, which at time of writing uses +;; Builder::makeConstantExpression, does not trigger an assertion failure. + +(module + (func $test (export "test") (param $0 externref) + (local $3 anyref) + (local.set $3 + (any.convert_extern + (local.get $0) + ) + ) + ) +) +;; CHECK: (type $0 (func (param externref))) + +;; CHECK: (export "test" (func $test_1)) + +;; CHECK: (func $test_1 (type $0) (param $0 externref) +;; CHECK-NEXT: (local $3 anyref) +;; CHECK-NEXT: (nop) +;; CHECK-NEXT: ) diff --git a/test/lit/exec/exact.wast b/test/lit/exec/exact.wast new file mode 100644 index 00000000000..2667e0e4dce --- /dev/null +++ b/test/lit/exec/exact.wast @@ -0,0 +1,21 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --output=fuzz-exec and should not be edited. + +;; RUN: wasm-opt %s -all --fuzz-exec -q -o /dev/null 2>&1 | filecheck %s + +(module + ;; CHECK: [fuzz-exec] calling convert-null-extern + ;; CHECK-NEXT: [fuzz-exec] note result: convert-null-extern => null + (func $convert-null-extern (export "convert-null-extern") (result (exact nullref)) + (local externref) + ;; The value produced by this cast must be exact to avoid triggering an + ;; assertion. + (ref.cast (exact nullref) + (any.convert_extern + (local.get 0) + ) + ) + ) +) +;; CHECK: [fuzz-exec] calling convert-null-extern +;; CHECK-NEXT: [fuzz-exec] note result: convert-null-extern => null +;; CHECK-NEXT: [fuzz-exec] comparing convert-null-extern diff --git a/test/lit/heap-types.wast b/test/lit/heap-types.wast index 4613f4894fd..baf77196611 100644 --- a/test/lit/heap-types.wast +++ b/test/lit/heap-types.wast @@ -12,7 +12,7 @@ (type $struct.B (struct i32)) ;; CHECK: (func $test (type $0) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.test (ref none) + ;; CHECK-NEXT: (ref.test (ref exact none) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -29,7 +29,7 @@ (type $struct.B (struct i32)) ;; CHECK: (func $test (type $0) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.cast nullref + ;; CHECK-NEXT: (ref.cast (exact nullref) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/cfp.wast b/test/lit/passes/cfp.wast index 0bc4372ec35..ab9b7f8a745 100644 --- a/test/lit/passes/cfp.wast +++ b/test/lit/passes/cfp.wast @@ -1034,7 +1034,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.as_non_null ;; CHECK-NEXT: (local.get $struct3) @@ -1233,7 +1233,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.as_non_null ;; CHECK-NEXT: (local.get $struct3) diff --git a/test/lit/passes/coalesce-locals-exact.wast b/test/lit/passes/coalesce-locals-exact.wast new file mode 100644 index 00000000000..1568d3634cf --- /dev/null +++ b/test/lit/passes/coalesce-locals-exact.wast @@ -0,0 +1,47 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. + +;; Check that TypeUpdating::handleNonDefaultableLocals handles locals with exact +;; reference types correctly, and in particular that it preserves the exactness +;; of the types. + +;; RUN: wasm-opt %s -all --coalesce-locals -S -o - | filecheck %s + +(module + ;; CHECK: (func $test (type $0) (param $0 (exact i31ref)) (result (ref exact i31)) + ;; CHECK-NEXT: (local $1 (exact i31ref)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $l + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (param (exact i31ref)) (result (ref exact i31)) + (local $l (ref exact i31)) + ;; This dead set will be optimized out. + (local.set $l + (ref.as_non_null + (local.get 0) + ) + ) + (block $l + ;; This remaining set does not structurally dominate the get. + (local.set $l + (ref.as_non_null + (local.get 0) + ) + ) + ) + ;; This will have to be fixed up and the local made nullable. + (local.get $l) + ) +) diff --git a/test/lit/passes/code-pushing-gc.wast b/test/lit/passes/code-pushing-gc.wast index 1aac5c55cf7..fb9ff12f7da 100644 --- a/test/lit/passes/code-pushing-gc.wast +++ b/test/lit/passes/code-pushing-gc.wast @@ -7,7 +7,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block $out (result (ref func)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (br_on_cast $out nullfuncref (ref nofunc) + ;; CHECK-NEXT: (br_on_cast $out (exact nullfuncref) (ref exact nofunc) ;; CHECK-NEXT: (ref.null nofunc) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -48,7 +48,7 @@ ;; CHECK-NEXT: (ref.func $br_on_no) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (br_on_cast $out nullfuncref (ref nofunc) + ;; CHECK-NEXT: (br_on_cast $out (exact nullfuncref) (ref exact nofunc) ;; CHECK-NEXT: (ref.null nofunc) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/dae-gc-refine-params.wast b/test/lit/passes/dae-gc-refine-params.wast index 8cefbe88184..a93f3e7347f 100644 --- a/test/lit/passes/dae-gc-refine-params.wast +++ b/test/lit/passes/dae-gc-refine-params.wast @@ -262,7 +262,7 @@ ) ;; This function is called in ways that allow us to make the first parameter ;; non-nullable. - ;; CHECK: (func $various-params-null (type $13) (param $x (ref none)) (param $y (ref null $"{i32}")) + ;; CHECK: (func $various-params-null (type $13) (param $x (ref exact none)) (param $y (ref null $"{i32}")) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index 1f39567d146..015e515c02b 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -110,7 +110,7 @@ ) ;; CHECK: (func $bar (type $2) (param $0 i31ref) - ;; CHECK-NEXT: (local $1 nullref) + ;; CHECK-NEXT: (local $1 (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/flatten_all-features.wast b/test/lit/passes/flatten_all-features.wast index b601b8a1295..6c117d0a907 100644 --- a/test/lit/passes/flatten_all-features.wast +++ b/test/lit/passes/flatten_all-features.wast @@ -3581,8 +3581,8 @@ ;; CHECK: (func $subtype (type $7) (result anyref) ;; CHECK-NEXT: (local $0 eqref) ;; CHECK-NEXT: (local $1 anyref) - ;; CHECK-NEXT: (local $2 nullref) - ;; CHECK-NEXT: (local $3 nullref) + ;; CHECK-NEXT: (local $2 (exact nullref)) + ;; CHECK-NEXT: (local $3 (exact nullref)) ;; CHECK-NEXT: (local $4 eqref) ;; CHECK-NEXT: (local $5 eqref) ;; CHECK-NEXT: (local $6 eqref) @@ -3680,7 +3680,7 @@ ;; CHECK: (type $0 (func (result funcref))) ;; CHECK: (func $0 (type $0) (result funcref) - ;; CHECK-NEXT: (local $0 (ref nofunc)) + ;; CHECK-NEXT: (local $0 (ref exact nofunc)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (ref.as_non_null ;; CHECK-NEXT: (ref.null nofunc) diff --git a/test/lit/passes/global-refining.wast b/test/lit/passes/global-refining.wast index 927dfd1f26c..7be5a9e4e2a 100644 --- a/test/lit/passes/global-refining.wast +++ b/test/lit/passes/global-refining.wast @@ -11,8 +11,8 @@ ;; CLOSD: (type $foo_t (func)) (type $foo_t (func)) - ;; CHECK: (global $func-null-init (mut nullfuncref) (ref.null nofunc)) - ;; CLOSD: (global $func-null-init (mut nullfuncref) (ref.null nofunc)) + ;; CHECK: (global $func-null-init (mut (exact nullfuncref)) (ref.null nofunc)) + ;; CLOSD: (global $func-null-init (mut (exact nullfuncref)) (ref.null nofunc)) (global $func-null-init (mut funcref) (ref.null $foo_t)) ;; CHECK: (global $func-func-init (mut (ref $foo_t)) (ref.func $foo)) ;; CLOSD: (global $func-func-init (mut (ref $foo_t)) (ref.func $foo)) @@ -32,8 +32,8 @@ ;; CLOSD: (type $foo_t (func)) (type $foo_t (func)) - ;; CHECK: (global $func-null-init (mut nullfuncref) (ref.null nofunc)) - ;; CLOSD: (global $func-null-init (mut nullfuncref) (ref.null nofunc)) + ;; CHECK: (global $func-null-init (mut (exact nullfuncref)) (ref.null nofunc)) + ;; CLOSD: (global $func-null-init (mut (exact nullfuncref)) (ref.null nofunc)) (global $func-null-init (mut funcref) (ref.null $foo_t)) ;; CHECK: (global $func-func-init (mut (ref null $foo_t)) (ref.func $foo)) ;; CLOSD: (global $func-func-init (mut (ref null $foo_t)) (ref.func $foo)) @@ -202,16 +202,16 @@ ;; (ref null func) to nullfuncref only when not exported, and if exported, then ;; only when immutable in open world. (module - ;; CHECK: (global $mut (mut nullfuncref) (ref.null nofunc)) - ;; CLOSD: (global $mut (mut nullfuncref) (ref.null nofunc)) + ;; CHECK: (global $mut (mut (exact nullfuncref)) (ref.null nofunc)) + ;; CLOSD: (global $mut (mut (exact nullfuncref)) (ref.null nofunc)) (global $mut (mut (ref null func)) (ref.null nofunc)) - ;; CHECK: (global $imm nullfuncref (ref.null nofunc)) - ;; CLOSD: (global $imm nullfuncref (ref.null nofunc)) + ;; CHECK: (global $imm (exact nullfuncref) (ref.null nofunc)) + ;; CLOSD: (global $imm (exact nullfuncref) (ref.null nofunc)) (global $imm (ref null func) (ref.null nofunc)) ;; CHECK: (global $mut-exp (mut funcref) (ref.null nofunc)) ;; CLOSD: (global $mut-exp (mut funcref) (ref.null nofunc)) (global $mut-exp (mut (ref null func)) (ref.null nofunc)) - ;; CHECK: (global $imm-exp nullfuncref (ref.null nofunc)) + ;; CHECK: (global $imm-exp (exact nullfuncref) (ref.null nofunc)) ;; CLOSD: (global $imm-exp funcref (ref.null nofunc)) (global $imm-exp (ref null func) (ref.null nofunc)) diff --git a/test/lit/passes/gufa-refs.wast b/test/lit/passes/gufa-refs.wast index 6d48d651a5f..6142550638d 100644 --- a/test/lit/passes/gufa-refs.wast +++ b/test/lit/passes/gufa-refs.wast @@ -955,7 +955,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (struct.get $child 1 ;; CHECK-NEXT: (local.get $child) @@ -970,7 +970,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (struct.get $parent 0 ;; CHECK-NEXT: (local.get $parent) @@ -1072,9 +1072,9 @@ ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block $block (result nullref) + ;; CHECK-NEXT: (block $block (result (exact nullref)) ;; CHECK-NEXT: (br $block ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) @@ -1085,9 +1085,9 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block $block0 (result nullref) + ;; CHECK-NEXT: (block $block0 (result (exact nullref)) ;; CHECK-NEXT: (br $block0 ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) @@ -1098,13 +1098,13 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block $block1 (result nullref) + ;; CHECK-NEXT: (block $block1 (result (exact nullref)) ;; CHECK-NEXT: (br $block1 - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.cast nullref + ;; CHECK-NEXT: (ref.cast (exact nullref) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -1237,7 +1237,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (struct.get $child 0 ;; CHECK-NEXT: (local.get $child) @@ -1577,7 +1577,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (array.get $null ;; CHECK-NEXT: (array.new_default $null @@ -1775,10 +1775,10 @@ ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.set $x - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (struct.new $storage - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $pass-through ;; CHECK-NEXT: (ref.null none) @@ -1928,7 +1928,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) @@ -2153,7 +2153,7 @@ ;; CHECK-NEXT: (pop (tuple anyref anyref)) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (tuple.drop 2 ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) @@ -2213,7 +2213,7 @@ ;; CHECK: (func $func (type $1) (result (ref $"{}")) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block $block (result (ref none)) + ;; CHECK-NEXT: (block $block (result (ref exact none)) ;; CHECK-NEXT: (br_on_non_null $block ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) @@ -2539,10 +2539,10 @@ ;; CHECK: (func $test-nulls (type $2) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.cast nullref - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (ref.cast (exact nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $import) ;; CHECK-NEXT: ) @@ -2554,9 +2554,9 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.cast nullref + ;; CHECK-NEXT: (ref.cast (exact nullref) ;; CHECK-NEXT: (select (result i31ref) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: (ref.i31 @@ -3256,7 +3256,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.eq - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $import) ;; CHECK-NEXT: ) @@ -3793,7 +3793,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (array.get $chars ;; CHECK-NEXT: (local.get $chars) @@ -6056,7 +6056,7 @@ ) (module - ;; CHECK: (type $0 (func (result i64 nullref i32))) + ;; CHECK: (type $0 (func (result i64 (exact nullref) i32))) ;; CHECK: (type $array (sub (array (mut i8)))) (type $array (sub (array (mut i8)))) @@ -6096,7 +6096,7 @@ ;; CHECK: (func $loop-tuple-br_on (type $2) ;; CHECK-NEXT: (tuple.drop 3 - ;; CHECK-NEXT: (loop $loop (type $0) (result i64 nullref i32) + ;; CHECK-NEXT: (loop $loop (type $0) (result i64 (exact nullref) i32) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop diff --git a/test/lit/passes/gufa-vs-cfp.wast b/test/lit/passes/gufa-vs-cfp.wast index bd731d89150..eb26820b74d 100644 --- a/test/lit/passes/gufa-vs-cfp.wast +++ b/test/lit/passes/gufa-vs-cfp.wast @@ -1087,7 +1087,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (struct.get $struct3 2 ;; CHECK-NEXT: (local.get $ref) @@ -1329,7 +1329,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $create3) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/heap2local-rmw.wast b/test/lit/passes/heap2local-rmw.wast index 33fef1cb506..f0b397dcc99 100644 --- a/test/lit/passes/heap2local-rmw.wast +++ b/test/lit/passes/heap2local-rmw.wast @@ -50,7 +50,7 @@ ;; CHECK-NEXT: (local $1 (ref null $struct)) ;; CHECK-NEXT: (struct.atomic.rmw.cmpxchg $struct 0 ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) @@ -74,7 +74,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -107,7 +107,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -140,7 +140,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -173,7 +173,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -206,7 +206,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -239,7 +239,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -270,7 +270,7 @@ ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (local $3 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -312,7 +312,7 @@ ;; CHECK-NEXT: (local $1 i64) ;; CHECK-NEXT: (local $2 i64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i64.const 0) ;; CHECK-NEXT: ) @@ -345,7 +345,7 @@ ;; CHECK-NEXT: (local $1 i64) ;; CHECK-NEXT: (local $2 i64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i64.const 0) ;; CHECK-NEXT: ) @@ -378,7 +378,7 @@ ;; CHECK-NEXT: (local $1 i64) ;; CHECK-NEXT: (local $2 i64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i64.const 0) ;; CHECK-NEXT: ) @@ -411,7 +411,7 @@ ;; CHECK-NEXT: (local $1 i64) ;; CHECK-NEXT: (local $2 i64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i64.const 0) ;; CHECK-NEXT: ) @@ -444,7 +444,7 @@ ;; CHECK-NEXT: (local $1 i64) ;; CHECK-NEXT: (local $2 i64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i64.const 0) ;; CHECK-NEXT: ) @@ -477,7 +477,7 @@ ;; CHECK-NEXT: (local $1 i64) ;; CHECK-NEXT: (local $2 i64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i64.const 0) ;; CHECK-NEXT: ) @@ -508,7 +508,7 @@ ;; CHECK-NEXT: (local $2 i64) ;; CHECK-NEXT: (local $3 i64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i64.const 0) ;; CHECK-NEXT: ) @@ -550,7 +550,7 @@ ;; CHECK-NEXT: (local $2 (ref null $struct)) ;; CHECK-NEXT: (local $3 (ref null $struct)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) @@ -581,7 +581,7 @@ ;; CHECK-NEXT: (local $4 (ref null $struct)) ;; CHECK-NEXT: (local $5 (ref null $struct)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) @@ -623,7 +623,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -657,7 +657,7 @@ ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (local $3 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/heap2local.wast b/test/lit/passes/heap2local.wast index 619a33a23c5..8b0384671c3 100644 --- a/test/lit/passes/heap2local.wast +++ b/test/lit/passes/heap2local.wast @@ -50,7 +50,7 @@ ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (local $1 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -74,7 +74,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -102,7 +102,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -135,7 +135,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -162,7 +162,7 @@ ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (local $1 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -193,7 +193,7 @@ ;; CHECK-NEXT: (local $5 i32) ;; CHECK-NEXT: (local $6 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $3 ;; CHECK-NEXT: (i32.const 1337) ;; CHECK-NEXT: ) @@ -259,7 +259,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $5 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -318,7 +318,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) @@ -388,7 +388,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result (ref $struct.A)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (struct.new_default $struct.A) ;; CHECK-NEXT: ) @@ -418,7 +418,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -456,7 +456,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -494,7 +494,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -559,7 +559,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -673,7 +673,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -713,7 +713,7 @@ ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (local $3 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -771,7 +771,7 @@ ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (local $3 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -821,7 +821,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -901,7 +901,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -927,7 +927,7 @@ ;; CHECK-NEXT: (local $ref (ref null $struct.recursive)) ;; CHECK-NEXT: (local $1 (ref null $struct.recursive)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) @@ -975,7 +975,7 @@ ;; CHECK-NEXT: (local $1 (ref null $struct.recursive)) ;; CHECK-NEXT: (local $2 (ref null $struct.recursive)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (struct.new_default $struct.recursive) ;; CHECK-NEXT: ) @@ -1070,7 +1070,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result (ref $struct.A)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (local.get $a) ;; CHECK-NEXT: ) @@ -1104,7 +1104,7 @@ ;; CHECK-NEXT: (local $5 f64) ;; CHECK-NEXT: (loop $outer ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $4 ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) @@ -1271,7 +1271,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -1287,7 +1287,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -1303,7 +1303,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $4 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -1343,7 +1343,7 @@ ;; CHECK-NEXT: (local $3 i32) ;; CHECK-NEXT: (local $4 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -1362,7 +1362,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $3 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -1411,7 +1411,7 @@ ;; CHECK-NEXT: (local $4 i32) ;; CHECK-NEXT: (local $5 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -1422,7 +1422,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $4 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -1553,7 +1553,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -1749,7 +1749,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -1800,7 +1800,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -1851,7 +1851,7 @@ ;; CHECK-NEXT: (block $block (result (ref null $struct.A)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (br_if $block - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $3 ;; CHECK-NEXT: (i32.const 42) ;; CHECK-NEXT: ) @@ -1905,7 +1905,7 @@ ;; CHECK-NEXT: (br_if $loop ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -1938,7 +1938,7 @@ ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -1977,7 +1977,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -2061,7 +2061,7 @@ ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (local $3 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -2096,7 +2096,7 @@ ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (local $3 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -2138,7 +2138,7 @@ ;; CHECK-NEXT: (local.get $other) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $3 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -2174,7 +2174,7 @@ ;; CHECK-NEXT: (local $3 i32) ;; CHECK-NEXT: (local $4 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $3 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -2216,7 +2216,7 @@ ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (local $3 f64) ;; CHECK-NEXT: (ref.eq - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -2253,7 +2253,7 @@ ;; CHECK-NEXT: (local $3 f64) ;; CHECK-NEXT: (ref.eq ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -2287,7 +2287,7 @@ ;; CHECK-NEXT: (local $4 i32) ;; CHECK-NEXT: (local $5 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $4 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -2334,7 +2334,7 @@ ;; CHECK-NEXT: (local $6 i32) ;; CHECK-NEXT: (local $7 f64) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -2351,7 +2351,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $6 ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) @@ -2396,7 +2396,7 @@ ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.is_null - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $3 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -2450,7 +2450,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -2472,7 +2472,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $6 ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) @@ -2494,7 +2494,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $10 ;; CHECK-NEXT: (i32.const 3) ;; CHECK-NEXT: ) @@ -2560,7 +2560,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $3 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -2582,7 +2582,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $7 ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) @@ -2648,7 +2648,7 @@ ;; CHECK-NEXT: (ref.cast (ref $B) ;; CHECK-NEXT: (block (result (ref $A)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $3 ;; CHECK-NEXT: (struct.new $A ;; CHECK-NEXT: (ref.null none) @@ -2697,7 +2697,7 @@ ;; CHECK-NEXT: (local $2 (ref $A)) ;; CHECK-NEXT: (local $3 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (struct.new $A ;; CHECK-NEXT: (ref.null none) @@ -2736,7 +2736,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (struct.new $A ;; CHECK-NEXT: (ref.null none) @@ -2776,7 +2776,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (struct.new $A ;; CHECK-NEXT: (ref.null none) @@ -2819,9 +2819,9 @@ ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) - ;; CHECK-NEXT: (block (result nullref) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) + ;; CHECK-NEXT: (block (result (exact nullref)) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -2870,7 +2870,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) @@ -2907,7 +2907,7 @@ ;; CHECK-NEXT: (struct.new_default $struct) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) @@ -3005,7 +3005,7 @@ ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (local $3 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -3151,7 +3151,7 @@ ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 1337) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $4 ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: ) @@ -3197,7 +3197,7 @@ ;; CHECK-NEXT: (local $4 i32) ;; CHECK-NEXT: (local $5 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $4 ;; CHECK-NEXT: (call $get-i32) ;; CHECK-NEXT: ) @@ -3327,11 +3327,11 @@ ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (local $3 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 42) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $3 ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: ) @@ -3391,7 +3391,7 @@ ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (call $get-i32) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $8 ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) @@ -3500,7 +3500,7 @@ ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (call $get-i32) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -3512,7 +3512,7 @@ ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (call $get-i32) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $3 ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: ) @@ -3533,7 +3533,7 @@ ;; CHECK-NEXT: (local.set $4 ;; CHECK-NEXT: (call $get-i32) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $24 ;; CHECK-NEXT: (local.get $4) ;; CHECK-NEXT: ) @@ -3706,7 +3706,7 @@ ;; CHECK: (func $array.nested.refinalize.get (type $3) (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -3726,7 +3726,7 @@ ;; CHECK: (func $array.nested.refinalize.set (type $2) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -3756,7 +3756,7 @@ ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $3 ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: ) @@ -3849,7 +3849,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -3867,7 +3867,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -3916,7 +3916,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -3983,7 +3983,7 @@ ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $4 ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) @@ -4030,7 +4030,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -4070,7 +4070,7 @@ ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $3 ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: ) @@ -4173,7 +4173,7 @@ ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (local $3 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -4253,7 +4253,7 @@ ;; CHECK-NEXT: (local $2 i32) ;; CHECK-NEXT: (local $3 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -4319,7 +4319,7 @@ ;; CHECK: (func $array.cast.struct (type $0) (result (ref struct)) ;; CHECK-NEXT: (local $eq (ref eq)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -4341,7 +4341,7 @@ ;; CHECK: (func $array.cast.struct.null (type $3) (result structref) ;; CHECK-NEXT: (local $eq (ref eq)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -4380,7 +4380,7 @@ ;; CHECK-NEXT: (local $eq (ref eq)) ;; CHECK-NEXT: (local $array (ref array)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -4405,7 +4405,7 @@ ;; CHECK-NEXT: (local.tee $struct ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -4442,7 +4442,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result structref) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) @@ -4513,7 +4513,7 @@ ;; CHECK-NEXT: (pop i32) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (local.get $2) ;; CHECK-NEXT: ) @@ -4565,7 +4565,7 @@ ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (local.get $7) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (local.set $4 ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) @@ -4620,7 +4620,7 @@ ;; CHECK-NEXT: (local $0 (ref null $struct)) ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result (ref null (shared none))) + ;; CHECK-NEXT: (block (result (ref null exact (shared none))) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -4666,7 +4666,7 @@ ;; CHECK-NEXT: (local $0 (ref null $struct)) ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result (ref null (shared none))) + ;; CHECK-NEXT: (block (result (ref null exact (shared none))) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/local-subtyping-exact.wast b/test/lit/passes/local-subtyping-exact.wast new file mode 100644 index 00000000000..725c7d499fb --- /dev/null +++ b/test/lit/passes/local-subtyping-exact.wast @@ -0,0 +1,39 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. + +;; Check that LocalSubtyping handles exact references properly when it +;; determines that a local that would otherwise be non-nullable must be nullable +;; because of control flow dominance constraints. + +;; RUN: wasm-opt %s -all --local-subtyping -S -o - | filecheck %s + +(module + ;; CHECK: (func $test (type $0) (param $0 (exact nullref)) (result anyref) + ;; CHECK-NEXT: (local $1 (exact nullref)) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + (func $test (param (exact nullref)) (result anyref) + (local (exact nullref)) + (if + (i32.const 0) + (then + (local.set 1 + ;; This would let the local be (ref exact none) if it dominated the get. + (ref.as_non_null + (local.get 0) + ) + ) + ) + ) + (local.get 1) + ) +) diff --git a/test/lit/passes/local-subtyping-nn.wast b/test/lit/passes/local-subtyping-nn.wast index 3754230d82f..f2237156f01 100644 --- a/test/lit/passes/local-subtyping-nn.wast +++ b/test/lit/passes/local-subtyping-nn.wast @@ -8,7 +8,7 @@ (import "out" "i32" (func $i32 (result i32))) ;; CHECK: (func $non-nullable (type $1) - ;; CHECK-NEXT: (local $x (ref none)) + ;; CHECK-NEXT: (local $x (ref exact none)) ;; CHECK-NEXT: (local $y (ref $0)) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (ref.as_non_null @@ -41,7 +41,7 @@ ) ;; CHECK: (func $uses-default (type $2) (param $i i32) - ;; CHECK-NEXT: (local $x nullref) + ;; CHECK-NEXT: (local $x (exact nullref)) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (local.get $i) ;; CHECK-NEXT: (then diff --git a/test/lit/passes/local-subtyping.wast b/test/lit/passes/local-subtyping.wast index 795a0a01cd1..777df93524f 100644 --- a/test/lit/passes/local-subtyping.wast +++ b/test/lit/passes/local-subtyping.wast @@ -273,7 +273,7 @@ ) ;; CHECK: (func $multiple-iterations-refinalize-call-ref-bottom (type $0) - ;; CHECK-NEXT: (local $f nullfuncref) + ;; CHECK-NEXT: (local $f (exact nullfuncref)) ;; CHECK-NEXT: (local $x (ref none)) ;; CHECK-NEXT: (local.set $f ;; CHECK-NEXT: (ref.null nofunc) diff --git a/test/lit/passes/merge-blocks.wast b/test/lit/passes/merge-blocks.wast index 86181f7a488..7a517ccbc27 100644 --- a/test/lit/passes/merge-blocks.wast +++ b/test/lit/passes/merge-blocks.wast @@ -20,7 +20,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block $label$1 (result i31ref) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (br_on_cast $label$1 nullref (ref none) + ;; CHECK-NEXT: (br_on_cast $label$1 (exact nullref) (ref exact none) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/monomorphize-context.wast b/test/lit/passes/monomorphize-context.wast index 7f9cb2f566e..92b483a7ae1 100644 --- a/test/lit/passes/monomorphize-context.wast +++ b/test/lit/passes/monomorphize-context.wast @@ -277,7 +277,7 @@ ;; ALWAYS-NEXT: ) ;; ALWAYS-NEXT: ) ;; ALWAYS-NEXT: (local.set $21 -;; ALWAYS-NEXT: (ref.cast nullref +;; ALWAYS-NEXT: (ref.cast (exact nullref) ;; ALWAYS-NEXT: (ref.null none) ;; ALWAYS-NEXT: ) ;; ALWAYS-NEXT: ) @@ -555,7 +555,7 @@ ;; ALWAYS-NEXT: ) ;; ALWAYS-NEXT: ) ;; ALWAYS-NEXT: (local.set $21 -;; ALWAYS-NEXT: (ref.cast nullref +;; ALWAYS-NEXT: (ref.cast (exact nullref) ;; ALWAYS-NEXT: (ref.null none) ;; ALWAYS-NEXT: ) ;; ALWAYS-NEXT: ) diff --git a/test/lit/passes/optimize-instructions-exact.wast b/test/lit/passes/optimize-instructions-exact.wast new file mode 100644 index 00000000000..61769b78f34 --- /dev/null +++ b/test/lit/passes/optimize-instructions-exact.wast @@ -0,0 +1,33 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. + +;; Check that optimizations on casts involving exact reference types work +;; correctly. + +;; RUN: wasm-opt %s -all --optimize-instructions -S -o - | filecheck %s + +(module + ;; CHECK: (func $cast-any-to-exact-none (type $0) (param $0 anyref) (result (exact nullref)) + ;; CHECK-NEXT: (ref.cast (exact nullref) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cast-any-to-exact-none (param anyref) (result (exact nullref)) + ;; This will not be changed, but should not trigger an assertion. + (ref.cast (exact nullref) + (local.get 0) + ) + ) + ;; CHECK: (func $cast-null-to-exact-none (type $1) (result (exact nullref)) + ;; CHECK-NEXT: (local $0 nullref) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + (func $cast-null-to-exact-none (result (exact nullref)) + (local nullref) + (ref.cast (exact nullref) + (local.get 0) + ) + ) +) diff --git a/test/lit/passes/optimize-instructions-gc-tnh.wast b/test/lit/passes/optimize-instructions-gc-tnh.wast index c930f2fc19a..3893e048e14 100644 --- a/test/lit/passes/optimize-instructions-gc-tnh.wast +++ b/test/lit/passes/optimize-instructions-gc-tnh.wast @@ -509,7 +509,7 @@ ;; TNH-NEXT: ) ;; TNH-NEXT: (block ;; TNH-NEXT: (drop - ;; TNH-NEXT: (block (result nullref) + ;; TNH-NEXT: (block (result (exact nullref)) ;; TNH-NEXT: (drop ;; TNH-NEXT: (call $get-i32) ;; TNH-NEXT: ) @@ -532,7 +532,7 @@ ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: (block ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (block (result nullref) + ;; NO_TNH-NEXT: (block (result (exact nullref)) ;; NO_TNH-NEXT: (drop ;; NO_TNH-NEXT: (call $get-i32) ;; NO_TNH-NEXT: ) diff --git a/test/lit/passes/optimize-instructions-gc.wast b/test/lit/passes/optimize-instructions-gc.wast index 4b88507ffc7..86804d7ddbd 100644 --- a/test/lit/passes/optimize-instructions-gc.wast +++ b/test/lit/passes/optimize-instructions-gc.wast @@ -1570,7 +1570,7 @@ ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.tee $a ;; CHECK-NEXT: (ref.null none) @@ -1590,7 +1590,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result nullref) ;; CHECK-NEXT: (ref.cast nullref diff --git a/test/lit/passes/precompute-gc.wast b/test/lit/passes/precompute-gc.wast index fc4cc7c2ee9..94adaa53fbb 100644 --- a/test/lit/passes/precompute-gc.wast +++ b/test/lit/passes/precompute-gc.wast @@ -28,7 +28,7 @@ ;; CHECK: (func $test-fallthrough (type $func-return-i32) (result i32) ;; CHECK-NEXT: (local $x funcref) ;; CHECK-NEXT: (local.set $x - ;; CHECK-NEXT: (block (result nullfuncref) + ;; CHECK-NEXT: (block (result (exact nullfuncref)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $test-fallthrough) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/remove-unused-brs-exact.wast b/test/lit/passes/remove-unused-brs-exact.wast new file mode 100644 index 00000000000..3bebd07de02 --- /dev/null +++ b/test/lit/passes/remove-unused-brs-exact.wast @@ -0,0 +1,40 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. + +;; RUN: wasm-opt %s -all --remove-unused-brs -S -o - | filecheck %s + +;; Check that we optimize the cast correctly when the fallthrough has exact +;; type. In particular, we should not insert a ref.as_non_null, which would +;; trap. + +(module + ;; CHECK: (func $br_on_cast_fail (type $0) (param $0 (exact nullref)) + ;; CHECK-NEXT: (local $1 nullref) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.cast (exact nullref) + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $br_on_cast_fail (param (ref null exact none)) + (local $1 nullref) + (drop + (block $block (result (ref none)) + (drop + (br_on_cast_fail $block nullref nullref + (local.tee $1 + (local.get 0) + ) + ) + ) + (return) + ) + ) + ) +) diff --git a/test/lit/passes/remove-unused-brs-gc.wast b/test/lit/passes/remove-unused-brs-gc.wast index 268bbde2092..64f11f8ca0b 100644 --- a/test/lit/passes/remove-unused-brs-gc.wast +++ b/test/lit/passes/remove-unused-brs-gc.wast @@ -63,7 +63,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (br_on_non_null $block ;; CHECK-NEXT: (local.get $struct) ;; CHECK-NEXT: ) @@ -119,7 +119,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (br_on_non_null $block ;; CHECK-NEXT: (ref.cast (ref null $struct) ;; CHECK-NEXT: (local.tee $any @@ -438,7 +438,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (br_on_non_null $block ;; CHECK-NEXT: (local.get $nullable-struct2) ;; CHECK-NEXT: ) @@ -509,7 +509,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (br_on_non_null $block ;; CHECK-NEXT: (local.tee $any ;; CHECK-NEXT: (local.get $nullable-struct2) @@ -571,7 +571,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (br_on_non_null $block ;; CHECK-NEXT: (local.tee $any ;; CHECK-NEXT: (local.get $nullable-struct2) @@ -706,7 +706,7 @@ ;; CHECK-NEXT: (if (result i32) ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (ref.test (ref none) + ;; CHECK-NEXT: (ref.test (ref exact none) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -716,13 +716,13 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (if (result nullref) + ;; CHECK-NEXT: (if (result (exact nullref)) ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (then ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (ref.cast nullref + ;; CHECK-NEXT: (ref.cast (exact nullref) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -734,7 +734,7 @@ ;; CHECK-NEXT: (then ;; CHECK-NEXT: (block $something (result (ref null $struct)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (br_on_non_null $something ;; CHECK-NEXT: (local.get $struct) ;; CHECK-NEXT: ) @@ -750,8 +750,8 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (select (result nullref) - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (select (result (exact nullref)) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (block $nothing ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block @@ -858,7 +858,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (select (result nullref) + ;; CHECK-NEXT: (select (result (exact nullref)) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: (local.get $x) diff --git a/test/lit/passes/remove-unused-types-exact.wast b/test/lit/passes/remove-unused-types-exact.wast new file mode 100644 index 00000000000..6cb9c1ddd74 --- /dev/null +++ b/test/lit/passes/remove-unused-types-exact.wast @@ -0,0 +1,16 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. + +;; RUN: wasm-opt %s -all --closed-world --remove-unused-types -S -o - | filecheck %s + +;; Test that a simple type rewrite handles exact references in heap type +;; definitions correctly. In particular, the function should continue returning +;; an exact nullref and the call expression should have the same type. + +(module + ;; CHECK: (func $return-exact (type $0) (result (exact nullref)) + ;; CHECK-NEXT: (call $return-exact) + ;; CHECK-NEXT: ) + (func $return-exact (result (exact nullref)) + (call $return-exact) + ) +) diff --git a/test/lit/passes/signature-refining_gto.wat b/test/lit/passes/signature-refining_gto.wat index c69eeb24455..1eedcd4f67c 100644 --- a/test/lit/passes/signature-refining_gto.wat +++ b/test/lit/passes/signature-refining_gto.wat @@ -9,11 +9,11 @@ ;; CHECK-NOT: (type $A (type $A (struct (field (mut (ref null $A))))) - ;; CHECK: (type $0 (func (param (ref none)))) + ;; CHECK: (type $0 (func (param (ref exact none)))) ;; CHECK: (type $1 (func (param funcref i32))) - ;; CHECK: (func $struct.get (type $0) (param $0 (ref none)) + ;; CHECK: (func $struct.get (type $0) (param $0 (ref exact none)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/ssa.wast b/test/lit/passes/ssa.wast index 0d696f75a0d..a0b18b0777d 100644 --- a/test/lit/passes/ssa.wast +++ b/test/lit/passes/ssa.wast @@ -36,9 +36,9 @@ ;; CHECK: (func $refine-to-null (type $3) (result (ref $A)) ;; CHECK-NEXT: (local $0 (ref null $A)) - ;; CHECK-NEXT: (block $label (result (ref none)) + ;; CHECK-NEXT: (block $label (result (ref exact none)) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (br_on_cast $label nullref (ref none) + ;; CHECK-NEXT: (br_on_cast $label (exact nullref) (ref exact none) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/type-refining-gufa.wast b/test/lit/passes/type-refining-gufa.wast index c866d80dca2..2304086078c 100644 --- a/test/lit/passes/type-refining-gufa.wast +++ b/test/lit/passes/type-refining-gufa.wast @@ -28,7 +28,7 @@ ;; NRML: (rec ;; NRML-NEXT: (type $A (sub (struct (field (mut nullref))))) ;; GUFA: (rec - ;; GUFA-NEXT: (type $A (sub (struct (field (mut nullref))))) + ;; GUFA-NEXT: (type $A (sub (struct (field (mut (exact nullref)))))) ;; O3O3: (rec ;; O3O3-NEXT: (type $A (sub (struct))) (type $A (sub (struct (field (mut anyref))))) @@ -376,7 +376,7 @@ ;; the field to nullref. (module ;; NRML: (type $struct (struct (field nullfuncref))) - ;; GUFA: (type $struct (struct (field nullfuncref))) + ;; GUFA: (type $struct (struct (field (exact nullfuncref)))) (type $struct (struct (field funcref))) ;; NRML: (global $C (ref $struct) (struct.new_default $struct)) diff --git a/test/lit/passes/type-refining-isorecursive.wast b/test/lit/passes/type-refining-isorecursive.wast index 537f99e668e..025b8cec9f5 100644 --- a/test/lit/passes/type-refining-isorecursive.wast +++ b/test/lit/passes/type-refining-isorecursive.wast @@ -5,11 +5,11 @@ ;; The types should be refined to a set of three mutually recursive types. ;; CHECK: (rec - ;; CHECK-NEXT: (type $2 (sub (struct (field nullexternref) (field (ref $0))))) + ;; CHECK-NEXT: (type $2 (sub (struct (field (exact nullexternref)) (field (ref $0))))) - ;; CHECK: (type $1 (sub (struct (field nullfuncref) (field (ref $2))))) + ;; CHECK: (type $1 (sub (struct (field (exact nullfuncref)) (field (ref $2))))) - ;; CHECK: (type $0 (sub (struct (field nullref) (field (ref $1))))) + ;; CHECK: (type $0 (sub (struct (field (exact nullref)) (field (ref $1))))) (type $0 (sub (struct nullref anyref))) (type $1 (sub (struct nullfuncref anyref))) (type $2 (sub (struct nullexternref anyref))) diff --git a/test/lit/passes/type-refining-rmw.wast b/test/lit/passes/type-refining-rmw.wast index a4bb0a85cae..7739fceb0f4 100644 --- a/test/lit/passes/type-refining-rmw.wast +++ b/test/lit/passes/type-refining-rmw.wast @@ -6,7 +6,7 @@ (module (rec ;; CHECK: (rec - ;; CHECK-NEXT: (type $null (shared (struct (field (mut (ref null (shared none))))))) + ;; CHECK-NEXT: (type $null (shared (struct (field (mut (ref null exact (shared none))))))) (type $null (shared (struct (field (mut (ref null (shared any))))))) ;; CHECK: (type $i31 (shared (struct (field (mut (ref (shared i31))))))) @@ -75,7 +75,7 @@ (module (rec ;; CHECK: (rec - ;; CHECK-NEXT: (type $null (shared (struct (field (mut (ref null (shared none))))))) + ;; CHECK-NEXT: (type $null (shared (struct (field (mut (ref null exact (shared none))))))) (type $null (shared (struct (field (mut (ref null (shared eq))))))) ;; CHECK: (type $i31 (shared (struct (field (mut (ref (shared i31))))))) diff --git a/test/lit/passes/type-refining.wast b/test/lit/passes/type-refining.wast index 622e7422011..02357e8b6c0 100644 --- a/test/lit/passes/type-refining.wast +++ b/test/lit/passes/type-refining.wast @@ -1101,7 +1101,7 @@ ;; CHECK-NEXT: (local.get $struct) ;; CHECK-NEXT: (block ;; (replaces unreachable StructGet we can't emit) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (block (result (exact nullref)) ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -1180,7 +1180,7 @@ (module (rec ;; CHECK: (rec - ;; CHECK-NEXT: (type $A (struct (field (mut nullref)))) + ;; CHECK-NEXT: (type $A (struct (field (mut (exact nullref))))) (type $A (struct (field (mut anyref)))) ;; CHECK: (type $B (struct (field (mut nullref)))) (type $B (struct (field (mut (ref null $A))))) @@ -1234,7 +1234,7 @@ (module ;; CHECK: (rec - ;; CHECK-NEXT: (type $A (struct (field (mut (ref noextern))))) + ;; CHECK-NEXT: (type $A (struct (field (mut (ref exact noextern))))) (type $A (struct (field (mut externref)))) ;; CHECK: (type $1 (func)) @@ -1252,7 +1252,7 @@ ;; CHECK: (func $struct.new (type $2) (param $extern externref) (result anyref) ;; CHECK-NEXT: (struct.new $A - ;; CHECK-NEXT: (ref.cast (ref noextern) + ;; CHECK-NEXT: (ref.cast (ref exact noextern) ;; CHECK-NEXT: (try (result externref) ;; CHECK-NEXT: (do ;; CHECK-NEXT: (struct.get $A 0 @@ -1304,7 +1304,7 @@ ;; CHECK: (func $struct.set (type $3) (param $ref (ref $A)) (param $extern externref) ;; CHECK-NEXT: (struct.set $A 0 ;; CHECK-NEXT: (local.get $ref) - ;; CHECK-NEXT: (ref.cast (ref noextern) + ;; CHECK-NEXT: (ref.cast (ref exact noextern) ;; CHECK-NEXT: (try (result externref) ;; CHECK-NEXT: (do ;; CHECK-NEXT: (struct.get $A 0 @@ -1581,7 +1581,7 @@ (type $never (sub (struct (field i32)))) ;; CHECK: (rec - ;; CHECK-NEXT: (type $optimizable (struct (field (mut nullfuncref)))) + ;; CHECK-NEXT: (type $optimizable (struct (field (mut (exact nullfuncref))))) (type $optimizable (struct (field (mut (ref null func))))) ;; CHECK: (type $2 (func)) @@ -1604,7 +1604,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result (ref none)) + ;; CHECK-NEXT: (block (result (ref exact none)) ;; CHECK-NEXT: (ref.as_non_null ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) diff --git a/test/passes/precompute_all-features.txt b/test/passes/precompute_all-features.txt index 189adadcec1..1372035920f 100644 --- a/test/passes/precompute_all-features.txt +++ b/test/passes/precompute_all-features.txt @@ -286,7 +286,7 @@ ) ) (drop - (block $l2 (result nullexternref) + (block $l2 (result (exact nullexternref)) (drop (block $l3 (global.set $global-mut