Commit 4d4cdaf4 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[wasm-gc] Support i31ref in ref.test/ref.cast

We must perform "smi-checks" before loading an object's map
whenever the object might be an i31ref.

Bug: v8:7748
Change-Id: I2d9839ddcb0c2e8c35b9bea38afe50d55dd084cb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2299370Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68893}
parent 3720f905
...@@ -5373,13 +5373,48 @@ Node* WasmGraphBuilder::RttSub(wasm::HeapType type, Node* parent_rtt) { ...@@ -5373,13 +5373,48 @@ Node* WasmGraphBuilder::RttSub(wasm::HeapType type, Node* parent_rtt) {
LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer())); LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer()));
} }
Node* IsI31(GraphAssembler* gasm, Node* object) {
if (COMPRESS_POINTERS_BOOL) {
return gasm->Word32Equal(
gasm->Word32And(object, gasm->Int32Constant(kSmiTagMask)),
gasm->Int32Constant(kSmiTag));
} else {
return gasm->WordEqual(
gasm->WordAnd(object, gasm->IntPtrConstant(kSmiTagMask)),
gasm->IntPtrConstant(kSmiTag));
}
}
void AssertFalse(GraphAssembler* gasm, Node* condition) {
#if DEBUG
if (FLAG_debug_code) {
auto ok = gasm->MakeLabel();
gasm->GotoIfNot(condition, &ok);
gasm->Unreachable();
gasm->Goto(&ok);
gasm->Bind(&ok);
}
#endif
}
Node* WasmGraphBuilder::RefTest(Node* object, Node* rtt, Node* WasmGraphBuilder::RefTest(Node* object, Node* rtt,
CheckForNull null_check) { CheckForNull null_check, CheckForI31 i31_check,
// TODO(7748): Check if {object} is an i31ref. RttIsI31 rtt_is_i31) {
auto done = gasm_->MakeLabel(MachineRepresentation::kWord32); auto done = gasm_->MakeLabel(MachineRepresentation::kWord32);
bool need_done_label = false;
if (i31_check == kWithI31Check) {
if (rtt_is_i31 == kRttIsI31) {
return IsI31(gasm_.get(), object);
}
gasm_->GotoIf(IsI31(gasm_.get(), object), &done, gasm_->Int32Constant(0));
need_done_label = true;
} else {
AssertFalse(gasm_.get(), IsI31(gasm_.get(), object));
}
if (null_check == kWithNullCheck) { if (null_check == kWithNullCheck) {
gasm_->GotoIf(gasm_->WordEqual(object, RefNull()), &done, gasm_->GotoIf(gasm_->WordEqual(object, RefNull()), &done,
gasm_->Int32Constant(0)); gasm_->Int32Constant(0));
need_done_label = true;
} }
Node* map = gasm_->Load(MachineType::TaggedPointer(), object, Node* map = gasm_->Load(MachineType::TaggedPointer(), object,
...@@ -5389,7 +5424,7 @@ Node* WasmGraphBuilder::RefTest(Node* object, Node* rtt, ...@@ -5389,7 +5424,7 @@ Node* WasmGraphBuilder::RefTest(Node* object, Node* rtt,
WasmIsRttSubtype, map, rtt, WasmIsRttSubtype, map, rtt,
LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer()))); LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer())));
if (null_check == kWithNullCheck) { if (need_done_label) {
gasm_->Goto(&done, subtype_check); gasm_->Goto(&done, subtype_check);
gasm_->Bind(&done); gasm_->Bind(&done);
subtype_check = done.PhiAt(0); subtype_check = done.PhiAt(0);
...@@ -5398,9 +5433,19 @@ Node* WasmGraphBuilder::RefTest(Node* object, Node* rtt, ...@@ -5398,9 +5433,19 @@ Node* WasmGraphBuilder::RefTest(Node* object, Node* rtt,
} }
Node* WasmGraphBuilder::RefCast(Node* object, Node* rtt, Node* WasmGraphBuilder::RefCast(Node* object, Node* rtt,
CheckForNull null_check, CheckForNull null_check, CheckForI31 i31_check,
RttIsI31 rtt_is_i31,
wasm::WasmCodePosition position) { wasm::WasmCodePosition position) {
// TODO(7748): Check if {object} is an i31ref. if (i31_check == kWithI31Check) {
if (rtt_is_i31 == kRttIsI31) {
TrapIfFalse(wasm::kTrapIllegalCast, IsI31(gasm_.get(), object), position);
return object;
} else {
TrapIfTrue(wasm::kTrapIllegalCast, IsI31(gasm_.get(), object), position);
}
} else {
AssertFalse(gasm_.get(), IsI31(gasm_.get(), object));
}
if (null_check == kWithNullCheck) { if (null_check == kWithNullCheck) {
TrapIfTrue(wasm::kTrapIllegalCast, gasm_->WordEqual(object, RefNull()), TrapIfTrue(wasm::kTrapIllegalCast, gasm_->WordEqual(object, RefNull()),
position); position);
......
...@@ -167,6 +167,14 @@ class WasmGraphBuilder { ...@@ -167,6 +167,14 @@ class WasmGraphBuilder {
kWithNullCheck = true, kWithNullCheck = true,
kWithoutNullCheck = false kWithoutNullCheck = false
}; };
enum CheckForI31 : bool { // --
kWithI31Check = true,
kNoI31Check = false
};
enum RttIsI31 : bool { // --
kRttIsI31 = true,
kRttIsNotI31 = false
};
V8_EXPORT_PRIVATE WasmGraphBuilder( V8_EXPORT_PRIVATE WasmGraphBuilder(
wasm::CompilationEnv* env, Zone* zone, MachineGraph* mcgraph, wasm::CompilationEnv* env, Zone* zone, MachineGraph* mcgraph,
...@@ -407,8 +415,10 @@ class WasmGraphBuilder { ...@@ -407,8 +415,10 @@ class WasmGraphBuilder {
Node* I31GetU(Node* input); Node* I31GetU(Node* input);
Node* RttCanon(wasm::HeapType type); Node* RttCanon(wasm::HeapType type);
Node* RttSub(wasm::HeapType type, Node* parent_rtt); Node* RttSub(wasm::HeapType type, Node* parent_rtt);
Node* RefTest(Node* object, Node* rtt, CheckForNull null_check); Node* RefTest(Node* object, Node* rtt, CheckForNull null_check,
CheckForI31 i31_check, RttIsI31 rtt_is_i31);
Node* RefCast(Node* object, Node* rtt, CheckForNull null_check, Node* RefCast(Node* object, Node* rtt, CheckForNull null_check,
CheckForI31 i31_check, RttIsI31 rtt_is_i31,
wasm::WasmCodePosition position); wasm::WasmCodePosition position);
bool has_simd() const { return has_simd_; } bool has_simd() const { return has_simd_; }
......
...@@ -751,20 +751,39 @@ class WasmGraphBuildingInterface { ...@@ -751,20 +751,39 @@ class WasmGraphBuildingInterface {
void RefTest(FullDecoder* decoder, const Value& object, const Value& rtt, void RefTest(FullDecoder* decoder, const Value& object, const Value& rtt,
Value* result) { Value* result) {
using CheckForNull = compiler::WasmGraphBuilder::CheckForNull; using CheckForNull = compiler::WasmGraphBuilder::CheckForNull;
using CheckForI31 = compiler::WasmGraphBuilder::CheckForI31;
using RttIsI31 = compiler::WasmGraphBuilder::RttIsI31;
CheckForNull null_check = object.type.is_nullable() CheckForNull null_check = object.type.is_nullable()
? CheckForNull::kWithNullCheck ? CheckForNull::kWithNullCheck
: CheckForNull::kWithoutNullCheck; : CheckForNull::kWithoutNullCheck;
result->node = BUILD(RefTest, object.node, rtt.node, null_check); CheckForI31 i31_check =
IsSubtypeOf(kWasmI31Ref, object.type, decoder->module_)
? CheckForI31::kWithI31Check
: CheckForI31::kNoI31Check;
RttIsI31 rtt_is_i31 = rtt.type.heap_representation() == HeapType::kI31
? RttIsI31::kRttIsI31
: RttIsI31::kRttIsNotI31;
result->node = BUILD(RefTest, object.node, rtt.node, null_check, i31_check,
rtt_is_i31);
} }
void RefCast(FullDecoder* decoder, const Value& object, const Value& rtt, void RefCast(FullDecoder* decoder, const Value& object, const Value& rtt,
Value* result) { Value* result) {
using CheckForNull = compiler::WasmGraphBuilder::CheckForNull; using CheckForNull = compiler::WasmGraphBuilder::CheckForNull;
using CheckForI31 = compiler::WasmGraphBuilder::CheckForI31;
using RttIsI31 = compiler::WasmGraphBuilder::RttIsI31;
CheckForNull null_check = object.type.is_nullable() CheckForNull null_check = object.type.is_nullable()
? CheckForNull::kWithNullCheck ? CheckForNull::kWithNullCheck
: CheckForNull::kWithoutNullCheck; : CheckForNull::kWithoutNullCheck;
result->node = CheckForI31 i31_check =
BUILD(RefCast, object.node, rtt.node, null_check, decoder->position()); IsSubtypeOf(kWasmI31Ref, object.type, decoder->module_)
? CheckForI31::kWithI31Check
: CheckForI31::kNoI31Check;
RttIsI31 rtt_is_i31 = rtt.type.heap_representation() == HeapType::kI31
? RttIsI31::kRttIsI31
: RttIsI31::kRttIsNotI31;
result->node = BUILD(RefCast, object.node, rtt.node, null_check, i31_check,
rtt_is_i31, decoder->position());
} }
void PassThrough(FullDecoder* decoder, const Value& from, Value* to) { void PassThrough(FullDecoder* decoder, const Value& from, Value* to) {
......
...@@ -692,6 +692,64 @@ TEST(BasicI31) { ...@@ -692,6 +692,64 @@ TEST(BasicI31) {
tester.CheckResult(kUnsigned, 0x7FFFFFFF, 0x7FFFFFFF); tester.CheckResult(kUnsigned, 0x7FFFFFFF, 0x7FFFFFFF);
} }
TEST(I31Casts) {
WasmGCTester tester;
uint32_t struct_type = tester.DefineStruct({F(wasm::kWasmI32, true)});
uint32_t i31_rtt = tester.AddGlobal(ValueType::Rtt(HeapType::kI31, 1), false,
WasmInitExpr::RttCanon(HeapType::kI31));
uint32_t struct_rtt =
tester.AddGlobal(ValueType::Rtt(struct_type, 1), false,
WasmInitExpr::RttCanon(
static_cast<HeapType::Representation>(struct_type)));
// Adds the result of a successful typecheck to the untagged value, i.e.
// should return 1 + 42 = 43.
const uint32_t kTestAndCastSuccess = tester.DefineFunction(
tester.sigs.i_v(), {kWasmEqRef},
{WASM_SET_LOCAL(0, WASM_I31_NEW(WASM_I32V(42))),
WASM_I32_ADD(WASM_REF_TEST(kLocalEqRef, kLocalI31Ref, WASM_GET_LOCAL(0),
WASM_GET_GLOBAL(i31_rtt)),
WASM_I31_GET_S(WASM_REF_CAST(kLocalEqRef, kLocalI31Ref,
WASM_GET_LOCAL(0),
WASM_GET_GLOBAL(i31_rtt)))),
kExprEnd});
// Adds the results of two unsuccessful type checks (an i31ref is not a
// struct, nor the other way round).
const uint32_t kTestFalse = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_I32_ADD(
WASM_REF_TEST(kLocalEqRef, kLocalI31Ref,
WASM_STRUCT_NEW_WITH_RTT(struct_type, WASM_I32V(42),
WASM_GET_GLOBAL(struct_rtt)),
WASM_GET_GLOBAL(i31_rtt)),
WASM_REF_TEST(kLocalEqRef, struct_type, WASM_I31_NEW(WASM_I32V(23)),
WASM_GET_GLOBAL(struct_rtt))),
kExprEnd});
// Tries to cast an i31ref to a struct, which should trap.
const uint32_t kCastI31ToStruct = tester.DefineFunction(
tester.sigs.i_i(), // Argument and return value ignored
{},
{WASM_STRUCT_GET(
struct_type, 0,
WASM_REF_CAST(kLocalEqRef, struct_type, WASM_I31_NEW(WASM_I32V(42)),
WASM_GET_GLOBAL(struct_rtt))),
kExprEnd});
// Tries to cast a struct to i31ref, which should trap.
const uint32_t kCastStructToI31 = tester.DefineFunction(
tester.sigs.i_i(), // Argument and return value ignored
{},
{WASM_I31_GET_S(
WASM_REF_CAST(kLocalEqRef, kLocalI31Ref,
WASM_STRUCT_NEW_WITH_RTT(struct_type, WASM_I32V(42),
WASM_GET_GLOBAL(struct_rtt)),
WASM_GET_GLOBAL(i31_rtt))),
kExprEnd});
tester.CompileModule();
tester.CheckResult(kTestAndCastSuccess, 43);
tester.CheckResult(kTestFalse, 0);
tester.CheckHasThrown(kCastI31ToStruct, 0);
tester.CheckHasThrown(kCastStructToI31, 0);
}
TEST(JsAccessDisallowed) { TEST(JsAccessDisallowed) {
WasmGCTester tester; WasmGCTester tester;
uint32_t type_index = tester.DefineStruct({F(wasm::kWasmI32, true)}); uint32_t type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
......
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