Commit 3a639c3b authored by Matthias Liedtke's avatar Matthias Liedtke Committed by V8 LUCI CQ

[wasm-gc] Introduce extern null type noextern

noextern is the abstract null type for the extern type.

Bug: v8:7748
Change-Id: I03ac0daf3051f479e096f3d05f4fa7cbf03968f1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3810191Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Matthias Liedtke <mliedtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82276}
parent c1874ac3
......@@ -239,6 +239,7 @@ HeapType read_heap_type(Decoder* decoder, const byte* pc,
case kArrayRefCode:
case kAnyRefCode:
case kNoneCode:
case kNoExternCode:
case kNoFuncCode:
if (!VALIDATE(enabled.has_gc())) {
DecodeError<validate>(
......@@ -317,6 +318,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
case kArrayRefCode:
case kAnyRefCode:
case kNoneCode:
case kNoExternCode:
case kNoFuncCode:
if (!VALIDATE(enabled.has_gc())) {
DecodeError<validate>(
......
......@@ -72,6 +72,7 @@ class HeapType {
kStringViewIter, // shorthand: z.
kNone, //
kNoFunc, //
kNoExtern, //
// This value is used to represent failures in the parsing of heap types and
// does not correspond to a wasm heap type. It has to be last in this list.
kBottom
......@@ -103,6 +104,8 @@ class HeapType {
return HeapType(kStringViewIter);
case ValueTypeCode::kNoneCode:
return HeapType(kNone);
case ValueTypeCode::kNoExternCode:
return HeapType(kNoExtern);
case ValueTypeCode::kNoFuncCode:
return HeapType(kNoFunc);
default:
......@@ -171,6 +174,8 @@ class HeapType {
return std::string("stringview_iter");
case kNone:
return std::string("none");
case kNoExtern:
return std::string("noextern");
case kNoFunc:
return std::string("nofunc");
default:
......@@ -208,6 +213,8 @@ class HeapType {
return mask | kStringViewIterCode;
case kNone:
return mask | kNoneCode;
case kNoExtern:
return mask | kNoExternCode;
case kNoFunc:
return mask | kNoFuncCode;
default:
......@@ -430,6 +437,7 @@ class ValueType {
// If {this} is (ref null $t), returns (ref $t). Otherwise, returns {this}.
constexpr ValueType AsNonNull() const {
if (is_reference_to(HeapType::kNone) ||
is_reference_to(HeapType::kNoExtern) ||
is_reference_to(HeapType::kNoFunc)) {
// Non-null none type is not a valid type.
return ValueType::Primitive(kBottom);
......@@ -556,6 +564,8 @@ class ValueType {
return kStringViewIterCode;
case HeapType::kNone:
return kNoneCode;
case HeapType::kNoExtern:
return kNoExternCode;
case HeapType::kNoFunc:
return kNoFuncCode;
default:
......@@ -701,6 +711,8 @@ constexpr ValueType kWasmStringViewWtf16 =
constexpr ValueType kWasmStringViewIter =
ValueType::RefNull(HeapType::kStringViewIter);
constexpr ValueType kWasmNullRef = ValueType::RefNull(HeapType::kNone);
constexpr ValueType kWasmNullExternRef =
ValueType::RefNull(HeapType::kNoExtern);
constexpr ValueType kWasmNullFuncRef = ValueType::RefNull(HeapType::kNoFunc);
// Constants used by the generic js-to-wasm wrapper.
......
......@@ -39,6 +39,7 @@ enum ValueTypeCode : uint8_t {
kFuncRefCode = 0x70,
// TODO(7784): Switch to official opcodes once they are aligned with the
// stringref proposal for nofunc and noextern.
kNoExternCode = 0x69,
kNoFuncCode = 0x68,
kExternRefCode = 0x6f,
// typed-funcref and GC proposal types
......
......@@ -107,17 +107,18 @@ HeapType::Representation NullSentinel(HeapType type, const WasmModule* module) {
case HeapType::kData:
case HeapType::kArray:
case HeapType::kAny:
case HeapType::kExtern: // TODO(mliedtke): Add noextern.
case HeapType::kString:
case HeapType::kStringViewWtf8:
case HeapType::kStringViewWtf16:
case HeapType::kStringViewIter:
return HeapType::kNone;
case HeapType::kExtern:
case HeapType::kNoExtern:
return HeapType::kNoExtern;
case HeapType::kFunc:
case HeapType::kNoFunc:
return HeapType::kNoFunc;
default:
// TODO(mliedtke): Add noextern.
return module->has_signature(type.ref_index()) ? HeapType::kNoFunc
: HeapType::kNone;
}
......@@ -126,8 +127,8 @@ HeapType::Representation NullSentinel(HeapType type, const WasmModule* module) {
bool IsNullSentinel(HeapType type) {
switch (type.representation()) {
case HeapType::kNone:
case HeapType::kNoExtern:
case HeapType::kNoFunc:
// TODO(mliedtke): Add noextern.
return true;
default:
return false;
......@@ -228,11 +229,15 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsHeapSubtypeOfImpl(
case HeapType::kNone:
// none is a subtype of every non-func, non-extern reference type under
// wasm-gc.
// TODO(mliedtke): Break-up extern and nullref subtyping.
if (super_heap.is_index()) {
return !super_module->has_signature(super_heap.ref_index());
}
return super_heap != HeapType::kFunc && super_heap != HeapType::kNoFunc;
return super_heap != HeapType::kFunc && super_heap != HeapType::kNoFunc &&
super_heap != HeapType::kExtern &&
super_heap != HeapType::kNoExtern;
case HeapType::kNoExtern:
return super_heap == HeapType::kNoExtern ||
super_heap == HeapType::kExtern;
case HeapType::kNoFunc:
// nofunc is a subtype of every funcref type under wasm-gc.
if (super_heap.is_index()) {
......@@ -268,6 +273,7 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsHeapSubtypeOfImpl(
case HeapType::kBottom:
UNREACHABLE();
case HeapType::kNone:
case HeapType::kNoExtern:
case HeapType::kNoFunc:
// Abstract null types are not supertypes for any index type.
return false;
......@@ -391,6 +397,7 @@ HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
return HeapType::kAny;
case HeapType::kFunc:
case HeapType::kExtern:
case HeapType::kNoExtern:
case HeapType::kNoFunc:
UNREACHABLE();
default:
......@@ -410,6 +417,7 @@ HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
return HeapType::kAny;
case HeapType::kFunc:
case HeapType::kExtern:
case HeapType::kNoExtern:
case HeapType::kNoFunc:
UNREACHABLE();
default:
......@@ -430,6 +438,7 @@ HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
return HeapType::kAny;
case HeapType::kFunc:
case HeapType::kExtern:
case HeapType::kNoExtern:
case HeapType::kNoFunc:
UNREACHABLE();
default:
......@@ -452,6 +461,7 @@ HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
case HeapType::kEq:
case HeapType::kAny:
case HeapType::kExtern:
case HeapType::kNoExtern:
UNREACHABLE();
case HeapType::kNoFunc:
return HeapType::kNoFunc;
......@@ -462,6 +472,11 @@ HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
? heap2.representation()
: HeapType::kBottom;
}
case HeapType::kNoExtern:
return heap2.representation() == HeapType::kExtern ? HeapType::kExtern
: HeapType::kNoExtern;
case HeapType::kExtern:
return HeapType::kExtern;
default:
UNREACHABLE();
}
......
......@@ -1546,7 +1546,7 @@ WASM_COMPILED_EXEC_TEST(CallAbstractNullTypeImplicitConversion) {
{kWasmDataRef.AsNullable(), kNoneCode},
{kWasmArrayRef.AsNullable(), kNoneCode},
{kWasmAnyRef, kNoneCode},
{kWasmExternRef, kNoneCode},
{kWasmExternRef, kNoExternCode},
{refNull(0), kNoneCode}, // struct
{refNull(1), kNoneCode}, // array
{refNull(2), kNoFuncCode}, // signature
......
......@@ -2142,6 +2142,19 @@ TEST_F(FunctionBodyDecoderTest, NullRefGlobals) {
&sig, {WASM_GLOBAL_SET(0, WASM_REF_NULL(kNoneCode)), WASM_LOCAL_GET(0)});
}
TEST_F(FunctionBodyDecoderTest, NullExternRefGlobals) {
WASM_FEATURE_SCOPE(gc);
ValueType nullExternRefs[] = {kWasmNullExternRef, kWasmNullExternRef,
kWasmNullExternRef};
FunctionSig sig(1, 2, nullExternRefs);
builder.AddGlobal(kWasmNullExternRef);
ExpectValidates(&sig, {WASM_GLOBAL_GET(0)});
ExpectValidates(&sig,
{WASM_GLOBAL_SET(0, WASM_LOCAL_GET(0)), WASM_LOCAL_GET(0)});
ExpectValidates(&sig, {WASM_GLOBAL_SET(0, WASM_REF_NULL(kNoExternCode)),
WASM_LOCAL_GET(0)});
}
TEST_F(FunctionBodyDecoderTest, NullFuncRefGlobals) {
WASM_FEATURE_SCOPE(gc);
ValueType nullFuncRefs[] = {kWasmNullFuncRef, kWasmNullFuncRef,
......
......@@ -189,6 +189,7 @@ struct ValueTypePair {
{kFuncRefCode, kWasmFuncRef}, // --
{kNoFuncCode, kWasmNullFuncRef}, // --
{kExternRefCode, kWasmExternRef}, // --
{kNoExternCode, kWasmNullExternRef}, // --
{kAnyRefCode, kWasmAnyRef}, // --
{kEqRefCode, kWasmEqRef}, // --
{kI31RefCode, kWasmI31Ref}, // --
......
......@@ -129,13 +129,14 @@ TEST_F(WasmSubtypingTest, Subtyping) {
constexpr ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
kWasmS128};
constexpr ValueType ref_types[] = {
// TODO(mliedtke): Add externref
kWasmFuncRef, kWasmEqRef, kWasmI31Ref, // --
kWasmDataRef, kWasmArrayRef, kWasmAnyRef, // --
kWasmNullRef, kWasmNullFuncRef, // --
refNull(0), ref(0), // struct
refNull(2), ref(2), // array
refNull(11), ref(11) // signature
kWasmFuncRef, kWasmEqRef, // --
kWasmDataRef, kWasmArrayRef, // --
kWasmI31Ref, kWasmAnyRef, // --
kWasmExternRef, kWasmNullExternRef, // --
kWasmNullRef, kWasmNullFuncRef, // --
refNull(0), ref(0), // struct
refNull(2), ref(2), // array
refNull(11), ref(11) // signature
};
// Some macros to help managing types and modules.
......@@ -192,6 +193,8 @@ TEST_F(WasmSubtypingTest, Subtyping) {
}
for (ValueType ref_type : ref_types) {
const bool is_extern =
ref_type == kWasmExternRef || ref_type == kWasmNullExternRef;
const bool is_any_func = ref_type == kWasmFuncRef ||
ref_type == kWasmNullFuncRef ||
ref_type == refNull(11) || ref_type == ref(11);
......@@ -199,7 +202,7 @@ TEST_F(WasmSubtypingTest, Subtyping) {
// Concrete reference types, i31ref and dataref are subtypes of eqref,
// externref/funcref/anyref/functions are not.
SUBTYPE_IFF(ref_type, kWasmEqRef,
ref_type != kWasmAnyRef && !is_any_func);
ref_type != kWasmAnyRef && !is_any_func && !is_extern);
// Struct/array types are subtypes of dataref.
SUBTYPE_IFF(ref_type, kWasmDataRef,
ref_type == kWasmDataRef || ref_type == kWasmArrayRef ||
......@@ -215,16 +218,17 @@ TEST_F(WasmSubtypingTest, Subtyping) {
// Each reference type is a subtype of itself.
SUBTYPE(ref_type, ref_type);
// Each non-func, non-extern reference type is a subtype of anyref.
SUBTYPE_IFF(ref_type, kWasmAnyRef, !is_any_func);
SUBTYPE_IFF(ref_type, kWasmAnyRef, !is_any_func && !is_extern);
// Only anyref is a subtype of anyref.
SUBTYPE_IFF(kWasmAnyRef, ref_type, ref_type == kWasmAnyRef);
// TODO(mliedtke): Improve test coverage for externref.
// Only externref is a subtype of externref.
NOT_SUBTYPE(kWasmExternRef, ref_type);
// Only externref and nullexternref are subtypes of externref.
SUBTYPE_IFF(ref_type, kWasmExternRef, is_extern);
// Only nullexternref is a subtype of nullexternref.
SUBTYPE_IFF(ref_type, kWasmNullExternRef, ref_type == kWasmNullExternRef);
// Each nullable non-func, non-extern reference type is a supertype of
// nullref.
SUBTYPE_IFF(kWasmNullRef, ref_type,
ref_type.is_nullable() && !is_any_func);
ref_type.is_nullable() && !is_any_func && !is_extern);
// Only nullref is a subtype of nullref.
SUBTYPE_IFF(ref_type, kWasmNullRef, ref_type == kWasmNullRef);
// Only nullable funcs are supertypes of nofunc.
......@@ -348,8 +352,9 @@ TEST_F(WasmSubtypingTest, Subtyping) {
UNION(type, type, type);
INTERSECTION(type, type, type);
if (type == kWasmFuncRef || type == kWasmNullFuncRef || type == ref(11) ||
type == refNull(11)) {
// functypes don't share the same type hierarchy as anyref.
type == refNull(11) || type == kWasmExternRef ||
type == kWasmNullExternRef) {
// func and extern types don't share the same type hierarchy as anyref.
INTERSECTION(type, kWasmAnyRef, kWasmBottom);
continue;
}
......@@ -377,12 +382,30 @@ TEST_F(WasmSubtypingTest, Subtyping) {
kWasmEqRef.AsNonNull());
UNION(kWasmDataRef, kWasmArrayRef, kWasmDataRef);
UNION(kWasmI31Ref.AsNonNull(), kWasmArrayRef, kWasmEqRef);
UNION(kWasmAnyRef, kWasmNullRef, kWasmAnyRef);
UNION(kWasmExternRef, kWasmNullExternRef, kWasmExternRef);
UNION(kWasmFuncRef, kWasmNullFuncRef, kWasmFuncRef);
INTERSECTION(kWasmExternRef, kWasmEqRef, kWasmBottom);
INTERSECTION(kWasmExternRef, kWasmDataRef, kWasmBottom);
INTERSECTION(kWasmExternRef, kWasmI31Ref.AsNonNull(), kWasmBottom);
INTERSECTION(kWasmExternRef, kWasmArrayRef, kWasmBottom);
INTERSECTION(kWasmExternRef, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmExternRef, kWasmFuncRef, kWasmBottom);
INTERSECTION(kWasmNullExternRef, kWasmEqRef, kWasmBottom);
INTERSECTION(kWasmNullExternRef, kWasmDataRef, kWasmBottom);
INTERSECTION(kWasmNullExternRef, kWasmI31Ref, kWasmBottom);
INTERSECTION(kWasmNullExternRef, kWasmArrayRef, kWasmBottom);
INTERSECTION(kWasmNullExternRef, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmNullExternRef, kWasmExternRef, kWasmNullExternRef);
INTERSECTION(kWasmNullExternRef, kWasmExternRef.AsNonNull(), kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmEqRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmDataRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmI31Ref.AsNonNull(), kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmArrayRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmNullExternRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmEqRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmDataRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmI31Ref, kWasmBottom);
......@@ -390,6 +413,8 @@ TEST_F(WasmSubtypingTest, Subtyping) {
INTERSECTION(kWasmNullFuncRef, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmFuncRef, kWasmNullFuncRef);
INTERSECTION(kWasmNullFuncRef, kWasmFuncRef.AsNonNull(), kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmNullExternRef, kWasmBottom);
INTERSECTION(kWasmEqRef, kWasmDataRef, kWasmDataRef);
INTERSECTION(kWasmEqRef, kWasmI31Ref, kWasmI31Ref);
INTERSECTION(kWasmEqRef, kWasmArrayRef, kWasmArrayRef);
......
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