%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/wasm/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/wasm/wasm-subtyping.cc |
// Copyright 2020 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.
#include "src/wasm/wasm-subtyping.h"
#include "src/base/v8-fallthrough.h"
#include "src/wasm/canonical-types.h"
#include "src/wasm/wasm-module.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace {
V8_INLINE bool EquivalentIndices(uint32_t index1, uint32_t index2,
const WasmModule* module1,
const WasmModule* module2) {
DCHECK(index1 != index2 || module1 != module2);
return module1->isorecursive_canonical_type_ids[index1] ==
module2->isorecursive_canonical_type_ids[index2];
}
bool ValidStructSubtypeDefinition(uint32_t subtype_index,
uint32_t supertype_index,
const WasmModule* sub_module,
const WasmModule* super_module) {
const StructType* sub_struct = sub_module->types[subtype_index].struct_type;
const StructType* super_struct =
super_module->types[supertype_index].struct_type;
if (sub_struct->field_count() < super_struct->field_count()) {
return false;
}
for (uint32_t i = 0; i < super_struct->field_count(); i++) {
bool sub_mut = sub_struct->mutability(i);
bool super_mut = super_struct->mutability(i);
if (sub_mut != super_mut ||
(sub_mut &&
!EquivalentTypes(sub_struct->field(i), super_struct->field(i),
sub_module, super_module)) ||
(!sub_mut && !IsSubtypeOf(sub_struct->field(i), super_struct->field(i),
sub_module, super_module))) {
return false;
}
}
return true;
}
bool ValidArraySubtypeDefinition(uint32_t subtype_index,
uint32_t supertype_index,
const WasmModule* sub_module,
const WasmModule* super_module) {
const ArrayType* sub_array = sub_module->types[subtype_index].array_type;
const ArrayType* super_array =
super_module->types[supertype_index].array_type;
bool sub_mut = sub_array->mutability();
bool super_mut = super_array->mutability();
return (sub_mut && super_mut &&
EquivalentTypes(sub_array->element_type(),
super_array->element_type(), sub_module,
super_module)) ||
(!sub_mut && !super_mut &&
IsSubtypeOf(sub_array->element_type(), super_array->element_type(),
sub_module, super_module));
}
bool ValidFunctionSubtypeDefinition(uint32_t subtype_index,
uint32_t supertype_index,
const WasmModule* sub_module,
const WasmModule* super_module) {
const FunctionSig* sub_func = sub_module->types[subtype_index].function_sig;
const FunctionSig* super_func =
super_module->types[supertype_index].function_sig;
if (sub_func->parameter_count() != super_func->parameter_count() ||
sub_func->return_count() != super_func->return_count()) {
return false;
}
for (uint32_t i = 0; i < sub_func->parameter_count(); i++) {
// Contravariance for params.
if (!IsSubtypeOf(super_func->parameters()[i], sub_func->parameters()[i],
super_module, sub_module)) {
return false;
}
}
for (uint32_t i = 0; i < sub_func->return_count(); i++) {
// Covariance for returns.
if (!IsSubtypeOf(sub_func->returns()[i], super_func->returns()[i],
sub_module, super_module)) {
return false;
}
}
return true;
}
HeapType::Representation NullSentinelImpl(HeapType type,
const WasmModule* module) {
switch (type.representation()) {
case HeapType::kI31:
case HeapType::kNone:
case HeapType::kEq:
case HeapType::kStruct:
case HeapType::kArray:
case HeapType::kAny:
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:
return module->has_signature(type.ref_index()) ? HeapType::kNoFunc
: HeapType::kNone;
}
}
bool IsNullSentinel(HeapType type) {
switch (type.representation()) {
case HeapType::kNone:
case HeapType::kNoExtern:
case HeapType::kNoFunc:
return true;
default:
return false;
}
}
} // namespace
bool ValidSubtypeDefinition(uint32_t subtype_index, uint32_t supertype_index,
const WasmModule* sub_module,
const WasmModule* super_module) {
const TypeDefinition& subtype = sub_module->types[subtype_index];
const TypeDefinition& supertype = super_module->types[supertype_index];
if (subtype.kind != supertype.kind) return false;
if (supertype.is_final) return false;
switch (subtype.kind) {
case TypeDefinition::kFunction:
return ValidFunctionSubtypeDefinition(subtype_index, supertype_index,
sub_module, super_module);
case TypeDefinition::kStruct:
return ValidStructSubtypeDefinition(subtype_index, supertype_index,
sub_module, super_module);
case TypeDefinition::kArray:
return ValidArraySubtypeDefinition(subtype_index, supertype_index,
sub_module, super_module);
}
}
V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
ValueType subtype, ValueType supertype, const WasmModule* sub_module,
const WasmModule* super_module) {
DCHECK(subtype != supertype || sub_module != super_module);
switch (subtype.kind()) {
case kI32:
case kI64:
case kF32:
case kF64:
case kS128:
case kI8:
case kI16:
case kVoid:
case kBottom:
return subtype == supertype;
case kRtt:
return supertype.kind() == kRtt &&
EquivalentIndices(subtype.ref_index(), supertype.ref_index(),
sub_module, super_module);
case kRef:
case kRefNull:
break;
}
DCHECK(subtype.is_object_reference());
bool compatible_references = subtype.is_nullable()
? supertype.is_nullable()
: supertype.is_object_reference();
if (!compatible_references) return false;
DCHECK(supertype.is_object_reference());
// Now check that sub_heap and super_heap are subtype-related.
HeapType sub_heap = subtype.heap_type();
HeapType super_heap = supertype.heap_type();
return IsHeapSubtypeOfImpl(sub_heap, super_heap, sub_module, super_module);
}
V8_NOINLINE V8_EXPORT_PRIVATE bool IsHeapSubtypeOfImpl(
HeapType sub_heap, HeapType super_heap, const WasmModule* sub_module,
const WasmModule* super_module) {
switch (sub_heap.representation()) {
case HeapType::kFunc:
return sub_heap == super_heap;
case HeapType::kEq:
return sub_heap == super_heap || super_heap == HeapType::kAny;
case HeapType::kAny:
return super_heap == HeapType::kAny;
case HeapType::kExtern:
return super_heap == HeapType::kExtern;
case HeapType::kI31:
case HeapType::kStruct:
case HeapType::kArray:
return super_heap == sub_heap || super_heap == HeapType::kEq ||
super_heap == HeapType::kAny;
case HeapType::kString:
// stringref is a subtype of anyref.
return sub_heap == super_heap || super_heap == HeapType::kAny;
case HeapType::kStringViewWtf8:
case HeapType::kStringViewWtf16:
case HeapType::kStringViewIter:
return sub_heap == super_heap;
case HeapType::kBottom:
UNREACHABLE();
case HeapType::kNone:
// none is a subtype of every non-func, non-extern reference type under
// wasm-gc.
if (super_heap.is_index()) {
return !super_module->has_signature(super_heap.ref_index());
}
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()) {
return super_module->has_signature(super_heap.ref_index());
}
return super_heap == HeapType::kNoFunc || super_heap == HeapType::kFunc;
default:
break;
}
DCHECK(sub_heap.is_index());
uint32_t sub_index = sub_heap.ref_index();
DCHECK(sub_module->has_type(sub_index));
switch (super_heap.representation()) {
case HeapType::kFunc:
return sub_module->has_signature(sub_index);
case HeapType::kStruct:
return sub_module->has_struct(sub_index);
case HeapType::kEq:
case HeapType::kAny:
return !sub_module->has_signature(sub_index);
case HeapType::kArray:
return sub_module->has_array(sub_index);
case HeapType::kI31:
return false;
case HeapType::kExtern:
return false;
case HeapType::kString:
case HeapType::kStringViewWtf8:
case HeapType::kStringViewWtf16:
case HeapType::kStringViewIter:
return false;
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;
default:
break;
}
DCHECK(super_heap.is_index());
uint32_t super_index = super_heap.ref_index();
DCHECK(super_module->has_type(super_index));
// The {IsSubtypeOf} entry point already has a fast path checking ValueType
// equality; here we catch (ref $x) being a subtype of (ref null $x).
if (sub_module == super_module && sub_index == super_index) return true;
return GetTypeCanonicalizer()->IsCanonicalSubtype(sub_index, super_index,
sub_module, super_module);
}
V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
const WasmModule* module1,
const WasmModule* module2) {
if (type1 == type2 && module1 == module2) return true;
if (!type1.has_index() || !type2.has_index()) return type1 == type2;
if (type1.kind() != type2.kind()) return false;
DCHECK(type1 != type2 || module1 != module2);
DCHECK(type1.has_index() && module1->has_type(type1.ref_index()) &&
type2.has_index() && module2->has_type(type2.ref_index()));
return EquivalentIndices(type1.ref_index(), type2.ref_index(), module1,
module2);
}
namespace {
// Returns the least common ancestor of two type indices, as a type index in
// {module1}.
HeapType::Representation CommonAncestor(uint32_t type_index1,
uint32_t type_index2,
const WasmModule* module1,
const WasmModule* module2) {
TypeDefinition::Kind kind1 = module1->types[type_index1].kind;
TypeDefinition::Kind kind2 = module2->types[type_index2].kind;
{
int depth1 = GetSubtypingDepth(module1, type_index1);
int depth2 = GetSubtypingDepth(module2, type_index2);
while (depth1 > depth2) {
type_index1 = module1->supertype(type_index1);
depth1--;
}
while (depth2 > depth1) {
type_index2 = module2->supertype(type_index2);
depth2--;
}
}
DCHECK_NE(type_index1, kNoSuperType);
DCHECK_NE(type_index2, kNoSuperType);
while (type_index1 != kNoSuperType &&
!(type_index1 == type_index2 && module1 == module2) &&
!EquivalentIndices(type_index1, type_index2, module1, module2)) {
type_index1 = module1->supertype(type_index1);
type_index2 = module2->supertype(type_index2);
}
DCHECK_EQ(type_index1 == kNoSuperType, type_index2 == kNoSuperType);
if (type_index1 != kNoSuperType) {
return static_cast<HeapType::Representation>(type_index1);
}
switch (kind1) {
case TypeDefinition::kFunction:
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:
return HeapType::kBottom;
case TypeDefinition::kStruct:
return HeapType::kStruct;
case TypeDefinition::kArray:
return HeapType::kEq;
}
case TypeDefinition::kArray:
switch (kind2) {
case TypeDefinition::kFunction:
return HeapType::kBottom;
case TypeDefinition::kStruct:
return HeapType::kEq;
case TypeDefinition::kArray:
return HeapType::kArray;
}
}
}
// Returns the least common ancestor of a generic HeapType {heap1}, and
// another HeapType {heap2}.
HeapType::Representation CommonAncestorWithGeneric(HeapType heap1,
HeapType heap2,
const WasmModule* module2) {
DCHECK(heap1.is_generic());
switch (heap1.representation()) {
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: {
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()) {
case HeapType::kI31:
case HeapType::kNone:
return HeapType::kI31;
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::kStruct:
switch (heap2.representation()) {
case HeapType::kStruct:
case HeapType::kNone:
return HeapType::kStruct;
case HeapType::kArray:
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:
case HeapType::kStringViewIter:
case HeapType::kStringViewWtf8:
case HeapType::kStringViewWtf16:
case HeapType::kBottom:
return HeapType::kBottom;
default:
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()) {
case HeapType::kArray:
case HeapType::kNone:
return HeapType::kArray;
case HeapType::kStruct:
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:
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::kBottom:
return HeapType::kBottom;
case HeapType::kNone:
switch (heap2.representation()) {
case HeapType::kArray:
case HeapType::kNone:
case HeapType::kStruct:
case HeapType::kI31:
case HeapType::kEq:
case HeapType::kAny:
case HeapType::kString:
return heap2.representation();
case HeapType::kExtern:
case HeapType::kNoExtern:
case HeapType::kNoFunc:
case 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())
? HeapType::kBottom
: heap2.representation();
}
case HeapType::kNoFunc:
return (heap2 == HeapType::kNoFunc || heap2 == HeapType::kFunc ||
(heap2.is_index() && module2->has_signature(heap2.ref_index())))
? heap2.representation()
: HeapType::kBottom;
case HeapType::kNoExtern:
return heap2 == HeapType::kExtern || heap2 == HeapType::kNoExtern
? heap2.representation()
: HeapType::kBottom;
case HeapType::kExtern:
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();
}
}
} // namespace
V8_EXPORT_PRIVATE TypeInModule Union(ValueType type1, ValueType type2,
const WasmModule* module1,
const WasmModule* module2) {
if (!type1.is_object_reference() || !type2.is_object_reference()) {
return {
EquivalentTypes(type1, type2, module1, module2) ? type1 : kWasmBottom,
module1};
}
Nullability nullability =
type1.is_nullable() || type2.is_nullable() ? kNullable : kNonNullable;
HeapType heap1 = type1.heap_type();
HeapType heap2 = type2.heap_type();
if (heap1 == heap2 && module1 == module2) {
return {ValueType::RefMaybeNull(heap1, nullability), module1};
}
HeapType::Representation result_repr;
const WasmModule* result_module;
if (heap1.is_generic()) {
result_repr = CommonAncestorWithGeneric(heap1, heap2, module2);
result_module = module2;
} else if (heap2.is_generic()) {
result_repr = CommonAncestorWithGeneric(heap2, heap1, module1);
result_module = module1;
} else {
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,
const WasmModule* module1,
const WasmModule* module2) {
if (!type1.is_object_reference() || !type2.is_object_reference()) {
return {
EquivalentTypes(type1, type2, module1, module2) ? type1 : kWasmBottom,
module1};
}
Nullability nullability =
type1.is_nullable() && type2.is_nullable() ? kNullable : kNonNullable;
// non-nullable null type is not a valid type.
if (nullability == kNonNullable && (IsNullSentinel(type1.heap_type()) ||
IsNullSentinel(type2.heap_type()))) {
return {kWasmBottom, module1};
}
if (IsHeapSubtypeOf(type1.heap_type(), type2.heap_type(), module1, module2)) {
return TypeInModule{ValueType::RefMaybeNull(type1.heap_type(), nullability),
module1};
}
if (IsHeapSubtypeOf(type2.heap_type(), type1.heap_type(), module2, module1)) {
return TypeInModule{ValueType::RefMaybeNull(type2.heap_type(), nullability),
module2};
}
if (nullability == kNonNullable) {
return {kWasmBottom, module1};
}
// Check for common null representation.
ValueType null_type1 = ToNullSentinel({type1, module1});
if (null_type1 == ToNullSentinel({type2, module2})) {
return {null_type1, module1};
}
return {kWasmBottom, module1};
}
ValueType ToNullSentinel(TypeInModule type) {
HeapType::Representation null_heap =
NullSentinelImpl(type.type.heap_type(), type.module);
DCHECK(
IsHeapSubtypeOf(HeapType(null_heap), type.type.heap_type(), type.module));
return ValueType::RefNull(null_heap);
}
bool IsSameTypeHierarchy(HeapType type1, HeapType type2,
const WasmModule* module) {
return NullSentinelImpl(type1, module) == NullSentinelImpl(type2, module);
}
} // namespace wasm
} // namespace internal
} // namespace v8