Commit 074cfd61 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Classification functions

The latest wasm-gc spec introduces classification functions
{ref.as, ref.is, br_on}_{func, data, i31}, to cast values typed as
anyref and eqref.

Bug: v8:7748
Change-Id: I39c288e4a4c96466e64e490f164ccc76b00011ee
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2655506
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72434}
parent 0cca09ac
......@@ -5848,24 +5848,40 @@ void WasmGraphBuilder::TypeCheck(
callbacks.fail_if_not(gasm_->TaggedEqual(maybe_match, rtt));
}
Node* WasmGraphBuilder::BrOnCast(Node* object, Node* rtt,
ObjectReferenceKnowledge config,
Node** match_control, Node** match_effect,
Node** no_match_control,
Node** no_match_effect) {
void WasmGraphBuilder::DataCheck(Node* object, bool object_can_be_null,
Callbacks callbacks) {
if (object_can_be_null) {
callbacks.fail_if(gasm_->WordEqual(object, RefNull()));
}
callbacks.fail_if(gasm_->IsI31(object));
Node* map = gasm_->LoadMap(object);
callbacks.fail_if_not(gasm_->IsDataRefMap(map));
}
void WasmGraphBuilder::FuncCheck(Node* object, bool object_can_be_null,
Callbacks callbacks) {
if (object_can_be_null) {
callbacks.fail_if(gasm_->WordEqual(object, RefNull()));
}
callbacks.fail_if(gasm_->IsI31(object));
callbacks.fail_if_not(gasm_->HasInstanceType(object, JS_FUNCTION_TYPE));
}
Node* WasmGraphBuilder::BrOnCastAbs(
Node** match_control, Node** match_effect, Node** no_match_control,
Node** no_match_effect, std::function<void(Callbacks)> type_checker) {
SmallNodeVector no_match_controls, no_match_effects, match_controls,
match_effects;
TypeCheck(object, rtt, config, false,
BranchCallbacks(no_match_controls, no_match_effects, match_controls,
match_effects));
type_checker(BranchCallbacks(no_match_controls, no_match_effects,
match_controls, match_effects));
match_controls.emplace_back(control());
match_effects.emplace_back(effect());
// Wire up the control/effect nodes.
unsigned count = static_cast<unsigned>(match_controls.size());
DCHECK_EQ(2, count);
DCHECK_EQ(match_controls.size(), match_effects.size());
*match_control = Merge(count, match_controls.data());
// EffectPhis need their control dependency as an additional input.
match_effects.emplace_back(*match_control);
......@@ -5902,6 +5918,106 @@ Node* WasmGraphBuilder::RefCast(Node* object, Node* rtt,
return object;
}
Node* WasmGraphBuilder::BrOnCast(Node* object, Node* rtt,
ObjectReferenceKnowledge config,
Node** match_control, Node** match_effect,
Node** no_match_control,
Node** no_match_effect) {
return BrOnCastAbs(match_control, match_effect, no_match_control,
no_match_effect, [=](Callbacks callbacks) -> void {
return TypeCheck(object, rtt, config, false, callbacks);
});
}
Node* WasmGraphBuilder::RefIsData(Node* object, bool object_can_be_null) {
auto done = gasm_->MakeLabel(MachineRepresentation::kWord32);
DataCheck(object, object_can_be_null, TestCallbacks(&done));
gasm_->Goto(&done, gasm_->Int32Constant(1));
gasm_->Bind(&done);
return done.PhiAt(0);
}
Node* WasmGraphBuilder::RefAsData(Node* object, bool object_can_be_null,
wasm::WasmCodePosition position) {
auto done = gasm_->MakeLabel();
DataCheck(object, object_can_be_null, CastCallbacks(&done, position));
gasm_->Goto(&done);
gasm_->Bind(&done);
return object;
}
Node* WasmGraphBuilder::BrOnData(Node* object, Node* /*rtt*/,
ObjectReferenceKnowledge config,
Node** match_control, Node** match_effect,
Node** no_match_control,
Node** no_match_effect) {
return BrOnCastAbs(match_control, match_effect, no_match_control,
no_match_effect, [=](Callbacks callbacks) -> void {
return DataCheck(object, config.object_can_be_null,
callbacks);
});
}
Node* WasmGraphBuilder::RefIsFunc(Node* object, bool object_can_be_null) {
auto done = gasm_->MakeLabel(MachineRepresentation::kWord32);
FuncCheck(object, object_can_be_null, TestCallbacks(&done));
gasm_->Goto(&done, gasm_->Int32Constant(1));
gasm_->Bind(&done);
return done.PhiAt(0);
}
Node* WasmGraphBuilder::RefAsFunc(Node* object, bool object_can_be_null,
wasm::WasmCodePosition position) {
auto done = gasm_->MakeLabel();
FuncCheck(object, object_can_be_null, CastCallbacks(&done, position));
gasm_->Goto(&done);
gasm_->Bind(&done);
return object;
}
Node* WasmGraphBuilder::BrOnFunc(Node* object, Node* /*rtt*/,
ObjectReferenceKnowledge config,
Node** match_control, Node** match_effect,
Node** no_match_control,
Node** no_match_effect) {
return BrOnCastAbs(match_control, match_effect, no_match_control,
no_match_effect, [=](Callbacks callbacks) -> void {
return FuncCheck(object, config.object_can_be_null,
callbacks);
});
}
Node* WasmGraphBuilder::RefIsI31(Node* object) { return gasm_->IsI31(object); }
Node* WasmGraphBuilder::RefAsI31(Node* object,
wasm::WasmCodePosition position) {
TrapIfFalse(wasm::kTrapIllegalCast, gasm_->IsI31(object), position);
return object;
}
Node* WasmGraphBuilder::BrOnI31(Node* object, Node* /* rtt */,
ObjectReferenceKnowledge /* config */,
Node** match_control, Node** match_effect,
Node** no_match_control,
Node** no_match_effect) {
Node* branch =
graph()->NewNode(mcgraph()->common()->Branch(BranchHint::kTrue),
gasm_->IsI31(object), control());
Node* if_true = graph()->NewNode(mcgraph()->common()->IfTrue(), branch);
Node* if_false = graph()->NewNode(mcgraph()->common()->IfFalse(), branch);
SetControl(if_false);
*match_control = if_true;
*match_effect = effect();
*no_match_control = if_false;
*no_match_effect = effect();
// Unused return value, needed for typing of BUILD in graph-builder-interface.
return nullptr;
}
Node* WasmGraphBuilder::StructGet(Node* struct_object,
const wasm::StructType* struct_type,
uint32_t field_index, CheckForNull null_check,
......
......@@ -437,12 +437,30 @@ class WasmGraphBuilder {
Node* I31GetU(Node* input);
Node* RttCanon(uint32_t type_index);
Node* RttSub(uint32_t type_index, Node* parent_rtt);
Node* RefTest(Node* object, Node* rtt, ObjectReferenceKnowledge config);
Node* RefCast(Node* object, Node* rtt, ObjectReferenceKnowledge config,
wasm::WasmCodePosition position);
Node* BrOnCast(Node* object, Node* rtt, ObjectReferenceKnowledge config,
Node** match_control, Node** match_effect,
Node** no_match_control, Node** no_match_effect);
Node* RefIsData(Node* object, bool object_can_be_null);
Node* RefAsData(Node* object, bool object_can_be_null,
wasm::WasmCodePosition position);
Node* BrOnData(Node* object, Node* rtt, ObjectReferenceKnowledge config,
Node** match_control, Node** match_effect,
Node** no_match_control, Node** no_match_effect);
Node* RefIsFunc(Node* object, bool object_can_be_null);
Node* RefAsFunc(Node* object, bool object_can_be_null,
wasm::WasmCodePosition position);
Node* BrOnFunc(Node* object, Node* rtt, ObjectReferenceKnowledge config,
Node** match_control, Node** match_effect,
Node** no_match_control, Node** no_match_effect);
Node* RefIsI31(Node* object);
Node* RefAsI31(Node* object, wasm::WasmCodePosition position);
Node* BrOnI31(Node* object, Node* rtt, ObjectReferenceKnowledge config,
Node** match_control, Node** match_effect,
Node** no_match_control, Node** no_match_effect);
bool has_simd() const { return has_simd_; }
......@@ -607,6 +625,12 @@ class WasmGraphBuilder {
void TypeCheck(Node* object, Node* rtt, ObjectReferenceKnowledge config,
bool null_succeeds, Callbacks callbacks);
void DataCheck(Node* object, bool object_can_be_null, Callbacks callbacks);
void FuncCheck(Node* object, bool object_can_be_null, Callbacks callbacks);
Node* BrOnCastAbs(Node** match_control, Node** match_effect,
Node** no_match_control, Node** no_match_effect,
std::function<void(Callbacks)> type_checker);
// Asm.js specific functionality.
Node* BuildI32AsmjsSConvertF32(Node* input);
......
......@@ -4523,6 +4523,45 @@ class LiftoffCompiler {
__ PushRegister(obj.type, obj_reg);
}
void RefIsData(FullDecoder* decoder, const Value& object, Value* result) {
unsupported(decoder, LiftoffBailoutReason::kGC, "ref.is_data");
}
void RefAsData(FullDecoder* decoder, const Value& object, Value* result) {
unsupported(decoder, LiftoffBailoutReason::kGC, "ref.as_data");
}
void BrOnData(FullDecoder* decoder, const Value& object,
Value* value_on_branch, uint32_t br_depth) {
unsupported(decoder, LiftoffBailoutReason::kGC, "br_on_data");
}
void RefIsFunc(FullDecoder* decoder, const Value& object, Value* result) {
unsupported(decoder, LiftoffBailoutReason::kGC, "ref.is_func");
}
void RefAsFunc(FullDecoder* decoder, const Value& object, Value* result) {
unsupported(decoder, LiftoffBailoutReason::kGC, "ref.as_func");
}
void BrOnFunc(FullDecoder* decoder, const Value& object,
Value* value_on_branch, uint32_t br_depth) {
unsupported(decoder, LiftoffBailoutReason::kGC, "br_on_func");
}
void RefIsI31(FullDecoder* decoder, const Value& object, Value* result) {
unsupported(decoder, LiftoffBailoutReason::kGC, "ref.is_i31");
}
void RefAsI31(FullDecoder* decoder, const Value& object, Value* result) {
unsupported(decoder, LiftoffBailoutReason::kGC, "ref.as_i31");
}
void BrOnI31(FullDecoder* decoder, const Value& object,
Value* value_on_branch, uint32_t br_depth) {
unsupported(decoder, LiftoffBailoutReason::kGC, "br_on_i31");
}
void Forward(FullDecoder* decoder, const Value& from, Value* to) {
// Nothing to do here.
}
......
......@@ -1154,6 +1154,15 @@ struct ControlBase : public PcForErrors<validate> {
F(RefCast, const Value& obj, const Value& rtt, Value* result) \
F(BrOnCast, const Value& obj, const Value& rtt, Value* result_on_branch, \
uint32_t depth) \
F(RefIsData, 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(RefIsFunc, const Value& object, Value* result) \
F(RefAsFunc, const Value& object, Value* result) \
F(BrOnFunc, const Value& object, Value* value_on_branch, uint32_t br_depth) \
F(RefIsI31, const Value& object, Value* result) \
F(RefAsI31, const Value& object, Value* result) \
F(BrOnI31, const Value& object, Value* value_on_branch, uint32_t br_depth) \
F(Forward, const Value& from, Value* to)
// Generic Wasm bytecode decoder with utilities for decoding immediates,
......@@ -4195,6 +4204,71 @@ class WasmFullDecoder : public WasmDecoder<validate> {
*result_on_fallthrough = obj;
return opcode_length + branch_depth.length;
}
#define ABSTRACT_TYPE_CHECK(heap_type) \
case kExprRefIs##heap_type: { \
Value arg = Pop(0, kWasmAnyRef); \
Value* result = Push(kWasmI32); \
CALL_INTERFACE_IF_REACHABLE(RefIs##heap_type, arg, result); \
return opcode_length; \
}
ABSTRACT_TYPE_CHECK(Data)
ABSTRACT_TYPE_CHECK(Func)
ABSTRACT_TYPE_CHECK(I31)
#undef ABSTRACT_TYPE_CHECK
#define ABSTRACT_TYPE_CAST(heap_type) \
case kExprRefAs##heap_type: { \
Value arg = Pop(0, kWasmAnyRef); \
if (!arg.type.is_bottom()) { \
Value* result = \
Push(ValueType::Ref(HeapType::k##heap_type, kNonNullable)); \
CALL_INTERFACE_IF_REACHABLE(RefAs##heap_type, arg, result); \
} \
return opcode_length; \
}
ABSTRACT_TYPE_CAST(Data)
ABSTRACT_TYPE_CAST(Func)
ABSTRACT_TYPE_CAST(I31)
#undef ABSTRACT_TYPE_CAST
case kExprBrOnData:
case kExprBrOnFunc:
case kExprBrOnI31: {
BranchDepthImmediate<validate> branch_depth(this,
this->pc_ + opcode_length);
if (!this->Validate(this->pc_ + opcode_length, branch_depth,
control_.size())) {
return 0;
}
Value obj = Pop(0, kWasmAnyRef);
Control* c = control_at(branch_depth.depth);
HeapType::Representation heap_type =
opcode == kExprBrOnFunc
? HeapType::kFunc
: opcode == kExprBrOnData ? HeapType::kData : HeapType::kI31;
Value* result_on_branch = Push(ValueType::Ref(heap_type, kNonNullable));
TypeCheckBranchResult check_result = TypeCheckBranch(c, true);
if (V8_LIKELY(check_result == kReachableBranch)) {
if (opcode == kExprBrOnFunc) {
CALL_INTERFACE(BrOnFunc, obj, result_on_branch, branch_depth.depth);
} else if (opcode == kExprBrOnData) {
CALL_INTERFACE(BrOnData, obj, result_on_branch, branch_depth.depth);
} else {
CALL_INTERFACE(BrOnI31, obj, result_on_branch, branch_depth.depth);
}
c->br_merge()->reached = true;
} else if (check_result == kInvalidStack) {
return 0;
}
Pop(0); // Drop {result_on_branch}, restore original value.
Value* result_on_fallthrough = Push(obj.type);
*result_on_fallthrough = obj;
return opcode_length + branch_depth.length;
}
default:
this->DecodeError("invalid gc opcode");
return 0;
......
......@@ -1008,15 +1008,22 @@ class WasmGraphBuildingInterface {
BUILD(RefCast, object.node, rtt.node, config, decoder->position());
}
void BrOnCast(FullDecoder* decoder, const Value& object, const Value& rtt,
Value* value_on_branch, uint32_t br_depth) {
template <TFNode* (compiler::WasmGraphBuilder::*branch_function)(
TFNode*, TFNode*, StaticKnowledge, TFNode**, TFNode**, TFNode**,
TFNode**)>
void BrOnCastAbs(FullDecoder* decoder, const Value& object, const Value& rtt,
Value* value_on_branch, uint32_t br_depth) {
StaticKnowledge config =
ComputeStaticKnowledge(object.type, rtt.type, decoder->module_);
SsaEnv* match_env = Split(decoder->zone(), ssa_env_);
SsaEnv* no_match_env = Steal(decoder->zone(), ssa_env_);
no_match_env->SetNotMerged();
BUILD(BrOnCast, object.node, rtt.node, config, &match_env->control,
&match_env->effect, &no_match_env->control, &no_match_env->effect);
DCHECK(decoder->ok());
CheckForException(
decoder,
(builder_->*branch_function)(
object.node, rtt.node, config, &match_env->control,
&match_env->effect, &no_match_env->control, &no_match_env->effect));
builder_->SetControl(no_match_env->control);
SetEnv(match_env);
value_on_branch->node = object.node;
......@@ -1024,6 +1031,59 @@ class WasmGraphBuildingInterface {
SetEnv(no_match_env);
}
void BrOnCast(FullDecoder* decoder, const Value& object, const Value& rtt,
Value* value_on_branch, uint32_t br_depth) {
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnCast>(
decoder, object, rtt, value_on_branch, br_depth);
}
void RefIsData(FullDecoder* decoder, const Value& object, Value* result) {
result->node = BUILD(RefIsData, object.node, object.type.is_nullable());
}
void RefAsData(FullDecoder* decoder, const Value& object, Value* result) {
result->node = BUILD(RefAsData, object.node, object.type.is_nullable(),
decoder->position());
}
void BrOnData(FullDecoder* decoder, const Value& object,
Value* value_on_branch, uint32_t br_depth) {
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnData>(
decoder, object, Value{nullptr, kWasmBottom}, value_on_branch,
br_depth);
}
void RefIsFunc(FullDecoder* decoder, const Value& object, Value* result) {
result->node = BUILD(RefIsFunc, object.node, object.type.is_nullable());
}
void RefAsFunc(FullDecoder* decoder, const Value& object, Value* result) {
result->node = BUILD(RefAsFunc, object.node, object.type.is_nullable(),
decoder->position());
}
void BrOnFunc(FullDecoder* decoder, const Value& object,
Value* value_on_branch, uint32_t br_depth) {
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnFunc>(
decoder, object, Value{nullptr, kWasmBottom}, value_on_branch,
br_depth);
}
void RefIsI31(FullDecoder* decoder, const Value& object, Value* result) {
result->node = BUILD(RefIsI31, object.node);
}
void RefAsI31(FullDecoder* decoder, const Value& object, Value* result) {
result->node = BUILD(RefAsI31, object.node, decoder->position());
}
void BrOnI31(FullDecoder* decoder, const Value& object,
Value* value_on_branch, uint32_t br_depth) {
BrOnCastAbs<&compiler::WasmGraphBuilder::BrOnI31>(
decoder, object, Value{nullptr, kWasmBottom}, value_on_branch,
br_depth);
}
void Forward(FullDecoder* decoder, const Value& from, Value* to) {
to->node = from.node;
}
......
......@@ -411,10 +411,18 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(RefTest, "ref.test")
CASE_OP(RefCast, "ref.cast")
CASE_OP(BrOnCast, "br_on_cast")
CASE_OP(RefIsFunc, "ref.is_func")
CASE_OP(RefIsData, "ref.is_data")
CASE_OP(RefIsI31, "ref.is_i31")
CASE_OP(RefAsFunc, "ref.as_func")
CASE_OP(RefAsData, "ref.as_data")
CASE_OP(RefAsI31, "ref.as_i31")
CASE_OP(BrOnFunc, "br_on_func")
CASE_OP(BrOnData, "br_on_data")
CASE_OP(BrOnI31, "br_on_i31")
CASE_OP(RefEq, "ref.eq")
CASE_OP(Let, "let")
case kNumericPrefix:
case kSimdPrefix:
case kAtomicPrefix:
......
......@@ -677,7 +677,16 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(RttSub, 0xfb31, _) \
V(RefTest, 0xfb40, _) \
V(RefCast, 0xfb41, _) \
V(BrOnCast, 0xfb42, _)
V(BrOnCast, 0xfb42, _) \
V(RefIsFunc, 0xfb50, _) \
V(RefIsData, 0xfb51, _) \
V(RefIsI31, 0xfb52, _) \
V(RefAsFunc, 0xfb58, _) \
V(RefAsData, 0xfb59, _) \
V(RefAsI31, 0xfb5a, _) \
V(BrOnFunc, 0xfb60, _) \
V(BrOnData, 0xfb61, _) \
V(BrOnI31, 0xfb62, _)
#define FOREACH_ATOMIC_0_OPERAND_OPCODE(V) \
/* AtomicFence does not target a particular linear memory. */ \
......
......@@ -1076,6 +1076,135 @@ WASM_COMPILED_EXEC_TEST(RefTestCastNull) {
tester.CheckResult(kRefCastNull, 1);
}
TEST(AbstractTypeChecks) {
WasmGCTester tester;
byte array_index = tester.DefineArray(kWasmI32, true);
byte function_index =
tester.DefineFunction(tester.sigs.v_v(), {}, {kExprEnd});
byte sig_index = 1;
// This is just so func_index counts as "declared".
tester.AddGlobal(ValueType::Ref(sig_index, kNullable), false,
WasmInitExpr::RefFuncConst(function_index));
byte kDataCheckNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_DATA(WASM_REF_NULL(kAnyRefCode)), kExprEnd});
byte kFuncCheckNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_FUNC(WASM_REF_NULL(kAnyRefCode)), kExprEnd});
byte kI31CheckNull = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_REF_IS_I31(WASM_REF_NULL(kAnyRefCode)), kExprEnd});
byte kDataCastNull =
tester.DefineFunction(tester.sigs.i_v(), {},
{WASM_REF_AS_DATA(WASM_REF_NULL(kAnyRefCode)),
WASM_DROP, WASM_I32V(1), kExprEnd});
byte kFuncCastNull =
tester.DefineFunction(tester.sigs.i_v(), {},
{WASM_REF_AS_FUNC(WASM_REF_NULL(kAnyRefCode)),
WASM_DROP, WASM_I32V(1), kExprEnd});
byte kI31CastNull =
tester.DefineFunction(tester.sigs.i_v(), {},
{WASM_REF_AS_I31(WASM_REF_NULL(kAnyRefCode)),
WASM_DROP, WASM_I32V(1), kExprEnd});
#define TYPE_CHECK(type, value) \
tester.DefineFunction(tester.sigs.i_v(), {kWasmAnyRef}, \
{WASM_LOCAL_SET(0, WASM_SEQ(value)), \
WASM_REF_IS_##type(WASM_LOCAL_GET(0)), kExprEnd})
byte kDataCheckSuccess =
TYPE_CHECK(DATA, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kDataCheckFailure = TYPE_CHECK(DATA, WASM_I31_NEW(WASM_I32V(42)));
byte kFuncCheckSuccess = TYPE_CHECK(FUNC, WASM_REF_FUNC(function_index));
byte kFuncCheckFailure =
TYPE_CHECK(FUNC, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kI31CheckSuccess = TYPE_CHECK(I31, WASM_I31_NEW(WASM_I32V(42)));
byte kI31CheckFailure =
TYPE_CHECK(I31, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
#undef TYPE_CHECK
#define TYPE_CAST(type, value) \
tester.DefineFunction(tester.sigs.i_v(), {kWasmAnyRef}, \
{WASM_LOCAL_SET(0, WASM_SEQ(value)), \
WASM_REF_AS_##type(WASM_LOCAL_GET(0)), WASM_DROP, \
WASM_I32V(1), kExprEnd})
byte kDataCastSuccess =
TYPE_CAST(DATA, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kDataCastFailure = TYPE_CAST(DATA, WASM_I31_NEW(WASM_I32V(42)));
byte kFuncCastSuccess = TYPE_CAST(FUNC, WASM_REF_FUNC(function_index));
byte kFuncCastFailure =
TYPE_CAST(FUNC, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kI31CastSuccess = TYPE_CAST(I31, WASM_I31_NEW(WASM_I32V(42)));
byte kI31CastFailure =
TYPE_CAST(I31, WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
#undef TYPE_CAST
// If the branch is not taken, we return 0. If it is taken, then the respective
// type check should succeed, and we return 1.
#define BR_ON(TYPE, type, value) \
tester.DefineFunction( \
tester.sigs.i_v(), {kWasmAnyRef}, \
{WASM_LOCAL_SET(0, WASM_SEQ(value)), \
WASM_REF_IS_##TYPE(WASM_BLOCK_R( \
kWasm##type##Ref, WASM_BR_ON_##TYPE(0, WASM_LOCAL_GET(0)), \
WASM_RETURN(WASM_I32V(0)))), \
kExprEnd})
byte kBrOnDataTaken =
BR_ON(DATA, Data,
WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
byte kBrOnDataNotTaken = BR_ON(DATA, Data, WASM_REF_FUNC(function_index));
byte kBrOnFuncTaken = BR_ON(FUNC, Func, WASM_REF_FUNC(function_index));
byte kBrOnFuncNotTaken = BR_ON(FUNC, Func, WASM_I31_NEW(WASM_I32V(42)));
byte kBrOnI31Taken = BR_ON(I31, I31, WASM_I31_NEW(WASM_I32V(42)));
byte kBrOnI31NotTaken =
BR_ON(I31, I31,
WASM_ARRAY_NEW_DEFAULT(array_index, WASM_I32V(10),
WASM_RTT_CANON(array_index)));
#undef BR_ON
tester.CompileModule();
tester.CheckResult(kDataCheckNull, 0);
tester.CheckHasThrown(kDataCastNull);
tester.CheckResult(kDataCheckSuccess, 1);
tester.CheckResult(kDataCheckFailure, 0);
tester.CheckResult(kDataCastSuccess, 1);
tester.CheckHasThrown(kDataCastFailure);
tester.CheckResult(kBrOnDataTaken, 1);
tester.CheckResult(kBrOnDataNotTaken, 0);
tester.CheckResult(kFuncCheckNull, 0);
tester.CheckHasThrown(kFuncCastNull);
tester.CheckResult(kFuncCheckSuccess, 1);
tester.CheckResult(kFuncCheckFailure, 0);
tester.CheckResult(kFuncCastSuccess, 1);
tester.CheckHasThrown(kFuncCastFailure);
tester.CheckResult(kBrOnFuncTaken, 1);
tester.CheckResult(kBrOnFuncNotTaken, 0);
tester.CheckResult(kI31CheckNull, 0);
tester.CheckHasThrown(kI31CastNull);
tester.CheckResult(kI31CheckSuccess, 1);
tester.CheckResult(kI31CheckFailure, 0);
tester.CheckResult(kI31CastSuccess, 1);
tester.CheckHasThrown(kI31CastFailure);
tester.CheckResult(kBrOnI31Taken, 1);
tester.CheckResult(kBrOnI31NotTaken, 0);
}
WASM_COMPILED_EXEC_TEST(BasicI31) {
WasmGCTester tester(execution_tier);
FLAG_experimental_liftoff_extern_ref = true;
......
......@@ -215,6 +215,7 @@
val, cond, kExprBrIf, static_cast<byte>(depth), kExprDrop
#define WASM_CONTINUE(depth) kExprBr, static_cast<byte>(depth)
#define WASM_UNREACHABLE kExprUnreachable
#define WASM_RETURN(...) __VA_ARGS__, kExprReturn
#define WASM_BR_TABLE(key, count, ...) \
key, kExprBrTable, U32V_1(count), __VA_ARGS__
......@@ -497,7 +498,7 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
struct_obj, value, WASM_GC_OP(kExprStructSet), static_cast<byte>(typeidx), \
static_cast<byte>(fieldidx)
#define WASM_REF_NULL(type_encoding) kExprRefNull, type_encoding
#define WASM_REF_FUNC(val) kExprRefFunc, val
#define WASM_REF_FUNC(index) kExprRefFunc, index
#define WASM_REF_IS_NULL(val) val, kExprRefIsNull
#define WASM_REF_AS_NON_NULL(val) val, kExprRefAsNonNull
#define WASM_REF_EQ(lhs, rhs) lhs, rhs, kExprRefEq
......@@ -508,6 +509,19 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_BR_ON_CAST(depth, rtt) \
rtt, WASM_GC_OP(kExprBrOnCast), static_cast<byte>(depth)
#define WASM_REF_IS_DATA(ref) ref, WASM_GC_OP(kExprRefIsData)
#define WASM_REF_AS_DATA(ref) ref, WASM_GC_OP(kExprRefAsData)
#define WASM_BR_ON_DATA(depth, ref) \
ref, WASM_GC_OP(kExprBrOnData), static_cast<byte>(depth)
#define WASM_REF_IS_FUNC(ref) ref, WASM_GC_OP(kExprRefIsFunc)
#define WASM_REF_AS_FUNC(ref) ref, WASM_GC_OP(kExprRefAsFunc)
#define WASM_BR_ON_FUNC(depth, ref) \
ref, WASM_GC_OP(kExprBrOnFunc), static_cast<byte>(depth)
#define WASM_REF_IS_I31(ref) ref, WASM_GC_OP(kExprRefIsI31)
#define WASM_REF_AS_I31(ref) ref, WASM_GC_OP(kExprRefAsI31)
#define WASM_BR_ON_I31(depth, ref) \
ref, WASM_GC_OP(kExprBrOnI31), static_cast<byte>(depth)
#define WASM_ARRAY_NEW_WITH_RTT(index, default_value, length, rtt) \
default_value, length, rtt, WASM_GC_OP(kExprArrayNewWithRtt), \
static_cast<byte>(index)
......
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