Commit 95e8d867 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by V8 LUCI CQ

[wasm-gc] Allow struct.new_with_rtt as constant expression

Changes:
- Add struct.new_with_rtt as a new WasmInitExpr. Parse it in
  consume_init_expr(). Add it to
  InstanceBuilder::EvaluateInitExpression().
- Change WasmInitExpr::operand_ to vector operands_.
- In consume_init_expr(), use parsed over hard-coded opcode length.
- Improve WasmStruct::WasmStructPrint slightly.
- Add Factory::NewWasmStruct().
- Add WasmValue::CopyToWithSystemEndianness.
- In wasm-module-builder.js, generalize emit_init_expr for expressions
  with operands. Add missing init. expression types.
- Add tests.

Bug: v8:7748
Change-Id: Ica12378d202730aff1b57c7d4240aa00ef124f8e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2940893
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75006}
parent cea8c2ca
...@@ -1864,12 +1864,18 @@ void WasmStruct::WasmStructPrint(std::ostream& os) { ...@@ -1864,12 +1864,18 @@ void WasmStruct::WasmStructPrint(std::ostream& os) {
os << base::ReadUnalignedValue<double>(field_address); os << base::ReadUnalignedValue<double>(field_address);
break; break;
case wasm::kI8: case wasm::kI8:
os << base::ReadUnalignedValue<int8_t>(field_address);
break;
case wasm::kI16: case wasm::kI16:
case wasm::kS128: os << base::ReadUnalignedValue<int16_t>(field_address);
break;
case wasm::kRef: case wasm::kRef:
case wasm::kOptRef: case wasm::kOptRef:
case wasm::kRtt: case wasm::kRtt:
case wasm::kRttWithDepth: case wasm::kRttWithDepth:
os << Brief(base::ReadUnalignedValue<Object>(field_address));
break;
case wasm::kS128:
case wasm::kBottom: case wasm::kBottom:
case wasm::kVoid: case wasm::kVoid:
os << "UNIMPLEMENTED"; // TODO(7748): Implement. os << "UNIMPLEMENTED"; // TODO(7748): Implement.
......
...@@ -67,6 +67,9 @@ ...@@ -67,6 +67,9 @@
#include "src/objects/transitions-inl.h" #include "src/objects/transitions-inl.h"
#include "src/roots/roots.h" #include "src/roots/roots.h"
#include "src/strings/unicode-inl.h" #include "src/strings/unicode-inl.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-value.h"
#endif
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -1454,6 +1457,25 @@ Handle<WasmCapiFunctionData> Factory::NewWasmCapiFunctionData( ...@@ -1454,6 +1457,25 @@ Handle<WasmCapiFunctionData> Factory::NewWasmCapiFunctionData(
return handle(result, isolate()); return handle(result, isolate());
} }
Handle<WasmStruct> Factory::NewWasmStruct(const wasm::StructType* type,
wasm::WasmValue* args,
Handle<Map> map) {
DCHECK_EQ(WasmStruct::Size(type), map->wasm_type_info().instance_size());
HeapObject raw = AllocateRaw(WasmStruct::Size(type), AllocationType::kYoung);
raw.set_map_after_allocation(*map);
WasmStruct result = WasmStruct::cast(raw);
result.set_raw_properties_or_hash(*empty_fixed_array());
for (uint32_t i = 0; i < type->field_count(); i++) {
Address address = result.RawFieldAddress(type->field_offset(i));
if (type->field(i).is_numeric()) {
args[i].CopyToWithSystemEndianness(reinterpret_cast<byte*>(address));
} else {
base::WriteUnalignedValue<Object>(address, *args[i].to_ref());
}
}
return handle(result, isolate());
}
Handle<SharedFunctionInfo> Handle<SharedFunctionInfo>
Factory::NewSharedFunctionInfoForWasmExportedFunction( Factory::NewSharedFunctionInfoForWasmExportedFunction(
Handle<String> name, Handle<WasmExportedFunctionData> data) { Handle<String> name, Handle<WasmExportedFunctionData> data) {
......
...@@ -69,6 +69,12 @@ class WasmCapiFunctionData; ...@@ -69,6 +69,12 @@ class WasmCapiFunctionData;
class WasmExportedFunctionData; class WasmExportedFunctionData;
class WasmJSFunctionData; class WasmJSFunctionData;
class WeakCell; class WeakCell;
#if V8_ENABLE_WEBASSEMBLY
namespace wasm {
class StructType;
class WasmValue;
} // namespace wasm
#endif
enum class SharedFlag : uint8_t; enum class SharedFlag : uint8_t;
enum class InitializedFlag : uint8_t; enum class InitializedFlag : uint8_t;
...@@ -569,6 +575,8 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> { ...@@ -569,6 +575,8 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
Address opt_call_target, Handle<JSReceiver> callable, int return_count, Address opt_call_target, Handle<JSReceiver> callable, int return_count,
int parameter_count, Handle<PodArray<wasm::ValueType>> serialized_sig, int parameter_count, Handle<PodArray<wasm::ValueType>> serialized_sig,
Handle<Code> wrapper_code); Handle<Code> wrapper_code);
Handle<WasmStruct> NewWasmStruct(const wasm::StructType* type,
wasm::WasmValue* args, Handle<Map> map);
Handle<SharedFunctionInfo> NewSharedFunctionInfoForWasmExportedFunction( Handle<SharedFunctionInfo> NewSharedFunctionInfoForWasmExportedFunction(
Handle<String> name, Handle<WasmExportedFunctionData> data); Handle<String> name, Handle<WasmExportedFunctionData> data);
......
...@@ -1808,10 +1808,57 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1808,10 +1808,57 @@ class ModuleDecoderImpl : public Decoder {
// the type check or stack height check at the end. // the type check or stack height check at the end.
opcode = read_prefixed_opcode<validate>(pc(), &len); opcode = read_prefixed_opcode<validate>(pc(), &len);
switch (opcode) { switch (opcode) {
case kExprStructNewWithRtt: {
if (!V8_LIKELY(enabled_features_.has_gc_experiments())) {
error(pc(),
"invalid opcode struct.new_with_rtt in init. expression, "
"enable with --experimental-wasm-gc-experiments");
return {};
}
IndexImmediate<validate> imm(this, pc() + len, "struct index");
if (!V8_LIKELY(module->has_struct(imm.index))) {
errorf(pc() + len, "invalid struct type index #%u", imm.index);
return {};
}
len += imm.length;
const StructType* type = module->struct_type(imm.index);
if (!V8_LIKELY(stack.size() >= type->field_count() + 1)) {
error(pc(), "not enough arguments for struct.new");
return {};
}
std::vector<WasmInitExpr> arguments(type->field_count() + 1);
WasmInitExpr* stack_args = &stack.back() - type->field_count();
for (uint32_t i = 0; i < type->field_count(); i++) {
WasmInitExpr& argument = stack_args[i];
if (!IsSubtypeOf(TypeOf(argument), type->field(i), module)) {
errorf(pc(), "struct.new[%u]: expected %s, found %s instead",
i, type->field(i).name().c_str(),
TypeOf(argument).name().c_str());
return {};
}
arguments[i] = std::move(argument);
}
WasmInitExpr& rtt = stack.back();
if (!IsSubtypeOf(TypeOf(rtt), ValueType::Rtt(imm.index),
module)) {
errorf(pc(), "struct.new[%u]: expected %s, found %s instead",
type->field_count(),
ValueType::Rtt(imm.index).name().c_str(),
TypeOf(rtt).name().c_str());
return {};
}
arguments[type->field_count()] = std::move(rtt);
for (uint32_t i = 0; i <= type->field_count(); i++) {
stack.pop_back();
}
stack.push_back(WasmInitExpr::StructNewWithRtt(
imm.index, std::move(arguments)));
break;
}
case kExprRttCanon: { case kExprRttCanon: {
IndexImmediate<validate> imm(this, pc() + 2, "type index"); IndexImmediate<validate> imm(this, pc() + len, "type index");
if (V8_UNLIKELY(imm.index >= module_->types.capacity())) { if (V8_UNLIKELY(!module_->has_type(imm.index))) {
errorf(pc() + 2, "type index %u is out of bounds", imm.index); errorf(pc() + len, "type index %u is out of bounds", imm.index);
return {}; return {};
} }
len += imm.length; len += imm.length;
...@@ -1826,9 +1873,9 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1826,9 +1873,9 @@ class ModuleDecoderImpl : public Decoder {
} }
V8_FALLTHROUGH; V8_FALLTHROUGH;
case kExprRttSub: { case kExprRttSub: {
IndexImmediate<validate> imm(this, pc() + 2, "type index"); IndexImmediate<validate> imm(this, pc() + len, "type index");
if (V8_UNLIKELY(imm.index >= module_->types.capacity())) { if (V8_UNLIKELY(!module_->has_type(imm.index))) {
errorf(pc() + 2, "type index %u is out of bounds", imm.index); errorf(pc() + len, "type index %u is out of bounds", imm.index);
return {}; return {};
} }
len += imm.length; len += imm.length;
......
...@@ -1669,6 +1669,18 @@ WasmValue InstanceBuilder::EvaluateInitExpression( ...@@ -1669,6 +1669,18 @@ WasmValue InstanceBuilder::EvaluateInitExpression(
UNREACHABLE(); UNREACHABLE();
} }
} }
case WasmInitExpr::kStructNewWithRtt: {
const StructType* type = module_->struct_type(init.immediate().index);
std::vector<WasmValue> fields(type->field_count());
for (uint32_t i = 0; i < type->field_count(); i++) {
fields[i] = EvaluateInitExpression(init.operands()[i], instance);
}
auto rtt = Handle<Map>::cast(
EvaluateInitExpression(init.operands().back(), instance).to_ref());
return WasmValue(
isolate_->factory()->NewWasmStruct(type, fields.data(), rtt),
init.type(module_, enabled_));
}
case WasmInitExpr::kRttCanon: { case WasmInitExpr::kRttCanon: {
int map_index = init.immediate().index; int map_index = init.immediate().index;
return WasmValue( return WasmValue(
...@@ -1678,7 +1690,7 @@ WasmValue InstanceBuilder::EvaluateInitExpression( ...@@ -1678,7 +1690,7 @@ WasmValue InstanceBuilder::EvaluateInitExpression(
case WasmInitExpr::kRttSub: case WasmInitExpr::kRttSub:
case WasmInitExpr::kRttFreshSub: { case WasmInitExpr::kRttFreshSub: {
uint32_t type = init.immediate().index; uint32_t type = init.immediate().index;
WasmValue parent = EvaluateInitExpression(*init.operand(), instance); WasmValue parent = EvaluateInitExpression(init.operands()[0], instance);
return WasmValue(AllocateSubRtt(isolate_, instance, type, return WasmValue(AllocateSubRtt(isolate_, instance, type,
Handle<Map>::cast(parent.to_ref()), Handle<Map>::cast(parent.to_ref()),
init.kind() == WasmInitExpr::kRttSub init.kind() == WasmInitExpr::kRttSub
...@@ -1698,7 +1710,7 @@ void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> instance) { ...@@ -1698,7 +1710,7 @@ void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> instance) {
WasmValue value = EvaluateInitExpression(global.init, instance); WasmValue value = EvaluateInitExpression(global.init, instance);
if (value.type().is_reference()) { if (global.type.is_reference()) {
tagged_globals_->set(global.offset, *value.to_ref()); tagged_globals_->set(global.offset, *value.to_ref());
} else { } else {
value.CopyTo(GetRawUntaggedGlobalPtr<byte>(global)); value.CopyTo(GetRawUntaggedGlobalPtr<byte>(global));
......
...@@ -38,11 +38,13 @@ ValueType WasmInitExpr::type(const WasmModule* module, ...@@ -38,11 +38,13 @@ ValueType WasmInitExpr::type(const WasmModule* module,
} }
case kRefNullConst: case kRefNullConst:
return ValueType::Ref(immediate().heap_type, kNullable); return ValueType::Ref(immediate().heap_type, kNullable);
case kStructNewWithRtt:
return ValueType::Ref(immediate().index, kNonNullable);
case kRttCanon: case kRttCanon:
return ValueType::Rtt(immediate().heap_type, 0); return ValueType::Rtt(immediate().heap_type, 0);
case kRttSub: case kRttSub:
case kRttFreshSub: { case kRttFreshSub: {
ValueType operand_type = operand()->type(module, enabled_features); ValueType operand_type = operands()[0].type(module, enabled_features);
if (!operand_type.is_rtt()) return kWasmBottom; if (!operand_type.is_rtt()) return kWasmBottom;
if (operand_type.has_depth()) { if (operand_type.has_depth()) {
return ValueType::Rtt(immediate().heap_type, operand_type.depth() + 1); return ValueType::Rtt(immediate().heap_type, operand_type.depth() + 1);
......
...@@ -33,6 +33,7 @@ class WasmInitExpr { ...@@ -33,6 +33,7 @@ class WasmInitExpr {
kS128Const, kS128Const,
kRefNullConst, kRefNullConst,
kRefFuncConst, kRefFuncConst,
kStructNewWithRtt,
kRttCanon, kRttCanon,
kRttSub, kRttSub,
kRttFreshSub, kRttFreshSub,
...@@ -88,6 +89,15 @@ class WasmInitExpr { ...@@ -88,6 +89,15 @@ class WasmInitExpr {
return expr; return expr;
} }
static WasmInitExpr StructNewWithRtt(uint32_t index,
std::vector<WasmInitExpr> elements) {
WasmInitExpr expr;
expr.kind_ = kStructNewWithRtt;
expr.immediate_.index = index;
expr.operands_ = std::move(elements);
return expr;
}
static WasmInitExpr RttCanon(uint32_t index) { static WasmInitExpr RttCanon(uint32_t index) {
WasmInitExpr expr; WasmInitExpr expr;
expr.kind_ = kRttCanon; expr.kind_ = kRttCanon;
...@@ -99,7 +109,7 @@ class WasmInitExpr { ...@@ -99,7 +109,7 @@ class WasmInitExpr {
WasmInitExpr expr; WasmInitExpr expr;
expr.kind_ = kRttSub; expr.kind_ = kRttSub;
expr.immediate_.index = index; expr.immediate_.index = index;
expr.operand_ = std::make_unique<WasmInitExpr>(std::move(supertype)); expr.operands_.push_back(std::move(supertype));
return expr; return expr;
} }
...@@ -107,13 +117,13 @@ class WasmInitExpr { ...@@ -107,13 +117,13 @@ class WasmInitExpr {
WasmInitExpr expr; WasmInitExpr expr;
expr.kind_ = kRttFreshSub; expr.kind_ = kRttFreshSub;
expr.immediate_.index = index; expr.immediate_.index = index;
expr.operand_ = std::make_unique<WasmInitExpr>(std::move(supertype)); expr.operands_.push_back(std::move(supertype));
return expr; return expr;
} }
Immediate immediate() const { return immediate_; } Immediate immediate() const { return immediate_; }
Operator kind() const { return kind_; } Operator kind() const { return kind_; }
WasmInitExpr* operand() const { return operand_.get(); } const std::vector<WasmInitExpr>& operands() const { return operands_; }
bool operator==(const WasmInitExpr& other) const { bool operator==(const WasmInitExpr& other) const {
if (kind() != other.kind()) return false; if (kind() != other.kind()) return false;
...@@ -136,14 +146,21 @@ class WasmInitExpr { ...@@ -136,14 +146,21 @@ class WasmInitExpr {
return immediate().s128_const == other.immediate().s128_const; return immediate().s128_const == other.immediate().s128_const;
case kRefNullConst: case kRefNullConst:
return immediate().heap_type == other.immediate().heap_type; return immediate().heap_type == other.immediate().heap_type;
case kStructNewWithRtt:
if (immediate().index != other.immediate().index) return false;
DCHECK_EQ(operands().size(), other.operands().size());
for (uint32_t i = 0; i < operands().size(); i++) {
if (operands()[i] != other.operands()[i]) return false;
}
return true;
case kRttSub: case kRttSub:
case kRttFreshSub: case kRttFreshSub:
return immediate().index == other.immediate().index && return immediate().index == other.immediate().index &&
*operand() == *other.operand(); operands()[0] == other.operands()[0];
} }
} }
V8_INLINE bool operator!=(const WasmInitExpr& other) { V8_INLINE bool operator!=(const WasmInitExpr& other) const {
return !(*this == other); return !(*this == other);
} }
...@@ -153,7 +170,7 @@ class WasmInitExpr { ...@@ -153,7 +170,7 @@ class WasmInitExpr {
private: private:
Immediate immediate_; Immediate immediate_;
Operator kind_; Operator kind_;
std::unique_ptr<WasmInitExpr> operand_ = nullptr; std::vector<WasmInitExpr> operands_;
}; };
} // namespace wasm } // namespace wasm
......
...@@ -517,6 +517,15 @@ void WriteInitializerExpression(ZoneBuffer* buffer, const WasmInitExpr& init, ...@@ -517,6 +517,15 @@ void WriteInitializerExpression(ZoneBuffer* buffer, const WasmInitExpr& init,
} }
break; break;
} }
case WasmInitExpr::kStructNewWithRtt:
STATIC_ASSERT((kExprStructNewWithRtt >> 8) == kGCPrefix);
buffer->write_u8(kGCPrefix);
buffer->write_u8(static_cast<uint8_t>(kExprStructNewWithRtt));
buffer->write_u32v(init.immediate().index);
for (const WasmInitExpr& operand : init.operands()) {
WriteInitializerExpression(buffer, operand, kWasmBottom);
}
break;
case WasmInitExpr::kRttCanon: case WasmInitExpr::kRttCanon:
STATIC_ASSERT((kExprRttCanon >> 8) == kGCPrefix); STATIC_ASSERT((kExprRttCanon >> 8) == kGCPrefix);
buffer->write_u8(kGCPrefix); buffer->write_u8(kGCPrefix);
...@@ -526,7 +535,7 @@ void WriteInitializerExpression(ZoneBuffer* buffer, const WasmInitExpr& init, ...@@ -526,7 +535,7 @@ void WriteInitializerExpression(ZoneBuffer* buffer, const WasmInitExpr& init,
case WasmInitExpr::kRttSub: case WasmInitExpr::kRttSub:
case WasmInitExpr::kRttFreshSub: case WasmInitExpr::kRttFreshSub:
// The operand to rtt.sub must be emitted first. // The operand to rtt.sub must be emitted first.
WriteInitializerExpression(buffer, *init.operand(), kWasmBottom); WriteInitializerExpression(buffer, init.operands()[0], kWasmBottom);
STATIC_ASSERT((kExprRttSub >> 8) == kGCPrefix); STATIC_ASSERT((kExprRttSub >> 8) == kGCPrefix);
STATIC_ASSERT((kExprRttFreshSub >> 8) == kGCPrefix); STATIC_ASSERT((kExprRttFreshSub >> 8) == kGCPrefix);
buffer->write_u8(kGCPrefix); buffer->write_u8(kGCPrefix);
......
...@@ -139,6 +139,44 @@ class WasmValue { ...@@ -139,6 +139,44 @@ class WasmValue {
type_.element_size_bytes()); type_.element_size_bytes());
} }
void CopyToWithSystemEndianness(byte* to) {
DCHECK(type_.is_numeric());
switch (type_.kind()) {
case kI32: {
int32_t value = to_i32();
base::Memcpy(static_cast<void*>(to), &value, sizeof(value));
break;
}
case kI64: {
int64_t value = to_i64();
base::Memcpy(static_cast<void*>(to), &value, sizeof(value));
break;
}
case kF32: {
float value = to_f32();
base::Memcpy(static_cast<void*>(to), &value, sizeof(value));
break;
}
case kF64: {
double value = to_f64();
base::Memcpy(static_cast<void*>(to), &value, sizeof(value));
break;
}
case kS128:
base::Memcpy(static_cast<void*>(to), to_s128().bytes(), kSimd128Size);
break;
case kRtt:
case kRttWithDepth:
case kRef:
case kOptRef:
case kBottom:
case kVoid:
case kI8:
case kI16:
UNREACHABLE();
}
}
template <typename T> template <typename T>
inline T to() const; inline T to() const;
......
...@@ -169,6 +169,7 @@ std::ostream& operator<<(std::ostream& os, const WasmInitExpr& expr) { ...@@ -169,6 +169,7 @@ std::ostream& operator<<(std::ostream& os, const WasmInitExpr& expr) {
case WasmInitExpr::kRttSub: case WasmInitExpr::kRttSub:
case WasmInitExpr::kRttFreshSub: case WasmInitExpr::kRttFreshSub:
case WasmInitExpr::kRefNullConst: case WasmInitExpr::kRefNullConst:
case WasmInitExpr::kStructNewWithRtt:
// TODO(manoskouk): Implement these. // TODO(manoskouk): Implement these.
UNIMPLEMENTED(); UNIMPLEMENTED();
case WasmInitExpr::kGlobalGet: case WasmInitExpr::kGlobalGet:
......
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --experimental-wasm-gc // Flags: --experimental-wasm-gc-experiments
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function Test1() { (function TestReferenceGlobals() {
var exporting_instance = (function () { var exporting_instance = (function() {
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
var sig_index = builder.addType(kSig_i_ii); var sig_index = builder.addType(kSig_i_ii);
...@@ -103,3 +103,49 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -103,3 +103,49 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// The correct function reference has been passed. // The correct function reference has been passed.
assertEquals(66, instance.exports.test_import(42, 24)); assertEquals(66, instance.exports.test_import(42, 24));
})(); })();
(function TestStructInitExpr() {
var builder = new WasmModuleBuilder();
var struct_index = builder.addStruct([{type: kWasmI32, mutability: false}]);
var composite_struct_index = builder.addStruct(
[{type: kWasmI32, mutability: false},
{type: wasmRefType(struct_index), mutability: false}]);
let field1_value = 432;
let field2_value = -123;
var global0 = builder.addGlobal(
wasmRefType(struct_index), false,
WasmInitExpr.StructNewWithRtt(
struct_index,
[WasmInitExpr.I32Const(field2_value),
WasmInitExpr.RttCanon(struct_index)]));
var global = builder.addGlobal(
wasmRefType(composite_struct_index), false,
WasmInitExpr.StructNewWithRtt(
composite_struct_index,
[WasmInitExpr.I32Const(field1_value),
WasmInitExpr.GlobalGet(global0.index),
WasmInitExpr.RttCanon(composite_struct_index)]));
builder.addFunction("field_1", kSig_i_v)
.addBody([
kExprGlobalGet, global.index,
kGCPrefix, kExprStructGet, composite_struct_index, 0
])
.exportFunc();
builder.addFunction("field_2", kSig_i_v)
.addBody([
kExprGlobalGet, global.index,
kGCPrefix, kExprStructGet, composite_struct_index, 1,
kGCPrefix, kExprStructGet, struct_index, 0
])
.exportFunc();
var instance = builder.instantiate({});
assertEquals(field1_value, instance.exports.field_1());
assertEquals(field2_value, instance.exports.field_2());
})();
...@@ -945,7 +945,7 @@ class Binary { ...@@ -945,7 +945,7 @@ class Binary {
} }
} }
emit_init_expr(expr) { emit_init_expr_recursive(expr) {
switch (expr.kind) { switch (expr.kind) {
case kExprGlobalGet: case kExprGlobalGet:
this.emit_u8(kExprGlobalGet); this.emit_u8(kExprGlobalGet);
...@@ -974,8 +974,35 @@ class Binary { ...@@ -974,8 +974,35 @@ class Binary {
this.emit_u8(kExprRefNull); this.emit_u8(kExprRefNull);
this.emit_u32v(expr.value); this.emit_u32v(expr.value);
break; break;
case kExprStructNewWithRtt:
for (let operand of expr.operands) {
this.emit_init_expr_recursive(operand);
}
this.emit_u8(kGCPrefix);
this.emit_u8(kExprStructNewWithRtt);
this.emit_u32v(expr.value);
break;
case kExprRttCanon:
this.emit_u8(kGCPrefix);
this.emit_u8(kExprRttCanon);
this.emit_u32v(expr.value);
break;
case kExprRttSub:
this.emit_init_expr_recursive(expr.parent);
this.emit_u8(kGcPrefix);
this.emit_u8(kExprRttSub);
this.emit_u32v(expr.value);
case kExprRttFreshSub:
this.emit_init_expr_recursive(expr.parent);
this.emit_u8(kGcPrefix);
this.emit_u8(kExprRttFreshSub);
this.emit_u32v(expr.value);
} }
this.emit_u8(kExprEnd); // end of init expression }
emit_init_expr(expr) {
this.emit_init_expr_recursive(expr);
this.emit_u8(kExprEnd);
} }
emit_header() { emit_header() {
...@@ -1099,6 +1126,18 @@ class WasmInitExpr { ...@@ -1099,6 +1126,18 @@ class WasmInitExpr {
static RefNull(type) { static RefNull(type) {
return {kind: kExprRefNull, value: type}; return {kind: kExprRefNull, value: type};
} }
static StructNewWithRtt(type, args) {
return {kind: kExprStructNewWithRtt, value: type, operands: args};
}
static RttCanon(type) {
return {kind: kExprRttCanon, value: type};
}
static RttSub(type, parent) {
return {kind: kExprRttSub, value: type, parent: parent};
}
static RttFreshSub(type, parent) {
return {kind: kExprRttFreshSub, value: type, parent: parent};
}
static defaultFor(type) { static defaultFor(type) {
switch (type) { switch (type) {
...@@ -1113,6 +1152,9 @@ class WasmInitExpr { ...@@ -1113,6 +1152,9 @@ class WasmInitExpr {
case kWasmS128: case kWasmS128:
return this.S128Const(new Array(16).fill(0)); return this.S128Const(new Array(16).fill(0));
default: default:
if ((typeof type) != 'number' && type.opcode != kWasmOptRef) {
throw new Error("Non-defaultable type");
}
let heap_type = (typeof type) == 'number' ? type : type.index; let heap_type = (typeof type) == 'number' ? type : type.index;
return this.RefNull(heap_type); return this.RefNull(heap_type);
} }
......
...@@ -35,6 +35,9 @@ namespace module_decoder_unittest { ...@@ -35,6 +35,9 @@ namespace module_decoder_unittest {
#define WASM_INIT_EXPR_FUNC_REF_NULL WASM_REF_NULL(kFuncRefCode), kExprEnd #define WASM_INIT_EXPR_FUNC_REF_NULL WASM_REF_NULL(kFuncRefCode), kExprEnd
#define WASM_INIT_EXPR_REF_FUNC(val) WASM_REF_FUNC(val), kExprEnd #define WASM_INIT_EXPR_REF_FUNC(val) WASM_REF_FUNC(val), kExprEnd
#define WASM_INIT_EXPR_GLOBAL(index) WASM_GLOBAL_GET(index), kExprEnd #define WASM_INIT_EXPR_GLOBAL(index) WASM_GLOBAL_GET(index), kExprEnd
#define WASM_INIT_EXPR_STRUCT_NEW(index, ...) \
WASM_STRUCT_NEW_WITH_RTT(index, __VA_ARGS__), kExprEnd
#define WASM_INIT_EXPR_RTT_CANON(index) WASM_RTT_CANON(index), kExprEnd
#define REF_NULL_ELEMENT kExprRefNull, kFuncRefCode, kExprEnd #define REF_NULL_ELEMENT kExprRefNull, kFuncRefCode, kExprEnd
#define REF_FUNC_ELEMENT(v) kExprRefFunc, U32V_1(v), kExprEnd #define REF_FUNC_ELEMENT(v) kExprRefFunc, U32V_1(v), kExprEnd
...@@ -1078,6 +1081,56 @@ TEST_F(WasmModuleVerifyTest, RttFreshSubGlobalTypeError) { ...@@ -1078,6 +1081,56 @@ TEST_F(WasmModuleVerifyTest, RttFreshSubGlobalTypeError) {
"(rtt 1 0)"); "(rtt 1 0)");
} }
TEST_F(WasmModuleVerifyTest, StructNewInitExpr) {
WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
WASM_FEATURE_SCOPE(gc_experiments);
static const byte basic[] = {
SECTION(Type, ENTRY_COUNT(1), // --
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true))),
SECTION(Global, ENTRY_COUNT(1), // --
kRefCode, 0, 0, // type, mutability
WASM_INIT_EXPR_STRUCT_NEW(0, WASM_I32V(42), WASM_RTT_CANON(0)))};
EXPECT_VERIFIES(basic);
static const byte global_args[] = {
SECTION(Type, ENTRY_COUNT(1), // --
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true))),
SECTION(Global, ENTRY_COUNT(3), // --
kI32Code, 0, // type, mutability
WASM_INIT_EXPR_I32V_1(10), // --
kRttCode, 0, 0, // type, mutability
WASM_RTT_SUB(0, WASM_RTT_CANON(0)), kExprEnd, // --
kRefCode, 0, 0, // type, mutability
WASM_INIT_EXPR_STRUCT_NEW(0, WASM_GLOBAL_GET(0),
WASM_GLOBAL_GET(1)))};
EXPECT_VERIFIES(global_args);
static const byte type_error[] = {
SECTION(Type, ENTRY_COUNT(2), // --
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI64Code, true))),
SECTION(Global, ENTRY_COUNT(1), // --
kRefCode, 1, 0, // type, mutability
WASM_INIT_EXPR_STRUCT_NEW(0, WASM_I32V(42), WASM_RTT_CANON(0)))};
EXPECT_FAILURE_WITH_MSG(
type_error,
"type error in init expression, expected (ref 1), got (ref 0)");
static const byte subexpr_type_error[] = {
SECTION(Type, ENTRY_COUNT(2), // --
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI64Code, true))),
SECTION(Global, ENTRY_COUNT(1), // --
kRefCode, 0, 0, // type, mutability
WASM_INIT_EXPR_STRUCT_NEW(0, WASM_I32V(42), WASM_RTT_CANON(1)))};
EXPECT_FAILURE_WITH_MSG(
subexpr_type_error,
"struct.new[1]: expected (rtt 0), found (rtt 0 1) instead");
}
TEST_F(WasmModuleVerifyTest, EmptyStruct) { TEST_F(WasmModuleVerifyTest, EmptyStruct) {
WASM_FEATURE_SCOPE(reftypes); WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref); WASM_FEATURE_SCOPE(typed_funcref);
......
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