Commit 103a42d3 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Bring V8 up to date with latest spec updates

Changes:
- Remove the restriction that ref.test, ref.cast and br_on_cast may only
  cast to subtypes of the cast object's type. Optimize unrelated type
  casts in the decoder. Add tests.
- Generalize Unreachable() interface function to Trap(TrapReason).
- Fix rtt.sub to be able to accept an rtt without depth. Modify related
  test accordingly.
- Type local.tee according to the local's type as opposed to the value's
  type.

Bug: v8:7748, v8:11541
Change-Id: I4d1846a2cfda891d32a9c1ed26781e4518d4cdf9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2756210Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73461}
parent e090f835
...@@ -2364,13 +2364,37 @@ class LiftoffCompiler { ...@@ -2364,13 +2364,37 @@ class LiftoffCompiler {
RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill); RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
} }
void Unreachable(FullDecoder* decoder) { WasmCode::RuntimeStubId GetRuntimeStubIdForTrapReason(TrapReason reason) {
Label* unreachable_label = switch (reason) {
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapUnreachable); #define RUNTIME_STUB_FOR_TRAP(trap_reason) \
__ emit_jump(unreachable_label); case k##trap_reason: \
return WasmCode::kThrowWasm##trap_reason;
FOREACH_WASM_TRAPREASON(RUNTIME_STUB_FOR_TRAP)
#undef RUNTIME_STUB_FOR_TRAP
default:
UNREACHABLE();
}
}
void Trap(FullDecoder* decoder, TrapReason reason) {
Label* trap_label =
AddOutOfLineTrap(decoder, GetRuntimeStubIdForTrapReason(reason));
__ emit_jump(trap_label);
__ AssertUnreachable(AbortReason::kUnexpectedReturnFromWasmTrap); __ AssertUnreachable(AbortReason::kUnexpectedReturnFromWasmTrap);
} }
void AssertNull(FullDecoder* decoder, const Value& arg, Value* result) {
LiftoffRegList pinned;
LiftoffRegister obj = pinned.set(__ PopToRegister(pinned));
Label* trap_label =
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapNullDereference);
LiftoffRegister null = __ GetUnusedRegister(kGpReg, pinned);
LoadNullValue(null.gp(), pinned);
__ emit_cond_jump(kUnequal, trap_label, kOptRef, obj.gp(), null.gp());
__ PushRegister(kOptRef, obj);
}
void NopForTestingUnsupportedInLiftoff(FullDecoder* decoder) { void NopForTestingUnsupportedInLiftoff(FullDecoder* decoder) {
unsupported(decoder, kOtherReason, "testing opcode"); unsupported(decoder, kOtherReason, "testing opcode");
} }
......
...@@ -1063,7 +1063,7 @@ struct ControlBase : public PcForErrors<validate> { ...@@ -1063,7 +1063,7 @@ struct ControlBase : public PcForErrors<validate> {
const TableIndexImmediate<validate>& imm) \ const TableIndexImmediate<validate>& imm) \
F(TableSet, const Value& index, const Value& value, \ F(TableSet, const Value& index, const Value& value, \
const TableIndexImmediate<validate>& imm) \ const TableIndexImmediate<validate>& imm) \
F(Unreachable) \ F(Trap, TrapReason reason) \
F(NopForTestingUnsupportedInLiftoff) \ F(NopForTestingUnsupportedInLiftoff) \
F(Select, const Value& cond, const Value& fval, const Value& tval, \ F(Select, const Value& cond, const Value& fval, const Value& tval, \
Value* result) \ Value* result) \
...@@ -1159,6 +1159,7 @@ struct ControlBase : public PcForErrors<validate> { ...@@ -1159,6 +1159,7 @@ struct ControlBase : public PcForErrors<validate> {
F(RttSub, uint32_t type_index, const Value& parent, Value* result) \ F(RttSub, uint32_t type_index, const Value& parent, Value* result) \
F(RefTest, const Value& obj, const Value& rtt, Value* result) \ F(RefTest, const Value& obj, const Value& rtt, Value* result) \
F(RefCast, const Value& obj, const Value& rtt, Value* result) \ F(RefCast, const Value& obj, const Value& rtt, Value* result) \
F(AssertNull, const Value& obj, Value* result) \
F(BrOnCast, const Value& obj, const Value& rtt, Value* result_on_branch, \ F(BrOnCast, const Value& obj, const Value& rtt, Value* result_on_branch, \
uint32_t depth) \ uint32_t depth) \
F(RefIsData, const Value& object, Value* result) \ F(RefIsData, const Value& object, Value* result) \
...@@ -2943,7 +2944,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2943,7 +2944,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
} }
DECODE(Unreachable) { DECODE(Unreachable) {
CALL_INTERFACE_IF_REACHABLE(Unreachable); CALL_INTERFACE_IF_REACHABLE(Trap, TrapReason::kTrapUnreachable);
EndControl(); EndControl();
return 1; return 1;
} }
...@@ -3079,8 +3080,9 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -3079,8 +3080,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
DECODE(LocalTee) { DECODE(LocalTee) {
LocalIndexImmediate<validate> imm(this, this->pc_ + 1); LocalIndexImmediate<validate> imm(this, this->pc_ + 1);
if (!this->Validate(this->pc_ + 1, imm)) return 0; if (!this->Validate(this->pc_ + 1, imm)) return 0;
Value value = Peek(0, 0, this->local_type(imm.index)); ValueType local_type = this->local_type(imm.index);
Value result = CreateValue(value.type); Value value = Peek(0, 0, local_type);
Value result = CreateValue(local_type);
CALL_INTERFACE_IF_REACHABLE(LocalTee, value, &result, imm); CALL_INTERFACE_IF_REACHABLE(LocalTee, value, &result, imm);
Drop(value); Drop(value);
Push(result); Push(result);
...@@ -4011,6 +4013,14 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -4011,6 +4013,14 @@ class WasmFullDecoder : public WasmDecoder<validate> {
} }
} }
bool ObjectRelatedWithRtt(Value obj, Value rtt) {
return IsSubtypeOf(ValueType::Ref(rtt.type.ref_index(), kNonNullable),
obj.type, this->module_) ||
IsSubtypeOf(obj.type,
ValueType::Ref(rtt.type.ref_index(), kNullable),
this->module_);
}
int DecodeGCOpcode(WasmOpcode opcode, uint32_t opcode_length) { int DecodeGCOpcode(WasmOpcode opcode, uint32_t opcode_length) {
switch (opcode) { switch (opcode) {
case kExprStructNewWithRtt: { case kExprStructNewWithRtt: {
...@@ -4302,8 +4312,10 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -4302,8 +4312,10 @@ class WasmFullDecoder : public WasmDecoder<validate> {
"rtt for a supertype of type " + std::to_string(imm.index)); "rtt for a supertype of type " + std::to_string(imm.index));
return 0; return 0;
} }
Value value = Value value = parent.type.has_depth()
CreateValue(ValueType::Rtt(imm.index, parent.type.depth() + 1)); ? CreateValue(ValueType::Rtt(
imm.index, parent.type.depth() + 1))
: CreateValue(ValueType::Rtt(imm.index));
CALL_INTERFACE_IF_REACHABLE(RttSub, imm.index, parent, &value); CALL_INTERFACE_IF_REACHABLE(RttSub, imm.index, parent, &value);
Drop(parent); Drop(parent);
...@@ -4329,15 +4341,14 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -4329,15 +4341,14 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return 0; return 0;
} }
if (!obj.type.is_bottom() && !rtt.type.is_bottom()) { if (!obj.type.is_bottom() && !rtt.type.is_bottom()) {
if (!VALIDATE(IsSubtypeOf( // This logic ensures that code generation can assume that functions
ValueType::Ref(rtt.type.ref_index(), kNonNullable), obj.type, // can only be cast to function types, and data objects to data types.
this->module_))) { if (V8_LIKELY(ObjectRelatedWithRtt(obj, rtt))) {
PopTypeError( CALL_INTERFACE_IF_REACHABLE(RefTest, obj, rtt, &value);
0, obj, } else {
"supertype of type " + std::to_string(rtt.type.ref_index())); // Unrelated types. Will always fail.
return 0; CALL_INTERFACE_IF_REACHABLE(I32Const, &value, 0);
} }
CALL_INTERFACE_IF_REACHABLE(RefTest, obj, rtt, &value);
} }
Drop(2); Drop(2);
Push(value); Push(value);
...@@ -4359,17 +4370,25 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -4359,17 +4370,25 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return 0; return 0;
} }
if (!obj.type.is_bottom() && !rtt.type.is_bottom()) { if (!obj.type.is_bottom() && !rtt.type.is_bottom()) {
if (!VALIDATE(IsSubtypeOf(
ValueType::Ref(rtt.type.ref_index(), kNonNullable), obj.type,
this->module_))) {
PopTypeError(
0, obj,
"supertype of type " + std::to_string(rtt.type.ref_index()));
return 0;
}
Value value = CreateValue( Value value = CreateValue(
ValueType::Ref(rtt.type.ref_index(), obj.type.nullability())); ValueType::Ref(rtt.type.ref_index(), obj.type.nullability()));
CALL_INTERFACE_IF_REACHABLE(RefCast, obj, rtt, &value); // This logic ensures that code generation can assume that functions
// can only be cast to function types, and data objects to data types.
if (V8_LIKELY(ObjectRelatedWithRtt(obj, rtt))) {
CALL_INTERFACE_IF_REACHABLE(RefCast, obj, rtt, &value);
} else {
// Unrelated types. The only way this will not trap is if the object
// is null.
if (obj.type.is_nullable()) {
// Drop rtt from the stack, then assert that obj is null.
CALL_INTERFACE_IF_REACHABLE(Drop);
CALL_INTERFACE_IF_REACHABLE(AssertNull, obj, &value);
} else {
// TODO(manoskouk): Change the trap label.
CALL_INTERFACE_IF_REACHABLE(Trap, TrapReason::kTrapIllegalCast);
EndControl();
}
}
Drop(2); Drop(2);
Push(value); Push(value);
} }
...@@ -4396,14 +4415,6 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -4396,14 +4415,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)"); PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
return 0; return 0;
} }
// The static type of {obj} must be a supertype of {rtt}'s type.
if (!VALIDATE(rtt.type.is_bottom() || obj.type.is_bottom() ||
IsHeapSubtypeOf(rtt.type.ref_index(),
obj.type.heap_representation(),
this->module_))) {
PopTypeError(1, rtt, obj.type);
return 0;
}
Control* c = control_at(branch_depth.depth); Control* c = control_at(branch_depth.depth);
if (c->br_merge()->arity == 0) { if (c->br_merge()->arity == 0) {
this->DecodeError( this->DecodeError(
...@@ -4423,13 +4434,18 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -4423,13 +4434,18 @@ class WasmFullDecoder : public WasmDecoder<validate> {
Push(result_on_branch); Push(result_on_branch);
TypeCheckBranchResult check_result = TypeCheckBranch(c, true, 0); TypeCheckBranchResult check_result = TypeCheckBranch(c, true, 0);
if (V8_LIKELY(check_result == kReachableBranch)) { if (V8_LIKELY(check_result == kReachableBranch)) {
// The {value_on_branch} parameter we pass to the interface must be // This logic ensures that code generation can assume that functions
// pointer-identical to the object on the stack, so we can't reuse // can only be cast to function types, and data objects to data types.
// {result_on_branch} which was passed-by-value to {Push}. if (V8_LIKELY(ObjectRelatedWithRtt(obj, rtt))) {
Value* value_on_branch = stack_value(1); // The {value_on_branch} parameter we pass to the interface must
CALL_INTERFACE(BrOnCast, obj, rtt, value_on_branch, // be pointer-identical to the object on the stack, so we can't
branch_depth.depth); // reuse {result_on_branch} which was passed-by-value to {Push}.
c->br_merge()->reached = true; Value* value_on_branch = stack_value(1);
CALL_INTERFACE(BrOnCast, obj, rtt, value_on_branch,
branch_depth.depth);
c->br_merge()->reached = true;
}
// Otherwise the types are unrelated. Do not branch.
} else if (check_result == kInvalidStack) { } else if (check_result == kInvalidStack) {
return 0; return 0;
} }
......
...@@ -397,13 +397,22 @@ class WasmGraphBuildingInterface { ...@@ -397,13 +397,22 @@ class WasmGraphBuildingInterface {
builder_->TableSet(imm.index, index.node, value.node, decoder->position()); builder_->TableSet(imm.index, index.node, value.node, decoder->position());
} }
void Unreachable(FullDecoder* decoder) { void Trap(FullDecoder* decoder, TrapReason reason) {
ValueVector values; ValueVector values;
if (FLAG_wasm_loop_unrolling) { if (FLAG_wasm_loop_unrolling) {
BuildNestedLoopExits(decoder, decoder->control_depth() - 1, false, BuildNestedLoopExits(decoder, decoder->control_depth() - 1, false,
values); values);
} }
builder_->Trap(wasm::TrapReason::kTrapUnreachable, decoder->position()); builder_->Trap(reason, decoder->position());
}
void AssertNull(FullDecoder* decoder, const Value& obj, Value* result) {
builder_->TrapIfFalse(
wasm::TrapReason::kTrapIllegalCast,
builder_->Binop(kExprRefEq, obj.node, builder_->RefNull(),
decoder->position()),
decoder->position());
result->node = obj.node;
} }
void NopForTestingUnsupportedInLiftoff(FullDecoder* decoder) {} void NopForTestingUnsupportedInLiftoff(FullDecoder* decoder) {}
...@@ -631,15 +640,15 @@ class WasmGraphBuildingInterface { ...@@ -631,15 +640,15 @@ class WasmGraphBuildingInterface {
} }
void BrOnNull(FullDecoder* decoder, const Value& ref_object, uint32_t depth) { void BrOnNull(FullDecoder* decoder, const Value& ref_object, uint32_t depth) {
SsaEnv* non_null_env = ssa_env_; SsaEnv* false_env = ssa_env_;
SsaEnv* null_env = Split(decoder->zone(), non_null_env); SsaEnv* true_env = Split(decoder->zone(), false_env);
non_null_env->SetNotMerged(); false_env->SetNotMerged();
builder_->BrOnNull(ref_object.node, &null_env->control, builder_->BrOnNull(ref_object.node, &true_env->control,
&non_null_env->control); &false_env->control);
builder_->SetControl(non_null_env->control); builder_->SetControl(false_env->control);
SetEnv(null_env); SetEnv(true_env);
BrOrRet(decoder, depth, 1); BrOrRet(decoder, depth, 1);
SetEnv(non_null_env); SetEnv(false_env);
} }
void SimdOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args, void SimdOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args,
......
...@@ -863,6 +863,105 @@ WASM_COMPILED_EXEC_TEST(BasicRtt) { ...@@ -863,6 +863,105 @@ WASM_COMPILED_EXEC_TEST(BasicRtt) {
tester.CheckResult(kRefCast, 43); tester.CheckResult(kRefCast, 43);
} }
WASM_COMPILED_EXEC_TEST(RefTrivialCasts) {
WasmGCTester tester(execution_tier);
byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
byte subtype_index =
tester.DefineStruct({F(wasm::kWasmI32, true), F(wasm::kWasmS128, false)});
ValueType sig_types[] = {kWasmS128, kWasmI32, kWasmF64};
FunctionSig sig(1, 2, sig_types);
byte sig_index = tester.DefineSignature(&sig);
const byte kRefTestNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST(WASM_REF_NULL(type_index), WASM_RTT_CANON(subtype_index)),
kExprEnd});
const byte kRefTestUpcast = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST(
WASM_STRUCT_NEW_DEFAULT(
subtype_index,
WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index))),
WASM_RTT_CANON(type_index)),
kExprEnd});
const byte kRefTestUpcastNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST(WASM_REF_NULL(subtype_index), WASM_RTT_CANON(type_index)),
kExprEnd});
const byte kRefTestUnrelated = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST(
WASM_STRUCT_NEW_DEFAULT(
subtype_index,
WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index))),
WASM_RTT_CANON(sig_index)),
kExprEnd});
const byte kRefTestUnrelatedNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST(WASM_REF_NULL(subtype_index), WASM_RTT_CANON(sig_index)),
kExprEnd});
const byte kRefTestUnrelatedNonNullable = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST(
WASM_STRUCT_NEW_DEFAULT(type_index, WASM_RTT_CANON(type_index)),
WASM_RTT_CANON(sig_index)),
kExprEnd});
const byte kRefCastNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST(WASM_REF_NULL(type_index),
WASM_RTT_CANON(subtype_index))),
kExprEnd});
const byte kRefCastUpcast = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST(
WASM_STRUCT_NEW_DEFAULT(
subtype_index,
WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index))),
WASM_RTT_CANON(type_index))),
kExprEnd});
const byte kRefCastUpcastNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST(WASM_REF_NULL(subtype_index),
WASM_RTT_CANON(type_index))),
kExprEnd});
const byte kRefCastUnrelated = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST(
WASM_STRUCT_NEW_DEFAULT(
subtype_index,
WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index))),
WASM_RTT_CANON(sig_index))),
kExprEnd});
const byte kRefCastUnrelatedNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST(WASM_REF_NULL(subtype_index),
WASM_RTT_CANON(sig_index))),
kExprEnd});
const byte kRefCastUnrelatedNonNullable = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST(
WASM_STRUCT_NEW_DEFAULT(type_index, WASM_RTT_CANON(type_index)),
WASM_RTT_CANON(sig_index))),
kExprEnd});
tester.CompileModule();
tester.CheckResult(kRefTestNull, 0);
tester.CheckResult(kRefTestUpcast, 1);
tester.CheckResult(kRefTestUpcastNull, 0);
tester.CheckResult(kRefTestUnrelated, 0);
tester.CheckResult(kRefTestUnrelatedNull, 0);
tester.CheckResult(kRefTestUnrelatedNonNullable, 0);
tester.CheckResult(kRefCastNull, 1);
tester.CheckResult(kRefCastUpcast, 0);
tester.CheckResult(kRefCastUpcastNull, 1);
tester.CheckHasThrown(kRefCastUnrelated);
tester.CheckResult(kRefCastUnrelatedNull, 1);
tester.CheckHasThrown(kRefCastUnrelatedNonNullable);
}
WASM_EXEC_TEST(NoDepthRtt) { WASM_EXEC_TEST(NoDepthRtt) {
WasmGCTester tester(execution_tier); WasmGCTester tester(execution_tier);
...@@ -871,14 +970,19 @@ WASM_EXEC_TEST(NoDepthRtt) { ...@@ -871,14 +970,19 @@ WASM_EXEC_TEST(NoDepthRtt) {
tester.DefineStruct({F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)}); tester.DefineStruct({F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)});
const byte empty_struct_index = tester.DefineStruct({}); const byte empty_struct_index = tester.DefineStruct({});
ValueType kRttTypeNoDepth = ValueType::Rtt(type_index);
FunctionSig sig_t1_v_nd(1, 0, &kRttTypeNoDepth);
ValueType kRttSubtypeNoDepth = ValueType::Rtt(subtype_index); ValueType kRttSubtypeNoDepth = ValueType::Rtt(subtype_index);
FunctionSig sig_t2_v_nd(1, 0, &kRttSubtypeNoDepth); FunctionSig sig_t2_v_nd(1, 0, &kRttSubtypeNoDepth);
const byte kRttTypeCanon = tester.DefineFunction(
&sig_t1_v_nd, {}, {WASM_RTT_CANON(type_index), kExprEnd});
const byte kRttSubtypeCanon = tester.DefineFunction( const byte kRttSubtypeCanon = tester.DefineFunction(
&sig_t2_v_nd, {}, {WASM_RTT_CANON(subtype_index), kExprEnd}); &sig_t2_v_nd, {}, {WASM_RTT_CANON(subtype_index), kExprEnd});
const byte kRttSubtypeSub = tester.DefineFunction( const byte kRttSubtypeSub = tester.DefineFunction(
&sig_t2_v_nd, {}, &sig_t2_v_nd, {},
{WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index)), kExprEnd}); {WASM_RTT_SUB(subtype_index, WASM_CALL_FUNCTION0(kRttTypeCanon)),
kExprEnd});
const byte kTestCanon = tester.DefineFunction( const byte kTestCanon = tester.DefineFunction(
tester.sigs.i_v(), {optref(type_index)}, tester.sigs.i_v(), {optref(type_index)},
...@@ -1059,25 +1163,6 @@ WASM_COMPILED_EXEC_TEST(CallRef) { ...@@ -1059,25 +1163,6 @@ WASM_COMPILED_EXEC_TEST(CallRef) {
tester.CheckResult(caller, 47, 5); tester.CheckResult(caller, 47, 5);
} }
WASM_COMPILED_EXEC_TEST(RefTestCastNull) {
WasmGCTester tester(execution_tier);
byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
const byte kRefTestNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST(WASM_REF_NULL(type_index), WASM_RTT_CANON(type_index)),
kExprEnd});
const byte kRefCastNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST(WASM_REF_NULL(type_index),
WASM_RTT_CANON(type_index))),
kExprEnd});
tester.CompileModule();
tester.CheckResult(kRefTestNull, 0);
tester.CheckResult(kRefCastNull, 1);
}
WASM_COMPILED_EXEC_TEST(AbstractTypeChecks) { WASM_COMPILED_EXEC_TEST(AbstractTypeChecks) {
WasmGCTester tester(execution_tier); WasmGCTester tester(execution_tier);
......
...@@ -4269,17 +4269,18 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) { ...@@ -4269,17 +4269,18 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
HeapType::Representation func_heap_2 = HeapType::Representation func_heap_2 =
static_cast<HeapType::Representation>(builder.AddSignature(sigs.i_v())); static_cast<HeapType::Representation>(builder.AddSignature(sigs.i_v()));
// Passing/failing tests due to static subtyping.
std::tuple<HeapType::Representation, HeapType::Representation, bool> tests[] = std::tuple<HeapType::Representation, HeapType::Representation, bool> tests[] =
{std::make_tuple(HeapType::kData, array_heap, true), {std::make_tuple(HeapType::kData, array_heap, true),
std::make_tuple(HeapType::kData, super_struct_heap, true), std::make_tuple(HeapType::kData, super_struct_heap, true),
std::make_tuple(HeapType::kFunc, func_heap_1, true), std::make_tuple(HeapType::kFunc, func_heap_1, true),
std::make_tuple(func_heap_1, func_heap_1, true), std::make_tuple(func_heap_1, func_heap_1, true),
std::make_tuple(func_heap_1, func_heap_2, false), std::make_tuple(func_heap_1, func_heap_2, true),
std::make_tuple(super_struct_heap, sub_struct_heap, true), std::make_tuple(super_struct_heap, sub_struct_heap, true),
std::make_tuple(sub_struct_heap, super_struct_heap, false), std::make_tuple(array_heap, sub_struct_heap, true),
std::make_tuple(sub_struct_heap, array_heap, false), std::make_tuple(super_struct_heap, func_heap_1, true),
std::make_tuple(HeapType::kFunc, array_heap, false)}; std::make_tuple(HeapType::kEq, super_struct_heap, false),
std::make_tuple(HeapType::kAny, func_heap_1, false),
std::make_tuple(HeapType::kI31, array_heap, false)};
for (auto test : tests) { for (auto test : tests) {
HeapType from_heap = HeapType(std::get<0>(test)); HeapType from_heap = HeapType(std::get<0>(test));
...@@ -4308,10 +4309,10 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) { ...@@ -4308,10 +4309,10 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
ExpectValidates(&cast_sig, ExpectValidates(&cast_sig,
{WASM_REF_CAST(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1))}); {WASM_REF_CAST(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1))});
} else { } else {
std::string error_message = "[0] expected supertype of type " + std::string error_message =
std::to_string(to_heap.ref_index()) + "[0] expected subtype of (ref null func) or (ref null data), found "
", found local.get of type " + "local.get of type " +
test_reps[1].name(); test_reps[1].name();
ExpectFailure(&test_sig, ExpectFailure(&test_sig,
{WASM_REF_TEST(WASM_LOCAL_GET(0), {WASM_REF_TEST(WASM_LOCAL_GET(0),
WASM_RTT_CANON(WASM_HEAP_TYPE(to_heap)))}, WASM_RTT_CANON(WASM_HEAP_TYPE(to_heap)))},
...@@ -4339,20 +4340,27 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) { ...@@ -4339,20 +4340,27 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
kAppendEnd, kAppendEnd,
"ref.cast[0] expected subtype of (ref null func) or (ref null data), " "ref.cast[0] expected subtype of (ref null func) or (ref null data), "
"found i32.const of type i32"); "found i32.const of type i32");
}
TEST_F(FunctionBodyDecoderTest, LocalTeeTyping) {
WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
TestModuleBuilder builder;
module = builder.module();
byte array_type = builder.AddArray(kWasmI8, true);
ValueType types[] = {ValueType::Ref(array_type, kNonNullable)};
FunctionSig sig(1, 0, types);
AddLocals(ValueType::Ref(array_type, kNullable), 1);
// Trivial type error.
ExpectFailure(
sigs.v_v(),
{WASM_REF_TEST(WASM_I32V(1), WASM_RTT_CANON(array_heap)), kExprDrop},
kAppendEnd,
"ref.test[0] expected subtype of (ref null func) or (ref null data), "
"found i32.const of type i32");
ExpectFailure( ExpectFailure(
sigs.v_v(), &sig,
{WASM_REF_CAST(WASM_I32V(1), WASM_RTT_CANON(array_heap)), kExprDrop}, {WASM_LOCAL_TEE(0, WASM_ARRAY_NEW_DEFAULT(array_type, WASM_I32V(5),
kAppendEnd, WASM_RTT_CANON(array_type)))},
"ref.cast[0] expected subtype of (ref null func) or (ref null data), " kAppendEnd, "expected (ref 0), got (ref null 0)");
"found i32.const of type i32");
} }
// This tests that num_locals_ in decoder remains consistent, even if we fail // This tests that num_locals_ in decoder remains consistent, even if we fail
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment