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

[wasm-gc] Implement array.init_from_elem

Bug: v8:7748
Change-Id: I65dbb496302045820063bd0f4f9ea054e6a645bd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3695580Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81128}
parent dac61556
......@@ -373,9 +373,18 @@ builtin WasmAllocateArray_Uninitialized(
builtin WasmArrayInitFromSegment(
dataSegment: uint32, offset: uint32, length: uint32, rtt: Map): Object {
const instance = LoadInstanceFromFrame();
tail runtime::WasmArrayInitFromSegment(
LoadContextFromInstance(instance), instance, SmiFromUint32(dataSegment),
SmiFromUint32(offset), SmiFromUint32(length), rtt);
try {
const smiOffset =
Convert<PositiveSmi>(offset) otherwise ElementSegmentOutOfBounds;
const smiLength = Convert<PositiveSmi>(length) otherwise ArrayTooLarge;
tail runtime::WasmArrayInitFromSegment(
LoadContextFromInstance(instance), instance, SmiFromUint32(dataSegment),
smiOffset, smiLength, rtt);
} label ElementSegmentOutOfBounds {
tail ThrowWasmTrapElementSegmentOutOfBounds();
} label ArrayTooLarge {
tail ThrowWasmTrapArrayTooLarge();
}
}
// We put all uint32 parameters at the beginning so that they are assigned to
......
......@@ -1900,7 +1900,7 @@ void WasmArray::WasmArrayPrint(std::ostream& os) {
PrintHeader(os, "WasmArray");
wasm::ArrayType* array_type = type();
uint32_t len = length();
os << "\n - type: " << array_type->element_type().name();
os << "\n - element type: " << array_type->element_type().name();
os << "\n - length: " << len;
Address data_ptr = ptr() + WasmArray::kHeaderSize - kHeapObjectTag;
switch (array_type->element_type().kind()) {
......
......@@ -69,6 +69,8 @@
#include "src/roots/roots.h"
#include "src/strings/unicode-inl.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/module-instantiate.h"
#include "src/wasm/wasm-result.h"
#include "src/wasm/wasm-value.h"
#endif
......@@ -1748,6 +1750,46 @@ Handle<WasmArray> Factory::NewWasmArrayFromMemory(uint32_t length,
return handle(result, isolate());
}
Handle<Object> Factory::NewWasmArrayFromElementSegment(
Handle<WasmInstanceObject> instance, const wasm::WasmElemSegment* segment,
uint32_t start_offset, uint32_t length, Handle<Map> map) {
wasm::ValueType element_type = WasmArray::type(*map)->element_type();
DCHECK(element_type.is_reference());
Handle<HeapObject> raw = handle(
AllocateRaw(WasmArray::SizeFor(*map, length), AllocationType::kYoung),
isolate());
{
DisallowGarbageCollection no_gc;
raw->set_map_after_allocation(*map);
WasmArray result = WasmArray::cast(*raw);
result.set_raw_properties_or_hash(*empty_fixed_array(), kRelaxedStore);
result.set_length(length);
// We have to initialize the elements to a default value, because we might
// allocate new objects while computing the elements below.
for (int i = 0; static_cast<uint32_t>(i) < length; i++) {
int offset = result.element_offset(i);
TaggedField<Object>::store(result, offset, *undefined_value());
}
}
Handle<WasmArray> result = Handle<WasmArray>::cast(raw);
AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
for (int i = 0; static_cast<uint32_t>(i) < length; i++) {
int offset = result->element_offset(i);
wasm::ValueOrError maybe_element =
wasm::EvaluateInitExpression(&zone, segment->entries[start_offset + i],
element_type, isolate(), instance);
if (wasm::is_error(maybe_element)) {
return handle(Smi::FromEnum(wasm::to_error(maybe_element)), isolate());
}
TaggedField<Object>::store(*result, offset,
*wasm::to_value(maybe_element).to_ref());
}
return result;
}
Handle<WasmStruct> Factory::NewWasmStruct(const wasm::StructType* type,
wasm::WasmValue* args,
Handle<Map> map) {
......
......@@ -76,6 +76,7 @@ class WeakCell;
namespace wasm {
class ArrayType;
class StructType;
struct WasmElemSegment;
class WasmValue;
} // namespace wasm
#endif
......@@ -646,6 +647,11 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
Handle<Map> map);
Handle<WasmArray> NewWasmArrayFromMemory(uint32_t length, Handle<Map> map,
Address source);
// Returns a handle to a WasmArray if successful, or a Smi containing a
// {MessageTemplate} if computing the array's elements leads to an error.
Handle<Object> NewWasmArrayFromElementSegment(
Handle<WasmInstanceObject> instance, const wasm::WasmElemSegment* segment,
uint32_t start_offset, uint32_t length, Handle<Map> map);
Handle<SharedFunctionInfo> NewSharedFunctionInfoForWasmExportedFunction(
Handle<String> name, Handle<WasmExportedFunctionData> data);
......
......@@ -734,18 +734,40 @@ RUNTIME_FUNCTION(Runtime_WasmArrayInitFromSegment) {
return ThrowWasmError(isolate, MessageTemplate::kWasmTrapArrayTooLarge);
}
uint32_t length_in_bytes = length * element_size;
DCHECK_EQ(length_in_bytes / element_size, length);
if (!base::IsInBounds<uint32_t>(
offset, length_in_bytes,
instance->data_segment_sizes()[segment_index])) {
return ThrowWasmError(isolate,
MessageTemplate::kWasmTrapDataSegmentOutOfBounds);
}
if (type->element_type().is_numeric()) {
uint32_t length_in_bytes = length * element_size;
DCHECK_EQ(length_in_bytes / element_size, length);
if (!base::IsInBounds<uint32_t>(
offset, length_in_bytes,
instance->data_segment_sizes()[segment_index])) {
return ThrowWasmError(isolate,
MessageTemplate::kWasmTrapDataSegmentOutOfBounds);
}
Address source = instance->data_segment_starts()[segment_index] + offset;
return *isolate->factory()->NewWasmArrayFromMemory(length, rtt, source);
} else {
const wasm::WasmElemSegment* elem_segment =
&instance->module()->elem_segments[segment_index];
if (!base::IsInBounds<size_t>(
offset, length,
instance->dropped_elem_segments()[segment_index]
? 0
: elem_segment->entries.size())) {
return ThrowWasmError(
isolate, MessageTemplate::kWasmTrapElementSegmentOutOfBounds);
}
Address source = instance->data_segment_starts()[segment_index] + offset;
return *isolate->factory()->NewWasmArrayFromMemory(length, rtt, source);
Handle<Object> result = isolate->factory()->NewWasmArrayFromElementSegment(
instance, elem_segment, offset, length, rtt);
if (result->IsSmi()) {
return ThrowWasmError(
isolate, static_cast<MessageTemplate>(result->ToSmi().value()));
} else {
return *result;
}
}
}
namespace {
......
......@@ -1975,7 +1975,8 @@ class WasmDecoder : public Decoder {
return length + dst_imm.length + src_imm.length;
}
case kExprArrayInitFromData:
case kExprArrayInitFromDataStatic: {
case kExprArrayInitFromDataStatic:
case kExprArrayInitFromElemStatic: {
ArrayIndexImmediate<validate> array_imm(decoder, pc + length);
IndexImmediate<validate> data_imm(
decoder, pc + length + array_imm.length, "segment index");
......@@ -2210,6 +2211,7 @@ class WasmDecoder : public Decoder {
case kExprArrayNew:
case kExprArrayNewDefaultWithRtt:
case kExprArrayInitFromDataStatic:
case kExprArrayInitFromElemStatic:
case kExprArrayGet:
case kExprArrayGetS:
case kExprArrayGetU:
......@@ -4450,6 +4452,53 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
Push(array);
return opcode_length + array_imm.length + data_segment.length;
}
case kExprArrayInitFromElemStatic: {
ArrayIndexImmediate<validate> array_imm(this,
this->pc_ + opcode_length);
if (!this->Validate(this->pc_ + opcode_length, array_imm)) return 0;
ValueType element_type = array_imm.array_type->element_type();
if (element_type.is_numeric()) {
this->DecodeError(
"array.init_from_elem can only be used with reference-type "
"arrays, found array type #%d instead",
array_imm.index);
return 0;
}
const byte* elem_index_pc =
this->pc_ + opcode_length + array_imm.length;
IndexImmediate<validate> elem_segment(this, elem_index_pc,
"data segment");
if (!this->ValidateElementSegment(elem_index_pc, elem_segment)) {
return 0;
}
ValueType rtt_type = ValueType::Rtt(array_imm.index);
Value rtt = CreateValue(rtt_type);
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, array_imm.index, &rtt);
Push(rtt);
Value array =
CreateValue(ValueType::Ref(array_imm.index, kNonNullable));
ValueType elem_segment_type =
this->module_->elem_segments[elem_segment.index].type;
if (V8_UNLIKELY(
!IsSubtypeOf(elem_segment_type, element_type, this->module_))) {
this->DecodeError(
"array.init_from_elem: segment type %s is not a subtype of array "
"element type %s",
elem_segment_type.name().c_str(), element_type.name().c_str());
return 0;
}
Value length = Peek(1, 1, kWasmI32);
Value offset = Peek(2, 0, kWasmI32);
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayInitFromSegment, array_imm,
elem_segment, offset, length, rtt,
&array);
Drop(3); // rtt, length, offset
Push(array);
return opcode_length + array_imm.length + elem_segment.length;
}
case kExprArrayGetS:
case kExprArrayGetU: {
NON_CONST_ONLY
......
......@@ -209,35 +209,62 @@ void InitExprInterface::ArrayInit(FullDecoder* decoder,
void InitExprInterface::ArrayInitFromSegment(
FullDecoder* decoder, const ArrayIndexImmediate<validate>& array_imm,
const IndexImmediate<validate>& data_segment_imm, const Value& offset_value,
const IndexImmediate<validate>& segment_imm, const Value& offset_value,
const Value& length_value, const Value& rtt, Value* result) {
if (!generate_value()) return;
uint32_t length = length_value.runtime_value.to_u32();
uint32_t offset = offset_value.runtime_value.to_u32();
const WasmDataSegment& data_segment =
module_->data_segments[data_segment_imm.index];
uint32_t length_in_bytes =
length * array_imm.array_type->element_type().value_kind_size();
// Error handling.
if (length >
static_cast<uint32_t>(WasmArray::MaxLength(array_imm.array_type))) {
error_ = MessageTemplate::kWasmTrapArrayTooLarge;
return;
}
if (!base::IsInBounds<uint32_t>(offset, length_in_bytes,
data_segment.source.length())) {
error_ = MessageTemplate::kWasmTrapDataSegmentOutOfBounds;
return;
}
ValueType element_type = array_imm.array_type->element_type();
ValueType result_type =
ValueType::Ref(HeapType(array_imm.index), kNonNullable);
if (element_type.is_numeric()) {
const WasmDataSegment& data_segment =
module_->data_segments[segment_imm.index];
uint32_t length_in_bytes =
length * array_imm.array_type->element_type().value_kind_size();
Address source =
instance_->data_segment_starts()[data_segment_imm.index] + offset;
Handle<WasmArray> array_value = isolate_->factory()->NewWasmArrayFromMemory(
length, Handle<Map>::cast(rtt.runtime_value.to_ref()), source);
result->runtime_value = WasmValue(
array_value, ValueType::Ref(HeapType(array_imm.index), kNonNullable));
if (!base::IsInBounds<uint32_t>(offset, length_in_bytes,
data_segment.source.length())) {
error_ = MessageTemplate::kWasmTrapDataSegmentOutOfBounds;
return;
}
Address source =
instance_->data_segment_starts()[segment_imm.index] + offset;
Handle<WasmArray> array_value = isolate_->factory()->NewWasmArrayFromMemory(
length, Handle<Map>::cast(rtt.runtime_value.to_ref()), source);
result->runtime_value = WasmValue(array_value, result_type);
} else {
const wasm::WasmElemSegment* elem_segment =
&decoder->module_->elem_segments[segment_imm.index];
// A constant expression should not observe if a passive segment is dropped.
// However, it should consider active and declarative segments as empty.
if (!base::IsInBounds<size_t>(
offset, length,
elem_segment->status == WasmElemSegment::kStatusPassive
? elem_segment->entries.size()
: 0)) {
error_ = MessageTemplate::kWasmTrapElementSegmentOutOfBounds;
return;
}
Handle<Object> array_object =
isolate_->factory()->NewWasmArrayFromElementSegment(
instance_, elem_segment, offset, length,
Handle<Map>::cast(rtt.runtime_value.to_ref()));
if (array_object->IsSmi()) {
// A smi result stands for an error code.
error_ = static_cast<MessageTemplate>(array_object->ToSmi().value());
} else {
result->runtime_value = WasmValue(array_object, result_type);
}
}
}
void InitExprInterface::RttCanon(FullDecoder* decoder, uint32_t type_index,
......
......@@ -417,6 +417,7 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(ArrayInitStatic, "array.init_static")
CASE_OP(ArrayInitFromData, "array.init_from_data")
CASE_OP(ArrayInitFromDataStatic, "array.init_from_data_static")
CASE_OP(ArrayInitFromElemStatic, "array.init_from_elem_static")
CASE_OP(I31New, "i31.new")
CASE_OP(I31GetS, "i31.get_s")
CASE_OP(I31GetU, "i31.get_u")
......
......@@ -691,13 +691,14 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(ArrayGetU, 0xfb15, _) \
V(ArraySet, 0xfb16, _) \
V(ArrayLen, 0xfb17, _) \
V(ArrayCopy, 0xfb18, _) /* not standardized - V8 experimental */ \
V(ArrayInit, 0xfb19, _) /* not standardized - V8 experimental */ \
V(ArrayInitStatic, 0xfb1a, _) /* not standardized - V8 experimental */ \
V(ArrayCopy, 0xfb18, _) /* not standardized - V8 experimental */ \
V(ArrayInit, 0xfb19, _) \
V(ArrayInitStatic, 0xfb1a, _) \
V(ArrayNew, 0xfb1b, _) \
V(ArrayNewDefault, 0xfb1c, _) \
V(ArrayInitFromData, 0xfb1e, _) /* not stand. - V8 experimental */ \
V(ArrayInitFromDataStatic, 0xfb1d, _) /* not stand. - V8 experimental */ \
V(ArrayInitFromData, 0xfb1e, _) \
V(ArrayInitFromDataStatic, 0xfb1d, _) \
V(ArrayInitFromElemStatic, 0xfb1f, _) \
V(I31New, 0xfb20, _) \
V(I31GetS, 0xfb21, _) \
V(I31GetU, 0xfb22, _) \
......
This diff is collapsed.
......@@ -501,6 +501,7 @@ let kExprArrayNew = 0x1b;
let kExprArrayNewDefault = 0x1c;
let kExprArrayInitFromData = 0x1e;
let kExprArrayInitFromDataStatic = 0x1d;
let kExprArrayInitFromElemStatic = 0x1f;
let kExprI31New = 0x20;
let kExprI31GetS = 0x21;
let kExprI31GetU = 0x22;
......@@ -908,6 +909,8 @@ let kTrapDataSegmentOutOfBounds = 9;
let kTrapElementSegmentOutOfBounds = 10;
let kTrapRethrowNull = 11;
let kTrapArrayTooLarge = 12;
let kTrapArrayOutOfBounds = 13;
let kTrapNullDereference = 14;
let kTrapMsgs = [
'unreachable', // --
......@@ -922,7 +925,9 @@ let kTrapMsgs = [
'data segment out of bounds', // --
'element segment out of bounds', // --
'rethrowing null value', // --
'requested new array is too large' // --
'requested new array is too large', // --
'array element access out of bounds', // --
'dereferencing a null pointer' // --
];
// This requires test/mjsunit/mjsunit.js.
......@@ -1093,13 +1098,14 @@ class Binary {
break;
case kExprArrayInitFromData:
case kExprArrayInitFromDataStatic:
case kExprArrayInitFromElemStatic:
for (let operand of expr.operands) {
this.emit_init_expr_recursive(operand);
}
this.emit_u8(kGCPrefix);
this.emit_u8(expr.kind);
this.emit_u32v(expr.array_index);
this.emit_u32v(expr.data_segment);
this.emit_u32v(expr.segment_index);
break;
case kExprRttCanon:
this.emit_u8(kGCPrefix);
......@@ -1288,19 +1294,23 @@ class WasmInitExpr {
static ArrayInitStatic(type, args) {
return {kind: kExprArrayInitStatic, value: type, operands: args};
}
static ArrayInitFromData(array_index, data_segment, args, builder) {
static ArrayInitFromData(array_index, segment_index, args, builder) {
// array.init_from_data means we need to pull the data count section before
// any section that may include init. expressions.
builder.early_data_count_section = true;
return {kind: kExprArrayInitFromData, array_index: array_index,
data_segment: data_segment, operands: args};
segment_index: segment_index, operands: args};
}
static ArrayInitFromDataStatic(array_index, data_segment, args, builder) {
static ArrayInitFromDataStatic(array_index, segment_index, args, builder) {
// array.init_from_data means we need to pull the data count section before
// any section that may include init. expressions.
builder.early_data_count_section = true;
return {kind: kExprArrayInitFromDataStatic, array_index: array_index,
data_segment: data_segment, operands: args};
segment_index: segment_index, operands: args};
}
static ArrayInitFromElemStatic(array_index, segment_index, args) {
return {kind: kExprArrayInitFromElemStatic, array_index: array_index,
segment_index: segment_index, operands: args};
}
static RttCanon(type) {
return {kind: kExprRttCanon, value: type};
......@@ -1355,6 +1365,7 @@ class WasmGlobalBuilder {
class WasmTableBuilder {
constructor(module, type, initial_size, max_size, init_expr) {
// TODO(manoskouk): Add the table index.
this.module = module;
this.type = type;
this.initial_size = initial_size;
......
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