diff options
Diffstat (limited to 'deps/v8/src/compiler/access-info.cc')
-rw-r--r-- | deps/v8/src/compiler/access-info.cc | 749 |
1 files changed, 389 insertions, 360 deletions
diff --git a/deps/v8/src/compiler/access-info.cc b/deps/v8/src/compiler/access-info.cc index b9e9293235..21f453f4d8 100644 --- a/deps/v8/src/compiler/access-info.cc +++ b/deps/v8/src/compiler/access-info.cc @@ -28,7 +28,7 @@ namespace compiler { namespace { -bool CanInlinePropertyAccess(Handle<Map> map, AccessMode access_mode) { +bool CanInlinePropertyAccess(MapRef map, AccessMode access_mode) { // We can inline property access to prototypes of all primitives, except // the special Oddball ones that have no wrapper counterparts (i.e. Null, // Undefined and TheHole). @@ -37,16 +37,17 @@ bool CanInlinePropertyAccess(Handle<Map> map, AccessMode access_mode) { // relationship between the map and the object (and therefore the property // dictionary). STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_HEAP_OBJECT_TYPE); - if (map->IsBooleanMap()) return true; - if (map->instance_type() < LAST_PRIMITIVE_HEAP_OBJECT_TYPE) return true; - if (map->IsJSObjectMap()) { - if (map->is_dictionary_map()) { + if (map.object()->IsBooleanMap()) return true; + if (map.instance_type() < LAST_PRIMITIVE_HEAP_OBJECT_TYPE) return true; + if (map.object()->IsJSObjectMap()) { + if (map.is_dictionary_map()) { if (!V8_DICT_PROPERTY_CONST_TRACKING_BOOL) return false; - return access_mode == AccessMode::kLoad && map->is_prototype_map(); + return access_mode == AccessMode::kLoad && + map.object()->is_prototype_map(); } - return !map->has_named_interceptor() && + return !map.object()->has_named_interceptor() && // TODO(verwaest): Allowlist contexts to which we have access. - !map->is_access_check_needed(); + !map.is_access_check_needed(); } return false; } @@ -82,8 +83,8 @@ std::ostream& operator<<(std::ostream& os, AccessMode access_mode) { } ElementAccessInfo::ElementAccessInfo( - ZoneVector<Handle<Map>>&& lookup_start_object_maps, - ElementsKind elements_kind, Zone* zone) + ZoneVector<MapRef>&& lookup_start_object_maps, ElementsKind elements_kind, + Zone* zone) : elements_kind_(elements_kind), lookup_start_object_maps_(lookup_start_object_maps), transition_sources_(zone) { @@ -96,22 +97,25 @@ PropertyAccessInfo PropertyAccessInfo::Invalid(Zone* zone) { } // static -PropertyAccessInfo PropertyAccessInfo::NotFound(Zone* zone, - Handle<Map> receiver_map, - MaybeHandle<JSObject> holder) { +PropertyAccessInfo PropertyAccessInfo::NotFound( + Zone* zone, MapRef receiver_map, base::Optional<JSObjectRef> holder) { return PropertyAccessInfo(zone, kNotFound, holder, {{receiver_map}, zone}); } // static PropertyAccessInfo PropertyAccessInfo::DataField( - Zone* zone, Handle<Map> receiver_map, + Zone* zone, MapRef receiver_map, ZoneVector<CompilationDependency const*>&& dependencies, FieldIndex field_index, Representation field_representation, - Type field_type, Handle<Map> field_owner_map, MaybeHandle<Map> field_map, - MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map) { + Type field_type, MapRef field_owner_map, base::Optional<MapRef> field_map, + base::Optional<JSObjectRef> holder, base::Optional<MapRef> transition_map) { DCHECK_IMPLIES( field_representation.IsDouble(), - HasFieldRepresentationDependenciesOnMap(dependencies, field_owner_map)); + HasFieldRepresentationDependenciesOnMap( + dependencies, transition_map.has_value() + ? transition_map->object() + : holder.has_value() ? holder->map().object() + : receiver_map.object())); return PropertyAccessInfo(kDataField, holder, transition_map, field_index, field_representation, field_type, field_owner_map, field_map, {{receiver_map}, zone}, @@ -120,11 +124,11 @@ PropertyAccessInfo PropertyAccessInfo::DataField( // static PropertyAccessInfo PropertyAccessInfo::FastDataConstant( - Zone* zone, Handle<Map> receiver_map, + Zone* zone, MapRef receiver_map, ZoneVector<CompilationDependency const*>&& dependencies, FieldIndex field_index, Representation field_representation, - Type field_type, Handle<Map> field_owner_map, MaybeHandle<Map> field_map, - MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map) { + Type field_type, MapRef field_owner_map, base::Optional<MapRef> field_map, + base::Optional<JSObjectRef> holder, base::Optional<MapRef> transition_map) { return PropertyAccessInfo(kFastDataConstant, holder, transition_map, field_index, field_representation, field_type, field_owner_map, field_map, {{receiver_map}, zone}, @@ -133,39 +137,38 @@ PropertyAccessInfo PropertyAccessInfo::FastDataConstant( // static PropertyAccessInfo PropertyAccessInfo::FastAccessorConstant( - Zone* zone, Handle<Map> receiver_map, Handle<Object> constant, - MaybeHandle<JSObject> holder) { - return PropertyAccessInfo(zone, kFastAccessorConstant, holder, constant, - MaybeHandle<Name>(), {{receiver_map}, zone}); + Zone* zone, MapRef receiver_map, base::Optional<ObjectRef> constant, + base::Optional<JSObjectRef> holder) { + return PropertyAccessInfo(zone, kFastAccessorConstant, holder, constant, {}, + {{receiver_map}, zone}); } // static PropertyAccessInfo PropertyAccessInfo::ModuleExport(Zone* zone, - Handle<Map> receiver_map, - Handle<Cell> cell) { - return PropertyAccessInfo(zone, kModuleExport, MaybeHandle<JSObject>(), cell, - MaybeHandle<Name>{}, {{receiver_map}, zone}); + MapRef receiver_map, + CellRef cell) { + return PropertyAccessInfo(zone, kModuleExport, {}, cell, {}, + {{receiver_map}, zone}); } // static PropertyAccessInfo PropertyAccessInfo::StringLength(Zone* zone, - Handle<Map> receiver_map) { - return PropertyAccessInfo(zone, kStringLength, MaybeHandle<JSObject>(), - {{receiver_map}, zone}); + MapRef receiver_map) { + return PropertyAccessInfo(zone, kStringLength, {}, {{receiver_map}, zone}); } // static PropertyAccessInfo PropertyAccessInfo::DictionaryProtoDataConstant( - Zone* zone, Handle<Map> receiver_map, Handle<JSObject> holder, - InternalIndex dictionary_index, Handle<Name> name) { + Zone* zone, MapRef receiver_map, JSObjectRef holder, + InternalIndex dictionary_index, NameRef name) { return PropertyAccessInfo(zone, kDictionaryProtoDataConstant, holder, {{receiver_map}, zone}, dictionary_index, name); } // static PropertyAccessInfo PropertyAccessInfo::DictionaryProtoAccessorConstant( - Zone* zone, Handle<Map> receiver_map, MaybeHandle<JSObject> holder, - Handle<Object> constant, Handle<Name> property_name) { + Zone* zone, MapRef receiver_map, base::Optional<JSObjectRef> holder, + ObjectRef constant, NameRef property_name) { return PropertyAccessInfo(zone, kDictionaryProtoAccessorConstant, holder, constant, property_name, {{receiver_map}, zone}); } @@ -193,8 +196,8 @@ PropertyAccessInfo::PropertyAccessInfo(Zone* zone) dictionary_index_(InternalIndex::NotFound()) {} PropertyAccessInfo::PropertyAccessInfo( - Zone* zone, Kind kind, MaybeHandle<JSObject> holder, - ZoneVector<Handle<Map>>&& lookup_start_object_maps) + Zone* zone, Kind kind, base::Optional<JSObjectRef> holder, + ZoneVector<MapRef>&& lookup_start_object_maps) : kind_(kind), lookup_start_object_maps_(lookup_start_object_maps), holder_(holder), @@ -204,9 +207,9 @@ PropertyAccessInfo::PropertyAccessInfo( dictionary_index_(InternalIndex::NotFound()) {} PropertyAccessInfo::PropertyAccessInfo( - Zone* zone, Kind kind, MaybeHandle<JSObject> holder, - Handle<Object> constant, MaybeHandle<Name> property_name, - ZoneVector<Handle<Map>>&& lookup_start_object_maps) + Zone* zone, Kind kind, base::Optional<JSObjectRef> holder, + base::Optional<ObjectRef> constant, base::Optional<NameRef> name, + ZoneVector<MapRef>&& lookup_start_object_maps) : kind_(kind), lookup_start_object_maps_(lookup_start_object_maps), constant_(constant), @@ -215,15 +218,16 @@ PropertyAccessInfo::PropertyAccessInfo( field_representation_(Representation::None()), field_type_(Type::Any()), dictionary_index_(InternalIndex::NotFound()), - name_(property_name) { - DCHECK_IMPLIES(kind == kDictionaryProtoAccessorConstant, - !property_name.is_null()); + name_(name) { + DCHECK_IMPLIES(kind == kDictionaryProtoAccessorConstant, name.has_value()); } + PropertyAccessInfo::PropertyAccessInfo( - Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map, - FieldIndex field_index, Representation field_representation, - Type field_type, Handle<Map> field_owner_map, MaybeHandle<Map> field_map, - ZoneVector<Handle<Map>>&& lookup_start_object_maps, + Kind kind, base::Optional<JSObjectRef> holder, + base::Optional<MapRef> transition_map, FieldIndex field_index, + Representation field_representation, Type field_type, + MapRef field_owner_map, base::Optional<MapRef> field_map, + ZoneVector<MapRef>&& lookup_start_object_maps, ZoneVector<CompilationDependency const*>&& unrecorded_dependencies) : kind_(kind), lookup_start_object_maps_(lookup_start_object_maps), @@ -236,14 +240,14 @@ PropertyAccessInfo::PropertyAccessInfo( field_owner_map_(field_owner_map), field_map_(field_map), dictionary_index_(InternalIndex::NotFound()) { - DCHECK_IMPLIES(!transition_map.is_null(), - field_owner_map.address() == transition_map.address()); + DCHECK_IMPLIES(transition_map.has_value(), + field_owner_map.equals(transition_map.value())); } PropertyAccessInfo::PropertyAccessInfo( - Zone* zone, Kind kind, MaybeHandle<JSObject> holder, - ZoneVector<Handle<Map>>&& lookup_start_object_maps, - InternalIndex dictionary_index, Handle<Name> name) + Zone* zone, Kind kind, base::Optional<JSObjectRef> holder, + ZoneVector<MapRef>&& lookup_start_object_maps, + InternalIndex dictionary_index, NameRef name) : kind_(kind), lookup_start_object_maps_(lookup_start_object_maps), holder_(holder), @@ -262,14 +266,31 @@ MinimorphicLoadPropertyAccessInfo::MinimorphicLoadPropertyAccessInfo( field_representation_(field_representation), field_type_(field_type) {} +namespace { + +template <class RefT> +bool OptionalRefEquals(base::Optional<RefT> lhs, base::Optional<RefT> rhs) { + if (!lhs.has_value()) return !rhs.has_value(); + if (!rhs.has_value()) return false; + return lhs->equals(rhs.value()); +} + +template <class T> +void AppendVector(ZoneVector<T>* dst, const ZoneVector<T>& src) { + dst->insert(dst->end(), src.begin(), src.end()); +} + +} // namespace + bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that, AccessMode access_mode, Zone* zone) { - if (this->kind_ != that->kind_) return false; - if (this->holder_.address() != that->holder_.address()) return false; + if (kind_ != that->kind_) return false; + if (!OptionalRefEquals(holder_, that->holder_)) return false; - switch (this->kind_) { + switch (kind_) { case kInvalid: - return that->kind_ == kInvalid; + DCHECK_EQ(that->kind_, kInvalid); + return true; case kDataField: case kFastDataConstant: { @@ -277,90 +298,70 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that, // GetFieldAccessStubKey method here just like the ICs do // since that way we only compare the relevant bits of the // field indices). - if (this->field_index_.GetFieldAccessStubKey() == + if (field_index_.GetFieldAccessStubKey() != that->field_index_.GetFieldAccessStubKey()) { - switch (access_mode) { - case AccessMode::kHas: - case AccessMode::kLoad: { - if (!this->field_representation_.Equals( - that->field_representation_)) { - if (this->field_representation_.IsDouble() || - that->field_representation_.IsDouble()) { - return false; - } - this->field_representation_ = Representation::Tagged(); - } - if (this->field_map_.address() != that->field_map_.address()) { - this->field_map_ = MaybeHandle<Map>(); - } - break; - } - case AccessMode::kStore: - case AccessMode::kStoreInLiteral: { - // For stores, the field map and field representation information - // must match exactly, otherwise we cannot merge the stores. We - // also need to make sure that in case of transitioning stores, - // the transition targets match. - if (this->field_map_.address() != that->field_map_.address() || - !this->field_representation_.Equals( - that->field_representation_) || - this->transition_map_.address() != - that->transition_map_.address()) { + return false; + } + + switch (access_mode) { + case AccessMode::kHas: + case AccessMode::kLoad: { + if (!field_representation_.Equals(that->field_representation_)) { + if (field_representation_.IsDouble() || + that->field_representation_.IsDouble()) { return false; } - break; + field_representation_ = Representation::Tagged(); + } + if (!OptionalRefEquals(field_map_, that->field_map_)) { + field_map_ = {}; + } + break; + } + case AccessMode::kStore: + case AccessMode::kStoreInLiteral: { + // For stores, the field map and field representation information + // must match exactly, otherwise we cannot merge the stores. We + // also need to make sure that in case of transitioning stores, + // the transition targets match. + if (!OptionalRefEquals(field_map_, that->field_map_) || + !field_representation_.Equals(that->field_representation_) || + !OptionalRefEquals(transition_map_, that->transition_map_)) { + return false; } + break; } - this->field_type_ = - Type::Union(this->field_type_, that->field_type_, zone); - this->lookup_start_object_maps_.insert( - this->lookup_start_object_maps_.end(), - that->lookup_start_object_maps_.begin(), - that->lookup_start_object_maps_.end()); - this->unrecorded_dependencies_.insert( - this->unrecorded_dependencies_.end(), - that->unrecorded_dependencies_.begin(), - that->unrecorded_dependencies_.end()); - return true; } - return false; + + field_type_ = Type::Union(field_type_, that->field_type_, zone); + AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_); + AppendVector(&unrecorded_dependencies_, that->unrecorded_dependencies_); + return true; } case kDictionaryProtoAccessorConstant: case kFastAccessorConstant: { // Check if we actually access the same constant. - if (this->constant_.address() == that->constant_.address()) { - DCHECK(this->unrecorded_dependencies_.empty()); - DCHECK(that->unrecorded_dependencies_.empty()); - this->lookup_start_object_maps_.insert( - this->lookup_start_object_maps_.end(), - that->lookup_start_object_maps_.begin(), - that->lookup_start_object_maps_.end()); - return true; - } - return false; + if (!OptionalRefEquals(constant_, that->constant_)) return false; + + DCHECK(unrecorded_dependencies_.empty()); + DCHECK(that->unrecorded_dependencies_.empty()); + AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_); + return true; } case kDictionaryProtoDataConstant: { DCHECK_EQ(AccessMode::kLoad, access_mode); - if (this->dictionary_index_ == that->dictionary_index_) { - this->lookup_start_object_maps_.insert( - this->lookup_start_object_maps_.end(), - that->lookup_start_object_maps_.begin(), - that->lookup_start_object_maps_.end()); - return true; - } - return false; + if (dictionary_index_ != that->dictionary_index_) return false; + AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_); + return true; } case kNotFound: case kStringLength: { - DCHECK(this->unrecorded_dependencies_.empty()); + DCHECK(unrecorded_dependencies_.empty()); DCHECK(that->unrecorded_dependencies_.empty()); - this->lookup_start_object_maps_.insert( - this->lookup_start_object_maps_.end(), - that->lookup_start_object_maps_.begin(), - that->lookup_start_object_maps_.end()); + AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_); return true; } case kModuleExport: @@ -369,10 +370,8 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that, } ConstFieldInfo PropertyAccessInfo::GetConstFieldInfo() const { - if (IsFastDataConstant()) { - return ConstFieldInfo(field_owner_map_.ToHandleChecked()); - } - return ConstFieldInfo::None(); + return IsFastDataConstant() ? ConstFieldInfo(field_owner_map_->object()) + : ConstFieldInfo::None(); } AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker, @@ -384,13 +383,9 @@ AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker, zone_(zone) {} base::Optional<ElementAccessInfo> AccessInfoFactory::ComputeElementAccessInfo( - Handle<Map> map, AccessMode access_mode) const { - // Check if it is safe to inline element access for the {map}. - base::Optional<MapRef> map_ref = TryMakeRef(broker(), map); - if (!map_ref.has_value()) return {}; - if (!CanInlineElementAccess(*map_ref)) return base::nullopt; - ElementsKind const elements_kind = map_ref->elements_kind(); - return ElementAccessInfo({{map}, zone()}, elements_kind, zone()); + MapRef map, AccessMode access_mode) const { + if (!CanInlineElementAccess(map)) return {}; + return ElementAccessInfo({{map}, zone()}, map.elements_kind(), zone()); } bool AccessInfoFactory::ComputeElementAccessInfos( @@ -412,13 +407,17 @@ bool AccessInfoFactory::ComputeElementAccessInfos( for (auto const& group : feedback.transition_groups()) { DCHECK(!group.empty()); - Handle<Map> target = group.front(); + base::Optional<MapRef> target = + MakeRefAssumeMemoryFence(broker(), group.front()); base::Optional<ElementAccessInfo> access_info = - ComputeElementAccessInfo(target, access_mode); + ComputeElementAccessInfo(target.value(), access_mode); if (!access_info.has_value()) return false; for (size_t i = 1; i < group.size(); ++i) { - access_info->AddTransitionSource(group[i]); + base::Optional<MapRef> map_ref = + MakeRefAssumeMemoryFence(broker(), group[i]); + if (!map_ref.has_value()) continue; + access_info->AddTransitionSource(map_ref.value()); } access_infos->push_back(*access_info); } @@ -426,11 +425,11 @@ bool AccessInfoFactory::ComputeElementAccessInfos( } PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo( - Handle<Map> receiver_map, Handle<Map> map, MaybeHandle<JSObject> holder, + MapRef receiver_map, MapRef map, base::Optional<JSObjectRef> holder, InternalIndex descriptor, AccessMode access_mode) const { DCHECK(descriptor.is_found()); - Handle<DescriptorArray> descriptors = broker()->CanonicalPersistentHandle( - map->instance_descriptors(kAcquireLoad)); + // TODO(jgruber,v8:7790): Use DescriptorArrayRef instead. + Handle<DescriptorArray> descriptors = map.instance_descriptors().object(); PropertyDetails const details = descriptors->GetDetails(descriptor); int index = descriptors->GetFieldIndex(descriptor); Representation details_representation = details.representation(); @@ -442,34 +441,31 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo( // here and fall back to use the regular IC logic instead. return Invalid(); } - FieldIndex field_index = - FieldIndex::ForPropertyIndex(*map, index, details_representation); + FieldIndex field_index = FieldIndex::ForPropertyIndex(*map.object(), index, + details_representation); Type field_type = Type::NonInternal(); - MaybeHandle<Map> field_map; - - base::Optional<MapRef> map_ref = TryMakeRef(broker(), map); - if (!map_ref.has_value()) return Invalid(); + base::Optional<MapRef> field_map; ZoneVector<CompilationDependency const*> unrecorded_dependencies(zone()); - if (!map_ref->TrySerializeOwnDescriptor(descriptor)) { - return Invalid(); - } + + Handle<FieldType> descriptors_field_type = + broker()->CanonicalPersistentHandle( + descriptors->GetFieldType(descriptor)); + base::Optional<ObjectRef> descriptors_field_type_ref = + TryMakeRef<Object>(broker(), descriptors_field_type); + if (!descriptors_field_type_ref.has_value()) return Invalid(); + if (details_representation.IsSmi()) { field_type = Type::SignedSmall(); unrecorded_dependencies.push_back( - dependencies()->FieldRepresentationDependencyOffTheRecord(*map_ref, - descriptor)); + dependencies()->FieldRepresentationDependencyOffTheRecord( + map, descriptor, details_representation)); } else if (details_representation.IsDouble()) { field_type = type_cache_->kFloat64; unrecorded_dependencies.push_back( - dependencies()->FieldRepresentationDependencyOffTheRecord(*map_ref, - descriptor)); + dependencies()->FieldRepresentationDependencyOffTheRecord( + map, descriptor, details_representation)); } else if (details_representation.IsHeapObject()) { - // Extract the field type from the property details (make sure its - // representation is TaggedPointer to reflect the heap object case). - Handle<FieldType> descriptors_field_type = - broker()->CanonicalPersistentHandle( - descriptors->GetFieldType(descriptor)); if (descriptors_field_type->IsNone()) { // Store is not safe if the field type was cleared. if (access_mode == AccessMode::kStore) { @@ -480,16 +476,15 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo( // about the contents now. } unrecorded_dependencies.push_back( - dependencies()->FieldRepresentationDependencyOffTheRecord(*map_ref, - descriptor)); + dependencies()->FieldRepresentationDependencyOffTheRecord( + map, descriptor, details_representation)); if (descriptors_field_type->IsClass()) { // Remember the field map, and try to infer a useful type. - Handle<Map> map = broker()->CanonicalPersistentHandle( - descriptors_field_type->AsClass()); - base::Optional<MapRef> maybe_ref = TryMakeRef(broker(), map); - if (!maybe_ref.has_value()) return Invalid(); - field_type = Type::For(*maybe_ref); - field_map = MaybeHandle<Map>(map); + base::Optional<MapRef> maybe_field_map = + TryMakeRef(broker(), descriptors_field_type->AsClass()); + if (!maybe_field_map.has_value()) return Invalid(); + field_type = Type::For(maybe_field_map.value()); + field_map = maybe_field_map; } } else { CHECK(details_representation.IsTagged()); @@ -497,63 +492,74 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo( // TODO(turbofan): We may want to do this only depending on the use // of the access info. unrecorded_dependencies.push_back( - dependencies()->FieldTypeDependencyOffTheRecord(*map_ref, descriptor)); + dependencies()->FieldTypeDependencyOffTheRecord( + map, descriptor, descriptors_field_type_ref.value())); PropertyConstness constness; if (details.IsReadOnly() && !details.IsConfigurable()) { constness = PropertyConstness::kConst; } else { - constness = dependencies()->DependOnFieldConstness(*map_ref, descriptor); + constness = dependencies()->DependOnFieldConstness(map, descriptor); } - // TODO(v8:11670): Make FindFieldOwner and friends robust wrt concurrency. - Handle<Map> field_owner_map = broker()->CanonicalPersistentHandle( - map->FindFieldOwner(isolate(), descriptor)); + + // Note: FindFieldOwner may be called multiple times throughout one + // compilation. This is safe since its result is fixed for a given map and + // descriptor. + MapRef field_owner_map = map.FindFieldOwner(descriptor); + switch (constness) { case PropertyConstness::kMutable: return PropertyAccessInfo::DataField( zone(), receiver_map, std::move(unrecorded_dependencies), field_index, details_representation, field_type, field_owner_map, field_map, - holder); + holder, {}); + case PropertyConstness::kConst: return PropertyAccessInfo::FastDataConstant( zone(), receiver_map, std::move(unrecorded_dependencies), field_index, details_representation, field_type, field_owner_map, field_map, - holder); + holder, {}); } UNREACHABLE(); } namespace { + using AccessorsObjectGetter = std::function<Handle<Object>()>; PropertyAccessInfo AccessorAccessInfoHelper( Isolate* isolate, Zone* zone, JSHeapBroker* broker, - const AccessInfoFactory* ai_factory, Handle<Map> receiver_map, - Handle<Name> name, Handle<Map> map, MaybeHandle<JSObject> holder, - AccessMode access_mode, AccessorsObjectGetter get_accessors) { - if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) { - DCHECK(map->is_prototype_map()); + const AccessInfoFactory* ai_factory, MapRef receiver_map, NameRef name, + MapRef map, base::Optional<JSObjectRef> holder, AccessMode access_mode, + AccessorsObjectGetter get_accessors) { + if (map.instance_type() == JS_MODULE_NAMESPACE_TYPE) { + DCHECK(map.object()->is_prototype_map()); Handle<PrototypeInfo> proto_info = broker->CanonicalPersistentHandle( - PrototypeInfo::cast(map->prototype_info())); + PrototypeInfo::cast(map.object()->prototype_info())); Handle<JSModuleNamespace> module_namespace = broker->CanonicalPersistentHandle( JSModuleNamespace::cast(proto_info->module_namespace())); Handle<Cell> cell = broker->CanonicalPersistentHandle( Cell::cast(module_namespace->module().exports().Lookup( - isolate, name, Smi::ToInt(name->GetHash())))); + isolate, name.object(), Smi::ToInt(name.object()->GetHash())))); if (cell->value().IsTheHole(isolate)) { // This module has not been fully initialized yet. return PropertyAccessInfo::Invalid(zone); } - return PropertyAccessInfo::ModuleExport(zone, receiver_map, cell); + base::Optional<CellRef> cell_ref = TryMakeRef(broker, cell); + if (!cell_ref.has_value()) { + return PropertyAccessInfo::Invalid(zone); + } + return PropertyAccessInfo::ModuleExport(zone, receiver_map, + cell_ref.value()); } if (access_mode == AccessMode::kHas) { // kHas is not supported for dictionary mode objects. - DCHECK(!map->is_dictionary_map()); + DCHECK(!map.is_dictionary_map()); // HasProperty checks don't call getter/setters, existence is sufficient. - return PropertyAccessInfo::FastAccessorConstant(zone, receiver_map, - Handle<Object>(), holder); + return PropertyAccessInfo::FastAccessorConstant(zone, receiver_map, {}, + holder); } Handle<Object> maybe_accessors = get_accessors(); if (!maybe_accessors->IsAccessorPair()) { @@ -561,61 +567,74 @@ PropertyAccessInfo AccessorAccessInfoHelper( } Handle<AccessorPair> accessors = Handle<AccessorPair>::cast(maybe_accessors); Handle<Object> accessor = broker->CanonicalPersistentHandle( - access_mode == AccessMode::kLoad ? accessors->getter() - : accessors->setter()); + access_mode == AccessMode::kLoad ? accessors->getter(kAcquireLoad) + : accessors->setter(kAcquireLoad)); - ObjectData* data = broker->TryGetOrCreateData(accessor); - if (data == nullptr) return PropertyAccessInfo::Invalid(zone); + base::Optional<ObjectRef> accessor_ref = TryMakeRef(broker, accessor); + if (!accessor_ref.has_value()) return PropertyAccessInfo::Invalid(zone); if (!accessor->IsJSFunction()) { CallOptimization optimization(broker->local_isolate_or_isolate(), accessor); if (!optimization.is_simple_api_call() || optimization.IsCrossContextLazyAccessorPair( - *broker->target_native_context().object(), *map)) { + *broker->target_native_context().object(), *map.object())) { return PropertyAccessInfo::Invalid(zone); } CallOptimization::HolderLookup lookup; - holder = broker->CanonicalPersistentHandle( + Handle<JSObject> holder_handle = broker->CanonicalPersistentHandle( optimization.LookupHolderOfExpectedType( - broker->local_isolate_or_isolate(), receiver_map, &lookup)); + broker->local_isolate_or_isolate(), receiver_map.object(), + &lookup)); if (lookup == CallOptimization::kHolderNotFound) { return PropertyAccessInfo::Invalid(zone); } DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver, - holder.is_null()); - DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound, !holder.is_null()); + holder_handle.is_null()); + DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound, + !holder_handle.is_null()); + + if (holder_handle.is_null()) { + holder = {}; + } else { + holder = TryMakeRef(broker, holder_handle); + if (!holder.has_value()) return PropertyAccessInfo::Invalid(zone); + } } if (access_mode == AccessMode::kLoad) { - base::Optional<Name> maybe_cached_property_name = + base::Optional<Name> cached_property_name = FunctionTemplateInfo::TryGetCachedPropertyName(isolate, *accessor); - if (maybe_cached_property_name.has_value()) { - Handle<Name> cached_property_name = - broker->CanonicalPersistentHandle(maybe_cached_property_name.value()); - PropertyAccessInfo access_info = ai_factory->ComputePropertyAccessInfo( - map, cached_property_name, access_mode); - if (!access_info.IsInvalid()) return access_info; + if (cached_property_name.has_value()) { + base::Optional<NameRef> cached_property_name_ref = + TryMakeRef(broker, cached_property_name.value()); + if (cached_property_name_ref.has_value()) { + PropertyAccessInfo access_info = ai_factory->ComputePropertyAccessInfo( + map, cached_property_name_ref.value(), access_mode); + if (!access_info.IsInvalid()) return access_info; + } } } - if (map->is_dictionary_map()) { + + if (map.is_dictionary_map()) { return PropertyAccessInfo::DictionaryProtoAccessorConstant( - zone, receiver_map, holder, accessor, name); + zone, receiver_map, holder, accessor_ref.value(), name); } else { - return PropertyAccessInfo::FastAccessorConstant(zone, receiver_map, - accessor, holder); + return PropertyAccessInfo::FastAccessorConstant( + zone, receiver_map, accessor_ref.value(), holder); } } } // namespace PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo( - Handle<Map> receiver_map, Handle<Name> name, Handle<Map> holder_map, - MaybeHandle<JSObject> holder, InternalIndex descriptor, + MapRef receiver_map, NameRef name, MapRef holder_map, + base::Optional<JSObjectRef> holder, InternalIndex descriptor, AccessMode access_mode) const { DCHECK(descriptor.is_found()); Handle<DescriptorArray> descriptors = broker()->CanonicalPersistentHandle( - holder_map->instance_descriptors(kRelaxedLoad)); - SLOW_DCHECK(descriptor == descriptors->Search(*name, *holder_map)); + holder_map.object()->instance_descriptors(kRelaxedLoad)); + SLOW_DCHECK(descriptor == + descriptors->Search(*name.object(), *holder_map.object())); auto get_accessors = [&]() { return broker()->CanonicalPersistentHandle( @@ -627,11 +646,11 @@ PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo( } PropertyAccessInfo AccessInfoFactory::ComputeDictionaryProtoAccessInfo( - Handle<Map> receiver_map, Handle<Name> name, Handle<JSObject> holder, + MapRef receiver_map, NameRef name, JSObjectRef holder, InternalIndex dictionary_index, AccessMode access_mode, PropertyDetails details) const { CHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL); - DCHECK(holder->map().is_prototype_map()); + DCHECK(holder.map().object()->is_prototype_map()); DCHECK_EQ(access_mode, AccessMode::kLoad); // We can only inline accesses to constant properties. @@ -645,11 +664,11 @@ PropertyAccessInfo AccessInfoFactory::ComputeDictionaryProtoAccessInfo( } auto get_accessors = [&]() { - return JSObject::DictionaryPropertyAt(isolate(), holder, dictionary_index); + return JSObject::DictionaryPropertyAt(isolate(), holder.object(), + dictionary_index); }; - Handle<Map> holder_map = broker()->CanonicalPersistentHandle(holder->map()); return AccessorAccessInfoHelper(isolate(), zone(), broker(), this, - receiver_map, name, holder_map, holder, + receiver_map, name, holder.map(), holder, access_mode, get_accessors); } @@ -668,15 +687,15 @@ MinimorphicLoadPropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( } bool AccessInfoFactory::TryLoadPropertyDetails( - Handle<Map> map, MaybeHandle<JSObject> maybe_holder, Handle<Name> name, + MapRef map, base::Optional<JSObjectRef> maybe_holder, NameRef name, InternalIndex* index_out, PropertyDetails* details_out) const { - if (map->is_dictionary_map()) { + if (map.is_dictionary_map()) { DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL); - DCHECK(map->is_prototype_map()); + DCHECK(map.object()->is_prototype_map()); DisallowGarbageCollection no_gc; - if (maybe_holder.is_null()) { + if (!maybe_holder.has_value()) { // TODO(v8:11457) In this situation, we have a dictionary mode prototype // as a receiver. Consider other means of obtaining the holder in this // situation. @@ -685,24 +704,24 @@ bool AccessInfoFactory::TryLoadPropertyDetails( return false; } - Handle<JSObject> holder = maybe_holder.ToHandleChecked(); + Handle<JSObject> holder = maybe_holder->object(); if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) { SwissNameDictionary dict = holder->property_dictionary_swiss(); - *index_out = dict.FindEntry(isolate(), name); + *index_out = dict.FindEntry(isolate(), name.object()); if (index_out->is_found()) { *details_out = dict.DetailsAt(*index_out); } } else { NameDictionary dict = holder->property_dictionary(); - *index_out = dict.FindEntry(isolate(), name); + *index_out = dict.FindEntry(isolate(), name.object()); if (index_out->is_found()) { *details_out = dict.DetailsAt(*index_out); } } } else { - DescriptorArray descriptors = map->instance_descriptors(kAcquireLoad); - *index_out = - descriptors.Search(*name, *map, broker()->is_concurrent_inlining()); + DescriptorArray descriptors = *map.instance_descriptors().object(); + *index_out = descriptors.Search(*name.object(), *map.object(), + broker()->is_concurrent_inlining()); if (index_out->is_found()) { *details_out = descriptors.GetDetails(*index_out); } @@ -712,12 +731,17 @@ bool AccessInfoFactory::TryLoadPropertyDetails( } PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( - Handle<Map> map, Handle<Name> name, AccessMode access_mode) const { - CHECK(name->IsUniqueName()); + MapRef map, NameRef name, AccessMode access_mode) const { + CHECK(name.IsUniqueName()); + + // Dictionary property const tracking is unsupported when concurrent inlining + // is enabled. + CHECK_IMPLIES(V8_DICT_PROPERTY_CONST_TRACKING_BOOL, + !broker()->is_concurrent_inlining()); JSHeapBroker::MapUpdaterGuardIfNeeded mumd_scope(broker()); - if (access_mode == AccessMode::kHas && !map->IsJSReceiverMap()) { + if (access_mode == AccessMode::kHas && !map.object()->IsJSReceiverMap()) { return Invalid(); } @@ -737,8 +761,21 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( bool fast_mode_prototype_on_chain = false; // Remember the receiver map. We use {map} as loop variable. - Handle<Map> receiver_map = map; - MaybeHandle<JSObject> holder; + MapRef receiver_map = map; + base::Optional<JSObjectRef> holder; + + // Perform the implicit ToObject for primitives here. + // Implemented according to ES6 section 7.3.2 GetV (V, P). + // Note: Keep sync'd with + // CompilationDependencies::DependOnStablePrototypeChains. + if (receiver_map.IsPrimitiveMap()) { + base::Optional<JSFunctionRef> constructor = + broker()->target_native_context().GetConstructorFunction(receiver_map); + if (!constructor.has_value()) return Invalid(); + map = constructor->initial_map(broker()->dependencies()); + DCHECK(!map.IsPrimitiveMap()); + } + while (true) { PropertyDetails details = PropertyDetails::Empty(); InternalIndex index = InternalIndex::NotFound(); @@ -749,13 +786,12 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( if (index.is_found()) { if (access_mode == AccessMode::kStore || access_mode == AccessMode::kStoreInLiteral) { - DCHECK(!map->is_dictionary_map()); + DCHECK(!map.is_dictionary_map()); // Don't bother optimizing stores to read-only properties. - if (details.IsReadOnly()) { - return Invalid(); - } - if (details.kind() == kData && !holder.is_null()) { + if (details.IsReadOnly()) return Invalid(); + + if (details.kind() == kData && holder.has_value()) { // This is a store to a property not found on the receiver but on a // prototype. According to ES6 section 9.1.9 [[Set]], we need to // create a new data property on the receiver. We can still optimize @@ -763,7 +799,8 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( return LookupTransition(receiver_map, name, holder); } } - if (map->is_dictionary_map()) { + + if (map.is_dictionary_map()) { DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL); if (fast_mode_prototype_on_chain) { @@ -776,10 +813,10 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( } // TryLoadPropertyDetails only succeeds if we know the holder. - return ComputeDictionaryProtoAccessInfo(receiver_map, name, - holder.ToHandleChecked(), index, - access_mode, details); + return ComputeDictionaryProtoAccessInfo( + receiver_map, name, holder.value(), index, access_mode, details); } + if (dictionary_prototype_on_chain) { // If V8_DICT_PROPERTY_CONST_TRACKING_BOOL was disabled, then a // dictionary prototype would have caused a bailout earlier. @@ -817,12 +854,13 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( } // The property wasn't found on {map}. Look on the prototype if appropriate. + DCHECK(!index.is_found()); // Don't search on the prototype chain for special indices in case of // integer indexed exotic objects (see ES6 section 9.4.5). - if (map->IsJSTypedArrayMap() && name->IsString()) { + if (map.object()->IsJSTypedArrayMap() && name.IsString()) { if (broker()->IsMainThread()) { - if (IsSpecialIndex(String::cast(*name))) { + if (IsSpecialIndex(String::cast(*name.object()))) { return Invalid(); } } else { @@ -839,72 +877,67 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( } // Don't lookup private symbols on the prototype chain. - if (name->IsPrivate()) { + if (name.object()->IsPrivate()) { return Invalid(); } - if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL && !holder.is_null()) { + if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL && holder.has_value()) { // At this point, we are past the first loop iteration. - DCHECK(holder.ToHandleChecked()->map().is_prototype_map()); - DCHECK_NE(holder.ToHandleChecked()->map(), *receiver_map); + DCHECK(holder->object()->map().is_prototype_map()); + DCHECK(!holder->map().equals(receiver_map)); fast_mode_prototype_on_chain = - fast_mode_prototype_on_chain || !map->is_dictionary_map(); + fast_mode_prototype_on_chain || !map.is_dictionary_map(); dictionary_prototype_on_chain = - dictionary_prototype_on_chain || map->is_dictionary_map(); + dictionary_prototype_on_chain || map.is_dictionary_map(); } // Walk up the prototype chain. - base::Optional<MapRef> map_ref = TryMakeRef(broker(), map); - if (!map_ref.has_value()) return Invalid(); - if (!map_ref->TrySerializePrototype()) return Invalid(); - - // Acquire synchronously the map's prototype's map to guarantee that every - // time we use it, we use the same Map. - Handle<Map> map_prototype_map = - broker()->CanonicalPersistentHandle(map->prototype().map(kAcquireLoad)); - if (!map_prototype_map->IsJSObjectMap()) { - // Perform the implicit ToObject for primitives here. - // Implemented according to ES6 section 7.3.2 GetV (V, P). - Handle<JSFunction> constructor; - base::Optional<JSFunction> maybe_constructor = - Map::GetConstructorFunction( - *map, *broker()->target_native_context().object()); - if (maybe_constructor.has_value()) { - map = broker()->CanonicalPersistentHandle( - maybe_constructor->initial_map()); - map_prototype_map = broker()->CanonicalPersistentHandle( - map->prototype().map(kAcquireLoad)); - DCHECK(map_prototype_map->IsJSObjectMap()); - } else if (map->prototype().IsNull()) { - if (dictionary_prototype_on_chain) { - // TODO(v8:11248) See earlier comment about - // dictionary_prototype_on_chain. We don't support absent properties - // with dictionary mode prototypes on the chain, either. This is again - // just due to how we currently deal with dependencies for dictionary - // properties during finalization. - return Invalid(); - } + if (!broker()->is_concurrent_inlining()) { + if (!map.TrySerializePrototype(NotConcurrentInliningTag{broker()})) { + return Invalid(); + } + } - // Store to property not found on the receiver or any prototype, we need - // to transition to a new data property. - // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) - if (access_mode == AccessMode::kStore) { - return LookupTransition(receiver_map, name, holder); - } - // The property was not found (access returns undefined or throws - // depending on the language mode of the load operation. - // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver) - return PropertyAccessInfo::NotFound(zone(), receiver_map, holder); - } else { + // Load the map's prototype's map to guarantee that every time we use it, + // we use the same Map. + base::Optional<HeapObjectRef> prototype = map.prototype(); + if (!prototype.has_value()) return Invalid(); + + MapRef map_prototype_map = prototype->map(); + if (!map_prototype_map.object()->IsJSObjectMap()) { + // Don't allow proxies on the prototype chain. + if (!prototype->IsNull()) { + DCHECK(prototype->object()->IsJSProxy()); + return Invalid(); + } + + DCHECK(prototype->IsNull()); + + if (dictionary_prototype_on_chain) { + // TODO(v8:11248) See earlier comment about + // dictionary_prototype_on_chain. We don't support absent properties + // with dictionary mode prototypes on the chain, either. This is again + // just due to how we currently deal with dependencies for dictionary + // properties during finalization. return Invalid(); } + + // Store to property not found on the receiver or any prototype, we need + // to transition to a new data property. + // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) + if (access_mode == AccessMode::kStore) { + return LookupTransition(receiver_map, name, holder); + } + + // The property was not found (access returns undefined or throws + // depending on the language mode of the load operation. + // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver) + return PropertyAccessInfo::NotFound(zone(), receiver_map, holder); } - holder = - broker()->CanonicalPersistentHandle(JSObject::cast(map->prototype())); + holder = prototype->AsJSObject(); map = map_prototype_map; - CHECK(!map->is_deprecated()); if (!CanInlinePropertyAccess(map, access_mode)) { return Invalid(); @@ -912,8 +945,12 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( // Successful lookup on prototype chain needs to guarantee that all the // prototypes up to the holder have stable maps, except for dictionary-mode - // prototypes. - CHECK_IMPLIES(!map->is_dictionary_map(), map->is_stable()); + // prototypes. We currently do this by taking a + // DependOnStablePrototypeChains dependency in the caller. + // + // TODO(jgruber): This is brittle and easy to miss. Consider a refactor + // that moves the responsibility of taking the dependency into + // AccessInfoFactory. } UNREACHABLE(); } @@ -932,15 +969,6 @@ PropertyAccessInfo AccessInfoFactory::FinalizePropertyAccessInfosAsOne( return Invalid(); } -void AccessInfoFactory::ComputePropertyAccessInfos( - MapHandles const& maps, Handle<Name> name, AccessMode access_mode, - ZoneVector<PropertyAccessInfo>* access_infos) const { - DCHECK(access_infos->empty()); - for (Handle<Map> map : maps) { - access_infos->push_back(ComputePropertyAccessInfo(map, name, access_mode)); - } -} - void PropertyAccessInfo::RecordDependencies( CompilationDependencies* dependencies) { for (CompilationDependency const* d : unrecorded_dependencies_) { @@ -1007,7 +1035,7 @@ Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind, base::Optional<ElementAccessInfo> AccessInfoFactory::ConsolidateElementLoad( ElementAccessFeedback const& feedback) const { - if (feedback.transition_groups().empty()) return base::nullopt; + if (feedback.transition_groups().empty()) return {}; DCHECK(!feedback.transition_groups().front().empty()); Handle<Map> first_map = feedback.transition_groups().front().front(); @@ -1016,20 +1044,20 @@ base::Optional<ElementAccessInfo> AccessInfoFactory::ConsolidateElementLoad( InstanceType instance_type = first_map_ref->instance_type(); ElementsKind elements_kind = first_map_ref->elements_kind(); - ZoneVector<Handle<Map>> maps(zone()); + ZoneVector<MapRef> maps(zone()); for (auto const& group : feedback.transition_groups()) { for (Handle<Map> map_handle : group) { base::Optional<MapRef> map = TryMakeRef(broker(), map_handle); if (!map.has_value()) return {}; if (map->instance_type() != instance_type || !CanInlineElementAccess(*map)) { - return base::nullopt; + return {}; } if (!GeneralizeElementsKind(elements_kind, map->elements_kind()) .To(&elements_kind)) { - return base::nullopt; + return {}; } - maps.push_back(map->object()); + maps.push_back(map.value()); } } @@ -1037,31 +1065,33 @@ base::Optional<ElementAccessInfo> AccessInfoFactory::ConsolidateElementLoad( } PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor( - Handle<Map> map, Handle<Name> name) const { + MapRef map, NameRef name) const { // Check for String::length field accessor. - if (map->IsStringMap()) { - if (Name::Equals(isolate(), name, isolate()->factory()->length_string())) { + if (map.object()->IsStringMap()) { + if (Name::Equals(isolate(), name.object(), + isolate()->factory()->length_string())) { return PropertyAccessInfo::StringLength(zone(), map); } return Invalid(); } // Check for special JSObject field accessors. FieldIndex field_index; - if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) { + if (Accessors::IsJSObjectFieldAccessor(isolate(), map.object(), name.object(), + &field_index)) { Type field_type = Type::NonInternal(); Representation field_representation = Representation::Tagged(); - if (map->IsJSArrayMap()) { - DCHECK( - Name::Equals(isolate(), isolate()->factory()->length_string(), name)); + if (map.object()->IsJSArrayMap()) { + DCHECK(Name::Equals(isolate(), isolate()->factory()->length_string(), + name.object())); // The JSArray::length property is a smi in the range // [0, FixedDoubleArray::kMaxLength] in case of fast double // elements, a smi in the range [0, FixedArray::kMaxLength] // in case of other fast elements, and [0, kMaxUInt32] in // case of other arrays. - if (IsDoubleElementsKind(map->elements_kind())) { + if (IsDoubleElementsKind(map.elements_kind())) { field_type = type_cache_->kFixedDoubleArrayLengthType; field_representation = Representation::Smi(); - } else if (IsFastElementsKind(map->elements_kind())) { + } else if (IsFastElementsKind(map.elements_kind())) { field_type = type_cache_->kFixedArrayLengthType; field_representation = Representation::Smi(); } else { @@ -1070,97 +1100,96 @@ PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor( } // Special fields are always mutable. return PropertyAccessInfo::DataField(zone(), map, {{}, zone()}, field_index, - field_representation, field_type, map); + field_representation, field_type, map, + {}, {}, {}); } return Invalid(); } PropertyAccessInfo AccessInfoFactory::LookupTransition( - Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder) const { + MapRef map, NameRef name, base::Optional<JSObjectRef> holder) const { // Check if the {map} has a data transition with the given {name}. - Map transition = - TransitionsAccessor(isolate(), map, broker()->is_concurrent_inlining()) - .SearchTransition(*name, kData, NONE); - if (transition.is_null()) { - return Invalid(); - } - - Handle<Map> transition_map = broker()->CanonicalPersistentHandle(transition); - InternalIndex const number = transition_map->LastAdded(); - Handle<DescriptorArray> descriptors = broker()->CanonicalPersistentHandle( - transition_map->instance_descriptors(kAcquireLoad)); + Map transition = TransitionsAccessor(isolate(), map.object(), + broker()->is_concurrent_inlining()) + .SearchTransition(*name.object(), kData, NONE); + if (transition.is_null()) return Invalid(); + + base::Optional<MapRef> maybe_transition_map = + TryMakeRef(broker(), transition); + if (!maybe_transition_map.has_value()) return Invalid(); + MapRef transition_map = maybe_transition_map.value(); + + InternalIndex const number = transition_map.object()->LastAdded(); + Handle<DescriptorArray> descriptors = + transition_map.instance_descriptors().object(); PropertyDetails const details = descriptors->GetDetails(number); + // Don't bother optimizing stores to read-only properties. - if (details.IsReadOnly()) { - return Invalid(); - } + if (details.IsReadOnly()) return Invalid(); + // TODO(bmeurer): Handle transition to data constant? - if (details.location() != kField) { - return Invalid(); - } + if (details.location() != kField) return Invalid(); + int const index = details.field_index(); Representation details_representation = details.representation(); - FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index, - details_representation); + FieldIndex field_index = FieldIndex::ForPropertyIndex( + *transition_map.object(), index, details_representation); Type field_type = Type::NonInternal(); - MaybeHandle<Map> field_map; - - base::Optional<MapRef> transition_map_ref = - TryMakeRef(broker(), transition_map); - if (!transition_map_ref.has_value()) return Invalid(); + base::Optional<MapRef> field_map; ZoneVector<CompilationDependency const*> unrecorded_dependencies(zone()); if (details_representation.IsSmi()) { field_type = Type::SignedSmall(); - if (!transition_map_ref->TrySerializeOwnDescriptor(number)) { - return Invalid(); - } unrecorded_dependencies.push_back( dependencies()->FieldRepresentationDependencyOffTheRecord( - *transition_map_ref, number)); + transition_map, number, details_representation)); } else if (details_representation.IsDouble()) { field_type = type_cache_->kFloat64; - if (!transition_map_ref->TrySerializeOwnDescriptor(number)) { - return Invalid(); - } unrecorded_dependencies.push_back( dependencies()->FieldRepresentationDependencyOffTheRecord( - *transition_map_ref, number)); + transition_map, number, details_representation)); } else if (details_representation.IsHeapObject()) { // Extract the field type from the property details (make sure its // representation is TaggedPointer to reflect the heap object case). + // TODO(jgruber,v8:7790): Use DescriptorArrayRef instead. Handle<FieldType> descriptors_field_type = broker()->CanonicalPersistentHandle(descriptors->GetFieldType(number)); + base::Optional<ObjectRef> descriptors_field_type_ref = + TryMakeRef<Object>(broker(), descriptors_field_type); + if (!descriptors_field_type_ref.has_value()) return Invalid(); + if (descriptors_field_type->IsNone()) { // Store is not safe if the field type was cleared. return Invalid(); } - if (!transition_map_ref->TrySerializeOwnDescriptor(number)) { - return Invalid(); - } unrecorded_dependencies.push_back( dependencies()->FieldRepresentationDependencyOffTheRecord( - *transition_map_ref, number)); + transition_map, number, details_representation)); if (descriptors_field_type->IsClass()) { unrecorded_dependencies.push_back( - dependencies()->FieldTypeDependencyOffTheRecord(*transition_map_ref, - number)); + dependencies()->FieldTypeDependencyOffTheRecord( + transition_map, number, + MakeRef<Object>(broker(), descriptors_field_type))); // Remember the field map, and try to infer a useful type. - Handle<Map> map = broker()->CanonicalPersistentHandle( - descriptors_field_type->AsClass()); - base::Optional<MapRef> map_ref = TryMakeRef(broker(), map); - if (!map_ref.has_value()) return Invalid(); - field_type = Type::For(*map_ref); - field_map = map; + base::Optional<MapRef> maybe_field_map = + TryMakeRef(broker(), descriptors_field_type->AsClass()); + if (!maybe_field_map.has_value()) return Invalid(); + field_type = Type::For(maybe_field_map.value()); + field_map = maybe_field_map; } } + unrecorded_dependencies.push_back( - dependencies()->TransitionDependencyOffTheRecord(*transition_map_ref)); - transition_map_ref->SerializeBackPointer(); // For BuildPropertyStore. + dependencies()->TransitionDependencyOffTheRecord(transition_map)); + if (!broker()->is_concurrent_inlining()) { + transition_map.SerializeBackPointer( + NotConcurrentInliningTag{broker()}); // For BuildPropertyStore. + } + // Transitioning stores *may* store to const fields. The resulting // DataConstant access infos can be distinguished from later, i.e. redundant, // stores to the same constant field by the presence of a transition map. - switch (dependencies()->DependOnFieldConstness(*transition_map_ref, number)) { + switch (dependencies()->DependOnFieldConstness(transition_map, number)) { case PropertyConstness::kMutable: return PropertyAccessInfo::DataField( zone(), map, std::move(unrecorded_dependencies), field_index, |