Commit fc912616 authored by Sathya Gunasekaran's avatar Sathya Gunasekaran Committed by V8 LUCI CQ

Revert "[wasm-gc] Implement br_on_cast_fail"

This reverts commit 8f39a585.

Reason for revert: https://ci.chromium.org/ui/p/v8/builders/ci/V8%20Win64%20-%20msvc/17874/blamelist

Original change's description:
> [wasm-gc] Implement br_on_cast_fail
>
> Bug: v8:7748
> Change-Id: I7894ad51ccf8ac41a5081c272a583a4ff25c1835
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2900225
> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#74652}

Bug: v8:7748
Change-Id: I90ca2d789e943cd00c2344e2d333c9175fcedee5
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2903482
Auto-Submit: Sathya Gunasekaran  <gsathya@chromium.org>
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/heads/master@{#74658}
parent 2e09d7eb
...@@ -5209,7 +5209,7 @@ class LiftoffCompiler { ...@@ -5209,7 +5209,7 @@ class LiftoffCompiler {
} }
void BrOnCast(FullDecoder* decoder, const Value& obj, const Value& rtt, void BrOnCast(FullDecoder* decoder, const Value& obj, const Value& rtt,
Value* /* result_on_branch */, uint32_t depth) { Value* result_on_branch, uint32_t depth) {
// Before branching, materialize all constants. This avoids repeatedly // Before branching, materialize all constants. This avoids repeatedly
// materializing them for each conditional branch. // materializing them for each conditional branch.
if (depth != decoder->control_depth() - 1) { if (depth != decoder->control_depth() - 1) {
...@@ -5230,27 +5230,6 @@ class LiftoffCompiler { ...@@ -5230,27 +5230,6 @@ class LiftoffCompiler {
__ PushRegister(obj.type.kind(), obj_reg); __ PushRegister(obj.type.kind(), obj_reg);
} }
void BrOnCastFail(FullDecoder* decoder, const Value& obj, const Value& rtt,
Value* /* result_on_fallthrough */, uint32_t depth) {
// Before branching, materialize all constants. This avoids repeatedly
// materializing them for each conditional branch.
if (depth != decoder->control_depth() - 1) {
__ MaterializeMergedConstants(
decoder->control_at(depth)->br_merge()->arity);
}
Label cont_branch, fallthrough;
LiftoffRegister obj_reg =
SubtypeCheck(decoder, obj, rtt, &cont_branch, kNullFails);
__ PushRegister(obj.type.kind(), obj_reg);
__ emit_jump(&fallthrough);
__ bind(&cont_branch);
BrOrRet(decoder, depth, 0);
__ bind(&fallthrough);
}
// Abstract type checkers. They all return the object register and fall // Abstract type checkers. They all return the object register and fall
// through to match. // through to match.
LiftoffRegister DataCheck(const Value& obj, Label* no_match, LiftoffRegister DataCheck(const Value& obj, Label* no_match,
......
...@@ -1143,8 +1143,6 @@ struct ControlBase : public PcForErrors<validate> { ...@@ -1143,8 +1143,6 @@ struct ControlBase : public PcForErrors<validate> {
F(AssertNull, const Value& obj, 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(BrOnCastFail, const Value& obj, const Value& rtt, \
Value* result_on_fallthrough, uint32_t depth) \
F(RefIsData, const Value& object, Value* result) \ F(RefIsData, const Value& object, Value* result) \
F(RefAsData, const Value& object, Value* result) \ F(RefAsData, const Value& object, Value* result) \
F(BrOnData, const Value& object, Value* value_on_branch, uint32_t br_depth) \ F(BrOnData, const Value& object, Value* value_on_branch, uint32_t br_depth) \
...@@ -1930,7 +1928,6 @@ class WasmDecoder : public Decoder { ...@@ -1930,7 +1928,6 @@ class WasmDecoder : public Decoder {
return length + imm.length; return length + imm.length;
} }
case kExprBrOnCast: case kExprBrOnCast:
case kExprBrOnCastFail:
case kExprBrOnData: case kExprBrOnData:
case kExprBrOnFunc: case kExprBrOnFunc:
case kExprBrOnI31: { case kExprBrOnI31: {
...@@ -2112,7 +2109,6 @@ class WasmDecoder : public Decoder { ...@@ -2112,7 +2109,6 @@ class WasmDecoder : public Decoder {
case kExprRefTest: case kExprRefTest:
case kExprRefCast: case kExprRefCast:
case kExprBrOnCast: case kExprBrOnCast:
case kExprBrOnCastFail:
return {2, 1}; return {2, 1};
case kExprArraySet: case kExprArraySet:
return {3, 0}; return {3, 0};
...@@ -4359,7 +4355,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -4359,7 +4355,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
// significantly more convenient to pass around the values that // significantly more convenient to pass around the values that
// will be on the stack when the branch is taken. // will be on the stack when the branch is taken.
// TODO(jkummerow): Reconsider this choice. // TODO(jkummerow): Reconsider this choice.
Drop(2); // {obj} and {rtt}. Drop(2); // {obj} and {ret}.
Value result_on_branch = CreateValue( Value result_on_branch = CreateValue(
rtt.type.is_bottom() rtt.type.is_bottom()
? kWasmBottom ? kWasmBottom
...@@ -4384,67 +4380,6 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -4384,67 +4380,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
Push(obj); // Restore stack state on fallthrough. Push(obj); // Restore stack state on fallthrough.
return opcode_length + branch_depth.length; return opcode_length + branch_depth.length;
} }
case kExprBrOnCastFail: {
BranchDepthImmediate<validate> branch_depth(this,
this->pc_ + opcode_length);
if (!this->Validate(this->pc_ + opcode_length, branch_depth,
control_.size())) {
return 0;
}
Value rtt = Peek(0, 1);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(1, rtt, "rtt");
return 0;
}
Value obj = Peek(1, 0);
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
IsSubtypeOf(obj.type,
ValueType::Ref(HeapType::kData, kNullable),
this->module_) ||
obj.type.is_bottom())) {
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
return 0;
}
Control* c = control_at(branch_depth.depth);
if (c->br_merge()->arity == 0) {
this->DecodeError(
"br_on_cast_fail must target a branch of arity at least 1");
return 0;
}
// Attention: contrary to most other instructions, we modify the stack
// before calling the interface function. This makes it significantly
// more convenient to pass around the values that will be on the stack
// when the branch is taken. In this case, we leave {obj} on the stack
// to type check the branch.
// TODO(jkummerow): Reconsider this choice.
Drop(rtt);
if (!VALIDATE(TypeCheckBranch<true>(c, 0))) return 0;
Value result_on_fallthrough = CreateValue(
rtt.type.is_bottom()
? kWasmBottom
: ValueType::Ref(rtt.type.ref_index(), kNonNullable));
// 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(current_code_reachable_and_ok_)) {
if (V8_LIKELY(ObjectRelatedWithRtt(obj, rtt))) {
CALL_INTERFACE(BrOnCastFail, obj, rtt, &result_on_fallthrough,
branch_depth.depth);
} else {
// Drop {rtt} in the interface.
CALL_INTERFACE(Drop);
// Otherwise the types are unrelated. Always branch.
CALL_INTERFACE(BrOrRet, branch_depth.depth, 0);
// We know that the following code is not reachable, but according
// to the spec it technically is. Set it to spec-only reachable.
SetSucceedingCodeDynamicallyUnreachable();
}
c->br_merge()->reached = true;
}
// Make sure the correct value is on the stack state on fallthrough.
Drop(obj);
Push(result_on_fallthrough);
return opcode_length + branch_depth.length;
}
#define ABSTRACT_TYPE_CHECK(heap_type) \ #define ABSTRACT_TYPE_CHECK(heap_type) \
case kExprRefIs##heap_type: { \ case kExprRefIs##heap_type: { \
Value arg = Peek(0, 0, kWasmAnyRef); \ Value arg = Peek(0, 0, kWasmAnyRef); \
......
...@@ -1037,37 +1037,28 @@ class WasmGraphBuildingInterface { ...@@ -1037,37 +1037,28 @@ class WasmGraphBuildingInterface {
TFNode*, TFNode*, StaticKnowledge, TFNode**, TFNode**, TFNode**, TFNode*, TFNode*, StaticKnowledge, TFNode**, TFNode**, TFNode**,
TFNode**)> TFNode**)>
void BrOnCastAbs(FullDecoder* decoder, const Value& object, const Value& rtt, void BrOnCastAbs(FullDecoder* decoder, const Value& object, const Value& rtt,
Value* forwarding_value, uint32_t br_depth, Value* value_on_branch, uint32_t br_depth) {
bool branch_on_match) {
StaticKnowledge config = StaticKnowledge config =
ComputeStaticKnowledge(object.type, rtt.type, decoder->module_); ComputeStaticKnowledge(object.type, rtt.type, decoder->module_);
SsaEnv* branch_env = Split(decoder->zone(), ssa_env_); SsaEnv* match_env = Split(decoder->zone(), ssa_env_);
SsaEnv* no_branch_env = Steal(decoder->zone(), ssa_env_); SsaEnv* no_match_env = Steal(decoder->zone(), ssa_env_);
no_branch_env->SetNotMerged(); no_match_env->SetNotMerged();
SsaEnv* match_env = branch_on_match ? branch_env : no_branch_env;
SsaEnv* no_match_env = branch_on_match ? no_branch_env : branch_env;
(builder_->*branch_function)(object.node, rtt.node, config, (builder_->*branch_function)(object.node, rtt.node, config,
&match_env->control, &match_env->effect, &match_env->control, &match_env->effect,
&no_match_env->control, &no_match_env->effect); &no_match_env->control, &no_match_env->effect);
builder_->SetControl(no_branch_env->control); builder_->SetControl(no_match_env->control);
SetEnv(branch_env); SetEnv(match_env);
forwarding_value->node = object.node; value_on_branch->node = object.node;
// Currently, br_on_* instructions modify the value stack before calling // Currently, br_on_* instructions modify the value stack before calling
// the interface function, so we don't need to drop any values here. // the interface function, so we don't need to drop any values here.
BrOrRet(decoder, br_depth, 0); BrOrRet(decoder, br_depth, 0);
SetEnv(no_branch_env); SetEnv(no_match_env);
} }
void BrOnCast(FullDecoder* decoder, const Value& object, const Value& rtt, void BrOnCast(FullDecoder* decoder, const Value& object, const Value& rtt,
Value* value_on_branch, uint32_t br_depth) { Value* value_on_branch, uint32_t br_depth) {
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnCast>( BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnCast>(
decoder, object, rtt, value_on_branch, br_depth, true); decoder, object, rtt, value_on_branch, br_depth);
}
void BrOnCastFail(FullDecoder* decoder, const Value& object, const Value& rtt,
Value* value_on_fallthrough, uint32_t br_depth) {
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnCast>(
decoder, object, rtt, value_on_fallthrough, br_depth, false);
} }
void RefIsData(FullDecoder* decoder, const Value& object, Value* result) { void RefIsData(FullDecoder* decoder, const Value& object, Value* result) {
...@@ -1082,8 +1073,8 @@ class WasmGraphBuildingInterface { ...@@ -1082,8 +1073,8 @@ class WasmGraphBuildingInterface {
void BrOnData(FullDecoder* decoder, const Value& object, void BrOnData(FullDecoder* decoder, const Value& object,
Value* value_on_branch, uint32_t br_depth) { Value* value_on_branch, uint32_t br_depth) {
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnData>( BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnData>(
decoder, object, Value{nullptr, kWasmBottom}, value_on_branch, br_depth, decoder, object, Value{nullptr, kWasmBottom}, value_on_branch,
true); br_depth);
} }
void RefIsFunc(FullDecoder* decoder, const Value& object, Value* result) { void RefIsFunc(FullDecoder* decoder, const Value& object, Value* result) {
...@@ -1098,8 +1089,8 @@ class WasmGraphBuildingInterface { ...@@ -1098,8 +1089,8 @@ class WasmGraphBuildingInterface {
void BrOnFunc(FullDecoder* decoder, const Value& object, void BrOnFunc(FullDecoder* decoder, const Value& object,
Value* value_on_branch, uint32_t br_depth) { Value* value_on_branch, uint32_t br_depth) {
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnFunc>( BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnFunc>(
decoder, object, Value{nullptr, kWasmBottom}, value_on_branch, br_depth, decoder, object, Value{nullptr, kWasmBottom}, value_on_branch,
true); br_depth);
} }
void RefIsI31(FullDecoder* decoder, const Value& object, Value* result) { void RefIsI31(FullDecoder* decoder, const Value& object, Value* result) {
...@@ -1113,8 +1104,8 @@ class WasmGraphBuildingInterface { ...@@ -1113,8 +1104,8 @@ class WasmGraphBuildingInterface {
void BrOnI31(FullDecoder* decoder, const Value& object, void BrOnI31(FullDecoder* decoder, const Value& object,
Value* value_on_branch, uint32_t br_depth) { Value* value_on_branch, uint32_t br_depth) {
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnI31>( BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnI31>(
decoder, object, Value{nullptr, kWasmBottom}, value_on_branch, br_depth, decoder, object, Value{nullptr, kWasmBottom}, value_on_branch,
true); br_depth);
} }
void Forward(FullDecoder* decoder, const Value& from, Value* to) { void Forward(FullDecoder* decoder, const Value& from, Value* to) {
......
...@@ -401,7 +401,6 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { ...@@ -401,7 +401,6 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(RefTest, "ref.test") CASE_OP(RefTest, "ref.test")
CASE_OP(RefCast, "ref.cast") CASE_OP(RefCast, "ref.cast")
CASE_OP(BrOnCast, "br_on_cast") CASE_OP(BrOnCast, "br_on_cast")
CASE_OP(BrOnCastFail, "br_on_cast_fail")
CASE_OP(RefIsFunc, "ref.is_func") CASE_OP(RefIsFunc, "ref.is_func")
CASE_OP(RefIsData, "ref.is_data") CASE_OP(RefIsData, "ref.is_data")
CASE_OP(RefIsI31, "ref.is_i31") CASE_OP(RefIsI31, "ref.is_i31")
......
...@@ -671,7 +671,6 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig, ...@@ -671,7 +671,6 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(RefTest, 0xfb40, _) \ V(RefTest, 0xfb40, _) \
V(RefCast, 0xfb41, _) \ V(RefCast, 0xfb41, _) \
V(BrOnCast, 0xfb42, _) \ V(BrOnCast, 0xfb42, _) \
V(BrOnCastFail, 0xfb43, _) \
V(RefIsFunc, 0xfb50, _) \ V(RefIsFunc, 0xfb50, _) \
V(RefIsData, 0xfb51, _) \ V(RefIsData, 0xfb51, _) \
V(RefIsI31, 0xfb52, _) \ V(RefIsI31, 0xfb52, _) \
......
...@@ -457,61 +457,6 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) { ...@@ -457,61 +457,6 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
tester.CheckResult(kTypedAfterBranch, 42); tester.CheckResult(kTypedAfterBranch, 42);
} }
WASM_COMPILED_EXEC_TEST(BrOnCastFail) {
WasmGCTester tester(execution_tier);
ValueType kDataRefNull = ValueType::Ref(HeapType::kData, kNullable);
const byte type0 = tester.DefineStruct({F(kWasmI32, true)});
const byte type1 =
tester.DefineStruct({F(kWasmI64, true), F(kWasmI32, true)});
const byte field0 = 5;
const byte field1 = 25;
const byte null_value = 45;
// local_0 = value;
// if (!(local_0 instanceof type0)) goto block1;
// return static_cast<type0>(local_0).field_0;
// block1:
// if (local_0 == nullptr) goto block2;
// return static_cast<type1>(local_0).field_1;
// block2:
// return null_value;
#define FUNCTION_BODY(value) \
WASM_LOCAL_SET(0, WASM_SEQ(value)), \
WASM_BLOCK( \
WASM_BLOCK_R(kDataRefNull, WASM_LOCAL_GET(0), \
WASM_BR_ON_CAST_FAIL(0, WASM_RTT_CANON(type0)), \
WASM_GC_OP(kExprStructGet), type0, 0, kExprReturn), \
kExprBrOnNull, 0, WASM_RTT_CANON(type1), WASM_GC_OP(kExprRefCast), \
WASM_GC_OP(kExprStructGet), type1, 1, kExprReturn), \
WASM_I32V(null_value), kExprEnd
const byte kBranchTaken = tester.DefineFunction(
tester.sigs.i_v(), {kDataRefNull},
{FUNCTION_BODY(WASM_STRUCT_NEW_WITH_RTT(
type1, WASM_I64V(10), WASM_I32V(field1), WASM_RTT_CANON(type1)))});
const byte kBranchNotTaken = tester.DefineFunction(
tester.sigs.i_v(), {kDataRefNull},
{FUNCTION_BODY(WASM_STRUCT_NEW_WITH_RTT(type0, WASM_I32V(field0),
WASM_RTT_CANON(type0)))});
const byte kNull = tester.DefineFunction(
tester.sigs.i_v(), {kDataRefNull}, {FUNCTION_BODY(WASM_REF_NULL(type0))});
const byte kUnrelatedTypes = tester.DefineFunction(
tester.sigs.i_v(), {ValueType::Ref(type1, kNullable)},
{FUNCTION_BODY(WASM_STRUCT_NEW_WITH_RTT(
type1, WASM_I64V(10), WASM_I32V(field1), WASM_RTT_CANON(type1)))});
#undef FUNCTION_BODY
tester.CompileModule();
tester.CheckResult(kBranchTaken, field1);
tester.CheckResult(kBranchNotTaken, field0);
tester.CheckResult(kNull, null_value);
tester.CheckResult(kUnrelatedTypes, field1);
}
WASM_COMPILED_EXEC_TEST(WasmRefEq) { WASM_COMPILED_EXEC_TEST(WasmRefEq) {
WasmGCTester tester(execution_tier); WasmGCTester tester(execution_tier);
byte type_index = tester.DefineStruct({F(kWasmI32, true), F(kWasmI32, true)}); byte type_index = tester.DefineStruct({F(kWasmI32, true), F(kWasmI32, true)});
......
...@@ -519,8 +519,6 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -519,8 +519,6 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
// conditional branches. // conditional branches.
#define WASM_BR_ON_CAST(depth, rtt) \ #define WASM_BR_ON_CAST(depth, rtt) \
rtt, WASM_GC_OP(kExprBrOnCast), static_cast<byte>(depth) rtt, WASM_GC_OP(kExprBrOnCast), static_cast<byte>(depth)
#define WASM_BR_ON_CAST_FAIL(depth, rtt) \
rtt, WASM_GC_OP(kExprBrOnCastFail), static_cast<byte>(depth)
#define WASM_REF_IS_DATA(ref) ref, WASM_GC_OP(kExprRefIsData) #define WASM_REF_IS_DATA(ref) ref, WASM_GC_OP(kExprRefIsData)
#define WASM_REF_AS_DATA(ref) ref, WASM_GC_OP(kExprRefAsData) #define WASM_REF_AS_DATA(ref) ref, WASM_GC_OP(kExprRefAsData)
......
...@@ -466,7 +466,6 @@ let kExprRttSub = 0x31; ...@@ -466,7 +466,6 @@ let kExprRttSub = 0x31;
let kExprRefTest = 0x40; let kExprRefTest = 0x40;
let kExprRefCast = 0x41; let kExprRefCast = 0x41;
let kExprBrOnCast = 0x42; let kExprBrOnCast = 0x42;
let kExprBrOnCastFail = 0x43;
let kExprRefIsFunc = 0x50; let kExprRefIsFunc = 0x50;
let kExprRefIsData = 0x51; let kExprRefIsData = 0x51;
let kExprRefIsI31 = 0x52; let kExprRefIsI31 = 0x52;
......
...@@ -4331,6 +4331,7 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) { ...@@ -4331,6 +4331,7 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
WASM_FEATURE_SCOPE(reftypes); WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref); WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc); WASM_FEATURE_SCOPE(gc);
WASM_FEATURE_SCOPE(eh);
TestModuleBuilder builder; TestModuleBuilder builder;
module = builder.module(); module = builder.module();
...@@ -4423,77 +4424,6 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) { ...@@ -4423,77 +4424,6 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
"found i32.const of type i32"); "found i32.const of type i32");
} }
TEST_F(FunctionBodyDecoderTest, BrOnCastOrCastFail) {
WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
TestModuleBuilder builder;
module = builder.module();
byte super_struct = builder.AddStruct({F(kWasmI16, true)});
byte sub_struct = builder.AddStruct({F(kWasmI16, true), F(kWasmI32, false)});
ValueType supertype = ValueType::Ref(super_struct, kNullable);
ValueType subtype = ValueType::Ref(sub_struct, kNullable);
ExpectValidates(
FunctionSig::Build(this->zone(), {kWasmI32, subtype}, {supertype}),
{WASM_I32V(42), WASM_LOCAL_GET(0),
WASM_BR_ON_CAST(0, WASM_RTT_CANON(sub_struct)),
WASM_RTT_CANON(sub_struct), WASM_GC_OP(kExprRefCast)});
ExpectValidates(
FunctionSig::Build(this->zone(), {kWasmI32, supertype}, {supertype}),
{WASM_I32V(42), WASM_LOCAL_GET(0),
WASM_BR_ON_CAST_FAIL(0, WASM_RTT_CANON(sub_struct))});
// Wrong branch type.
ExpectFailure(
FunctionSig::Build(this->zone(), {}, {supertype}),
{WASM_LOCAL_GET(0), WASM_BR_ON_CAST(0, WASM_RTT_CANON(sub_struct)),
WASM_UNREACHABLE},
kAppendEnd, "br_on_cast must target a branch of arity at least 1");
ExpectFailure(
FunctionSig::Build(this->zone(), {subtype}, {supertype}),
{WASM_I32V(42), WASM_LOCAL_GET(0),
WASM_BR_ON_CAST_FAIL(0, WASM_RTT_CANON(sub_struct))},
kAppendEnd,
"type error in branch[0] (expected (ref null 1), got (ref null 0))");
// Wrong fallthrough type.
ExpectFailure(
FunctionSig::Build(this->zone(), {subtype}, {supertype}),
{WASM_LOCAL_GET(0), WASM_BR_ON_CAST(0, WASM_RTT_CANON(sub_struct))},
kAppendEnd,
"type error in fallthru[0] (expected (ref null 1), got (ref null 0))");
ExpectFailure(
FunctionSig::Build(this->zone(), {supertype}, {supertype}),
{WASM_BLOCK_I(WASM_LOCAL_GET(0),
WASM_BR_ON_CAST_FAIL(0, WASM_RTT_CANON(sub_struct)))},
kAppendEnd, "type error in branch[0] (expected i32, got (ref null 0))");
// Argument type error.
ExpectFailure(
FunctionSig::Build(this->zone(), {subtype}, {kWasmAnyRef}),
{WASM_LOCAL_GET(0), WASM_BR_ON_CAST(0, WASM_RTT_CANON(sub_struct)),
WASM_RTT_CANON(sub_struct), WASM_GC_OP(kExprRefCast)},
kAppendEnd,
"br_on_cast[0] expected subtype of (ref null func) or (ref null data), "
"found local.get of type anyref");
ExpectFailure(
FunctionSig::Build(this->zone(), {supertype}, {kWasmAnyRef}),
{WASM_LOCAL_GET(0), WASM_BR_ON_CAST_FAIL(0, WASM_RTT_CANON(sub_struct))},
kAppendEnd,
"br_on_cast_fail[0] expected subtype of (ref null func) or (ref null "
"data), found local.get of type anyref");
// Trivial rtt type error.
ExpectFailure(FunctionSig::Build(this->zone(), {supertype}, {supertype}),
{WASM_LOCAL_GET(0), WASM_BR_ON_CAST_FAIL(0, WASM_I64V(42))},
kAppendEnd,
"br_on_cast_fail[1] expected rtt, found i64.const of type i64");
}
TEST_F(FunctionBodyDecoderTest, LocalTeeTyping) { TEST_F(FunctionBodyDecoderTest, LocalTeeTyping) {
WASM_FEATURE_SCOPE(reftypes); WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref); WASM_FEATURE_SCOPE(typed_funcref);
......
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