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

[wasm-gc] Optimize trivial abstract type checks

We optimize trivial type checks in the function body decoder, i.e.,
ref.as_<type> and ref.is_<type> when invoked on a value that is
statically known to be of typeable as <type>.

Bug: v8:7748
Change-Id: Ieee608a965ba44c4cadd9c7171ed8bdc129fce8b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3447375Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79061}
parent 5b08f5dd
......@@ -4715,35 +4715,70 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
Push(result_on_fallthrough);
return pc_offset;
}
#define ABSTRACT_TYPE_CHECK(heap_type) \
case kExprRefIs##heap_type: { \
NON_CONST_ONLY \
Value arg = Peek(0, 0, kWasmAnyRef); \
Value result = CreateValue(kWasmI32); \
CALL_INTERFACE_IF_OK_AND_REACHABLE(RefIs##heap_type, arg, &result); \
Drop(arg); \
Push(result); \
return opcode_length; \
#define ABSTRACT_TYPE_CHECK(h_type) \
case kExprRefIs##h_type: { \
NON_CONST_ONLY \
Value arg = Peek(0, 0, kWasmAnyRef); \
if (this->failed()) return 0; \
Value result = CreateValue(kWasmI32); \
if (V8_LIKELY(current_code_reachable_and_ok_)) { \
if (IsHeapSubtypeOf(arg.type.heap_representation(), HeapType::k##h_type, \
this->module_)) { \
if (arg.type.is_nullable()) { \
/* We abuse ref.as_non_null, which isn't otherwise used as a unary \
* operator, as a sentinel for the negation of ref.is_null. */ \
CALL_INTERFACE(UnOp, kExprRefAsNonNull, arg, &result); \
} else { \
CALL_INTERFACE(Drop); \
CALL_INTERFACE(I32Const, &result, 1); \
} \
} else if (!IsHeapSubtypeOf(HeapType::k##h_type, \
arg.type.heap_representation(), \
this->module_)) { \
CALL_INTERFACE(Drop); \
CALL_INTERFACE(I32Const, &result, 0); \
} else { \
CALL_INTERFACE(RefIs##h_type, arg, &result); \
} \
} \
Drop(arg); \
Push(result); \
return opcode_length; \
}
ABSTRACT_TYPE_CHECK(Data)
ABSTRACT_TYPE_CHECK(Func)
ABSTRACT_TYPE_CHECK(I31)
ABSTRACT_TYPE_CHECK(Array)
#undef ABSTRACT_TYPE_CHECK
#define ABSTRACT_TYPE_CAST(heap_type) \
case kExprRefAs##heap_type: { \
NON_CONST_ONLY \
Value arg = Peek(0, 0, kWasmAnyRef); \
Value result = \
CreateValue(ValueType::Ref(HeapType::k##heap_type, kNonNullable)); \
CALL_INTERFACE_IF_OK_AND_REACHABLE(RefAs##heap_type, arg, &result); \
Drop(arg); \
Push(result); \
return opcode_length; \
#define ABSTRACT_TYPE_CAST(h_type) \
case kExprRefAs##h_type: { \
NON_CONST_ONLY \
Value arg = Peek(0, 0, kWasmAnyRef); \
ValueType non_nullable_abstract_type = \
ValueType::Ref(HeapType::k##h_type, kNonNullable); \
Value result = CreateValue(non_nullable_abstract_type); \
if (V8_LIKELY(current_code_reachable_and_ok_)) { \
if (IsHeapSubtypeOf(arg.type.heap_representation(), HeapType::k##h_type, \
this->module_)) { \
if (arg.type.is_nullable()) { \
CALL_INTERFACE(RefAsNonNull, arg, &result); \
} else { \
CALL_INTERFACE(Forward, arg, &result); \
} \
} else if (!IsHeapSubtypeOf(HeapType::k##h_type, \
arg.type.heap_representation(), \
this->module_)) { \
CALL_INTERFACE(Trap, TrapReason::kTrapIllegalCast); \
EndControl(); \
} else { \
CALL_INTERFACE(RefAs##h_type, arg, &result); \
} \
} \
Drop(arg); \
Push(result); \
return opcode_length; \
}
ABSTRACT_TYPE_CAST(Data)
ABSTRACT_TYPE_CAST(Func)
ABSTRACT_TYPE_CAST(I31)
......
......@@ -72,17 +72,12 @@ V8_INLINE bool IsSubtypeOf(ValueType subtype, ValueType supertype,
// We have this function call IsSubtypeOf instead of the opposite because type
// checks are much more common than heap type checks.
V8_INLINE bool IsHeapSubtypeOf(uint32_t subtype_index,
V8_INLINE bool IsHeapSubtypeOf(HeapType::Representation subtype,
HeapType::Representation supertype,
const WasmModule* module) {
return IsSubtypeOf(ValueType::Ref(subtype_index, kNonNullable),
return IsSubtypeOf(ValueType::Ref(subtype, kNonNullable),
ValueType::Ref(supertype, kNonNullable), module);
}
V8_INLINE bool IsHeapSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
const WasmModule* module) {
return IsSubtypeOf(ValueType::Ref(subtype_index, kNonNullable),
ValueType::Ref(supertype_index, kNonNullable), module);
}
// Checks whether {subtype_index} is valid as a declared subtype of
// {supertype_index}.
......
......@@ -1599,6 +1599,97 @@ WASM_COMPILED_EXEC_TEST(RefTrivialCastsStatic) {
tester.CheckHasThrown(kRefCastUnrelatedNonNullable);
}
WASM_COMPILED_EXEC_TEST(TrivialAbstractCasts) {
// TODO(7748): Add tests for branch_on_*.
WasmGCTester tester(execution_tier);
byte type_index = tester.DefineArray(wasm::kWasmI32, true);
byte struct_type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
ValueType sig_types[] = {kWasmS128, kWasmI32, kWasmF64};
FunctionSig sig(1, 2, sig_types);
const byte kIsArrayNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_ARRAY(WASM_REF_NULL(kAnyRefCode)), kExprEnd});
const byte kIsArrayUpcast = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_ARRAY(WASM_ARRAY_NEW_DEFAULT_WITH_RTT(
type_index, WASM_I32V(10), WASM_RTT_CANON(type_index))),
kExprEnd});
const byte kIsArrayUpcastNullable = tester.DefineFunction(
tester.sigs.i_v(), {ValueType::Ref(type_index, kNullable)},
{WASM_LOCAL_SET(
0, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(type_index, WASM_I32V(10),
WASM_RTT_CANON(type_index))),
WASM_REF_IS_ARRAY(WASM_LOCAL_GET(0)), kExprEnd});
const byte kIsArrayUpcastNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_ARRAY(WASM_REF_NULL(type_index)), kExprEnd});
const byte kIsArrayUnrelated = tester.DefineFunction(
tester.sigs.i_v(), {ValueType::Ref(struct_type_index, kNullable)},
{WASM_LOCAL_SET(
0, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
struct_type_index, WASM_RTT_CANON(struct_type_index))),
WASM_REF_IS_ARRAY(WASM_LOCAL_GET(0)), kExprEnd});
const byte kIsArrayUnrelatedNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_ARRAY(WASM_REF_NULL(kI31RefCode)), kExprEnd});
const byte kIsArrayUnrelatedNonNullable = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_ARRAY(WASM_I31_NEW(WASM_I32V(10))), kExprEnd});
const byte kAsArrayNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_AS_ARRAY(WASM_REF_NULL(kAnyRefCode))),
kExprEnd});
const byte kAsArrayUpcast = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_AS_ARRAY(WASM_ARRAY_NEW_DEFAULT_WITH_RTT(
type_index, WASM_I32V(10), WASM_RTT_CANON(type_index)))),
kExprEnd});
const byte kAsArrayUpcastNullable = tester.DefineFunction(
tester.sigs.i_v(), {ValueType::Ref(type_index, kNullable)},
{WASM_LOCAL_SET(
0, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(type_index, WASM_I32V(10),
WASM_RTT_CANON(type_index))),
WASM_REF_IS_NULL(WASM_REF_AS_ARRAY(WASM_LOCAL_GET(0))), kExprEnd});
const byte kAsArrayUpcastNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_AS_ARRAY(WASM_REF_NULL(type_index))),
kExprEnd});
const byte kAsArrayUnrelated = tester.DefineFunction(
tester.sigs.i_v(), {ValueType::Ref(struct_type_index, kNullable)},
{WASM_LOCAL_SET(
0, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
struct_type_index, WASM_RTT_CANON(struct_type_index))),
WASM_REF_IS_NULL(WASM_REF_AS_ARRAY(WASM_LOCAL_GET(0))), kExprEnd});
const byte kAsArrayUnrelatedNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_AS_ARRAY(WASM_REF_NULL(kI31RefCode))),
kExprEnd});
const byte kAsArrayUnrelatedNonNullable = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_NULL(WASM_REF_AS_ARRAY(WASM_I31_NEW(WASM_I32V(10)))),
kExprEnd});
tester.CompileModule();
tester.CheckResult(kIsArrayNull, 0);
tester.CheckResult(kIsArrayUpcast, 1);
tester.CheckResult(kIsArrayUpcastNullable, 1);
tester.CheckResult(kIsArrayUpcastNull, 0);
tester.CheckResult(kIsArrayUnrelated, 0);
tester.CheckResult(kIsArrayUnrelatedNull, 0);
tester.CheckResult(kIsArrayUnrelatedNonNullable, 0);
tester.CheckHasThrown(kAsArrayNull);
tester.CheckResult(kAsArrayUpcast, 0);
tester.CheckResult(kAsArrayUpcastNullable, 0);
tester.CheckHasThrown(kAsArrayUpcastNull);
tester.CheckHasThrown(kAsArrayUnrelated);
tester.CheckHasThrown(kAsArrayUnrelatedNull);
tester.CheckHasThrown(kAsArrayUnrelatedNonNullable);
}
WASM_EXEC_TEST(NoDepthRtt) {
WasmGCTester tester(execution_tier);
......
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