diff options
-rw-r--r-- | src/binaryen-c.cpp | 15 | ||||
-rw-r--r-- | src/binaryen-c.h | 15 | ||||
-rw-r--r-- | src/ir/module-utils.cpp | 2 | ||||
-rw-r--r-- | src/passes/GUFA.cpp | 6 | ||||
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 19 | ||||
-rw-r--r-- | src/passes/Print.cpp | 5 | ||||
-rw-r--r-- | src/wasm-binary.h | 4 | ||||
-rw-r--r-- | src/wasm-builder.h | 4 | ||||
-rw-r--r-- | src/wasm-delegations-fields.def | 2 | ||||
-rw-r--r-- | src/wasm.h | 5 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 9 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 12 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 8 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 2 | ||||
-rw-r--r-- | test/example/c-api-kitchen-sink.c | 5 | ||||
-rw-r--r-- | test/example/c-api-kitchen-sink.txt | 2 | ||||
-rw-r--r-- | test/lit/binary/legacy-static-casts.test.wasm | bin | 66 -> 66 bytes | |||
-rw-r--r-- | test/lit/passes/optimize-instructions-gc.wast | 149 | ||||
-rw-r--r-- | test/spec/ref_test.wast | 330 |
19 files changed, 540 insertions, 54 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 3a42b6f25..b26ec3389 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -1742,10 +1742,9 @@ BinaryenExpressionRef BinaryenCallRef(BinaryenModuleRef module, } BinaryenExpressionRef BinaryenRefTest(BinaryenModuleRef module, BinaryenExpressionRef ref, - BinaryenHeapType intendedType) { + BinaryenType castType) { return static_cast<Expression*>( - Builder(*(Module*)module) - .makeRefTest((Expression*)ref, HeapType(intendedType))); + Builder(*(Module*)module).makeRefTest((Expression*)ref, Type(castType))); } BinaryenExpressionRef BinaryenRefCast(BinaryenModuleRef module, BinaryenExpressionRef ref, @@ -4062,16 +4061,16 @@ void BinaryenRefTestSetRef(BinaryenExpressionRef expr, assert(refExpr); static_cast<RefTest*>(expression)->ref = (Expression*)refExpr; } -BinaryenHeapType BinaryenRefTestGetIntendedType(BinaryenExpressionRef expr) { +BinaryenType BinaryenRefTestGetCastType(BinaryenExpressionRef expr) { auto* expression = (Expression*)expr; assert(expression->is<RefTest>()); - return static_cast<RefTest*>(expression)->intendedType.getID(); + return static_cast<RefTest*>(expression)->castType.getID(); } -void BinaryenRefTestSetIntendedType(BinaryenExpressionRef expr, - BinaryenHeapType intendedType) { +void BinaryenRefTestSetCastType(BinaryenExpressionRef expr, + BinaryenType castType) { auto* expression = (Expression*)expr; assert(expression->is<RefTest>()); - static_cast<RefTest*>(expression)->intendedType = HeapType(intendedType); + static_cast<RefTest*>(expression)->castType = Type(castType); } // RefCast BinaryenExpressionRef BinaryenRefCastGetRef(BinaryenExpressionRef expr) { diff --git a/src/binaryen-c.h b/src/binaryen-c.h index 9fc0d8b04..7894c1c24 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -1038,10 +1038,9 @@ BinaryenCallRef(BinaryenModuleRef module, BinaryenIndex numOperands, BinaryenType type, bool isReturn); -BINARYEN_API BinaryenExpressionRef -BinaryenRefTest(BinaryenModuleRef module, - BinaryenExpressionRef ref, - BinaryenHeapType intendedType); +BINARYEN_API BinaryenExpressionRef BinaryenRefTest(BinaryenModuleRef module, + BinaryenExpressionRef ref, + BinaryenType castType); BINARYEN_API BinaryenExpressionRef BinaryenRefCast(BinaryenModuleRef module, BinaryenExpressionRef ref, BinaryenType type); @@ -2362,10 +2361,10 @@ BINARYEN_API BinaryenExpressionRef BinaryenRefTestGetRef(BinaryenExpressionRef expr); BINARYEN_API void BinaryenRefTestSetRef(BinaryenExpressionRef expr, BinaryenExpressionRef refExpr); -BINARYEN_API BinaryenHeapType -BinaryenRefTestGetIntendedType(BinaryenExpressionRef expr); -BINARYEN_API void BinaryenRefTestSetIntendedType(BinaryenExpressionRef expr, - BinaryenHeapType intendedType); +BINARYEN_API BinaryenType +BinaryenRefTestGetCastType(BinaryenExpressionRef expr); +BINARYEN_API void BinaryenRefTestSetCastType(BinaryenExpressionRef expr, + BinaryenType intendedType); // RefCast diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp index f7ba7d776..482753b87 100644 --- a/src/ir/module-utils.cpp +++ b/src/ir/module-utils.cpp @@ -73,7 +73,7 @@ struct CodeScanner } else if (auto* cast = curr->dynCast<RefCast>()) { counts.note(cast->type); } else if (auto* cast = curr->dynCast<RefTest>()) { - counts.note(cast->intendedType); + counts.note(cast->castType); } else if (auto* cast = curr->dynCast<BrOn>()) { if (cast->op == BrOnCast || cast->op == BrOnCastFail) { counts.note(cast->intendedType); diff --git a/src/passes/GUFA.cpp b/src/passes/GUFA.cpp index 4250c76e9..137ffda17 100644 --- a/src/passes/GUFA.cpp +++ b/src/passes/GUFA.cpp @@ -237,10 +237,8 @@ struct GUFAOptimizer if (refType.isRef()) { // We have some knowledge of the type here. Use that to optimize: RefTest // returns 1 if the input is of a subtype of the intended type, that is, - // we are looking for a type in that cone of types. (Note that we use a - // non-nullable cone since only a non-null can pass the test.) - auto intendedContents = - PossibleContents::fullConeType(Type(curr->intendedType, NonNullable)); + // we are looking for a type in that cone of types. + auto intendedContents = PossibleContents::fullConeType(curr->castType); auto optimize = [&](int32_t result) { auto* last = Builder(*getModule()).makeConst(Literal(int32_t(result))); diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index ec714c1e1..097e0299a 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2037,20 +2037,29 @@ struct OptimizeInstructions Builder builder(*getModule()); + if (curr->ref->type.isNull()) { + // The input is null, so we know whether this will succeed or fail. + int32_t result = curr->castType.isNullable() ? 1 : 0; + replaceCurrent(builder.makeBlock( + {builder.makeDrop(curr->ref), builder.makeConst(int32_t(result))})); + return; + } + auto refType = curr->ref->type.getHeapType(); - auto intendedType = curr->intendedType; + auto intendedType = curr->castType.getHeapType(); // See above in RefCast. - if (!canBeCastTo(refType, intendedType)) { + if (!canBeCastTo(refType, intendedType) && + (curr->castType.isNonNullable() || curr->ref->type.isNonNullable())) { // This test cannot succeed, and will definitely return 0. replaceCurrent(builder.makeSequence(builder.makeDrop(curr->ref), builder.makeConst(int32_t(0)))); return; } - if (curr->ref->type.isNonNullable() && - HeapType::isSubType(refType, intendedType)) { - // This static test will definitely succeed. + if (HeapType::isSubType(refType, intendedType) && + (curr->castType.isNullable() || curr->ref->type.isNonNullable())) { + // This test will definitely succeed and return 1. replaceCurrent(builder.makeBlock( {builder.makeDrop(curr->ref), builder.makeConst(int32_t(1))})); return; diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 2cd021b6f..d2fbf24d8 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2110,7 +2110,10 @@ struct PrintExpressionContents } void visitRefTest(RefTest* curr) { printMedium(o, "ref.test "); - printHeapType(o, curr->intendedType, wasm); + if (curr->castType.isNullable()) { + printMedium(o, "null "); + } + printHeapType(o, curr->castType.getHeapType(), wasm); } void visitRefCast(RefCast* curr) { if (printUnreachableReplacement(curr)) { diff --git a/src/wasm-binary.h b/src/wasm-binary.h index e4abe4787..996d9ab4c 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1125,11 +1125,11 @@ enum ASTNodes { RefCastStatic = 0x45, BrOnCastStatic = 0x46, BrOnCastStaticFail = 0x47, - RefCastNop = 0x48, - // TODO: RefTestNull + RefTestNull = 0x48, RefCastNull = 0x49, // TODO: BrOnCastNull // TODO: BrOnCastFailNull + RefCastNop = 0x4c, RefIsFunc = 0x50, RefIsData = 0x51, RefIsI31 = 0x52, diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 173e5976f..58a3a3188 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -868,10 +868,10 @@ public: ret->finalize(); return ret; } - RefTest* makeRefTest(Expression* ref, HeapType intendedType) { + RefTest* makeRefTest(Expression* ref, Type castType) { auto* ret = wasm.allocator.alloc<RefTest>(); ret->ref = ref; - ret->intendedType = intendedType; + ret->castType = castType; ret->finalize(); return ret; } diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index dc0a13f41..9c966faec 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -614,7 +614,7 @@ switch (DELEGATE_ID) { } case Expression::Id::RefTestId: { DELEGATE_START(RefTest); - DELEGATE_FIELD_HEAPTYPE(RefTest, intendedType); + DELEGATE_FIELD_TYPE(RefTest, castType); DELEGATE_FIELD_CHILD(RefTest, ref); DELEGATE_END(RefTest); break; diff --git a/src/wasm.h b/src/wasm.h index 3a44556d5..f1dcd7b46 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1522,12 +1522,11 @@ public: Expression* ref; - HeapType intendedType; + Type castType; void finalize(); - // TODO: Support ref.test null as well. - Type getCastType() { return Type(intendedType, NonNullable); } + Type getCastType() { return castType; } }; class RefCast : public SpecificExpression<Expression::RefCastId> { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 0160ffaf6..386f89060 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -6893,11 +6893,14 @@ bool WasmBinaryBuilder::maybeVisitI31Get(Expression*& out, uint32_t code) { } bool WasmBinaryBuilder::maybeVisitRefTest(Expression*& out, uint32_t code) { - if (code == BinaryConsts::RefTestStatic || code == BinaryConsts::RefTest) { + if (code == BinaryConsts::RefTestStatic || code == BinaryConsts::RefTest || + code == BinaryConsts::RefTestNull) { bool legacy = code == BinaryConsts::RefTestStatic; - auto intendedType = legacy ? getIndexedHeapType() : getHeapType(); + auto castType = legacy ? getIndexedHeapType() : getHeapType(); + auto nullability = + (code == BinaryConsts::RefTestNull) ? Nullable : NonNullable; auto* ref = popNonVoidExpression(); - out = Builder(wasm).makeRefTest(ref, intendedType); + out = Builder(wasm).makeRefTest(ref, Type(castType, nullability)); return true; } return false; diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index cf287d8a1..4fdc29a66 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2776,9 +2776,15 @@ Expression* SExpressionWasmBuilder::makeI31Get(Element& s, bool signed_) { } Expression* SExpressionWasmBuilder::makeRefTest(Element& s) { - auto heapType = parseHeapType(*s[1]); - auto* ref = parseExpression(*s[2]); - return Builder(wasm).makeRefTest(ref, heapType); + int i = 1; + auto nullability = NonNullable; + if (s[0]->str().str != "ref.test_static" && s[1]->str().str == "null") { + nullability = Nullable; + ++i; + } + auto heapType = parseHeapType(*s[i++]); + auto* ref = parseExpression(*s[i++]); + return Builder(wasm).makeRefTest(ref, Type(heapType, nullability)); } Expression* SExpressionWasmBuilder::makeRefCast(Element& s) { diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 911447b48..e24060f2a 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2025,8 +2025,12 @@ void BinaryInstWriter::visitCallRef(CallRef* curr) { void BinaryInstWriter::visitRefTest(RefTest* curr) { o << int8_t(BinaryConsts::GCPrefix); - o << U32LEB(BinaryConsts::RefTest); - parent.writeHeapType(curr->intendedType); + if (curr->castType.isNullable()) { + o << U32LEB(BinaryConsts::RefTestNull); + } else { + o << U32LEB(BinaryConsts::RefTest); + } + parent.writeHeapType(curr->castType.getHeapType()); } void BinaryInstWriter::visitRefCast(RefCast* curr) { diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index b102ecb1c..68fb58ec4 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2513,7 +2513,7 @@ void FunctionValidator::visitRefTest(RefTest* curr) { return; } shouldBeEqual( - curr->intendedType.getBottom(), + curr->castType.getHeapType().getBottom(), curr->ref->type.getHeapType().getBottom(), curr, "ref.test target type and ref type must have a common supertype"); diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c index 0dadeef1d..de105d239 100644 --- a/test/example/c-api-kitchen-sink.c +++ b/test/example/c-api-kitchen-sink.c @@ -1102,9 +1102,8 @@ void test_core() { BinaryenI31New(module, makeInt32(module, 0)), BinaryenI31Get(module, i31refExpr, 1), BinaryenI31Get(module, BinaryenI31New(module, makeInt32(module, 2)), 0), - BinaryenRefTest(module, - BinaryenGlobalGet(module, "i8Array-global", i8Array), - BinaryenTypeGetHeapType(i8Array)), + BinaryenRefTest( + module, BinaryenGlobalGet(module, "i8Array-global", i8Array), i8Array), BinaryenRefCast( module, BinaryenGlobalGet(module, "i8Array-global", i8Array), i8Array), BinaryenStructNew(module, 0, 0, BinaryenTypeGetHeapType(i32Struct)), diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index 41f96c184..1ee529eb3 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -2165,7 +2165,7 @@ BinaryenFeatureAll: 126975 ) ) (drop - (ref.test $[mut:i8] + (ref.test null $[mut:i8] (global.get $i8Array-global) ) ) diff --git a/test/lit/binary/legacy-static-casts.test.wasm b/test/lit/binary/legacy-static-casts.test.wasm Binary files differindex 2dcfc9f01..aef179b2a 100644 --- a/test/lit/binary/legacy-static-casts.test.wasm +++ b/test/lit/binary/legacy-static-casts.test.wasm diff --git a/test/lit/passes/optimize-instructions-gc.wast b/test/lit/passes/optimize-instructions-gc.wast index bd697419b..1bf72ae04 100644 --- a/test/lit/passes/optimize-instructions-gc.wast +++ b/test/lit/passes/optimize-instructions-gc.wast @@ -14,18 +14,18 @@ (field $i64 (mut i64)) )) + ;; CHECK: (type $array (array (mut i8))) + ;; CHECK: (type $A (struct (field i32))) + ;; NOMNL: (type $array (array (mut i8))) + ;; NOMNL: (type $A (struct (field i32))) (type $A (struct (field i32))) - ;; CHECK: (type $B (struct_subtype (field i32) (field i32) (field f32) $A)) - - ;; CHECK: (type $array (array (mut i8))) - ;; NOMNL: (type $B (struct_subtype (field i32) (field i32) (field f32) $A)) - - ;; NOMNL: (type $array (array (mut i8))) (type $array (array (mut i8))) + ;; CHECK: (type $B (struct_subtype (field i32) (field i32) (field f32) $A)) + ;; NOMNL: (type $B (struct_subtype (field i32) (field i32) (field f32) $A)) (type $B (struct_subtype (field i32) (field i32) (field f32) $A)) ;; CHECK: (type $B-child (struct_subtype (field i32) (field i32) (field f32) (field i64) $B)) @@ -1882,6 +1882,21 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.test null $array + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; NOMNL: (func $incompatible-test (type $ref?|$struct|_=>_none) (param $struct (ref null $struct)) ;; NOMNL-NEXT: (drop @@ -1892,6 +1907,21 @@ ;; NOMNL-NEXT: (i32.const 0) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.test null $array + ;; NOMNL-NEXT: (local.get $struct) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (block (result i32) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.as_non_null + ;; NOMNL-NEXT: (local.get $struct) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (i32.const 0) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) (func $incompatible-test (param $struct (ref null $struct)) (drop @@ -1900,6 +1930,20 @@ (local.get $struct) ) ) + (drop + ;; But this one might succeed due to a null, so don't optimize it. + (ref.test null $array + (local.get $struct) + ) + ) + (drop + ;; This one cannot succeed due to a null, so optimize it. + (ref.test null $array + (ref.as_non_null + (local.get $struct) + ) + ) + ) ) ;; CHECK: (func $subtype-compatible (type $ref?|$A|_ref?|$B|_=>_none) (param $A (ref null $A)) (param $B (ref null $B)) @@ -1913,6 +1957,34 @@ ;; CHECK-NEXT: (local.get $B) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $B) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; NOMNL: (func $subtype-compatible (type $ref?|$A|_ref?|$B|_=>_none) (param $A (ref null $A)) (param $B (ref null $B)) ;; NOMNL-NEXT: (drop @@ -1925,6 +1997,34 @@ ;; NOMNL-NEXT: (local.get $B) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (block (result i32) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (local.get $B) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (i32.const 1) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (block (result i32) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.as_non_null + ;; NOMNL-NEXT: (local.get $B) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (i32.const 1) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (block (result i32) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.as_non_null + ;; NOMNL-NEXT: (local.get $B) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (i32.const 1) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) (func $subtype-compatible (param $A (ref null $A)) (param $B (ref null $B)) (drop @@ -1939,6 +2039,28 @@ (local.get $B) ) ) + (drop + ;; If the test is nullable, this will succeed. + (ref.test null $A + (local.get $B) + ) + ) + (drop + ;; We will also succeed if the input is non-nullable. + (ref.test $A + (ref.as_non_null + (local.get $B) + ) + ) + ) + (drop + ;; Or if the test is nullable and the input is non-nullable. + (ref.test null $A + (ref.as_non_null + (local.get $B) + ) + ) + ) ) ;; CHECK: (func $ref.test-unreachable (type $ref?|$A|_=>_none) (param $A (ref null $A)) ;; CHECK-NEXT: (drop @@ -1946,6 +2068,11 @@ ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.test null $A + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; NOMNL: (func $ref.test-unreachable (type $ref?|$A|_=>_none) (param $A (ref null $A)) ;; NOMNL-NEXT: (drop @@ -1953,6 +2080,11 @@ ;; NOMNL-NEXT: (unreachable) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.test null $A + ;; NOMNL-NEXT: (unreachable) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) (func $ref.test-unreachable (param $A (ref null $A)) (drop @@ -1962,6 +2094,11 @@ (unreachable) ) ) + (drop + (ref.test null $A + (unreachable) + ) + ) ) ;; CHECK: (func $consecutive-opts-with-unreachable (type $funcref_=>_none) (param $func funcref) diff --git a/test/spec/ref_test.wast b/test/spec/ref_test.wast new file mode 100644 index 000000000..bb6479548 --- /dev/null +++ b/test/spec/ref_test.wast @@ -0,0 +1,330 @@ +;; Abstract Types + +(module + (type $ft (func)) + (type $st (struct)) + (type $at (array i8)) + + (table $ta 10 anyref) + (table $tf 10 funcref) + (table $te 10 externref) + + (elem declare func $f) + (func $f) + + (func (export "init") + (table.set $ta (i32.const 0) (ref.null any)) + (table.set $ta (i32.const 1) (ref.null struct)) + (table.set $ta (i32.const 2) (ref.null none)) + (table.set $ta (i32.const 3) (i31.new (i32.const 7))) + (table.set $ta (i32.const 4) (struct.new_default $st)) + (table.set $ta (i32.const 5) (array.new_default $at (i32.const 0))) + ;; (table.set $ta (i32.const 6) (extern.internalize (extern.externalize (i31.new (i32.const 0))))) + ;; (table.set $ta (i32.const 7) (extern.internalize (ref.null extern))) + + (table.set $tf (i32.const 0) (ref.null nofunc)) + (table.set $tf (i32.const 1) (ref.null func)) + (table.set $tf (i32.const 2) (ref.func $f)) + + (table.set $te (i32.const 0) (ref.null noextern)) + (table.set $te (i32.const 1) (ref.null extern)) + ;; (table.set $te (i32.const 2) (extern.externalize (i31.new (i32.const 0)))) + ;; (table.set $te (i32.const 3) (extern.externalize (i31.new (i32.const 8)))) + ;; (table.set $te (i32.const 4) (extern.externalize (struct.new_default $st))) + ;; (table.set $te (i32.const 5) (extern.externalize (ref.null any))) + ) + + (func (export "ref_test_null_data") (param $i i32) (result i32) + (i32.add + (ref.is_null (table.get $ta (local.get $i))) + (ref.test null none (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_any") (param $i i32) (result i32) + (i32.add + (ref.test any (table.get $ta (local.get $i))) + (ref.test null any (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_eq") (param $i i32) (result i32) + (i32.add + (ref.test eq (table.get $ta (local.get $i))) + (ref.test null eq (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_i31") (param $i i32) (result i32) + (i32.add + (ref.test i31 (table.get $ta (local.get $i))) + (ref.test null i31 (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_struct") (param $i i32) (result i32) + (i32.add + (ref.test struct (table.get $ta (local.get $i))) + (ref.test null struct (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_array") (param $i i32) (result i32) + (i32.add + (ref.test array (table.get $ta (local.get $i))) + (ref.test null array (table.get $ta (local.get $i))) + ) + ) + + (func (export "ref_test_null_func") (param $i i32) (result i32) + (i32.add + (ref.is_null (table.get $tf (local.get $i))) + (ref.test null nofunc (table.get $tf (local.get $i))) + ) + ) + (func (export "ref_test_func") (param $i i32) (result i32) + (i32.add + (ref.test func (table.get $tf (local.get $i))) + (ref.test null func (table.get $tf (local.get $i))) + ) + ) + + (func (export "ref_test_null_extern") (param $i i32) (result i32) + (i32.add + (ref.is_null (table.get $te (local.get $i))) + (ref.test null noextern (table.get $te (local.get $i))) + ) + ) + (func (export "ref_test_extern") (param $i i32) (result i32) + (i32.add + (ref.test extern (table.get $te (local.get $i))) + (ref.test null extern (table.get $te (local.get $i))) + ) + ) +) + +(invoke "init") + +(assert_return (invoke "ref_test_null_data" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "ref_test_null_data" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "ref_test_null_data" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "ref_test_null_data" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "ref_test_null_data" (i32.const 4)) (i32.const 0)) +(assert_return (invoke "ref_test_null_data" (i32.const 5)) (i32.const 0)) +;; (assert_return (invoke "ref_test_null_data" (i32.const 6)) (i32.const 0)) +;; (assert_return (invoke "ref_test_null_data" (i32.const 7)) (i32.const 2)) + +(assert_return (invoke "ref_test_any" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_any" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_test_any" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "ref_test_any" (i32.const 3)) (i32.const 2)) +(assert_return (invoke "ref_test_any" (i32.const 4)) (i32.const 2)) +(assert_return (invoke "ref_test_any" (i32.const 5)) (i32.const 2)) +;; (assert_return (invoke "ref_test_any" (i32.const 6)) (i32.const 2)) +;; (assert_return (invoke "ref_test_any" (i32.const 7)) (i32.const 1)) + +(assert_return (invoke "ref_test_eq" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_eq" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_test_eq" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "ref_test_eq" (i32.const 3)) (i32.const 2)) +(assert_return (invoke "ref_test_eq" (i32.const 4)) (i32.const 2)) +(assert_return (invoke "ref_test_eq" (i32.const 5)) (i32.const 2)) +;; (assert_return (invoke "ref_test_eq" (i32.const 6)) (i32.const 0)) +;; (assert_return (invoke "ref_test_eq" (i32.const 7)) (i32.const 1)) + +(assert_return (invoke "ref_test_i31" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_i31" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_test_i31" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "ref_test_i31" (i32.const 3)) (i32.const 2)) +(assert_return (invoke "ref_test_i31" (i32.const 4)) (i32.const 0)) +(assert_return (invoke "ref_test_i31" (i32.const 5)) (i32.const 0)) +;; (assert_return (invoke "ref_test_i31" (i32.const 6)) (i32.const 0)) +;; (assert_return (invoke "ref_test_i31" (i32.const 7)) (i32.const 1)) + +(assert_return (invoke "ref_test_struct" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_struct" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_test_struct" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "ref_test_struct" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "ref_test_struct" (i32.const 4)) (i32.const 2)) +(assert_return (invoke "ref_test_struct" (i32.const 5)) (i32.const 2)) ;; TOOD: expect 0 once struct is not an alias for data +;; (assert_return (invoke "ref_test_struct" (i32.const 6)) (i32.const 0)) +;; (assert_return (invoke "ref_test_struct" (i32.const 7)) (i32.const 1)) + +(assert_return (invoke "ref_test_array" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_array" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_test_array" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "ref_test_array" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "ref_test_array" (i32.const 4)) (i32.const 0)) +(assert_return (invoke "ref_test_array" (i32.const 5)) (i32.const 2)) +;; (assert_return (invoke "ref_test_array" (i32.const 6)) (i32.const 0)) +;; (assert_return (invoke "ref_test_array" (i32.const 7)) (i32.const 1)) + +(assert_return (invoke "ref_test_null_func" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "ref_test_null_func" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "ref_test_null_func" (i32.const 2)) (i32.const 0)) + +(assert_return (invoke "ref_test_func" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_func" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_test_func" (i32.const 2)) (i32.const 2)) + +(assert_return (invoke "ref_test_null_extern" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "ref_test_null_extern" (i32.const 1)) (i32.const 2)) +;; (assert_return (invoke "ref_test_null_extern" (i32.const 2)) (i32.const 0)) +;; (assert_return (invoke "ref_test_null_extern" (i32.const 3)) (i32.const 0)) +;; (assert_return (invoke "ref_test_null_extern" (i32.const 4)) (i32.const 0)) +;; (assert_return (invoke "ref_test_null_extern" (i32.const 5)) (i32.const 2)) + +(assert_return (invoke "ref_test_extern" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_test_extern" (i32.const 1)) (i32.const 1)) +;; (assert_return (invoke "ref_test_extern" (i32.const 2)) (i32.const 2)) +;; (assert_return (invoke "ref_test_extern" (i32.const 3)) (i32.const 2)) +;; (assert_return (invoke "ref_test_extern" (i32.const 4)) (i32.const 2)) +;; (assert_return (invoke "ref_test_extern" (i32.const 5)) (i32.const 1)) + + +;; Concrete Types + +(module + (type $t0 (struct_subtype data)) + (type $t1 (struct_subtype i32 $t0)) + (type $t1' (struct_subtype i32 $t0)) + (type $t2 (struct_subtype i32 i32 $t1)) + (type $t2' (struct_subtype i32 i32 $t1')) + (type $t3 (struct_subtype i32 i32 $t0)) + (type $t0' (struct_subtype $t0)) + (type $t4 (struct_subtype i32 i32 $t0')) + + (table $tab 20 (ref null struct)) + + (func $init + (table.set $tab (i32.const 0) (struct.new_default $t0)) + (table.set $tab (i32.const 10) (struct.new_default $t0)) + (table.set $tab (i32.const 1) (struct.new_default $t1)) + (table.set $tab (i32.const 11) (struct.new_default $t1')) + (table.set $tab (i32.const 2) (struct.new_default $t2)) + (table.set $tab (i32.const 12) (struct.new_default $t2')) + (table.set $tab (i32.const 3) (struct.new_default $t3)) + (table.set $tab (i32.const 4) (struct.new_default $t4)) + ) + + (func (export "test-sub") + (call $init) + (block $l + ;; must hold + (br_if $l (i32.eqz (ref.test null $t0 (ref.null struct)))) + (br_if $l (i32.eqz (ref.test null $t0 (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test null $t0 (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test null $t0 (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test null $t0 (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test null $t0 (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test null $t0 (table.get $tab (i32.const 0))))) + (br_if $l (i32.eqz (ref.test null $t0 (table.get $tab (i32.const 1))))) + (br_if $l (i32.eqz (ref.test null $t0 (table.get $tab (i32.const 2))))) + (br_if $l (i32.eqz (ref.test null $t0 (table.get $tab (i32.const 3))))) + (br_if $l (i32.eqz (ref.test null $t0 (table.get $tab (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test null $t1 (ref.null struct)))) + (br_if $l (i32.eqz (ref.test null $t1 (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test null $t1 (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test null $t1 (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test null $t1 (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test null $t1 (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test null $t1 (table.get $tab (i32.const 1))))) + (br_if $l (i32.eqz (ref.test null $t1 (table.get $tab (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test null $t2 (ref.null struct)))) + (br_if $l (i32.eqz (ref.test null $t2 (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test null $t2 (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test null $t2 (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test null $t2 (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test null $t2 (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test null $t2 (table.get $tab (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test null $t3 (ref.null struct)))) + (br_if $l (i32.eqz (ref.test null $t3 (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test null $t3 (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test null $t3 (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test null $t3 (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test null $t3 (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test null $t3 (table.get $tab (i32.const 3))))) + + (br_if $l (i32.eqz (ref.test null $t4 (ref.null struct)))) + (br_if $l (i32.eqz (ref.test null $t4 (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test null $t4 (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test null $t4 (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test null $t4 (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test null $t4 (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test null $t4 (table.get $tab (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test $t0 (table.get $tab (i32.const 0))))) + (br_if $l (i32.eqz (ref.test $t0 (table.get $tab (i32.const 1))))) + (br_if $l (i32.eqz (ref.test $t0 (table.get $tab (i32.const 2))))) + (br_if $l (i32.eqz (ref.test $t0 (table.get $tab (i32.const 3))))) + (br_if $l (i32.eqz (ref.test $t0 (table.get $tab (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test $t1 (table.get $tab (i32.const 1))))) + (br_if $l (i32.eqz (ref.test $t1 (table.get $tab (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test $t2 (table.get $tab (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test $t3 (table.get $tab (i32.const 3))))) + + (br_if $l (i32.eqz (ref.test $t4 (table.get $tab (i32.const 4))))) + + ;; must not hold + (br_if $l (ref.test $t0 (ref.null struct))) + (br_if $l (ref.test $t1 (ref.null struct))) + (br_if $l (ref.test $t2 (ref.null struct))) + (br_if $l (ref.test $t3 (ref.null struct))) + (br_if $l (ref.test $t4 (ref.null struct))) + + (br_if $l (ref.test $t1 (table.get $tab (i32.const 0)))) + (br_if $l (ref.test $t1 (table.get $tab (i32.const 3)))) + (br_if $l (ref.test $t1 (table.get $tab (i32.const 4)))) + + (br_if $l (ref.test $t2 (table.get $tab (i32.const 0)))) + (br_if $l (ref.test $t2 (table.get $tab (i32.const 1)))) + (br_if $l (ref.test $t2 (table.get $tab (i32.const 3)))) + (br_if $l (ref.test $t2 (table.get $tab (i32.const 4)))) + + (br_if $l (ref.test $t3 (table.get $tab (i32.const 0)))) + (br_if $l (ref.test $t3 (table.get $tab (i32.const 1)))) + (br_if $l (ref.test $t3 (table.get $tab (i32.const 2)))) + (br_if $l (ref.test $t3 (table.get $tab (i32.const 4)))) + + (br_if $l (ref.test $t4 (table.get $tab (i32.const 0)))) + (br_if $l (ref.test $t4 (table.get $tab (i32.const 1)))) + (br_if $l (ref.test $t4 (table.get $tab (i32.const 2)))) + (br_if $l (ref.test $t4 (table.get $tab (i32.const 3)))) + + (return) + ) + (unreachable) + ) + + (func (export "test-canon") + (call $init) + (block $l + (br_if $l (i32.eqz (ref.test $t0 (table.get $tab (i32.const 0))))) + (br_if $l (i32.eqz (ref.test $t0 (table.get $tab (i32.const 1))))) + (br_if $l (i32.eqz (ref.test $t0 (table.get $tab (i32.const 2))))) + (br_if $l (i32.eqz (ref.test $t0 (table.get $tab (i32.const 3))))) + (br_if $l (i32.eqz (ref.test $t0 (table.get $tab (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test $t0 (table.get $tab (i32.const 10))))) + (br_if $l (i32.eqz (ref.test $t0 (table.get $tab (i32.const 11))))) + (br_if $l (i32.eqz (ref.test $t0 (table.get $tab (i32.const 12))))) + + (br_if $l (i32.eqz (ref.test $t1' (table.get $tab (i32.const 1))))) + (br_if $l (i32.eqz (ref.test $t1' (table.get $tab (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test $t1 (table.get $tab (i32.const 11))))) + (br_if $l (i32.eqz (ref.test $t1 (table.get $tab (i32.const 12))))) + + (br_if $l (i32.eqz (ref.test $t2' (table.get $tab (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test $t2 (table.get $tab (i32.const 12))))) + + (return) + ) + (unreachable) + ) +) + +(invoke "test-sub") +(invoke "test-canon") |