Commit 49e6633e authored by Manos Koukoutos's avatar Manos Koukoutos Committed by V8 LUCI CQ

[wasm-gc] Fix bugs and improve tests for br_on_cast(_fail)

There were multiple bugs and no test coverage for br_on_cast and br_on_cast_fail, specifically for the paths in the decoder where those
checks get optimized away.

Bug: v8:7748
Change-Id: I6e5d6449152df0456b43938174f57055a4c63fdd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3723503Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81349}
parent d22b3c96
......@@ -3403,18 +3403,19 @@ class LiftoffCompiler {
Label cont_false;
LiftoffRegList pinned;
LiftoffRegister ref = pinned.set(__ PopToRegister(pinned));
Register null = __ GetUnusedRegister(kGpReg, pinned).gp();
LoadNullValue(null, pinned);
LiftoffRegister null = __ GetUnusedRegister(kGpReg, pinned);
LoadNullValue(null.gp(), pinned);
__ emit_cond_jump(kUnequal, &cont_false, ref_object.type.kind(), ref.gp(),
null);
if (pass_null_along_branch) LoadNullValue(null, pinned);
null.gp());
if (pass_null_along_branch) __ PushRegister(kOptRef, null);
BrOrRet(decoder, depth, 0);
__ bind(&cont_false);
__ PushRegister(kRef, ref);
}
void BrOnNonNull(FullDecoder* decoder, const Value& ref_object,
Value* /* result */, uint32_t depth) {
Value* /* result */, uint32_t depth,
bool drop_null_on_fallthrough) {
// Before branching, materialize all constants. This avoids repeatedly
// materializing them for each conditional branch.
if (depth != decoder->control_depth() - 1) {
......@@ -3435,7 +3436,7 @@ class LiftoffCompiler {
BrOrRet(decoder, depth, 0);
// Drop the reference if we are not branching.
__ DropValues(1);
if (drop_null_on_fallthrough) __ DropValues(1);
__ bind(&cont_false);
}
......
......@@ -1052,7 +1052,8 @@ struct ControlBase : public PcForErrors<validate> {
const CallIndirectImmediate<validate>& imm, const Value args[]) \
F(BrOnNull, const Value& ref_object, uint32_t depth, \
bool pass_null_along_branch, Value* result_on_fallthrough) \
F(BrOnNonNull, const Value& ref_object, Value* result, uint32_t depth) \
F(BrOnNonNull, const Value& ref_object, Value* result, uint32_t depth, \
bool drop_null_on_fallthrough) \
F(SimdOp, WasmOpcode opcode, base::Vector<Value> args, Value* result) \
F(SimdLaneOp, WasmOpcode opcode, const SimdLaneImmediate<validate>& imm, \
const base::Vector<Value> inputs, Value* result) \
......@@ -2965,7 +2966,8 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
break;
case kOptRef: {
if (V8_LIKELY(current_code_reachable_and_ok_)) {
CALL_INTERFACE(BrOnNonNull, ref_object, value_on_branch, imm.depth);
CALL_INTERFACE(BrOnNonNull, ref_object, value_on_branch, imm.depth,
true);
c->br_merge()->reached = true;
}
break;
......@@ -4892,7 +4894,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
// The branch will still not be taken on null.
if (obj.type.is_nullable()) {
CALL_INTERFACE(BrOnNonNull, obj, value_on_branch,
branch_depth.depth);
branch_depth.depth, false);
} else {
CALL_INTERFACE(Forward, obj, value_on_branch);
CALL_INTERFACE(BrOrRet, branch_depth.depth, 0);
......@@ -4986,16 +4988,14 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
CALL_INTERFACE(BrOnNull, obj, branch_depth.depth, true,
&result_on_fallthrough);
c->br_merge()->reached = true;
} else {
// Drop {obj} in the interface.
CALL_INTERFACE(Drop);
}
// Otherwise, the type check always succeeds. Do not branch. Also,
// the object is already on the stack; do not manipulate the stack.
} else {
CALL_INTERFACE(BrOnCastFail, obj, rtt, &result_on_fallthrough,
branch_depth.depth);
c->br_merge()->reached = true;
}
// Otherwise, the type check always succeeds. Do not branch.
}
// Make sure the correct value is on the stack state on fallthrough.
Drop(obj);
......
......@@ -865,7 +865,7 @@ class WasmGraphBuildingInterface {
}
void BrOnNonNull(FullDecoder* decoder, const Value& ref_object, Value* result,
uint32_t depth) {
uint32_t depth, bool /* drop_null_on_fallthrough */) {
result->node =
builder_->TypeGuard(ref_object.node, ref_object.type.AsNonNull());
SsaEnv* false_env = ssa_env_;
......
......@@ -1586,7 +1586,6 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCasts) {
}
WASM_COMPILED_EXEC_TEST(RefTrivialCastsStatic) {
// TODO(7748): Add tests for branch_on_*.
WasmGCTester tester(execution_tier);
byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
byte subtype_index = tester.DefineStruct(
......@@ -1608,10 +1607,10 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCastsStatic) {
tester.sigs.i_v(), {},
{WASM_REF_TEST_STATIC(WASM_REF_NULL(subtype_index), type_index),
kExprEnd});
const byte kRefTestUnrelated = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST_STATIC(WASM_STRUCT_NEW_DEFAULT(subtype_index), sig_index),
kExprEnd});
const byte kRefTestUnrelatedNullable = tester.DefineFunction(
tester.sigs.i_v(), {optref(subtype_index)},
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(subtype_index)),
WASM_REF_TEST_STATIC(WASM_LOCAL_GET(0), sig_index), kExprEnd});
const byte kRefTestUnrelatedNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_TEST_STATIC(WASM_REF_NULL(subtype_index), sig_index),
......@@ -1636,10 +1635,10 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCastsStatic) {
{WASM_REF_IS_NULL(WASM_REF_CAST_STATIC(
WASM_REF_NULL(subtype_index), type_index)),
kExprEnd});
const byte kRefCastUnrelated = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_CAST_STATIC(
WASM_STRUCT_NEW_DEFAULT(subtype_index), sig_index)),
const byte kRefCastUnrelatedNullable = tester.DefineFunction(
tester.sigs.i_v(), {optref(subtype_index)},
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(subtype_index)),
WASM_REF_IS_NULL(WASM_REF_CAST_STATIC(WASM_LOCAL_GET(0), sig_index)),
kExprEnd});
const byte kRefCastUnrelatedNull =
tester.DefineFunction(tester.sigs.i_v(), {},
......@@ -1652,21 +1651,123 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCastsStatic) {
WASM_STRUCT_NEW_DEFAULT(type_index), sig_index)),
kExprEnd});
const byte kBrOnCastNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_BLOCK_R(optref(subtype_index), WASM_REF_NULL(type_index),
WASM_BR_ON_CAST_STATIC(0, subtype_index), WASM_DROP,
WASM_RETURN(WASM_I32V(0))),
WASM_DROP, WASM_I32V(1), WASM_END});
const byte kBrOnCastUpcast = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_BLOCK_R(optref(type_index), WASM_STRUCT_NEW_DEFAULT(subtype_index),
WASM_BR_ON_CAST_STATIC(0, type_index), WASM_DROP,
WASM_RETURN(WASM_I32V(0))),
WASM_DROP, WASM_I32V(1), WASM_END});
const byte kBrOnCastUpcastNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_BLOCK_R(optref(type_index), WASM_REF_NULL(subtype_index),
WASM_BR_ON_CAST_STATIC(0, type_index), WASM_DROP,
WASM_RETURN(WASM_I32V(0))),
WASM_DROP, WASM_I32V(1), WASM_END});
const byte kBrOnCastUnrelatedNullable = tester.DefineFunction(
tester.sigs.i_v(), {optref(subtype_index)},
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(subtype_index)),
WASM_BLOCK_R(optref(sig_index), WASM_LOCAL_GET(0),
WASM_BR_ON_CAST_STATIC(0, sig_index), WASM_DROP,
WASM_RETURN(WASM_I32V(0))),
WASM_DROP, WASM_I32V(1), WASM_END});
const byte kBrOnCastUnrelatedNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_BLOCK_R(optref(sig_index), WASM_REF_NULL(subtype_index),
WASM_BR_ON_CAST_STATIC(0, sig_index), WASM_DROP,
WASM_RETURN(WASM_I32V(0))),
WASM_DROP, WASM_I32V(1), WASM_END});
const byte kBrOnCastUnrelatedNonNullable = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_BLOCK_R(optref(sig_index), WASM_STRUCT_NEW_DEFAULT(subtype_index),
WASM_BR_ON_CAST_STATIC(0, sig_index), WASM_DROP,
WASM_RETURN(WASM_I32V(0))),
WASM_DROP, WASM_I32V(1), WASM_END});
const byte kBrOnCastFailNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_BLOCK_R(optref(type_index), WASM_REF_NULL(type_index),
WASM_BR_ON_CAST_STATIC_FAIL(0, subtype_index), WASM_DROP,
WASM_RETURN(WASM_I32V(0))),
WASM_DROP, WASM_I32V(1), WASM_END});
const byte kBrOnCastFailUpcast = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_BLOCK_R(optref(subtype_index),
WASM_STRUCT_NEW_DEFAULT(subtype_index),
WASM_BR_ON_CAST_STATIC_FAIL(0, type_index), WASM_DROP,
WASM_RETURN(WASM_I32V(0))),
WASM_DROP, WASM_I32V(1), WASM_END});
const byte kBrOnCastFailUpcastNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_BLOCK_R(optref(subtype_index), WASM_REF_NULL(subtype_index),
WASM_BR_ON_CAST_STATIC_FAIL(0, type_index), WASM_DROP,
WASM_RETURN(WASM_I32V(0))),
WASM_DROP, WASM_I32V(1), WASM_END});
const byte kBrOnCastFailUnrelatedNullable = tester.DefineFunction(
tester.sigs.i_v(), {optref(subtype_index)},
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(subtype_index)),
WASM_BLOCK_R(optref(subtype_index), WASM_LOCAL_GET(0),
WASM_BR_ON_CAST_STATIC_FAIL(0, sig_index), WASM_DROP,
WASM_RETURN(WASM_I32V(0))),
WASM_DROP, WASM_I32V(1), WASM_END});
const byte kBrOnCastFailUnrelatedNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_BLOCK_R(optref(subtype_index), WASM_REF_NULL(subtype_index),
WASM_BR_ON_CAST_STATIC_FAIL(0, sig_index), WASM_DROP,
WASM_RETURN(WASM_I32V(0))),
WASM_DROP, WASM_I32V(1), WASM_END});
const byte kBrOnCastFailUnrelatedNonNullable = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_BLOCK_R(optref(subtype_index),
WASM_STRUCT_NEW_DEFAULT(subtype_index),
WASM_BR_ON_CAST_STATIC_FAIL(0, sig_index), WASM_DROP,
WASM_RETURN(WASM_I32V(0))),
WASM_DROP, WASM_I32V(1), WASM_END});
tester.CompileModule();
tester.CheckResult(kRefTestNull, 0);
tester.CheckResult(kRefTestUpcast, 1);
tester.CheckResult(kRefTestUpcastNull, 0);
tester.CheckResult(kRefTestUnrelated, 0);
tester.CheckResult(kRefTestUnrelatedNullable, 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.CheckHasThrown(kRefCastUnrelatedNullable);
tester.CheckResult(kRefCastUnrelatedNull, 1);
tester.CheckHasThrown(kRefCastUnrelatedNonNullable);
tester.CheckResult(kBrOnCastNull, 0);
tester.CheckResult(kBrOnCastUpcast, 1);
tester.CheckResult(kBrOnCastUpcastNull, 0);
tester.CheckResult(kBrOnCastUnrelatedNullable, 0);
tester.CheckResult(kBrOnCastUnrelatedNull, 0);
tester.CheckResult(kBrOnCastUnrelatedNonNullable, 0);
tester.CheckResult(kBrOnCastFailNull, 1);
tester.CheckResult(kBrOnCastFailUpcast, 0);
tester.CheckResult(kBrOnCastFailUpcastNull, 1);
tester.CheckResult(kBrOnCastFailUnrelatedNullable, 1);
tester.CheckResult(kBrOnCastFailUnrelatedNull, 1);
tester.CheckResult(kBrOnCastFailUnrelatedNonNullable, 1);
}
WASM_COMPILED_EXEC_TEST(TrivialAbstractCasts) {
......
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