diff --git a/src/interpreter/Interpreter.cpp b/src/interpreter/Interpreter.cpp index 21292d99a..8b483337d 100644 --- a/src/interpreter/Interpreter.cpp +++ b/src/interpreter/Interpreter.cpp @@ -2213,23 +2213,25 @@ NEVER_INLINE bool Interpreter::testRefGeneric(void* refPtr, Value::Type type) return type == Value::AnyRef; } - ASSERT(type == Value::I31Ref || type == Value::StructRef || type == Value::ArrayRef); + ASSERT(type == Value::I31Ref || type == Value::StructRef + || type == Value::ArrayRef || type == Value::EqRef); if (Value::isI31Value(refPtr)) { - return type == Value::I31Ref; - } - - if (type == Value::I31Ref) { - return false; + return type == Value::I31Ref || type == Value::EqRef; } Object::Kind kind = reinterpret_cast(refPtr)->kind(); - if (type == Value::StructRef) { + switch (type) { + case Value::I31Ref: + return false; + case Value::StructRef: return kind == Object::StructKind; + case Value::ArrayRef: + return kind == Object::ArrayKind; + default: + return kind == Object::StructKind || kind == Object::ArrayKind; } - - return kind == Object::ArrayKind; } NEVER_INLINE bool Interpreter::testRefDefined(void* refPtr, const CompositeType** typeInfo) diff --git a/src/jit/GarbageCollectorInl.h b/src/jit/GarbageCollectorInl.h index e4af002f4..ce5334f07 100644 --- a/src/jit/GarbageCollectorInl.h +++ b/src/jit/GarbageCollectorInl.h @@ -221,12 +221,89 @@ static void emitGCCastGeneric(sljit_compiler* compiler, Instruction* instr) if ((srcInfo & (JumpIfCastGeneric::IsSrcNullable | JumpIfCastGeneric::IsSrcTagged)) == 0) { sljit_emit_op1(compiler, SLJIT_MOV_P, SLJIT_TMP_DEST_REG, 0, SLJIT_MEM1(srcReg), JITFieldAccessor::objectTypeInfo()); if (label != nullptr) { - label->jumpFrom(sljit_emit_cmp(compiler, isTestOrCastFail ? SLJIT_NOT_EQUAL : SLJIT_EQUAL, SLJIT_MEM1(SLJIT_TMP_DEST_REG), -static_cast(sizeof(sljit_up)), SLJIT_IMM, kind)); + sljit_s32 type = (genericType == Value::EqRef) ? SLJIT_LESS_EQUAL : SLJIT_EQUAL; + if (isTestOrCastFail) { + type ^= 0x1; + } + label->jumpFrom(sljit_emit_cmp(compiler, type, SLJIT_MEM1(SLJIT_TMP_DEST_REG), -static_cast(sizeof(sljit_up)), SLJIT_IMM, kind)); } else if (!isTestOrCastFail) { - context->appendTrapJump(ExecutionContext::CastFailureError, sljit_emit_cmp(compiler, SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_TMP_DEST_REG), -static_cast(sizeof(sljit_up)), SLJIT_IMM, kind)); + sljit_s32 type = (genericType == Value::EqRef) ? SLJIT_GREATER : SLJIT_NOT_EQUAL; + context->appendTrapJump(ExecutionContext::CastFailureError, sljit_emit_cmp(compiler, type, SLJIT_MEM1(SLJIT_TMP_DEST_REG), -static_cast(sizeof(sljit_up)), SLJIT_IMM, kind)); } else { - sljit_emit_op2u(compiler, SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_TMP_DEST_REG), -static_cast(sizeof(sljit_up)), SLJIT_IMM, kind); - sljit_emit_op_flags(compiler, SLJIT_MOV, args[1].arg, args[1].argw, SLJIT_EQUAL); + sljit_s32 type = (genericType == Value::EqRef) ? SLJIT_SET_LESS_EQUAL : SLJIT_SET_Z; + sljit_emit_op2u(compiler, SLJIT_SUB | type, SLJIT_MEM1(SLJIT_TMP_DEST_REG), -static_cast(sizeof(sljit_up)), SLJIT_IMM, kind); + type = (genericType == Value::EqRef) ? SLJIT_LESS_EQUAL : SLJIT_EQUAL; + sljit_emit_op_flags(compiler, SLJIT_MOV, args[1].arg, args[1].argw, type); + } + return; + } + + if (genericType == Value::EqRef) { + if ((srcInfo & JumpIfCastGeneric::IsSrcTagged) != 0) { + sljit_emit_op2(compiler, SLJIT_ROTR, SLJIT_TMP_DEST_REG, 0, srcReg, 0, SLJIT_IMM, 1); + sljit_jump* jump = sljit_emit_cmp(compiler, SLJIT_SIG_LESS_EQUAL, SLJIT_TMP_DEST_REG, 0, SLJIT_IMM, 0); + sljit_emit_op2(compiler, SLJIT_SHL, SLJIT_TMP_DEST_REG, 0, SLJIT_TMP_DEST_REG, 0, SLJIT_IMM, 1); + + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_TMP_DEST_REG, 0, SLJIT_MEM1(SLJIT_TMP_DEST_REG), JITFieldAccessor::objectTypeInfo()); + sljit_emit_op2(compiler, SLJIT_SUB, SLJIT_TMP_DEST_REG, 0, SLJIT_MEM1(SLJIT_TMP_DEST_REG), -static_cast(sizeof(sljit_up)), SLJIT_IMM, kind + 1); + sljit_set_label(jump, sljit_emit_label(compiler)); + + if (label != nullptr) { + sljit_s32 type = ((srcInfo & JumpIfCastGeneric::IsNullable) == 0) ? SLJIT_SIG_LESS : SLJIT_SIG_LESS_EQUAL; + if (isTestOrCastFail) { + type ^= 0x1; + } + label->jumpFrom(sljit_emit_cmp(compiler, type, SLJIT_TMP_DEST_REG, 0, SLJIT_IMM, 0)); + } else if (!isTestOrCastFail) { + sljit_s32 type = ((srcInfo & JumpIfCastGeneric::IsNullable) == 0) ? SLJIT_SIG_GREATER_EQUAL : SLJIT_SIG_GREATER; + context->appendTrapJump(ExecutionContext::CastFailureError, sljit_emit_cmp(compiler, type, SLJIT_TMP_DEST_REG, 0, SLJIT_IMM, 0)); + } else { + sljit_s32 type = ((srcInfo & JumpIfCastGeneric::IsNullable) == 0) ? SLJIT_SET_SIG_LESS : SLJIT_SET_SIG_LESS_EQUAL; + sljit_emit_op2u(compiler, SLJIT_SUB | type, SLJIT_TMP_DEST_REG, 0, SLJIT_IMM, 0); + type = ((srcInfo & JumpIfCastGeneric::IsNullable) == 0) ? SLJIT_SIG_LESS : SLJIT_SIG_LESS_EQUAL; + sljit_emit_op_flags(compiler, SLJIT_MOV, args[1].arg, args[1].argw, type); + } + return; + } + + if (srcReg != SLJIT_TMP_DEST_REG && label == nullptr && isTestOrCastFail) { + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_TMP_DEST_REG, 0, srcReg, 0); + srcReg = SLJIT_TMP_DEST_REG; + } + + sljit_jump* jump = sljit_emit_cmp(compiler, SLJIT_EQUAL, srcReg, 0, SLJIT_IMM, 0); + sljit_emit_op1(compiler, SLJIT_MOV_P, SLJIT_TMP_DEST_REG, 0, SLJIT_MEM1(srcReg), JITFieldAccessor::objectTypeInfo()); + + if (label != nullptr) { + label->jumpFrom(sljit_emit_cmp(compiler, isTestOrCastFail ? SLJIT_GREATER : SLJIT_LESS_EQUAL, SLJIT_MEM1(SLJIT_TMP_DEST_REG), -static_cast(sizeof(sljit_up)), SLJIT_IMM, kind)); + if ((srcInfo & JumpIfCastGeneric::IsNullable) != 0) { + isTestOrCastFail = !isTestOrCastFail; + } + + if (isTestOrCastFail) { + label->jumpFrom(jump); + } else { + sljit_set_label(jump, sljit_emit_label(compiler)); + } + } else if (!isTestOrCastFail) { + context->appendTrapJump(ExecutionContext::CastFailureError, sljit_emit_cmp(compiler, SLJIT_GREATER, SLJIT_MEM1(SLJIT_TMP_DEST_REG), -static_cast(sizeof(sljit_up)), SLJIT_IMM, kind)); + if ((srcInfo & JumpIfCastGeneric::IsNullable) != 0) { + sljit_set_label(jump, sljit_emit_label(compiler)); + } else { + context->appendTrapJump(ExecutionContext::CastFailureError, jump); + } + } else { + ASSERT(srcReg == SLJIT_TMP_DEST_REG); + kind++; + if ((srcInfo & JumpIfCastGeneric::IsNullable) != 0) { + sljit_emit_op1(compiler, SLJIT_MOV_P, SLJIT_TMP_DEST_REG, 0, SLJIT_MEM1(SLJIT_TMP_DEST_REG), -static_cast(sizeof(sljit_up))); + } else { + sljit_emit_op2(compiler, SLJIT_SUB, SLJIT_TMP_DEST_REG, 0, SLJIT_MEM1(SLJIT_TMP_DEST_REG), -static_cast(sizeof(sljit_up)), SLJIT_IMM, kind); + kind = 0; + } + sljit_set_label(jump, sljit_emit_label(compiler)); + sljit_emit_op2u(compiler, SLJIT_SUB | SLJIT_SET_SIG_LESS, SLJIT_TMP_DEST_REG, 0, SLJIT_IMM, kind); + sljit_emit_op_flags(compiler, SLJIT_MOV, args[1].arg, args[1].argw, SLJIT_SIG_LESS); } return; } diff --git a/src/parser/WASMParser.cpp b/src/parser/WASMParser.cpp index 5ce6d21ce..62d446538 100644 --- a/src/parser/WASMParser.cpp +++ b/src/parser/WASMParser.cpp @@ -2594,6 +2594,7 @@ class WASMBinaryReader : public wabt::WASMBinaryReaderDelegate { case Walrus::Value::I31Ref: case Walrus::Value::StructRef: case Walrus::Value::ArrayRef: + case Walrus::Value::EqRef: break; case Walrus::Value::NoAnyRef: case Walrus::Value::NoExternRef: @@ -2693,8 +2694,17 @@ class WASMBinaryReader : public wabt::WASMBinaryReaderDelegate { virtual void OnGCUnaryExpr(int opcode) override { switch (opcode) { - case Opcode::RefEq: + case Opcode::RefEq: { + auto src1 = popVMStack(); + auto src0 = popVMStack(); + auto dst = computeExprResultPosition(Walrus::Value::Type::I32); + if (sizeof(void*) == 4) { + pushByteCode(Walrus::I32Eq(src0, src1, dst), WASMOpcode::RefEqOpcode); + } else { + pushByteCode(Walrus::I64Eq(src0, src1, dst), WASMOpcode::RefEqOpcode); + } break; + } case Opcode::ArrayLen: { bool isNullable = Walrus::Value::isNullableRefType(peekVMStackInfo().valueType()); auto src = popVMStack(); @@ -2703,9 +2713,13 @@ class WASMBinaryReader : public wabt::WASMBinaryReaderDelegate { break; } case Opcode::AnyConvertExtern: + case Opcode::ExternConvertAny: { + Walrus::Value::Type type = (opcode == Opcode::AnyConvertExtern) ? Walrus::Value::Type::AnyRef : Walrus::Value::Type::ExternRef; + auto src = popVMStack(); + auto dst = computeExprResultPosition(type); + generateMoveCodeIfNeeds(src, dst, type); break; - case Opcode::ExternConvertAny: - break; + } case Opcode::RefI31: { auto src = popVMStack(); auto dst = computeExprResultPosition(Walrus::Value::Type::I31Ref); @@ -3008,7 +3022,7 @@ class WASMBinaryReader : public wabt::WASMBinaryReaderDelegate { } if (m_shouldContinueToGenerateByteCode) { for (size_t i = 0; i < m_currentFunctionType->result().size() && m_vmStack.size(); i++) { - ASSERT(popVMStackInfo().valueType() == m_currentFunctionType->result()[m_currentFunctionType->result().size() - i - 1]); + ASSERT(toDebugType(popVMStackInfo().valueType()) == toDebugType(m_currentFunctionType->result()[m_currentFunctionType->result().size() - i - 1])); } ASSERT(m_vmStack.empty()); } diff --git a/test/extended/gc/ref_eq.wast b/test/extended/gc/ref_eq.wast new file mode 100644 index 000000000..001efd69f --- /dev/null +++ b/test/extended/gc/ref_eq.wast @@ -0,0 +1,168 @@ +(module + (type $st (sub (struct))) + (type $st' (sub (struct (field i32)))) + (type $at (array i8)) + (type $st-sub1 (sub $st (struct))) + (type $st-sub2 (sub $st (struct))) + (type $st'-sub1 (sub $st' (struct (field i32)))) + (type $st'-sub2 (sub $st' (struct (field i32)))) + + (table 20 (ref null eq)) + + (func (export "init") + (table.set (i32.const 0) (ref.null eq)) + (table.set (i32.const 1) (ref.null i31)) + (table.set (i32.const 2) (ref.i31 (i32.const 7))) + (table.set (i32.const 3) (ref.i31 (i32.const 7))) + (table.set (i32.const 4) (ref.i31 (i32.const 8))) + (table.set (i32.const 5) (struct.new_default $st)) + (table.set (i32.const 6) (struct.new_default $st)) + (table.set (i32.const 7) (array.new_default $at (i32.const 0))) + (table.set (i32.const 8) (array.new_default $at (i32.const 0))) + ) + + (func (export "eq") (param $i i32) (param $j i32) (result i32) + (ref.eq (table.get (local.get $i)) (table.get (local.get $j))) + ) +) + +(invoke "init") + +(assert_return (invoke "eq" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 2) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 2)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 3)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 3) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 2)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 3)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 4) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 4)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 5) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 5)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 6) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 6)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 7) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 7)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 8)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 8) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 8)) (i32.const 1)) + +(assert_invalid + (module + (func (export "eq") (param $r (ref any)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref null any)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref func)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref null func)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref extern)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref null extern)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) diff --git a/test/extended/gc/ref_test.wast b/test/extended/gc/ref_test.wast new file mode 100644 index 000000000..590b81b8a --- /dev/null +++ b/test/extended/gc/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") (param $x externref) + (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) (ref.i31 (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) (any.convert_extern (local.get $x))) + (table.set $ta (i32.const 7) (any.convert_extern (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) (local.get $x)) + (table.set $te (i32.const 3) (extern.convert_any (ref.i31 (i32.const 8)))) + (table.set $te (i32.const 4) (extern.convert_any (struct.new_default $st))) + (table.set $te (i32.const 5) (extern.convert_any (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 nullref (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_any") (param $i i32) (result i32) + (i32.add + (ref.test (ref any) (table.get $ta (local.get $i))) + (ref.test anyref (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_eq") (param $i i32) (result i32) + (i32.add + (ref.test (ref eq) (table.get $ta (local.get $i))) + (ref.test eqref (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_i31") (param $i i32) (result i32) + (i32.add + (ref.test (ref i31) (table.get $ta (local.get $i))) + (ref.test i31ref (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_struct") (param $i i32) (result i32) + (i32.add + (ref.test (ref struct) (table.get $ta (local.get $i))) + (ref.test structref (table.get $ta (local.get $i))) + ) + ) + (func (export "ref_test_array") (param $i i32) (result i32) + (i32.add + (ref.test (ref array) (table.get $ta (local.get $i))) + (ref.test arrayref (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 (ref null nofunc) (table.get $tf (local.get $i))) + ) + ) + (func (export "ref_test_func") (param $i i32) (result i32) + (i32.add + (ref.test (ref func) (table.get $tf (local.get $i))) + (ref.test funcref (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 (ref null noextern) (table.get $te (local.get $i))) + ) + ) + (func (export "ref_test_extern") (param $i i32) (result i32) + (i32.add + (ref.test (ref extern) (table.get $te (local.get $i))) + (ref.test externref (table.get $te (local.get $i))) + ) + ) +) + +(invoke "init" (ref.extern 0)) + +(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 0)) +(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 (sub (struct))) + (type $t1 (sub $t0 (struct (field i32)))) + (type $t1' (sub $t0 (struct (field i32)))) + (type $t2 (sub $t1 (struct (field i32 i32)))) + (type $t2' (sub $t1' (struct (field i32 i32)))) + (type $t3 (sub $t0 (struct (field i32 i32)))) + (type $t0' (sub $t0 (struct))) + (type $t4 (sub $t0' (struct (field i32 i32)))) + + (table 20 (ref null struct)) + + (func $init + (table.set (i32.const 0) (struct.new_default $t0)) + (table.set (i32.const 10) (struct.new_default $t0)) + (table.set (i32.const 1) (struct.new_default $t1)) + (table.set (i32.const 11) (struct.new_default $t1')) + (table.set (i32.const 2) (struct.new_default $t2)) + (table.set (i32.const 12) (struct.new_default $t2')) + (table.set (i32.const 3) (struct.new_default $t3)) + (table.set (i32.const 4) (struct.new_default $t4)) + ) + + (func (export "test-sub") + (call $init) + (block $l + ;; must hold + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 0))))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 2))))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 3))))) + (br_if $l (i32.eqz (ref.test (ref null $t0) (table.get (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref null $t1) (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t2) (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t3) (table.get (i32.const 3))))) + + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null struct)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t0)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t1)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t2)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t3)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (ref.null $t4)))) + (br_if $l (i32.eqz (ref.test (ref null $t4) (table.get (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 0))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 2))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 3))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref $t2) (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref $t3) (table.get (i32.const 3))))) + + (br_if $l (i32.eqz (ref.test (ref $t4) (table.get (i32.const 4))))) + + ;; must not hold + (br_if $l (ref.test (ref $t0) (ref.null struct))) + (br_if $l (ref.test (ref $t1) (ref.null struct))) + (br_if $l (ref.test (ref $t2) (ref.null struct))) + (br_if $l (ref.test (ref $t3) (ref.null struct))) + (br_if $l (ref.test (ref $t4) (ref.null struct))) + + (br_if $l (ref.test (ref $t1) (table.get (i32.const 0)))) + (br_if $l (ref.test (ref $t1) (table.get (i32.const 3)))) + (br_if $l (ref.test (ref $t1) (table.get (i32.const 4)))) + + (br_if $l (ref.test (ref $t2) (table.get (i32.const 0)))) + (br_if $l (ref.test (ref $t2) (table.get (i32.const 1)))) + (br_if $l (ref.test (ref $t2) (table.get (i32.const 3)))) + (br_if $l (ref.test (ref $t2) (table.get (i32.const 4)))) + + (br_if $l (ref.test (ref $t3) (table.get (i32.const 0)))) + (br_if $l (ref.test (ref $t3) (table.get (i32.const 1)))) + (br_if $l (ref.test (ref $t3) (table.get (i32.const 2)))) + (br_if $l (ref.test (ref $t3) (table.get (i32.const 4)))) + + (br_if $l (ref.test (ref $t4) (table.get (i32.const 0)))) + (br_if $l (ref.test (ref $t4) (table.get (i32.const 1)))) + (br_if $l (ref.test (ref $t4) (table.get (i32.const 2)))) + (br_if $l (ref.test (ref $t4) (table.get (i32.const 3)))) + + (return) + ) + (unreachable) + ) + + (func (export "test-canon") + (call $init) + (block $l + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 0))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 2))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 3))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 4))))) + + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 10))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 11))))) + (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 12))))) + + (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 1))))) + (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 11))))) + (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 12))))) + + (br_if $l (i32.eqz (ref.test (ref $t2') (table.get (i32.const 2))))) + + (br_if $l (i32.eqz (ref.test (ref $t2) (table.get (i32.const 12))))) + + (return) + ) + (unreachable) + ) +) + +(assert_return (invoke "test-sub")) +(assert_return (invoke "test-canon")) diff --git a/test/jit/gc_cast_abstract.wast b/test/jit/gc_cast_abstract.wast index 7c387fd6e..c6bb22734 100644 --- a/test/jit/gc_cast_abstract.wast +++ b/test/jit/gc_cast_abstract.wast @@ -627,6 +627,107 @@ (block (result anyref) (br_on_cast_fail 0 anyref (ref null none) (table.get $tn_array (i32.const 1))) (i32.const 10) (br 1)) (drop) (i32.const 14)) ) + + (func (export "ref_eq1") (result i32 i32 i32) + (ref.test (ref eq) (table.get $t_any (i32.const 0))) + (ref.test (ref eq) (table.get $t_struct (i32.const 0))) + (ref.test (ref eq) (table.get $t_array (i32.const 0))) + (ref.cast (ref eq) (table.get $t_any (i32.const 1))) (drop) + (ref.cast (ref eq) (table.get $t_struct (i32.const 1))) (drop) + (ref.cast (ref eq) (table.get $t_array (i32.const 1))) (drop) + ) + + (func (export "ref_eq2") (result i32 i32 i32 i32 i32) + (ref.test (ref eq) (table.get $tn_any (i32.const 0))) + (ref.test (ref eq) (table.get $tn_any (i32.const 1))) + (ref.test (ref eq) (table.get $tn_any (i32.const 2))) + (ref.test (ref eq) (table.get $tn_struct (i32.const 0))) + (ref.test (ref eq) (table.get $tn_array (i32.const 1))) + (ref.cast (ref eq) (table.get $tn_any (i32.const 1))) (drop) + (ref.cast (ref eq) (table.get $tn_any (i32.const 2))) (drop) + (ref.cast (ref eq) (table.get $tn_struct (i32.const 1))) (drop) + (ref.cast (ref eq) (table.get $tn_array (i32.const 1))) (drop) + ) + + (func (export "ref_eq3") + (ref.cast (ref eq) (table.get $tn_any (i32.const 0))) (drop) + ) + + (func (export "ref_eq4") + (ref.cast (ref eq) (table.get $tn_array (i32.const 0))) (drop) + ) + + (func (export "ref_eq5") (result i32 i32 i32 i32 i32) + (ref.test (ref null eq) (table.get $tn_any (i32.const 0))) + (ref.test (ref null eq) (table.get $tn_any (i32.const 1))) + (ref.test (ref null eq) (table.get $tn_any (i32.const 2))) + (ref.test (ref null eq) (table.get $tn_struct (i32.const 1))) + (ref.test (ref null eq) (table.get $tn_array (i32.const 0))) + (ref.cast (ref null eq) (table.get $tn_any (i32.const 0))) (drop) + (ref.cast (ref null eq) (table.get $tn_any (i32.const 1))) (drop) + (ref.cast (ref null eq) (table.get $tn_any (i32.const 2))) (drop) + (ref.cast (ref null eq) (table.get $tn_struct (i32.const 0))) (drop) + (ref.cast (ref null eq) (table.get $tn_struct (i32.const 1))) (drop) + (ref.cast (ref null eq) (table.get $tn_array (i32.const 0))) (drop) + (ref.cast (ref null eq) (table.get $tn_array (i32.const 1))) (drop) + ) + + (func (export "ref_eq6") (result i32 i32 i32 i32 i32 i32) + (block (result i32) + (block (result anyref) (br_on_cast 0 anyref (ref eq) (table.get $tn_any (i32.const 0))) (i32.const 6) (br 1)) + (drop) (i32.const 12)) + (block (result i32) + (block (result anyref) (br_on_cast_fail 0 anyref (ref eq) (table.get $tn_any (i32.const 1))) (i32.const 7) (br 1)) + (drop) (i32.const 13)) + (block (result i32) + (block (result anyref) (br_on_cast 0 anyref (ref eq) (table.get $tn_any (i32.const 2))) (i32.const 8) (br 1)) + (drop) (i32.const 14)) + (block (result i32) + (block (result anyref) (br_on_cast_fail 0 anyref (ref eq) (table.get $tn_struct (i32.const 0))) (i32.const 9) (br 1)) + (drop) (i32.const 15)) + (block (result i32) + (block (result anyref) (br_on_cast 0 anyref (ref eq) (table.get $tn_array (i32.const 0))) (i32.const 10) (br 1)) + (drop) (i32.const 16)) + (block (result i32) + (block (result anyref) (br_on_cast_fail 0 anyref (ref eq) (table.get $tn_array (i32.const 1))) (i32.const 11) (br 1)) + (drop) (i32.const 17)) + ) + + (func (export "ref_eq7") (result i32 i32 i32 i32 i32 i32) + (block (result i32) + (block (result anyref) (br_on_cast 0 anyref (ref null eq) (table.get $tn_any (i32.const 0))) (i32.const 2) (br 1)) + (drop) (i32.const 8)) + (block (result i32) + (block (result anyref) (br_on_cast_fail 0 anyref (ref null eq) (table.get $tn_any (i32.const 1))) (i32.const 3) (br 1)) + (drop) (i32.const 9)) + (block (result i32) + (block (result anyref) (br_on_cast 0 anyref (ref null eq) (table.get $tn_any (i32.const 2))) (i32.const 4) (br 1)) + (drop) (i32.const 10)) + (block (result i32) + (block (result anyref) (br_on_cast_fail 0 anyref (ref null eq) (table.get $tn_struct (i32.const 0))) (i32.const 5) (br 1)) + (drop) (i32.const 11)) + (block (result i32) + (block (result anyref) (br_on_cast 0 anyref (ref null eq) (table.get $tn_array (i32.const 0))) (i32.const 6) (br 1)) + (drop) (i32.const 12)) + (block (result i32) + (block (result anyref) (br_on_cast_fail 0 anyref (ref null eq) (table.get $tn_array (i32.const 1))) (i32.const 7) (br 1)) + (drop) (i32.const 13)) + ) + + (func (export "ref_eq8") (result i32 i32 i32 i32) + (block (result i32) + (block (result anyref) (br_on_cast 0 anyref (ref eq) (table.get $t_any (i32.const 0))) (i32.const 9) (br 1)) + (drop) (i32.const 13)) + (block (result i32) + (block (result anyref) (br_on_cast_fail 0 anyref (ref null eq) (table.get $t_any (i32.const 1))) (i32.const 10) (br 1)) + (drop) (i32.const 14)) + (block (result i32) + (block (result anyref) (br_on_cast 0 anyref (ref eq) (table.get $t_struct (i32.const 1))) (i32.const 11) (br 1)) + (drop) (i32.const 15)) + (block (result i32) + (block (result anyref) (br_on_cast_fail 0 anyref (ref null eq) (table.get $t_array (i32.const 0))) (i32.const 12) (br 1)) + (drop) (i32.const 16)) + ) ) (invoke "init") @@ -695,3 +796,12 @@ (assert_return (invoke "ref_none6") (i32.const 1) (i32.const 0) (i32.const 1) (i32.const 0)) (assert_return (invoke "ref_none7") (i32.const 3) (i32.const 8) (i32.const 5) (i32.const 10)) (assert_return (invoke "ref_none8") (i32.const 11) (i32.const 8) (i32.const 9) (i32.const 14)) + +(assert_return (invoke "ref_eq1") (i32.const 1) (i32.const 1) (i32.const 1)) +(assert_return (invoke "ref_eq2") (i32.const 0) (i32.const 1) (i32.const 1) (i32.const 0) (i32.const 1)) +(assert_trap (invoke "ref_eq3") "cast failure") +(assert_trap (invoke "ref_eq4") "cast failure") +(assert_return (invoke "ref_eq5") (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1)) +(assert_return (invoke "ref_eq6") (i32.const 6) (i32.const 7) (i32.const 14) (i32.const 15) (i32.const 10) (i32.const 11)) +(assert_return (invoke "ref_eq7") (i32.const 8) (i32.const 3) (i32.const 10) (i32.const 5) (i32.const 12) (i32.const 7)) +(assert_return (invoke "ref_eq8") (i32.const 13) (i32.const 10) (i32.const 15) (i32.const 12))