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

[wasm-gc] Introduce supertype of all arrays

We introduce a type arrayref, which is a supertype of all array types
and a subtype of dataref. We change array.len to accept values of type
(ref null array).

Drive-by: Fix kEq/kData case in TypecheckJSObject.

Bug: v8:7748
Change-Id: I47c6a4487ddf5e7280c1427f43abe87a97c896bd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3368105Reviewed-by: 's avatarSimon Zünd <szuend@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78565}
parent eb129a5c
...@@ -6453,8 +6453,9 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { ...@@ -6453,8 +6453,9 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
WasmInternalFunction::kExternalOffset)); WasmInternalFunction::kExternalOffset));
} }
} }
case wasm::HeapType::kData:
case wasm::HeapType::kEq: case wasm::HeapType::kEq:
case wasm::HeapType::kData:
case wasm::HeapType::kArray:
case wasm::HeapType::kI31: case wasm::HeapType::kI31:
// TODO(7748): Update this when JS interop is settled. // TODO(7748): Update this when JS interop is settled.
if (type.kind() == wasm::kOptRef) { if (type.kind() == wasm::kOptRef) {
...@@ -6644,6 +6645,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { ...@@ -6644,6 +6645,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
BuildCheckValidRefValue(input, js_context, type); BuildCheckValidRefValue(input, js_context, type);
return BuildUnpackObjectWrapper(input); return BuildUnpackObjectWrapper(input);
case wasm::HeapType::kData: case wasm::HeapType::kData:
case wasm::HeapType::kArray:
case wasm::HeapType::kEq: case wasm::HeapType::kEq:
case wasm::HeapType::kI31: case wasm::HeapType::kI31:
// TODO(7748): Update this when JS interop has settled. // TODO(7748): Update this when JS interop has settled.
......
...@@ -796,6 +796,9 @@ Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type, ...@@ -796,6 +796,9 @@ Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type,
case wasm::HeapType::kData: case wasm::HeapType::kData:
generic_name = "data"; generic_name = "data";
break; break;
case wasm::HeapType::kArray:
generic_name = "array";
break;
case wasm::HeapType::kAny: case wasm::HeapType::kAny:
generic_name = "any"; generic_name = "any";
break; break;
......
...@@ -203,6 +203,7 @@ HeapType read_heap_type(Decoder* decoder, const byte* pc, ...@@ -203,6 +203,7 @@ HeapType read_heap_type(Decoder* decoder, const byte* pc,
case kEqRefCode: case kEqRefCode:
case kI31RefCode: case kI31RefCode:
case kDataRefCode: case kDataRefCode:
case kArrayRefCode:
case kAnyRefCode: case kAnyRefCode:
if (!VALIDATE(enabled.has_gc())) { if (!VALIDATE(enabled.has_gc())) {
DecodeError<validate>( DecodeError<validate>(
...@@ -269,6 +270,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc, ...@@ -269,6 +270,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
case kEqRefCode: case kEqRefCode:
case kI31RefCode: case kI31RefCode:
case kDataRefCode: case kDataRefCode:
case kArrayRefCode:
case kAnyRefCode: case kAnyRefCode:
if (!VALIDATE(enabled.has_gc())) { if (!VALIDATE(enabled.has_gc())) {
DecodeError<validate>( DecodeError<validate>(
...@@ -281,9 +283,10 @@ ValueType read_value_type(Decoder* decoder, const byte* pc, ...@@ -281,9 +283,10 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
case kExternRefCode: case kExternRefCode:
case kFuncRefCode: { case kFuncRefCode: {
HeapType heap_type = HeapType::from_code(code); HeapType heap_type = HeapType::from_code(code);
Nullability nullability = code == kI31RefCode || code == kDataRefCode Nullability nullability =
? kNonNullable code == kI31RefCode || code == kDataRefCode || code == kArrayRefCode
: kNullable; ? kNonNullable
: kNullable;
return ValueType::Ref(heap_type, nullability); return ValueType::Ref(heap_type, nullability);
} }
case kI32Code: case kI32Code:
...@@ -4312,9 +4315,11 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> { ...@@ -4312,9 +4315,11 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
} }
case kExprArrayLen: { case kExprArrayLen: {
NON_CONST_ONLY NON_CONST_ONLY
// Read but ignore an immediate array type index.
// TODO(7748): Remove this once we are ready to make breaking changes.
ArrayIndexImmediate<validate> imm(this, this->pc_ + opcode_length); ArrayIndexImmediate<validate> imm(this, this->pc_ + opcode_length);
if (!this->Validate(this->pc_ + opcode_length, imm)) return 0; Value array_obj =
Value array_obj = Peek(0, 0, ValueType::Ref(imm.index, kNullable)); Peek(0, 0, ValueType::Ref(HeapType::kArray, kNullable));
Value value = CreateValue(kWasmI32); Value value = CreateValue(kWasmI32);
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayLen, array_obj, &value); CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayLen, array_obj, &value);
Drop(array_obj); Drop(array_obj);
......
...@@ -66,6 +66,7 @@ class HeapType { ...@@ -66,6 +66,7 @@ class HeapType {
kEq, // shorthand: q kEq, // shorthand: q
kI31, // shorthand: j kI31, // shorthand: j
kData, // shorthand: o kData, // shorthand: o
kArray, // shorthand: g
kAny, // shorthand: a kAny, // shorthand: a
// This value is used to represent failures in the parsing of heap types and // This value is used to represent failures in the parsing of heap types and
// does not correspond to a wasm heap type. // does not correspond to a wasm heap type.
...@@ -90,6 +91,8 @@ class HeapType { ...@@ -90,6 +91,8 @@ class HeapType {
return HeapType(kAny); return HeapType(kAny);
case ValueTypeCode::kDataRefCode: case ValueTypeCode::kDataRefCode:
return HeapType(kData); return HeapType(kData);
case ValueTypeCode::kArrayRefCode:
return HeapType(kArray);
default: default:
return HeapType(kBottom); return HeapType(kBottom);
} }
...@@ -142,6 +145,8 @@ class HeapType { ...@@ -142,6 +145,8 @@ class HeapType {
return std::string("i31"); return std::string("i31");
case kData: case kData:
return std::string("data"); return std::string("data");
case kArray:
return std::string("array");
case kAny: case kAny:
return std::string("any"); return std::string("any");
default: default:
...@@ -165,6 +170,8 @@ class HeapType { ...@@ -165,6 +170,8 @@ class HeapType {
return mask | kI31RefCode; return mask | kI31RefCode;
case kData: case kData:
return mask | kDataRefCode; return mask | kDataRefCode;
case kArray:
return mask | kArrayRefCode;
case kAny: case kAny:
return mask | kAnyRefCode; return mask | kAnyRefCode;
default: default:
...@@ -470,6 +477,8 @@ class ValueType { ...@@ -470,6 +477,8 @@ class ValueType {
return kI31RefCode; return kI31RefCode;
case HeapType::kData: case HeapType::kData:
return kDataRefCode; return kDataRefCode;
case HeapType::kArray:
return kArrayRefCode;
default: default:
return kRefCode; return kRefCode;
} }
...@@ -493,11 +502,19 @@ class ValueType { ...@@ -493,11 +502,19 @@ class ValueType {
// Returns true iff the heap type is needed to encode this type in the wasm // Returns true iff the heap type is needed to encode this type in the wasm
// binary format, taking into account available type shorthands. // binary format, taking into account available type shorthands.
constexpr bool encoding_needs_heap_type() const { constexpr bool encoding_needs_heap_type() const {
return (kind() == kRef && heap_representation() != HeapType::kI31 && switch (kind()) {
heap_representation() != HeapType::kData) || case kRef:
(kind() == kOptRef && (heap_type().is_index() || return heap_representation() != HeapType::kI31 &&
heap_representation() == HeapType::kI31 || heap_representation() != HeapType::kArray &&
heap_representation() == HeapType::kData)); heap_representation() != HeapType::kData;
case kOptRef:
return heap_representation() != HeapType::kFunc &&
heap_representation() != HeapType::kExtern &&
heap_representation() != HeapType::kEq &&
heap_representation() != HeapType::kAny;
default:
return false;
}
} }
static constexpr int kLastUsedBit = 30; static constexpr int kLastUsedBit = 30;
...@@ -588,6 +605,8 @@ constexpr ValueType kWasmEqRef = ValueType::Ref(HeapType::kEq, kNullable); ...@@ -588,6 +605,8 @@ constexpr ValueType kWasmEqRef = ValueType::Ref(HeapType::kEq, kNullable);
constexpr ValueType kWasmI31Ref = ValueType::Ref(HeapType::kI31, kNonNullable); constexpr ValueType kWasmI31Ref = ValueType::Ref(HeapType::kI31, kNonNullable);
constexpr ValueType kWasmDataRef = constexpr ValueType kWasmDataRef =
ValueType::Ref(HeapType::kData, kNonNullable); ValueType::Ref(HeapType::kData, kNonNullable);
constexpr ValueType kWasmArrayRef =
ValueType::Ref(HeapType::kArray, kNonNullable);
constexpr ValueType kWasmAnyRef = ValueType::Ref(HeapType::kAny, kNullable); constexpr ValueType kWasmAnyRef = ValueType::Ref(HeapType::kAny, kNullable);
// This is used in wasm.tq. // This is used in wasm.tq.
......
...@@ -47,6 +47,7 @@ enum ValueTypeCode : uint8_t { ...@@ -47,6 +47,7 @@ enum ValueTypeCode : uint8_t {
kRttWithDepthCode = 0x69, kRttWithDepthCode = 0x69,
kRttCode = 0x68, kRttCode = 0x68,
kDataRefCode = 0x67, kDataRefCode = 0x67,
kArrayRefCode = 0x66
}; };
// Binary encoding of other types. // Binary encoding of other types.
constexpr uint8_t kWasmFunctionTypeCode = 0x60; constexpr uint8_t kWasmFunctionTypeCode = 0x60;
......
...@@ -1528,6 +1528,7 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1528,6 +1528,7 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
case i::wasm::HeapType::kEq: case i::wasm::HeapType::kEq:
case internal::wasm::HeapType::kI31: case internal::wasm::HeapType::kI31:
case internal::wasm::HeapType::kData: case internal::wasm::HeapType::kData:
case internal::wasm::HeapType::kArray:
default: default:
// TODO(7748): Implement these. // TODO(7748): Implement these.
UNIMPLEMENTED(); UNIMPLEMENTED();
...@@ -1719,6 +1720,7 @@ void EncodeExceptionValues(v8::Isolate* isolate, ...@@ -1719,6 +1720,7 @@ void EncodeExceptionValues(v8::Isolate* isolate,
case i::wasm::HeapType::kEq: case i::wasm::HeapType::kEq:
case i::wasm::HeapType::kI31: case i::wasm::HeapType::kI31:
case i::wasm::HeapType::kData: case i::wasm::HeapType::kData:
case i::wasm::HeapType::kArray:
values_out->set(index++, *Utils::OpenHandle(*value)); values_out->set(index++, *Utils::OpenHandle(*value));
break; break;
case internal::wasm::HeapType::kBottom: case internal::wasm::HeapType::kBottom:
...@@ -2272,6 +2274,7 @@ void WebAssemblyExceptionGetArg( ...@@ -2272,6 +2274,7 @@ void WebAssemblyExceptionGetArg(
case i::wasm::HeapType::kEq: case i::wasm::HeapType::kEq:
case i::wasm::HeapType::kI31: case i::wasm::HeapType::kI31:
case i::wasm::HeapType::kData: case i::wasm::HeapType::kData:
case i::wasm::HeapType::kArray:
decode_index++; decode_index++;
break; break;
case i::wasm::HeapType::kBottom: case i::wasm::HeapType::kBottom:
...@@ -2330,6 +2333,7 @@ void WebAssemblyExceptionGetArg( ...@@ -2330,6 +2333,7 @@ void WebAssemblyExceptionGetArg(
case i::wasm::HeapType::kAny: case i::wasm::HeapType::kAny:
case i::wasm::HeapType::kEq: case i::wasm::HeapType::kEq:
case i::wasm::HeapType::kI31: case i::wasm::HeapType::kI31:
case i::wasm::HeapType::kArray:
case i::wasm::HeapType::kData: { case i::wasm::HeapType::kData: {
auto obj = values->get(decode_index); auto obj = values->get(decode_index);
result = Utils::ToLocal(i::Handle<i::Object>(obj, i_isolate)); result = Utils::ToLocal(i::Handle<i::Object>(obj, i_isolate));
...@@ -2410,10 +2414,10 @@ void WebAssemblyGlobalGetValueCommon( ...@@ -2410,10 +2414,10 @@ void WebAssemblyGlobalGetValueCommon(
case i::wasm::kOptRef: case i::wasm::kOptRef:
switch (receiver->type().heap_representation()) { switch (receiver->type().heap_representation()) {
case i::wasm::HeapType::kExtern: case i::wasm::HeapType::kExtern:
case i::wasm::HeapType::kAny:
return_value.Set(Utils::ToLocal(receiver->GetRef())); return_value.Set(Utils::ToLocal(receiver->GetRef()));
break; break;
case i::wasm::HeapType::kFunc: case i::wasm::HeapType::kFunc: {
case i::wasm::HeapType::kAny: {
i::Handle<i::Object> result = receiver->GetRef(); i::Handle<i::Object> result = receiver->GetRef();
if (result->IsWasmInternalFunction()) { if (result->IsWasmInternalFunction()) {
result = handle( result = handle(
...@@ -2423,10 +2427,11 @@ void WebAssemblyGlobalGetValueCommon( ...@@ -2423,10 +2427,11 @@ void WebAssemblyGlobalGetValueCommon(
return_value.Set(Utils::ToLocal(result)); return_value.Set(Utils::ToLocal(result));
break; break;
} }
case internal::wasm::HeapType::kBottom: case i::wasm::HeapType::kBottom:
UNREACHABLE(); UNREACHABLE();
case internal::wasm::HeapType::kI31: case i::wasm::HeapType::kI31:
case internal::wasm::HeapType::kData: case i::wasm::HeapType::kData:
case i::wasm::HeapType::kArray:
case i::wasm::HeapType::kEq: case i::wasm::HeapType::kEq:
default: default:
// TODO(7748): Implement these. // TODO(7748): Implement these.
...@@ -2517,10 +2522,11 @@ void WebAssemblyGlobalSetValue( ...@@ -2517,10 +2522,11 @@ void WebAssemblyGlobalSetValue(
} }
break; break;
} }
case internal::wasm::HeapType::kBottom: case i::wasm::HeapType::kBottom:
UNREACHABLE(); UNREACHABLE();
case internal::wasm::HeapType::kI31: case i::wasm::HeapType::kI31:
case internal::wasm::HeapType::kData: case i::wasm::HeapType::kData:
case i::wasm::HeapType::kArray:
case i::wasm::HeapType::kEq: case i::wasm::HeapType::kEq:
default: default:
// TODO(7748): Implement these. // TODO(7748): Implement these.
......
...@@ -408,6 +408,7 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table, ...@@ -408,6 +408,7 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
return; return;
case wasm::HeapType::kEq: case wasm::HeapType::kEq:
case wasm::HeapType::kData: case wasm::HeapType::kData:
case wasm::HeapType::kArray:
case wasm::HeapType::kI31: case wasm::HeapType::kI31:
// TODO(7748): Implement once we have struct/arrays/i31ref tables. // TODO(7748): Implement once we have struct/arrays/i31ref tables.
UNREACHABLE(); UNREACHABLE();
...@@ -449,6 +450,7 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate, ...@@ -449,6 +450,7 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate,
case wasm::HeapType::kEq: case wasm::HeapType::kEq:
case wasm::HeapType::kI31: case wasm::HeapType::kI31:
case wasm::HeapType::kData: case wasm::HeapType::kData:
case wasm::HeapType::kArray:
case wasm::HeapType::kAny: case wasm::HeapType::kAny:
// TODO(7748): Implement once we have a story for struct/arrays/i31ref in // TODO(7748): Implement once we have a story for struct/arrays/i31ref in
// JS. // JS.
...@@ -2209,8 +2211,9 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module, ...@@ -2209,8 +2211,9 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
case kOptRef: case kOptRef:
if (value->IsNull(isolate)) return true; if (value->IsNull(isolate)) return true;
V8_FALLTHROUGH; V8_FALLTHROUGH;
case kRef: case kRef: {
switch (expected.heap_representation()) { HeapType::Representation repr = expected.heap_representation();
switch (repr) {
case HeapType::kFunc: { case HeapType::kFunc: {
if (!(WasmExternalFunction::IsWasmExternalFunction(*value) || if (!(WasmExternalFunction::IsWasmExternalFunction(*value) ||
WasmCapiFunction::IsWasmCapiFunction(*value))) { WasmCapiFunction::IsWasmCapiFunction(*value))) {
...@@ -2225,6 +2228,7 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module, ...@@ -2225,6 +2228,7 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
case HeapType::kAny: case HeapType::kAny:
return true; return true;
case HeapType::kData: case HeapType::kData:
case HeapType::kArray:
case HeapType::kEq: case HeapType::kEq:
case HeapType::kI31: { case HeapType::kI31: {
// TODO(7748): Change this when we have a decision on the JS API for // TODO(7748): Change this when we have a decision on the JS API for
...@@ -2242,22 +2246,21 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module, ...@@ -2242,22 +2246,21 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
value = it.GetDataValue(); value = it.GetDataValue();
} }
if (expected.is_reference_to(HeapType::kEq)) return true; if (repr == HeapType::kI31) {
if (expected.is_reference_to(HeapType::kData)) {
if (value->IsSmi()) {
*error_message = "dataref-typed object must be a heap object";
return false;
}
return true;
} else {
DCHECK(expected.is_reference_to(HeapType::kI31));
if (!value->IsSmi()) { if (!value->IsSmi()) {
*error_message = "i31ref-typed object cannot be a heap object"; *error_message = "i31ref-typed object cannot be a heap object";
return false; return false;
} }
return true; return true;
} }
if (!((repr == HeapType::kEq && value->IsSmi()) ||
(repr != HeapType::kArray && value->IsWasmStruct()) ||
value->IsWasmArray())) {
*error_message = "object incompatible with wasm type";
return false;
}
return true;
} }
default: default:
if (module == nullptr) { if (module == nullptr) {
...@@ -2327,6 +2330,7 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module, ...@@ -2327,6 +2330,7 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
"Javascript is not supported yet."; "Javascript is not supported yet.";
return false; return false;
} }
}
case kRtt: case kRtt:
case kRttWithDepth: case kRttWithDepth:
// TODO(7748): Implement when the JS API for rtts is decided on. // TODO(7748): Implement when the JS API for rtts is decided on.
......
...@@ -387,6 +387,9 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl( ...@@ -387,6 +387,9 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
case HeapType::kData: case HeapType::kData:
return super_heap == sub_heap || super_heap == HeapType::kEq || return super_heap == sub_heap || super_heap == HeapType::kEq ||
super_heap == HeapType::kAny; super_heap == HeapType::kAny;
case HeapType::kArray:
return super_heap == HeapType::kArray || super_heap == HeapType::kData ||
super_heap == HeapType::kEq || super_heap == HeapType::kAny;
case HeapType::kBottom: case HeapType::kBottom:
UNREACHABLE(); UNREACHABLE();
default: default:
...@@ -403,6 +406,8 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl( ...@@ -403,6 +406,8 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
case HeapType::kEq: case HeapType::kEq:
case HeapType::kData: case HeapType::kData:
return !sub_module->has_signature(sub_index); return !sub_module->has_signature(sub_index);
case HeapType::kArray:
return sub_module->has_array(sub_index);
case HeapType::kExtern: case HeapType::kExtern:
case HeapType::kI31: case HeapType::kI31:
return false; return false;
......
...@@ -26,16 +26,15 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl( ...@@ -26,16 +26,15 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
// Type equivalence (~) is described by the following rules (structural // Type equivalence (~) is described by the following rules (structural
// equivalence): // equivalence):
// - Two numeric types are equivalent iff they are equal. // - Two numeric types are equivalent iff they are equal.
// - optref(ht1) ~ optref(ht2) iff ht1 ~ ht2. // - T(ht1) ~ T(ht2) iff ht1 ~ ht2 for T in {ref, optref, rtt}.
// - ref(ht1) ~ ref(ht2) iff ht1 ~ ht2.
// - rtt(d1, ht1) ~ rtt(d2, ht2) iff (d1 = d2 and ht1 ~ ht2). // - rtt(d1, ht1) ~ rtt(d2, ht2) iff (d1 = d2 and ht1 ~ ht2).
// For heap types, the following rules hold: // Equivalence of heap types ht1 ~ ht2 is defined as follows:
// - Two generic heap types are equivalent iff they are equal. // - Two generic heap types are equivalent iff they are equal.
// - Two structs are equivalent iff they contain the same number of fields and // - Two structs are equivalent iff they contain the same number of fields and
// these are pairwise equivalent. // these are pairwise equivalent.
// - Two functions are equivalent iff they contain the same number of parameters // - Two functions are equivalent iff they contain the same number of parameters
// and returns and these are pairwise equivalent. // and returns and these are pairwise equivalent.
// - Two arrays are equivalent iff their underlying types are equivalent. // - Two arrays are equivalent iff their element types are equivalent.
V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2, V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
const WasmModule* module1, const WasmModule* module1,
const WasmModule* module2); const WasmModule* module2);
...@@ -47,7 +46,8 @@ V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2, ...@@ -47,7 +46,8 @@ V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
// - numeric types are subtype-related iff they are equal. // - numeric types are subtype-related iff they are equal.
// - optref(ht1) <: optref(ht2) iff ht1 <: ht2. // - optref(ht1) <: optref(ht2) iff ht1 <: ht2.
// - ref(ht1) <: ref/optref(ht2) iff ht1 <: ht2. // - ref(ht1) <: ref/optref(ht2) iff ht1 <: ht2.
// - rtt1 <: rtt2 iff rtt1 ~ rtt2. // - rtt1(d, ht1) <: rtt2(ht2) iff ht1 ~ ht2.
// - rtt1 <: rtt2 iff rtt1 ~ rtt2, otherwise
// For heap types, the following subtyping rules hold: // For heap types, the following subtyping rules hold:
// - The abstract heap types form the following type hierarchy: // - The abstract heap types form the following type hierarchy:
// any // any
...@@ -55,11 +55,15 @@ V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2, ...@@ -55,11 +55,15 @@ V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
// eq func extern // eq func extern
// / \ // / \
// i31 data // i31 data
// - All structs and arrays are subtypes of data. // |
// array
// - All functions are subtypes of func. // - All functions are subtypes of func.
// - All structs are subtypes of data.
// - All arrays are subtypes of array.
// - Struct subtyping: Subtype must have at least as many fields as supertype, // - Struct subtyping: Subtype must have at least as many fields as supertype,
// covariance for immutable fields, equivalence for mutable fields. // covariance for immutable fields, equivalence for mutable fields.
// - Array subtyping (mutable only) is the equivalence relation. // - Array subtyping: subtyping of respective element types for immutable
// arrays, equivalence of element types for mutable arrays.
// - Function subtyping depends on the enabled wasm features: if // - Function subtyping depends on the enabled wasm features: if
// --experimental-wasm-gc is enabled, then subtyping is computed // --experimental-wasm-gc is enabled, then subtyping is computed
// contravariantly for parameter types and covariantly for return types. // contravariantly for parameter types and covariantly for return types.
......
...@@ -935,12 +935,12 @@ WASM_COMPILED_EXEC_TEST(WasmBasicArray) { ...@@ -935,12 +935,12 @@ WASM_COMPILED_EXEC_TEST(WasmBasicArray) {
kExprEnd}); kExprEnd});
// Reads and returns an array's length. // Reads and returns an array's length.
const byte kGetLength = tester.DefineFunction( const byte kGetLength =
tester.sigs.i_v(), {}, tester.DefineFunction(tester.sigs.i_v(), {},
{WASM_ARRAY_LEN(type_index, WASM_ARRAY_NEW_WITH_RTT( {WASM_ARRAY_LEN(WASM_ARRAY_NEW_WITH_RTT(
type_index, WASM_I32V(0), WASM_I32V(42), type_index, WASM_I32V(0), WASM_I32V(42),
WASM_RTT_CANON(type_index))), WASM_RTT_CANON(type_index))),
kExprEnd}); kExprEnd});
// Create an array of length 2, initialized to [42, 42]. // Create an array of length 2, initialized to [42, 42].
const byte kAllocate = tester.DefineFunction( const byte kAllocate = tester.DefineFunction(
......
...@@ -3170,6 +3170,7 @@ class WasmInterpreterInternals { ...@@ -3170,6 +3170,7 @@ class WasmInterpreterInternals {
case HeapType::kFunc: case HeapType::kFunc:
case HeapType::kEq: case HeapType::kEq:
case HeapType::kData: case HeapType::kData:
case HeapType::kArray:
case HeapType::kI31: case HeapType::kI31:
case HeapType::kAny: { case HeapType::kAny: {
Handle<Object> ref = value.to_ref(); Handle<Object> ref = value.to_ref();
...@@ -3275,6 +3276,7 @@ class WasmInterpreterInternals { ...@@ -3275,6 +3276,7 @@ class WasmInterpreterInternals {
case HeapType::kFunc: case HeapType::kFunc:
case HeapType::kEq: case HeapType::kEq:
case HeapType::kData: case HeapType::kData:
case HeapType::kArray:
case HeapType::kI31: case HeapType::kI31:
case HeapType::kAny: { case HeapType::kAny: {
Handle<Object> ref(encoded_values->get(encoded_index++), Handle<Object> ref(encoded_values->get(encoded_index++),
......
...@@ -564,8 +564,8 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -564,8 +564,8 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
array, index, WASM_GC_OP(kExprArrayGetS), static_cast<byte>(typeidx) array, index, WASM_GC_OP(kExprArrayGetS), static_cast<byte>(typeidx)
#define WASM_ARRAY_SET(typeidx, array, index, value) \ #define WASM_ARRAY_SET(typeidx, array, index, value) \
array, index, value, WASM_GC_OP(kExprArraySet), static_cast<byte>(typeidx) array, index, value, WASM_GC_OP(kExprArraySet), static_cast<byte>(typeidx)
#define WASM_ARRAY_LEN(typeidx, array) \ #define WASM_ARRAY_LEN(array) \
array, WASM_GC_OP(kExprArrayLen), static_cast<byte>(typeidx) array, WASM_GC_OP(kExprArrayLen), /* dummy index */ 0
#define WASM_ARRAY_COPY(dst_idx, src_idx, dst_array, dst_index, src_array, \ #define WASM_ARRAY_COPY(dst_idx, src_idx, dst_array, dst_index, src_array, \
src_index, length) \ src_index, length) \
dst_array, dst_index, src_array, src_index, length, \ dst_array, dst_index, src_array, src_index, length, \
......
...@@ -2138,6 +2138,20 @@ void WasmGenerator::GenerateRef(HeapType type, DataRange* data, ...@@ -2138,6 +2138,20 @@ void WasmGenerator::GenerateRef(HeapType type, DataRange* data,
} }
return; return;
} }
case HeapType::kArray: {
DCHECK(liftoff_as_reference_);
constexpr uint8_t fallback_to_dataref = 1;
uint8_t random =
data->get<uint8_t>() % (num_arrays_ + fallback_to_dataref);
// Try generating one of the alternatives and continue to the rest of the
// methods in case it fails.
if (random >= num_arrays_) {
if (GenerateOneOf(alternatives_other, type, data, nullability)) return;
random = data->get<uint8_t>() % num_arrays_;
}
GenerateRef(HeapType(random), data, nullability);
return;
}
case HeapType::kData: { case HeapType::kData: {
DCHECK(liftoff_as_reference_); DCHECK(liftoff_as_reference_);
constexpr uint8_t fallback_to_dataref = 2; constexpr uint8_t fallback_to_dataref = 2;
......
...@@ -231,6 +231,8 @@ std::string ValueTypeToConstantName(ValueType type) { ...@@ -231,6 +231,8 @@ std::string ValueTypeToConstantName(ValueType type) {
return "kWasmAnyRef"; return "kWasmAnyRef";
case HeapType::kData: case HeapType::kData:
return "wasmOptRefType(kWasmDataRef)"; return "wasmOptRefType(kWasmDataRef)";
case HeapType::kArray:
return "wasmOptRefType(kWasmArrayRef)";
case HeapType::kI31: case HeapType::kI31:
return "wasmOptRefType(kWasmI31Ref)"; return "wasmOptRefType(kWasmI31Ref)";
case HeapType::kBottom: case HeapType::kBottom:
...@@ -249,6 +251,8 @@ std::string ValueTypeToConstantName(ValueType type) { ...@@ -249,6 +251,8 @@ std::string ValueTypeToConstantName(ValueType type) {
return "wasmRefType(kWasmAnyRef)"; return "wasmRefType(kWasmAnyRef)";
case HeapType::kData: case HeapType::kData:
return "wasmRefType(kWasmDataRef)"; return "wasmRefType(kWasmDataRef)";
case HeapType::kArray:
return "wasmRefType(kWasmArrayRef)";
case HeapType::kI31: case HeapType::kI31:
return "wasmRefType(kWasmI31Ref)"; return "wasmRefType(kWasmI31Ref)";
case HeapType::kBottom: case HeapType::kBottom:
...@@ -272,6 +276,8 @@ std::string HeapTypeToConstantName(HeapType heap_type) { ...@@ -272,6 +276,8 @@ std::string HeapTypeToConstantName(HeapType heap_type) {
return "kWasmI31Ref"; return "kWasmI31Ref";
case HeapType::kData: case HeapType::kData:
return "kWasmDataRef"; return "kWasmDataRef";
case HeapType::kArray:
return "kWasmArrayRef";
case HeapType::kAny: case HeapType::kAny:
return "kWasmAnyRef"; return "kWasmAnyRef";
case HeapType::kBottom: case HeapType::kBottom:
......
...@@ -121,6 +121,7 @@ let kWasmAnyRef = -0x12; ...@@ -121,6 +121,7 @@ let kWasmAnyRef = -0x12;
let kWasmEqRef = -0x13; let kWasmEqRef = -0x13;
let kWasmI31Ref = -0x16; let kWasmI31Ref = -0x16;
let kWasmDataRef = -0x19; let kWasmDataRef = -0x19;
let kWasmArrayRef = -0x20;
// Use the positive-byte versions inside function bodies. // Use the positive-byte versions inside function bodies.
let kLeb128Mask = 0x7f; let kLeb128Mask = 0x7f;
...@@ -129,8 +130,9 @@ let kAnyFuncCode = kFuncRefCode; // Alias named as in the JS API spec ...@@ -129,8 +130,9 @@ let kAnyFuncCode = kFuncRefCode; // Alias named as in the JS API spec
let kExternRefCode = kWasmExternRef & kLeb128Mask; let kExternRefCode = kWasmExternRef & kLeb128Mask;
let kAnyRefCode = kWasmAnyRef & kLeb128Mask; let kAnyRefCode = kWasmAnyRef & kLeb128Mask;
let kEqRefCode = kWasmEqRef & kLeb128Mask; let kEqRefCode = kWasmEqRef & kLeb128Mask;
let kI31RefCode = kWasmI31Ref & kLeb128Mask; let kI31RefCode = kWasmI31Ref & kLeb128Mask;
let kDataRefCode = kWasmDataRef & kLeb128Mask; let kDataRefCode = kWasmDataRef & kLeb128Mask;
let kArrayRefCode = kWasmArrayRef & kLeb128Mask;
let kWasmOptRef = 0x6c; let kWasmOptRef = 0x6c;
let kWasmRef = 0x6b; let kWasmRef = 0x6b;
......
...@@ -4071,6 +4071,9 @@ TEST_F(FunctionBodyDecoderTest, GCArray) { ...@@ -4071,6 +4071,9 @@ TEST_F(FunctionBodyDecoderTest, GCArray) {
ValueType reps_c_r[] = {kWasmFuncRef, array_type}; ValueType reps_c_r[] = {kWasmFuncRef, array_type};
ValueType reps_f_r[] = {kWasmF32, array_type}; ValueType reps_f_r[] = {kWasmF32, array_type};
ValueType reps_i_r[] = {kWasmI32, array_type}; ValueType reps_i_r[] = {kWasmI32, array_type};
ValueType reps_i_a[] = {kWasmI32, kWasmArrayRef};
ValueType reps_i_s[] = {kWasmI32,
ValueType::Ref(struct_type_index, kNonNullable)};
const FunctionSig sig_c_r(1, 1, reps_c_r); const FunctionSig sig_c_r(1, 1, reps_c_r);
const FunctionSig sig_v_r(0, 1, &array_type); const FunctionSig sig_v_r(0, 1, &array_type);
const FunctionSig sig_v_r2(0, 1, &immutable_array_type); const FunctionSig sig_v_r2(0, 1, &immutable_array_type);
...@@ -4078,6 +4081,8 @@ TEST_F(FunctionBodyDecoderTest, GCArray) { ...@@ -4078,6 +4081,8 @@ TEST_F(FunctionBodyDecoderTest, GCArray) {
const FunctionSig sig_f_r(1, 1, reps_f_r); const FunctionSig sig_f_r(1, 1, reps_f_r);
const FunctionSig sig_v_cr(0, 2, reps_c_r); const FunctionSig sig_v_cr(0, 2, reps_c_r);
const FunctionSig sig_i_r(1, 1, reps_i_r); const FunctionSig sig_i_r(1, 1, reps_i_r);
const FunctionSig sig_i_a(1, 1, reps_i_a);
const FunctionSig sig_i_s(1, 1, reps_i_s);
/** array.new_with_rtt **/ /** array.new_with_rtt **/
ExpectValidates(&sig_r_v, ExpectValidates(&sig_r_v,
...@@ -4186,16 +4191,16 @@ TEST_F(FunctionBodyDecoderTest, GCArray) { ...@@ -4186,16 +4191,16 @@ TEST_F(FunctionBodyDecoderTest, GCArray) {
"array.set[2] expected type funcref, found i64.const of type i64"); "array.set[2] expected type funcref, found i64.const of type i64");
/** array.len **/ /** array.len **/
ExpectValidates(&sig_i_r, // Works both with conrete array types and arrayref.
{WASM_ARRAY_LEN(array_type_index, WASM_LOCAL_GET(0))}); ExpectValidates(&sig_i_r, {WASM_ARRAY_LEN(WASM_LOCAL_GET(0))});
ExpectValidates(&sig_i_a, {WASM_ARRAY_LEN(WASM_LOCAL_GET(0))});
// Wrong return type. // Wrong return type.
ExpectFailure(&sig_f_r, {WASM_ARRAY_LEN(array_type_index, WASM_LOCAL_GET(0))}, ExpectFailure(&sig_f_r, {WASM_ARRAY_LEN(WASM_LOCAL_GET(0))}, kAppendEnd,
kAppendEnd,
"type error in fallthru[0] (expected f32, got i32)"); "type error in fallthru[0] (expected f32, got i32)");
// Non-array type index. // Non-array argument.
ExpectFailure(&sig_i_r, ExpectFailure(&sig_i_s, {WASM_ARRAY_LEN(WASM_LOCAL_GET(0))}, kAppendEnd,
{WASM_ARRAY_LEN(struct_type_index, WASM_LOCAL_GET(0))}, "array.len[0] expected type (ref null array), found local.get "
kAppendEnd, "invalid array index: 1"); "of type (ref 1)");
// Immutable array. // Immutable array.
// Allocating and reading is OK: // Allocating and reading is OK:
......
...@@ -14,8 +14,12 @@ namespace subtyping_unittest { ...@@ -14,8 +14,12 @@ namespace subtyping_unittest {
class WasmSubtypingTest : public ::testing::Test {}; class WasmSubtypingTest : public ::testing::Test {};
using FieldInit = std::pair<ValueType, bool>; using FieldInit = std::pair<ValueType, bool>;
ValueType ref(uint32_t index) { return ValueType::Ref(index, kNonNullable); } constexpr ValueType ref(uint32_t index) {
ValueType optRef(uint32_t index) { return ValueType::Ref(index, kNullable); } return ValueType::Ref(index, kNonNullable);
}
constexpr ValueType optRef(uint32_t index) {
return ValueType::Ref(index, kNullable);
}
FieldInit mut(ValueType type) { return FieldInit(type, true); } FieldInit mut(ValueType type) { return FieldInit(type, true); }
FieldInit immut(ValueType type) { return FieldInit(type, false); } FieldInit immut(ValueType type) { return FieldInit(type, false); }
...@@ -73,12 +77,12 @@ TEST_F(WasmSubtypingTest, Subtyping) { ...@@ -73,12 +77,12 @@ TEST_F(WasmSubtypingTest, Subtyping) {
/* 16 */ DefineSignature(module, {ref(0)}, {ref(0)}); /* 16 */ DefineSignature(module, {ref(0)}, {ref(0)});
} }
ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64, constexpr ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
kWasmS128}; kWasmS128};
ValueType ref_types[] = {kWasmExternRef, kWasmFuncRef, kWasmEqRef, constexpr ValueType ref_types[] = {
kWasmI31Ref, kWasmDataRef, kWasmAnyRef, kWasmExternRef, kWasmFuncRef, kWasmEqRef, kWasmI31Ref, kWasmDataRef,
optRef(0), ref(0), optRef(2), kWasmArrayRef, kWasmAnyRef, optRef(0), ref(0), optRef(2),
ref(2), optRef(11), ref(11)}; ref(2), optRef(11), ref(11)};
#define SUBTYPE(type1, type2) \ #define SUBTYPE(type1, type2) \
EXPECT_TRUE(IsSubtypeOf(type1, type2, module1, module)) EXPECT_TRUE(IsSubtypeOf(type1, type2, module1, module))
...@@ -112,9 +116,12 @@ TEST_F(WasmSubtypingTest, Subtyping) { ...@@ -112,9 +116,12 @@ TEST_F(WasmSubtypingTest, Subtyping) {
ref_type != kWasmAnyRef && ref_type != optRef(11) && ref_type != kWasmAnyRef && ref_type != optRef(11) &&
ref_type != ref(11)); ref_type != ref(11));
// Non-nullable struct/array types are subtypes of dataref. // Non-nullable struct/array types are subtypes of dataref.
SUBTYPE_IFF( SUBTYPE_IFF(ref_type, kWasmDataRef,
ref_type, kWasmDataRef, ref_type == kWasmDataRef || ref_type == kWasmArrayRef ||
ref_type == kWasmDataRef || ref_type == ref(0) || ref_type == ref(2)); ref_type == ref(0) || ref_type == ref(2));
// Non-nullable array types are subtypes of arrayref.
SUBTYPE_IFF(ref_type, kWasmArrayRef,
ref_type == kWasmArrayRef || ref_type == ref(2));
// Functions are subtypes of funcref. // Functions are subtypes of funcref.
SUBTYPE_IFF(ref_type, kWasmFuncRef, SUBTYPE_IFF(ref_type, kWasmFuncRef,
ref_type == kWasmFuncRef || ref_type == optRef(11) || ref_type == kWasmFuncRef || ref_type == optRef(11) ||
...@@ -128,8 +135,10 @@ TEST_F(WasmSubtypingTest, Subtyping) { ...@@ -128,8 +135,10 @@ TEST_F(WasmSubtypingTest, Subtyping) {
} }
// The rest of ref. types are unrelated. // The rest of ref. types are unrelated.
for (ValueType type_1 : {kWasmExternRef, kWasmFuncRef, kWasmI31Ref}) { for (ValueType type_1 :
for (ValueType type_2 : {kWasmExternRef, kWasmFuncRef, kWasmI31Ref}) { {kWasmExternRef, kWasmFuncRef, kWasmI31Ref, kWasmArrayRef}) {
for (ValueType type_2 :
{kWasmExternRef, kWasmFuncRef, kWasmI31Ref, kWasmArrayRef}) {
SUBTYPE_IFF(type_1, type_2, type_1 == type_2); SUBTYPE_IFF(type_1, type_2, type_1 == type_2);
} }
} }
......
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