Commit 1646c9be authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Add mutability and packed types to arrays/structs

Bug: v8:7748
Change-Id: I4ae500548e7ab09f5bd037563af5c057751197bb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2215049Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67996}
parent 5058c397
...@@ -3223,10 +3223,18 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -3223,10 +3223,18 @@ class WasmFullDecoder : public WasmDecoder<validate> {
case kExprStructGet: { case kExprStructGet: {
FieldIndexImmediate<validate> field(this, this->pc_ + len); FieldIndexImmediate<validate> field(this, this->pc_ + len);
if (!this->Validate(this->pc_ + len, field)) break; if (!this->Validate(this->pc_ + len, field)) break;
ValueType field_type =
field.struct_index.struct_type->field(field.index);
if (field_type.IsPacked()) {
this->error(this->pc_,
"struct.get used with a field of packed type. "
"Use struct.get_s or struct.get_u instead.");
break;
}
len += field.length; len += field.length;
Value struct_obj = Value struct_obj =
Pop(0, ValueType(ValueType::kOptRef, field.struct_index.index)); Pop(0, ValueType(ValueType::kOptRef, field.struct_index.index));
Value* value = Push(field.struct_index.struct_type->field(field.index)); Value* value = Push(field_type);
CALL_INTERFACE_IF_REACHABLE(StructGet, struct_obj, field, value); CALL_INTERFACE_IF_REACHABLE(StructGet, struct_obj, field, value);
break; break;
} }
...@@ -3234,8 +3242,14 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -3234,8 +3242,14 @@ class WasmFullDecoder : public WasmDecoder<validate> {
FieldIndexImmediate<validate> field(this, this->pc_ + len); FieldIndexImmediate<validate> field(this, this->pc_ + len);
if (!this->Validate(this->pc_ + len, field)) break; if (!this->Validate(this->pc_ + len, field)) break;
len += field.length; len += field.length;
if (!field.struct_index.struct_type->mutability(field.index)) {
this->error(this->pc_, "setting immutable struct field");
break;
}
Value field_value = Pop( Value field_value = Pop(
0, ValueType(field.struct_index.struct_type->field(field.index))); 0,
ValueType(
field.struct_index.struct_type->field(field.index).Unpack()));
Value struct_obj = Value struct_obj =
Pop(0, ValueType(ValueType::kOptRef, field.struct_index.index)); Pop(0, ValueType(ValueType::kOptRef, field.struct_index.index));
CALL_INTERFACE_IF_REACHABLE(StructSet, struct_obj, field, field_value); CALL_INTERFACE_IF_REACHABLE(StructSet, struct_obj, field, field_value);
...@@ -3256,6 +3270,12 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -3256,6 +3270,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
ArrayIndexImmediate<validate> imm(this, this->pc_ + len); ArrayIndexImmediate<validate> imm(this, this->pc_ + len);
len += imm.length; len += imm.length;
if (!this->Validate(this->pc_ + len, imm)) break; if (!this->Validate(this->pc_ + len, imm)) break;
if (imm.array_type->element_type().IsPacked()) {
this->error(this->pc_,
"array.get used with a field of packed type. "
"Use array.get_s or array.get_u instead.");
break;
}
Value index = Pop(0, kWasmI32); Value index = Pop(0, kWasmI32);
Value array_obj = Pop(0, ValueType(ValueType::kOptRef, imm.index)); Value array_obj = Pop(0, ValueType(ValueType::kOptRef, imm.index));
Value* value = Push(imm.array_type->element_type()); Value* value = Push(imm.array_type->element_type());
...@@ -3267,7 +3287,11 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -3267,7 +3287,11 @@ class WasmFullDecoder : public WasmDecoder<validate> {
ArrayIndexImmediate<validate> imm(this, this->pc_ + len); ArrayIndexImmediate<validate> imm(this, this->pc_ + len);
len += imm.length; len += imm.length;
if (!this->Validate(this->pc_ + len, imm)) break; if (!this->Validate(this->pc_ + len, imm)) break;
Value value = Pop(0, imm.array_type->element_type()); if (!imm.array_type->mutability()) {
this->error(this->pc_, "setting element of immutable array");
break;
}
Value value = Pop(0, imm.array_type->element_type().Unpack());
Value index = Pop(0, kWasmI32); Value index = Pop(0, kWasmI32);
Value array_obj = Pop(0, ValueType(ValueType::kOptRef, imm.index)); Value array_obj = Pop(0, ValueType(ValueType::kOptRef, imm.index));
// TODO(7748): Optimize this when array_obj is non-nullable ref. // TODO(7748): Optimize this when array_obj is non-nullable ref.
......
...@@ -1754,6 +1754,21 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1754,6 +1754,21 @@ class ModuleDecoderImpl : public Decoder {
return result; return result;
} }
ValueType consume_storage_type() {
uint8_t opcode = read_u8<kValidate>(this->pc());
switch (opcode) {
case kLocalI8:
consume_bytes(1);
return kWasmI8;
case kLocalI16:
consume_bytes(1);
return kWasmI16;
default:
// It is not a packed type, so it has to be a value type.
return consume_value_type();
}
}
// Reads a single 8-bit integer, interpreting it as a reference type. // Reads a single 8-bit integer, interpreting it as a reference type.
ValueType consume_reference_type() { ValueType consume_reference_type() {
byte val = consume_u8("reference type"); byte val = consume_u8("reference type");
...@@ -1824,30 +1839,35 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1824,30 +1839,35 @@ class ModuleDecoderImpl : public Decoder {
// TODO(7748): Introduce a proper maximum. // TODO(7748): Introduce a proper maximum.
uint32_t field_count = consume_count("field count", 999); uint32_t field_count = consume_count("field count", 999);
if (failed()) return nullptr; if (failed()) return nullptr;
std::vector<ValueType> fields; ValueType* fields = zone->NewArray<ValueType>(field_count);
bool* mutabilities = zone->NewArray<bool>(field_count);
for (uint32_t i = 0; ok() && i < field_count; ++i) { for (uint32_t i = 0; ok() && i < field_count; ++i) {
ValueType field = consume_value_type(); ValueType field = consume_storage_type();
if (field.has_immediate()) { if (field.has_immediate()) {
deferred_struct_field_type_offsets_.emplace(field.ref_index(), deferred_struct_field_type_offsets_.emplace(field.ref_index(),
pc_offset()); pc_offset());
} }
fields.push_back(field); fields[i] = field;
bool mutability = consume_mutability();
mutabilities[i] = mutability;
} }
if (failed()) return nullptr; if (failed()) return nullptr;
ValueType* buffer = zone->NewArray<ValueType>(field_count);
for (uint32_t i = 0; i < field_count; i++) buffer[i] = fields[i];
uint32_t* offsets = zone->NewArray<uint32_t>(field_count); uint32_t* offsets = zone->NewArray<uint32_t>(field_count);
return new (zone) StructType(field_count, offsets, buffer); return new (zone) StructType(field_count, offsets, fields, mutabilities);
} }
const ArrayType* consume_array(Zone* zone) { const ArrayType* consume_array(Zone* zone) {
ValueType field = consume_value_type(); ValueType field = consume_storage_type();
if (failed()) return nullptr; if (failed()) return nullptr;
if (field.has_immediate()) { if (field.has_immediate()) {
deferred_struct_field_type_offsets_.emplace(field.ref_index(), deferred_struct_field_type_offsets_.emplace(field.ref_index(),
pc_offset()); pc_offset());
} }
return new (zone) ArrayType(field); bool mutability = consume_mutability();
if (!mutability) {
error(this->pc() - 1, "immutable arrays are not supported yet");
}
return new (zone) ArrayType(field, mutability);
} }
// Consume the attribute field of an exception. // Consume the attribute field of an exception.
......
...@@ -18,8 +18,11 @@ namespace wasm { ...@@ -18,8 +18,11 @@ namespace wasm {
class StructType : public ZoneObject { class StructType : public ZoneObject {
public: public:
StructType(uint32_t field_count, uint32_t* field_offsets, StructType(uint32_t field_count, uint32_t* field_offsets,
const ValueType* reps) const ValueType* reps, const bool* mutabilities)
: field_count_(field_count), field_offsets_(field_offsets), reps_(reps) { : field_count_(field_count),
field_offsets_(field_offsets),
reps_(reps),
mutabilities_(mutabilities) {
InitializeOffsets(); InitializeOffsets();
} }
...@@ -30,15 +33,26 @@ class StructType : public ZoneObject { ...@@ -30,15 +33,26 @@ class StructType : public ZoneObject {
return reps_[index]; return reps_[index];
} }
bool mutability(uint32_t index) const {
DCHECK_LT(index, field_count_);
return mutabilities_[index];
}
// Iteration support. // Iteration support.
base::iterator_range<const ValueType*> fields() const { base::iterator_range<const ValueType*> fields() const {
return {reps_, reps_ + field_count_}; return {reps_, reps_ + field_count_};
} }
base::iterator_range<const bool*> mutabilities() const {
return {mutabilities_, mutabilities_ + field_count_};
}
bool operator==(const StructType& other) const { bool operator==(const StructType& other) const {
if (this == &other) return true; if (this == &other) return true;
if (field_count() != other.field_count()) return false; if (field_count() != other.field_count()) return false;
return std::equal(fields().begin(), fields().end(), other.fields().begin()); return std::equal(fields().begin(), fields().end(),
other.fields().begin()) &&
std::equal(mutabilities().begin(), mutabilities().end(),
other.mutabilities().begin());
} }
bool operator!=(const StructType& other) const { return !(*this == other); } bool operator!=(const StructType& other) const { return !(*this == other); }
...@@ -70,17 +84,20 @@ class StructType : public ZoneObject { ...@@ -70,17 +84,20 @@ class StructType : public ZoneObject {
: field_count_(field_count), : field_count_(field_count),
zone_(zone), zone_(zone),
cursor_(0), cursor_(0),
buffer_(zone->NewArray<ValueType>(static_cast<int>(field_count))) {} buffer_(zone->NewArray<ValueType>(static_cast<int>(field_count))),
mutabilities_(zone->NewArray<bool>(static_cast<int>(field_count))) {}
void AddField(ValueType type) { void AddField(ValueType type, bool mutability) {
DCHECK_LT(cursor_, field_count_); DCHECK_LT(cursor_, field_count_);
mutabilities_[cursor_] = mutability;
buffer_[cursor_++] = type; buffer_[cursor_++] = type;
} }
StructType* Build() { StructType* Build() {
DCHECK_EQ(cursor_, field_count_); DCHECK_EQ(cursor_, field_count_);
uint32_t* offsets = zone_->NewArray<uint32_t>(field_count_); uint32_t* offsets = zone_->NewArray<uint32_t>(field_count_);
return new (zone_) StructType(field_count_, offsets, buffer_); return new (zone_)
StructType(field_count_, offsets, buffer_, mutabilities_);
} }
private: private:
...@@ -88,25 +105,30 @@ class StructType : public ZoneObject { ...@@ -88,25 +105,30 @@ class StructType : public ZoneObject {
Zone* zone_; Zone* zone_;
uint32_t cursor_; uint32_t cursor_;
ValueType* buffer_; ValueType* buffer_;
bool* mutabilities_;
}; };
private: private:
uint32_t field_count_; uint32_t field_count_;
uint32_t* field_offsets_; uint32_t* field_offsets_;
const ValueType* reps_; const ValueType* reps_;
const bool* mutabilities_;
}; };
class ArrayType : public ZoneObject { class ArrayType : public ZoneObject {
public: public:
constexpr explicit ArrayType(ValueType rep) : rep_(rep) {} constexpr explicit ArrayType(ValueType rep, bool mutability)
: rep_(rep), mutability_(mutability) {}
ValueType element_type() const { return rep_; } ValueType element_type() const { return rep_; }
bool mutability() const { return mutability_; }
bool operator==(const ArrayType& other) const { return rep_ == other.rep_; } bool operator==(const ArrayType& other) const { return rep_ == other.rep_; }
bool operator!=(const ArrayType& other) const { return rep_ != other.rep_; } bool operator!=(const ArrayType& other) const { return rep_ != other.rep_; }
private: private:
const ValueType rep_; const ValueType rep_;
const bool mutability_;
}; };
} // namespace wasm } // namespace wasm
......
...@@ -450,8 +450,9 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const { ...@@ -450,8 +450,9 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
StructType* struct_type = type.struct_type; StructType* struct_type = type.struct_type;
buffer->write_u8(kWasmStructTypeCode); buffer->write_u8(kWasmStructTypeCode);
buffer->write_size(struct_type->field_count()); buffer->write_size(struct_type->field_count());
for (auto field : struct_type->fields()) { for (uint32_t i = 0; i < struct_type->field_count(); i++) {
WriteValueType(buffer, field); WriteValueType(buffer, struct_type->field(i));
buffer->write_u8(struct_type->mutability(i) ? 1 : 0);
} }
break; break;
} }
...@@ -459,6 +460,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const { ...@@ -459,6 +460,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
ArrayType* array_type = type.array_type; ArrayType* array_type = type.array_type;
buffer->write_u8(kWasmArrayTypeCode); buffer->write_u8(kWasmArrayTypeCode);
WriteValueType(buffer, array_type->element_type()); WriteValueType(buffer, array_type->element_type());
buffer->write_u8(array_type->mutability() ? 1 : 0);
break; break;
} }
} }
......
...@@ -37,8 +37,8 @@ WASM_EXEC_TEST(BasicStruct) { ...@@ -37,8 +37,8 @@ WASM_EXEC_TEST(BasicStruct) {
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
StructType::Builder type_builder(&zone, 2); StructType::Builder type_builder(&zone, 2);
type_builder.AddField(kWasmI32); type_builder.AddField(kWasmI32, true);
type_builder.AddField(kWasmI32); type_builder.AddField(kWasmI32, true);
int32_t type_index = builder->AddStructType(type_builder.Build()); int32_t type_index = builder->AddStructType(type_builder.Build());
ValueType kRefTypes[] = {ValueType(ValueType::kRef, type_index)}; ValueType kRefTypes[] = {ValueType(ValueType::kRef, type_index)};
ValueType kOptRefType = ValueType(ValueType::kOptRef, type_index); ValueType kOptRefType = ValueType(ValueType::kOptRef, type_index);
...@@ -218,8 +218,8 @@ WASM_EXEC_TEST(LetInstruction) { ...@@ -218,8 +218,8 @@ WASM_EXEC_TEST(LetInstruction) {
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
StructType::Builder type_builder(&zone, 2); StructType::Builder type_builder(&zone, 2);
type_builder.AddField(kWasmI32); type_builder.AddField(kWasmI32, true);
type_builder.AddField(kWasmI32); type_builder.AddField(kWasmI32, true);
int32_t type_index = builder->AddStructType(type_builder.Build()); int32_t type_index = builder->AddStructType(type_builder.Build());
ValueType kRefTypes[] = {ValueType(ValueType::kRef, type_index)}; ValueType kRefTypes[] = {ValueType(ValueType::kRef, type_index)};
FunctionSig sig_q_v(1, 0, kRefTypes); FunctionSig sig_q_v(1, 0, kRefTypes);
...@@ -316,7 +316,7 @@ WASM_EXEC_TEST(BasicArray) { ...@@ -316,7 +316,7 @@ WASM_EXEC_TEST(BasicArray) {
Zone zone(&allocator, ZONE_NAME); Zone zone(&allocator, ZONE_NAME);
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
ArrayType type(wasm::kWasmI32); ArrayType type(wasm::kWasmI32, true);
int32_t type_index = builder->AddArrayType(&type); int32_t type_index = builder->AddArrayType(&type);
ValueType kRefTypes[] = {ValueType(ValueType::kRef, type_index)}; ValueType kRefTypes[] = {ValueType(ValueType::kRef, type_index)};
FunctionSig sig_q_v(1, 0, kRefTypes); FunctionSig sig_q_v(1, 0, kRefTypes);
......
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