[wasm-gc] Implement anyref

Bug: v8:7748
Change-Id: I5d0cc06fafbe7fc05549a4b8fd7f602eaf838bba
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2526382
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71283}
This commit is contained in:
Manos Koukoutos 2020-11-19 14:51:14 +00:00 committed by Commit Bot
parent b0295b8c65
commit ba5fa195ed
19 changed files with 382 additions and 154 deletions

View File

@ -5665,31 +5665,34 @@ Node* WasmGraphBuilder::ArrayNewWithRtt(uint32_t array_index,
}
Node* WasmGraphBuilder::RttCanon(wasm::HeapType type) {
if (type.is_generic()) {
switch (type.representation()) {
case wasm::HeapType::kEq:
return LOAD_FULL_POINTER(
BuildLoadIsolateRoot(),
IsolateData::root_slot_offset(RootIndex::kWasmRttEqrefMap));
case wasm::HeapType::kExtern:
return LOAD_FULL_POINTER(
BuildLoadIsolateRoot(),
IsolateData::root_slot_offset(RootIndex::kWasmRttExternrefMap));
case wasm::HeapType::kFunc:
return LOAD_FULL_POINTER(
BuildLoadIsolateRoot(),
IsolateData::root_slot_offset(RootIndex::kWasmRttFuncrefMap));
case wasm::HeapType::kI31:
return LOAD_FULL_POINTER(
BuildLoadIsolateRoot(),
IsolateData::root_slot_offset(RootIndex::kWasmRttI31refMap));
default:
UNREACHABLE();
RootIndex index;
switch (type.representation()) {
case wasm::HeapType::kEq:
index = RootIndex::kWasmRttEqrefMap;
break;
case wasm::HeapType::kExtern:
index = RootIndex::kWasmRttExternrefMap;
break;
case wasm::HeapType::kFunc:
index = RootIndex::kWasmRttFuncrefMap;
break;
case wasm::HeapType::kI31:
index = RootIndex::kWasmRttI31refMap;
break;
case wasm::HeapType::kAny:
index = RootIndex::kWasmRttAnyrefMap;
break;
case wasm::HeapType::kBottom:
UNREACHABLE();
default: {
// User-defined type.
Node* maps_list =
LOAD_INSTANCE_FIELD(ManagedObjectMaps, MachineType::TaggedPointer());
return LOAD_FIXED_ARRAY_SLOT_PTR(maps_list, type.ref_index());
}
}
Node* maps_list =
LOAD_INSTANCE_FIELD(ManagedObjectMaps, MachineType::TaggedPointer());
return LOAD_FIXED_ARRAY_SLOT_PTR(maps_list, type.ref_index());
return LOAD_FULL_POINTER(BuildLoadIsolateRoot(),
IsolateData::root_slot_offset(index));
}
Node* WasmGraphBuilder::RttSub(wasm::HeapType type, Node* parent_rtt) {
@ -5731,6 +5734,7 @@ Node* WasmGraphBuilder::RefTest(Node* object, Node* rtt,
Node* map = gasm_->LoadMap(object);
gasm_->GotoIf(gasm_->TaggedEqual(map, rtt), &done, gasm_->Int32Constant(1));
if (!config.object_must_be_data_ref) {
gasm_->GotoIfNot(gasm_->IsDataRefMap(map), &done, gasm_->Int32Constant(0));
}
@ -6235,12 +6239,17 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
if (representation == wasm::HeapType::kEq) {
return BuildAllocateObjectWrapper(node);
}
if (representation == wasm::HeapType::kAny) {
// TODO(7748): Add wrapping for arrays/structs, or proper JS API when
// available.
return node;
}
if (type.has_index() && module_->has_signature(type.ref_index())) {
// Typed function
return node;
}
// TODO(7748): Figure out a JS interop story for arrays and structs.
// If this is reached, then IsJSCompatibleSignature() is too permissive.
// TODO(7748): Figure out a JS interop story for arrays and structs.
UNREACHABLE();
}
case wasm::ValueType::kRtt:
@ -6354,6 +6363,9 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
switch (type.heap_representation()) {
case wasm::HeapType::kExtern:
case wasm::HeapType::kExn:
// TODO(7748): Possibly implement different cases for 'any' when the
// JS API has settled.
case wasm::HeapType::kAny:
return input;
case wasm::HeapType::kFunc:
BuildCheckValidRefValue(input, js_context, type);

View File

@ -511,6 +511,7 @@ bool Heap::CreateInitialMaps() {
ALLOCATE_MAP(WASM_STRUCT_TYPE, 0, wasm_rttcanon_externref)
ALLOCATE_MAP(WASM_STRUCT_TYPE, 0, wasm_rttcanon_funcref)
ALLOCATE_MAP(WASM_STRUCT_TYPE, 0, wasm_rttcanon_i31ref)
ALLOCATE_MAP(WASM_STRUCT_TYPE, 0, wasm_rttcanon_anyref)
ALLOCATE_MAP(WASM_TYPE_INFO_TYPE, WasmTypeInfo::kSize, wasm_type_info)
ALLOCATE_MAP(WEAK_CELL_TYPE, WeakCell::kSize, weak_cell)
@ -611,28 +612,65 @@ bool Heap::CreateInitialMaps() {
}
// Set up the WasmTypeInfo objects for built-in generic Wasm RTTs.
#define ALLOCATE_TYPE_INFO(which) \
{ \
int slot_count = ArrayList::kHeaderFields; \
if (!AllocateRaw(ArrayList::SizeFor(slot_count), AllocationType::kOld) \
.To(&obj)) { \
return false; \
} \
obj.set_map_after_allocation(roots.array_list_map()); \
ArrayList subtypes = ArrayList::cast(obj); \
subtypes.set_length(slot_count); \
subtypes.SetLength(0); \
if (!AllocateRaw(WasmTypeInfo::kSize, AllocationType::kOld).To(&obj)) { \
return false; \
} \
obj.set_map_after_allocation(roots.wasm_type_info_map(), \
SKIP_WRITE_BARRIER); \
WasmTypeInfo type_info = WasmTypeInfo::cast(obj); \
type_info.set_subtypes(subtypes); \
type_info.set_supertypes(roots.empty_fixed_array()); \
type_info.set_parent(roots.null_map()); \
type_info.clear_foreign_address(isolate()); \
wasm_rttcanon_##which##_map().set_wasm_type_info(type_info); \
// anyref:
{
/* Subtypes. We do not cache subtypes for (rtt.canon any). */
int slot_count = ArrayList::kHeaderFields;
if (!AllocateRaw(ArrayList::SizeFor(slot_count), AllocationType::kOld)
.To(&obj)) {
return false;
}
obj.set_map_after_allocation(roots.array_list_map());
ArrayList subtypes = ArrayList::cast(obj);
subtypes.set_length(slot_count);
subtypes.SetLength(0);
/* TypeInfo */
if (!AllocateRaw(WasmTypeInfo::kSize, AllocationType::kOld).To(&obj)) {
return false;
}
obj.set_map_after_allocation(roots.wasm_type_info_map(),
SKIP_WRITE_BARRIER);
WasmTypeInfo type_info = WasmTypeInfo::cast(obj);
type_info.set_subtypes(subtypes);
type_info.set_supertypes(roots.empty_fixed_array());
type_info.set_parent(roots.null_map());
type_info.clear_foreign_address(isolate());
wasm_rttcanon_anyref_map().set_wasm_type_info(type_info);
}
// Rest of builtin types:
#define ALLOCATE_TYPE_INFO(which) \
{ \
/* Subtypes */ \
int slot_count = ArrayList::kHeaderFields; \
if (!AllocateRaw(ArrayList::SizeFor(slot_count), AllocationType::kOld) \
.To(&obj)) { \
return false; \
} \
obj.set_map_after_allocation(roots.array_list_map()); \
ArrayList subtypes = ArrayList::cast(obj); \
subtypes.set_length(slot_count); \
subtypes.SetLength(0); \
/* Supertypes */ \
if (!AllocateRaw(FixedArray::SizeFor(1), AllocationType::kOld).To(&obj)) { \
return false; \
} \
obj.set_map_after_allocation(roots.fixed_array_map(), SKIP_WRITE_BARRIER); \
FixedArray supertypes = FixedArray::cast(obj); \
supertypes.set_length(1); \
supertypes.set(0, wasm_rttcanon_anyref_map()); \
/* TypeInfo */ \
if (!AllocateRaw(WasmTypeInfo::kSize, AllocationType::kOld).To(&obj)) { \
return false; \
} \
obj.set_map_after_allocation(roots.wasm_type_info_map(), \
SKIP_WRITE_BARRIER); \
WasmTypeInfo type_info = WasmTypeInfo::cast(obj); \
type_info.set_subtypes(subtypes); \
type_info.set_supertypes(supertypes); \
type_info.set_parent(wasm_rttcanon_anyref_map()); \
type_info.clear_foreign_address(isolate()); \
wasm_rttcanon_##which##_map().set_wasm_type_info(type_info); \
}
ALLOCATE_TYPE_INFO(eqref)

View File

@ -205,6 +205,7 @@ class Symbol;
V(Map, wasm_rttcanon_externref_map, WasmRttExternrefMap) \
V(Map, wasm_rttcanon_funcref_map, WasmRttFuncrefMap) \
V(Map, wasm_rttcanon_i31ref_map, WasmRttI31refMap) \
V(Map, wasm_rttcanon_anyref_map, WasmRttAnyrefMap) \
/* Canonical empty values */ \
V(Script, empty_script, EmptyScript) \
V(FeedbackCell, many_closures_cell, ManyClosuresCell) \

View File

@ -187,7 +187,9 @@ V8_INLINE WasmFeature feature_for_heap_type(HeapType heap_type) {
return WasmFeature::kFeature_eh;
case HeapType::kEq:
case HeapType::kI31:
case HeapType::kAny:
return WasmFeature::kFeature_gc;
case HeapType::kBottom:
default:
UNREACHABLE();
}
@ -211,7 +213,8 @@ HeapType read_heap_type(Decoder* decoder, const byte* pc,
case kExnRefCode:
case kEqRefCode:
case kExternRefCode:
case kI31RefCode: {
case kI31RefCode:
case kAnyRefCode: {
HeapType result = HeapType::from_code(code);
if (!VALIDATE(enabled.contains(feature_for_heap_type(result)))) {
DecodeError<validate>(
@ -269,7 +272,8 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
case kExnRefCode:
case kEqRefCode:
case kExternRefCode:
case kI31RefCode: {
case kI31RefCode:
case kAnyRefCode: {
HeapType heap_type = HeapType::from_code(code);
ValueType result = ValueType::Ref(
heap_type, code == kI31RefCode ? kNonNullable : kNullable);
@ -3875,7 +3879,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
HeapTypeImmediate<validate> imm(this->enabled_, this,
this->pc_ + opcode_length);
if (!this->Validate(this->pc_ + opcode_length, imm)) return 0;
Value* value = Push(ValueType::Rtt(imm.type, 1));
Value* value =
Push(ValueType::Rtt(imm.type, imm.type == HeapType::kAny ? 0 : 1));
CALL_INTERFACE_IF_REACHABLE(RttCanon, imm, value);
return opcode_length + imm.length;
}
@ -3905,7 +3910,16 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
Value* value =
Push(ValueType::Rtt(imm.type, parent.type.depth() + 1));
CALL_INTERFACE_IF_REACHABLE(RttSub, imm, parent, value);
// (rtt.sub $t (rtt.canon any)) is reduced to (rtt.canon $t),
// unless t == any.
// This is important because other canonical rtts are not cached in
// (rtt.canon any)'s subtype list.
if (parent.type == ValueType::Rtt(HeapType::kAny, 0) &&
imm.type != HeapType::kAny) {
CALL_INTERFACE_IF_REACHABLE(RttCanon, imm, value);
} else {
CALL_INTERFACE_IF_REACHABLE(RttSub, imm, parent, value);
}
}
return opcode_length + imm.length;
}

View File

@ -1358,10 +1358,10 @@ class ModuleDecoderImpl : public Decoder {
}
case WasmInitExpr::kRefNullConst:
return ValueType::Ref(expr.immediate().heap_type, kNullable);
case WasmInitExpr::kRttCanon:
// TODO(7748): If heaptype is "anyref" (not introduced yet),
// then this should be uint8_t{0}.
return ValueType::Rtt(expr.immediate().heap_type, uint8_t{1});
case WasmInitExpr::kRttCanon: {
uint8_t depth = expr.immediate().heap_type == HeapType::kAny ? 0 : 1;
return ValueType::Rtt(expr.immediate().heap_type, depth);
}
case WasmInitExpr::kRttSub: {
ValueType operand_type = TypeOf(*expr.operand());
if (operand_type.is_rtt()) {

View File

@ -603,24 +603,24 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
//--------------------------------------------------------------------------
// Create maps for managed objects (GC proposal).
// Must happen before {InitGlobals} because globals can refer to these maps.
// We do not need to cache the canonical rtts to (rtt.canon any)'s subtype
// list.
//--------------------------------------------------------------------------
if (enabled_.has_gc()) {
Handle<FixedArray> maps = isolate_->factory()->NewUninitializedFixedArray(
static_cast<int>(module_->type_kinds.size()));
// TODO(7748): Do we want a different sentinel here?
Handle<Map> anyref_sentinel_map = isolate_->factory()->null_map();
Handle<Map> anyref_map =
Handle<Map>::cast(isolate_->root_handle(RootIndex::kWasmRttAnyrefMap));
for (int map_index = 0;
map_index < static_cast<int>(module_->type_kinds.size());
map_index++) {
Handle<Map> map;
switch (module_->type_kinds[map_index]) {
case kWasmStructTypeCode:
map = CreateStructMap(isolate_, module_, map_index,
anyref_sentinel_map);
map = CreateStructMap(isolate_, module_, map_index, anyref_map);
break;
case kWasmArrayTypeCode:
map =
CreateArrayMap(isolate_, module_, map_index, anyref_sentinel_map);
map = CreateArrayMap(isolate_, module_, map_index, anyref_map);
break;
case kWasmFunctionTypeCode:
// TODO(7748): Think about canonicalizing rtts to make them work for
@ -1599,6 +1599,8 @@ Handle<Object> InstanceBuilder::RecursivelyEvaluateGlobalInitializer(
return isolate_->root_handle(RootIndex::kWasmRttFuncrefMap);
case wasm::HeapType::kI31:
return isolate_->root_handle(RootIndex::kWasmRttI31refMap);
case wasm::HeapType::kAny:
return isolate_->root_handle(RootIndex::kWasmRttAnyrefMap);
case wasm::HeapType::kExn:
UNIMPLEMENTED(); // TODO(jkummerow): This is going away?
case wasm::HeapType::kBottom:

View File

@ -57,6 +57,7 @@ class HeapType {
kEq, // shorthand: q
kExn, // shorthand: x
kI31, // shorthand: j
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.
kBottom
@ -64,7 +65,7 @@ class HeapType {
// Internal use only; defined in the public section to make it easy to
// check that they are defined correctly:
static constexpr Representation kFirstSentinel = kFunc;
static constexpr Representation kLastSentinel = kI31;
static constexpr Representation kLastSentinel = kAny;
static constexpr HeapType from_code(uint8_t code) {
switch (code) {
@ -78,6 +79,8 @@ class HeapType {
return HeapType(kExn);
case ValueTypeCode::kI31RefCode:
return HeapType(kI31);
case ValueTypeCode::kAnyRefCode:
return HeapType(kAny);
default:
return HeapType(kBottom);
}
@ -130,6 +133,8 @@ class HeapType {
return std::string("exn");
case kI31:
return std::string("i31");
case kAny:
return std::string("any");
default:
return std::to_string(representation_);
}
@ -151,6 +156,8 @@ class HeapType {
return mask | kEqRefCode;
case kI31:
return mask | kI31RefCode;
case kAny:
return mask | kAnyRefCode;
default:
return static_cast<int32_t>(representation_);
}
@ -366,6 +373,8 @@ class ValueType {
return kEqRefCode;
case HeapType::kExn:
return kExnRefCode;
case HeapType::kAny:
return kAnyRefCode;
default:
return kOptRefCode;
}
@ -503,6 +512,7 @@ constexpr ValueType kWasmExternRef =
ValueType::Ref(HeapType::kExtern, kNullable);
constexpr ValueType kWasmEqRef = ValueType::Ref(HeapType::kEq, kNullable);
constexpr ValueType kWasmI31Ref = ValueType::Ref(HeapType::kI31, kNonNullable);
constexpr ValueType kWasmAnyRef = ValueType::Ref(HeapType::kAny, kNullable);
#define FOREACH_WASMVALUE_CTYPES(V) \
V(kI32, int32_t) \

View File

@ -33,7 +33,7 @@ enum ValueTypeCode : uint8_t {
kI16Code = 0x79,
kFuncRefCode = 0x70,
kExternRefCode = 0x6f,
// kAnyCode = 0x6e, // TODO(7748): Implement
kAnyRefCode = 0x6e,
kEqRefCode = 0x6d,
kOptRefCode = 0x6c,
kRefCode = 0x6b,

View File

@ -1351,7 +1351,8 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
case i::wasm::ValueType::kOptRef: {
switch (type.heap_representation()) {
case i::wasm::HeapType::kExtern:
case i::wasm::HeapType::kExn: {
case i::wasm::HeapType::kExn:
case i::wasm::HeapType::kAny: {
if (args.Length() < 2) {
// When no initial value is provided, we have to use the WebAssembly
// default value 'null', and not the JS default value 'undefined'.
@ -1851,6 +1852,7 @@ void WebAssemblyGlobalGetValueCommon(
case i::wasm::HeapType::kExtern:
case i::wasm::HeapType::kFunc:
case i::wasm::HeapType::kExn:
case i::wasm::HeapType::kAny:
return_value.Set(Utils::ToLocal(receiver->GetRef()));
break;
case i::wasm::HeapType::kEq:
@ -1939,6 +1941,7 @@ void WebAssemblyGlobalSetValue(
switch (receiver->type().heap_representation()) {
case i::wasm::HeapType::kExtern:
case i::wasm::HeapType::kExn:
case i::wasm::HeapType::kAny:
receiver->SetExternRef(Utils::OpenHandle(*args[0]));
break;
case i::wasm::HeapType::kFunc: {

View File

@ -189,9 +189,9 @@ void WasmGlobalObject::SetF64(double value) {
}
void WasmGlobalObject::SetExternRef(Handle<Object> value) {
// We use this getter externref and exnref.
DCHECK(type().is_reference_to(wasm::HeapType::kExtern) ||
type().is_reference_to(wasm::HeapType::kExn));
type().is_reference_to(wasm::HeapType::kExn) ||
type().is_reference_to(wasm::HeapType::kAny));
tagged_buffer().set(offset(), *value);
}

View File

@ -446,6 +446,7 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
switch (table->type().heap_representation()) {
case wasm::HeapType::kExtern:
case wasm::HeapType::kExn:
case wasm::HeapType::kAny:
entries->set(entry_index, *entry);
return;
case wasm::HeapType::kFunc:
@ -500,6 +501,7 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate,
break;
case wasm::HeapType::kEq:
case wasm::HeapType::kI31:
case wasm::HeapType::kAny:
// TODO(7748): Implement once we have a story for struct/arrays/i31ref in
// JS.
UNIMPLEMENTED();
@ -2155,6 +2157,7 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
}
case HeapType::kExtern:
case HeapType::kExn:
case HeapType::kAny:
return true;
case HeapType::kEq: {
// TODO(7748): Change this when we have a decision on the JS API for

View File

@ -269,29 +269,26 @@ bool FunctionIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
} // namespace
// TODO(7748): Extend this with any-heap subtyping.
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);
// This function checks for subtyping based on the kind of subtype.
if (!subtype.is_reference_type()) return subtype == supertype;
if (subtype.kind() == ValueType::kRtt) {
if (subtype.is_rtt()) {
return subtype.heap_type().is_generic()
? subtype == supertype
: (supertype.kind() == ValueType::kRtt &&
subtype.depth() == supertype.depth() &&
: (supertype.is_rtt() && subtype.depth() == supertype.depth() &&
supertype.has_index() &&
EquivalentIndices(subtype.ref_index(), supertype.ref_index(),
sub_module, super_module));
}
DCHECK(subtype.is_object_reference_type());
bool compatible_references = subtype.kind() == ValueType::kOptRef
? supertype.kind() == ValueType::kOptRef
bool compatible_references = subtype.is_nullable()
? supertype.is_nullable()
: supertype.is_object_reference_type();
if (!compatible_references) return false;
@ -302,23 +299,43 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
HeapType sub_heap = subtype.heap_type();
HeapType super_heap = supertype.heap_type();
if (sub_heap.representation() == HeapType::kI31 &&
super_heap.representation() == HeapType::kEq) {
return true;
switch (sub_heap.representation()) {
case HeapType::kFunc:
case HeapType::kExtern:
case HeapType::kEq:
case HeapType::kExn:
return sub_heap == super_heap || super_heap == HeapType::kAny;
case HeapType::kAny:
return super_heap == HeapType::kAny;
case HeapType::kI31:
return super_heap == HeapType::kI31 || super_heap == HeapType::kEq ||
super_heap == HeapType::kAny;
case HeapType::kBottom:
UNREACHABLE();
default:
break;
}
if (sub_heap.is_generic()) return sub_heap == super_heap;
DCHECK(sub_heap.is_index());
uint32_t sub_index = sub_heap.ref_index();
DCHECK(sub_module->has_type(sub_index));
if (super_heap.representation() == HeapType::kEq) {
return !sub_module->has_signature(sub_heap.ref_index());
switch (super_heap.representation()) {
case HeapType::kFunc:
return sub_module->has_signature(sub_index);
case HeapType::kEq:
return !sub_module->has_signature(sub_index);
case HeapType::kExtern:
case HeapType::kExn:
case HeapType::kI31:
return false;
case HeapType::kAny:
return true;
case HeapType::kBottom:
UNREACHABLE();
default:
break;
}
if (super_heap.representation() == HeapType::kFunc) {
return sub_module->has_signature(sub_heap.ref_index());
}
if (super_heap.is_generic()) return false;
DCHECK(super_heap.is_index());
uint32_t super_index = super_heap.ref_index();
@ -358,7 +375,7 @@ V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
DCHECK(type1.has_index() && type2.has_index() &&
(type1 != type2 || module1 != module2));
DCHECK_IMPLIES(type1.has_depth(), type2.has_depth()); // Due to 'if' above
DCHECK_IMPLIES(type1.has_depth(), type2.has_depth()); // Due to 'if' above.
if (type1.has_depth() && type1.depth() != type2.depth()) return false;
DCHECK(type1.has_index() && module1->has_type(type1.ref_index()) &&

View File

@ -21,16 +21,16 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
// module2.
// Type equivalence (~) is described by the following rules (structural
// equivalence):
// - Two numeric types are equivalent if they are equal.
// - Two numeric types are equivalent iff they are equal.
// - optref(ht1) ~ optref(ht2) iff ht1 ~ ht2.
// - ref(ht1) ~ ref(ht2) iff ht1 ~ ht2.
// - rtt(d1, ht1) ~ rtt(d2, ht2) iff (d1 = d2 and ht1 ~ ht2).
// For heap types, the following rules hold:
// - 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.
// these are pairwise equivalent.
// - Two functions are equivalent iff they contain the same number of parameters
// and returns and these are pairwise equivalent.
// and returns and these are pairwise equivalent.
// - Two arrays are equivalent iff their underlying types are equivalent.
V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
const WasmModule* module1,
@ -46,6 +46,7 @@ V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
// - rtt1 <: rtt2 iff rtt1 ~ rtt2.
// For heap types, the following subtyping rules hold:
// - Each generic heap type is a subtype of itself.
// - All heap types are subtypes of any.
// - All functions are subtypes of func.
// - i31, structs and arrays are subtypes of eq.
// - Struct subtyping: Subtype must have at least as many fields as supertype,

View File

@ -853,6 +853,80 @@ TEST(BasicRTT) {
tester.CheckResult(kRefCast, 43);
}
TEST(AnyRefRtt) {
WasmGCTester tester;
ValueType any_rtt_0_type = ValueType::Rtt(HeapType::kAny, 0);
FunctionSig sig_any_canon(1, 0, &any_rtt_0_type);
byte kAnyRttCanon = tester.DefineFunction(
&sig_any_canon, {}, {WASM_RTT_CANON(kAnyRefCode), kExprEnd});
ValueType any_rtt_1_type = ValueType::Rtt(HeapType::kAny, 1);
FunctionSig sig_any_sub(1, 0, &any_rtt_1_type);
byte kAnyRttSub = tester.DefineFunction(
&sig_any_sub, {},
{WASM_RTT_SUB(kAnyRefCode, WASM_RTT_CANON(kAnyRefCode)), kExprEnd});
ValueType func_rtt_1_type = ValueType::Rtt(HeapType::kFunc, 1);
FunctionSig sig_func_sub(1, 0, &func_rtt_1_type);
byte kFuncRttSub = tester.DefineFunction(
&sig_func_sub, {},
{WASM_RTT_SUB(kFuncRefCode, WASM_RTT_CANON(kAnyRefCode)), kExprEnd});
ValueType eq_rtt_1_type = ValueType::Rtt(HeapType::kEq, 1);
FunctionSig sig_eq_sub(1, 0, &eq_rtt_1_type);
byte kEqRttSub = tester.DefineFunction(
&sig_eq_sub, {},
{WASM_RTT_SUB(kEqRefCode, WASM_RTT_CANON(kAnyRefCode)), kExprEnd});
const byte type_index = tester.DefineArray(kWasmI32, true);
ValueType array_rtt_type = ValueType::Rtt(type_index, 1);
FunctionSig sig_array_canon(1, 0, &array_rtt_type);
byte kArrayRttCanon = tester.DefineFunction(
&sig_array_canon, {}, {WASM_RTT_CANON(type_index), kExprEnd});
byte kCheckArrayAgainstAny = tester.DefineFunction(
tester.sigs.i_v(), {kWasmAnyRef},
{WASM_SET_LOCAL(0, WASM_ARRAY_NEW_DEFAULT(type_index, WASM_I32V(5),
WASM_RTT_CANON(type_index))),
WASM_REF_TEST(kAnyRefCode, type_index, WASM_GET_LOCAL(0),
WASM_RTT_CANON(type_index)),
kExprEnd});
byte kCheckAnyAgainstAny = tester.DefineFunction(
tester.sigs.i_v(), {kWasmAnyRef},
{WASM_SET_LOCAL(0, WASM_ARRAY_NEW_DEFAULT(type_index, WASM_I32V(5),
WASM_RTT_CANON(type_index))),
WASM_REF_TEST(kAnyRefCode, kAnyRefCode, WASM_GET_LOCAL(0),
WASM_RTT_CANON(kAnyRefCode)),
kExprEnd});
tester.CompileModule();
// Check (rtt.canon any).
Handle<Object> result_any_canon =
tester.GetResultObject(kAnyRttCanon).ToHandleChecked();
CHECK(result_any_canon->IsMap());
Handle<Map> any_map = Handle<Map>::cast(result_any_canon);
CHECK_EQ(any_map->wasm_type_info().parent(),
tester.isolate()->root(RootIndex::kNullMap));
CHECK_EQ(any_map->wasm_type_info().supertypes().length(), 0);
for (byte func_index : {kArrayRttCanon, kAnyRttSub, kFuncRttSub, kEqRttSub}) {
Handle<Object> result =
tester.GetResultObject(func_index).ToHandleChecked();
CHECK(result->IsMap());
Handle<Map> map = Handle<Map>::cast(result);
// Its parent should be (rtt.canon any).
CHECK_EQ(map->wasm_type_info().parent(), *any_map);
CHECK_EQ(map->wasm_type_info().supertypes().get(0), *any_map);
CHECK_EQ(map->wasm_type_info().supertypes().length(), 1);
}
tester.CheckResult(kCheckArrayAgainstAny, 1);
tester.CheckResult(kCheckAnyAgainstAny, 1);
}
TEST(ArrayNewMap) {
WasmGCTester tester;
const byte type_index = tester.DefineArray(kWasmI32, true);

View File

@ -3045,7 +3045,8 @@ class WasmInterpreterInternals {
switch (sig->GetParam(i).heap_representation()) {
case HeapType::kExtern:
case HeapType::kExn:
case HeapType::kFunc: {
case HeapType::kFunc:
case HeapType::kAny: {
Handle<Object> externref = value.to_externref();
encoded_values->set(encoded_index++, *externref);
break;
@ -3162,7 +3163,8 @@ class WasmInterpreterInternals {
switch (sig->GetParam(i).heap_representation()) {
case HeapType::kExtern:
case HeapType::kExn:
case HeapType::kFunc: {
case HeapType::kFunc:
case HeapType::kAny: {
Handle<Object> externref(encoded_values->get(encoded_index++),
isolate_);
value = WasmValue(externref);

View File

@ -133,7 +133,11 @@ const char* ValueTypeToConstantName(ValueType type) {
return "kWasmFuncRef";
case HeapType::kExn:
return "kWasmExnRef";
case HeapType::kAny:
case HeapType::kI31:
case HeapType::kBottom:
default:
// TODO(7748): Implement these if fuzzing for them is enabled.
UNREACHABLE();
}
default:

View File

@ -3617,6 +3617,7 @@ TEST_F(FunctionBodyDecoderTest, RefEq) {
kWasmExnRef,
kWasmExternRef,
kWasmFuncRef,
kWasmAnyRef,
ValueType::Ref(HeapType::kExn, kNonNullable),
ValueType::Ref(HeapType::kExtern, kNonNullable),
ValueType::Ref(HeapType::kFunc, kNonNullable)};
@ -3655,7 +3656,7 @@ TEST_F(FunctionBodyDecoderTest, RefAsNonNull) {
byte array_type_index = builder.AddArray(kWasmI32, true);
uint32_t heap_types[] = {
struct_type_index, array_type_index, HeapType::kExn, HeapType::kFunc,
HeapType::kEq, HeapType::kExtern, HeapType::kI31};
HeapType::kEq, HeapType::kExtern, HeapType::kI31, HeapType::kAny};
ValueType non_compatible_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
kWasmS128};
@ -3698,7 +3699,7 @@ TEST_F(FunctionBodyDecoderTest, RefNull) {
byte array_type_index = builder.AddArray(kWasmI32, true);
uint32_t type_reprs[] = {
struct_type_index, array_type_index, HeapType::kExn, HeapType::kFunc,
HeapType::kEq, HeapType::kExtern, HeapType::kI31};
HeapType::kEq, HeapType::kExtern, HeapType::kI31, HeapType::kAny};
// It works with heap types.
for (uint32_t type_repr : type_reprs) {
const ValueType type = ValueType::Ref(type_repr, kNullable);
@ -3728,7 +3729,7 @@ TEST_F(FunctionBodyDecoderTest, RefIsNull) {
byte array_type_index = builder.AddArray(kWasmI32, true);
uint32_t heap_types[] = {
struct_type_index, array_type_index, HeapType::kExn, HeapType::kFunc,
HeapType::kEq, HeapType::kExtern, HeapType::kI31};
HeapType::kEq, HeapType::kExtern, HeapType::kI31, HeapType::kAny};
for (uint32_t heap_type : heap_types) {
const ValueType types[] = {kWasmI32, ValueType::Ref(heap_type, kNullable)};
@ -4148,13 +4149,16 @@ TEST_F(FunctionBodyDecoderTest, RttCanon) {
for (HeapType::Representation heap :
{HeapType::kExtern, HeapType::kEq, HeapType::kExn, HeapType::kI31,
static_cast<HeapType::Representation>(array_type_index),
HeapType::kAny, static_cast<HeapType::Representation>(array_type_index),
static_cast<HeapType::Representation>(struct_type_index)}) {
ValueType rtt1 = ValueType::Rtt(HeapType(heap), 1);
ValueType rtt1 =
ValueType::Rtt(HeapType(heap), heap == HeapType::kAny ? 0 : 1);
FunctionSig sig1(1, 0, &rtt1);
ExpectValidates(&sig1, {WASM_RTT_CANON(rtt1.heap_type().code() & 0x7F)});
ValueType rtt2 = ValueType::Rtt(HeapType(heap), 2);
// rtt.canon should fail for incorrect depth.
ValueType rtt2 =
ValueType::Rtt(HeapType(heap), heap == HeapType::kAny ? 1 : 2);
FunctionSig sig2(1, 0, &rtt2);
ExpectFailure(&sig2, {WASM_RTT_CANON(rtt2.heap_type().code() & 0x7F)},
kAppendEnd, "type error in merge[0]");
@ -4182,6 +4186,43 @@ TEST_F(FunctionBodyDecoderTest, RttSub) {
{WASM_RTT_SUB(kFuncRefCode, WASM_RTT_CANON(kFuncRefCode))});
}
{
// Can build an rtt.sub from a generic type with itself.
ValueType type = ValueType::Rtt(HeapType::kAny, 1);
FunctionSig sig(1, 0, &type);
ExpectValidates(&sig,
{WASM_RTT_SUB(kAnyRefCode, WASM_RTT_CANON(kAnyRefCode))});
}
// Can build an rtt.sub between related generic types.
{
ValueType type = ValueType::Rtt(HeapType::kFunc, 1);
FunctionSig sig(1, 0, &type);
ExpectValidates(&sig,
{WASM_RTT_SUB(kFuncRefCode, WASM_RTT_CANON(kAnyRefCode))});
}
{
ValueType type = ValueType::Rtt(HeapType::kEq, 1);
FunctionSig sig(1, 0, &type);
ExpectValidates(&sig,
{WASM_RTT_SUB(kEqRefCode, WASM_RTT_CANON(kAnyRefCode))});
}
{
ValueType type = ValueType::Rtt(HeapType::kI31, 2);
FunctionSig sig(1, 0, &type);
ExpectValidates(&sig,
{WASM_RTT_SUB(kI31RefCode, WASM_RTT_CANON(kEqRefCode))});
}
// Cannot build an rtt.sub between unrelated generic types.
{
ValueType type = ValueType::Rtt(HeapType::kFunc, 2);
FunctionSig sig(1, 0, &type);
ExpectFailure(&sig,
{WASM_RTT_SUB(kFuncRefCode, WASM_RTT_CANON(kI31RefCode))},
kAppendEnd, "rtt.sub requires a supertype rtt on stack");
}
// Trivial type error.
ExpectFailure(sigs.v_v(),
{WASM_RTT_SUB(kFuncRefCode, WASM_I32V(42)), kExprDrop},
@ -4254,10 +4295,10 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
// Passing/failing tests due to static subtyping.
std::pair<HeapType::Representation, HeapType::Representation> valid_pairs[] =
{{HeapType::kEq, HeapType::kI31},
{HeapType::kFunc, HeapType::kFunc},
{HeapType::kEq, array_heap},
{HeapType::kEq, super_struct_heap},
{{HeapType::kAny, HeapType::kI31}, {HeapType::kAny, HeapType::kFunc},
{HeapType::kAny, array_heap}, {HeapType::kAny, super_struct_heap},
{HeapType::kEq, HeapType::kI31}, {HeapType::kFunc, HeapType::kFunc},
{HeapType::kEq, array_heap}, {HeapType::kEq, super_struct_heap},
{super_struct_heap, sub_struct_heap}};
for (auto pair : valid_pairs) {
@ -4279,10 +4320,10 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
}
std::pair<HeapType::Representation, HeapType::Representation>
invalid_pairs[] = {{HeapType::kI31, HeapType::kEq},
{array_heap, super_struct_heap},
{array_heap, HeapType::kEq},
{HeapType::kExtern, HeapType::kExn}};
invalid_pairs[] = {
{array_heap, HeapType::kAny}, {HeapType::kEq, HeapType::kAny},
{HeapType::kI31, HeapType::kEq}, {array_heap, super_struct_heap},
{array_heap, HeapType::kEq}, {HeapType::kExtern, HeapType::kExn}};
for (auto pair : invalid_pairs) {
HeapType from_heap = HeapType(pair.first);

View File

@ -57,9 +57,9 @@ TEST_F(WasmSubtypingTest, Subtyping) {
ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
kWasmS128};
ValueType ref_types[] = {kWasmExternRef, kWasmFuncRef, kWasmExnRef,
kWasmEqRef, kWasmI31Ref, optRef(0),
ref(0), optRef(2), ref(2)};
ValueType ref_types[] = {
kWasmExternRef, kWasmFuncRef, kWasmExnRef, kWasmEqRef, kWasmI31Ref,
kWasmAnyRef, optRef(0), ref(0), optRef(2), ref(2)};
// Type judgements across modules should work the same as within one module.
for (WasmModule* module : {module1, module2}) {
@ -81,12 +81,17 @@ TEST_F(WasmSubtypingTest, Subtyping) {
for (ValueType ref_type : ref_types) {
// Concrete reference types and i31ref are subtypes of eqref,
// exnref/externref/funcref are not.
// exnref/externref/funcref/anyref are not.
CHECK_EQ(IsSubtypeOf(ref_type, kWasmEqRef, module1, module),
ref_type != kWasmFuncRef && ref_type != kWasmExternRef &&
ref_type != kWasmExnRef);
ref_type != kWasmExnRef && ref_type != kWasmAnyRef);
// Each reference type is a subtype of itself.
CHECK(IsSubtypeOf(ref_type, ref_type, module1, module));
// Each reference type is a subtype of anyref.
CHECK(IsSubtypeOf(ref_type, kWasmAnyRef, module1, module));
// Only anyref is a subtype of anyref.
CHECK_EQ(IsSubtypeOf(kWasmAnyRef, ref_type, module1, module),
ref_type == kWasmAnyRef);
}
// The rest of ref. types are unrelated.

View File

@ -362,9 +362,10 @@ KNOWN_MAPS = {
("map_space", 0x02115): (1057, "ExternalMap"),
("map_space", 0x0213d): (1072, "JSMessageObjectMap"),
("map_space", 0x02165): (182, "WasmRttEqrefMap"),
("map_space", 0x0218d): (182, "WasmRttExternrefMap"),
("map_space", 0x021b5): (182, "WasmRttFuncrefMap"),
("map_space", 0x021dd): (182, "WasmRttI31refMap"),
("map_space", 0x0218d): (182, "WasmRttAnyrefMap"),
("map_space", 0x021b5): (182, "WasmRttExternrefMap"),
("map_space", 0x021dd): (182, "WasmRttFuncrefMap"),
("map_space", 0x02205): (182, "WasmRttI31refMap"),
}
# List of known V8 objects.
@ -427,49 +428,49 @@ KNOWN_OBJECTS = {
("old_space", 0x023bd): "RegExpResultIndicesAccessor",
("old_space", 0x02401): "StringLengthAccessor",
("old_space", 0x02445): "InvalidPrototypeValidityCell",
("old_space", 0x024dd): "EmptyScript",
("old_space", 0x0251d): "ManyClosuresCell",
("old_space", 0x02529): "ArrayConstructorProtector",
("old_space", 0x0253d): "NoElementsProtector",
("old_space", 0x02551): "IsConcatSpreadableProtector",
("old_space", 0x02565): "ArraySpeciesProtector",
("old_space", 0x02579): "TypedArraySpeciesProtector",
("old_space", 0x0258d): "PromiseSpeciesProtector",
("old_space", 0x025a1): "RegExpSpeciesProtector",
("old_space", 0x025b5): "StringLengthProtector",
("old_space", 0x025c9): "ArrayIteratorProtector",
("old_space", 0x025dd): "ArrayBufferDetachingProtector",
("old_space", 0x025f1): "PromiseHookProtector",
("old_space", 0x02605): "PromiseResolveProtector",
("old_space", 0x02619): "MapIteratorProtector",
("old_space", 0x0262d): "PromiseThenProtector",
("old_space", 0x02641): "SetIteratorProtector",
("old_space", 0x02655): "StringIteratorProtector",
("old_space", 0x02669): "SingleCharacterStringCache",
("old_space", 0x02a71): "StringSplitCache",
("old_space", 0x02e79): "RegExpMultipleCache",
("old_space", 0x03281): "BuiltinsConstantsTable",
("old_space", 0x03669): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x03691): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x036b9): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x036e1): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x03709): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x03731): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x03759): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x03781): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x037a9): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x037d1): "PromiseAllResolveElementSharedFun",
("old_space", 0x037f9): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x03821): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x03849): "PromiseAnyRejectElementSharedFun",
("old_space", 0x03871): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x03899): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x038c1): "PromiseCatchFinallySharedFun",
("old_space", 0x038e9): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x03911): "PromiseThenFinallySharedFun",
("old_space", 0x03939): "PromiseThrowerFinallySharedFun",
("old_space", 0x03961): "PromiseValueThunkFinallySharedFun",
("old_space", 0x03989): "ProxyRevokeSharedFun",
("old_space", 0x02531): "EmptyScript",
("old_space", 0x02571): "ManyClosuresCell",
("old_space", 0x0257d): "ArrayConstructorProtector",
("old_space", 0x02591): "NoElementsProtector",
("old_space", 0x025a5): "IsConcatSpreadableProtector",
("old_space", 0x025b9): "ArraySpeciesProtector",
("old_space", 0x025cd): "TypedArraySpeciesProtector",
("old_space", 0x025e1): "PromiseSpeciesProtector",
("old_space", 0x025f5): "RegExpSpeciesProtector",
("old_space", 0x02609): "StringLengthProtector",
("old_space", 0x0261d): "ArrayIteratorProtector",
("old_space", 0x02631): "ArrayBufferDetachingProtector",
("old_space", 0x02645): "PromiseHookProtector",
("old_space", 0x02659): "PromiseResolveProtector",
("old_space", 0x0266d): "MapIteratorProtector",
("old_space", 0x02681): "PromiseThenProtector",
("old_space", 0x02695): "SetIteratorProtector",
("old_space", 0x026a9): "StringIteratorProtector",
("old_space", 0x026bd): "SingleCharacterStringCache",
("old_space", 0x02ac5): "StringSplitCache",
("old_space", 0x02ecd): "RegExpMultipleCache",
("old_space", 0x032d5): "BuiltinsConstantsTable",
("old_space", 0x036bd): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x036e5): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x0370d): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x03735): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x0375d): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x03785): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x037ad): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x037d5): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x037fd): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x03825): "PromiseAllResolveElementSharedFun",
("old_space", 0x0384d): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x03875): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x0389d): "PromiseAnyRejectElementSharedFun",
("old_space", 0x038c5): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x038ed): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x03915): "PromiseCatchFinallySharedFun",
("old_space", 0x0393d): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x03965): "PromiseThenFinallySharedFun",
("old_space", 0x0398d): "PromiseThrowerFinallySharedFun",
("old_space", 0x039b5): "PromiseValueThunkFinallySharedFun",
("old_space", 0x039dd): "ProxyRevokeSharedFun",
}
# Lower 32 bits of first page addresses for various heap spaces.