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

[wasm-gc] Preliminary changes for array.init_from_data

Changes:
- Rename kWasmTrapDataSegmentDropped to the more accurate ~OutOfBounds.
- Drop unused argument from {WasmCompiler::ArrayInit}.
- Rename {Factory::NewWasmArray} -> NewWasmArrayFromElements.
- Add error handling to {InitExprInterface}.
- Allow the data count section to appear anywhere in the module under
  --experimental-wasm-gc. Add the same capability in
  wasm-module-builder.js.
- Add {WasmArray::MaxLength(uint32_t element_size_log2)}.
- Add kTrapArrayTooLarge in wasm-module-builder.js.
- Small test improvements in gc-nominal.js.

Bug: v8:7748
Change-Id: I68ca0e8b08f906503f0d82e5866395018d216382
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3401593Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78730}
parent 21e51043
......@@ -423,7 +423,7 @@ extern enum MessageTemplate {
kWasmTrapRemByZero,
kWasmTrapFloatUnrepresentable,
kWasmTrapFuncSigMismatch,
kWasmTrapDataSegmentDropped,
kWasmTrapDataSegmentOutOfBounds,
kWasmTrapElemSegmentDropped,
kWasmTrapTableOutOfBounds,
kWasmTrapRethrowNull,
......
......@@ -638,8 +638,8 @@ builtin ThrowWasmTrapFuncSigMismatch(): JSAny {
tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapFuncSigMismatch));
}
builtin ThrowWasmTrapDataSegmentDropped(): JSAny {
tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapDataSegmentDropped));
builtin ThrowWasmTrapDataSegmentOutOfBounds(): JSAny {
tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapDataSegmentOutOfBounds));
}
builtin ThrowWasmTrapElemSegmentDropped(): JSAny {
......
......@@ -1761,7 +1761,7 @@ enum IsolateAddressId {
V(TrapRemByZero) \
V(TrapFloatUnrepresentable) \
V(TrapFuncSigMismatch) \
V(TrapDataSegmentDropped) \
V(TrapDataSegmentOutOfBounds) \
V(TrapElemSegmentDropped) \
V(TrapTableOutOfBounds) \
V(TrapRethrowNull) \
......
......@@ -590,7 +590,7 @@ namespace internal {
T(WasmTrapFuncSigMismatch, "null function or function signature mismatch") \
T(WasmTrapMultiReturnLengthMismatch, "multi-return length mismatch") \
T(WasmTrapJSTypeError, "type incompatibility when transforming from/to JS") \
T(WasmTrapDataSegmentDropped, "data segment has been dropped") \
T(WasmTrapDataSegmentOutOfBounds, "data segment out of bounds") \
T(WasmTrapElemSegmentDropped, "element segment has been dropped") \
T(WasmTrapRethrowNull, "rethrowing null value") \
T(WasmTrapNullDereference, "dereferencing a null pointer") \
......
......@@ -5393,6 +5393,7 @@ void WasmGraphBuilder::MemoryInit(uint32_t data_segment_index, Node* dst,
MachineType sig_types[] = {MachineType::Int32(), MachineType::Pointer()};
MachineSignature sig(1, 1, sig_types);
Node* call = BuildCCall(&sig, function, stack_slot);
// TODO(manoskouk): Also throw kDataSegmentOutOfBounds.
TrapIfFalse(wasm::kTrapMemOutOfBounds, call, position);
}
......@@ -5602,8 +5603,7 @@ Node* WasmGraphBuilder::ArrayNewWithRtt(uint32_t array_index,
return a;
}
Node* WasmGraphBuilder::ArrayInit(uint32_t array_index,
const wasm::ArrayType* type, Node* rtt,
Node* WasmGraphBuilder::ArrayInit(const wasm::ArrayType* type, Node* rtt,
base::Vector<Node*> elements) {
wasm::ValueType element_type = type->element_type();
// TODO(7748): Consider using gasm_->Allocate().
......
......@@ -505,7 +505,7 @@ class WasmGraphBuilder {
void ArrayCopy(Node* dst_array, Node* dst_index, CheckForNull dst_null_check,
Node* src_array, Node* src_index, CheckForNull src_null_check,
Node* length, wasm::WasmCodePosition position);
Node* ArrayInit(uint32_t array_index, const wasm::ArrayType* type, Node* rtt,
Node* ArrayInit(const wasm::ArrayType* type, Node* rtt,
base::Vector<Node*> elements);
Node* I31New(Node* input);
Node* I31GetS(Node* input);
......
......@@ -1620,12 +1620,13 @@ Handle<WasmCapiFunctionData> Factory::NewWasmCapiFunctionData(
return handle(result, isolate());
}
Handle<WasmArray> Factory::NewWasmArray(
Handle<WasmArray> Factory::NewWasmArrayFromElements(
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);
DisallowGarbageCollection no_gc;
raw.set_map_after_allocation(*map);
WasmArray result = WasmArray::cast(raw);
result.set_raw_properties_or_hash(*empty_fixed_array(), kRelaxedStore);
......
......@@ -615,9 +615,9 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
Handle<HeapObject> suspender);
Handle<WasmStruct> NewWasmStruct(const wasm::StructType* type,
wasm::WasmValue* args, Handle<Map> map);
Handle<WasmArray> NewWasmArray(const wasm::ArrayType* type,
const std::vector<wasm::WasmValue>& elements,
Handle<Map> map);
Handle<WasmArray> NewWasmArrayFromElements(
const wasm::ArrayType* type, const std::vector<wasm::WasmValue>& elements,
Handle<Map> map);
Handle<SharedFunctionInfo> NewSharedFunctionInfoForWasmExportedFunction(
Handle<String> name, Handle<WasmExportedFunctionData> data);
......
......@@ -4844,6 +4844,7 @@ class LiftoffCompiler {
// register for the result.
LiftoffRegister result(instance);
GenerateCCall(&result, &sig, kVoid, args, ext_ref);
// TODO(manoskouk): Also throw kDataSegmentOutOfBounds.
Label* trap_label =
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapMemOutOfBounds);
__ emit_cond_jump(kEqual, trap_label, kI32, result.gp());
......
......@@ -1108,8 +1108,8 @@ class WasmGraphBuildingInterface {
for (uint32_t i = 0; i < elements.size(); i++) {
element_nodes[i] = elements[i].node;
}
result->node = builder_->ArrayInit(imm.index, imm.array_type, rtt.node,
VectorOf(element_nodes));
result->node =
builder_->ArrayInit(imm.array_type, rtt.node, VectorOf(element_nodes));
}
void I31New(FullDecoder* decoder, const Value& input, Value* result) {
......
......@@ -19,34 +19,34 @@ namespace wasm {
void InitExprInterface::I32Const(FullDecoder* decoder, Value* result,
int32_t value) {
if (isolate_ != nullptr) result->runtime_value = WasmValue(value);
if (generate_result()) result->runtime_value = WasmValue(value);
}
void InitExprInterface::I64Const(FullDecoder* decoder, Value* result,
int64_t value) {
if (isolate_ != nullptr) result->runtime_value = WasmValue(value);
if (generate_result()) result->runtime_value = WasmValue(value);
}
void InitExprInterface::F32Const(FullDecoder* decoder, Value* result,
float value) {
if (isolate_ != nullptr) result->runtime_value = WasmValue(value);
if (generate_result()) result->runtime_value = WasmValue(value);
}
void InitExprInterface::F64Const(FullDecoder* decoder, Value* result,
double value) {
if (isolate_ != nullptr) result->runtime_value = WasmValue(value);
if (generate_result()) result->runtime_value = WasmValue(value);
}
void InitExprInterface::S128Const(FullDecoder* decoder,
Simd128Immediate<validate>& imm,
Value* result) {
if (isolate_ == nullptr) return;
if (!generate_result()) return;
result->runtime_value = WasmValue(imm.value, kWasmS128);
}
void InitExprInterface::RefNull(FullDecoder* decoder, ValueType type,
Value* result) {
if (isolate_ == nullptr) return;
if (!generate_result()) return;
result->runtime_value = WasmValue(isolate_->factory()->null_value(), type);
}
......@@ -56,6 +56,7 @@ void InitExprInterface::RefFunc(FullDecoder* decoder, uint32_t function_index,
outer_module_->functions[function_index].declared = true;
return;
}
if (!generate_result()) return;
ValueType type = ValueType::Ref(module_->functions[function_index].sig_index,
kNonNullable);
Handle<WasmInternalFunction> internal =
......@@ -66,7 +67,7 @@ void InitExprInterface::RefFunc(FullDecoder* decoder, uint32_t function_index,
void InitExprInterface::GlobalGet(FullDecoder* decoder, Value* result,
const GlobalIndexImmediate<validate>& imm) {
if (isolate_ == nullptr) return;
if (!generate_result()) return;
const WasmGlobal& global = module_->globals[imm.index];
DCHECK(!global.mutability);
result->runtime_value =
......@@ -85,7 +86,7 @@ void InitExprInterface::GlobalGet(FullDecoder* decoder, Value* result,
void InitExprInterface::StructNewWithRtt(
FullDecoder* decoder, const StructIndexImmediate<validate>& imm,
const Value& rtt, const Value args[], Value* result) {
if (isolate_ == nullptr) return;
if (!generate_result()) return;
std::vector<WasmValue> field_values(imm.struct_type->field_count());
for (size_t i = 0; i < field_values.size(); i++) {
field_values[i] = args[i].runtime_value;
......@@ -127,7 +128,7 @@ WasmValue DefaultValueForType(ValueType type, Isolate* isolate) {
void InitExprInterface::StructNewDefault(
FullDecoder* decoder, const StructIndexImmediate<validate>& imm,
const Value& rtt, Value* result) {
if (isolate_ == nullptr) return;
if (!generate_result()) return;
std::vector<WasmValue> field_values(imm.struct_type->field_count());
for (uint32_t i = 0; i < field_values.size(); i++) {
field_values[i] = DefaultValueForType(imm.struct_type->field(i), isolate_);
......@@ -143,11 +144,11 @@ void InitExprInterface::ArrayInit(FullDecoder* decoder,
const ArrayIndexImmediate<validate>& imm,
const base::Vector<Value>& elements,
const Value& rtt, Value* result) {
if (isolate_ == nullptr) return;
if (!generate_result()) return;
std::vector<WasmValue> element_values;
for (Value elem : elements) element_values.push_back(elem.runtime_value);
result->runtime_value =
WasmValue(isolate_->factory()->NewWasmArray(
WasmValue(isolate_->factory()->NewWasmArrayFromElements(
imm.array_type, element_values,
Handle<Map>::cast(rtt.runtime_value.to_ref())),
ValueType::Ref(HeapType(imm.index), kNonNullable));
......@@ -155,7 +156,7 @@ void InitExprInterface::ArrayInit(FullDecoder* decoder,
void InitExprInterface::RttCanon(FullDecoder* decoder, uint32_t type_index,
Value* result) {
if (isolate_ == nullptr) return;
if (!generate_result()) return;
result->runtime_value = WasmValue(
handle(instance_->managed_object_maps().get(type_index), isolate_),
ValueType::Rtt(type_index, 0));
......@@ -164,7 +165,7 @@ void InitExprInterface::RttCanon(FullDecoder* decoder, uint32_t type_index,
void InitExprInterface::RttSub(FullDecoder* decoder, uint32_t type_index,
const Value& parent, Value* result,
WasmRttSubMode mode) {
if (isolate_ == nullptr) return;
if (!generate_result()) return;
ValueType type = parent.type.has_depth()
? ValueType::Rtt(type_index, parent.type.depth() + 1)
: ValueType::Rtt(type_index);
......@@ -180,7 +181,7 @@ void InitExprInterface::DoReturn(FullDecoder* decoder,
end_found_ = true;
// End decoding on "end".
decoder->set_end(decoder->pc() + 1);
if (isolate_ != nullptr) result_ = decoder->stack_value(1)->runtime_value;
if (generate_result()) result_ = decoder->stack_value(1)->runtime_value;
}
} // namespace wasm
......
......@@ -75,9 +75,13 @@ class InitExprInterface {
return result_;
}
bool end_found() { return end_found_; }
bool runtime_error() { return error_ != nullptr; }
const char* runtime_error_msg() { return error_; }
private:
bool generate_result() { return isolate_ != nullptr && !runtime_error(); }
bool end_found_ = false;
const char* error_ = nullptr;
WasmValue result_;
const WasmModule* module_;
WasmModule* outer_module_;
......
......@@ -409,15 +409,20 @@ class ModuleDecoderImpl : public Decoder {
break;
case kDataCountSectionCode:
if (!CheckUnorderedSection(section_code)) return;
if (!CheckSectionOrder(section_code, kElementSectionCode,
kCodeSectionCode))
// If wasm-gc is enabled, we allow the data cound section anywhere in
// the module.
if (!enabled_features_.has_gc() &&
!CheckSectionOrder(section_code, kElementSectionCode,
kCodeSectionCode)) {
return;
}
break;
case kTagSectionCode:
if (!CheckUnorderedSection(section_code)) return;
if (!CheckSectionOrder(section_code, kMemorySectionCode,
kGlobalSectionCode))
kGlobalSectionCode)) {
return;
}
break;
case kNameSectionCode:
// TODO(titzer): report out of place name section as a warning.
......
......@@ -936,9 +936,10 @@ bool HasDefaultToNumberBehaviour(Isolate* isolate,
return true;
}
V8_INLINE WasmValue
EvaluateInitExpression(Zone* zone, ConstantExpression expr, ValueType expected,
Isolate* isolate, Handle<WasmInstanceObject> instance) {
V8_INLINE WasmValue EvaluateInitExpression(Zone* zone, ConstantExpression expr,
ValueType expected, Isolate* isolate,
Handle<WasmInstanceObject> instance,
ErrorThrower* thrower) {
switch (expr.kind()) {
case ConstantExpression::kEmpty:
UNREACHABLE();
......@@ -976,6 +977,11 @@ EvaluateInitExpression(Zone* zone, ConstantExpression expr, ValueType expected,
decoder.DecodeFunctionBody();
if (decoder.interface().runtime_error()) {
thrower->RuntimeError("%s", decoder.interface().runtime_error_msg());
return {};
}
return decoder.interface().result();
}
}
......@@ -1042,17 +1048,20 @@ void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
if (module_->is_memory64) {
uint64_t dest_offset_64 =
EvaluateInitExpression(&init_expr_zone_, segment.dest_addr, kWasmI64,
isolate_, instance)
isolate_, instance, thrower_)
.to_u64();
if (thrower_->error()) return;
// Clamp to {std::numeric_limits<size_t>::max()}, which is always an
// invalid offset.
DCHECK_GT(std::numeric_limits<size_t>::max(), instance->memory_size());
dest_offset = static_cast<size_t>(std::min(
dest_offset_64, uint64_t{std::numeric_limits<size_t>::max()}));
} else {
dest_offset = EvaluateInitExpression(&init_expr_zone_, segment.dest_addr,
kWasmI32, isolate_, instance)
.to_u32();
dest_offset =
EvaluateInitExpression(&init_expr_zone_, segment.dest_addr, kWasmI32,
isolate_, instance, thrower_)
.to_u32();
if (thrower_->error()) return;
}
if (!base::IsInBounds<size_t>(dest_offset, size, instance->memory_size())) {
......@@ -1749,8 +1758,10 @@ void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> instance) {
// Happens with imported globals.
if (!global.init.is_set()) continue;
WasmValue value = EvaluateInitExpression(&init_expr_zone_, global.init,
global.type, isolate_, instance);
WasmValue value =
EvaluateInitExpression(&init_expr_zone_, global.init, global.type,
isolate_, instance, thrower_);
if (thrower_->error()) return;
if (global.type.is_reference()) {
tagged_globals_->set(global.offset, *value.to_ref());
......@@ -2014,13 +2025,14 @@ void InstanceBuilder::InitializeNonDefaultableTables(
SetFunctionTableNullEntry(isolate_, table_object, entry_index);
}
} else {
Handle<Object> value =
WasmValue value =
EvaluateInitExpression(&init_expr_zone_, table.initial_value,
table.type, isolate_, instance)
.to_ref();
table.type, isolate_, instance, thrower_);
if (thrower_->error()) return;
for (uint32_t entry_index = 0; entry_index < table.initial_size;
entry_index++) {
WasmTableObject::Set(isolate_, table_object, entry_index, value);
WasmTableObject::Set(isolate_, table_object, entry_index,
value.to_ref());
}
}
}
......@@ -2050,6 +2062,8 @@ bool LoadElemSegmentImpl(Zone* zone, Isolate* isolate,
bool is_function_table =
IsSubtypeOf(table_object->type(), kWasmFuncRef, instance->module());
ErrorThrower thrower(isolate, "LoadElemSegment");
for (size_t i = 0; i < count; ++i) {
ConstantExpression entry = elem_segment.entries[src + i];
int entry_index = static_cast<int>(dst + i);
......@@ -2060,11 +2074,10 @@ bool LoadElemSegmentImpl(Zone* zone, Isolate* isolate,
entry.kind() == ConstantExpression::kRefNull) {
SetFunctionTableNullEntry(isolate, table_object, entry_index);
} else {
Handle<Object> value =
EvaluateInitExpression(zone, entry, elem_segment.type, isolate,
instance)
.to_ref();
WasmTableObject::Set(isolate, table_object, entry_index, value);
WasmValue value = EvaluateInitExpression(zone, entry, elem_segment.type,
isolate, instance, &thrower);
if (thrower.error()) return false;
WasmTableObject::Set(isolate, table_object, entry_index, value.to_ref());
}
}
return true;
......@@ -2079,9 +2092,11 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
if (elem_segment.status != WasmElemSegment::kStatusActive) continue;
uint32_t table_index = elem_segment.table_index;
uint32_t dst = EvaluateInitExpression(&init_expr_zone_, elem_segment.offset,
kWasmI32, isolate_, instance)
.to_u32();
uint32_t dst =
EvaluateInitExpression(&init_expr_zone_, elem_segment.offset, kWasmI32,
isolate_, instance, thrower_)
.to_u32();
if (thrower_->error()) return;
uint32_t src = 0;
size_t count = elem_segment.entries.size();
......
......@@ -967,12 +967,15 @@ class WasmArray : public TorqueGeneratedWasmArray<WasmArray, WasmObject> {
inline uint32_t element_offset(uint32_t index);
inline Address ElementAddress(uint32_t index);
static int MaxLength(const wasm::ArrayType* type) {
static int MaxLength(uint32_t element_size_log2) {
// The total object size must fit into a Smi, for filler objects. To make
// the behavior of Wasm programs independent from the Smi configuration,
// we hard-code the smaller of the two supported ranges.
int element_shift = type->element_type().element_size_log2();
return (SmiTagging<4>::kSmiMaxValue - kHeaderSize) >> element_shift;
return (SmiTagging<4>::kSmiMaxValue - kHeaderSize) >> element_size_log2;
}
static int MaxLength(const wasm::ArrayType* type) {
return MaxLength(type->element_type().element_size_log2());
}
static inline void EncodeElementSizeInMap(int element_size, Map map);
......
......@@ -6,7 +6,8 @@
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function() {
(function TestNominalTypesBasic() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
let struct1 = builder.addStructSubtype([makeField(kWasmI32, true)]);
let struct2 = builder.addStructSubtype(
......@@ -42,13 +43,14 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
builder.instantiate();
})();
(function () {
let builder = new WasmModuleBuilder();
let t0 = builder.addStructSubtype([]);
for (let i = 0; i < 32; i++) {
builder.addStructSubtype([], i);
}
assertThrows(
() => builder.instantiate(), WebAssembly.CompileError,
/subtyping depth is greater than allowed/);
(function TestSubtypingDepthTooLarge() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addStructSubtype([]);
for (let i = 0; i < 32; i++) {
builder.addStructSubtype([], i);
}
assertThrows(
() => builder.instantiate(), WebAssembly.CompileError,
/subtyping depth is greater than allowed/);
})();
......@@ -867,9 +867,10 @@ let kTrapFloatUnrepresentable = 5;
let kTrapTableOutOfBounds = 6;
let kTrapFuncSigMismatch = 7;
let kTrapUnalignedAccess = 8;
let kTrapDataSegmentDropped = 9;
let kTrapDataSegmentOutOfBounds = 9;
let kTrapElemSegmentDropped = 10;
let kTrapRethrowNull = 11;
let kTrapArrayTooLarge = 12;
let kTrapMsgs = [
'unreachable', // --
......@@ -881,9 +882,10 @@ let kTrapMsgs = [
'table index is out of bounds', // --
'null function or function signature mismatch', // --
'operation does not support unaligned accesses', // --
'data segment has been dropped', // --
'data segment out of bounds', // --
'element segment has been dropped', // --
'rethrowing null value' // --
'rethrowing null value', // --
'requested new array is too large' // --
];
// This requires test/mjsunit/mjsunit.js.
......@@ -1364,6 +1366,7 @@ class WasmModuleBuilder {
this.num_imported_globals = 0;
this.num_imported_tables = 0;
this.num_imported_tags = 0;
this.early_data_count_section = false;
return this;
}
......@@ -1768,6 +1771,14 @@ class WasmModuleBuilder {
});
}
// If there are any passive data segments, add the DataCount section.
if (this.early_data_count_section &&
wasm.data_segments.some(seg => !seg.is_active)) {
binary.emit_section(kDataCountSectionCode, section => {
section.emit_u32v(wasm.data_segments.length);
});
}
// Add table section
if (wasm.tables.length > 0) {
if (debug) print('emitting tables @ ' + binary.length);
......@@ -1930,7 +1941,8 @@ class WasmModuleBuilder {
}
// If there are any passive data segments, add the DataCount section.
if (wasm.data_segments.some(seg => !seg.is_active)) {
if (!this.early_data_count_section &&
wasm.data_segments.some(seg => !seg.is_active)) {
binary.emit_section(kDataCountSectionCode, section => {
section.emit_u32v(wasm.data_segments.length);
});
......
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