Commit a7f8ffe7 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[wasm-gc] Implement array.new

along with WASM_ARRAY_TYPE, a WasmArray class, and a very basic
test.

Bug: v8:7748
Change-Id: I1ad4ff78e428972be52130cc179a91c76fcdbdc6
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2185136
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67671}
parent aed91bc8
...@@ -841,6 +841,7 @@ namespace internal { ...@@ -841,6 +841,7 @@ namespace internal {
ASM(WasmDebugBreak, Dummy) \ ASM(WasmDebugBreak, Dummy) \
TFC(WasmFloat32ToNumber, WasmFloat32ToNumber) \ TFC(WasmFloat32ToNumber, WasmFloat32ToNumber) \
TFC(WasmFloat64ToNumber, WasmFloat64ToNumber) \ TFC(WasmFloat64ToNumber, WasmFloat64ToNumber) \
TFS(WasmAllocateArray, kMapIndex, kLength, kElementSize) \
TFS(WasmAllocateStruct, kMapIndex) \ TFS(WasmAllocateStruct, kMapIndex) \
TFC(WasmAtomicNotify, WasmAtomicNotify) \ TFC(WasmAtomicNotify, WasmAtomicNotify) \
TFS(WasmGetOwnProperty, kObject, kUniqueName) \ TFS(WasmGetOwnProperty, kObject, kUniqueName) \
......
...@@ -278,6 +278,30 @@ TF_BUILTIN(WasmTableCopy, WasmBuiltinsAssembler) { ...@@ -278,6 +278,30 @@ TF_BUILTIN(WasmTableCopy, WasmBuiltinsAssembler) {
src_table, dst, src, size); src_table, dst, src, size);
} }
TF_BUILTIN(WasmAllocateArray, WasmBuiltinsAssembler) {
TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
TNode<Smi> map_index = CAST(Parameter(Descriptor::kMapIndex));
TNode<Smi> length = CAST(Parameter(Descriptor::kLength));
TNode<Smi> element_size = CAST(Parameter(Descriptor::kElementSize));
TNode<FixedArray> maps_list = LoadObjectField<FixedArray>(
instance, WasmInstanceObject::kManagedObjectMapsOffset);
TNode<Map> map = CAST(LoadFixedArrayElement(maps_list, map_index));
TNode<IntPtrT> untagged_length = SmiUntag(length);
// instance_size = WasmArray::kHeaderSize
// + RoundUp(element_size * length, kObjectAlignment)
TNode<IntPtrT> raw_size = IntPtrMul(SmiUntag(element_size), untagged_length);
TNode<IntPtrT> rounded_size =
WordAnd(IntPtrAdd(raw_size, IntPtrConstant(kObjectAlignmentMask)),
IntPtrConstant(~kObjectAlignmentMask));
TNode<IntPtrT> instance_size =
IntPtrAdd(IntPtrConstant(WasmArray::kHeaderSize), rounded_size);
TNode<WasmArray> result = UncheckedCast<WasmArray>(Allocate(instance_size));
StoreMap(result, map);
StoreObjectFieldNoWriteBarrier(result, WasmArray::kLengthOffset,
TruncateIntPtrToInt32(untagged_length));
Return(result);
}
TF_BUILTIN(WasmAllocateStruct, WasmBuiltinsAssembler) { TF_BUILTIN(WasmAllocateStruct, WasmBuiltinsAssembler) {
TNode<WasmInstanceObject> instance = LoadInstanceFromFrame(); TNode<WasmInstanceObject> instance = LoadInstanceFromFrame();
TNode<Smi> map_index = CAST(Parameter(Descriptor::kMapIndex)); TNode<Smi> map_index = CAST(Parameter(Descriptor::kMapIndex));
......
...@@ -245,6 +245,7 @@ Type::bitset BitsetType::Lub(const MapRefLike& map) { ...@@ -245,6 +245,7 @@ Type::bitset BitsetType::Lub(const MapRefLike& map) {
case JS_WEAK_REF_TYPE: case JS_WEAK_REF_TYPE:
case JS_WEAK_SET_TYPE: case JS_WEAK_SET_TYPE:
case JS_PROMISE_TYPE: case JS_PROMISE_TYPE:
case WASM_ARRAY_TYPE:
case WASM_EXCEPTION_OBJECT_TYPE: case WASM_EXCEPTION_OBJECT_TYPE:
case WASM_GLOBAL_OBJECT_TYPE: case WASM_GLOBAL_OBJECT_TYPE:
case WASM_INSTANCE_OBJECT_TYPE: case WASM_INSTANCE_OBJECT_TYPE:
......
...@@ -5054,7 +5054,10 @@ Node* WasmGraphBuilder::StructNew(uint32_t struct_index, ...@@ -5054,7 +5054,10 @@ Node* WasmGraphBuilder::StructNew(uint32_t struct_index,
int map_index = 0; int map_index = 0;
const std::vector<uint8_t>& type_kinds = env_->module->type_kinds; const std::vector<uint8_t>& type_kinds = env_->module->type_kinds;
for (uint32_t i = 0; i < struct_index; i++) { for (uint32_t i = 0; i < struct_index; i++) {
if (type_kinds[i] == wasm::kWasmStructTypeCode) map_index++; if (type_kinds[i] == wasm::kWasmStructTypeCode ||
type_kinds[i] == wasm::kWasmArrayTypeCode) {
map_index++;
}
} }
CallDescriptor* call_descriptor = CallDescriptor* call_descriptor =
...@@ -5064,8 +5067,10 @@ Node* WasmGraphBuilder::StructNew(uint32_t struct_index, ...@@ -5064,8 +5067,10 @@ Node* WasmGraphBuilder::StructNew(uint32_t struct_index,
mcgraph()->common()->NumberConstant(Builtins::kWasmAllocateStruct)); mcgraph()->common()->NumberConstant(Builtins::kWasmAllocateStruct));
Node* native_context = Node* native_context =
LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer()); LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer());
Node* s = gasm_->Call(call_descriptor, call_target, Node* s = gasm_->Call(
gasm_->Int32Constant(map_index), native_context); call_descriptor, call_target,
graph()->NewNode(mcgraph()->common()->NumberConstant(map_index)),
native_context);
for (uint32_t i = 0; i < type->field_count(); i++) { for (uint32_t i = 0; i < type->field_count(); i++) {
StoreStructFieldUnchecked(mcgraph(), gasm_.get(), s, type, i, fields[i]); StoreStructFieldUnchecked(mcgraph(), gasm_.get(), s, type, i, fields[i]);
...@@ -5073,6 +5078,59 @@ Node* WasmGraphBuilder::StructNew(uint32_t struct_index, ...@@ -5073,6 +5078,59 @@ Node* WasmGraphBuilder::StructNew(uint32_t struct_index,
return s; return s;
} }
Node* WasmGraphBuilder::ArrayNew(uint32_t array_index,
const wasm::ArrayType* type, Node* length,
Node* initial_value) {
// This logic is duplicated from module-instantiate.cc.
// TODO(jkummerow): Find a nicer solution.
int map_index = 0;
const std::vector<uint8_t>& type_kinds = env_->module->type_kinds;
for (uint32_t i = 0; i < array_index; i++) {
if (type_kinds[i] == wasm::kWasmStructTypeCode ||
type_kinds[i] == wasm::kWasmArrayTypeCode) {
map_index++;
}
}
wasm::ValueType element_type = type->element_type();
Node* a = CALL_BUILTIN(
WasmAllocateArray,
graph()->NewNode(mcgraph()->common()->NumberConstant(map_index)),
BuildChangeUint31ToSmi(length),
graph()->NewNode(mcgraph()->common()->NumberConstant(
element_type.element_size_bytes())),
LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer()));
WriteBarrierKind write_barrier =
element_type.IsReferenceType() ? kPointerWriteBarrier : kNoWriteBarrier;
StoreRepresentation rep(element_type.machine_representation(), write_barrier);
auto loop = gasm_->MakeLoopLabel(MachineRepresentation::kWord32);
auto done = gasm_->MakeLabel();
Node* start_offset =
gasm_->Int32Constant(WasmArray::kHeaderSize - kHeapObjectTag);
Node* element_size = gasm_->Int32Constant(element_type.element_size_bytes());
Node* end_offset =
gasm_->Int32Add(start_offset, gasm_->Int32Mul(element_size, length));
// "Goto" requires the graph's end to have been set up.
// TODO(jkummerow): Figure out if there's a more elegant solution.
Graph* g = mcgraph()->graph();
if (!g->end()) {
g->SetEnd(g->NewNode(mcgraph()->common()->End(0)));
}
gasm_->Goto(&loop, start_offset);
gasm_->Bind(&loop);
{
Node* offset = loop.PhiAt(0);
Node* check = gasm_->Uint32LessThan(offset, end_offset);
gasm_->GotoIfNot(check, &done);
gasm_->Store(rep, a, offset, initial_value);
offset = gasm_->Int32Add(offset, element_size);
gasm_->Goto(&loop, offset);
}
gasm_->Bind(&done);
return a;
}
Node* WasmGraphBuilder::StructGet(Node* struct_object, Node* WasmGraphBuilder::StructGet(Node* struct_object,
const wasm::StructType* type, const wasm::StructType* type,
uint32_t field_index, uint32_t field_index,
......
...@@ -374,6 +374,8 @@ class WasmGraphBuilder { ...@@ -374,6 +374,8 @@ class WasmGraphBuilder {
Node* StructSet(Node* struct_object, const wasm::StructType* type, Node* StructSet(Node* struct_object, const wasm::StructType* type,
uint32_t field_index, Node* value, uint32_t field_index, Node* value,
wasm::WasmCodePosition position); wasm::WasmCodePosition position);
Node* ArrayNew(uint32_t array_index, const wasm::ArrayType* type,
Node* length, Node* initial_value);
bool has_simd() const { return has_simd_; } bool has_simd() const { return has_simd_; }
......
...@@ -1731,6 +1731,46 @@ void WasmStruct::WasmStructPrint(std::ostream& os) { // NOLINT ...@@ -1731,6 +1731,46 @@ void WasmStruct::WasmStructPrint(std::ostream& os) { // NOLINT
os << "\n"; os << "\n";
} }
void WasmArray::WasmArrayPrint(std::ostream& os) { // NOLINT
PrintHeader(os, "WasmArray");
wasm::ArrayType* array_type = type();
uint32_t len = length();
os << "\n - type: " << array_type->element_type().type_name();
os << "\n - length: " << len;
Address data_ptr = ptr() + WasmArray::kHeaderSize - kHeapObjectTag;
switch (array_type->element_type().kind()) {
case wasm::ValueType::kI32:
PrintTypedArrayElements(os, reinterpret_cast<int32_t*>(data_ptr), len,
true);
break;
case wasm::ValueType::kI64:
PrintTypedArrayElements(os, reinterpret_cast<int64_t*>(data_ptr), len,
true);
break;
case wasm::ValueType::kF32:
PrintTypedArrayElements(os, reinterpret_cast<float*>(data_ptr), len,
true);
break;
case wasm::ValueType::kF64:
PrintTypedArrayElements(os, reinterpret_cast<double*>(data_ptr), len,
true);
break;
case wasm::ValueType::kS128:
case wasm::ValueType::kAnyRef:
case wasm::ValueType::kFuncRef:
case wasm::ValueType::kNullRef:
case wasm::ValueType::kExnRef:
case wasm::ValueType::kRef:
case wasm::ValueType::kOptRef:
case wasm::ValueType::kEqRef:
case wasm::ValueType::kBottom:
case wasm::ValueType::kStmt:
UNIMPLEMENTED(); // TODO(7748): Implement.
break;
}
os << "\n";
}
void WasmDebugInfo::WasmDebugInfoPrint(std::ostream& os) { // NOLINT void WasmDebugInfo::WasmDebugInfoPrint(std::ostream& os) { // NOLINT
PrintHeader(os, "WasmDebugInfo"); PrintHeader(os, "WasmDebugInfo");
os << "\n - wasm_instance: " << Brief(wasm_instance()); os << "\n - wasm_instance: " << Brief(wasm_instance());
......
...@@ -57,6 +57,7 @@ namespace internal { ...@@ -57,6 +57,7 @@ namespace internal {
V(TransitionArray) \ V(TransitionArray) \
V(UncompiledDataWithoutPreparseData) \ V(UncompiledDataWithoutPreparseData) \
V(UncompiledDataWithPreparseData) \ V(UncompiledDataWithPreparseData) \
V(WasmArray) \
V(WasmCapiFunctionData) \ V(WasmCapiFunctionData) \
V(WasmIndirectFunctionTable) \ V(WasmIndirectFunctionTable) \
V(WasmInstanceObject) \ V(WasmInstanceObject) \
......
...@@ -758,7 +758,7 @@ ACCESSORS_CHECKED(Map, native_context, NativeContext, ...@@ -758,7 +758,7 @@ ACCESSORS_CHECKED(Map, native_context, NativeContext,
IsContextMap()) IsContextMap())
ACCESSORS_CHECKED(Map, wasm_type_info, Foreign, ACCESSORS_CHECKED(Map, wasm_type_info, Foreign,
kConstructorOrBackPointerOrNativeContextOffset, kConstructorOrBackPointerOrNativeContextOffset,
IsWasmStructMap()) IsWasmStructMap() || IsWasmArrayMap())
bool Map::IsPrototypeValidityCellValid() const { bool Map::IsPrototypeValidityCellValid() const {
Object validity_cell = prototype_validity_cell(); Object validity_cell = prototype_validity_cell();
......
...@@ -362,8 +362,10 @@ VisitorId Map::GetVisitorId(Map map) { ...@@ -362,8 +362,10 @@ VisitorId Map::GetVisitorId(Map map) {
case SYNTHETIC_MODULE_TYPE: case SYNTHETIC_MODULE_TYPE:
return kVisitSyntheticModule; return kVisitSyntheticModule;
case WASM_ARRAY_TYPE:
return kVisitWasmArray;
case WASM_STRUCT_TYPE: case WASM_STRUCT_TYPE:
return kVisitWasmStruct; // TODO(7748): Other Wasm object types. return kVisitWasmStruct;
#define MAKE_TQ_CASE(TYPE, Name) \ #define MAKE_TQ_CASE(TYPE, Name) \
case TYPE: \ case TYPE: \
......
...@@ -75,6 +75,7 @@ enum InstanceType : uint16_t; ...@@ -75,6 +75,7 @@ enum InstanceType : uint16_t;
V(WasmCapiFunctionData) \ V(WasmCapiFunctionData) \
V(WasmIndirectFunctionTable) \ V(WasmIndirectFunctionTable) \
V(WasmInstanceObject) \ V(WasmInstanceObject) \
V(WasmArray) \
V(WasmStruct) \ V(WasmStruct) \
V(WeakCell) V(WeakCell)
......
...@@ -226,6 +226,7 @@ class ZoneForwardList; ...@@ -226,6 +226,7 @@ class ZoneForwardList;
V(UncompiledDataWithoutPreparseData) \ V(UncompiledDataWithoutPreparseData) \
V(Undetectable) \ V(Undetectable) \
V(UniqueName) \ V(UniqueName) \
V(WasmArray) \
V(WasmExceptionObject) \ V(WasmExceptionObject) \
V(WasmExceptionPackage) \ V(WasmExceptionPackage) \
V(WasmGlobalObject) \ V(WasmGlobalObject) \
......
...@@ -800,6 +800,27 @@ class CodeDataContainer::BodyDescriptor final : public BodyDescriptorBase { ...@@ -800,6 +800,27 @@ class CodeDataContainer::BodyDescriptor final : public BodyDescriptorBase {
} }
}; };
class WasmArray::BodyDescriptor final : public BodyDescriptorBase {
public:
static bool IsValidSlot(Map map, HeapObject obj, int offset) {
// Fields in WasmArrays never change their types in place, so
// there should never be a need to call this function.
UNREACHABLE();
return false;
}
template <typename ObjectVisitor>
static inline void IterateBody(Map map, HeapObject obj, int object_size,
ObjectVisitor* v) {
if (!WasmArray::type(map)->element_type().IsReferenceType()) return;
IteratePointers(obj, WasmArray::kHeaderSize, object_size, v);
}
static inline int SizeOf(Map map, HeapObject object) {
return WasmArray::SizeFor(map, WasmArray::cast(object).length());
}
};
class WasmStruct::BodyDescriptor final : public BodyDescriptorBase { class WasmStruct::BodyDescriptor final : public BodyDescriptorBase {
public: public:
static bool IsValidSlot(Map map, HeapObject obj, int offset) { static bool IsValidSlot(Map map, HeapObject obj, int offset) {
...@@ -946,6 +967,8 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3, T4 p4) { ...@@ -946,6 +967,8 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3, T4 p4) {
return Op::template apply<FeedbackVector::BodyDescriptor>(p1, p2, p3, p4); return Op::template apply<FeedbackVector::BodyDescriptor>(p1, p2, p3, p4);
case COVERAGE_INFO_TYPE: case COVERAGE_INFO_TYPE:
return Op::template apply<CoverageInfo::BodyDescriptor>(p1, p2, p3, p4); return Op::template apply<CoverageInfo::BodyDescriptor>(p1, p2, p3, p4);
case WASM_ARRAY_TYPE:
return Op::template apply<WasmArray::BodyDescriptor>(p1, p2, p3, p4);
case WASM_STRUCT_TYPE: case WASM_STRUCT_TYPE:
return Op::template apply<WasmStruct::BodyDescriptor>(p1, p2, p3, p4); return Op::template apply<WasmStruct::BodyDescriptor>(p1, p2, p3, p4);
case JS_OBJECT_TYPE: case JS_OBJECT_TYPE:
......
...@@ -2291,6 +2291,9 @@ int HeapObject::SizeFromMap(Map map) const { ...@@ -2291,6 +2291,9 @@ int HeapObject::SizeFromMap(Map map) const {
return CoverageInfo::SizeFor( return CoverageInfo::SizeFor(
CoverageInfo::unchecked_cast(*this).slot_count()); CoverageInfo::unchecked_cast(*this).slot_count());
} }
if (instance_type == WASM_ARRAY_TYPE) {
return WasmArray::SizeFor(map, WasmArray::cast(*this).length());
}
DCHECK_EQ(instance_type, EMBEDDER_DATA_ARRAY_TYPE); DCHECK_EQ(instance_type, EMBEDDER_DATA_ARRAY_TYPE);
return EmbedderDataArray::SizeFor( return EmbedderDataArray::SizeFor(
EmbedderDataArray::unchecked_cast(*this).length()); EmbedderDataArray::unchecked_cast(*this).length());
......
...@@ -3365,6 +3365,13 @@ class LiftoffCompiler { ...@@ -3365,6 +3365,13 @@ class LiftoffCompiler {
unsupported(decoder, kGC, "struct.set"); unsupported(decoder, kGC, "struct.set");
} }
void ArrayNew(FullDecoder* decoder, const ArrayIndexImmediate<validate>& imm,
const Value& length, const Value& initial_value,
Value* result) {
// TODO(7748): Implement.
unsupported(decoder, kGC, "array.new");
}
private: private:
// Emit additional source positions for return addresses. Used by debugging to // Emit additional source positions for return addresses. Used by debugging to
// OSR frames with different sets of breakpoints. // OSR frames with different sets of breakpoints.
......
...@@ -488,6 +488,16 @@ struct FieldIndexImmediate { ...@@ -488,6 +488,16 @@ struct FieldIndexImmediate {
} }
}; };
template <Decoder::ValidateFlag validate>
struct ArrayIndexImmediate {
uint32_t index = 0;
uint32_t length = 0;
const ArrayType* array_type = nullptr;
inline ArrayIndexImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_u32v<validate>(pc, &length, "array index");
}
};
template <Decoder::ValidateFlag validate> template <Decoder::ValidateFlag validate>
struct CallIndirectImmediate { struct CallIndirectImmediate {
uint32_t table_index; uint32_t table_index;
...@@ -904,7 +914,9 @@ enum class LoadTransformationKind : uint8_t { ...@@ -904,7 +914,9 @@ enum class LoadTransformationKind : uint8_t {
F(StructGet, const Value& struct_object, \ F(StructGet, const Value& struct_object, \
const FieldIndexImmediate<validate>& field, Value* result) \ const FieldIndexImmediate<validate>& field, Value* result) \
F(StructSet, const Value& struct_object, \ F(StructSet, const Value& struct_object, \
const FieldIndexImmediate<validate>& field, const Value& field_value) const FieldIndexImmediate<validate>& field, const Value& field_value) \
F(ArrayNew, const ArrayIndexImmediate<validate>& imm, const Value& length, \
const Value& initial_value, Value* result)
// Generic Wasm bytecode decoder with utilities for decoding immediates, // Generic Wasm bytecode decoder with utilities for decoding immediates,
// lengths, etc. // lengths, etc.
...@@ -1082,6 +1094,20 @@ class WasmDecoder : public Decoder { ...@@ -1082,6 +1094,20 @@ class WasmDecoder : public Decoder {
return false; return false;
} }
inline bool Complete(const byte* pc, ArrayIndexImmediate<validate>& imm) {
if (!VALIDATE(module_ != nullptr && module_->has_array(imm.index))) {
return false;
}
imm.array_type = module_->array_type(imm.index);
return true;
}
inline bool Validate(const byte* pc, ArrayIndexImmediate<validate>& imm) {
if (Complete(pc, imm)) return true;
errorf(pc, "invalid array index: %u", imm.index);
return false;
}
inline bool CanReturnCall(const FunctionSig* target_sig) { inline bool CanReturnCall(const FunctionSig* target_sig) {
if (target_sig == nullptr) return false; if (target_sig == nullptr) return false;
size_t num_returns = sig_->return_count(); size_t num_returns = sig_->return_count();
...@@ -2959,9 +2985,17 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2959,9 +2985,17 @@ class WasmFullDecoder : public WasmDecoder<validate> {
CALL_INTERFACE_IF_REACHABLE(StructSet, struct_obj, field, field_value); CALL_INTERFACE_IF_REACHABLE(StructSet, struct_obj, field, field_value);
break; break;
} }
case kExprArrayNew: case kExprArrayNew: {
UNIMPLEMENTED(); // TODO(7748): Implement. ArrayIndexImmediate<validate> imm(this, this->pc_ + len);
len += imm.length;
if (!this->Validate(this->pc_, imm)) break;
auto length = Pop(0, kWasmI32);
auto initial_value = Pop(0, imm.array_type->element_type());
auto* value = Push(ValueType(ValueType::kRef, imm.index));
CALL_INTERFACE_IF_REACHABLE(ArrayNew, imm, length, initial_value,
value);
break; break;
}
case kExprArrayGet: case kExprArrayGet:
UNIMPLEMENTED(); // TODO(7748): Implement. UNIMPLEMENTED(); // TODO(7748): Implement.
break; break;
......
...@@ -626,6 +626,13 @@ class WasmGraphBuildingInterface { ...@@ -626,6 +626,13 @@ class WasmGraphBuildingInterface {
field.index, field_value.node, decoder->position()); field.index, field_value.node, decoder->position());
} }
void ArrayNew(FullDecoder* decoder, const ArrayIndexImmediate<validate>& imm,
const Value& length, const Value& initial_value,
Value* result) {
result->node = BUILD(ArrayNew, imm.index, imm.array_type, length.node,
initial_value.node);
}
private: private:
SsaEnv* ssa_env_ = nullptr; SsaEnv* ssa_env_ = nullptr;
compiler::WasmGraphBuilder* builder_; compiler::WasmGraphBuilder* builder_;
......
...@@ -103,6 +103,21 @@ Handle<Map> CreateStructMap(Isolate* isolate, const WasmModule* module, ...@@ -103,6 +103,21 @@ Handle<Map> CreateStructMap(Isolate* isolate, const WasmModule* module,
return map; return map;
} }
Handle<Map> CreateArrayMap(Isolate* isolate, const WasmModule* module,
int array_index) {
const wasm::ArrayType* type = module->array_type(array_index);
int inobject_properties = 0;
int instance_size = kVariableSizeSentinel;
InstanceType instance_type = WASM_ARRAY_TYPE;
ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND;
Handle<Foreign> type_info =
isolate->factory()->NewForeign(reinterpret_cast<Address>(type));
Handle<Map> map = isolate->factory()->NewMap(
instance_type, instance_size, elements_kind, inobject_properties);
map->set_wasm_type_info(*type_info);
return map;
}
} // namespace } // namespace
// A helper class to simplify instantiating a module from a module object. // A helper class to simplify instantiating a module from a module object.
...@@ -557,9 +572,10 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() { ...@@ -557,9 +572,10 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
if (enabled_.has_gc()) { if (enabled_.has_gc()) {
int count = 0; int count = 0;
// TODO(7748): Add array support to both loops.
for (uint8_t type_kind : module_->type_kinds) { for (uint8_t type_kind : module_->type_kinds) {
if (type_kind == kWasmStructTypeCode) count++; if (type_kind == kWasmStructTypeCode || type_kind == kWasmArrayTypeCode) {
count++;
}
} }
Handle<FixedArray> maps = Handle<FixedArray> maps =
isolate_->factory()->NewUninitializedFixedArray(count); isolate_->factory()->NewUninitializedFixedArray(count);
...@@ -569,6 +585,10 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() { ...@@ -569,6 +585,10 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
Handle<Map> map = CreateStructMap(isolate_, module_, i); Handle<Map> map = CreateStructMap(isolate_, module_, i);
maps->set(index++, *map); maps->set(index++, *map);
} }
if (module_->type_kinds[i] == kWasmArrayTypeCode) {
Handle<Map> map = CreateArrayMap(isolate_, module_, i);
maps->set(index++, *map);
}
} }
instance->set_managed_object_maps(*maps); instance->set_managed_object_maps(*maps);
} }
......
...@@ -100,6 +100,8 @@ class ArrayType : public ZoneObject { ...@@ -100,6 +100,8 @@ class ArrayType : public ZoneObject {
public: public:
constexpr explicit ArrayType(ValueType rep) : rep_(rep) {} constexpr explicit ArrayType(ValueType rep) : rep_(rep) {}
ValueType element_type() const { return 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_; } bool operator!=(const ArrayType& other) const { return rep_ != other.rep_; }
......
...@@ -286,6 +286,12 @@ uint32_t WasmModuleBuilder::AddStructType(StructType* type) { ...@@ -286,6 +286,12 @@ uint32_t WasmModuleBuilder::AddStructType(StructType* type) {
return index; return index;
} }
uint32_t WasmModuleBuilder::AddArrayType(ArrayType* type) {
uint32_t index = static_cast<uint32_t>(types_.size());
types_.push_back(Type(type));
return index;
}
uint32_t WasmModuleBuilder::AllocateIndirectFunctions(uint32_t count) { uint32_t WasmModuleBuilder::AllocateIndirectFunctions(uint32_t count) {
DCHECK(allocating_indirect_functions_allowed_); DCHECK(allocating_indirect_functions_allowed_);
uint32_t index = static_cast<uint32_t>(indirect_functions_.size()); uint32_t index = static_cast<uint32_t>(indirect_functions_.size());
...@@ -441,7 +447,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const { ...@@ -441,7 +447,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
break; break;
} }
case Type::kStructType: { case Type::kStructType: {
StructType* struct_type = type.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 (auto field : struct_type->fields()) {
...@@ -449,6 +455,12 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const { ...@@ -449,6 +455,12 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
} }
break; break;
} }
case Type::kArrayType: {
ArrayType* array_type = type.array_type;
buffer->write_u8(kWasmArrayTypeCode);
WriteValueType(buffer, array_type->element_type());
break;
}
} }
} }
FixupSection(buffer, start); FixupSection(buffer, start);
......
...@@ -242,6 +242,7 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { ...@@ -242,6 +242,7 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
void AddDataSegment(const byte* data, uint32_t size, uint32_t dest); void AddDataSegment(const byte* data, uint32_t size, uint32_t dest);
uint32_t AddSignature(FunctionSig* sig); uint32_t AddSignature(FunctionSig* sig);
uint32_t AddStructType(StructType* type); uint32_t AddStructType(StructType* type);
uint32_t AddArrayType(ArrayType* type);
// In the current implementation, it's supported to have uninitialized slots // In the current implementation, it's supported to have uninitialized slots
// at the beginning and/or end of the indirect function table, as long as // at the beginning and/or end of the indirect function table, as long as
// the filled slots form a contiguous block in the middle. // the filled slots form a contiguous block in the middle.
...@@ -276,15 +277,18 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { ...@@ -276,15 +277,18 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
private: private:
struct Type { struct Type {
enum Kind { kFunctionSig, kStructType }; enum Kind { kFunctionSig, kStructType, kArrayType };
explicit Type(FunctionSig* signature) explicit Type(FunctionSig* signature)
: kind(kFunctionSig), sig(signature) {} : kind(kFunctionSig), sig(signature) {}
explicit Type(StructType* struct_type) explicit Type(StructType* struct_type)
: kind(kStructType), type(struct_type) {} : kind(kStructType), struct_type(struct_type) {}
explicit Type(ArrayType* array_type)
: kind(kArrayType), array_type(array_type) {}
Kind kind; Kind kind;
union { union {
FunctionSig* sig; FunctionSig* sig;
StructType* type; StructType* struct_type;
ArrayType* array_type;
}; };
}; };
......
...@@ -38,6 +38,7 @@ OBJECT_CONSTRUCTORS_IMPL(WasmModuleObject, JSObject) ...@@ -38,6 +38,7 @@ OBJECT_CONSTRUCTORS_IMPL(WasmModuleObject, JSObject)
OBJECT_CONSTRUCTORS_IMPL(WasmTableObject, JSObject) OBJECT_CONSTRUCTORS_IMPL(WasmTableObject, JSObject)
OBJECT_CONSTRUCTORS_IMPL(AsmWasmData, Struct) OBJECT_CONSTRUCTORS_IMPL(AsmWasmData, Struct)
TQ_OBJECT_CONSTRUCTORS_IMPL(WasmStruct) TQ_OBJECT_CONSTRUCTORS_IMPL(WasmStruct)
TQ_OBJECT_CONSTRUCTORS_IMPL(WasmArray)
NEVER_READ_ONLY_SPACE_IMPL(WasmDebugInfo) NEVER_READ_ONLY_SPACE_IMPL(WasmDebugInfo)
...@@ -51,6 +52,7 @@ CAST_ACCESSOR(WasmModuleObject) ...@@ -51,6 +52,7 @@ CAST_ACCESSOR(WasmModuleObject)
CAST_ACCESSOR(WasmTableObject) CAST_ACCESSOR(WasmTableObject)
CAST_ACCESSOR(AsmWasmData) CAST_ACCESSOR(AsmWasmData)
CAST_ACCESSOR(WasmStruct) CAST_ACCESSOR(WasmStruct)
CAST_ACCESSOR(WasmArray)
#define OPTIONAL_ACCESSORS(holder, name, type, offset) \ #define OPTIONAL_ACCESSORS(holder, name, type, offset) \
DEF_GETTER(holder, has_##name, bool) { \ DEF_GETTER(holder, has_##name, bool) { \
...@@ -438,6 +440,29 @@ ObjectSlot WasmStruct::RawField(int raw_offset) { ...@@ -438,6 +440,29 @@ ObjectSlot WasmStruct::RawField(int raw_offset) {
return ObjectSlot(FIELD_ADDR(*this, offset)); return ObjectSlot(FIELD_ADDR(*this, offset));
} }
wasm::ArrayType* WasmArray::type(Map map) {
DCHECK_EQ(WASM_ARRAY_TYPE, map.instance_type());
Foreign foreign = map.wasm_type_info();
return reinterpret_cast<wasm::ArrayType*>(foreign.foreign_address());
}
wasm::ArrayType* WasmArray::GcSafeType(Map map) {
DCHECK_EQ(WASM_ARRAY_TYPE, map.instance_type());
HeapObject raw = HeapObject::cast(map.constructor_or_backpointer());
MapWord map_word = raw.map_word();
HeapObject forwarded =
map_word.IsForwardingAddress() ? map_word.ToForwardingAddress() : raw;
Foreign foreign = Foreign::cast(forwarded);
return reinterpret_cast<wasm::ArrayType*>(foreign.foreign_address());
}
wasm::ArrayType* WasmArray::type() const { return type(map()); }
int WasmArray::SizeFor(Map map, int length) {
int element_size = type(map)->element_type().element_size_bytes();
return kHeaderSize + RoundUp(element_size * length, kTaggedSize);
}
#include "src/objects/object-macros-undef.h" #include "src/objects/object-macros-undef.h"
} // namespace internal } // namespace internal
......
...@@ -954,6 +954,22 @@ class WasmStruct : public TorqueGeneratedWasmStruct<WasmStruct, HeapObject> { ...@@ -954,6 +954,22 @@ class WasmStruct : public TorqueGeneratedWasmStruct<WasmStruct, HeapObject> {
TQ_OBJECT_CONSTRUCTORS(WasmStruct) TQ_OBJECT_CONSTRUCTORS(WasmStruct)
}; };
class WasmArray : public TorqueGeneratedWasmArray<WasmArray, HeapObject> {
public:
static inline wasm::ArrayType* type(Map map);
inline wasm::ArrayType* type() const;
static inline wasm::ArrayType* GcSafeType(Map map);
static inline int SizeFor(Map map, int length);
DECL_CAST(WasmArray)
DECL_PRINTER(WasmArray)
class BodyDescriptor;
TQ_OBJECT_CONSTRUCTORS(WasmArray)
};
#undef DECL_OPTIONAL_ACCESSORS #undef DECL_OPTIONAL_ACCESSORS
} // namespace internal } // namespace internal
......
...@@ -106,3 +106,11 @@ extern class AsmWasmData extends Struct { ...@@ -106,3 +106,11 @@ extern class AsmWasmData extends Struct {
@generateCppClass @generateCppClass
extern class WasmStruct extends HeapObject { extern class WasmStruct extends HeapObject {
} }
@generateCppClass
extern class WasmArray extends HeapObject {
length: uint32;
@if(TAGGED_SIZE_8_BYTES) optional_padding: uint32;
@ifnot(TAGGED_SIZE_8_BYTES) optional_padding: void;
}
...@@ -111,6 +111,56 @@ WASM_EXEC_TEST(BasicStruct) { ...@@ -111,6 +111,56 @@ WASM_EXEC_TEST(BasicStruct) {
"j", 0, nullptr)); "j", 0, nullptr));
} }
WASM_EXEC_TEST(BasicArray) {
// TODO(7748): Implement support in other tiers.
if (execution_tier == ExecutionTier::kLiftoff) return;
if (execution_tier == ExecutionTier::kInterpreter) return;
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(anyref);
v8::internal::AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
ArrayType type(wasm::kWasmI32);
int32_t type_index = builder->AddArrayType(&type);
ValueType kRefTypes[] = {ValueType(ValueType::kRef, type_index)};
FunctionSig sig_q_v(1, 0, kRefTypes);
WasmFunctionBuilder* h = builder->AddFunction(&sig_q_v);
h->builder()->AddExport(CStrVector("h"), h);
// Create an array of length 2, initialized to [42, 42].
byte h_code[] = {WASM_ARRAY_NEW(type_index, WASM_I32V(42), WASM_I32V(2)),
kExprEnd};
h->EmitCode(h_code, sizeof(h_code));
ZoneBuffer buffer(&zone);
builder->WriteTo(&buffer);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
testing::SetupIsolateForWasmModule(isolate);
ErrorThrower thrower(isolate, "Test");
Handle<WasmInstanceObject> instance =
testing::CompileAndInstantiateForTesting(
isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()))
.ToHandleChecked();
// TODO(7748): This uses the JavaScript interface to retrieve the plain
// WasmArray. Once the JS interaction story is settled, this may well
// need to be changed.
Handle<WasmExportedFunction> h_export =
testing::GetExportedFunction(isolate, instance, "h").ToHandleChecked();
Handle<Object> undefined = isolate->factory()->undefined_value();
Handle<Object> ref_result =
Execution::Call(isolate, h_export, undefined, 0, nullptr)
.ToHandleChecked();
CHECK(ref_result->IsWasmArray());
#if OBJECT_PRINT
ref_result->Print();
#endif
}
} // namespace test_gc } // namespace test_gc
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
......
...@@ -419,6 +419,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -419,6 +419,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
__VA_ARGS__, WASM_GC_OP(kExprStructSet), static_cast<byte>(typeidx), \ __VA_ARGS__, WASM_GC_OP(kExprStructSet), static_cast<byte>(typeidx), \
static_cast<byte>(fieldidx) static_cast<byte>(fieldidx)
#define WASM_ARRAY_NEW(index, default_value, length) \
default_value, length, WASM_GC_OP(kExprArrayNew), static_cast<byte>(index)
// Pass: sig_index, ...args, func_index // Pass: sig_index, ...args, func_index
#define WASM_CALL_INDIRECT(sig_index, ...) \ #define WASM_CALL_INDIRECT(sig_index, ...) \
__VA_ARGS__, kExprCallIndirect, static_cast<byte>(sig_index), TABLE_ZERO __VA_ARGS__, kExprCallIndirect, static_cast<byte>(sig_index), TABLE_ZERO
......
...@@ -138,14 +138,15 @@ INSTANCE_TYPES = { ...@@ -138,14 +138,15 @@ INSTANCE_TYPES = {
174: "SMI_BOX_TYPE", 174: "SMI_BOX_TYPE",
175: "SMI_PAIR_TYPE", 175: "SMI_PAIR_TYPE",
176: "SORT_STATE_TYPE", 176: "SORT_STATE_TYPE",
177: "WASM_STRUCT_TYPE", 177: "WASM_ARRAY_TYPE",
178: "WEAK_ARRAY_LIST_TYPE", 178: "WASM_STRUCT_TYPE",
179: "WEAK_CELL_TYPE", 179: "WEAK_ARRAY_LIST_TYPE",
180: "JS_PROXY_TYPE", 180: "WEAK_CELL_TYPE",
181: "JS_PROXY_TYPE",
1057: "JS_OBJECT_TYPE", 1057: "JS_OBJECT_TYPE",
181: "JS_GLOBAL_OBJECT_TYPE", 182: "JS_GLOBAL_OBJECT_TYPE",
182: "JS_GLOBAL_PROXY_TYPE", 183: "JS_GLOBAL_PROXY_TYPE",
183: "JS_MODULE_NAMESPACE_TYPE", 184: "JS_MODULE_NAMESPACE_TYPE",
1040: "JS_SPECIAL_API_OBJECT_TYPE", 1040: "JS_SPECIAL_API_OBJECT_TYPE",
1041: "JS_PRIMITIVE_WRAPPER_TYPE", 1041: "JS_PRIMITIVE_WRAPPER_TYPE",
1042: "JS_MAP_KEY_ITERATOR_TYPE", 1042: "JS_MAP_KEY_ITERATOR_TYPE",
...@@ -270,10 +271,10 @@ KNOWN_MAPS = { ...@@ -270,10 +271,10 @@ KNOWN_MAPS = {
("read_only_space", 0x00dd5): (152, "SyntheticModuleMap"), ("read_only_space", 0x00dd5): (152, "SyntheticModuleMap"),
("read_only_space", 0x00dfd): (154, "UncompiledDataWithoutPreparseDataMap"), ("read_only_space", 0x00dfd): (154, "UncompiledDataWithoutPreparseDataMap"),
("read_only_space", 0x00e25): (153, "UncompiledDataWithPreparseDataMap"), ("read_only_space", 0x00e25): (153, "UncompiledDataWithPreparseDataMap"),
("read_only_space", 0x00e4d): (178, "WeakArrayListMap"), ("read_only_space", 0x00e4d): (179, "WeakArrayListMap"),
("read_only_space", 0x00e75): (119, "EphemeronHashTableMap"), ("read_only_space", 0x00e75): (119, "EphemeronHashTableMap"),
("read_only_space", 0x00e9d): (162, "EmbedderDataArrayMap"), ("read_only_space", 0x00e9d): (162, "EmbedderDataArrayMap"),
("read_only_space", 0x00ec5): (179, "WeakCellMap"), ("read_only_space", 0x00ec5): (180, "WeakCellMap"),
("read_only_space", 0x00eed): (32, "StringMap"), ("read_only_space", 0x00eed): (32, "StringMap"),
("read_only_space", 0x00f15): (41, "ConsOneByteStringMap"), ("read_only_space", 0x00f15): (41, "ConsOneByteStringMap"),
("read_only_space", 0x00f3d): (33, "ConsStringMap"), ("read_only_space", 0x00f3d): (33, "ConsStringMap"),
......
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