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

[wasm-gc] Add array.init constant expression

Bug: v8:7748
Change-Id: I3fa510b4dc35d3f58532ecbbeecd79d2826ff667
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2951722
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@{#75115}
parent e4921a2a
...@@ -1457,6 +1457,29 @@ Handle<WasmCapiFunctionData> Factory::NewWasmCapiFunctionData( ...@@ -1457,6 +1457,29 @@ Handle<WasmCapiFunctionData> Factory::NewWasmCapiFunctionData(
return handle(result, isolate()); return handle(result, isolate());
} }
Handle<WasmArray> Factory::NewWasmArray(
const wasm::ArrayType* type, const std::vector<wasm::WasmValue>& elements,
Handle<Map> map) {
uint32_t length = static_cast<uint32_t>(elements.size());
HeapObject raw =
AllocateRaw(WasmArray::SizeFor(*map, length), AllocationType::kYoung);
raw.set_map_after_allocation(*map);
WasmArray result = WasmArray::cast(raw);
result.set_raw_properties_or_hash(*empty_fixed_array());
result.set_length(length);
for (uint32_t i = 0; i < length; i++) {
Address address = result.ElementAddress(i);
if (type->element_type().is_numeric()) {
elements[i]
.Packed(type->element_type())
.CopyTo(reinterpret_cast<byte*>(address));
} else {
base::WriteUnalignedValue<Object>(address, *elements[i].to_ref());
}
}
return handle(result, isolate());
}
Handle<WasmStruct> Factory::NewWasmStruct(const wasm::StructType* type, Handle<WasmStruct> Factory::NewWasmStruct(const wasm::StructType* type,
wasm::WasmValue* args, wasm::WasmValue* args,
Handle<Map> map) { Handle<Map> map) {
......
...@@ -71,6 +71,7 @@ class WasmJSFunctionData; ...@@ -71,6 +71,7 @@ class WasmJSFunctionData;
class WeakCell; class WeakCell;
#if V8_ENABLE_WEBASSEMBLY #if V8_ENABLE_WEBASSEMBLY
namespace wasm { namespace wasm {
class ArrayType;
class StructType; class StructType;
class WasmValue; class WasmValue;
} // namespace wasm } // namespace wasm
...@@ -578,6 +579,9 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> { ...@@ -578,6 +579,9 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
Handle<Code> wrapper_code); Handle<Code> wrapper_code);
Handle<WasmStruct> NewWasmStruct(const wasm::StructType* type, Handle<WasmStruct> NewWasmStruct(const wasm::StructType* type,
wasm::WasmValue* args, Handle<Map> map); wasm::WasmValue* args, Handle<Map> map);
Handle<WasmArray> NewWasmArray(const wasm::ArrayType* type,
const std::vector<wasm::WasmValue>& elements,
Handle<Map> map);
Handle<SharedFunctionInfo> NewSharedFunctionInfoForWasmExportedFunction( Handle<SharedFunctionInfo> NewSharedFunctionInfoForWasmExportedFunction(
Handle<String> name, Handle<WasmExportedFunctionData> data); Handle<String> name, Handle<WasmExportedFunctionData> data);
......
...@@ -1823,7 +1823,10 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1823,7 +1823,10 @@ class ModuleDecoderImpl : public Decoder {
len += imm.length; len += imm.length;
const StructType* type = module->struct_type(imm.index); const StructType* type = module->struct_type(imm.index);
if (!V8_LIKELY(stack.size() >= type->field_count() + 1)) { if (!V8_LIKELY(stack.size() >= type->field_count() + 1)) {
error(pc(), "not enough arguments for struct.new"); errorf(pc(),
"not enough arguments on the stack for struct.new: "
"expected %u, found %zu",
type->field_count() + 1, stack.size());
return {}; return {};
} }
std::vector<WasmInitExpr> arguments(type->field_count() + 1); std::vector<WasmInitExpr> arguments(type->field_count() + 1);
...@@ -1856,6 +1859,71 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1856,6 +1859,71 @@ class ModuleDecoderImpl : public Decoder {
imm.index, std::move(arguments))); imm.index, std::move(arguments)));
break; break;
} }
case kExprArrayInit: {
if (!V8_LIKELY(enabled_features_.has_gc_experiments())) {
error(pc(),
"invalid opcode array.init in init. expression, enable "
"with --experimental-wasm-gc-experiments");
return {};
}
IndexImmediate<validate> array_imm(this, pc() + len,
"array index");
if (!V8_LIKELY(module->has_array(array_imm.index))) {
errorf(pc() + len, "invalid array type index #%u",
array_imm.index);
return {};
}
IndexImmediate<validate> length_imm(
this, pc() + len + array_imm.length, "array.init length");
uint32_t elem_count = length_imm.index;
if (elem_count > kV8MaxWasmArrayInitLength) {
errorf(pc() + len + array_imm.length,
"Requested length %u for array.init too large, maximum "
"is %zu",
length_imm.index, kV8MaxWasmArrayInitLength);
return {};
}
len += array_imm.length + length_imm.length;
const ArrayType* array_type =
module_->array_type(array_imm.index);
if (stack.size() < elem_count + 1) {
errorf(pc(),
"not enough arguments on the stack for array.init: "
"expected %u, found %zu",
elem_count + 1, stack.size());
return {};
}
std::vector<WasmInitExpr> arguments(elem_count + 1);
WasmInitExpr* stack_args = &stack.back() - elem_count;
for (uint32_t i = 0; i < elem_count; i++) {
WasmInitExpr& argument = stack_args[i];
if (!IsSubtypeOf(TypeOf(argument),
array_type->element_type().Unpacked(),
module)) {
errorf(pc(), "array.init[%u]: expected %s, found %s instead",
i, array_type->element_type().name().c_str(),
TypeOf(argument).name().c_str());
return {};
}
arguments[i] = std::move(argument);
}
WasmInitExpr& rtt = stack.back();
if (!IsSubtypeOf(TypeOf(rtt), ValueType::Rtt(array_imm.index),
module)) {
errorf(pc(), "array.init[%u]: expected %s, found %s instead",
elem_count,
ValueType::Rtt(array_imm.index).name().c_str(),
TypeOf(rtt).name().c_str());
return {};
}
arguments[elem_count] = std::move(rtt);
for (uint32_t i = 0; i <= elem_count; i++) {
stack.pop_back();
}
stack.push_back(WasmInitExpr::ArrayInit(array_imm.index,
std::move(arguments)));
break;
}
case kExprRttCanon: { case kExprRttCanon: {
IndexImmediate<validate> imm(this, pc() + len, "type index"); IndexImmediate<validate> imm(this, pc() + len, "type index");
if (V8_UNLIKELY(!module_->has_type(imm.index))) { if (V8_UNLIKELY(!module_->has_type(imm.index))) {
...@@ -2049,7 +2117,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -2049,7 +2117,7 @@ class ModuleDecoderImpl : public Decoder {
ValueType field = consume_storage_type(); ValueType field = consume_storage_type();
if (failed()) return nullptr; if (failed()) return nullptr;
bool mutability = consume_mutability(); bool mutability = consume_mutability();
if (!mutability) { if (!V8_LIKELY(mutability)) {
error(this->pc() - 1, "immutable arrays are not supported yet"); error(this->pc() - 1, "immutable arrays are not supported yet");
} }
return zone->New<ArrayType>(field, mutability); return zone->New<ArrayType>(field, mutability);
......
...@@ -1576,6 +1576,17 @@ WasmValue InstanceBuilder::EvaluateInitExpression( ...@@ -1576,6 +1576,17 @@ WasmValue InstanceBuilder::EvaluateInitExpression(
isolate_->factory()->NewWasmStruct(type, fields.data(), rtt), isolate_->factory()->NewWasmStruct(type, fields.data(), rtt),
init.type(module_, enabled_)); init.type(module_, enabled_));
} }
case WasmInitExpr::kArrayInit: {
const ArrayType* type = module_->array_type(init.immediate().index);
std::vector<WasmValue> elements(init.operands().size() - 1);
for (uint32_t i = 0; i < elements.size(); i++) {
elements[i] = EvaluateInitExpression(init.operands()[i], instance);
}
auto rtt = Handle<Map>::cast(
EvaluateInitExpression(init.operands().back(), instance).to_ref());
return WasmValue(isolate_->factory()->NewWasmArray(type, elements, 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(
......
...@@ -39,6 +39,7 @@ ValueType WasmInitExpr::type(const WasmModule* module, ...@@ -39,6 +39,7 @@ 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: case kStructNewWithRtt:
case kArrayInit:
return ValueType::Ref(immediate().index, kNonNullable); return ValueType::Ref(immediate().index, kNonNullable);
case kRttCanon: case kRttCanon:
return ValueType::Rtt(immediate().heap_type, 0); return ValueType::Rtt(immediate().heap_type, 0);
......
...@@ -34,6 +34,7 @@ class WasmInitExpr { ...@@ -34,6 +34,7 @@ class WasmInitExpr {
kRefNullConst, kRefNullConst,
kRefFuncConst, kRefFuncConst,
kStructNewWithRtt, kStructNewWithRtt,
kArrayInit,
kRttCanon, kRttCanon,
kRttSub, kRttSub,
kRttFreshSub, kRttFreshSub,
...@@ -98,6 +99,15 @@ class WasmInitExpr { ...@@ -98,6 +99,15 @@ class WasmInitExpr {
return expr; return expr;
} }
static WasmInitExpr ArrayInit(uint32_t index,
std::vector<WasmInitExpr> elements) {
WasmInitExpr expr;
expr.kind_ = kArrayInit;
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;
...@@ -153,6 +163,13 @@ class WasmInitExpr { ...@@ -153,6 +163,13 @@ class WasmInitExpr {
if (operands()[i] != other.operands()[i]) return false; if (operands()[i] != other.operands()[i]) return false;
} }
return true; return true;
case kArrayInit:
if (immediate().index != other.immediate().index) return false;
if (operands().size() != other.operands().size()) return false;
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 &&
......
...@@ -61,6 +61,7 @@ constexpr uint32_t kV8MaxRttSubtypingDepth = 31; ...@@ -61,6 +61,7 @@ constexpr uint32_t kV8MaxRttSubtypingDepth = 31;
// Maximum supported by implementation: ((1<<27)-3). // Maximum supported by implementation: ((1<<27)-3).
// Reason: total object size in bytes must fit into a Smi, for filler objects. // Reason: total object size in bytes must fit into a Smi, for filler objects.
constexpr size_t kV8MaxWasmArrayLength = 1u << 26; constexpr size_t kV8MaxWasmArrayLength = 1u << 26;
constexpr size_t kV8MaxWasmArrayInitLength = 999;
static_assert(kV8MaxWasmTableSize <= 4294967295, // 2^32 - 1 static_assert(kV8MaxWasmTableSize <= 4294967295, // 2^32 - 1
"v8 should not exceed WebAssembly's non-web embedding limits"); "v8 should not exceed WebAssembly's non-web embedding limits");
......
...@@ -526,6 +526,16 @@ void WriteInitializerExpression(ZoneBuffer* buffer, const WasmInitExpr& init, ...@@ -526,6 +526,16 @@ void WriteInitializerExpression(ZoneBuffer* buffer, const WasmInitExpr& init,
buffer->write_u8(static_cast<uint8_t>(kExprStructNewWithRtt)); buffer->write_u8(static_cast<uint8_t>(kExprStructNewWithRtt));
buffer->write_u32v(init.immediate().index); buffer->write_u32v(init.immediate().index);
break; break;
case WasmInitExpr::kArrayInit:
STATIC_ASSERT((kExprArrayInit >> 8) == kGCPrefix);
for (const WasmInitExpr& operand : init.operands()) {
WriteInitializerExpression(buffer, operand, kWasmBottom);
}
buffer->write_u8(kGCPrefix);
buffer->write_u8(static_cast<uint8_t>(kExprArrayInit));
buffer->write_u32v(init.immediate().index);
buffer->write_u32v(static_cast<uint32_t>(init.operands().size() - 1));
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);
......
...@@ -1685,6 +1685,12 @@ ObjectSlot WasmArray::ElementSlot(uint32_t index) { ...@@ -1685,6 +1685,12 @@ ObjectSlot WasmArray::ElementSlot(uint32_t index) {
return RawField(kHeaderSize + kTaggedSize * index); return RawField(kHeaderSize + kTaggedSize * index);
} }
Address WasmArray::ElementAddress(uint32_t index) {
DCHECK_LE(index, length());
return ptr() + WasmArray::kHeaderSize +
index * type()->element_type().element_size_bytes() - kHeapObjectTag;
}
// static // static
Handle<WasmExceptionObject> WasmExceptionObject::New( Handle<WasmExceptionObject> WasmExceptionObject::New(
Isolate* isolate, const wasm::FunctionSig* sig, Isolate* isolate, const wasm::FunctionSig* sig,
......
...@@ -1000,6 +1000,9 @@ class WasmArray : public TorqueGeneratedWasmArray<WasmArray, WasmObject> { ...@@ -1000,6 +1000,9 @@ class WasmArray : public TorqueGeneratedWasmArray<WasmArray, WasmObject> {
Handle<WasmArray> array, Handle<WasmArray> array,
uint32_t index); uint32_t index);
// Returns the Address of the element at {index}.
Address ElementAddress(uint32_t index);
DECL_CAST(WasmArray) DECL_CAST(WasmArray)
DECL_PRINTER(WasmArray) DECL_PRINTER(WasmArray)
......
...@@ -396,6 +396,7 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { ...@@ -396,6 +396,7 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(ArraySet, "array.set") CASE_OP(ArraySet, "array.set")
CASE_OP(ArrayLen, "array.len") CASE_OP(ArrayLen, "array.len")
CASE_OP(ArrayCopy, "array.copy") CASE_OP(ArrayCopy, "array.copy")
CASE_OP(ArrayInit, "array.init")
CASE_OP(I31New, "i31.new") CASE_OP(I31New, "i31.new")
CASE_OP(I31GetS, "i31.get_s") CASE_OP(I31GetS, "i31.get_s")
CASE_OP(I31GetU, "i31.get_u") CASE_OP(I31GetU, "i31.get_u")
......
...@@ -664,6 +664,7 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig, ...@@ -664,6 +664,7 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(ArraySet, 0xfb16, _) \ V(ArraySet, 0xfb16, _) \
V(ArrayLen, 0xfb17, _) \ V(ArrayLen, 0xfb17, _) \
V(ArrayCopy, 0xfb18, _) /* not standardized - V8 experimental */ \ V(ArrayCopy, 0xfb18, _) /* not standardized - V8 experimental */ \
V(ArrayInit, 0xfb19, _) /* not standardized - V8 experimental */ \
V(I31New, 0xfb20, _) \ V(I31New, 0xfb20, _) \
V(I31GetS, 0xfb21, _) \ V(I31GetS, 0xfb21, _) \
V(I31GetU, 0xfb22, _) \ V(I31GetU, 0xfb22, _) \
......
...@@ -560,6 +560,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -560,6 +560,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
dst_array, dst_index, src_array, src_index, length, \ dst_array, dst_index, src_array, src_index, length, \
WASM_GC_OP(kExprArrayCopy), static_cast<byte>(dst_idx), \ WASM_GC_OP(kExprArrayCopy), static_cast<byte>(dst_idx), \
static_cast<byte>(src_idx) static_cast<byte>(src_idx)
#define WASM_ARRAY_INIT(index, length, ...) \
__VA_ARGS__, WASM_GC_OP(kExprArrayInit), static_cast<byte>(index), \
static_cast<byte>(length)
#define WASM_RTT_WITH_DEPTH(depth, typeidx) \ #define WASM_RTT_WITH_DEPTH(depth, typeidx) \
kRttWithDepthCode, U32V_1(depth), U32V_1(typeidx) kRttWithDepthCode, U32V_1(depth), U32V_1(typeidx)
......
...@@ -170,6 +170,7 @@ std::ostream& operator<<(std::ostream& os, const WasmInitExpr& expr) { ...@@ -170,6 +170,7 @@ std::ostream& operator<<(std::ostream& os, const WasmInitExpr& expr) {
case WasmInitExpr::kRttFreshSub: case WasmInitExpr::kRttFreshSub:
case WasmInitExpr::kRefNullConst: case WasmInitExpr::kRefNullConst:
case WasmInitExpr::kStructNewWithRtt: case WasmInitExpr::kStructNewWithRtt:
case WasmInitExpr::kArrayInit:
// TODO(manoskouk): Implement these. // TODO(manoskouk): Implement these.
UNIMPLEMENTED(); UNIMPLEMENTED();
case WasmInitExpr::kGlobalGet: case WasmInitExpr::kGlobalGet:
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestReferenceGlobals() { (function TestReferenceGlobals() {
print(arguments.callee.name);
var exporting_instance = (function() { var exporting_instance = (function() {
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
...@@ -105,6 +107,8 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -105,6 +107,8 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
})(); })();
(function TestStructInitExpr() { (function TestStructInitExpr() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
var struct_index = builder.addStruct([{type: kWasmI32, mutability: false}]); var struct_index = builder.addStruct([{type: kWasmI32, mutability: false}]);
var composite_struct_index = builder.addStruct( var composite_struct_index = builder.addStruct(
...@@ -158,3 +162,96 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -158,3 +162,96 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
assertEquals(field2_value, instance.exports.field_2()); assertEquals(field2_value, instance.exports.field_2());
assertEquals((field3_value << 24) >> 24, instance.exports.field_3()); assertEquals((field3_value << 24) >> 24, instance.exports.field_3());
})(); })();
(function TestArrayInitExprNumeric() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
var array_index = builder.addArray(kWasmI16, true);
let element0_value = -44;
let element1_value = 55;
var global0 = builder.addGlobal(
kWasmI32, false,
WasmInitExpr.I32Const(element0_value));
var global = builder.addGlobal(
wasmRefType(array_index), false,
WasmInitExpr.ArrayInit(
array_index,
[WasmInitExpr.GlobalGet(global0.index),
WasmInitExpr.I32Const(element1_value),
WasmInitExpr.RttCanon(array_index)]));
builder.addFunction("get_element", kSig_i_i)
.addBody([
kExprGlobalGet, global.index,
kExprLocalGet, 0,
kGCPrefix, kExprArrayGetS, array_index])
.exportFunc();
var instance = builder.instantiate({});
assertEquals(element0_value, instance.exports.get_element(0));
assertEquals(element1_value, instance.exports.get_element(1));
})();
(function TestArrayInitExprRef() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
var struct_index = builder.addStruct([{type: kWasmI32, mutability: false}]);
var array_index = builder.addArray(wasmOptRefType(struct_index), true);
let element0_value = 44;
let element2_value = 55;
var global0 = builder.addGlobal(
wasmRefType(struct_index), false,
WasmInitExpr.StructNewWithRtt(
struct_index,
[WasmInitExpr.I32Const(element0_value),
WasmInitExpr.RttCanon(struct_index)]));
var global = builder.addGlobal(
wasmRefType(array_index), false,
WasmInitExpr.ArrayInit(
array_index,
[WasmInitExpr.GlobalGet(global0.index),
WasmInitExpr.RefNull(struct_index),
WasmInitExpr.StructNewWithRtt(
struct_index,
[WasmInitExpr.I32Const(element2_value),
WasmInitExpr.RttCanon(struct_index)]),
WasmInitExpr.RttCanon(array_index)]));
builder.addFunction("element0", kSig_i_v)
.addBody([
kExprGlobalGet, global.index,
kExprI32Const, 0,
kGCPrefix, kExprArrayGet, array_index,
kGCPrefix, kExprStructGet, struct_index, 0])
.exportFunc();
builder.addFunction("element1", makeSig([], [kWasmAnyRef]))
.addBody([
kExprGlobalGet, global.index,
kExprI32Const, 1,
kGCPrefix, kExprArrayGet, array_index])
.exportFunc();
builder.addFunction("element2", kSig_i_v)
.addBody([
kExprGlobalGet, global.index,
kExprI32Const, 2,
kGCPrefix, kExprArrayGet, array_index,
kGCPrefix, kExprStructGet, struct_index, 0])
.exportFunc();
var instance = builder.instantiate({});
assertEquals(element0_value, instance.exports.element0());
assertEquals(null, instance.exports.element1());
assertEquals(element2_value, instance.exports.element2());
})();
...@@ -15,7 +15,7 @@ let instances = []; ...@@ -15,7 +15,7 @@ let instances = [];
function createArray_i() { function createArray_i() {
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();
const type_index = builder.addArray(kWasmI32); const type_index = builder.addArray(kWasmI32, true);
let sig_a_i = makeSig_r_x(kWasmDataRef, kWasmI32); let sig_a_i = makeSig_r_x(kWasmDataRef, kWasmI32);
let sig_i_ai = makeSig([kWasmDataRef, kWasmI32], [kWasmI32]); let sig_i_ai = makeSig([kWasmDataRef, kWasmI32], [kWasmI32]);
......
...@@ -458,6 +458,8 @@ let kExprArrayGetS = 0x14; ...@@ -458,6 +458,8 @@ let kExprArrayGetS = 0x14;
let kExprArrayGetU = 0x15; let kExprArrayGetU = 0x15;
let kExprArraySet = 0x16; let kExprArraySet = 0x16;
let kExprArrayLen = 0x17; let kExprArrayLen = 0x17;
let kExprArrayCopy = 0x18;
let kExprArrayInit = 0x19;
let kExprI31New = 0x20; let kExprI31New = 0x20;
let kExprI31GetS = 0x21; let kExprI31GetS = 0x21;
let kExprI31GetU = 0x22; let kExprI31GetU = 0x22;
...@@ -982,6 +984,15 @@ class Binary { ...@@ -982,6 +984,15 @@ class Binary {
this.emit_u8(kExprStructNewWithRtt); this.emit_u8(kExprStructNewWithRtt);
this.emit_u32v(expr.value); this.emit_u32v(expr.value);
break; break;
case kExprArrayInit:
for (let operand of expr.operands) {
this.emit_init_expr_recursive(operand);
}
this.emit_u8(kGCPrefix);
this.emit_u8(kExprArrayInit);
this.emit_u32v(expr.value);
this.emit_u32v(expr.operands.length - 1);
break;
case kExprRttCanon: case kExprRttCanon:
this.emit_u8(kGCPrefix); this.emit_u8(kGCPrefix);
this.emit_u8(kExprRttCanon); this.emit_u8(kExprRttCanon);
...@@ -992,11 +1003,13 @@ class Binary { ...@@ -992,11 +1003,13 @@ class Binary {
this.emit_u8(kGcPrefix); this.emit_u8(kGcPrefix);
this.emit_u8(kExprRttSub); this.emit_u8(kExprRttSub);
this.emit_u32v(expr.value); this.emit_u32v(expr.value);
break;
case kExprRttFreshSub: case kExprRttFreshSub:
this.emit_init_expr_recursive(expr.parent); this.emit_init_expr_recursive(expr.parent);
this.emit_u8(kGcPrefix); this.emit_u8(kGcPrefix);
this.emit_u8(kExprRttFreshSub); this.emit_u8(kExprRttFreshSub);
this.emit_u32v(expr.value); this.emit_u32v(expr.value);
break;
} }
} }
...@@ -1129,6 +1142,9 @@ class WasmInitExpr { ...@@ -1129,6 +1142,9 @@ class WasmInitExpr {
static StructNewWithRtt(type, args) { static StructNewWithRtt(type, args) {
return {kind: kExprStructNewWithRtt, value: type, operands: args}; return {kind: kExprStructNewWithRtt, value: type, operands: args};
} }
static ArrayInit(type, args) {
return {kind: kExprArrayInit, value: type, operands: args};
}
static RttCanon(type) { static RttCanon(type) {
return {kind: kExprRttCanon, value: type}; return {kind: kExprRttCanon, value: type};
} }
...@@ -1212,8 +1228,10 @@ class WasmStruct { ...@@ -1212,8 +1228,10 @@ class WasmStruct {
} }
class WasmArray { class WasmArray {
constructor(type) { constructor(type, mutability) {
this.type = type; this.type = type;
if (!mutability) throw new Error("Immutable arrays are not supported yet");
this.mutability = mutability;
} }
} }
...@@ -1339,8 +1357,8 @@ class WasmModuleBuilder { ...@@ -1339,8 +1357,8 @@ class WasmModuleBuilder {
return this.types.length - 1; return this.types.length - 1;
} }
addArray(type) { addArray(type, mutability) {
this.types.push(new WasmArray(type)); this.types.push(new WasmArray(type, mutability));
return this.types.length - 1; return this.types.length - 1;
} }
...@@ -1581,7 +1599,7 @@ class WasmModuleBuilder { ...@@ -1581,7 +1599,7 @@ class WasmModuleBuilder {
} else if (type instanceof WasmArray) { } else if (type instanceof WasmArray) {
section.emit_u8(kWasmArrayTypeForm); section.emit_u8(kWasmArrayTypeForm);
section.emit_type(type.type); section.emit_type(type.type);
section.emit_u8(1); // Only mutable arrays supported currently. section.emit_u8(type.mutability ? 1 : 0);
} else { } else {
section.emit_u8(kWasmFunctionTypeForm); section.emit_u8(kWasmFunctionTypeForm);
section.emit_u32v(type.params.length); section.emit_u32v(type.params.length);
......
...@@ -37,6 +37,8 @@ namespace module_decoder_unittest { ...@@ -37,6 +37,8 @@ namespace module_decoder_unittest {
#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, ...) \ #define WASM_INIT_EXPR_STRUCT_NEW(index, ...) \
WASM_STRUCT_NEW_WITH_RTT(index, __VA_ARGS__), kExprEnd WASM_STRUCT_NEW_WITH_RTT(index, __VA_ARGS__), kExprEnd
#define WASM_INIT_EXPR_ARRAY_INIT(index, length, ...) \
WASM_ARRAY_INIT(index, length, __VA_ARGS__), kExprEnd
#define WASM_INIT_EXPR_RTT_CANON(index) WASM_RTT_CANON(index), 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
...@@ -1131,6 +1133,52 @@ TEST_F(WasmModuleVerifyTest, StructNewInitExpr) { ...@@ -1131,6 +1133,52 @@ TEST_F(WasmModuleVerifyTest, StructNewInitExpr) {
"struct.new[1]: expected (rtt 0), found (rtt 0 1) instead"); "struct.new[1]: expected (rtt 0), found (rtt 0 1) instead");
} }
TEST_F(WasmModuleVerifyTest, ArrayInitInitExpr) {
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_ARRAY_DEF(kI16Code, true)),
SECTION(Global, ENTRY_COUNT(1), // --
kRefCode, 0, 0, // type, mutability
WASM_INIT_EXPR_ARRAY_INIT(0, 3, WASM_I32V(10), WASM_I32V(20),
WASM_I32V(30), WASM_RTT_CANON(0)))};
EXPECT_VERIFIES(basic);
static const byte type_error[] = {
SECTION(Type, ENTRY_COUNT(2), // --
WASM_ARRAY_DEF(kI32Code, true),
WASM_ARRAY_DEF(WASM_SEQ(kRefCode, 0), true)),
SECTION(
Global, ENTRY_COUNT(1), // --
kRefCode, 1, 0, // type, mutability
WASM_INIT_EXPR_ARRAY_INIT(0, 1, 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(1), WASM_ARRAY_DEF(kI64Code, true)),
SECTION(Global, ENTRY_COUNT(1), // --
kRefCode, 0, 0, // type, mutability
WASM_INIT_EXPR_ARRAY_INIT(0, 2, WASM_I64V(42), WASM_I32V(142),
WASM_RTT_CANON(0)))};
EXPECT_FAILURE_WITH_MSG(subexpr_type_error,
"array.init[1]: expected i64, found i32 instead");
static const byte length_error[] = {
SECTION(Type, ENTRY_COUNT(1), WASM_ARRAY_DEF(kI16Code, true)),
SECTION(Global, ENTRY_COUNT(1), // --
kRefCode, 0, 0, // type, mutability
WASM_INIT_EXPR_ARRAY_INIT(0, 10, WASM_I32V(10), WASM_I32V(20),
WASM_I32V(30), WASM_RTT_CANON(0)))};
EXPECT_FAILURE_WITH_MSG(
length_error,
"not enough arguments on the stack for array.init: expected 11, found 4");
}
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