diff options
32 files changed, 157 insertions, 362 deletions
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index 232429fe0..e452f82d6 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -606,9 +606,9 @@ instructions = [ ("ref.is_data", "makeRefTest(s, Type(HeapType::data, NonNullable))"), ("ref.is_i31", "makeRefTest(s, Type(HeapType::i31, NonNullable))"), ("ref.as_non_null", "makeRefAs(s, RefAsNonNull)"), - ("ref.as_func", "makeRefAs(s, RefAsFunc)"), - ("ref.as_data", "makeRefAs(s, RefAsData)"), - ("ref.as_i31", "makeRefAs(s, RefAsI31)"), + ("ref.as_func", "makeRefCast(s, Type(HeapType::func, NonNullable))"), + ("ref.as_data", "makeRefCast(s, Type(HeapType::data, NonNullable))"), + ("ref.as_i31", "makeRefCast(s, Type(HeapType::i31, NonNullable))"), ("extern.internalize", "makeRefAs(s, ExternInternalize)"), ("extern.externalize", "makeRefAs(s, ExternExternalize)"), ("string.new_wtf8", "makeStringNew(s, StringNewWTF8)"), diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 416c8d45b..e37270f61 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -1009,9 +1009,6 @@ BinaryenOp BinaryenPromoteLowVecF32x4ToVecF64x2(void) { } BinaryenOp BinaryenSwizzleVecI8x16(void) { return SwizzleVecI8x16; } BinaryenOp BinaryenRefAsNonNull(void) { return RefAsNonNull; } -BinaryenOp BinaryenRefAsFunc(void) { return RefAsFunc; } -BinaryenOp BinaryenRefAsData(void) { return RefAsData; } -BinaryenOp BinaryenRefAsI31(void) { return RefAsI31; } BinaryenOp BinaryenRefAsExternInternalize(void) { return ExternInternalize; } BinaryenOp BinaryenRefAsExternExternalize(void) { return ExternExternalize; } BinaryenOp BinaryenBrOnNull(void) { return BrOnNull; } diff --git a/src/binaryen-c.h b/src/binaryen-c.h index 2c861a141..b9f78e2a7 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -671,9 +671,6 @@ BINARYEN_API BinaryenOp BinaryenDemoteZeroVecF64x2ToVecF32x4(void); BINARYEN_API BinaryenOp BinaryenPromoteLowVecF32x4ToVecF64x2(void); BINARYEN_API BinaryenOp BinaryenSwizzleVecI8x16(void); BINARYEN_API BinaryenOp BinaryenRefAsNonNull(void); -BINARYEN_API BinaryenOp BinaryenRefAsFunc(void); -BINARYEN_API BinaryenOp BinaryenRefAsData(void); -BINARYEN_API BinaryenOp BinaryenRefAsI31(void); BINARYEN_API BinaryenOp BinaryenRefAsExternInternalize(void); BINARYEN_API BinaryenOp BinaryenRefAsExternExternalize(void); BINARYEN_API BinaryenOp BinaryenBrOnNull(void); diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index 70644827c..dbf3b71bf 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -3000,13 +3000,13 @@ switch (buf[0]) { case 'a': { switch (buf[7]) { case 'd': - if (op == "ref.as_data"sv) { return makeRefAs(s, RefAsData); } + if (op == "ref.as_data"sv) { return makeRefCast(s, Type(HeapType::data, NonNullable)); } goto parse_error; case 'f': - if (op == "ref.as_func"sv) { return makeRefAs(s, RefAsFunc); } + if (op == "ref.as_func"sv) { return makeRefCast(s, Type(HeapType::func, NonNullable)); } goto parse_error; case 'i': - if (op == "ref.as_i31"sv) { return makeRefAs(s, RefAsI31); } + if (op == "ref.as_i31"sv) { return makeRefCast(s, Type(HeapType::i31, NonNullable)); } goto parse_error; case 'n': if (op == "ref.as_non_null"sv) { return makeRefAs(s, RefAsNonNull); } @@ -8599,21 +8599,21 @@ switch (buf[0]) { switch (buf[7]) { case 'd': if (op == "ref.as_data"sv) { - auto ret = makeRefAs(ctx, pos, RefAsData); + auto ret = makeRefCast(ctx, pos, Type(HeapType::data, NonNullable)); CHECK_ERR(ret); return *ret; } goto parse_error; case 'f': if (op == "ref.as_func"sv) { - auto ret = makeRefAs(ctx, pos, RefAsFunc); + auto ret = makeRefCast(ctx, pos, Type(HeapType::func, NonNullable)); CHECK_ERR(ret); return *ret; } goto parse_error; case 'i': if (op == "ref.as_i31"sv) { - auto ret = makeRefAs(ctx, pos, RefAsI31); + auto ret = makeRefCast(ctx, pos, Type(HeapType::i31, NonNullable)); CHECK_ERR(ret); return *ret; } diff --git a/src/ir/gc-type-utils.h b/src/ir/gc-type-utils.h index c8f2b26af..318b1b3a3 100644 --- a/src/ir/gc-type-utils.h +++ b/src/ir/gc-type-utils.h @@ -119,28 +119,6 @@ inline EvaluationResult evaluateKindCheck(Expression* curr) { WASM_UNREACHABLE("unhandled BrOn"); } child = br->ref; - } else if (auto* as = curr->dynCast<RefAs>()) { - switch (as->op) { - // We don't check nullability here. - case RefAsNonNull: - return Unknown; - case RefAsFunc: - expected = Func; - break; - case RefAsData: - expected = Data; - break; - case RefAsI31: - expected = I31; - break; - case ExternInternalize: - case ExternExternalize: - // These instructions can never be removed. - return Unknown; - default: - WASM_UNREACHABLE("unhandled RefAs"); - } - child = as->value; } else { WASM_UNREACHABLE("invalid input to evaluateKindCheck"); } diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index 2a08c5e5a..da121e23a 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -550,9 +550,6 @@ function initializeConstants() { 'PromoteLowVecF32x4ToVecF64x2', 'SwizzleVecI8x16', 'RefAsNonNull', - 'RefAsFunc', - 'RefAsData', - 'RefAsI31', 'RefAsExternInternalize', 'RefAsExternExternalize', 'BrOnNull', @@ -2345,15 +2342,6 @@ function wrapModule(module, self = {}) { 'as_non_null'(value) { return Module['_BinaryenRefAs'](module, Module['RefAsNonNull'], value); }, - 'as_func'(value) { - return Module['_BinaryenRefAs'](module, Module['RefAsFunc'], value); - }, - 'as_data'(value) { - return Module['_BinaryenRefAs'](module, Module['RefAsData'], value); - }, - 'as_i31'(value) { - return Module['_BinaryenRefAs'](module, Module['RefAsI31'], value); - }, 'func'(func, type) { return preserveStack(() => Module['_BinaryenRefFunc'](module, strToStack(func), type)); }, diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 884afcdd4..e8c6dc1fe 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2118,35 +2118,13 @@ struct OptimizeInstructions if (curr->op == ExternExternalize || curr->op == ExternInternalize) { // We can't optimize these. Even removing a non-null cast is not valid as - // they allow nulls to filter through, unlike other RefAs* + // they allow nulls to filter through, unlike other RefAs*. return; } + assert(curr->op == RefAsNonNull); skipNonNullCast(curr->value); - - // Check if the type is the kind we are checking for. - auto result = GCTypeUtils::evaluateKindCheck(curr); - - if (result == GCTypeUtils::Success) { - // We know the kind is correct, so all that is left is a check for - // non-nullability, which we do lower down. - curr->op = RefAsNonNull; - } else if (result == GCTypeUtils::Failure) { - // This is the wrong kind, so it will trap. The binaryen optimizer does - // not differentiate traps, so we can perform a replacement here. We - // replace 2 bytes of ref.as_* with one byte of unreachable and one of a - // drop, which is no worse, and the value and the drop can be optimized - // out later if the value has no side effects. - Builder builder(*getModule()); - // Make sure to emit a block with the same type as us; leave updating - // types for other passes. - replaceCurrent(builder.makeBlock( - {builder.makeDrop(curr->value), builder.makeUnreachable()}, - curr->type)); - return; - } - - if (curr->op == RefAsNonNull && !curr->value->type.isNullable()) { + if (!curr->value->type.isNullable()) { replaceCurrent(curr->value); return; } diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 12c185e69..e0d9a46a3 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2122,6 +2122,23 @@ struct PrintExpressionContents if (curr->safety == RefCast::Unsafe) { printMedium(o, "ref.cast_nop "); } else { + // TODO: These instructions are deprecated. Remove them. + if (auto type = curr->type.getHeapType(); + type.isBasic() && curr->type.isNonNullable()) { + switch (type.getBasic()) { + case HeapType::func: + printMedium(o, "ref.as_func"); + return; + case HeapType::data: + printMedium(o, "ref.as_data"); + return; + case HeapType::i31: + printMedium(o, "ref.as_i31"); + return; + default: + break; + } + } if (curr->type.isNullable()) { printMedium(o, "ref.cast null "); } else { @@ -2329,15 +2346,6 @@ struct PrintExpressionContents case RefAsNonNull: printMedium(o, "ref.as_non_null"); break; - case RefAsFunc: - printMedium(o, "ref.as_func"); - break; - case RefAsData: - printMedium(o, "ref.as_data"); - break; - case RefAsI31: - printMedium(o, "ref.as_i31"); - break; case ExternInternalize: printMedium(o, "extern.internalize"); break; diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 1a3e5c82b..b2e1ef0c6 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1748,6 +1748,7 @@ public: void visitThrow(Throw* curr); void visitRethrow(Rethrow* curr); void visitCallRef(CallRef* curr); + void visitRefAsCast(RefCast* curr, uint32_t code); void visitRefAs(RefAs* curr, uint8_t code); [[noreturn]] void throwError(std::string text); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 104f06585..43b001f35 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1787,29 +1787,12 @@ public: switch (curr->op) { case RefAsNonNull: // We've already checked for a null. - break; - case RefAsFunc: - if (!value.type.isFunction()) { - trap("not a func"); - } - break; - case RefAsData: - if (!value.isData()) { - trap("not a data"); - } - break; - case RefAsI31: - if (value.type.getHeapType() != HeapType::i31) { - trap("not an i31"); - } - break; + return value; case ExternInternalize: case ExternExternalize: WASM_UNREACHABLE("unimplemented extern conversion"); - default: - WASM_UNREACHABLE("unimplemented ref.as_*"); } - return value; + WASM_UNREACHABLE("unimplemented ref.as_*"); } Flow visitStringNew(StringNew* curr) { WASM_UNREACHABLE("unimp"); } Flow visitStringConst(StringConst* curr) { WASM_UNREACHABLE("unimp"); } diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 6ae3e57be..013730c52 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -285,7 +285,8 @@ private: Expression* makeI31Get(Element& s, bool signed_); Expression* makeRefTest(Element& s, std::optional<Type> castType = std::nullopt); - Expression* makeRefCast(Element& s); + Expression* makeRefCast(Element& s, + std::optional<Type> castType = std::nullopt); Expression* makeRefCastNop(Element& s); Expression* makeBrOnNull(Element& s, bool onFail = false); Expression* @@ -302,6 +303,7 @@ private: Expression* makeArrayLen(Element& s); Expression* makeArrayCopy(Element& s); Expression* makeRefAs(Element& s, RefAsOp op); + Expression* makeRefAsNonNull(Element& s); Expression* makeStringNew(Element& s, StringNewOp op); Expression* makeStringConst(Element& s); Expression* makeStringMeasure(Element& s, StringMeasureOp op); diff --git a/src/wasm.h b/src/wasm.h index 7b75ec3b8..f7683e6bb 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -559,9 +559,6 @@ enum SIMDTernaryOp { enum RefAsOp { RefAsNonNull, - RefAsFunc, - RefAsData, - RefAsI31, ExternInternalize, ExternExternalize, }; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index b1f4b8907..767e0c2ab 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -4032,8 +4032,12 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { } if (opcode == BinaryConsts::RefAsFunc || opcode == BinaryConsts::RefAsData || - opcode == BinaryConsts::RefAsI31 || - opcode == BinaryConsts::ExternInternalize || + opcode == BinaryConsts::RefAsI31) { + visitRefAsCast((curr = allocator.alloc<RefCast>())->cast<RefCast>(), + opcode); + break; + } + if (opcode == BinaryConsts::ExternInternalize || opcode == BinaryConsts::ExternExternalize) { visitRefAs((curr = allocator.alloc<RefAs>())->cast<RefAs>(), opcode); break; @@ -6910,6 +6914,26 @@ bool WasmBinaryBuilder::maybeVisitRefTest(Expression*& out, uint32_t code) { return false; } +void WasmBinaryBuilder::visitRefAsCast(RefCast* curr, uint32_t code) { + // TODO: These instructions are deprecated. Remove them. + switch (code) { + case BinaryConsts::RefAsFunc: + curr->type = Type(HeapType::func, NonNullable); + break; + case BinaryConsts::RefAsData: + curr->type = Type(HeapType::data, NonNullable); + break; + case BinaryConsts::RefAsI31: + curr->type = Type(HeapType::i31, NonNullable); + break; + default: + WASM_UNREACHABLE("unexpected ref.as*"); + } + curr->ref = popNonVoidExpression(); + curr->safety = RefCast::Safe; + curr->finalize(); +} + bool WasmBinaryBuilder::maybeVisitRefCast(Expression*& out, uint32_t code) { if (code == BinaryConsts::RefCastStatic || code == BinaryConsts::RefCast || code == BinaryConsts::RefCastNull || code == BinaryConsts::RefCastNop) { @@ -7439,15 +7463,6 @@ void WasmBinaryBuilder::visitRefAs(RefAs* curr, uint8_t code) { case BinaryConsts::RefAsNonNull: curr->op = RefAsNonNull; break; - case BinaryConsts::RefAsFunc: - curr->op = RefAsFunc; - break; - case BinaryConsts::RefAsData: - curr->op = RefAsData; - break; - case BinaryConsts::RefAsI31: - curr->op = RefAsI31; - break; case BinaryConsts::ExternInternalize: curr->op = ExternInternalize; break; diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 4f725d48b..03dba223b 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2790,27 +2790,27 @@ Expression* SExpressionWasmBuilder::makeRefTest(Element& s, return Builder(wasm).makeRefTest(ref, *castType); } -Expression* SExpressionWasmBuilder::makeRefCast(Element& s) { +Expression* SExpressionWasmBuilder::makeRefCast(Element& s, + std::optional<Type> castType) { int i = 1; - Nullability nullability; bool legacy = false; - if (s[0]->str().str == "ref.cast_static") { - legacy = true; - } else { - nullability = NonNullable; - if (s[i]->str().str == "null") { + if (!castType) { + Nullability nullability = NonNullable; + if (s[0]->str().str == "ref.cast_static") { + legacy = true; + } else if (s[i]->str().str == "null") { nullability = Nullable; ++i; } + auto type = parseHeapType(*s[i++]); + castType = Type(type, nullability); } - auto heapType = parseHeapType(*s[i++]); auto* ref = parseExpression(*s[i++]); if (legacy) { // Legacy polymorphic behavior. - nullability = ref->type.getNullability(); + castType = Type(castType->getHeapType(), ref->type.getNullability()); } - auto type = Type(heapType, nullability); - return Builder(wasm).makeRefCast(ref, type, RefCast::Safe); + return Builder(wasm).makeRefCast(ref, *castType, RefCast::Safe); } Expression* SExpressionWasmBuilder::makeRefCastNop(Element& s) { diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 82c7c511f..c4e1a863c 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2041,6 +2041,23 @@ void BinaryInstWriter::visitRefCast(RefCast* curr) { o << U32LEB(BinaryConsts::RefCastNop); parent.writeIndexedHeapType(curr->type.getHeapType()); } else { + // TODO: These instructions are deprecated. Remove them. + if (auto type = curr->type.getHeapType(); + type.isBasic() && curr->type.isNonNullable()) { + switch (type.getBasic()) { + case HeapType::func: + o << U32LEB(BinaryConsts::RefAsFunc); + return; + case HeapType::data: + o << U32LEB(BinaryConsts::RefAsData); + return; + case HeapType::i31: + o << U32LEB(BinaryConsts::RefAsI31); + return; + default: + break; + } + } if (curr->type.isNullable()) { o << U32LEB(BinaryConsts::RefCastNull); } else { @@ -2244,15 +2261,6 @@ void BinaryInstWriter::visitRefAs(RefAs* curr) { case RefAsNonNull: o << int8_t(BinaryConsts::RefAsNonNull); break; - case RefAsFunc: - o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::RefAsFunc); - break; - case RefAsData: - o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::RefAsData); - break; - case RefAsI31: - o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::RefAsI31); - break; case ExternInternalize: o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::ExternInternalize); diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index c121ba89a..4a72f6ea6 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1115,15 +1115,6 @@ void RefAs::finalize() { case RefAsNonNull: type = Type(value->type.getHeapType(), NonNullable); break; - case RefAsFunc: - type = Type(HeapType::func, NonNullable); - break; - case RefAsData: - type = Type(HeapType::data, NonNullable); - break; - case RefAsI31: - type = Type(HeapType::i31, NonNullable); - break; case ExternInternalize: type = Type(HeapType::any, value->type.getNullability()); break; diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp index 08eec41c6..c2d06ae90 100644 --- a/src/wasm/wat-parser.cpp +++ b/src/wasm/wat-parser.cpp @@ -2346,7 +2346,9 @@ Result<typename Ctx::InstrT> makeI31Get(Ctx&, Index, bool signed_); template<typename Ctx> Result<typename Ctx::InstrT> makeRefTest(Ctx&, Index, std::optional<Type> castType = std::nullopt); -template<typename Ctx> Result<typename Ctx::InstrT> makeRefCast(Ctx&, Index); +template<typename Ctx> +Result<typename Ctx::InstrT> +makeRefCast(Ctx&, Index, std::optional<Type> castType = std::nullopt); template<typename Ctx> Result<typename Ctx::InstrT> makeRefCastNop(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeBrOnNull(Ctx&, Index, bool onFail = false); @@ -3444,7 +3446,8 @@ makeRefTest(Ctx& ctx, Index pos, std::optional<Type> castType) { } template<typename Ctx> -Result<typename Ctx::InstrT> makeRefCast(Ctx& ctx, Index pos) { +Result<typename Ctx::InstrT> +makeRefCast(Ctx& ctx, Index pos, std::optional<Type> castType) { return ctx.in.err("unimplemented instruction"); } diff --git a/test/binaryen.js/expressions.js b/test/binaryen.js/expressions.js index ee55ee90b..c9111c9b4 100644 --- a/test/binaryen.js/expressions.js +++ b/test/binaryen.js/expressions.js @@ -1450,6 +1450,7 @@ console.log("# RefAs"); var op = binaryen.Operations.RefAsNonNull; var value = module.local.get(1, binaryen.anyref); + var externref = module.local.get(3, binaryen.externref); const theRefAs = binaryen.RefAs(module.ref.as_non_null(value)); assert(theRefAs instanceof binaryen.RefAs); assert(theRefAs instanceof binaryen.Expression); @@ -1457,9 +1458,9 @@ console.log("# RefAs"); assert(theRefAs.value === value); assert(theRefAs.type !== binaryen.i32); // TODO: === (ref any) - theRefAs.op = op = binaryen.Operations.RefAsFunc; + theRefAs.op = op = binaryen.Operations.RefAsExternExternalize; assert(theRefAs.op === op); - theRefAs.op = op = binaryen.Operations.RefAsNull; + theRefAs.op = op = binaryen.Operations.RefAsNonNull; theRefAs.value = value = module.local.get(2, binaryen.anyref); assert(theRefAs.value === value); theRefAs.type = binaryen.f64; @@ -1473,21 +1474,7 @@ console.log("# RefAs"); "(ref.as_non_null\n (local.get $2)\n)\n" ); - assert( - binaryen.RefAs(module.ref.as_func(value)).toText() - == - "(ref.as_func\n (local.get $2)\n)\n" - ); - assert( - binaryen.RefAs(module.ref.as_data(value)).toText() - == - "(ref.as_data\n (local.get $2)\n)\n" - ); - assert( - binaryen.RefAs(module.ref.as_i31(value)).toText() - == - "(ref.as_i31\n (local.get $2)\n)\n" - ); + // TODO: extern.externalize and extern.internalize module.dispose(); })(); diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c index cc2c96538..45af52e21 100644 --- a/test/example/c-api-kitchen-sink.c +++ b/test/example/c-api-kitchen-sink.c @@ -1018,15 +1018,6 @@ void test_core() { BinaryenRefAsNonNull(), BinaryenRefNull(module, BinaryenTypeNullref())), BinaryenRefAs(module, - BinaryenRefAsFunc(), - BinaryenRefNull(module, BinaryenTypeNullref())), - BinaryenRefAs(module, - BinaryenRefAsData(), - BinaryenRefNull(module, BinaryenTypeNullref())), - BinaryenRefAs(module, - BinaryenRefAsI31(), - BinaryenRefNull(module, BinaryenTypeNullref())), - BinaryenRefAs(module, BinaryenRefAsExternInternalize(), BinaryenRefNull(module, BinaryenTypeNullExternref())), BinaryenRefAs(module, diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index 51298d1a2..6e4af6cf0 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -2009,21 +2009,6 @@ BinaryenFeatureAll: 126975 ) ) (drop - (ref.as_func - (ref.null none) - ) - ) - (drop - (ref.as_data - (ref.null none) - ) - ) - (drop - (ref.as_i31 - (ref.null none) - ) - ) - (drop (extern.internalize (ref.null noextern) ) diff --git a/test/heap-types.wast b/test/heap-types.wast index 5171d5a66..f64dd8814 100644 --- a/test/heap-types.wast +++ b/test/heap-types.wast @@ -185,9 +185,9 @@ (if (ref.is_data (local.get $x)) (unreachable)) (if (ref.is_i31 (local.get $x)) (unreachable)) ) - (func $ref.as_X (param $x anyref) + (func $ref.as_X (param $x anyref) (param $f funcref) (drop (ref.as_non_null (local.get $x))) - (drop (ref.as_func (local.get $x))) + (drop (ref.as_func (local.get $f))) (drop (ref.as_data (local.get $x))) (drop (ref.as_i31 (local.get $x))) ) diff --git a/test/heap-types.wast.from-wast b/test/heap-types.wast.from-wast index b551a9073..703759720 100644 --- a/test/heap-types.wast.from-wast +++ b/test/heap-types.wast.from-wast @@ -9,13 +9,14 @@ (type $parent (struct )) (type $child (struct_subtype (field i32) $parent)) (type $grandchild (struct_subtype (field i32) (field i64) $child)) - (type $anyref_=>_none (func (param anyref))) (type $ref?|$vector|_=>_none (func (param (ref null $vector)))) (type $nested-child-struct (struct (field (mut (ref $child))))) (type $words (array (mut i32))) (type $nested-child-array (array (mut (ref $child)))) + (type $anyref_=>_none (func (param anyref))) (type $ref|$struct.A|_ref?|$struct.A|_ref?|$grandchild|_ref?|$struct.C|_ref?|$nested-child-struct|_=>_ref|$struct.B| (func (param (ref $struct.A) (ref null $struct.A) (ref null $grandchild) (ref null $struct.C) (ref null $nested-child-struct)) (result (ref $struct.B)))) (type $ref|$vector|_ref?|$nested-child-array|_ref?|$grandchild|_=>_ref|$matrix| (func (param (ref $vector) (ref null $nested-child-array) (ref null $grandchild)) (result (ref $matrix)))) + (type $anyref_funcref_=>_none (func (param anyref funcref))) (type $ref?|$struct.C|_=>_none (func (param (ref null $struct.C)))) (type $ref|$vector|_ref?|$vector|_=>_none (func (param (ref $vector) (ref null $vector)))) (type $none_=>_ref|$vector| (func (result (ref $vector)))) @@ -189,7 +190,7 @@ (unreachable) ) ) - (func $ref.as_X (type $anyref_=>_none) (param $x anyref) + (func $ref.as_X (type $anyref_funcref_=>_none) (param $x anyref) (param $f funcref) (drop (ref.as_non_null (local.get $x) @@ -197,7 +198,7 @@ ) (drop (ref.as_func - (local.get $x) + (local.get $f) ) ) (drop diff --git a/test/heap-types.wast.fromBinary b/test/heap-types.wast.fromBinary index 485611fef..8e42051bc 100644 --- a/test/heap-types.wast.fromBinary +++ b/test/heap-types.wast.fromBinary @@ -9,13 +9,14 @@ (type $parent (struct )) (type $child (struct_subtype (field i32) $parent)) (type $grandchild (struct_subtype (field i32) (field i64) $child)) - (type $anyref_=>_none (func (param anyref))) (type $ref?|$vector|_=>_none (func (param (ref null $vector)))) (type $nested-child-struct (struct (field (mut (ref $child))))) (type $words (array (mut i32))) (type $nested-child-array (array (mut (ref $child)))) + (type $anyref_=>_none (func (param anyref))) (type $ref|$struct.A|_ref?|$struct.A|_ref?|$grandchild|_ref?|$struct.C|_ref?|$nested-child-struct|_=>_ref|$struct.B| (func (param (ref $struct.A) (ref null $struct.A) (ref null $grandchild) (ref null $struct.C) (ref null $nested-child-struct)) (result (ref $struct.B)))) (type $ref|$vector|_ref?|$nested-child-array|_ref?|$grandchild|_=>_ref|$matrix| (func (param (ref $vector) (ref null $nested-child-array) (ref null $grandchild)) (result (ref $matrix)))) + (type $anyref_funcref_=>_none (func (param anyref funcref))) (type $ref?|$struct.C|_=>_none (func (param (ref null $struct.C)))) (type $ref|$vector|_ref?|$vector|_=>_none (func (param (ref $vector) (ref null $vector)))) (type $none_=>_ref|$vector| (func (result (ref $vector)))) @@ -187,7 +188,7 @@ (unreachable) ) ) - (func $ref.as_X (type $anyref_=>_none) (param $x anyref) + (func $ref.as_X (type $anyref_funcref_=>_none) (param $x anyref) (param $f funcref) (drop (ref.as_non_null (local.get $x) @@ -195,7 +196,7 @@ ) (drop (ref.as_func - (local.get $x) + (local.get $f) ) ) (drop diff --git a/test/heap-types.wast.fromBinary.noDebugInfo b/test/heap-types.wast.fromBinary.noDebugInfo index 03047def7..3c7203066 100644 --- a/test/heap-types.wast.fromBinary.noDebugInfo +++ b/test/heap-types.wast.fromBinary.noDebugInfo @@ -9,13 +9,14 @@ (type ${} (struct )) (type ${i32} (struct_subtype (field i32) ${})) (type ${i32_i64} (struct_subtype (field i32) (field i64) ${i32})) - (type $anyref_=>_none (func (param anyref))) (type $ref?|[mut:f64]|_=>_none (func (param (ref null $[mut:f64])))) (type ${mut:ref|{i32}|} (struct (field (mut (ref ${i32}))))) (type $[mut:i32] (array (mut i32))) (type $[mut:ref|{i32}|] (array (mut (ref ${i32})))) + (type $anyref_=>_none (func (param anyref))) (type $ref|{i32_f32_f64}|_ref?|{i32_f32_f64}|_ref?|{i32_i64}|_ref?|{mut:f32}|_ref?|{mut:ref|{i32}|}|_=>_ref|{i8_mut:i16_ref|{i32_f32_f64}|_mut:ref|{i32_f32_f64}|}| (func (param (ref ${i32_f32_f64}) (ref null ${i32_f32_f64}) (ref null ${i32_i64}) (ref null ${mut:f32}) (ref null ${mut:ref|{i32}|})) (result (ref ${i8_mut:i16_ref|{i32_f32_f64}|_mut:ref|{i32_f32_f64}|})))) (type $ref|[mut:f64]|_ref?|[mut:ref|{i32}|]|_ref?|{i32_i64}|_=>_ref|[mut:ref?|[mut:f64]|]| (func (param (ref $[mut:f64]) (ref null $[mut:ref|{i32}|]) (ref null ${i32_i64})) (result (ref $[mut:ref?|[mut:f64]|])))) + (type $anyref_funcref_=>_none (func (param anyref funcref))) (type $ref?|{mut:f32}|_=>_none (func (param (ref null ${mut:f32})))) (type $ref|[mut:f64]|_ref?|[mut:f64]|_=>_none (func (param (ref $[mut:f64]) (ref null $[mut:f64])))) (type $none_=>_ref|[mut:f64]| (func (result (ref $[mut:f64])))) @@ -187,7 +188,7 @@ (unreachable) ) ) - (func $3 (type $anyref_=>_none) (param $0 anyref) + (func $3 (type $anyref_funcref_=>_none) (param $0 anyref) (param $1 funcref) (drop (ref.as_non_null (local.get $0) @@ -195,7 +196,7 @@ ) (drop (ref.as_func - (local.get $0) + (local.get $1) ) ) (drop diff --git a/test/lit/binary/bad-ref-as.test b/test/lit/binary/bad-ref-as.test index 2afa19904..3accdd553 100644 --- a/test/lit/binary/bad-ref-as.test +++ b/test/lit/binary/bad-ref-as.test @@ -1,6 +1,5 @@ ;; Test that we error properly on a file with a ref.as of a non-ref type. -;; RUN: not wasm-opt %s.wasm 2>&1 | filecheck %s - -;; CHECK: parse exception: bad input type for ref.as: i32 +;; RUN: not wasm-opt -all %s.wasm 2>&1 | filecheck %s +;; CHECK: ref.cast ref must have ref type diff --git a/test/lit/passes/gufa-extern.wast b/test/lit/passes/gufa-extern.wast index 2c38a0118..9d71357aa 100644 --- a/test/lit/passes/gufa-extern.wast +++ b/test/lit/passes/gufa-extern.wast @@ -39,10 +39,13 @@ ;; CHECK: (func $non-exported (type $externref_anyref_=>_none) (param $ext externref) (param $any anyref) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.as_data - ;; CHECK-NEXT: (extern.internalize - ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (extern.internalize + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop diff --git a/test/lit/passes/optimize-instructions-gc-tnh.wast b/test/lit/passes/optimize-instructions-gc-tnh.wast index 948e03296..502590cde 100644 --- a/test/lit/passes/optimize-instructions-gc-tnh.wast +++ b/test/lit/passes/optimize-instructions-gc-tnh.wast @@ -86,9 +86,7 @@ ;; TNH: (func $ref.is (type $eqref_=>_i32) (param $a eqref) (result i32) ;; TNH-NEXT: (drop ;; TNH-NEXT: (ref.cast $struct - ;; TNH-NEXT: (ref.as_data - ;; TNH-NEXT: (local.get $a) - ;; TNH-NEXT: ) + ;; TNH-NEXT: (local.get $a) ;; TNH-NEXT: ) ;; TNH-NEXT: ) ;; TNH-NEXT: (i32.const 0) @@ -96,9 +94,7 @@ ;; NO_TNH: (func $ref.is (type $eqref_=>_i32) (param $a eqref) (result i32) ;; NO_TNH-NEXT: (drop ;; NO_TNH-NEXT: (ref.cast $struct - ;; NO_TNH-NEXT: (ref.as_data - ;; NO_TNH-NEXT: (local.get $a) - ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (local.get $a) ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: (i32.const 0) @@ -140,23 +136,23 @@ ) ) - ;; TNH: (func $ref.is_func (type $anyref_=>_i32) (param $a anyref) (result i32) + ;; TNH: (func $ref.is_func (type $funcref_=>_i32) (param $a funcref) (result i32) ;; TNH-NEXT: (drop - ;; TNH-NEXT: (ref.as_func + ;; TNH-NEXT: (ref.as_non_null ;; TNH-NEXT: (local.get $a) ;; TNH-NEXT: ) ;; TNH-NEXT: ) ;; TNH-NEXT: (i32.const 1) ;; TNH-NEXT: ) - ;; NO_TNH: (func $ref.is_func (type $anyref_=>_i32) (param $a anyref) (result i32) + ;; NO_TNH: (func $ref.is_func (type $funcref_=>_i32) (param $a funcref) (result i32) ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (ref.as_func + ;; NO_TNH-NEXT: (ref.as_non_null ;; NO_TNH-NEXT: (local.get $a) ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: (i32.const 1) ;; NO_TNH-NEXT: ) - (func $ref.is_func (param $a (ref null any)) (result i32) + (func $ref.is_func (param $a funcref) (result i32) ;; The check must succeed. We can return 1 here, and drop the rest, with or ;; without TNH (in particular, TNH should not just remove the cast but not ;; return a 1). diff --git a/test/lit/passes/optimize-instructions-gc.wast b/test/lit/passes/optimize-instructions-gc.wast index e6ee4784a..631697e27 100644 --- a/test/lit/passes/optimize-instructions-gc.wast +++ b/test/lit/passes/optimize-instructions-gc.wast @@ -366,95 +366,6 @@ ) ) - ;; similar to $unneeded_as, but the values are of mixed kind (as_func of - ;; data, etc.), so we know we will trap - ;; CHECK: (func $unneeded_as_bad_kinds (type $funcref_dataref_i31ref_=>_none) (param $func funcref) (param $struct dataref) (param $i31 i31ref) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result (ref func)) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $struct) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result (ref i31)) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $func) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result (ref func)) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $struct) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result (ref i31)) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $func) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; NOMNL: (func $unneeded_as_bad_kinds (type $funcref_dataref_i31ref_=>_none) (param $func funcref) (param $struct dataref) (param $i31 i31ref) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (block (result (ref func)) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (local.get $struct) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (unreachable) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (block (result (ref i31)) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (local.get $func) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (unreachable) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (block (result (ref func)) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (local.get $struct) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (unreachable) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (block (result (ref i31)) - ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (local.get $func) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: (unreachable) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: ) - ;; NOMNL-NEXT: ) - (func $unneeded_as_bad_kinds - (param $func (ref null func)) - (param $struct (ref null struct)) - (param $i31 (ref null i31)) - (drop - (ref.as_func (local.get $struct)) - ) - (drop - (ref.as_i31 (local.get $func)) - ) - ;; also check non-nullable types as inputs - (drop - (ref.as_func (ref.as_non_null (local.get $struct))) - ) - (drop - (ref.as_i31 (ref.as_non_null (local.get $func))) - ) - ) - ;; CHECK: (func $unneeded_unreachability (type $void) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.is_func @@ -462,7 +373,10 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.as_func + ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -474,7 +388,10 @@ ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: (drop - ;; NOMNL-NEXT: (ref.as_func + ;; NOMNL-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (unreachable) + ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: (unreachable) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) @@ -3129,12 +3046,18 @@ ) ;; CHECK: (func $as_of_unreachable (type $none_=>_ref|data|) (result (ref data)) - ;; CHECK-NEXT: (ref.as_data + ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; NOMNL: (func $as_of_unreachable (type $none_=>_ref|data|) (result (ref data)) - ;; NOMNL-NEXT: (ref.as_data + ;; NOMNL-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (unreachable) + ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: (unreachable) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) diff --git a/test/lit/passes/vacuum-gc.wast b/test/lit/passes/vacuum-gc.wast index c7ff5a37f..c131bef20 100644 --- a/test/lit/passes/vacuum-gc.wast +++ b/test/lit/passes/vacuum-gc.wast @@ -12,11 +12,6 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (ref.as_func - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.as_data ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) @@ -36,11 +31,6 @@ ) ) (drop - (ref.as_func - (local.get $x) - ) - ) - (drop (ref.as_data (local.get $x) ) diff --git a/test/lit/passes/vacuum-tnh.wast b/test/lit/passes/vacuum-tnh.wast index c1e8c2059..40aac16c1 100644 --- a/test/lit/passes/vacuum-tnh.wast +++ b/test/lit/passes/vacuum-tnh.wast @@ -27,11 +27,6 @@ ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: ) ;; NO_TNH-NEXT: (drop - ;; NO_TNH-NEXT: (ref.as_func - ;; NO_TNH-NEXT: (local.get $y) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: ) - ;; NO_TNH-NEXT: (drop ;; NO_TNH-NEXT: (ref.as_data ;; NO_TNH-NEXT: (local.get $y) ;; NO_TNH-NEXT: ) @@ -61,11 +56,6 @@ ;; Other ref.as* as well. (drop - (ref.as_func - (local.get $y) - ) - ) - (drop (ref.as_data (local.get $y) ) diff --git a/test/passes/Oz_fuzz-exec_all-features.txt b/test/passes/Oz_fuzz-exec_all-features.txt index 30949def4..2ff239257 100644 --- a/test/passes/Oz_fuzz-exec_all-features.txt +++ b/test/passes/Oz_fuzz-exec_all-features.txt @@ -22,8 +22,6 @@ [fuzz-exec] calling br-on_non_null-2 [LoggingExternalInterface logging 1] [trap unreachable] -[fuzz-exec] calling ref-as-func-of-data -[trap not a func] [fuzz-exec] calling ref-as-func-of-func [fuzz-exec] calling cast-on-func [LoggingExternalInterface logging 0] @@ -73,17 +71,16 @@ (export "cast-null-anyref-to-gc" (func $5)) (export "br-on_non_null" (func $7)) (export "br-on_non_null-2" (func $8)) - (export "ref-as-func-of-data" (func $9)) (export "ref-as-func-of-func" (func $7)) - (export "cast-on-func" (func $12)) + (export "cast-on-func" (func $11)) (export "array-alloc-failure" (func $7)) - (export "init-array-packed" (func $14)) - (export "array-copy" (func $16)) - (export "array.init_static" (func $17)) - (export "array.init_static-packed" (func $18)) - (export "static-casts" (func $19)) + (export "init-array-packed" (func $13)) + (export "array-copy" (func $15)) + (export "array.init_static" (func $16)) + (export "array.init_static-packed" (func $17)) + (export "static-casts" (func $18)) (export "static-br_on_cast" (func $2)) - (export "static-br_on_cast_fail" (func $21)) + (export "static-br_on_cast_fail" (func $20)) (func $0 (type $void_func) (; has Stack IR ;) (local $0 i32) (call $log @@ -196,12 +193,7 @@ ) ) ) - (func $9 (type $void_func) (; has Stack IR ;) - (drop - (unreachable) - ) - ) - (func $12 (type $void_func) (; has Stack IR ;) + (func $11 (type $void_func) (; has Stack IR ;) (call $log (i32.const 0) ) @@ -213,7 +205,7 @@ ) (unreachable) ) - (func $14 (type $int_func) (; has Stack IR ;) (result i32) + (func $13 (type $int_func) (; has Stack IR ;) (result i32) (array.get_u $bytes (array.new $bytes (i32.const -43) @@ -222,7 +214,7 @@ (i32.const 10) ) ) - (func $16 (type $void_func) (; has Stack IR ;) + (func $15 (type $void_func) (; has Stack IR ;) (local $0 (ref $bytes)) (local $1 (ref $bytes)) (array.set $bytes @@ -277,7 +269,7 @@ ) ) ) - (func $17 (type $void_func) (; has Stack IR ;) + (func $16 (type $void_func) (; has Stack IR ;) (local $0 (ref $bytes)) (call $log (array.len @@ -302,7 +294,7 @@ ) ) ) - (func $18 (type $void_func) (; has Stack IR ;) + (func $17 (type $void_func) (; has Stack IR ;) (call $log (array.get_u $bytes (array.init_static $bytes @@ -312,7 +304,7 @@ ) ) ) - (func $19 (type $void_func) (; has Stack IR ;) + (func $18 (type $void_func) (; has Stack IR ;) (call $log (i32.const 1) ) @@ -332,7 +324,7 @@ (i32.const 1) ) ) - (func $21 (type $void_func) (; has Stack IR ;) + (func $20 (type $void_func) (; has Stack IR ;) (call $log (i32.const -2) ) @@ -362,8 +354,6 @@ [fuzz-exec] calling br-on_non_null-2 [LoggingExternalInterface logging 1] [trap unreachable] -[fuzz-exec] calling ref-as-func-of-data -[trap unreachable] [fuzz-exec] calling ref-as-func-of-func [fuzz-exec] calling cast-on-func [LoggingExternalInterface logging 0] diff --git a/test/passes/Oz_fuzz-exec_all-features.wast b/test/passes/Oz_fuzz-exec_all-features.wast index 0ce79aa4b..9828d7022 100644 --- a/test/passes/Oz_fuzz-exec_all-features.wast +++ b/test/passes/Oz_fuzz-exec_all-features.wast @@ -177,14 +177,6 @@ ) ) ) - (func "ref-as-func-of-data" - (drop - ;; This should trap. - (ref.as_func - (struct.new_default $struct) - ) - ) - ) (func "ref-as-func-of-func" (drop (ref.as_func |