[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:
Jakob Kummerow 2020-08-07 15:14:23 +02:00 committed by Commit Bot
parent 54f2fa94dc
commit d3fab076a9
7 changed files with 186 additions and 13 deletions

View File

@ -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));
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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) {

View File

@ -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)});

View File

@ -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) \

View File

@ -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);