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

[wasm-gc] All type shorthands should be nullable

arrayref, dataref and i31ref get changed to (ref null t).

Bug: v8:7748
Change-Id: Iae0e6969a1f71ccf1f193c267d761b7a1796f67b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3788093
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82048}
parent a2993308
......@@ -6034,7 +6034,9 @@ Node* WasmGraphBuilder::I31New(Node* input) {
return gasm_->WordShl(input, gasm_->IntPtrConstant(kI31To32BitSmiShift));
}
Node* WasmGraphBuilder::I31GetS(Node* input) {
Node* WasmGraphBuilder::I31GetS(Node* input, CheckForNull null_check,
wasm::WasmCodePosition position) {
if (null_check == kWithNullCheck) input = AssertNotNull(input, position);
if (SmiValuesAre31Bits()) {
input = gasm_->BuildTruncateIntPtrToInt32(input);
return gasm_->Word32SarShiftOutZeros(input,
......@@ -6045,7 +6047,9 @@ Node* WasmGraphBuilder::I31GetS(Node* input) {
gasm_->WordSar(input, gasm_->IntPtrConstant(kI31To32BitSmiShift)));
}
Node* WasmGraphBuilder::I31GetU(Node* input) {
Node* WasmGraphBuilder::I31GetU(Node* input, CheckForNull null_check,
wasm::WasmCodePosition position) {
if (null_check == kWithNullCheck) input = AssertNotNull(input, position);
if (SmiValuesAre31Bits()) {
input = gasm_->BuildTruncateIntPtrToInt32(input);
return gasm_->Word32Shr(input, gasm_->BuildSmiShiftBitsConstant32());
......
......@@ -502,8 +502,10 @@ class WasmGraphBuilder {
Node* offset, Node* length, Node* rtt,
wasm::WasmCodePosition position);
Node* I31New(Node* input);
Node* I31GetS(Node* input);
Node* I31GetU(Node* input);
Node* I31GetS(Node* input, CheckForNull null_check,
wasm::WasmCodePosition position);
Node* I31GetU(Node* input, CheckForNull null_check,
wasm::WasmCodePosition position);
Node* RttCanon(uint32_t type_index);
Node* RefTest(Node* object, Node* rtt, WasmTypeCheckConfig config);
......
......@@ -5756,7 +5756,9 @@ class LiftoffCompiler {
}
void I31GetS(FullDecoder* decoder, const Value& input, Value* result) {
LiftoffRegister src = __ PopToRegister();
LiftoffRegList pinned;
LiftoffRegister src = pinned.set(__ PopToRegister());
MaybeEmitNullCheck(decoder, src.gp(), pinned, input.type);
LiftoffRegister dst = __ GetUnusedRegister(kGpReg, {src}, {});
if (SmiValuesAre31Bits()) {
__ emit_i32_sari(dst.gp(), src.gp(), kSmiTagSize);
......@@ -5768,7 +5770,9 @@ class LiftoffCompiler {
}
void I31GetU(FullDecoder* decoder, const Value& input, Value* result) {
LiftoffRegister src = __ PopToRegister();
LiftoffRegList pinned;
LiftoffRegister src = pinned.set(__ PopToRegister());
MaybeEmitNullCheck(decoder, src.gp(), pinned, input.type);
LiftoffRegister dst = __ GetUnusedRegister(kGpReg, {src}, {});
if (SmiValuesAre31Bits()) {
__ emit_i32_shri(dst.gp(), src.gp(), kSmiTagSize);
......
......@@ -325,14 +325,8 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
}
V8_FALLTHROUGH;
case kExternRefCode:
case kFuncRefCode: {
HeapType heap_type = HeapType::from_code(code);
Nullability nullability =
code == kI31RefCode || code == kDataRefCode || code == kArrayRefCode
? kNonNullable
: kNullable;
return ValueType::RefMaybeNull(heap_type, nullability);
}
case kFuncRefCode:
return ValueType::RefNull(HeapType::from_code(code));
case kStringRefCode:
case kStringViewWtf8Code:
case kStringViewWtf16Code:
......@@ -4561,7 +4555,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
// 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);
Value array_obj = Peek(0, 0, ValueType::RefNull(HeapType::kArray));
Value array_obj = Peek(0, 0, kWasmArrayRef);
Value value = CreateValue(kWasmI32);
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayLen, array_obj, &value);
Drop(array_obj);
......@@ -4635,7 +4629,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
}
case kExprI31New: {
Value input = Peek(0, 0, kWasmI32);
Value value = CreateValue(kWasmI31Ref);
Value value = CreateValue(ValueType::Ref(HeapType::kI31));
CALL_INTERFACE_IF_OK_AND_REACHABLE(I31New, input, &value);
Drop(input);
Push(value);
......@@ -4671,8 +4665,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
Value obj = Peek(1);
Value value = CreateValue(kWasmI32);
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
IsSubtypeOf(obj.type, ValueType::RefNull(HeapType::kData),
this->module_) ||
IsSubtypeOf(obj.type, kWasmDataRef, this->module_) ||
obj.type.is_bottom())) {
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
return 0;
......@@ -4718,8 +4711,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
opcode_length += imm.length;
Value obj = Peek(0);
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
IsSubtypeOf(obj.type, ValueType::RefNull(HeapType::kData),
this->module_) ||
IsSubtypeOf(obj.type, kWasmDataRef, this->module_) ||
obj.type.is_bottom())) {
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
return 0;
......@@ -4743,8 +4735,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
Push(rtt);
Value obj = Peek(1);
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
IsSubtypeOf(obj.type, ValueType::RefNull(HeapType::kData),
this->module_) ||
IsSubtypeOf(obj.type, kWasmDataRef, this->module_) ||
obj.type.is_bottom())) {
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
return 0;
......@@ -4801,8 +4792,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
// anyway.
Value obj = Peek(0);
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
IsSubtypeOf(obj.type, ValueType::RefNull(HeapType::kData),
this->module_) ||
IsSubtypeOf(obj.type, kWasmDataRef, this->module_) ||
obj.type.is_bottom())) {
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
return 0;
......@@ -4869,8 +4859,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, imm.index, &rtt);
Value obj = Peek(0);
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
IsSubtypeOf(obj.type, ValueType::RefNull(HeapType::kData),
this->module_) ||
IsSubtypeOf(obj.type, kWasmDataRef, this->module_) ||
obj.type.is_bottom())) {
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
return 0;
......
......@@ -1195,11 +1195,15 @@ class WasmGraphBuildingInterface {
}
void I31GetS(FullDecoder* decoder, const Value& input, Value* result) {
SetAndTypeNode(result, builder_->I31GetS(input.node));
SetAndTypeNode(result,
builder_->I31GetS(input.node, NullCheckFor(input.type),
decoder->position()));
}
void I31GetU(FullDecoder* decoder, const Value& input, Value* result) {
SetAndTypeNode(result, builder_->I31GetU(input.node));
SetAndTypeNode(result,
builder_->I31GetU(input.node, NullCheckFor(input.type),
decoder->position()));
}
void RttCanon(FullDecoder* decoder, uint32_t type_index, Value* result) {
......
......@@ -532,6 +532,12 @@ class ValueType {
return kExternRefCode;
case HeapType::kAny:
return kAnyRefCode;
case HeapType::kI31:
return kI31RefCode;
case HeapType::kData:
return kDataRefCode;
case HeapType::kArray:
return kArrayRefCode;
case HeapType::kString:
return kStringRefCode;
case HeapType::kStringViewWtf8:
......@@ -546,16 +552,7 @@ class ValueType {
return kRefNullCode;
}
case kRef:
switch (heap_representation()) {
case HeapType::kI31:
return kI31RefCode;
case HeapType::kData:
return kDataRefCode;
case HeapType::kArray:
return kArrayRefCode;
default:
return kRefCode;
}
return kRefCode;
#define NUMERIC_TYPE_CASE(kind, ...) \
case k##kind: \
return k##kind##Code;
......@@ -574,23 +571,7 @@ 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 {
switch (kind()) {
case kRef:
return heap_representation() != HeapType::kI31 &&
heap_representation() != HeapType::kArray &&
heap_representation() != HeapType::kData;
case kRefNull:
return heap_representation() != HeapType::kFunc &&
heap_representation() != HeapType::kEq &&
heap_representation() != HeapType::kAny &&
heap_representation() != HeapType::kExtern &&
heap_representation() != HeapType::kString &&
heap_representation() != HeapType::kStringViewWtf8 &&
heap_representation() != HeapType::kStringViewWtf16 &&
heap_representation() != HeapType::kStringViewIter;
default:
return false;
}
return kind() == kRef || (kind() == kRefNull && heap_type().is_index());
}
/****************************** Pretty-printing *****************************/
......@@ -600,12 +581,13 @@ class ValueType {
std::ostringstream buf;
switch (kind()) {
case kRef:
buf << "(ref " << heap_type().name() << ")";
break;
case kRefNull:
if (encoding_needs_heap_type()) {
buf << "(ref " << (kind() == kRefNull ? "null " : "")
<< heap_type().name() << ")";
} else {
if (heap_type().is_generic()) {
buf << heap_type().name() << "ref";
} else {
buf << "(ref null " << heap_type().name() << ")";
}
break;
case kRtt:
......@@ -694,10 +676,9 @@ constexpr ValueType kWasmFuncRef = ValueType::RefNull(HeapType::kFunc);
constexpr ValueType kWasmAnyRef = ValueType::RefNull(HeapType::kAny);
constexpr ValueType kWasmExternRef = ValueType::RefNull(HeapType::kExtern);
constexpr ValueType kWasmEqRef = ValueType::RefNull(HeapType::kEq);
constexpr ValueType kWasmI31Ref = ValueType::Ref(HeapType::kI31);
constexpr ValueType kWasmDataRef = ValueType::Ref(HeapType::kData);
constexpr ValueType kWasmArrayRef = ValueType::Ref(HeapType::kArray);
constexpr ValueType kWasmNullRef = ValueType::RefNull(HeapType::kNone);
constexpr ValueType kWasmI31Ref = ValueType::RefNull(HeapType::kI31);
constexpr ValueType kWasmDataRef = ValueType::RefNull(HeapType::kData);
constexpr ValueType kWasmArrayRef = ValueType::RefNull(HeapType::kArray);
constexpr ValueType kWasmStringRef = ValueType::RefNull(HeapType::kString);
constexpr ValueType kWasmStringViewWtf8 =
ValueType::RefNull(HeapType::kStringViewWtf8);
......@@ -705,6 +686,7 @@ constexpr ValueType kWasmStringViewWtf16 =
ValueType::RefNull(HeapType::kStringViewWtf16);
constexpr ValueType kWasmStringViewIter =
ValueType::RefNull(HeapType::kStringViewIter);
constexpr ValueType kWasmNullRef = ValueType::RefNull(HeapType::kNone);
// Constants used by the generic js-to-wasm wrapper.
constexpr int kWasmValueKindBitsMask = (1u << ValueType::kKindBits) - 1;
......
......@@ -571,12 +571,11 @@ WASM_COMPILED_EXEC_TEST(RefCastStaticNoChecks) {
WASM_COMPILED_EXEC_TEST(BrOnCast) {
WasmGCTester tester(execution_tier);
ValueType kDataRefNull = ValueType::RefNull(HeapType::kData);
const byte type_index = tester.DefineStruct({F(kWasmI32, true)});
const byte other_type_index = tester.DefineStruct({F(kWasmF32, true)});
const byte kTestStructStatic = tester.DefineFunction(
tester.sigs.i_v(), {kWasmI32, kDataRefNull},
tester.sigs.i_v(), {kWasmI32, kWasmDataRef},
{WASM_BLOCK_R(
ValueType::RefNull(type_index), WASM_LOCAL_SET(0, WASM_I32V(111)),
// Pipe a struct through a local so it's statically typed
......@@ -599,7 +598,7 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
kExprI32Add, kExprEnd});
const byte kTestNull = tester.DefineFunction(
tester.sigs.i_v(), {kWasmI32, kDataRefNull},
tester.sigs.i_v(), {kWasmI32, kWasmDataRef},
{WASM_BLOCK_R(ValueType::RefNull(type_index),
WASM_LOCAL_SET(0, WASM_I32V(111)),
WASM_LOCAL_GET(1), // Put a nullref onto the value stack.
......@@ -611,7 +610,7 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
WASM_DROP, WASM_LOCAL_GET(0), kExprEnd});
const byte kTypedAfterBranch = tester.DefineFunction(
tester.sigs.i_v(), {kWasmI32, kDataRefNull},
tester.sigs.i_v(), {kWasmI32, kWasmDataRef},
{WASM_LOCAL_SET(1, WASM_STRUCT_NEW(type_index, WASM_I32V(42))),
WASM_BLOCK_I(
// The inner block should take the early branch with a struct
......@@ -633,7 +632,6 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
WASM_COMPILED_EXEC_TEST(BrOnCastFail) {
WasmGCTester tester(execution_tier);
ValueType kDataRefNull = ValueType::RefNull(HeapType::kData);
const byte type0 = tester.DefineStruct({F(kWasmI32, true)});
const byte type1 =
tester.DefineStruct({F(kWasmI64, true), F(kWasmI32, true)});
......@@ -653,7 +651,7 @@ WASM_COMPILED_EXEC_TEST(BrOnCastFail) {
#define FUNCTION_BODY(value) \
WASM_LOCAL_SET(0, WASM_SEQ(value)), \
WASM_BLOCK( \
WASM_BLOCK_R(kDataRefNull, WASM_LOCAL_GET(0), \
WASM_BLOCK_R(kWasmDataRef, WASM_LOCAL_GET(0), \
WASM_BR_ON_CAST_STATIC_FAIL(0, type0), \
WASM_GC_OP(kExprStructGet), type0, 0, kExprReturn), \
kExprBrOnNull, 0, WASM_GC_OP(kExprRefCastStatic), type1, \
......@@ -661,16 +659,16 @@ WASM_COMPILED_EXEC_TEST(BrOnCastFail) {
WASM_I32V(null_value), kExprEnd
const byte kBranchTaken = tester.DefineFunction(
tester.sigs.i_v(), {kDataRefNull},
tester.sigs.i_v(), {kWasmDataRef},
{FUNCTION_BODY(
WASM_STRUCT_NEW(type1, WASM_I64V(10), WASM_I32V(field1_value)))});
const byte kBranchNotTaken = tester.DefineFunction(
tester.sigs.i_v(), {kDataRefNull},
tester.sigs.i_v(), {kWasmDataRef},
{FUNCTION_BODY(WASM_STRUCT_NEW(type0, WASM_I32V(field0_value)))});
const byte kNull = tester.DefineFunction(
tester.sigs.i_v(), {kDataRefNull}, {FUNCTION_BODY(WASM_REF_NULL(type0))});
tester.sigs.i_v(), {kWasmDataRef}, {FUNCTION_BODY(WASM_REF_NULL(type0))});
const byte kUnrelatedTypes = tester.DefineFunction(
tester.sigs.i_v(), {ValueType::RefNull(type1)},
......@@ -679,11 +677,11 @@ WASM_COMPILED_EXEC_TEST(BrOnCastFail) {
#undef FUNCTION_BODY
const byte kBranchTakenStatic = tester.DefineFunction(
tester.sigs.i_v(), {kDataRefNull},
tester.sigs.i_v(), {kWasmDataRef},
{WASM_LOCAL_SET(
0, WASM_STRUCT_NEW(type1, WASM_I64V(10), WASM_I32V(field1_value))),
WASM_BLOCK(
WASM_BLOCK_R(kDataRefNull, WASM_LOCAL_GET(0),
WASM_BLOCK_R(kWasmDataRef, WASM_LOCAL_GET(0),
WASM_BR_ON_CAST_STATIC_FAIL(0, type0),
WASM_GC_OP(kExprStructGet), type0, 0, kExprReturn),
kExprBrOnNull, 0, WASM_GC_OP(kExprRefCastStatic), type1,
......@@ -1831,25 +1829,6 @@ WASM_COMPILED_EXEC_TEST(AbstractTypeChecks) {
tester.CheckResult(kBrOnNonI31NotTaken, 1);
}
WASM_COMPILED_EXEC_TEST(BasicI31) {
WasmGCTester tester(execution_tier);
const byte kSigned = tester.DefineFunction(
tester.sigs.i_i(), {},
{WASM_I31_GET_S(WASM_I31_NEW(WASM_LOCAL_GET(0))), kExprEnd});
const byte kUnsigned = tester.DefineFunction(
tester.sigs.i_i(), {},
{WASM_I31_GET_U(WASM_I31_NEW(WASM_LOCAL_GET(0))), kExprEnd});
tester.CompileModule();
tester.CheckResult(kSigned, 123, 123);
tester.CheckResult(kUnsigned, 123, 123);
// Truncation:
tester.CheckResult(kSigned, 0x1234, static_cast<int32_t>(0x80001234));
tester.CheckResult(kUnsigned, 0x1234, static_cast<int32_t>(0x80001234));
// Sign/zero extension:
tester.CheckResult(kSigned, -1, 0x7FFFFFFF);
tester.CheckResult(kUnsigned, 0x7FFFFFFF, 0x7FFFFFFF);
}
// This flushed out a few bugs, so it serves as a regression test. It can also
// be modified (made to run longer) to measure performance of casts.
WASM_COMPILED_EXEC_TEST(CastsBenchmark) {
......@@ -1858,8 +1837,7 @@ WASM_COMPILED_EXEC_TEST(CastsBenchmark) {
const byte SubType = tester.DefineStruct(
{F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)}, SuperType);
ValueType kDataRefNull = ValueType::RefNull(HeapType::kData);
const byte ListType = tester.DefineArray(kDataRefNull, true);
const byte ListType = tester.DefineArray(kWasmDataRef, true);
const byte List =
tester.AddGlobal(ValueType::RefNull(ListType), true,
......
......@@ -8,7 +8,8 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
var builder = new WasmModuleBuilder();
var sig_index = builder.addType({params: [kWasmDataRef], results: []});
var sig_index = builder.addType(
{params: [wasmRefType(kWasmDataRef)], results: []});
builder.addFunction('main', sig_index).addBody([]).exportFunc();
......
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-gc
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function I31RefBasic() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
builder.addFunction("signed", kSig_i_i)
.addLocals(wasmRefType(kWasmI31Ref), 1)
.addBody([kExprLocalGet, 0, kGCPrefix, kExprI31New, kExprLocalTee, 1,
kGCPrefix, kExprI31GetS])
.exportFunc();
builder.addFunction("unsigned", kSig_i_i)
.addLocals(wasmRefType(kWasmI31Ref), 1)
.addBody([kExprLocalGet, 0, kGCPrefix, kExprI31New, kExprLocalTee, 1,
kGCPrefix, kExprI31GetU])
.exportFunc();
let instance = builder.instantiate();
assertEquals(123, instance.exports.signed(123));
assertEquals(123, instance.exports.unsigned(123));
// Truncation:
assertEquals(0x1234, instance.exports.signed(0x80001234));
assertEquals(0x1234, instance.exports.unsigned(0x80001234));
// Sign/zero extention:
assertEquals(-1, instance.exports.signed(0x7fffffff));
assertEquals(0x7fffffff, instance.exports.unsigned(0x7fffffff));
})();
(function I31RefNullable() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
builder.addFunction("i31_null", kSig_i_i)
.addLocals(wasmRefNullType(kWasmI31Ref), 1)
.addBody([
kExprLocalGet, 0,
kExprIf, kWasmVoid,
kExprRefNull, kI31RefCode, kExprLocalSet, 1,
kExprElse,
...wasmI32Const(42), kGCPrefix, kExprI31New, kExprLocalSet, 1,
kExprEnd,
kExprLocalGet, 1, kGCPrefix, kExprI31GetS])
.exportFunc();
let instance = builder.instantiate();
assertEquals(42, instance.exports.i31_null(0));
assertTraps(kTrapNullDereference, () => instance.exports.i31_null(1));
})();
......@@ -3715,12 +3715,9 @@ TEST_F(FunctionBodyDecoderTest, RefEq) {
WASM_FEATURE_SCOPE(gc);
byte struct_type_index = builder.AddStruct({F(kWasmI32, true)});
ValueType eqref_subtypes[] = {kWasmEqRef,
kWasmI31Ref,
ValueType::Ref(HeapType::kEq),
ValueType::RefNull(HeapType::kI31),
ref(struct_type_index),
refNull(struct_type_index)};
ValueType eqref_subtypes[] = {
kWasmEqRef, kWasmI31Ref.AsNonNull(), kWasmEqRef.AsNonNull(),
kWasmI31Ref, ref(struct_type_index), refNull(struct_type_index)};
ValueType non_eqref_subtypes[] = {kWasmI32,
kWasmI64,
kWasmF32,
......@@ -4130,9 +4127,9 @@ TEST_F(FunctionBodyDecoderTest, GCArray) {
ExpectFailure(&sig_f_r, {WASM_ARRAY_LEN(WASM_LOCAL_GET(0))}, kAppendEnd,
"type error in fallthru[0] (expected f32, got i32)");
// 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)");
ExpectFailure(
&sig_i_s, {WASM_ARRAY_LEN(WASM_LOCAL_GET(0))}, kAppendEnd,
"array.len[0] expected type arrayref, found local.get of type (ref 1)");
// Immutable array.
// Allocating and reading is OK:
......
......@@ -196,13 +196,16 @@ TEST_F(WasmSubtypingTest, Subtyping) {
SUBTYPE_IFF(ref_type, kWasmEqRef,
ref_type != kWasmFuncRef && ref_type != kWasmAnyRef &&
ref_type != refNull(11) && ref_type != ref(11));
// Non-nullable struct/array types are subtypes of dataref.
// Struct/array types are subtypes of dataref.
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.
ref_type == kWasmNullRef || ref_type == ref(0) ||
ref_type == ref(2) || ref_type == refNull(0) ||
ref_type == refNull(2));
// Array types are subtypes of arrayref.
SUBTYPE_IFF(ref_type, kWasmArrayRef,
ref_type == kWasmArrayRef || ref_type == ref(2));
ref_type == kWasmArrayRef || ref_type == ref(2) ||
ref_type == kWasmNullRef || ref_type == refNull(2));
// Functions are subtypes of funcref.
SUBTYPE_IFF(ref_type, kWasmFuncRef,
ref_type == kWasmFuncRef || ref_type == refNull(11) ||
......@@ -353,29 +356,34 @@ TEST_F(WasmSubtypingTest, Subtyping) {
UNION(kWasmEqRef, kWasmI31Ref, kWasmEqRef);
UNION(kWasmEqRef, kWasmArrayRef, kWasmEqRef);
UNION(kWasmEqRef, kWasmNullRef, kWasmEqRef);
UNION(kWasmDataRef, kWasmI31Ref, kWasmEqRef.AsNonNull());
UNION(kWasmDataRef, kWasmI31Ref, kWasmEqRef);
UNION(kWasmDataRef, kWasmArrayRef, kWasmDataRef);
UNION(kWasmDataRef, kWasmNullRef, kWasmDataRef.AsNullable());
UNION(kWasmI31Ref, kWasmArrayRef, kWasmEqRef.AsNonNull());
UNION(kWasmI31Ref.AsNonNull(), kWasmArrayRef.AsNonNull(),
kWasmEqRef.AsNonNull());
UNION(kWasmI31Ref, kWasmNullRef, kWasmI31Ref.AsNullable());
UNION(kWasmArrayRef, kWasmNullRef, kWasmArrayRef.AsNullable());
UNION(kWasmDataRef.AsNonNull(), kWasmI31Ref.AsNonNull(),
kWasmEqRef.AsNonNull());
UNION(kWasmDataRef, kWasmArrayRef, kWasmDataRef);
UNION(kWasmI31Ref.AsNonNull(), kWasmArrayRef, kWasmEqRef);
INTERSECTION(kWasmFuncRef, kWasmEqRef, kWasmNullRef);
INTERSECTION(kWasmFuncRef, kWasmDataRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmI31Ref, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmArrayRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmDataRef, kWasmNullRef);
INTERSECTION(kWasmFuncRef, kWasmI31Ref.AsNonNull(), kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmArrayRef, kWasmNullRef);
INTERSECTION(kWasmFuncRef, kWasmNullRef, kWasmNullRef);
INTERSECTION(kWasmEqRef, kWasmDataRef, kWasmDataRef);
INTERSECTION(kWasmEqRef, kWasmI31Ref, kWasmI31Ref);
INTERSECTION(kWasmEqRef, kWasmArrayRef, kWasmArrayRef);
INTERSECTION(kWasmEqRef, kWasmNullRef, kWasmNullRef);
INTERSECTION(kWasmEqRef, kWasmFuncRef, kWasmNullRef);
INTERSECTION(kWasmDataRef, kWasmI31Ref, kWasmBottom);
INTERSECTION(kWasmDataRef, kWasmI31Ref, kWasmNullRef);
INTERSECTION(kWasmDataRef, kWasmArrayRef, kWasmArrayRef);
INTERSECTION(kWasmDataRef, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmI31Ref, kWasmArrayRef, kWasmBottom);
INTERSECTION(kWasmI31Ref, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmArrayRef, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmDataRef, kWasmNullRef, kWasmNullRef);
INTERSECTION(kWasmI31Ref, kWasmArrayRef, kWasmNullRef);
INTERSECTION(kWasmI31Ref.AsNonNull(), kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmArrayRef.AsNonNull(), kWasmNullRef, kWasmBottom);
ValueType struct_type = ref(0);
ValueType array_type = ref(2);
......@@ -398,21 +406,21 @@ TEST_F(WasmSubtypingTest, Subtyping) {
UNION(kWasmDataRef, struct_type, kWasmDataRef);
UNION(kWasmDataRef, array_type, kWasmDataRef);
UNION(kWasmDataRef, function_type, kWasmAnyRef.AsNonNull());
UNION(kWasmDataRef, function_type, kWasmAnyRef);
INTERSECTION(kWasmDataRef, struct_type, struct_type);
INTERSECTION(kWasmDataRef, array_type, array_type);
INTERSECTION(kWasmDataRef, function_type, kWasmBottom);
UNION(kWasmI31Ref, struct_type, kWasmEqRef.AsNonNull());
UNION(kWasmI31Ref, array_type, kWasmEqRef.AsNonNull());
UNION(kWasmI31Ref, function_type, kWasmAnyRef.AsNonNull());
UNION(kWasmI31Ref, struct_type, kWasmEqRef);
UNION(kWasmI31Ref, array_type, kWasmEqRef);
UNION(kWasmI31Ref, function_type, kWasmAnyRef);
INTERSECTION(kWasmI31Ref, struct_type, kWasmBottom);
INTERSECTION(kWasmI31Ref, array_type, kWasmBottom);
INTERSECTION(kWasmI31Ref, function_type, kWasmBottom);
UNION(kWasmArrayRef, struct_type, kWasmDataRef);
UNION(kWasmArrayRef, array_type, kWasmArrayRef);
UNION(kWasmArrayRef, function_type, kWasmAnyRef.AsNonNull());
UNION(kWasmArrayRef.AsNonNull(), function_type, kWasmAnyRef.AsNonNull());
INTERSECTION(kWasmArrayRef, struct_type, kWasmBottom);
INTERSECTION(kWasmArrayRef, array_type, array_type);
INTERSECTION(kWasmArrayRef, function_type, kWasmBottom);
......@@ -425,7 +433,7 @@ TEST_F(WasmSubtypingTest, Subtyping) {
INTERSECTION(kWasmNullRef, function_type, kWasmBottom);
// Indexed types of different kinds.
UNION(struct_type, array_type, kWasmDataRef);
UNION(struct_type, array_type, kWasmDataRef.AsNonNull());
UNION(struct_type, function_type, kWasmAnyRef.AsNonNull());
UNION(array_type, function_type, kWasmAnyRef.AsNonNull());
INTERSECTION(struct_type, array_type, kWasmBottom);
......@@ -449,7 +457,7 @@ TEST_F(WasmSubtypingTest, Subtyping) {
// No common ancestor.
UNION(ref(6), refNull(2), kWasmArrayRef.AsNullable());
INTERSECTION(ref(6), refNull(2), kWasmBottom);
UNION(ref(0), ref(17), kWasmDataRef);
UNION(ref(0), ref(17), kWasmDataRef.AsNonNull());
INTERSECTION(ref(0), ref(17), kWasmBottom);
UNION(ref(10), refNull(11), kWasmFuncRef);
INTERSECTION(ref(10), refNull(11), kWasmBottom);
......
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