[wasm-gc] Fix and extend type union
Bug: v8:7748 Change-Id: Ia0486dd543bdb2c9eb42899fd57aae22297f8cd2 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4177095 Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Reviewed-by: Matthias Liedtke <mliedtke@chromium.org> Cr-Commit-Position: refs/heads/main@{#85367}
This commit is contained in:
parent
e3c6e32ec0
commit
e9024ad26c
@ -343,12 +343,17 @@ HeapType::Representation CommonAncestor(uint32_t type_index1,
|
||||
}
|
||||
switch (kind1) {
|
||||
case TypeDefinition::kFunction:
|
||||
DCHECK_EQ(kind2, kind1);
|
||||
return HeapType::kFunc;
|
||||
switch (kind2) {
|
||||
case TypeDefinition::kFunction:
|
||||
return HeapType::kFunc;
|
||||
case TypeDefinition::kStruct:
|
||||
case TypeDefinition::kArray:
|
||||
return HeapType::kBottom;
|
||||
}
|
||||
case TypeDefinition::kStruct:
|
||||
switch (kind2) {
|
||||
case TypeDefinition::kFunction:
|
||||
UNREACHABLE();
|
||||
return HeapType::kBottom;
|
||||
case TypeDefinition::kStruct:
|
||||
return HeapType::kStruct;
|
||||
case TypeDefinition::kArray:
|
||||
@ -357,7 +362,7 @@ HeapType::Representation CommonAncestor(uint32_t type_index1,
|
||||
case TypeDefinition::kArray:
|
||||
switch (kind2) {
|
||||
case TypeDefinition::kFunction:
|
||||
UNREACHABLE();
|
||||
return HeapType::kBottom;
|
||||
case TypeDefinition::kStruct:
|
||||
return HeapType::kEq;
|
||||
case TypeDefinition::kArray:
|
||||
@ -368,21 +373,67 @@ HeapType::Representation CommonAncestor(uint32_t type_index1,
|
||||
|
||||
// Returns the least common ancestor of a generic HeapType {heap1}, and
|
||||
// another HeapType {heap2}.
|
||||
// TODO(7748): This function sometimes assumes that incompatible types cannot be
|
||||
// compared, in some cases explicitly and in others implicitly. Make it
|
||||
// consistent.
|
||||
HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
|
||||
HeapType heap2,
|
||||
const WasmModule* module2) {
|
||||
DCHECK(heap1.is_generic());
|
||||
switch (heap1.representation()) {
|
||||
case HeapType::kFunc:
|
||||
DCHECK(IsHeapSubtypeOf(heap2, heap1, module2, module2));
|
||||
return HeapType::kFunc;
|
||||
case HeapType::kFunc: {
|
||||
if (heap2 == HeapType::kFunc || heap2 == HeapType::kNoFunc ||
|
||||
(heap2.is_index() && module2->has_signature(heap2.ref_index()))) {
|
||||
return HeapType::kFunc;
|
||||
} else {
|
||||
return HeapType::kBottom;
|
||||
}
|
||||
}
|
||||
case HeapType::kAny: {
|
||||
switch (heap2.representation()) {
|
||||
case HeapType::kI31:
|
||||
case HeapType::kNone:
|
||||
case HeapType::kEq:
|
||||
case HeapType::kStruct:
|
||||
case HeapType::kArray:
|
||||
case HeapType::kAny:
|
||||
case HeapType::kString:
|
||||
return HeapType::kAny;
|
||||
case HeapType::kFunc:
|
||||
case HeapType::kExtern:
|
||||
case HeapType::kNoExtern:
|
||||
case HeapType::kNoFunc:
|
||||
case HeapType::kStringViewIter:
|
||||
case HeapType::kStringViewWtf8:
|
||||
case HeapType::kStringViewWtf16:
|
||||
case HeapType::kBottom:
|
||||
return HeapType::kBottom;
|
||||
default:
|
||||
return module2->has_signature(heap2.ref_index()) ? HeapType::kBottom
|
||||
: HeapType::kAny;
|
||||
}
|
||||
}
|
||||
case HeapType::kEq: {
|
||||
return IsHeapSubtypeOf(heap2, heap1, module2, module2)
|
||||
? heap1.representation()
|
||||
: HeapType::kAny;
|
||||
switch (heap2.representation()) {
|
||||
case HeapType::kI31:
|
||||
case HeapType::kNone:
|
||||
case HeapType::kEq:
|
||||
case HeapType::kStruct:
|
||||
case HeapType::kArray:
|
||||
return HeapType::kEq;
|
||||
case HeapType::kAny:
|
||||
case HeapType::kString:
|
||||
return HeapType::kAny;
|
||||
case HeapType::kFunc:
|
||||
case HeapType::kExtern:
|
||||
case HeapType::kNoExtern:
|
||||
case HeapType::kNoFunc:
|
||||
case HeapType::kStringViewIter:
|
||||
case HeapType::kStringViewWtf8:
|
||||
case HeapType::kStringViewWtf16:
|
||||
case HeapType::kBottom:
|
||||
return HeapType::kBottom;
|
||||
default:
|
||||
return module2->has_signature(heap2.ref_index()) ? HeapType::kBottom
|
||||
: HeapType::kEq;
|
||||
}
|
||||
}
|
||||
case HeapType::kI31:
|
||||
switch (heap2.representation()) {
|
||||
@ -394,12 +445,17 @@ HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
|
||||
case HeapType::kArray:
|
||||
return HeapType::kEq;
|
||||
case HeapType::kAny:
|
||||
case HeapType::kString:
|
||||
return HeapType::kAny;
|
||||
case HeapType::kFunc:
|
||||
case HeapType::kExtern:
|
||||
case HeapType::kNoExtern:
|
||||
case HeapType::kNoFunc:
|
||||
UNREACHABLE();
|
||||
case HeapType::kStringViewIter:
|
||||
case HeapType::kStringViewWtf8:
|
||||
case HeapType::kStringViewWtf16:
|
||||
case HeapType::kBottom:
|
||||
return HeapType::kBottom;
|
||||
default:
|
||||
return module2->has_signature(heap2.ref_index()) ? HeapType::kBottom
|
||||
: HeapType::kEq;
|
||||
@ -410,21 +466,25 @@ HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
|
||||
case HeapType::kNone:
|
||||
return HeapType::kStruct;
|
||||
case HeapType::kArray:
|
||||
return HeapType::kEq;
|
||||
case HeapType::kI31:
|
||||
case HeapType::kEq:
|
||||
return HeapType::kEq;
|
||||
case HeapType::kAny:
|
||||
case HeapType::kString:
|
||||
return HeapType::kAny;
|
||||
case HeapType::kFunc:
|
||||
case HeapType::kExtern:
|
||||
case HeapType::kNoExtern:
|
||||
case HeapType::kNoFunc:
|
||||
UNREACHABLE();
|
||||
case HeapType::kStringViewIter:
|
||||
case HeapType::kStringViewWtf8:
|
||||
case HeapType::kStringViewWtf16:
|
||||
case HeapType::kBottom:
|
||||
return HeapType::kBottom;
|
||||
default:
|
||||
return module2->has_signature(heap2.ref_index()) ? HeapType::kBottom
|
||||
: module2->has_struct(heap2.ref_index()) ? HeapType::kStruct
|
||||
: HeapType::kEq;
|
||||
return module2->has_struct(heap2.ref_index()) ? HeapType::kStruct
|
||||
: module2->has_array(heap2.ref_index()) ? HeapType::kEq
|
||||
: HeapType::kBottom;
|
||||
}
|
||||
case HeapType::kArray:
|
||||
switch (heap2.representation()) {
|
||||
@ -432,29 +492,29 @@ HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
|
||||
case HeapType::kNone:
|
||||
return HeapType::kArray;
|
||||
case HeapType::kStruct:
|
||||
return HeapType::kEq;
|
||||
case HeapType::kI31:
|
||||
case HeapType::kEq:
|
||||
return HeapType::kEq;
|
||||
case HeapType::kAny:
|
||||
case HeapType::kString:
|
||||
return HeapType::kAny;
|
||||
case HeapType::kFunc:
|
||||
case HeapType::kExtern:
|
||||
case HeapType::kNoExtern:
|
||||
case HeapType::kNoFunc:
|
||||
UNREACHABLE();
|
||||
case HeapType::kStringViewIter:
|
||||
case HeapType::kStringViewWtf8:
|
||||
case HeapType::kStringViewWtf16:
|
||||
case HeapType::kBottom:
|
||||
return HeapType::kBottom;
|
||||
default:
|
||||
return module2->has_array(heap2.ref_index()) ? HeapType::kArray
|
||||
: module2->has_struct(heap2.ref_index()) ? HeapType::kEq
|
||||
: HeapType::kBottom;
|
||||
}
|
||||
case HeapType::kAny:
|
||||
return HeapType::kAny;
|
||||
case HeapType::kBottom:
|
||||
return HeapType::kBottom;
|
||||
case HeapType::kNone:
|
||||
return heap2.representation();
|
||||
case HeapType::kNoFunc:
|
||||
switch (heap2.representation()) {
|
||||
case HeapType::kArray:
|
||||
case HeapType::kNone:
|
||||
@ -462,25 +522,63 @@ HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
|
||||
case HeapType::kI31:
|
||||
case HeapType::kEq:
|
||||
case HeapType::kAny:
|
||||
case HeapType::kString:
|
||||
return heap2.representation();
|
||||
case HeapType::kExtern:
|
||||
case HeapType::kNoExtern:
|
||||
UNREACHABLE();
|
||||
case HeapType::kNoFunc:
|
||||
return HeapType::kNoFunc;
|
||||
case HeapType::kFunc:
|
||||
return HeapType::kFunc;
|
||||
case HeapType::kStringViewIter:
|
||||
case HeapType::kStringViewWtf8:
|
||||
case HeapType::kStringViewWtf16:
|
||||
case HeapType::kBottom:
|
||||
return HeapType::kBottom;
|
||||
default:
|
||||
return module2->has_signature(heap2.ref_index())
|
||||
? heap2.representation()
|
||||
: HeapType::kBottom;
|
||||
? HeapType::kBottom
|
||||
: heap2.representation();
|
||||
}
|
||||
case HeapType::kNoFunc:
|
||||
return (heap2 == HeapType::kNoFunc || heap2 == HeapType::kFunc ||
|
||||
module2->has_signature(heap2.ref_index()))
|
||||
? heap2.representation()
|
||||
: HeapType::kBottom;
|
||||
case HeapType::kNoExtern:
|
||||
return heap2.representation() == HeapType::kExtern ? HeapType::kExtern
|
||||
: HeapType::kNoExtern;
|
||||
return heap2 == HeapType::kExtern || heap2 == HeapType::kNoExtern
|
||||
? heap2.representation()
|
||||
: HeapType::kBottom;
|
||||
case HeapType::kExtern:
|
||||
return HeapType::kExtern;
|
||||
case HeapType::kString:
|
||||
return heap2 == HeapType::kExtern || heap2 == HeapType::kNoExtern
|
||||
? HeapType::kExtern
|
||||
: HeapType::kBottom;
|
||||
case HeapType::kString: {
|
||||
switch (heap2.representation()) {
|
||||
case HeapType::kI31:
|
||||
case HeapType::kEq:
|
||||
case HeapType::kStruct:
|
||||
case HeapType::kArray:
|
||||
case HeapType::kAny:
|
||||
return HeapType::kAny;
|
||||
case HeapType::kNone:
|
||||
case HeapType::kString:
|
||||
return HeapType::kString;
|
||||
case HeapType::kFunc:
|
||||
case HeapType::kExtern:
|
||||
case HeapType::kNoExtern:
|
||||
case HeapType::kNoFunc:
|
||||
case HeapType::kStringViewIter:
|
||||
case HeapType::kStringViewWtf8:
|
||||
case HeapType::kStringViewWtf16:
|
||||
case HeapType::kBottom:
|
||||
return HeapType::kBottom;
|
||||
default:
|
||||
return module2->has_signature(heap2.ref_index()) ? HeapType::kBottom
|
||||
: HeapType::kAny;
|
||||
}
|
||||
}
|
||||
case HeapType::kStringViewIter:
|
||||
case HeapType::kStringViewWtf16:
|
||||
case HeapType::kStringViewWtf8:
|
||||
return heap1 == heap2 ? heap1.representation() : HeapType::kBottom;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
@ -503,21 +601,23 @@ V8_EXPORT_PRIVATE TypeInModule Union(ValueType type1, ValueType type2,
|
||||
if (heap1 == heap2 && module1 == module2) {
|
||||
return {ValueType::RefMaybeNull(heap1, nullability), module1};
|
||||
}
|
||||
HeapType::Representation result_repr;
|
||||
const WasmModule* result_module;
|
||||
if (heap1.is_generic()) {
|
||||
return {ValueType::RefMaybeNull(
|
||||
CommonAncestorWithGeneric(heap1, heap2, module2), nullability),
|
||||
module1};
|
||||
result_repr = CommonAncestorWithGeneric(heap1, heap2, module2);
|
||||
result_module = module2;
|
||||
} else if (heap2.is_generic()) {
|
||||
return {ValueType::RefMaybeNull(
|
||||
CommonAncestorWithGeneric(heap2, heap1, module1), nullability),
|
||||
module1};
|
||||
result_repr = CommonAncestorWithGeneric(heap2, heap1, module1);
|
||||
result_module = module1;
|
||||
} else {
|
||||
return {ValueType::RefMaybeNull(
|
||||
CommonAncestor(heap1.ref_index(), heap2.ref_index(), module1,
|
||||
module2),
|
||||
nullability),
|
||||
module1};
|
||||
result_repr =
|
||||
CommonAncestor(heap1.ref_index(), heap2.ref_index(), module1, module2);
|
||||
result_module = module1;
|
||||
}
|
||||
return {result_repr == HeapType::kBottom
|
||||
? kWasmBottom
|
||||
: ValueType::RefMaybeNull(result_repr, nullability),
|
||||
result_module};
|
||||
}
|
||||
|
||||
TypeInModule Intersection(ValueType type1, ValueType type2,
|
||||
|
@ -49,14 +49,14 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool EquivalentTypes(ValueType type1,
|
||||
// For heap types, the following subtyping rules hold:
|
||||
// - The abstract heap types form the following type hierarchies:
|
||||
//
|
||||
// any func extern
|
||||
// | | |
|
||||
// eq nofunc noextern
|
||||
// / | \
|
||||
// i31 array struct
|
||||
// \ | /
|
||||
// \ | /
|
||||
// none
|
||||
// any func extern
|
||||
// / \ | |
|
||||
// eq \ nofunc noextern
|
||||
// / | \ \
|
||||
// i31 array struct string
|
||||
// \___|______|_____/
|
||||
// |
|
||||
// none
|
||||
//
|
||||
// - All functions are subtypes of func.
|
||||
// - All structs are subtypes of struct.
|
||||
@ -147,6 +147,7 @@ inline std::ostream& operator<<(std::ostream& oss, TypeInModule type) {
|
||||
<< reinterpret_cast<intptr_t>(type.module);
|
||||
}
|
||||
|
||||
// Returns {kWasmBottom} if the union of {type1} and {type2} is not defined.
|
||||
V8_EXPORT_PRIVATE TypeInModule Union(ValueType type1, ValueType type2,
|
||||
const WasmModule* module1,
|
||||
const WasmModule* module2);
|
||||
|
@ -170,12 +170,13 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
||||
#define DISTINCT(index1, index2) \
|
||||
EXPECT_FALSE(EquivalentTypes(ValueType::RefNull(index1), \
|
||||
ValueType::RefNull(index2), module1, module));
|
||||
// Union always expresses the result in terms of module1.
|
||||
#define UNION(type1, type2, type_result) \
|
||||
EXPECT_EQ(Union(type1, type2, module1, module), \
|
||||
TypeInModule(type_result, module1))
|
||||
// Intersection might return either module, so we have a version which checks
|
||||
// the module and one which deos not.
|
||||
// For union and intersection, we have a version that also checks the module,
|
||||
// and one that does not.
|
||||
#define UNION(type1, type2, type_result) \
|
||||
EXPECT_EQ(Union(type1, type2, module1, module).type, type_result)
|
||||
#define UNION_M(type1, type2, type_result, module_result) \
|
||||
EXPECT_EQ(Union(type1, type2, module1, module), \
|
||||
TypeInModule(type_result, module_result))
|
||||
#define INTERSECTION(type1, type2, type_result) \
|
||||
EXPECT_EQ(Intersection(type1, type2, module1, module).type, type_result)
|
||||
#define INTERSECTION_M(type1, type2, type_result, module_result) \
|
||||
@ -409,6 +410,20 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
||||
UNION(kWasmAnyRef, kWasmNullRef, kWasmAnyRef);
|
||||
UNION(kWasmExternRef, kWasmNullExternRef, kWasmExternRef);
|
||||
UNION(kWasmFuncRef, kWasmNullFuncRef, kWasmFuncRef);
|
||||
UNION(kWasmFuncRef, kWasmStructRef, kWasmBottom);
|
||||
UNION(kWasmFuncRef, kWasmArrayRef, kWasmBottom);
|
||||
UNION(kWasmFuncRef, kWasmAnyRef, kWasmBottom);
|
||||
UNION(kWasmFuncRef, kWasmEqRef, kWasmBottom);
|
||||
UNION(kWasmStringRef, kWasmAnyRef, kWasmAnyRef);
|
||||
UNION(kWasmStringRef, kWasmStructRef, kWasmAnyRef);
|
||||
UNION(kWasmStringRef, kWasmArrayRef, kWasmAnyRef);
|
||||
UNION(kWasmStringRef, kWasmFuncRef, kWasmBottom);
|
||||
UNION(kWasmStringViewIter, kWasmStringRef, kWasmBottom);
|
||||
UNION(kWasmStringViewWtf8, kWasmStringRef, kWasmBottom);
|
||||
UNION(kWasmStringViewWtf16, kWasmStringRef, kWasmBottom);
|
||||
UNION(kWasmStringViewIter, kWasmAnyRef, kWasmBottom);
|
||||
UNION(kWasmStringViewWtf8, kWasmAnyRef, kWasmBottom);
|
||||
UNION(kWasmStringViewWtf16, kWasmAnyRef, kWasmBottom);
|
||||
|
||||
INTERSECTION(kWasmExternRef, kWasmEqRef, kWasmBottom);
|
||||
INTERSECTION(kWasmExternRef, kWasmStructRef, kWasmBottom);
|
||||
@ -457,11 +472,15 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
||||
|
||||
// Abstract vs indexed types.
|
||||
UNION(kWasmFuncRef, function_type, kWasmFuncRef);
|
||||
UNION(kWasmFuncRef, struct_type, kWasmBottom);
|
||||
UNION(kWasmFuncRef, array_type, kWasmBottom);
|
||||
INTERSECTION(kWasmFuncRef, struct_type, kWasmBottom);
|
||||
INTERSECTION(kWasmFuncRef, array_type, kWasmBottom);
|
||||
INTERSECTION(kWasmFuncRef, function_type, function_type);
|
||||
INTERSECTION_M(kWasmFuncRef, function_type, function_type, module);
|
||||
|
||||
UNION(kWasmNullFuncRef, function_type, function_type.AsNullable());
|
||||
UNION(kWasmNullFuncRef, struct_type, kWasmBottom);
|
||||
UNION(kWasmNullFuncRef, array_type, kWasmBottom);
|
||||
INTERSECTION(kWasmNullFuncRef, struct_type, kWasmBottom);
|
||||
INTERSECTION(kWasmNullFuncRef, struct_type.AsNullable(), kWasmBottom);
|
||||
INTERSECTION(kWasmNullFuncRef, array_type, kWasmBottom);
|
||||
@ -478,7 +497,8 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
||||
|
||||
UNION(kWasmStructRef, struct_type, kWasmStructRef);
|
||||
UNION(kWasmStructRef, array_type, kWasmEqRef);
|
||||
INTERSECTION(kWasmStructRef, struct_type, struct_type);
|
||||
UNION(kWasmStructRef, function_type, kWasmBottom);
|
||||
INTERSECTION_M(kWasmStructRef, struct_type, struct_type, module);
|
||||
INTERSECTION(kWasmStructRef, array_type, kWasmBottom);
|
||||
INTERSECTION(kWasmStructRef, function_type, kWasmBottom);
|
||||
|
||||
@ -490,17 +510,22 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
||||
|
||||
UNION(kWasmArrayRef, struct_type, kWasmEqRef);
|
||||
UNION(kWasmArrayRef, array_type, kWasmArrayRef);
|
||||
UNION(kWasmArrayRef, function_type, kWasmBottom);
|
||||
INTERSECTION(kWasmArrayRef, struct_type, kWasmBottom);
|
||||
INTERSECTION(kWasmArrayRef, array_type, array_type);
|
||||
INTERSECTION_M(kWasmArrayRef, array_type, array_type, module);
|
||||
INTERSECTION(kWasmArrayRef, function_type, kWasmBottom);
|
||||
|
||||
UNION(kWasmNullRef, struct_type, struct_type.AsNullable());
|
||||
UNION(kWasmNullRef, array_type, array_type.AsNullable());
|
||||
UNION(kWasmNullRef, function_type, function_type.AsNullable());
|
||||
UNION_M(kWasmNullRef, struct_type, struct_type.AsNullable(), module);
|
||||
UNION_M(kWasmNullRef, array_type, array_type.AsNullable(), module);
|
||||
UNION(kWasmNullRef, function_type, kWasmBottom);
|
||||
INTERSECTION(kWasmNullRef, struct_type, kWasmBottom);
|
||||
INTERSECTION(kWasmNullRef, array_type, kWasmBottom);
|
||||
INTERSECTION(kWasmNullRef, function_type, kWasmBottom);
|
||||
|
||||
UNION(struct_type, kWasmStringRef, kWasmAnyRef);
|
||||
UNION(array_type, kWasmStringRef, kWasmAnyRef);
|
||||
UNION(function_type, kWasmStringRef, kWasmBottom);
|
||||
|
||||
// Indexed types of different kinds.
|
||||
UNION(struct_type, array_type, kWasmEqRef.AsNonNull());
|
||||
INTERSECTION(struct_type, array_type, kWasmBottom);
|
||||
@ -516,11 +541,11 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
||||
|
||||
// Concrete types of the same kind.
|
||||
// Subtyping relation.
|
||||
UNION(refNull(4), ref(1), refNull(1));
|
||||
UNION_M(refNull(4), ref(1), refNull(1), module1);
|
||||
INTERSECTION_M(refNull(4), ref(1), ref(4), module1);
|
||||
INTERSECTION_M(refNull(1), refNull(4), refNull(4), module);
|
||||
// Common ancestor.
|
||||
UNION(ref(4), ref(31), ref(1));
|
||||
UNION_M(ref(4), ref(31), ref(1), module1);
|
||||
INTERSECTION(ref(4), ref(31), kWasmBottom);
|
||||
// No common ancestor.
|
||||
UNION(ref(6), refNull(2), kWasmArrayRef.AsNullable());
|
||||
@ -538,6 +563,7 @@ TEST_F(WasmSubtypingTest, Subtyping) {
|
||||
#undef IDENTICAL
|
||||
#undef DISTINCT
|
||||
#undef UNION
|
||||
#undef UNION_M
|
||||
#undef INTERSECTION
|
||||
#undef INTERSECTION_M
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user