From ba5fa195ed8d334f7f7fbfb1b02f5fd75f13779d Mon Sep 17 00:00:00 2001 From: Manos Koukoutos Date: Thu, 19 Nov 2020 14:51:14 +0000 Subject: [PATCH] [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 Reviewed-by: Ulan Degenbaev Reviewed-by: Jakob Kummerow Cr-Commit-Position: refs/heads/master@{#71283} --- src/compiler/wasm-compiler.cc | 60 +++++++----- src/heap/setup-heap-internal.cc | 82 +++++++++++----- src/roots/roots.h | 1 + src/wasm/function-body-decoder-impl.h | 22 ++++- src/wasm/module-decoder.cc | 8 +- src/wasm/module-instantiate.cc | 14 +-- src/wasm/value-type.h | 12 ++- src/wasm/wasm-constants.h | 2 +- src/wasm/wasm-js.cc | 5 +- src/wasm/wasm-objects-inl.h | 4 +- src/wasm/wasm-objects.cc | 3 + src/wasm/wasm-subtyping.cc | 55 +++++++---- src/wasm/wasm-subtyping.h | 7 +- test/cctest/wasm/test-gc.cc | 74 +++++++++++++++ test/common/wasm/wasm-interpreter.cc | 6 +- test/fuzzer/wasm-fuzzer-common.cc | 4 + .../wasm/function-body-decoder-unittest.cc | 69 +++++++++++--- test/unittests/wasm/subtyping-unittest.cc | 15 ++- tools/v8heapconst.py | 93 ++++++++++--------- 19 files changed, 382 insertions(+), 154 deletions(-) diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc index 2799e42044..99b53320ac 100644 --- a/src/compiler/wasm-compiler.cc +++ b/src/compiler/wasm-compiler.cc @@ -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); diff --git a/src/heap/setup-heap-internal.cc b/src/heap/setup-heap-internal.cc index 1f7e8a896f..27ee2f3fa2 100644 --- a/src/heap/setup-heap-internal.cc +++ b/src/heap/setup-heap-internal.cc @@ -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) diff --git a/src/roots/roots.h b/src/roots/roots.h index b33c166df0..e9f33ca1fe 100644 --- a/src/roots/roots.h +++ b/src/roots/roots.h @@ -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) \ diff --git a/src/wasm/function-body-decoder-impl.h b/src/wasm/function-body-decoder-impl.h index ab75b34dde..ff271322ba 100644 --- a/src/wasm/function-body-decoder-impl.h +++ b/src/wasm/function-body-decoder-impl.h @@ -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( @@ -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 { HeapTypeImmediate 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 { } 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; } diff --git a/src/wasm/module-decoder.cc b/src/wasm/module-decoder.cc index 2797334c0a..90c26629dd 100644 --- a/src/wasm/module-decoder.cc +++ b/src/wasm/module-decoder.cc @@ -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()) { diff --git a/src/wasm/module-instantiate.cc b/src/wasm/module-instantiate.cc index 09e3f89b46..dc7e830dad 100644 --- a/src/wasm/module-instantiate.cc +++ b/src/wasm/module-instantiate.cc @@ -603,24 +603,24 @@ MaybeHandle 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 maps = isolate_->factory()->NewUninitializedFixedArray( static_cast(module_->type_kinds.size())); - // TODO(7748): Do we want a different sentinel here? - Handle anyref_sentinel_map = isolate_->factory()->null_map(); + Handle anyref_map = + Handle::cast(isolate_->root_handle(RootIndex::kWasmRttAnyrefMap)); for (int map_index = 0; map_index < static_cast(module_->type_kinds.size()); map_index++) { Handle 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 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: diff --git a/src/wasm/value-type.h b/src/wasm/value-type.h index 7f8484e2dd..c6b21a78fd 100644 --- a/src/wasm/value-type.h +++ b/src/wasm/value-type.h @@ -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(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) \ diff --git a/src/wasm/wasm-constants.h b/src/wasm/wasm-constants.h index 0fc3d4051a..2c47e994f2 100644 --- a/src/wasm/wasm-constants.h +++ b/src/wasm/wasm-constants.h @@ -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, diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc index 87cd733d86..eddc580b2d 100644 --- a/src/wasm/wasm-js.cc +++ b/src/wasm/wasm-js.cc @@ -1351,7 +1351,8 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo& 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: { diff --git a/src/wasm/wasm-objects-inl.h b/src/wasm/wasm-objects-inl.h index 45265dee45..cb16a03548 100644 --- a/src/wasm/wasm-objects-inl.h +++ b/src/wasm/wasm-objects-inl.h @@ -189,9 +189,9 @@ void WasmGlobalObject::SetF64(double value) { } void WasmGlobalObject::SetExternRef(Handle 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); } diff --git a/src/wasm/wasm-objects.cc b/src/wasm/wasm-objects.cc index f83fb89c94..0e4c1dfd89 100644 --- a/src/wasm/wasm-objects.cc +++ b/src/wasm/wasm-objects.cc @@ -446,6 +446,7 @@ void WasmTableObject::Set(Isolate* isolate, Handle 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 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 diff --git a/src/wasm/wasm-subtyping.cc b/src/wasm/wasm-subtyping.cc index 630c1aa628..861114b54c 100644 --- a/src/wasm/wasm-subtyping.cc +++ b/src/wasm/wasm-subtyping.cc @@ -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()) && diff --git a/src/wasm/wasm-subtyping.h b/src/wasm/wasm-subtyping.h index bc3fe9e834..c0a527cdae 100644 --- a/src/wasm/wasm-subtyping.h +++ b/src/wasm/wasm-subtyping.h @@ -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, diff --git a/test/cctest/wasm/test-gc.cc b/test/cctest/wasm/test-gc.cc index c1a9ca0f8a..f609effacb 100644 --- a/test/cctest/wasm/test-gc.cc +++ b/test/cctest/wasm/test-gc.cc @@ -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 result_any_canon = + tester.GetResultObject(kAnyRttCanon).ToHandleChecked(); + CHECK(result_any_canon->IsMap()); + Handle any_map = Handle::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 result = + tester.GetResultObject(func_index).ToHandleChecked(); + CHECK(result->IsMap()); + Handle map = Handle::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); diff --git a/test/common/wasm/wasm-interpreter.cc b/test/common/wasm/wasm-interpreter.cc index cdbcff8b41..3dd0e05d39 100644 --- a/test/common/wasm/wasm-interpreter.cc +++ b/test/common/wasm/wasm-interpreter.cc @@ -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 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 externref(encoded_values->get(encoded_index++), isolate_); value = WasmValue(externref); diff --git a/test/fuzzer/wasm-fuzzer-common.cc b/test/fuzzer/wasm-fuzzer-common.cc index bcdba055a2..7e3c608afb 100644 --- a/test/fuzzer/wasm-fuzzer-common.cc +++ b/test/fuzzer/wasm-fuzzer-common.cc @@ -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: diff --git a/test/unittests/wasm/function-body-decoder-unittest.cc b/test/unittests/wasm/function-body-decoder-unittest.cc index 459a20e497..823073183a 100644 --- a/test/unittests/wasm/function-body-decoder-unittest.cc +++ b/test/unittests/wasm/function-body-decoder-unittest.cc @@ -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(array_type_index), + HeapType::kAny, static_cast(array_type_index), static_cast(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 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 - 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); diff --git a/test/unittests/wasm/subtyping-unittest.cc b/test/unittests/wasm/subtyping-unittest.cc index 5b43924493..ec6e30ac75 100644 --- a/test/unittests/wasm/subtyping-unittest.cc +++ b/test/unittests/wasm/subtyping-unittest.cc @@ -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. diff --git a/tools/v8heapconst.py b/tools/v8heapconst.py index f7fcf86325..e588ece58f 100644 --- a/tools/v8heapconst.py +++ b/tools/v8heapconst.py @@ -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.