Upgrade V8 to 8.8.278.14
Bug: 162604069
Bug: 167389063
Test: gts-tradefed run gts-dev --module GtsGmscoreHostTestCases
--test com.google.android.gts.devicepolicy.DeviceOwnerTest#testProxyPacProxyTest
Test: m -j proxy_resolver_v8_unittest && adb sync && adb shell \
/data/nativetest/proxy_resolver_v8_unittest/proxy_resolver_v8_unittest
Merged-In: Ifb09923b9d7f6d8990fb062d7dc0294edf2c098e
Change-Id: Ifb09923b9d7f6d8990fb062d7dc0294edf2c098e
(cherry picked from commit 9580a23bc5b8874a0979001d3595d027cbb68128)
diff --git a/src/compiler/access-info.cc b/src/compiler/access-info.cc
index d563e1d..ddf742e 100644
--- a/src/compiler/access-info.cc
+++ b/src/compiler/access-info.cc
@@ -4,14 +4,22 @@
#include <ostream>
-#include "src/accessors.h"
-#include "src/compilation-dependencies.h"
#include "src/compiler/access-info.h"
+
+#include "src/builtins/accessors.h"
+#include "src/compiler/compilation-dependencies.h"
+#include "src/compiler/compilation-dependency.h"
+#include "src/compiler/simplified-operator.h"
#include "src/compiler/type-cache.h"
-#include "src/field-index-inl.h"
-#include "src/field-type.h"
#include "src/ic/call-optimization.h"
-#include "src/objects-inl.h"
+#include "src/logging/counters.h"
+#include "src/objects/cell-inl.h"
+#include "src/objects/field-index-inl.h"
+#include "src/objects/field-type.h"
+#include "src/objects/module-inl.h"
+#include "src/objects/objects-inl.h"
+#include "src/objects/struct-inl.h"
+#include "src/objects/templates.h"
namespace v8 {
namespace internal {
@@ -19,30 +27,32 @@
namespace {
-bool CanInlineElementAccess(Handle<Map> map) {
- if (!map->IsJSObjectMap()) return false;
- if (map->is_access_check_needed()) return false;
- if (map->has_indexed_interceptor()) return false;
- ElementsKind const elements_kind = map->elements_kind();
- if (IsFastElementsKind(elements_kind)) return true;
- if (IsFixedTypedArrayElementsKind(elements_kind)) return true;
- return false;
-}
-
-
bool CanInlinePropertyAccess(Handle<Map> map) {
// 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).
- STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_TYPE);
+ STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_HEAP_OBJECT_TYPE);
if (map->IsBooleanMap()) return true;
- if (map->instance_type() < LAST_PRIMITIVE_TYPE) return true;
+ if (map->instance_type() < LAST_PRIMITIVE_HEAP_OBJECT_TYPE) return true;
return map->IsJSObjectMap() && !map->is_dictionary_map() &&
!map->has_named_interceptor() &&
- // TODO(verwaest): Whitelist contexts to which we have access.
+ // TODO(verwaest): Allowlist contexts to which we have access.
!map->is_access_check_needed();
}
+#ifdef DEBUG
+bool HasFieldRepresentationDependenciesOnMap(
+ ZoneVector<CompilationDependency const*>& dependencies,
+ Handle<Map> const& field_owner_map) {
+ for (auto dep : dependencies) {
+ if (dep->IsFieldRepresentationDependencyOnMap(field_owner_map)) {
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
} // namespace
@@ -54,532 +64,854 @@
return os << "Store";
case AccessMode::kStoreInLiteral:
return os << "StoreInLiteral";
+ case AccessMode::kHas:
+ return os << "Has";
}
UNREACHABLE();
- return os;
}
-ElementAccessInfo::ElementAccessInfo() {}
-
-ElementAccessInfo::ElementAccessInfo(MapList const& receiver_maps,
- ElementsKind elements_kind)
- : elements_kind_(elements_kind), receiver_maps_(receiver_maps) {}
+ElementAccessInfo::ElementAccessInfo(
+ ZoneVector<Handle<Map>>&& lookup_start_object_maps,
+ ElementsKind elements_kind, Zone* zone)
+ : elements_kind_(elements_kind),
+ lookup_start_object_maps_(lookup_start_object_maps),
+ transition_sources_(zone) {
+ CHECK(!lookup_start_object_maps.empty());
+}
// static
-PropertyAccessInfo PropertyAccessInfo::NotFound(MapList const& receiver_maps,
+PropertyAccessInfo PropertyAccessInfo::Invalid(Zone* zone) {
+ return PropertyAccessInfo(zone);
+}
+
+// static
+PropertyAccessInfo PropertyAccessInfo::NotFound(Zone* zone,
+ Handle<Map> receiver_map,
MaybeHandle<JSObject> holder) {
- return PropertyAccessInfo(holder, receiver_maps);
-}
-
-// static
-PropertyAccessInfo PropertyAccessInfo::DataConstant(
- MapList const& receiver_maps, Handle<Object> constant,
- MaybeHandle<JSObject> holder) {
- return PropertyAccessInfo(kDataConstant, holder, constant, receiver_maps);
+ return PropertyAccessInfo(zone, kNotFound, holder, {{receiver_map}, zone});
}
// static
PropertyAccessInfo PropertyAccessInfo::DataField(
- PropertyConstness constness, MapList const& receiver_maps,
- FieldIndex field_index, MachineRepresentation field_representation,
- Type* field_type, MaybeHandle<Map> field_map, MaybeHandle<JSObject> holder,
- MaybeHandle<Map> transition_map) {
- Kind kind = constness == kConst ? kDataConstantField : kDataField;
- return PropertyAccessInfo(kind, holder, transition_map, field_index,
- field_representation, field_type, field_map,
- receiver_maps);
+ Zone* zone, Handle<Map> 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) {
+ DCHECK_IMPLIES(
+ field_representation.IsDouble(),
+ HasFieldRepresentationDependenciesOnMap(dependencies, field_owner_map));
+ return PropertyAccessInfo(kDataField, holder, transition_map, field_index,
+ field_representation, field_type, field_owner_map,
+ field_map, {{receiver_map}, zone},
+ std::move(dependencies));
+}
+
+// static
+PropertyAccessInfo PropertyAccessInfo::DataConstant(
+ Zone* zone, Handle<Map> 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) {
+ return PropertyAccessInfo(kDataConstant, holder, transition_map, field_index,
+ field_representation, field_type, field_owner_map,
+ field_map, {{receiver_map}, zone},
+ std::move(dependencies));
}
// static
PropertyAccessInfo PropertyAccessInfo::AccessorConstant(
- MapList const& receiver_maps, Handle<Object> constant,
+ Zone* zone, Handle<Map> receiver_map, Handle<Object> constant,
MaybeHandle<JSObject> holder) {
- return PropertyAccessInfo(kAccessorConstant, holder, constant, receiver_maps);
+ return PropertyAccessInfo(zone, kAccessorConstant, holder, constant,
+ {{receiver_map}, zone});
}
// static
-PropertyAccessInfo PropertyAccessInfo::Generic(MapList const& receiver_maps) {
- return PropertyAccessInfo(kGeneric, MaybeHandle<JSObject>(), Handle<Object>(),
- receiver_maps);
+PropertyAccessInfo PropertyAccessInfo::ModuleExport(Zone* zone,
+ Handle<Map> receiver_map,
+ Handle<Cell> cell) {
+ return PropertyAccessInfo(zone, kModuleExport, MaybeHandle<JSObject>(), cell,
+ {{receiver_map}, zone});
}
-PropertyAccessInfo::PropertyAccessInfo()
+// static
+PropertyAccessInfo PropertyAccessInfo::StringLength(Zone* zone,
+ Handle<Map> receiver_map) {
+ return PropertyAccessInfo(zone, kStringLength, MaybeHandle<JSObject>(),
+ {{receiver_map}, zone});
+}
+
+// static
+MinimorphicLoadPropertyAccessInfo MinimorphicLoadPropertyAccessInfo::DataField(
+ int offset, bool is_inobject, Representation field_representation,
+ Type field_type) {
+ return MinimorphicLoadPropertyAccessInfo(kDataField, offset, is_inobject,
+ field_representation, field_type);
+}
+
+// static
+MinimorphicLoadPropertyAccessInfo MinimorphicLoadPropertyAccessInfo::Invalid() {
+ return MinimorphicLoadPropertyAccessInfo(
+ kInvalid, -1, false, Representation::None(), Type::None());
+}
+
+PropertyAccessInfo::PropertyAccessInfo(Zone* zone)
: kind_(kInvalid),
- field_representation_(MachineRepresentation::kNone),
+ lookup_start_object_maps_(zone),
+ unrecorded_dependencies_(zone),
+ field_representation_(Representation::None()),
field_type_(Type::None()) {}
-PropertyAccessInfo::PropertyAccessInfo(MaybeHandle<JSObject> holder,
- MapList const& receiver_maps)
- : kind_(kNotFound),
- receiver_maps_(receiver_maps),
- holder_(holder),
- field_representation_(MachineRepresentation::kNone),
- field_type_(Type::None()) {}
-
-PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
- Handle<Object> constant,
- MapList const& receiver_maps)
+PropertyAccessInfo::PropertyAccessInfo(
+ Zone* zone, Kind kind, MaybeHandle<JSObject> holder,
+ ZoneVector<Handle<Map>>&& lookup_start_object_maps)
: kind_(kind),
- receiver_maps_(receiver_maps),
+ lookup_start_object_maps_(lookup_start_object_maps),
+ unrecorded_dependencies_(zone),
+ holder_(holder),
+ field_representation_(Representation::None()),
+ field_type_(Type::None()) {}
+
+PropertyAccessInfo::PropertyAccessInfo(
+ Zone* zone, Kind kind, MaybeHandle<JSObject> holder,
+ Handle<Object> constant, ZoneVector<Handle<Map>>&& lookup_start_object_maps)
+ : kind_(kind),
+ lookup_start_object_maps_(lookup_start_object_maps),
+ unrecorded_dependencies_(zone),
constant_(constant),
holder_(holder),
- field_representation_(MachineRepresentation::kNone),
+ field_representation_(Representation::None()),
field_type_(Type::Any()) {}
PropertyAccessInfo::PropertyAccessInfo(
Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
- FieldIndex field_index, MachineRepresentation field_representation,
- Type* field_type, MaybeHandle<Map> field_map, MapList const& receiver_maps)
+ 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,
+ ZoneVector<CompilationDependency const*>&& unrecorded_dependencies)
: kind_(kind),
- receiver_maps_(receiver_maps),
+ lookup_start_object_maps_(lookup_start_object_maps),
+ unrecorded_dependencies_(std::move(unrecorded_dependencies)),
transition_map_(transition_map),
holder_(holder),
field_index_(field_index),
field_representation_(field_representation),
field_type_(field_type),
- field_map_(field_map) {}
+ field_owner_map_(field_owner_map),
+ field_map_(field_map) {
+ DCHECK_IMPLIES(!transition_map.is_null(),
+ field_owner_map.address() == transition_map.address());
+}
-bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that) {
+MinimorphicLoadPropertyAccessInfo::MinimorphicLoadPropertyAccessInfo(
+ Kind kind, int offset, bool is_inobject,
+ Representation field_representation, Type field_type)
+ : kind_(kind),
+ is_inobject_(is_inobject),
+ offset_(offset),
+ field_representation_(field_representation),
+ field_type_(field_type) {}
+
+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;
switch (this->kind_) {
case kInvalid:
- break;
+ return that->kind_ == kInvalid;
case kDataField:
- case kDataConstantField: {
- // Check if we actually access the same field.
- if (this->kind_ == that->kind_ &&
- this->transition_map_.address() == that->transition_map_.address() &&
- this->field_index_ == that->field_index_ &&
- this->field_map_.address() == that->field_map_.address() &&
- this->field_type_->Is(that->field_type_) &&
- that->field_type_->Is(this->field_type_) &&
- this->field_representation_ == that->field_representation_) {
- this->receiver_maps_.insert(this->receiver_maps_.end(),
- that->receiver_maps_.begin(),
- that->receiver_maps_.end());
+ case kDataConstant: {
+ // Check if we actually access the same field (we use the
+ // 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() ==
+ 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;
+ }
+ 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;
}
- case kDataConstant:
case kAccessorConstant: {
// Check if we actually access the same constant.
if (this->constant_.address() == that->constant_.address()) {
- this->receiver_maps_.insert(this->receiver_maps_.end(),
- that->receiver_maps_.begin(),
- that->receiver_maps_.end());
+ 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;
}
case kNotFound:
- case kGeneric: {
- this->receiver_maps_.insert(this->receiver_maps_.end(),
- that->receiver_maps_.begin(),
- that->receiver_maps_.end());
+ case kStringLength: {
+ 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;
+ }
+ case kModuleExport:
+ return false;
+ }
+}
+
+ConstFieldInfo PropertyAccessInfo::GetConstFieldInfo() const {
+ if (IsDataConstant()) {
+ return ConstFieldInfo(field_owner_map_.ToHandleChecked());
+ }
+ return ConstFieldInfo::None();
+}
+
+AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
+ CompilationDependencies* dependencies,
+ Zone* zone)
+ : broker_(broker),
+ dependencies_(dependencies),
+ type_cache_(TypeCache::Get()),
+ 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}.
+ MapRef map_ref(broker(), map);
+ if (!CanInlineElementAccess(map_ref)) return base::nullopt;
+ ElementsKind const elements_kind = map_ref.elements_kind();
+ return ElementAccessInfo({{map}, zone()}, elements_kind, zone());
+}
+
+bool AccessInfoFactory::ComputeElementAccessInfos(
+ ElementAccessFeedback const& feedback,
+ ZoneVector<ElementAccessInfo>* access_infos) const {
+ AccessMode access_mode = feedback.keyed_mode().access_mode();
+ if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
+ // For polymorphic loads of similar elements kinds (i.e. all tagged or all
+ // double), always use the "worst case" code without a transition. This is
+ // much faster than transitioning the elements to the worst case, trading a
+ // TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
+ base::Optional<ElementAccessInfo> access_info =
+ ConsolidateElementLoad(feedback);
+ if (access_info.has_value()) {
+ access_infos->push_back(*access_info);
return true;
}
}
+ for (auto const& group : feedback.transition_groups()) {
+ DCHECK(!group.empty());
+ Handle<Map> target = group.front();
+ base::Optional<ElementAccessInfo> access_info =
+ ComputeElementAccessInfo(target, access_mode);
+ if (!access_info.has_value()) return false;
+
+ for (size_t i = 1; i < group.size(); ++i) {
+ access_info->AddTransitionSource(group[i]);
+ }
+ access_infos->push_back(*access_info);
+ }
+ return true;
+}
+
+PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
+ Handle<Map> receiver_map, Handle<Map> map, MaybeHandle<JSObject> holder,
+ InternalIndex descriptor, AccessMode access_mode) const {
+ DCHECK(descriptor.is_found());
+ Handle<DescriptorArray> descriptors(map->instance_descriptors(kRelaxedLoad),
+ isolate());
+ PropertyDetails const details = descriptors->GetDetails(descriptor);
+ int index = descriptors->GetFieldIndex(descriptor);
+ Representation details_representation = details.representation();
+ if (details_representation.IsNone()) {
+ // The ICs collect feedback in PREMONOMORPHIC state already,
+ // but at this point the {receiver_map} might still contain
+ // fields for which the representation has not yet been
+ // determined by the runtime. So we need to catch this case
+ // here and fall back to use the regular IC logic instead.
+ return PropertyAccessInfo::Invalid(zone());
+ }
+ FieldIndex field_index =
+ FieldIndex::ForPropertyIndex(*map, index, details_representation);
+ Type field_type = Type::NonInternal();
+ MaybeHandle<Map> field_map;
+ MapRef map_ref(broker(), map);
+ ZoneVector<CompilationDependency const*> unrecorded_dependencies(zone());
+ map_ref.SerializeOwnDescriptor(descriptor);
+ if (details_representation.IsSmi()) {
+ field_type = Type::SignedSmall();
+ unrecorded_dependencies.push_back(
+ dependencies()->FieldRepresentationDependencyOffTheRecord(map_ref,
+ descriptor));
+ } else if (details_representation.IsDouble()) {
+ field_type = type_cache_->kFloat64;
+ if (!FLAG_unbox_double_fields) {
+ unrecorded_dependencies.push_back(
+ dependencies()->FieldRepresentationDependencyOffTheRecord(
+ map_ref, descriptor));
+ }
+ } 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(
+ descriptors->GetFieldType(descriptor), isolate());
+ if (descriptors_field_type->IsNone()) {
+ // Store is not safe if the field type was cleared.
+ if (access_mode == AccessMode::kStore) {
+ return PropertyAccessInfo::Invalid(zone());
+ }
+
+ // The field type was cleared by the GC, so we don't know anything
+ // about the contents now.
+ }
+ unrecorded_dependencies.push_back(
+ dependencies()->FieldRepresentationDependencyOffTheRecord(map_ref,
+ descriptor));
+ if (descriptors_field_type->IsClass()) {
+ // Remember the field map, and try to infer a useful type.
+ Handle<Map> map(descriptors_field_type->AsClass(), isolate());
+ field_type = Type::For(MapRef(broker(), map));
+ field_map = MaybeHandle<Map>(map);
+ }
+ } else {
+ CHECK(details_representation.IsTagged());
+ }
+ // 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));
+
+ PropertyConstness constness;
+ if (details.IsReadOnly() && !details.IsConfigurable()) {
+ constness = PropertyConstness::kConst;
+ } else if (broker()->is_turboprop() && !map->is_prototype_map()) {
+ // The constness feedback is too unstable for the aggresive compilation
+ // of turboprop.
+ constness = PropertyConstness::kMutable;
+ } else {
+ map_ref.SerializeOwnDescriptor(descriptor);
+ constness = dependencies()->DependOnFieldConstness(map_ref, descriptor);
+ }
+ Handle<Map> field_owner_map(map->FindFieldOwner(isolate(), descriptor),
+ isolate());
+ 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);
+ case PropertyConstness::kConst:
+ return PropertyAccessInfo::DataConstant(
+ zone(), receiver_map, std::move(unrecorded_dependencies), field_index,
+ details_representation, field_type, field_owner_map, field_map,
+ holder);
+ }
UNREACHABLE();
- return false;
}
-AccessInfoFactory::AccessInfoFactory(CompilationDependencies* dependencies,
- Handle<Context> native_context, Zone* zone)
- : dependencies_(dependencies),
- native_context_(native_context),
- isolate_(native_context->GetIsolate()),
- type_cache_(TypeCache::Get()),
- zone_(zone) {
- DCHECK(native_context->IsNativeContext());
-}
+PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
+ Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map,
+ MaybeHandle<JSObject> holder, InternalIndex descriptor,
+ AccessMode access_mode) const {
+ DCHECK(descriptor.is_found());
+ Handle<DescriptorArray> descriptors(map->instance_descriptors(kRelaxedLoad),
+ isolate());
+ SLOW_DCHECK(descriptor == descriptors->Search(*name, *map));
+ if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
+ DCHECK(map->is_prototype_map());
+ Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(map->prototype_info()),
+ isolate());
+ Handle<JSModuleNamespace> module_namespace(
+ JSModuleNamespace::cast(proto_info->module_namespace()), isolate());
+ Handle<Cell> cell(Cell::cast(module_namespace->module().exports().Lookup(
+ isolate(), name, Smi::ToInt(name->GetHash()))),
+ isolate());
+ if (cell->value().IsTheHole(isolate())) {
+ // This module has not been fully initialized yet.
+ return PropertyAccessInfo::Invalid(zone());
+ }
+ return PropertyAccessInfo::ModuleExport(zone(), receiver_map, cell);
+ }
+ if (access_mode == AccessMode::kHas) {
+ // HasProperty checks don't call getter/setters, existence is sufficient.
+ return PropertyAccessInfo::AccessorConstant(zone(), receiver_map,
+ Handle<Object>(), holder);
+ }
+ Handle<Object> accessors(descriptors->GetStrongValue(descriptor), isolate());
+ if (!accessors->IsAccessorPair()) {
+ return PropertyAccessInfo::Invalid(zone());
+ }
+ Handle<Object> accessor(access_mode == AccessMode::kLoad
+ ? Handle<AccessorPair>::cast(accessors)->getter()
+ : Handle<AccessorPair>::cast(accessors)->setter(),
+ isolate());
+ if (!accessor->IsJSFunction()) {
+ CallOptimization optimization(isolate(), accessor);
+ if (!optimization.is_simple_api_call() ||
+ optimization.IsCrossContextLazyAccessorPair(
+ *broker()->target_native_context().object(), *map)) {
+ return PropertyAccessInfo::Invalid(zone());
+ }
-
-bool AccessInfoFactory::ComputeElementAccessInfo(
- Handle<Map> map, AccessMode access_mode, ElementAccessInfo* access_info) {
- // Check if it is safe to inline element access for the {map}.
- if (!CanInlineElementAccess(map)) return false;
- ElementsKind const elements_kind = map->elements_kind();
- *access_info = ElementAccessInfo(MapList{map}, elements_kind);
- return true;
-}
-
-
-bool AccessInfoFactory::ComputeElementAccessInfos(
- MapHandleList const& maps, AccessMode access_mode,
- ZoneVector<ElementAccessInfo>* access_infos) {
- // Collect possible transition targets.
- MapHandleList possible_transition_targets(maps.length());
- for (Handle<Map> map : maps) {
- if (Map::TryUpdate(map).ToHandle(&map)) {
- if (CanInlineElementAccess(map) &&
- IsFastElementsKind(map->elements_kind()) &&
- GetInitialFastElementsKind() != map->elements_kind()) {
- possible_transition_targets.Add(map);
- }
+ CallOptimization::HolderLookup lookup;
+ holder = optimization.LookupHolderOfExpectedType(receiver_map, &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());
+ }
+ if (access_mode == AccessMode::kLoad) {
+ Handle<Name> cached_property_name;
+ if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), accessor)
+ .ToHandle(&cached_property_name)) {
+ PropertyAccessInfo access_info =
+ ComputePropertyAccessInfo(map, cached_property_name, access_mode);
+ if (!access_info.IsInvalid()) return access_info;
}
}
-
- // Separate the actual receiver maps and the possible transition sources.
- MapHandleList receiver_maps(maps.length());
- MapTransitionList transitions(maps.length());
- for (Handle<Map> map : maps) {
- if (Map::TryUpdate(map).ToHandle(&map)) {
- Map* transition_target = map->is_stable() ?
- nullptr :
- map->FindElementsKindTransitionedMap(&possible_transition_targets);
- if (transition_target == nullptr) {
- receiver_maps.Add(map);
- } else {
- transitions.push_back(std::make_pair(map, handle(transition_target)));
- }
- }
- }
-
- for (Handle<Map> receiver_map : receiver_maps) {
- // Compute the element access information.
- ElementAccessInfo access_info;
- if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
- return false;
- }
-
- // Collect the possible transitions for the {receiver_map}.
- for (auto transition : transitions) {
- if (transition.second.is_identical_to(receiver_map)) {
- access_info.transitions().push_back(transition);
- }
- }
-
- // Schedule the access information.
- access_infos->push_back(access_info);
- }
- return true;
+ return PropertyAccessInfo::AccessorConstant(zone(), receiver_map, accessor,
+ holder);
}
+MinimorphicLoadPropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
+ MinimorphicLoadPropertyAccessFeedback const& feedback) const {
+ DCHECK(feedback.handler()->IsSmi());
+ int handler = Smi::cast(*feedback.handler()).value();
+ bool is_inobject = LoadHandler::IsInobjectBits::decode(handler);
+ bool is_double = LoadHandler::IsDoubleBits::decode(handler);
+ int offset = LoadHandler::FieldIndexBits::decode(handler) * kTaggedSize;
+ Representation field_rep =
+ is_double ? Representation::Double() : Representation::Tagged();
+ Type field_type = is_double ? Type::Number() : Type::Any();
+ return MinimorphicLoadPropertyAccessInfo::DataField(offset, is_inobject,
+ field_rep, field_type);
+}
-bool AccessInfoFactory::ComputePropertyAccessInfo(
- Handle<Map> map, Handle<Name> name, AccessMode access_mode,
- PropertyAccessInfo* access_info) {
+PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
+ Handle<Map> map, Handle<Name> name, AccessMode access_mode) const {
+ CHECK(name->IsUniqueName());
+
+ if (access_mode == AccessMode::kHas && !map->IsJSReceiverMap()) {
+ return PropertyAccessInfo::Invalid(zone());
+ }
+
// Check if it is safe to inline property access for the {map}.
- if (!CanInlinePropertyAccess(map)) return false;
-
- // Compute the receiver type.
- Handle<Map> receiver_map = map;
-
- // Property lookups require the name to be internalized.
- name = isolate()->factory()->InternalizeName(name);
+ if (!CanInlinePropertyAccess(map)) {
+ return PropertyAccessInfo::Invalid(zone());
+ }
// We support fast inline cases for certain JSObject getters.
- if (access_mode == AccessMode::kLoad &&
- LookupSpecialFieldAccessor(map, name, access_info)) {
- return true;
+ if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
+ PropertyAccessInfo access_info = LookupSpecialFieldAccessor(map, name);
+ if (!access_info.IsInvalid()) return access_info;
}
+ // Remember the receiver map. We use {map} as loop variable.
+ Handle<Map> receiver_map = map;
MaybeHandle<JSObject> holder;
- do {
+ while (true) {
// Lookup the named property on the {map}.
- Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
- int const number = descriptors->SearchWithCache(isolate(), *name, *map);
- if (number != DescriptorArray::kNotFound) {
+ Handle<DescriptorArray> descriptors(map->instance_descriptors(kAcquireLoad),
+ isolate());
+ InternalIndex const number =
+ descriptors->Search(*name, *map, broker()->is_concurrent_inlining());
+ if (number.is_found()) {
PropertyDetails const details = descriptors->GetDetails(number);
if (access_mode == AccessMode::kStore ||
access_mode == AccessMode::kStoreInLiteral) {
// Don't bother optimizing stores to read-only properties.
if (details.IsReadOnly()) {
- return false;
+ return PropertyAccessInfo::Invalid(zone());
}
- // Check for store to data property on a prototype.
if (details.kind() == kData && !holder.is_null()) {
- // Store to property not found on the receiver but on a prototype, we
- // need to transition to a new data property.
- // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
- return LookupTransition(receiver_map, name, holder, access_info);
+ // 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
+ // if such a transition already exists.
+ return LookupTransition(receiver_map, name, holder);
}
}
if (details.location() == kField) {
if (details.kind() == kData) {
- int index = descriptors->GetFieldIndex(number);
- Representation details_representation = details.representation();
- FieldIndex field_index = FieldIndex::ForPropertyIndex(
- *map, index, details_representation.IsDouble());
- Type* field_type = Type::NonInternal();
- MachineRepresentation field_representation =
- MachineRepresentation::kTagged;
- MaybeHandle<Map> field_map;
- if (details_representation.IsSmi()) {
- field_type = Type::SignedSmall();
- field_representation = MachineRepresentation::kTaggedSigned;
- } else if (details_representation.IsDouble()) {
- field_type = type_cache_.kFloat64;
- field_representation = MachineRepresentation::kFloat64;
- } 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).
- field_representation = MachineRepresentation::kTaggedPointer;
- Handle<FieldType> descriptors_field_type(
- descriptors->GetFieldType(number), isolate());
- if (descriptors_field_type->IsNone()) {
- // Store is not safe if the field type was cleared.
- if (access_mode == AccessMode::kStore) return false;
-
- // The field type was cleared by the GC, so we don't know anything
- // about the contents now.
- } else if (descriptors_field_type->IsClass()) {
- // Add proper code dependencies in case of stable field map(s).
- Handle<Map> field_owner_map(map->FindFieldOwner(number),
- isolate());
- dependencies()->AssumeFieldOwner(field_owner_map);
-
- // Remember the field map, and try to infer a useful type.
- field_type = Type::For(descriptors_field_type->AsClass());
- field_map = descriptors_field_type->AsClass();
- }
- }
- *access_info = PropertyAccessInfo::DataField(
- details.constness(), MapList{receiver_map}, field_index,
- field_representation, field_type, field_map, holder);
- return true;
+ return ComputeDataFieldAccessInfo(receiver_map, map, holder, number,
+ access_mode);
} else {
DCHECK_EQ(kAccessor, details.kind());
// TODO(turbofan): Add support for general accessors?
- return false;
+ return PropertyAccessInfo::Invalid(zone());
}
-
} else {
DCHECK_EQ(kDescriptor, details.location());
- if (details.kind() == kData) {
- DCHECK(!FLAG_track_constant_fields);
- *access_info = PropertyAccessInfo::DataConstant(
- MapList{receiver_map},
- handle(descriptors->GetValue(number), isolate()), holder);
- return true;
- } else {
- DCHECK_EQ(kAccessor, details.kind());
- Handle<Object> accessors(descriptors->GetValue(number), isolate());
- if (!accessors->IsAccessorPair()) return false;
- Handle<Object> accessor(
- access_mode == AccessMode::kLoad
- ? Handle<AccessorPair>::cast(accessors)->getter()
- : Handle<AccessorPair>::cast(accessors)->setter(),
- isolate());
- if (!accessor->IsJSFunction()) {
- CallOptimization optimization(accessor);
- if (!optimization.is_simple_api_call()) {
- return false;
- }
- if (optimization.api_call_info()->fast_handler()->IsCode()) {
- return false;
- }
- if (V8_UNLIKELY(FLAG_runtime_stats)) return false;
- }
- if (access_mode == AccessMode::kLoad) {
- Handle<Name> cached_property_name;
- if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(),
- accessor)
- .ToHandle(&cached_property_name)) {
- if (ComputePropertyAccessInfo(map, cached_property_name,
- access_mode, access_info)) {
- return true;
- }
- }
- }
- *access_info = PropertyAccessInfo::AccessorConstant(
- MapList{receiver_map}, accessor, holder);
- return true;
- }
+ DCHECK_EQ(kAccessor, details.kind());
+ return ComputeAccessorDescriptorAccessInfo(receiver_map, name, map,
+ holder, number, access_mode);
}
UNREACHABLE();
- return false;
}
+ // The property wasn't found on {map}. Look on the prototype if appropriate.
+
// 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() &&
- IsSpecialIndex(isolate()->unicode_cache(), String::cast(*name))) {
- return false;
+ IsSpecialIndex(String::cast(*name))) {
+ return PropertyAccessInfo::Invalid(zone());
}
- // Don't search on the prototype when storing in literals
+ // Don't search on the prototype when storing in literals.
if (access_mode == AccessMode::kStoreInLiteral) {
- return LookupTransition(receiver_map, name, holder, access_info);
+ return LookupTransition(receiver_map, name, holder);
}
// Don't lookup private symbols on the prototype chain.
- if (name->IsPrivate()) return false;
+ if (name->IsPrivate()) {
+ return PropertyAccessInfo::Invalid(zone());
+ }
// Walk up the prototype chain.
- if (!map->prototype()->IsJSObject()) {
+ MapRef(broker(), map).SerializePrototype();
+ // 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(map->prototype().synchronized_map(),
+ isolate());
+ 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;
- if (Map::GetConstructorFunction(map, native_context())
+ if (Map::GetConstructorFunction(
+ map, broker()->target_native_context().object())
.ToHandle(&constructor)) {
map = handle(constructor->initial_map(), isolate());
- DCHECK(map->prototype()->IsJSObject());
- } else if (map->prototype()->IsNull(isolate())) {
+ map_prototype_map =
+ handle(map->prototype().synchronized_map(), isolate());
+ DCHECK(map_prototype_map->IsJSObjectMap());
+ } else if (map->prototype().IsNull()) {
// 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, access_info);
+ return LookupTransition(receiver_map, name, holder);
}
- // The property was not found, return undefined or throw depending
- // on the language mode of the load operation.
+ // 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)
- *access_info =
- PropertyAccessInfo::NotFound(MapList{receiver_map}, holder);
- return true;
+ return PropertyAccessInfo::NotFound(zone(), receiver_map, holder);
} else {
- return false;
+ return PropertyAccessInfo::Invalid(zone());
}
}
- Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
- if (map_prototype->map()->is_deprecated()) {
- // Try to migrate the prototype object so we don't embed the deprecated
- // map into the optimized code.
- JSObject::TryMigrateInstance(map_prototype);
+
+ holder = handle(JSObject::cast(map->prototype()), isolate());
+ map = map_prototype_map;
+ CHECK(!map->is_deprecated());
+
+ if (!CanInlinePropertyAccess(map)) {
+ return PropertyAccessInfo::Invalid(zone());
}
- map = handle(map_prototype->map(), isolate());
- holder = map_prototype;
- } while (CanInlinePropertyAccess(map));
- return false;
+
+ // Successful lookup on prototype chain needs to guarantee that all
+ // the prototypes up to the holder have stable maps. Let us make sure
+ // the prototype maps are stable here.
+ CHECK(map->is_stable());
+ }
+ UNREACHABLE();
}
-bool AccessInfoFactory::ComputePropertyAccessInfos(
- MapHandleList const& maps, Handle<Name> name, AccessMode access_mode,
- ZoneVector<PropertyAccessInfo>* access_infos) {
- for (Handle<Map> map : maps) {
- if (Map::TryUpdate(map).ToHandle(&map)) {
- PropertyAccessInfo access_info;
- if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) {
- return false;
- }
- // Try to merge the {access_info} with an existing one.
- bool merged = false;
- for (PropertyAccessInfo& other_info : *access_infos) {
- if (other_info.Merge(&access_info)) {
- merged = true;
- break;
- }
- }
- if (!merged) access_infos->push_back(access_info);
+PropertyAccessInfo AccessInfoFactory::FinalizePropertyAccessInfosAsOne(
+ ZoneVector<PropertyAccessInfo> access_infos, AccessMode access_mode) const {
+ ZoneVector<PropertyAccessInfo> merged_access_infos(zone());
+ MergePropertyAccessInfos(access_infos, access_mode, &merged_access_infos);
+ if (merged_access_infos.size() == 1) {
+ PropertyAccessInfo& result = merged_access_infos.front();
+ if (!result.IsInvalid()) {
+ result.RecordDependencies(dependencies());
+ return result;
}
}
+ return PropertyAccessInfo::Invalid(zone());
+}
+
+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_) {
+ dependencies->RecordDependency(d);
+ }
+ unrecorded_dependencies_.clear();
+}
+
+bool AccessInfoFactory::FinalizePropertyAccessInfos(
+ ZoneVector<PropertyAccessInfo> access_infos, AccessMode access_mode,
+ ZoneVector<PropertyAccessInfo>* result) const {
+ if (access_infos.empty()) return false;
+ MergePropertyAccessInfos(access_infos, access_mode, result);
+ for (PropertyAccessInfo const& info : *result) {
+ if (info.IsInvalid()) return false;
+ }
+ for (PropertyAccessInfo& info : *result) {
+ info.RecordDependencies(dependencies());
+ }
return true;
}
+void AccessInfoFactory::MergePropertyAccessInfos(
+ ZoneVector<PropertyAccessInfo> infos, AccessMode access_mode,
+ ZoneVector<PropertyAccessInfo>* result) const {
+ DCHECK(result->empty());
+ for (auto it = infos.begin(), end = infos.end(); it != end; ++it) {
+ bool merged = false;
+ for (auto ot = it + 1; ot != end; ++ot) {
+ if (ot->Merge(&(*it), access_mode, zone())) {
+ merged = true;
+ break;
+ }
+ }
+ if (!merged) result->push_back(*it);
+ }
+ CHECK(!result->empty());
+}
-bool AccessInfoFactory::LookupSpecialFieldAccessor(
- Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) {
+Isolate* AccessInfoFactory::isolate() const { return broker()->isolate(); }
+
+namespace {
+
+Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
+ ElementsKind that_kind) {
+ if (IsHoleyElementsKind(this_kind)) {
+ that_kind = GetHoleyElementsKind(that_kind);
+ } else if (IsHoleyElementsKind(that_kind)) {
+ this_kind = GetHoleyElementsKind(this_kind);
+ }
+ if (this_kind == that_kind) return Just(this_kind);
+ if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
+ if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
+ return Just(this_kind);
+ }
+ if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
+ return Just(that_kind);
+ }
+ }
+ return Nothing<ElementsKind>();
+}
+
+} // namespace
+
+base::Optional<ElementAccessInfo> AccessInfoFactory::ConsolidateElementLoad(
+ ElementAccessFeedback const& feedback) const {
+ if (feedback.transition_groups().empty()) return base::nullopt;
+
+ DCHECK(!feedback.transition_groups().front().empty());
+ MapRef first_map(broker(), feedback.transition_groups().front().front());
+ InstanceType instance_type = first_map.instance_type();
+ ElementsKind elements_kind = first_map.elements_kind();
+
+ ZoneVector<Handle<Map>> maps(zone());
+ for (auto const& group : feedback.transition_groups()) {
+ for (Handle<Map> map_handle : group) {
+ MapRef map(broker(), map_handle);
+ if (map.instance_type() != instance_type ||
+ !CanInlineElementAccess(map)) {
+ return base::nullopt;
+ }
+ if (!GeneralizeElementsKind(elements_kind, map.elements_kind())
+ .To(&elements_kind)) {
+ return base::nullopt;
+ }
+ maps.push_back(map.object());
+ }
+ }
+
+ return ElementAccessInfo(std::move(maps), elements_kind, zone());
+}
+
+PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor(
+ Handle<Map> map, Handle<Name> name) const {
+ // Check for String::length field accessor.
+ if (map->IsStringMap()) {
+ if (Name::Equals(isolate(), name, isolate()->factory()->length_string())) {
+ return PropertyAccessInfo::StringLength(zone(), map);
+ }
+ return PropertyAccessInfo::Invalid(zone());
+ }
// Check for special JSObject field accessors.
- int offset;
- if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) {
- FieldIndex field_index = FieldIndex::ForInObjectOffset(offset);
- Type* field_type = Type::NonInternal();
- MachineRepresentation field_representation = MachineRepresentation::kTagged;
- if (map->IsStringMap()) {
- DCHECK(Name::Equals(factory()->length_string(), name));
- // The String::length property is always a smi in the range
- // [0, String::kMaxLength].
- field_type = type_cache_.kStringLengthType;
- field_representation = MachineRepresentation::kTaggedSigned;
- } else if (map->IsJSArrayMap()) {
- DCHECK(Name::Equals(factory()->length_string(), name));
+ FieldIndex field_index;
+ if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
+ Type field_type = Type::NonInternal();
+ Representation field_representation = Representation::Tagged();
+ if (map->IsJSArrayMap()) {
+ DCHECK(
+ Name::Equals(isolate(), isolate()->factory()->length_string(), name));
// 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 (IsFastDoubleElementsKind(map->elements_kind())) {
- field_type = type_cache_.kFixedDoubleArrayLengthType;
- field_representation = MachineRepresentation::kTaggedSigned;
+ if (IsDoubleElementsKind(map->elements_kind())) {
+ field_type = type_cache_->kFixedDoubleArrayLengthType;
+ field_representation = Representation::Smi();
} else if (IsFastElementsKind(map->elements_kind())) {
- field_type = type_cache_.kFixedArrayLengthType;
- field_representation = MachineRepresentation::kTaggedSigned;
+ field_type = type_cache_->kFixedArrayLengthType;
+ field_representation = Representation::Smi();
} else {
- field_type = type_cache_.kJSArrayLengthType;
+ field_type = type_cache_->kJSArrayLengthType;
}
}
// Special fields are always mutable.
- *access_info = PropertyAccessInfo::DataField(
- kMutable, MapList{map}, field_index, field_representation, field_type);
- return true;
+ return PropertyAccessInfo::DataField(zone(), map, {{}, zone()}, field_index,
+ field_representation, field_type, map);
}
- return false;
+ return PropertyAccessInfo::Invalid(zone());
}
-
-bool AccessInfoFactory::LookupTransition(Handle<Map> map, Handle<Name> name,
- MaybeHandle<JSObject> holder,
- PropertyAccessInfo* access_info) {
+PropertyAccessInfo AccessInfoFactory::LookupTransition(
+ Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder) const {
// Check if the {map} has a data transition with the given {name}.
- if (map->unused_property_fields() == 0) {
- *access_info = PropertyAccessInfo::Generic(MapList{map});
- return true;
+ Map transition =
+ TransitionsAccessor(isolate(), map, broker()->is_concurrent_inlining())
+ .SearchTransition(*name, kData, NONE);
+ if (transition.is_null()) {
+ return PropertyAccessInfo::Invalid(zone());
}
- Handle<Map> transition_map;
- if (TransitionArray::SearchTransition(map, kData, name, NONE)
- .ToHandle(&transition_map)) {
- int const number = transition_map->LastAdded();
- PropertyDetails const details =
- transition_map->instance_descriptors()->GetDetails(number);
- // Don't bother optimizing stores to read-only properties.
- if (details.IsReadOnly()) return false;
- // TODO(bmeurer): Handle transition to data constant?
- if (details.location() != kField) return false;
- int const index = details.field_index();
- Representation details_representation = details.representation();
- FieldIndex field_index = FieldIndex::ForPropertyIndex(
- *transition_map, index, details_representation.IsDouble());
- Type* field_type = Type::NonInternal();
- MaybeHandle<Map> field_map;
- MachineRepresentation field_representation = MachineRepresentation::kTagged;
- if (details_representation.IsSmi()) {
- field_type = Type::SignedSmall();
- field_representation = MachineRepresentation::kTaggedSigned;
- } else if (details_representation.IsDouble()) {
- field_type = type_cache_.kFloat64;
- field_representation = MachineRepresentation::kFloat64;
- } 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).
- field_representation = MachineRepresentation::kTaggedPointer;
- Handle<FieldType> descriptors_field_type(
- transition_map->instance_descriptors()->GetFieldType(number),
- isolate());
- if (descriptors_field_type->IsNone()) {
- // Store is not safe if the field type was cleared.
- return false;
- } else if (descriptors_field_type->IsClass()) {
- // Add proper code dependencies in case of stable field map(s).
- Handle<Map> field_owner_map(transition_map->FindFieldOwner(number),
- isolate());
- dependencies()->AssumeFieldOwner(field_owner_map);
- // Remember the field map, and try to infer a useful type.
- field_type = Type::For(descriptors_field_type->AsClass());
- field_map = descriptors_field_type->AsClass();
- }
+ Handle<Map> transition_map(transition, isolate());
+ InternalIndex const number = transition_map->LastAdded();
+ Handle<DescriptorArray> descriptors(
+ transition_map->instance_descriptors(kAcquireLoad), isolate());
+ PropertyDetails const details = descriptors->GetDetails(number);
+ // Don't bother optimizing stores to read-only properties.
+ if (details.IsReadOnly()) {
+ return PropertyAccessInfo::Invalid(zone());
+ }
+ // TODO(bmeurer): Handle transition to data constant?
+ if (details.location() != kField) {
+ return PropertyAccessInfo::Invalid(zone());
+ }
+ int const index = details.field_index();
+ Representation details_representation = details.representation();
+ FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index,
+ details_representation);
+ Type field_type = Type::NonInternal();
+ MaybeHandle<Map> field_map;
+ MapRef transition_map_ref(broker(), transition_map);
+ ZoneVector<CompilationDependency const*> unrecorded_dependencies(zone());
+ if (details_representation.IsSmi()) {
+ field_type = Type::SignedSmall();
+ transition_map_ref.SerializeOwnDescriptor(number);
+ unrecorded_dependencies.push_back(
+ dependencies()->FieldRepresentationDependencyOffTheRecord(
+ transition_map_ref, number));
+ } else if (details_representation.IsDouble()) {
+ field_type = type_cache_->kFloat64;
+ if (!FLAG_unbox_double_fields) {
+ transition_map_ref.SerializeOwnDescriptor(number);
+ unrecorded_dependencies.push_back(
+ dependencies()->FieldRepresentationDependencyOffTheRecord(
+ transition_map_ref, number));
}
- dependencies()->AssumeMapNotDeprecated(transition_map);
- // Transitioning stores are never stores to constant fields.
- *access_info = PropertyAccessInfo::DataField(
- kMutable, MapList{map}, field_index, field_representation, field_type,
- field_map, holder, transition_map);
- return true;
+ } 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(descriptors->GetFieldType(number),
+ isolate());
+ if (descriptors_field_type->IsNone()) {
+ // Store is not safe if the field type was cleared.
+ return PropertyAccessInfo::Invalid(zone());
+ }
+ transition_map_ref.SerializeOwnDescriptor(number);
+ unrecorded_dependencies.push_back(
+ dependencies()->FieldRepresentationDependencyOffTheRecord(
+ transition_map_ref, number));
+ if (descriptors_field_type->IsClass()) {
+ unrecorded_dependencies.push_back(
+ dependencies()->FieldTypeDependencyOffTheRecord(transition_map_ref,
+ number));
+ // Remember the field map, and try to infer a useful type.
+ Handle<Map> map(descriptors_field_type->AsClass(), isolate());
+ field_type = Type::For(MapRef(broker(), map));
+ field_map = MaybeHandle<Map>(map);
+ }
}
- return false;
+ unrecorded_dependencies.push_back(
+ dependencies()->TransitionDependencyOffTheRecord(
+ MapRef(broker(), transition_map)));
+ transition_map_ref.SerializeBackPointer(); // 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 (details.constness()) {
+ case PropertyConstness::kMutable:
+ return PropertyAccessInfo::DataField(
+ zone(), map, std::move(unrecorded_dependencies), field_index,
+ details_representation, field_type, transition_map, field_map, holder,
+ transition_map);
+ case PropertyConstness::kConst:
+ return PropertyAccessInfo::DataConstant(
+ zone(), map, std::move(unrecorded_dependencies), field_index,
+ details_representation, field_type, transition_map, field_map, holder,
+ transition_map);
+ }
+ UNREACHABLE();
}
-
-Factory* AccessInfoFactory::factory() const { return isolate()->factory(); }
-
} // namespace compiler
} // namespace internal
} // namespace v8