LCOV - code coverage report
Current view: top level - src/compiler - access-info.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 216 242 89.3 %
Date: 2019-04-19 Functions: 18 29 62.1 %

          Line data    Source code
       1             : // Copyright 2015 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include <ostream>
       6             : 
       7             : #include "src/compiler/access-info.h"
       8             : 
       9             : #include "src/accessors.h"
      10             : #include "src/compiler/compilation-dependencies.h"
      11             : #include "src/compiler/type-cache.h"
      12             : #include "src/counters.h"
      13             : #include "src/field-index-inl.h"
      14             : #include "src/field-type.h"
      15             : #include "src/ic/call-optimization.h"
      16             : #include "src/objects-inl.h"
      17             : #include "src/objects/cell-inl.h"
      18             : #include "src/objects/module-inl.h"
      19             : #include "src/objects/struct-inl.h"
      20             : #include "src/objects/templates.h"
      21             : 
      22             : namespace v8 {
      23             : namespace internal {
      24             : namespace compiler {
      25             : 
      26             : namespace {
      27             : 
      28      256532 : bool CanInlinePropertyAccess(Handle<Map> map) {
      29             :   // We can inline property access to prototypes of all primitives, except
      30             :   // the special Oddball ones that have no wrapper counterparts (i.e. Null,
      31             :   // Undefined and TheHole).
      32             :   STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_TYPE);
      33      256532 :   if (map->IsBooleanMap()) return true;
      34      256005 :   if (map->instance_type() < LAST_PRIMITIVE_TYPE) return true;
      35      480738 :   return map->IsJSObjectMap() && !map->is_dictionary_map() &&
      36      481912 :          !map->has_named_interceptor() &&
      37             :          // TODO(verwaest): Whitelist contexts to which we have access.
      38             :          !map->is_access_check_needed();
      39             : }
      40             : 
      41             : }  // namespace
      42             : 
      43             : 
      44           0 : std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
      45           0 :   switch (access_mode) {
      46             :     case AccessMode::kLoad:
      47           0 :       return os << "Load";
      48             :     case AccessMode::kStore:
      49           0 :       return os << "Store";
      50             :     case AccessMode::kStoreInLiteral:
      51           0 :       return os << "StoreInLiteral";
      52             :     case AccessMode::kHas:
      53           0 :       return os << "Has";
      54             :   }
      55           0 :   UNREACHABLE();
      56             : }
      57             : 
      58             : ElementAccessInfo::ElementAccessInfo() = default;
      59             : 
      60       23163 : ElementAccessInfo::ElementAccessInfo(MapHandles const& receiver_maps,
      61             :                                      ElementsKind elements_kind)
      62       23163 :     : elements_kind_(elements_kind), receiver_maps_(receiver_maps) {
      63       23163 :   CHECK(!receiver_maps.empty());
      64       23163 : }
      65             : 
      66             : // static
      67           0 : PropertyAccessInfo PropertyAccessInfo::NotFound(MapHandles const& receiver_maps,
      68             :                                                 MaybeHandle<JSObject> holder) {
      69           0 :   return PropertyAccessInfo(kNotFound, holder, receiver_maps);
      70             : }
      71             : 
      72             : // static
      73           0 : PropertyAccessInfo PropertyAccessInfo::DataConstant(
      74             :     MapHandles const& receiver_maps, Handle<Object> constant,
      75             :     MaybeHandle<JSObject> holder) {
      76           0 :   return PropertyAccessInfo(kDataConstant, holder, constant, receiver_maps);
      77             : }
      78             : 
      79             : // static
      80           0 : PropertyAccessInfo PropertyAccessInfo::DataField(
      81             :     PropertyConstness constness, MapHandles const& receiver_maps,
      82             :     FieldIndex field_index, MachineRepresentation field_representation,
      83             :     Type field_type, MaybeHandle<Map> field_map, MaybeHandle<JSObject> holder,
      84             :     MaybeHandle<Map> transition_map) {
      85             :   Kind kind =
      86      101596 :       constness == PropertyConstness::kConst ? kDataConstantField : kDataField;
      87             :   return PropertyAccessInfo(kind, holder, transition_map, field_index,
      88             :                             field_representation, field_type, field_map,
      89           0 :                             receiver_maps);
      90             : }
      91             : 
      92             : // static
      93           0 : PropertyAccessInfo PropertyAccessInfo::AccessorConstant(
      94             :     MapHandles const& receiver_maps, Handle<Object> constant,
      95             :     MaybeHandle<JSObject> holder) {
      96           0 :   return PropertyAccessInfo(kAccessorConstant, holder, constant, receiver_maps);
      97             : }
      98             : 
      99             : // static
     100           0 : PropertyAccessInfo PropertyAccessInfo::ModuleExport(
     101             :     MapHandles const& receiver_maps, Handle<Cell> cell) {
     102             :   return PropertyAccessInfo(kModuleExport, MaybeHandle<JSObject>(), cell,
     103           0 :                             receiver_maps);
     104             : }
     105             : 
     106             : // static
     107           0 : PropertyAccessInfo PropertyAccessInfo::StringLength(
     108             :     MapHandles const& receiver_maps) {
     109             :   return PropertyAccessInfo(kStringLength, MaybeHandle<JSObject>(),
     110           0 :                             receiver_maps);
     111             : }
     112             : 
     113           0 : PropertyAccessInfo::PropertyAccessInfo()
     114             :     : kind_(kInvalid),
     115             :       field_representation_(MachineRepresentation::kNone),
     116      247096 :       field_type_(Type::None()) {}
     117             : 
     118           0 : PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
     119             :                                        MapHandles const& receiver_maps)
     120             :     : kind_(kind),
     121             :       receiver_maps_(receiver_maps),
     122             :       holder_(holder),
     123             :       field_representation_(MachineRepresentation::kNone),
     124       15852 :       field_type_(Type::None()) {}
     125             : 
     126           0 : PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
     127             :                                        Handle<Object> constant,
     128             :                                        MapHandles const& receiver_maps)
     129             :     : kind_(kind),
     130             :       receiver_maps_(receiver_maps),
     131             :       constant_(constant),
     132             :       holder_(holder),
     133             :       field_representation_(MachineRepresentation::kNone),
     134       10506 :       field_type_(Type::Any()) {}
     135             : 
     136           0 : PropertyAccessInfo::PropertyAccessInfo(
     137             :     Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
     138             :     FieldIndex field_index, MachineRepresentation field_representation,
     139             :     Type field_type, MaybeHandle<Map> field_map,
     140             :     MapHandles const& receiver_maps)
     141             :     : kind_(kind),
     142             :       receiver_maps_(receiver_maps),
     143             :       transition_map_(transition_map),
     144             :       holder_(holder),
     145             :       field_index_(field_index),
     146             :       field_representation_(field_representation),
     147             :       field_type_(field_type),
     148      262626 :       field_map_(field_map) {}
     149             : 
     150       17348 : bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
     151             :                                AccessMode access_mode, Zone* zone) {
     152       17348 :   if (this->kind_ != that->kind_) return false;
     153       16581 :   if (this->holder_.address() != that->holder_.address()) return false;
     154             : 
     155       15657 :   switch (this->kind_) {
     156             :     case kInvalid:
     157        4038 :       return that->kind_ == kInvalid;
     158             : 
     159             :     case kDataField:
     160             :     case kDataConstantField: {
     161             :       // Check if we actually access the same field (we use the
     162             :       // GetFieldAccessStubKey method here just like the ICs do
     163             :       // since that way we only compare the relevant bits of the
     164             :       // field indices).
     165       11288 :       if (this->field_index_.GetFieldAccessStubKey() ==
     166             :           that->field_index_.GetFieldAccessStubKey()) {
     167       10701 :         switch (access_mode) {
     168             :           case AccessMode::kHas:
     169             :           case AccessMode::kLoad: {
     170        6272 :             if (this->field_representation_ != that->field_representation_) {
     171         748 :               if (!IsAnyTagged(this->field_representation_) ||
     172             :                   !IsAnyTagged(that->field_representation_)) {
     173             :                 return false;
     174             :               }
     175         374 :               this->field_representation_ = MachineRepresentation::kTagged;
     176             :             }
     177        6272 :             if (this->field_map_.address() != that->field_map_.address()) {
     178         211 :               this->field_map_ = MaybeHandle<Map>();
     179             :             }
     180             :             break;
     181             :           }
     182             :           case AccessMode::kStore:
     183             :           case AccessMode::kStoreInLiteral: {
     184             :             // For stores, the field map and field representation information
     185             :             // must match exactly, otherwise we cannot merge the stores. We
     186             :             // also need to make sure that in case of transitioning stores,
     187             :             // the transition targets match.
     188        8810 :             if (this->field_map_.address() != that->field_map_.address() ||
     189        8702 :                 this->field_representation_ != that->field_representation_ ||
     190             :                 this->transition_map_.address() !=
     191             :                     that->transition_map_.address()) {
     192             :               return false;
     193             :             }
     194             :             break;
     195             :           }
     196             :         }
     197             :         // Merge the field type.
     198             :         this->field_type_ =
     199        6370 :             Type::Union(this->field_type_, that->field_type_, zone);
     200             :         // Merge the receiver maps.
     201             :         this->receiver_maps_.insert(this->receiver_maps_.end(),
     202             :                                     that->receiver_maps_.begin(),
     203        6370 :                                     that->receiver_maps_.end());
     204        6370 :         return true;
     205             :       }
     206             :       return false;
     207             :     }
     208             : 
     209             :     case kDataConstant:
     210             :     case kAccessorConstant: {
     211             :       // Check if we actually access the same constant.
     212         254 :       if (this->constant_.address() == that->constant_.address()) {
     213             :         this->receiver_maps_.insert(this->receiver_maps_.end(),
     214             :                                     that->receiver_maps_.begin(),
     215         247 :                                     that->receiver_maps_.end());
     216         247 :         return true;
     217             :       }
     218             :       return false;
     219             :     }
     220             : 
     221             :     case kNotFound:
     222             :     case kStringLength: {
     223             :       this->receiver_maps_.insert(this->receiver_maps_.end(),
     224             :                                   that->receiver_maps_.begin(),
     225          77 :                                   that->receiver_maps_.end());
     226          77 :       return true;
     227             :     }
     228             :     case kModuleExport:
     229             :       return false;
     230             :   }
     231           0 : }
     232             : 
     233           7 : Handle<Cell> PropertyAccessInfo::export_cell() const {
     234             :   DCHECK_EQ(kModuleExport, kind_);
     235           7 :   return Handle<Cell>::cast(constant_);
     236             : }
     237             : 
     238      165941 : AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
     239             :                                      CompilationDependencies* dependencies,
     240             :                                      Zone* zone)
     241             :     : broker_(broker),
     242             :       dependencies_(dependencies),
     243      165941 :       type_cache_(TypeCache::Get()),
     244      331882 :       zone_(zone) {}
     245             : 
     246       11039 : bool AccessInfoFactory::ComputeElementAccessInfo(
     247             :     Handle<Map> map, AccessMode access_mode,
     248             :     ElementAccessInfo* access_info) const {
     249             :   // Check if it is safe to inline element access for the {map}.
     250             :   MapRef map_ref(broker(), map);
     251       11039 :   if (!CanInlineElementAccess(map_ref)) return false;
     252        9707 :   ElementsKind const elements_kind = map_ref.elements_kind();
     253       38828 :   *access_info = ElementAccessInfo(MapHandles{map}, elements_kind);
     254        9707 :   return true;
     255             : }
     256             : 
     257       23399 : bool AccessInfoFactory::ComputeElementAccessInfos(
     258             :     FeedbackNexus nexus, MapHandles const& maps, AccessMode access_mode,
     259             :     ZoneVector<ElementAccessInfo>* access_infos) const {
     260             :   ElementAccessFeedback const* processed =
     261             :       FLAG_concurrent_inlining
     262       23415 :           ? broker()->GetElementAccessFeedback(FeedbackSource(nexus))
     263       46798 :           : broker()->ProcessFeedbackMapsForElementAccess(maps);
     264       23399 :   if (processed == nullptr) return false;
     265             : 
     266       23391 :   if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
     267             :     // For polymorphic loads of similar elements kinds (i.e. all tagged or all
     268             :     // double), always use the "worst case" code without a transition.  This is
     269             :     // much faster than transitioning the elements to the worst case, trading a
     270             :     // TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
     271        1458 :     ElementAccessInfo access_info;
     272       14914 :     if (ConsolidateElementLoad(*processed, &access_info)) {
     273       13456 :       access_infos->push_back(access_info);
     274       13456 :       return true;
     275             :     }
     276             :   }
     277             : 
     278       19642 :   for (Handle<Map> receiver_map : processed->receiver_maps) {
     279             :     // Compute the element access information.
     280        9707 :     ElementAccessInfo access_info;
     281       11039 :     if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
     282        1332 :       return false;
     283             :     }
     284             : 
     285             :     // Collect the possible transitions for the {receiver_map}.
     286       10343 :     for (auto transition : processed->transitions) {
     287         636 :       if (transition.second.equals(receiver_map)) {
     288         560 :         access_info.AddTransitionSource(transition.first);
     289             :       }
     290             :     }
     291             : 
     292             :     // Schedule the access information.
     293        9707 :     access_infos->push_back(access_info);
     294             :   }
     295        8603 :   return true;
     296             : }
     297             : 
     298      101596 : PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
     299             :     Handle<Map> receiver_map, Handle<Map> map, MaybeHandle<JSObject> holder,
     300             :     int number, AccessMode access_mode) const {
     301             :   DCHECK_NE(number, DescriptorArray::kNotFound);
     302             :   Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
     303      101596 :   PropertyDetails const details = descriptors->GetDetails(number);
     304      203192 :   int index = descriptors->GetFieldIndex(number);
     305             :   Representation details_representation = details.representation();
     306      101596 :   if (details_representation.IsNone()) {
     307             :     // The ICs collect feedback in PREMONOMORPHIC state already,
     308             :     // but at this point the {receiver_map} might still contain
     309             :     // fields for which the representation has not yet been
     310             :     // determined by the runtime. So we need to catch this case
     311             :     // here and fall back to use the regular IC logic instead.
     312             :     return {};
     313             :   }
     314             :   FieldIndex field_index =
     315      101596 :       FieldIndex::ForPropertyIndex(*map, index, details_representation);
     316             :   Type field_type = Type::NonInternal();
     317             : #ifdef V8_COMPRESS_POINTERS
     318             :   MachineRepresentation field_representation =
     319             :       MachineRepresentation::kCompressed;
     320             : #else
     321             :   MachineRepresentation field_representation = MachineRepresentation::kTagged;
     322             : #endif
     323             :   MaybeHandle<Map> field_map;
     324             :   MapRef map_ref(broker(), map);
     325      101596 :   if (details_representation.IsSmi()) {
     326             :     field_type = Type::SignedSmall();
     327             : #ifdef V8_COMPRESS_POINTERS
     328             :     field_representation = MachineRepresentation::kCompressedSigned;
     329             : #else
     330             :     field_representation = MachineRepresentation::kTaggedSigned;
     331             : #endif
     332        7824 :     map_ref.SerializeOwnDescriptors();  // TODO(neis): Remove later.
     333        7824 :     dependencies()->DependOnFieldRepresentation(map_ref, number);
     334       93772 :   } else if (details_representation.IsDouble()) {
     335         909 :     field_type = type_cache_->kFloat64;
     336             :     field_representation = MachineRepresentation::kFloat64;
     337       92863 :   } else if (details_representation.IsHeapObject()) {
     338             :     // Extract the field type from the property details (make sure its
     339             :     // representation is TaggedPointer to reflect the heap object case).
     340             : #ifdef V8_COMPRESS_POINTERS
     341             :     field_representation = MachineRepresentation::kCompressedPointer;
     342             : #else
     343             :     field_representation = MachineRepresentation::kTaggedPointer;
     344             : #endif
     345             :     Handle<FieldType> descriptors_field_type(descriptors->GetFieldType(number),
     346      167382 :                                              isolate());
     347       83691 :     if (descriptors_field_type->IsNone()) {
     348             :       // Store is not safe if the field type was cleared.
     349          55 :       if (access_mode == AccessMode::kStore) return {};
     350             : 
     351             :       // The field type was cleared by the GC, so we don't know anything
     352             :       // about the contents now.
     353             :     }
     354       83691 :     map_ref.SerializeOwnDescriptors();  // TODO(neis): Remove later.
     355       83691 :     dependencies()->DependOnFieldRepresentation(map_ref, number);
     356       83691 :     if (descriptors_field_type->IsClass()) {
     357        3231 :       dependencies()->DependOnFieldType(map_ref, number);
     358             :       // Remember the field map, and try to infer a useful type.
     359        6462 :       Handle<Map> map(descriptors_field_type->AsClass(), isolate());
     360        3231 :       field_type = Type::For(MapRef(broker(), map));
     361             :       field_map = MaybeHandle<Map>(map);
     362             :     }
     363             :   }
     364             :   return PropertyAccessInfo::DataField(
     365             :       details.constness(), MapHandles{receiver_map}, field_index,
     366      203192 :       field_representation, field_type, field_map, holder);
     367             : }
     368             : 
     369        8109 : PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
     370             :     Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map,
     371             :     MaybeHandle<JSObject> holder, int number, AccessMode access_mode) const {
     372             :   DCHECK_NE(number, DescriptorArray::kNotFound);
     373             :   Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
     374             :   SLOW_DCHECK(number == descriptors->Search(*name, *map));
     375        8109 :   if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
     376             :     DCHECK(map->is_prototype_map());
     377             :     Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(map->prototype_info()),
     378             :                                      isolate());
     379             :     Handle<JSModuleNamespace> module_namespace(
     380             :         JSModuleNamespace::cast(proto_info->module_namespace()), isolate());
     381             :     Handle<Cell> cell(
     382          44 :         Cell::cast(module_namespace->module()->exports()->Lookup(
     383          44 :             ReadOnlyRoots(isolate()), name, Smi::ToInt(name->GetHash()))),
     384          66 :         isolate());
     385          22 :     if (cell->value()->IsTheHole(isolate())) {
     386             :       // This module has not been fully initialized yet.
     387             :       return {};
     388             :     }
     389          44 :     return PropertyAccessInfo::ModuleExport(MapHandles{receiver_map}, cell);
     390             :   }
     391        8087 :   if (access_mode == AccessMode::kHas) {
     392             :     // HasProperty checks don't call getter/setters, existence is sufficient.
     393             :     return PropertyAccessInfo::AccessorConstant(MapHandles{receiver_map},
     394         224 :                                                 Handle<Object>(), holder);
     395             :   }
     396             :   Handle<Object> accessors(descriptors->GetStrongValue(number), isolate());
     397        7975 :   if (!accessors->IsAccessorPair()) return {};
     398             :   Handle<Object> accessor(access_mode == AccessMode::kLoad
     399             :                               ? Handle<AccessorPair>::cast(accessors)->getter()
     400             :                               : Handle<AccessorPair>::cast(accessors)->setter(),
     401        6618 :                           isolate());
     402        6618 :   if (!accessor->IsJSFunction()) {
     403        3378 :     CallOptimization optimization(isolate(), accessor);
     404       10134 :     if (!optimization.is_simple_api_call() ||
     405         172 :         optimization.IsCrossContextLazyAccessorPair(
     406        3722 :             *broker()->native_context().object(), *map)) {
     407        3244 :       return {};
     408             :     }
     409             : 
     410             :     CallOptimization::HolderLookup lookup;
     411         166 :     holder = optimization.LookupHolderOfExpectedType(receiver_map, &lookup);
     412         166 :     if (lookup == CallOptimization::kHolderNotFound) return {};
     413             :     DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
     414             :                    holder.is_null());
     415             :     DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound, !holder.is_null());
     416         134 :     if (V8_UNLIKELY(TracingFlags::is_runtime_stats_enabled())) return {};
     417             :   }
     418        3374 :   if (access_mode == AccessMode::kLoad) {
     419             :     Handle<Name> cached_property_name;
     420        4986 :     if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), accessor)
     421             :             .ToHandle(&cached_property_name)) {
     422             :       PropertyAccessInfo access_info =
     423           6 :           ComputePropertyAccessInfo(map, cached_property_name, access_mode);
     424           6 :       if (!access_info.IsInvalid()) return access_info;
     425             :     }
     426             :   }
     427             :   return PropertyAccessInfo::AccessorConstant(MapHandles{receiver_map},
     428        6736 :                                               accessor, holder);
     429             : }
     430             : 
     431      159292 : PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
     432             :     Handle<Map> map, Handle<Name> name, AccessMode access_mode) const {
     433      159292 :   CHECK(name->IsUniqueName());
     434             : 
     435      159612 :   if (access_mode == AccessMode::kHas && !map->IsJSReceiverMap()) return {};
     436             : 
     437             :   // Check if it is safe to inline property access for the {map}.
     438      159285 :   if (!CanInlinePropertyAccess(map)) return {};
     439             : 
     440             :   // We support fast inline cases for certain JSObject getters.
     441      149835 :   if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
     442      113152 :     PropertyAccessInfo access_info = LookupSpecialFieldAccessor(map, name);
     443      113152 :     if (!access_info.IsInvalid()) return access_info;
     444             :   }
     445             : 
     446             :   // Remember the receiver map. We use {map} as loop variable.
     447             :   Handle<Map> receiver_map = map;
     448             :   MaybeHandle<JSObject> holder;
     449             :   while (true) {
     450             :     // Lookup the named property on the {map}.
     451             :     Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
     452      471502 :     int const number = descriptors->Search(*name, *map);
     453      235751 :     if (number != DescriptorArray::kNotFound) {
     454      115149 :       PropertyDetails const details = descriptors->GetDetails(number);
     455      115149 :       if (access_mode == AccessMode::kStore ||
     456             :           access_mode == AccessMode::kStoreInLiteral) {
     457             :         // Don't bother optimizing stores to read-only properties.
     458       15468 :         if (details.IsReadOnly()) return {};
     459       12228 :         if (details.kind() == kData && !holder.is_null()) {
     460             :           // This is a store to a property not found on the receiver but on a
     461             :           // prototype. According to ES6 section 9.1.9 [[Set]], we need to
     462             :           // create a new data property on the receiver. We can still optimize
     463             :           // if such a transition already exists.
     464        2204 :           return LookupTransition(receiver_map, name, holder);
     465             :         }
     466             :       }
     467      109705 :       if (details.location() == kField) {
     468      101596 :         if (details.kind() == kData) {
     469             :           return ComputeDataFieldAccessInfo(receiver_map, map, holder, number,
     470      101596 :                                             access_mode);
     471             :         } else {
     472             :           DCHECK_EQ(kAccessor, details.kind());
     473             :           // TODO(turbofan): Add support for general accessors?
     474             :           return {};
     475             :         }
     476             :       } else {
     477             :         DCHECK_EQ(kDescriptor, details.location());
     478        8109 :         if (details.kind() == kData) {
     479             :           DCHECK(!FLAG_track_constant_fields);
     480             :           return PropertyAccessInfo::DataConstant(
     481             :               MapHandles{receiver_map},
     482           0 :               handle(descriptors->GetStrongValue(number), isolate()), holder);
     483             :         } else {
     484             :           DCHECK_EQ(kAccessor, details.kind());
     485             :           return ComputeAccessorDescriptorAccessInfo(
     486        8109 :               receiver_map, name, map, holder, number, access_mode);
     487             :         }
     488             :       }
     489             :       UNREACHABLE();
     490             :     }
     491             : 
     492             :     // The property wasn't found on {map}. Look on the prototype if appropriate.
     493             : 
     494             :     // Don't search on the prototype chain for special indices in case of
     495             :     // integer indexed exotic objects (see ES6 section 9.4.5).
     496      123373 :     if (map->IsJSTypedArrayMap() && name->IsString() &&
     497        1365 :         IsSpecialIndex(String::cast(*name))) {
     498             :       return {};
     499             :     }
     500             : 
     501             :     // Don't search on the prototype when storing in literals.
     502      120595 :     if (access_mode == AccessMode::kStoreInLiteral) {
     503         110 :       return LookupTransition(receiver_map, name, holder);
     504             :     }
     505             : 
     506             :     // Don't lookup private symbols on the prototype chain.
     507      120485 :     if (name->IsPrivate()) return {};
     508             : 
     509             :     // Walk up the prototype chain.
     510      120484 :     if (!map->prototype()->IsJSObject()) {
     511             :       // Perform the implicit ToObject for primitives here.
     512             :       // Implemented according to ES6 section 7.3.2 GetV (V, P).
     513             :       Handle<JSFunction> constructor;
     514       94125 :       if (Map::GetConstructorFunction(map, broker()->native_context().object())
     515             :               .ToHandle(&constructor)) {
     516             :         map = handle(constructor->initial_map(), isolate());
     517             :         DCHECK(map->prototype()->IsJSObject());
     518       23237 :       } else if (map->prototype()->IsNull(isolate())) {
     519             :         // Store to property not found on the receiver or any prototype, we need
     520             :         // to transition to a new data property.
     521             :         // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
     522       23230 :         if (access_mode == AccessMode::kStore) {
     523       19742 :           return LookupTransition(receiver_map, name, holder);
     524             :         }
     525             :         // The property was not found (access returns undefined or throws
     526             :         // depending on the language mode of the load operation.
     527             :         // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
     528        6976 :         return PropertyAccessInfo::NotFound(MapHandles{receiver_map}, holder);
     529             :       } else {
     530             :         return {};
     531             :       }
     532             :     }
     533             :     Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
     534       97247 :     if (map_prototype->map()->is_deprecated()) {
     535             :       // Try to migrate the prototype object so we don't embed the deprecated
     536             :       // map into the optimized code.
     537           0 :       JSObject::TryMigrateInstance(map_prototype);
     538             :     }
     539             :     map = handle(map_prototype->map(), isolate());
     540             :     holder = map_prototype;
     541             : 
     542       97247 :     if (!CanInlinePropertyAccess(map)) return {};
     543             : 
     544             :     // Successful lookup on prototype chain needs to guarantee that all
     545             :     // the prototypes up to the holder have stable maps. Let us make sure
     546             :     // the prototype maps are stable here.
     547       95403 :     CHECK(map->is_stable());
     548             :   }
     549             : }
     550             : 
     551        1217 : PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
     552             :     MapHandles const& maps, Handle<Name> name, AccessMode access_mode) const {
     553             :   ZoneVector<PropertyAccessInfo> raw_access_infos(zone());
     554        1217 :   ComputePropertyAccessInfos(maps, name, access_mode, &raw_access_infos);
     555             :   ZoneVector<PropertyAccessInfo> access_infos(zone());
     556        2434 :   if (FinalizePropertyAccessInfos(raw_access_infos, access_mode,
     557        1738 :                                   &access_infos) &&
     558             :       access_infos.size() == 1) {
     559         521 :     return access_infos.front();
     560             :   }
     561             :   return {};
     562             : }
     563             : 
     564      141413 : void AccessInfoFactory::ComputePropertyAccessInfos(
     565             :     MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
     566             :     ZoneVector<PropertyAccessInfo>* access_infos) const {
     567             :   DCHECK(access_infos->empty());
     568      299570 :   for (Handle<Map> map : maps) {
     569      316314 :     access_infos->push_back(ComputePropertyAccessInfo(map, name, access_mode));
     570             :   }
     571      141413 : }
     572             : 
     573      141413 : bool AccessInfoFactory::FinalizePropertyAccessInfos(
     574             :     ZoneVector<PropertyAccessInfo> infos, AccessMode access_mode,
     575             :     ZoneVector<PropertyAccessInfo>* result) const {
     576             :   DCHECK(result->empty());
     577      280383 :   for (auto it = infos.begin(), end = infos.end(); it != end; ++it) {
     578             :     bool merged = false;
     579      160689 :     for (auto ot = it + 1; ot != end; ++ot) {
     580       17348 :       if (ot->Merge(&(*it), access_mode, zone())) {
     581             :         merged = true;
     582             :         break;
     583             :       }
     584             :     }
     585      154073 :     if (it->IsInvalid()) return false;
     586      138970 :     if (!merged) result->push_back(*it);
     587             :   }
     588      126310 :   CHECK(!result->empty());
     589             :   return true;
     590             : }
     591             : 
     592             : namespace {
     593             : 
     594       15307 : Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
     595             :                                            ElementsKind that_kind) {
     596       15307 :   if (IsHoleyElementsKind(this_kind)) {
     597             :     that_kind = GetHoleyElementsKind(that_kind);
     598       11800 :   } else if (IsHoleyElementsKind(that_kind)) {
     599             :     this_kind = GetHoleyElementsKind(this_kind);
     600             :   }
     601       15307 :   if (this_kind == that_kind) return Just(this_kind);
     602         756 :   if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
     603         696 :     if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
     604             :       return Just(this_kind);
     605             :     }
     606          96 :     if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
     607             :       return Just(that_kind);
     608             :     }
     609             :   }
     610             :   return Nothing<ElementsKind>();
     611             : }
     612             : 
     613             : }  // namespace
     614             : 
     615       14914 : bool AccessInfoFactory::ConsolidateElementLoad(
     616             :     ElementAccessFeedback const& processed,
     617             :     ElementAccessInfo* access_info) const {
     618       14914 :   ElementAccessFeedback::MapIterator it = processed.all_maps(broker());
     619       14914 :   MapRef first_map = it.current();
     620       14914 :   InstanceType instance_type = first_map.instance_type();
     621       14914 :   ElementsKind elements_kind = first_map.elements_kind();
     622             :   MapHandles maps;
     623       45216 :   for (; !it.done(); it.advance()) {
     624       16609 :     MapRef map = it.current();
     625       16609 :     if (map.instance_type() != instance_type || !CanInlineElementAccess(map)) {
     626        1458 :       return false;
     627             :     }
     628       30614 :     if (!GeneralizeElementsKind(elements_kind, map.elements_kind())
     629             :              .To(&elements_kind)) {
     630             :       return false;
     631             :     }
     632       30302 :     maps.push_back(map.object());
     633             :   }
     634             : 
     635       26912 :   *access_info = ElementAccessInfo(maps, elements_kind);
     636       13456 :   return true;
     637             : }
     638             : 
     639      113152 : PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor(
     640             :     Handle<Map> map, Handle<Name> name) const {
     641             :   // Check for String::length field accessor.
     642      113152 :   if (map->IsStringMap()) {
     643        7940 :     if (Name::Equals(isolate(), name, isolate()->factory()->length_string())) {
     644        3592 :       return PropertyAccessInfo::StringLength(MapHandles{map});
     645             :     }
     646             :     return {};
     647             :   }
     648             :   // Check for special JSObject field accessors.
     649             :   FieldIndex field_index;
     650      105212 :   if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
     651             :     Type field_type = Type::NonInternal();
     652             :     MachineRepresentation field_representation = MachineRepresentation::kTagged;
     653        7691 :     if (map->IsJSArrayMap()) {
     654             :       DCHECK(
     655             :           Name::Equals(isolate(), isolate()->factory()->length_string(), name));
     656             :       // The JSArray::length property is a smi in the range
     657             :       // [0, FixedDoubleArray::kMaxLength] in case of fast double
     658             :       // elements, a smi in the range [0, FixedArray::kMaxLength]
     659             :       // in case of other fast elements, and [0, kMaxUInt32] in
     660             :       // case of other arrays.
     661        7691 :       if (IsDoubleElementsKind(map->elements_kind())) {
     662         284 :         field_type = type_cache_->kFixedDoubleArrayLengthType;
     663             :         field_representation = MachineRepresentation::kTaggedSigned;
     664        7407 :       } else if (IsFastElementsKind(map->elements_kind())) {
     665        7122 :         field_type = type_cache_->kFixedArrayLengthType;
     666             :         field_representation = MachineRepresentation::kTaggedSigned;
     667             :       } else {
     668         285 :         field_type = type_cache_->kJSArrayLengthType;
     669             :       }
     670             :     }
     671             :     // Special fields are always mutable.
     672             :     return PropertyAccessInfo::DataField(PropertyConstness::kMutable,
     673             :                                          MapHandles{map}, field_index,
     674       15382 :                                          field_representation, field_type);
     675             :   }
     676             :   return {};
     677             : }
     678             : 
     679       22056 : PropertyAccessInfo AccessInfoFactory::LookupTransition(
     680             :     Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder) const {
     681             :   // Check if the {map} has a data transition with the given {name}.
     682             :   Map transition =
     683       22056 :       TransitionsAccessor(isolate(), map).SearchTransition(*name, kData, NONE);
     684       22056 :   if (transition.is_null()) return {};
     685             : 
     686             :   Handle<Map> transition_map(transition, isolate());
     687             :   int const number = transition_map->LastAdded();
     688             :   PropertyDetails const details =
     689       22026 :       transition_map->instance_descriptors()->GetDetails(number);
     690             :   // Don't bother optimizing stores to read-only properties.
     691       22026 :   if (details.IsReadOnly()) return {};
     692             :   // TODO(bmeurer): Handle transition to data constant?
     693       22026 :   if (details.location() != kField) return {};
     694             :   int const index = details.field_index();
     695             :   Representation details_representation = details.representation();
     696             :   FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index,
     697       22026 :                                                         details_representation);
     698             :   Type field_type = Type::NonInternal();
     699             :   MaybeHandle<Map> field_map;
     700             :   MachineRepresentation field_representation = MachineRepresentation::kTagged;
     701             :   MapRef transition_map_ref(broker(), transition_map);
     702       22026 :   if (details_representation.IsSmi()) {
     703             :     field_type = Type::SignedSmall();
     704             :     field_representation = MachineRepresentation::kTaggedSigned;
     705       18733 :     transition_map_ref.SerializeOwnDescriptors();  // TODO(neis): Remove later.
     706       18733 :     dependencies()->DependOnFieldRepresentation(transition_map_ref, number);
     707        3293 :   } else if (details_representation.IsDouble()) {
     708         152 :     field_type = type_cache_->kFloat64;
     709             :     field_representation = MachineRepresentation::kFloat64;
     710        3141 :   } else if (details_representation.IsHeapObject()) {
     711             :     // Extract the field type from the property details (make sure its
     712             :     // representation is TaggedPointer to reflect the heap object case).
     713             :     field_representation = MachineRepresentation::kTaggedPointer;
     714             :     Handle<FieldType> descriptors_field_type(
     715        1518 :         transition_map->instance_descriptors()->GetFieldType(number),
     716         759 :         isolate());
     717         759 :     if (descriptors_field_type->IsNone()) {
     718             :       // Store is not safe if the field type was cleared.
     719             :       return {};
     720             :     }
     721         759 :     transition_map_ref.SerializeOwnDescriptors();  // TODO(neis): Remove later.
     722         759 :     dependencies()->DependOnFieldRepresentation(transition_map_ref, number);
     723         759 :     if (descriptors_field_type->IsClass()) {
     724         126 :       dependencies()->DependOnFieldType(transition_map_ref, number);
     725             :       // Remember the field map, and try to infer a useful type.
     726         252 :       Handle<Map> map(descriptors_field_type->AsClass(), isolate());
     727         126 :       field_type = Type::For(MapRef(broker(), map));
     728             :       field_map = MaybeHandle<Map>(map);
     729             :     }
     730             :   }
     731       22026 :   dependencies()->DependOnTransition(MapRef(broker(), transition_map));
     732             :   // Transitioning stores are never stores to constant fields.
     733             :   return PropertyAccessInfo::DataField(
     734             :       PropertyConstness::kMutable, MapHandles{map}, field_index,
     735       44052 :       field_representation, field_type, field_map, holder, transition_map);
     736             : }
     737             : 
     738             : }  // namespace compiler
     739             : }  // namespace internal
     740      122036 : }  // namespace v8

Generated by: LCOV version 1.10