[wasm-gc] Implement {array,struct}.new_default_with_rtt
Bug: v8:7748 Change-Id: If876c9499373f091067299fe333e7b59d6cefb41 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2343077 Reviewed-by: Manos Koukoutos <manoskouk@chromium.org> Commit-Queue: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/master@{#69305}
This commit is contained in:
parent
54f2fa94dc
commit
d3fab076a9
@ -854,8 +854,7 @@ class WasmStruct::BodyDescriptor final : public BodyDescriptorBase {
|
||||
wasm::StructType* type = WasmStruct::GcSafeType(map);
|
||||
for (uint32_t i = 0; i < type->field_count(); i++) {
|
||||
if (!type->field(i).is_reference_type()) continue;
|
||||
int offset =
|
||||
WasmStruct::kHeaderSize + static_cast<int>(type->field_offset(i));
|
||||
int offset = static_cast<int>(type->field_offset(i));
|
||||
v->VisitPointer(wasm_struct, wasm_struct.RawField(offset));
|
||||
}
|
||||
}
|
||||
|
@ -3515,6 +3515,12 @@ class LiftoffCompiler {
|
||||
// TODO(7748): Implement.
|
||||
unsupported(decoder, kGC, "struct.new_with_rtt");
|
||||
}
|
||||
void StructNewDefault(FullDecoder* decoder,
|
||||
const StructIndexImmediate<validate>& imm,
|
||||
const Value& rtt, Value* result) {
|
||||
// TODO(7748): Implement.
|
||||
unsupported(decoder, kGC, "struct.new_default_with_rtt");
|
||||
}
|
||||
void StructGet(FullDecoder* decoder, const Value& struct_obj,
|
||||
const FieldIndexImmediate<validate>& field, bool is_signed,
|
||||
Value* result) {
|
||||
@ -3535,6 +3541,12 @@ class LiftoffCompiler {
|
||||
// TODO(7748): Implement.
|
||||
unsupported(decoder, kGC, "array.new_with_rtt");
|
||||
}
|
||||
void ArrayNewDefault(FullDecoder* decoder,
|
||||
const ArrayIndexImmediate<validate>& imm,
|
||||
const Value& length, const Value& rtt, Value* result) {
|
||||
// TODO(7748): Implement.
|
||||
unsupported(decoder, kGC, "array.new_default_with_rtt");
|
||||
}
|
||||
void ArrayGet(FullDecoder* decoder, const Value& array_obj,
|
||||
const ArrayIndexImmediate<validate>& imm, const Value& index,
|
||||
bool is_signed, Value* result) {
|
||||
|
@ -947,6 +947,8 @@ struct ControlBase {
|
||||
const Value& value, const Value& count) \
|
||||
F(StructNewWithRtt, const StructIndexImmediate<validate>& imm, \
|
||||
const Value& rtt, const Value args[], Value* result) \
|
||||
F(StructNewDefault, const StructIndexImmediate<validate>& imm, \
|
||||
const Value& rtt, Value* result) \
|
||||
F(StructGet, const Value& struct_object, \
|
||||
const FieldIndexImmediate<validate>& field, bool is_signed, Value* result) \
|
||||
F(StructSet, const Value& struct_object, \
|
||||
@ -954,6 +956,8 @@ struct ControlBase {
|
||||
F(ArrayNewWithRtt, const ArrayIndexImmediate<validate>& imm, \
|
||||
const Value& length, const Value& initial_value, const Value& rtt, \
|
||||
Value* result) \
|
||||
F(ArrayNewDefault, const ArrayIndexImmediate<validate>& imm, \
|
||||
const Value& length, const Value& rtt, Value* result) \
|
||||
F(ArrayGet, const Value& array_obj, \
|
||||
const ArrayIndexImmediate<validate>& imm, const Value& index, \
|
||||
bool is_signed, Value* result) \
|
||||
@ -1839,6 +1843,7 @@ class WasmDecoder : public Decoder {
|
||||
case kGCPrefix: {
|
||||
opcode = this->read_prefixed_opcode<validate>(pc);
|
||||
switch (opcode) {
|
||||
case kExprStructNewDefault:
|
||||
case kExprStructGet:
|
||||
case kExprStructGetS:
|
||||
case kExprStructGetU:
|
||||
@ -1850,6 +1855,7 @@ class WasmDecoder : public Decoder {
|
||||
return {1, 1};
|
||||
case kExprStructSet:
|
||||
return {2, 0};
|
||||
case kExprArrayNewDefault:
|
||||
case kExprArrayGet:
|
||||
case kExprArrayGetS:
|
||||
case kExprArrayGetU:
|
||||
@ -3406,19 +3412,18 @@ class WasmFullDecoder : public WasmDecoder<validate> {
|
||||
switch (opcode) {
|
||||
case kExprStructNewWithRtt: {
|
||||
StructIndexImmediate<validate> imm(this, this->pc_ + 2);
|
||||
if (!this->Validate(this->pc_, imm)) return 0;
|
||||
if (!this->Validate(this->pc_ + 2, imm)) return 0;
|
||||
Value rtt = Pop(imm.struct_type->field_count());
|
||||
if (!VALIDATE(rtt.type.kind() == ValueType::kRtt)) {
|
||||
this->errorf(
|
||||
this->pc_ + 2,
|
||||
"struct.new_with_rtt expected type rtt, found %s of type %s",
|
||||
SafeOpcodeNameAt(rtt.pc), rtt.type.name().c_str());
|
||||
this->errorf(this->pc_,
|
||||
"struct.new_with_rtt expected rtt, found %s of type %s",
|
||||
SafeOpcodeNameAt(rtt.pc), rtt.type.name().c_str());
|
||||
return 0;
|
||||
}
|
||||
// TODO(7748): Drop this check if {imm} is dropped from the proposal
|
||||
// à la https://github.com/WebAssembly/function-references/pull/31.
|
||||
if (!VALIDATE(rtt.type.heap_representation() == imm.index)) {
|
||||
this->errorf(this->pc_ + 2,
|
||||
this->errorf(this->pc_,
|
||||
"struct.new_with_rtt expected rtt for type %d, found "
|
||||
"rtt for type %s",
|
||||
imm.index, rtt.type.heap_type().name().c_str());
|
||||
@ -3430,6 +3435,43 @@ class WasmFullDecoder : public WasmDecoder<validate> {
|
||||
value);
|
||||
return 2 + imm.length;
|
||||
}
|
||||
case kExprStructNewDefault: {
|
||||
StructIndexImmediate<validate> imm(this, this->pc_ + 2);
|
||||
if (!this->Validate(this->pc_ + 2, imm)) return 0;
|
||||
if (validate) {
|
||||
for (uint32_t i = 0; i < imm.struct_type->field_count(); i++) {
|
||||
ValueType ftype = imm.struct_type->field(i);
|
||||
if (!VALIDATE(ftype.is_defaultable())) {
|
||||
this->errorf(this->pc_,
|
||||
"struct.new_default_with_rtt: struct type %d has "
|
||||
"non-defaultable type %s for field %d",
|
||||
imm.index, ftype.name().c_str(), i);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Value rtt = Pop(0);
|
||||
if (!VALIDATE(rtt.type.kind() == ValueType::kRtt)) {
|
||||
this->errorf(
|
||||
this->pc_,
|
||||
"struct.new_default_with_rtt expected rtt, found %s of type %s",
|
||||
SafeOpcodeNameAt(rtt.pc), rtt.type.name().c_str());
|
||||
return 0;
|
||||
}
|
||||
// TODO(7748): Drop this check if {imm} is dropped from the proposal
|
||||
// à la https://github.com/WebAssembly/function-references/pull/31.
|
||||
if (!VALIDATE(rtt.type.heap_representation() == imm.index)) {
|
||||
this->errorf(
|
||||
this->pc_,
|
||||
"struct.new_default_with_rtt expected rtt for type %d, found "
|
||||
"rtt for type %s",
|
||||
imm.index, rtt.type.heap_type().name().c_str());
|
||||
return 0;
|
||||
}
|
||||
Value* value = Push(ValueType::Ref(imm.index, kNonNullable));
|
||||
CALL_INTERFACE_IF_REACHABLE(StructNewDefault, imm, rtt, value);
|
||||
return 2 + imm.length;
|
||||
}
|
||||
case kExprStructGet: {
|
||||
FieldIndexImmediate<validate> field(this, this->pc_ + 2);
|
||||
if (!this->Validate(this->pc_ + 2, field)) return 0;
|
||||
@ -3486,10 +3528,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
|
||||
if (!this->Validate(this->pc_ + 2, imm)) return 0;
|
||||
Value rtt = Pop(2);
|
||||
if (!VALIDATE(rtt.type.kind() == ValueType::kRtt)) {
|
||||
this->errorf(
|
||||
this->pc_ + 2,
|
||||
"array.new_with_rtt expected type rtt, found %s of type %s",
|
||||
SafeOpcodeNameAt(rtt.pc), rtt.type.name().c_str());
|
||||
this->errorf(this->pc_ + 2,
|
||||
"array.new_with_rtt expected rtt, found %s of type %s",
|
||||
SafeOpcodeNameAt(rtt.pc), rtt.type.name().c_str());
|
||||
return 0;
|
||||
}
|
||||
// TODO(7748): Drop this check if {imm} is dropped from the proposal
|
||||
@ -3508,6 +3549,39 @@ class WasmFullDecoder : public WasmDecoder<validate> {
|
||||
rtt, value);
|
||||
return 2 + imm.length;
|
||||
}
|
||||
case kExprArrayNewDefault: {
|
||||
ArrayIndexImmediate<validate> imm(this, this->pc_ + 2);
|
||||
if (!this->Validate(this->pc_ + 2, imm)) return 0;
|
||||
if (!VALIDATE(imm.array_type->element_type().is_defaultable())) {
|
||||
this->errorf(this->pc_,
|
||||
"array.new_default_with_rtt: array type %d has "
|
||||
"non-defaultable element type %s",
|
||||
imm.index,
|
||||
imm.array_type->element_type().name().c_str());
|
||||
return 0;
|
||||
}
|
||||
Value rtt = Pop(1);
|
||||
if (!VALIDATE(rtt.type.kind() == ValueType::kRtt)) {
|
||||
this->errorf(
|
||||
this->pc_ + 2,
|
||||
"array.new_default_with_rtt expected rtt, found %s of type %s",
|
||||
SafeOpcodeNameAt(rtt.pc), rtt.type.name().c_str());
|
||||
return 0;
|
||||
}
|
||||
// TODO(7748): Drop this check if {imm} is dropped from the proposal
|
||||
// à la https://github.com/WebAssembly/function-references/pull/31.
|
||||
if (!VALIDATE(rtt.type.heap_representation() == imm.index)) {
|
||||
this->errorf(this->pc_ + 2,
|
||||
"array.new_default_with_rtt expected rtt for type %d, "
|
||||
"found rtt for type %s",
|
||||
imm.index, rtt.type.heap_type().name().c_str());
|
||||
return 0;
|
||||
}
|
||||
Value length = Pop(0, kWasmI32);
|
||||
Value* value = Push(ValueType::Ref(imm.index, kNonNullable));
|
||||
CALL_INTERFACE_IF_REACHABLE(ArrayNewDefault, imm, length, rtt, value);
|
||||
return 2 + imm.length;
|
||||
}
|
||||
case kExprArrayGetS:
|
||||
case kExprArrayGetU: {
|
||||
ArrayIndexImmediate<validate> imm(this, this->pc_ + 2);
|
||||
|
@ -657,6 +657,17 @@ class WasmGraphBuildingInterface {
|
||||
result->node = BUILD(StructNewWithRtt, imm.index, imm.struct_type, rtt.node,
|
||||
VectorOf(arg_nodes));
|
||||
}
|
||||
void StructNewDefault(FullDecoder* decoder,
|
||||
const StructIndexImmediate<validate>& imm,
|
||||
const Value& rtt, Value* result) {
|
||||
uint32_t field_count = imm.struct_type->field_count();
|
||||
base::SmallVector<TFNode*, 16> arg_nodes(field_count);
|
||||
for (uint32_t i = 0; i < field_count; i++) {
|
||||
arg_nodes[i] = DefaultValue(imm.struct_type->field(i));
|
||||
}
|
||||
result->node = BUILD(StructNewWithRtt, imm.index, imm.struct_type, rtt.node,
|
||||
VectorOf(arg_nodes));
|
||||
}
|
||||
|
||||
void StructGet(FullDecoder* decoder, const Value& struct_object,
|
||||
const FieldIndexImmediate<validate>& field, bool is_signed,
|
||||
@ -689,6 +700,14 @@ class WasmGraphBuildingInterface {
|
||||
length.node, initial_value.node, rtt.node);
|
||||
}
|
||||
|
||||
void ArrayNewDefault(FullDecoder* decoder,
|
||||
const ArrayIndexImmediate<validate>& imm,
|
||||
const Value& length, const Value& rtt, Value* result) {
|
||||
TFNode* initial_value = DefaultValue(imm.array_type->element_type());
|
||||
result->node = BUILD(ArrayNewWithRtt, imm.index, imm.array_type,
|
||||
length.node, initial_value, rtt.node);
|
||||
}
|
||||
|
||||
void ArrayGet(FullDecoder* decoder, const Value& array_obj,
|
||||
const ArrayIndexImmediate<validate>& imm, const Value& index,
|
||||
bool is_signed, Value* result) {
|
||||
|
@ -673,6 +673,39 @@ TEST(WasmPackedArrayS) {
|
||||
tester.CheckResult(kF, static_cast<int16_t>(expected_outputs[3]), 3);
|
||||
}
|
||||
|
||||
TEST(NewDefault) {
|
||||
WasmGCTester tester;
|
||||
const byte struct_type = tester.DefineStruct(
|
||||
{F(wasm::kWasmI32, true), F(wasm::kWasmF64, true), F(optref(0), true)});
|
||||
const byte array_type = tester.DefineArray(wasm::kWasmI32, true);
|
||||
// Returns: struct[0] + f64_to_i32(struct[1]) + (struct[2].is_null ^ 1) == 0.
|
||||
const byte allocate_struct = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {optref(struct_type)},
|
||||
{WASM_SET_LOCAL(0, WASM_STRUCT_NEW_DEFAULT(struct_type,
|
||||
WASM_RTT_CANON(struct_type))),
|
||||
WASM_I32_ADD(
|
||||
WASM_I32_ADD(WASM_STRUCT_GET(struct_type, 0, WASM_GET_LOCAL(0)),
|
||||
WASM_I32_SCONVERT_F64(WASM_STRUCT_GET(
|
||||
struct_type, 1, WASM_GET_LOCAL(0)))),
|
||||
WASM_I32_XOR(WASM_REF_IS_NULL(
|
||||
WASM_STRUCT_GET(struct_type, 2, WASM_GET_LOCAL(0))),
|
||||
WASM_I32V(1))),
|
||||
kExprEnd});
|
||||
const byte allocate_array = tester.DefineFunction(
|
||||
tester.sigs.i_v(), {optref(array_type)},
|
||||
{WASM_SET_LOCAL(0, WASM_ARRAY_NEW_DEFAULT(array_type, WASM_I32V(2),
|
||||
WASM_RTT_CANON(array_type))),
|
||||
WASM_I32_ADD(
|
||||
WASM_ARRAY_GET(array_type, WASM_GET_LOCAL(0), WASM_I32V(0)),
|
||||
WASM_ARRAY_GET(array_type, WASM_GET_LOCAL(0), WASM_I32V(1))),
|
||||
kExprEnd});
|
||||
|
||||
tester.CompileModule();
|
||||
|
||||
tester.CheckResult(allocate_struct, 0);
|
||||
tester.CheckResult(allocate_array, 0);
|
||||
}
|
||||
|
||||
TEST(BasicRTT) {
|
||||
WasmGCTester tester;
|
||||
const byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
|
||||
|
@ -81,7 +81,6 @@
|
||||
#define DEPTH_0 0
|
||||
#define DEPTH_1 1
|
||||
#define DEPTH_2 2
|
||||
#define ARITY_2 2
|
||||
|
||||
#define WASM_HEAP_TYPE(heap_type) static_cast<byte>((heap_type).code() & 0x7f)
|
||||
|
||||
@ -429,6 +428,8 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
|
||||
#define WASM_GC_OP(op) kGCPrefix, static_cast<byte>(op)
|
||||
#define WASM_STRUCT_NEW_WITH_RTT(index, ...) \
|
||||
__VA_ARGS__, WASM_GC_OP(kExprStructNewWithRtt), static_cast<byte>(index)
|
||||
#define WASM_STRUCT_NEW_DEFAULT(index, rtt) \
|
||||
rtt, WASM_GC_OP(kExprStructNewDefault), static_cast<byte>(index)
|
||||
#define WASM_STRUCT_GET(typeidx, fieldidx, struct_obj) \
|
||||
struct_obj, WASM_GC_OP(kExprStructGet), static_cast<byte>(typeidx), \
|
||||
static_cast<byte>(fieldidx)
|
||||
@ -458,6 +459,8 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
|
||||
#define WASM_ARRAY_NEW_WITH_RTT(index, default_value, length, rtt) \
|
||||
default_value, length, rtt, WASM_GC_OP(kExprArrayNewWithRtt), \
|
||||
static_cast<byte>(index)
|
||||
#define WASM_ARRAY_NEW_DEFAULT(index, length, rtt) \
|
||||
length, rtt, WASM_GC_OP(kExprArrayNewDefault), static_cast<byte>(index)
|
||||
#define WASM_ARRAY_GET(typeidx, array, index) \
|
||||
array, index, WASM_GC_OP(kExprArrayGet), static_cast<byte>(typeidx)
|
||||
#define WASM_ARRAY_GET_U(typeidx, array, index) \
|
||||
|
@ -3398,6 +3398,39 @@ ValueType optref(byte type_index) {
|
||||
return ValueType::Ref(type_index, kNullable);
|
||||
}
|
||||
|
||||
TEST_F(FunctionBodyDecoderTest, StructNewDefault) {
|
||||
WASM_FEATURE_SCOPE(reftypes);
|
||||
WASM_FEATURE_SCOPE(typed_funcref);
|
||||
WASM_FEATURE_SCOPE(gc);
|
||||
{
|
||||
TestModuleBuilder builder;
|
||||
byte type_index = builder.AddStruct({F(kWasmI32, true)});
|
||||
byte bad_type_index = builder.AddStruct({F(ref(type_index), true)});
|
||||
module = builder.module();
|
||||
ExpectValidates(sigs.v_v(), {WASM_STRUCT_NEW_DEFAULT(
|
||||
type_index, WASM_RTT_CANON(type_index)),
|
||||
WASM_DROP});
|
||||
ExpectFailure(sigs.v_v(),
|
||||
{WASM_STRUCT_NEW_DEFAULT(bad_type_index,
|
||||
WASM_RTT_CANON(bad_type_index)),
|
||||
WASM_DROP});
|
||||
}
|
||||
{
|
||||
TestModuleBuilder builder;
|
||||
byte type_index = builder.AddArray(kWasmI32, true);
|
||||
byte bad_type_index = builder.AddArray(ref(type_index), true);
|
||||
module = builder.module();
|
||||
ExpectValidates(sigs.v_v(),
|
||||
{WASM_ARRAY_NEW_DEFAULT(type_index, WASM_I32V(3),
|
||||
WASM_RTT_CANON(type_index)),
|
||||
WASM_DROP});
|
||||
ExpectFailure(sigs.v_v(),
|
||||
{WASM_ARRAY_NEW_DEFAULT(bad_type_index, WASM_I32V(3),
|
||||
WASM_RTT_CANON(bad_type_index)),
|
||||
WASM_DROP});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(FunctionBodyDecoderTest, DefaultableLocal) {
|
||||
WASM_FEATURE_SCOPE(typed_funcref);
|
||||
WASM_FEATURE_SCOPE(reftypes);
|
||||
|
Loading…
Reference in New Issue
Block a user