[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/+/3368105
Reviewed-by: Simon Zünd <szuend@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78565}
This commit is contained in:
Manos Koukoutos 2022-01-11 12:55:55 +00:00 committed by V8 LUCI CQ
parent eb129a5cf1
commit a9668e25e6
17 changed files with 156 additions and 69 deletions

View File

@ -6453,8 +6453,9 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
WasmInternalFunction::kExternalOffset));
}
}
case wasm::HeapType::kData:
case wasm::HeapType::kEq:
case wasm::HeapType::kData:
case wasm::HeapType::kArray:
case wasm::HeapType::kI31:
// TODO(7748): Update this when JS interop is settled.
if (type.kind() == wasm::kOptRef) {
@ -6644,6 +6645,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
BuildCheckValidRefValue(input, js_context, type);
return BuildUnpackObjectWrapper(input);
case wasm::HeapType::kData:
case wasm::HeapType::kArray:
case wasm::HeapType::kEq:
case wasm::HeapType::kI31:
// TODO(7748): Update this when JS interop has settled.

View File

@ -796,6 +796,9 @@ Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type,
case wasm::HeapType::kData:
generic_name = "data";
break;
case wasm::HeapType::kArray:
generic_name = "array";
break;
case wasm::HeapType::kAny:
generic_name = "any";
break;

View File

@ -203,6 +203,7 @@ HeapType read_heap_type(Decoder* decoder, const byte* pc,
case kEqRefCode:
case kI31RefCode:
case kDataRefCode:
case kArrayRefCode:
case kAnyRefCode:
if (!VALIDATE(enabled.has_gc())) {
DecodeError<validate>(
@ -269,6 +270,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
case kEqRefCode:
case kI31RefCode:
case kDataRefCode:
case kArrayRefCode:
case kAnyRefCode:
if (!VALIDATE(enabled.has_gc())) {
DecodeError<validate>(
@ -281,9 +283,10 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
case kExternRefCode:
case kFuncRefCode: {
HeapType heap_type = HeapType::from_code(code);
Nullability nullability = code == kI31RefCode || code == kDataRefCode
? kNonNullable
: kNullable;
Nullability nullability =
code == kI31RefCode || code == kDataRefCode || code == kArrayRefCode
? kNonNullable
: kNullable;
return ValueType::Ref(heap_type, nullability);
}
case kI32Code:
@ -4312,9 +4315,11 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
}
case kExprArrayLen: {
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);
if (!this->Validate(this->pc_ + opcode_length, imm)) return 0;
Value array_obj = Peek(0, 0, ValueType::Ref(imm.index, kNullable));
Value array_obj =
Peek(0, 0, ValueType::Ref(HeapType::kArray, kNullable));
Value value = CreateValue(kWasmI32);
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayLen, array_obj, &value);
Drop(array_obj);

View File

@ -66,6 +66,7 @@ class HeapType {
kEq, // shorthand: q
kI31, // shorthand: j
kData, // shorthand: o
kArray, // shorthand: g
kAny, // shorthand: a
// This value is used to represent failures in the parsing of heap types and
// does not correspond to a wasm heap type.
@ -90,6 +91,8 @@ class HeapType {
return HeapType(kAny);
case ValueTypeCode::kDataRefCode:
return HeapType(kData);
case ValueTypeCode::kArrayRefCode:
return HeapType(kArray);
default:
return HeapType(kBottom);
}
@ -142,6 +145,8 @@ class HeapType {
return std::string("i31");
case kData:
return std::string("data");
case kArray:
return std::string("array");
case kAny:
return std::string("any");
default:
@ -165,6 +170,8 @@ class HeapType {
return mask | kI31RefCode;
case kData:
return mask | kDataRefCode;
case kArray:
return mask | kArrayRefCode;
case kAny:
return mask | kAnyRefCode;
default:
@ -470,6 +477,8 @@ class ValueType {
return kI31RefCode;
case HeapType::kData:
return kDataRefCode;
case HeapType::kArray:
return kArrayRefCode;
default:
return kRefCode;
}
@ -493,11 +502,19 @@ class ValueType {
// Returns true iff the heap type is needed to encode this type in the wasm
// binary format, taking into account available type shorthands.
constexpr bool encoding_needs_heap_type() const {
return (kind() == kRef && heap_representation() != HeapType::kI31 &&
heap_representation() != HeapType::kData) ||
(kind() == kOptRef && (heap_type().is_index() ||
heap_representation() == HeapType::kI31 ||
heap_representation() == HeapType::kData));
switch (kind()) {
case kRef:
return heap_representation() != HeapType::kI31 &&
heap_representation() != HeapType::kArray &&
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;
@ -588,6 +605,8 @@ constexpr ValueType kWasmEqRef = ValueType::Ref(HeapType::kEq, kNullable);
constexpr ValueType kWasmI31Ref = ValueType::Ref(HeapType::kI31, kNonNullable);
constexpr ValueType kWasmDataRef =
ValueType::Ref(HeapType::kData, kNonNullable);
constexpr ValueType kWasmArrayRef =
ValueType::Ref(HeapType::kArray, kNonNullable);
constexpr ValueType kWasmAnyRef = ValueType::Ref(HeapType::kAny, kNullable);
// This is used in wasm.tq.

View File

@ -47,6 +47,7 @@ enum ValueTypeCode : uint8_t {
kRttWithDepthCode = 0x69,
kRttCode = 0x68,
kDataRefCode = 0x67,
kArrayRefCode = 0x66
};
// Binary encoding of other types.
constexpr uint8_t kWasmFunctionTypeCode = 0x60;

View File

@ -1528,6 +1528,7 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
case i::wasm::HeapType::kEq:
case internal::wasm::HeapType::kI31:
case internal::wasm::HeapType::kData:
case internal::wasm::HeapType::kArray:
default:
// TODO(7748): Implement these.
UNIMPLEMENTED();
@ -1719,6 +1720,7 @@ void EncodeExceptionValues(v8::Isolate* isolate,
case i::wasm::HeapType::kEq:
case i::wasm::HeapType::kI31:
case i::wasm::HeapType::kData:
case i::wasm::HeapType::kArray:
values_out->set(index++, *Utils::OpenHandle(*value));
break;
case internal::wasm::HeapType::kBottom:
@ -2272,6 +2274,7 @@ void WebAssemblyExceptionGetArg(
case i::wasm::HeapType::kEq:
case i::wasm::HeapType::kI31:
case i::wasm::HeapType::kData:
case i::wasm::HeapType::kArray:
decode_index++;
break;
case i::wasm::HeapType::kBottom:
@ -2330,6 +2333,7 @@ void WebAssemblyExceptionGetArg(
case i::wasm::HeapType::kAny:
case i::wasm::HeapType::kEq:
case i::wasm::HeapType::kI31:
case i::wasm::HeapType::kArray:
case i::wasm::HeapType::kData: {
auto obj = values->get(decode_index);
result = Utils::ToLocal(i::Handle<i::Object>(obj, i_isolate));
@ -2410,10 +2414,10 @@ void WebAssemblyGlobalGetValueCommon(
case i::wasm::kOptRef:
switch (receiver->type().heap_representation()) {
case i::wasm::HeapType::kExtern:
case i::wasm::HeapType::kAny:
return_value.Set(Utils::ToLocal(receiver->GetRef()));
break;
case i::wasm::HeapType::kFunc:
case i::wasm::HeapType::kAny: {
case i::wasm::HeapType::kFunc: {
i::Handle<i::Object> result = receiver->GetRef();
if (result->IsWasmInternalFunction()) {
result = handle(
@ -2423,10 +2427,11 @@ void WebAssemblyGlobalGetValueCommon(
return_value.Set(Utils::ToLocal(result));
break;
}
case internal::wasm::HeapType::kBottom:
case i::wasm::HeapType::kBottom:
UNREACHABLE();
case internal::wasm::HeapType::kI31:
case internal::wasm::HeapType::kData:
case i::wasm::HeapType::kI31:
case i::wasm::HeapType::kData:
case i::wasm::HeapType::kArray:
case i::wasm::HeapType::kEq:
default:
// TODO(7748): Implement these.
@ -2517,10 +2522,11 @@ void WebAssemblyGlobalSetValue(
}
break;
}
case internal::wasm::HeapType::kBottom:
case i::wasm::HeapType::kBottom:
UNREACHABLE();
case internal::wasm::HeapType::kI31:
case internal::wasm::HeapType::kData:
case i::wasm::HeapType::kI31:
case i::wasm::HeapType::kData:
case i::wasm::HeapType::kArray:
case i::wasm::HeapType::kEq:
default:
// TODO(7748): Implement these.

View File

@ -408,6 +408,7 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
return;
case wasm::HeapType::kEq:
case wasm::HeapType::kData:
case wasm::HeapType::kArray:
case wasm::HeapType::kI31:
// TODO(7748): Implement once we have struct/arrays/i31ref tables.
UNREACHABLE();
@ -449,6 +450,7 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate,
case wasm::HeapType::kEq:
case wasm::HeapType::kI31:
case wasm::HeapType::kData:
case wasm::HeapType::kArray:
case wasm::HeapType::kAny:
// TODO(7748): Implement once we have a story for struct/arrays/i31ref in
// JS.
@ -2209,8 +2211,9 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
case kOptRef:
if (value->IsNull(isolate)) return true;
V8_FALLTHROUGH;
case kRef:
switch (expected.heap_representation()) {
case kRef: {
HeapType::Representation repr = expected.heap_representation();
switch (repr) {
case HeapType::kFunc: {
if (!(WasmExternalFunction::IsWasmExternalFunction(*value) ||
WasmCapiFunction::IsWasmCapiFunction(*value))) {
@ -2225,6 +2228,7 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
case HeapType::kAny:
return true;
case HeapType::kData:
case HeapType::kArray:
case HeapType::kEq:
case HeapType::kI31: {
// 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,
value = it.GetDataValue();
}
if (expected.is_reference_to(HeapType::kEq)) return true;
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 (repr == HeapType::kI31) {
if (!value->IsSmi()) {
*error_message = "i31ref-typed object cannot be a heap object";
return false;
}
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:
if (module == nullptr) {
@ -2327,6 +2330,7 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
"Javascript is not supported yet.";
return false;
}
}
case kRtt:
case kRttWithDepth:
// TODO(7748): Implement when the JS API for rtts is decided on.

View File

@ -387,6 +387,9 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
case HeapType::kData:
return super_heap == sub_heap || super_heap == HeapType::kEq ||
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:
UNREACHABLE();
default:
@ -403,6 +406,8 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
case HeapType::kEq:
case HeapType::kData:
return !sub_module->has_signature(sub_index);
case HeapType::kArray:
return sub_module->has_array(sub_index);
case HeapType::kExtern:
case HeapType::kI31:
return false;

View File

@ -26,16 +26,15 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
// Type equivalence (~) is described by the following rules (structural
// equivalence):
// - Two numeric types are equivalent iff they are equal.
// - optref(ht1) ~ optref(ht2) iff ht1 ~ ht2.
// - ref(ht1) ~ ref(ht2) iff ht1 ~ ht2.
// - T(ht1) ~ T(ht2) iff ht1 ~ ht2 for T in {ref, optref, rtt}.
// - 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 structs are equivalent iff they contain the same number of fields and
// these are pairwise equivalent.
// - Two functions are equivalent iff they contain the same number of parameters
// 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,
const WasmModule* module1,
const WasmModule* module2);
@ -47,7 +46,8 @@ V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
// - numeric types are subtype-related iff they are equal.
// - optref(ht1) <: 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:
// - The abstract heap types form the following type hierarchy:
// any
@ -55,11 +55,15 @@ V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
// eq func extern
// / \
// i31 data
// - All structs and arrays are subtypes of data.
// |
// array
// - 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,
// 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
// --experimental-wasm-gc is enabled, then subtyping is computed
// contravariantly for parameter types and covariantly for return types.

View File

@ -935,12 +935,12 @@ WASM_COMPILED_EXEC_TEST(WasmBasicArray) {
kExprEnd});
// Reads and returns an array's length.
const byte kGetLength = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_ARRAY_LEN(type_index, WASM_ARRAY_NEW_WITH_RTT(
type_index, WASM_I32V(0), WASM_I32V(42),
WASM_RTT_CANON(type_index))),
kExprEnd});
const byte kGetLength =
tester.DefineFunction(tester.sigs.i_v(), {},
{WASM_ARRAY_LEN(WASM_ARRAY_NEW_WITH_RTT(
type_index, WASM_I32V(0), WASM_I32V(42),
WASM_RTT_CANON(type_index))),
kExprEnd});
// Create an array of length 2, initialized to [42, 42].
const byte kAllocate = tester.DefineFunction(

View File

@ -3170,6 +3170,7 @@ class WasmInterpreterInternals {
case HeapType::kFunc:
case HeapType::kEq:
case HeapType::kData:
case HeapType::kArray:
case HeapType::kI31:
case HeapType::kAny: {
Handle<Object> ref = value.to_ref();
@ -3275,6 +3276,7 @@ class WasmInterpreterInternals {
case HeapType::kFunc:
case HeapType::kEq:
case HeapType::kData:
case HeapType::kArray:
case HeapType::kI31:
case HeapType::kAny: {
Handle<Object> ref(encoded_values->get(encoded_index++),

View File

@ -564,8 +564,8 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
array, index, WASM_GC_OP(kExprArrayGetS), static_cast<byte>(typeidx)
#define WASM_ARRAY_SET(typeidx, array, index, value) \
array, index, value, WASM_GC_OP(kExprArraySet), static_cast<byte>(typeidx)
#define WASM_ARRAY_LEN(typeidx, array) \
array, WASM_GC_OP(kExprArrayLen), static_cast<byte>(typeidx)
#define WASM_ARRAY_LEN(array) \
array, WASM_GC_OP(kExprArrayLen), /* dummy index */ 0
#define WASM_ARRAY_COPY(dst_idx, src_idx, dst_array, dst_index, src_array, \
src_index, length) \
dst_array, dst_index, src_array, src_index, length, \

View File

@ -2138,6 +2138,20 @@ void WasmGenerator::GenerateRef(HeapType type, DataRange* data,
}
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: {
DCHECK(liftoff_as_reference_);
constexpr uint8_t fallback_to_dataref = 2;

View File

@ -231,6 +231,8 @@ std::string ValueTypeToConstantName(ValueType type) {
return "kWasmAnyRef";
case HeapType::kData:
return "wasmOptRefType(kWasmDataRef)";
case HeapType::kArray:
return "wasmOptRefType(kWasmArrayRef)";
case HeapType::kI31:
return "wasmOptRefType(kWasmI31Ref)";
case HeapType::kBottom:
@ -249,6 +251,8 @@ std::string ValueTypeToConstantName(ValueType type) {
return "wasmRefType(kWasmAnyRef)";
case HeapType::kData:
return "wasmRefType(kWasmDataRef)";
case HeapType::kArray:
return "wasmRefType(kWasmArrayRef)";
case HeapType::kI31:
return "wasmRefType(kWasmI31Ref)";
case HeapType::kBottom:
@ -272,6 +276,8 @@ std::string HeapTypeToConstantName(HeapType heap_type) {
return "kWasmI31Ref";
case HeapType::kData:
return "kWasmDataRef";
case HeapType::kArray:
return "kWasmArrayRef";
case HeapType::kAny:
return "kWasmAnyRef";
case HeapType::kBottom:

View File

@ -121,6 +121,7 @@ let kWasmAnyRef = -0x12;
let kWasmEqRef = -0x13;
let kWasmI31Ref = -0x16;
let kWasmDataRef = -0x19;
let kWasmArrayRef = -0x20;
// Use the positive-byte versions inside function bodies.
let kLeb128Mask = 0x7f;
@ -129,8 +130,9 @@ let kAnyFuncCode = kFuncRefCode; // Alias named as in the JS API spec
let kExternRefCode = kWasmExternRef & kLeb128Mask;
let kAnyRefCode = kWasmAnyRef & kLeb128Mask;
let kEqRefCode = kWasmEqRef & kLeb128Mask;
let kI31RefCode = kWasmI31Ref & kLeb128Mask;
let kDataRefCode = kWasmDataRef & kLeb128Mask;
let kI31RefCode = kWasmI31Ref & kLeb128Mask;
let kDataRefCode = kWasmDataRef & kLeb128Mask;
let kArrayRefCode = kWasmArrayRef & kLeb128Mask;
let kWasmOptRef = 0x6c;
let kWasmRef = 0x6b;

View File

@ -4071,6 +4071,9 @@ TEST_F(FunctionBodyDecoderTest, GCArray) {
ValueType reps_c_r[] = {kWasmFuncRef, array_type};
ValueType reps_f_r[] = {kWasmF32, 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_v_r(0, 1, &array_type);
const FunctionSig sig_v_r2(0, 1, &immutable_array_type);
@ -4078,6 +4081,8 @@ TEST_F(FunctionBodyDecoderTest, GCArray) {
const FunctionSig sig_f_r(1, 1, reps_f_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_a(1, 1, reps_i_a);
const FunctionSig sig_i_s(1, 1, reps_i_s);
/** array.new_with_rtt **/
ExpectValidates(&sig_r_v,
@ -4186,16 +4191,16 @@ TEST_F(FunctionBodyDecoderTest, GCArray) {
"array.set[2] expected type funcref, found i64.const of type i64");
/** array.len **/
ExpectValidates(&sig_i_r,
{WASM_ARRAY_LEN(array_type_index, WASM_LOCAL_GET(0))});
// Works both with conrete array types and arrayref.
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.
ExpectFailure(&sig_f_r, {WASM_ARRAY_LEN(array_type_index, WASM_LOCAL_GET(0))},
kAppendEnd,
ExpectFailure(&sig_f_r, {WASM_ARRAY_LEN(WASM_LOCAL_GET(0))}, kAppendEnd,
"type error in fallthru[0] (expected f32, got i32)");
// Non-array type index.
ExpectFailure(&sig_i_r,
{WASM_ARRAY_LEN(struct_type_index, WASM_LOCAL_GET(0))},
kAppendEnd, "invalid array index: 1");
// Non-array argument.
ExpectFailure(&sig_i_s, {WASM_ARRAY_LEN(WASM_LOCAL_GET(0))}, kAppendEnd,
"array.len[0] expected type (ref null array), found local.get "
"of type (ref 1)");
// Immutable array.
// Allocating and reading is OK:

View File

@ -14,8 +14,12 @@ namespace subtyping_unittest {
class WasmSubtypingTest : public ::testing::Test {};
using FieldInit = std::pair<ValueType, bool>;
ValueType ref(uint32_t index) { return ValueType::Ref(index, kNonNullable); }
ValueType optRef(uint32_t index) { return ValueType::Ref(index, kNullable); }
constexpr ValueType ref(uint32_t index) {
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 immut(ValueType type) { return FieldInit(type, false); }
@ -73,12 +77,12 @@ TEST_F(WasmSubtypingTest, Subtyping) {
/* 16 */ DefineSignature(module, {ref(0)}, {ref(0)});
}
ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
kWasmS128};
ValueType ref_types[] = {kWasmExternRef, kWasmFuncRef, kWasmEqRef,
kWasmI31Ref, kWasmDataRef, kWasmAnyRef,
optRef(0), ref(0), optRef(2),
ref(2), optRef(11), ref(11)};
constexpr ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
kWasmS128};
constexpr ValueType ref_types[] = {
kWasmExternRef, kWasmFuncRef, kWasmEqRef, kWasmI31Ref, kWasmDataRef,
kWasmArrayRef, kWasmAnyRef, optRef(0), ref(0), optRef(2),
ref(2), optRef(11), ref(11)};
#define SUBTYPE(type1, type2) \
EXPECT_TRUE(IsSubtypeOf(type1, type2, module1, module))
@ -112,9 +116,12 @@ TEST_F(WasmSubtypingTest, Subtyping) {
ref_type != kWasmAnyRef && ref_type != optRef(11) &&
ref_type != ref(11));
// Non-nullable struct/array types are subtypes of dataref.
SUBTYPE_IFF(
ref_type, kWasmDataRef,
ref_type == kWasmDataRef || ref_type == ref(0) || ref_type == ref(2));
SUBTYPE_IFF(ref_type, kWasmDataRef,
ref_type == kWasmDataRef || ref_type == kWasmArrayRef ||
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.
SUBTYPE_IFF(ref_type, kWasmFuncRef,
ref_type == kWasmFuncRef || ref_type == optRef(11) ||
@ -128,8 +135,10 @@ TEST_F(WasmSubtypingTest, Subtyping) {
}
// The rest of ref. types are unrelated.
for (ValueType type_1 : {kWasmExternRef, kWasmFuncRef, kWasmI31Ref}) {
for (ValueType type_2 : {kWasmExternRef, kWasmFuncRef, kWasmI31Ref}) {
for (ValueType type_1 :
{kWasmExternRef, kWasmFuncRef, kWasmI31Ref, kWasmArrayRef}) {
for (ValueType type_2 :
{kWasmExternRef, kWasmFuncRef, kWasmI31Ref, kWasmArrayRef}) {
SUBTYPE_IFF(type_1, type_2, type_1 == type_2);
}
}