Home | History | Annotate | Download | only in compiler
      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/field-index-inl.h"
     13 #include "src/field-type.h"
     14 #include "src/ic/call-optimization.h"
     15 #include "src/objects-inl.h"
     16 #include "src/objects/module-inl.h"
     17 #include "src/objects/templates.h"
     18 
     19 namespace v8 {
     20 namespace internal {
     21 namespace compiler {
     22 
     23 namespace {
     24 
     25 bool CanInlineElementAccess(Handle<Map> map) {
     26   if (!map->IsJSObjectMap()) return false;
     27   if (map->is_access_check_needed()) return false;
     28   if (map->has_indexed_interceptor()) return false;
     29   ElementsKind const elements_kind = map->elements_kind();
     30   if (IsFastElementsKind(elements_kind)) return true;
     31   if (IsFixedTypedArrayElementsKind(elements_kind) &&
     32       elements_kind != BIGUINT64_ELEMENTS &&
     33       elements_kind != BIGINT64_ELEMENTS) {
     34     return true;
     35   }
     36   return false;
     37 }
     38 
     39 
     40 bool CanInlinePropertyAccess(Handle<Map> map) {
     41   // We can inline property access to prototypes of all primitives, except
     42   // the special Oddball ones that have no wrapper counterparts (i.e. Null,
     43   // Undefined and TheHole).
     44   STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_TYPE);
     45   if (map->IsBooleanMap()) return true;
     46   if (map->instance_type() < LAST_PRIMITIVE_TYPE) return true;
     47   return map->IsJSObjectMap() && !map->is_dictionary_map() &&
     48          !map->has_named_interceptor() &&
     49          // TODO(verwaest): Whitelist contexts to which we have access.
     50          !map->is_access_check_needed();
     51 }
     52 
     53 }  // namespace
     54 
     55 
     56 std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
     57   switch (access_mode) {
     58     case AccessMode::kLoad:
     59       return os << "Load";
     60     case AccessMode::kStore:
     61       return os << "Store";
     62     case AccessMode::kStoreInLiteral:
     63       return os << "StoreInLiteral";
     64   }
     65   UNREACHABLE();
     66 }
     67 
     68 ElementAccessInfo::ElementAccessInfo() {}
     69 
     70 ElementAccessInfo::ElementAccessInfo(MapHandles const& receiver_maps,
     71                                      ElementsKind elements_kind)
     72     : elements_kind_(elements_kind), receiver_maps_(receiver_maps) {}
     73 
     74 // static
     75 PropertyAccessInfo PropertyAccessInfo::NotFound(MapHandles const& receiver_maps,
     76                                                 MaybeHandle<JSObject> holder) {
     77   return PropertyAccessInfo(holder, receiver_maps);
     78 }
     79 
     80 // static
     81 PropertyAccessInfo PropertyAccessInfo::DataConstant(
     82     MapHandles const& receiver_maps, Handle<Object> constant,
     83     MaybeHandle<JSObject> holder) {
     84   return PropertyAccessInfo(kDataConstant, holder, constant, receiver_maps);
     85 }
     86 
     87 // static
     88 PropertyAccessInfo PropertyAccessInfo::DataField(
     89     PropertyConstness constness, MapHandles const& receiver_maps,
     90     FieldIndex field_index, MachineRepresentation field_representation,
     91     Type field_type, MaybeHandle<Map> field_map, MaybeHandle<JSObject> holder,
     92     MaybeHandle<Map> transition_map) {
     93   Kind kind =
     94       constness == PropertyConstness::kConst ? kDataConstantField : kDataField;
     95   return PropertyAccessInfo(kind, holder, transition_map, field_index,
     96                             field_representation, field_type, field_map,
     97                             receiver_maps);
     98 }
     99 
    100 // static
    101 PropertyAccessInfo PropertyAccessInfo::AccessorConstant(
    102     MapHandles const& receiver_maps, Handle<Object> constant,
    103     MaybeHandle<JSObject> holder) {
    104   return PropertyAccessInfo(kAccessorConstant, holder, constant, receiver_maps);
    105 }
    106 
    107 // static
    108 PropertyAccessInfo PropertyAccessInfo::ModuleExport(
    109     MapHandles const& receiver_maps, Handle<Cell> cell) {
    110   return PropertyAccessInfo(kModuleExport, MaybeHandle<JSObject>(), cell,
    111                             receiver_maps);
    112 }
    113 
    114 PropertyAccessInfo::PropertyAccessInfo()
    115     : kind_(kInvalid),
    116       field_representation_(MachineRepresentation::kNone),
    117       field_type_(Type::None()) {}
    118 
    119 PropertyAccessInfo::PropertyAccessInfo(MaybeHandle<JSObject> holder,
    120                                        MapHandles const& receiver_maps)
    121     : kind_(kNotFound),
    122       receiver_maps_(receiver_maps),
    123       holder_(holder),
    124       field_representation_(MachineRepresentation::kNone),
    125       field_type_(Type::None()) {}
    126 
    127 PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
    128                                        Handle<Object> constant,
    129                                        MapHandles const& receiver_maps)
    130     : kind_(kind),
    131       receiver_maps_(receiver_maps),
    132       constant_(constant),
    133       holder_(holder),
    134       field_representation_(MachineRepresentation::kNone),
    135       field_type_(Type::Any()) {}
    136 
    137 PropertyAccessInfo::PropertyAccessInfo(
    138     Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
    139     FieldIndex field_index, MachineRepresentation field_representation,
    140     Type field_type, MaybeHandle<Map> field_map,
    141     MapHandles const& receiver_maps)
    142     : kind_(kind),
    143       receiver_maps_(receiver_maps),
    144       transition_map_(transition_map),
    145       holder_(holder),
    146       field_index_(field_index),
    147       field_representation_(field_representation),
    148       field_type_(field_type),
    149       field_map_(field_map) {}
    150 
    151 bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
    152                                AccessMode access_mode, Zone* zone) {
    153   if (this->kind_ != that->kind_) return false;
    154   if (this->holder_.address() != that->holder_.address()) return false;
    155 
    156   switch (this->kind_) {
    157     case kInvalid:
    158       break;
    159 
    160     case kDataField:
    161     case kDataConstantField: {
    162       // Check if we actually access the same field (we use the
    163       // GetFieldAccessStubKey method here just like the ICs do
    164       // since that way we only compare the relevant bits of the
    165       // field indices).
    166       if (this->field_index_.GetFieldAccessStubKey() ==
    167           that->field_index_.GetFieldAccessStubKey()) {
    168         switch (access_mode) {
    169           case AccessMode::kLoad: {
    170             if (this->field_representation_ != that->field_representation_) {
    171               if (!IsAnyTagged(this->field_representation_) ||
    172                   !IsAnyTagged(that->field_representation_)) {
    173                 return false;
    174               }
    175               this->field_representation_ = MachineRepresentation::kTagged;
    176             }
    177             if (this->field_map_.address() != that->field_map_.address()) {
    178               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             if (this->field_map_.address() != that->field_map_.address() ||
    189                 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             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                                     that->receiver_maps_.end());
    204         return true;
    205       }
    206       return false;
    207     }
    208 
    209     case kDataConstant:
    210     case kAccessorConstant: {
    211       // Check if we actually access the same constant.
    212       if (this->constant_.address() == that->constant_.address()) {
    213         this->receiver_maps_.insert(this->receiver_maps_.end(),
    214                                     that->receiver_maps_.begin(),
    215                                     that->receiver_maps_.end());
    216         return true;
    217       }
    218       return false;
    219     }
    220 
    221     case kNotFound: {
    222       this->receiver_maps_.insert(this->receiver_maps_.end(),
    223                                   that->receiver_maps_.begin(),
    224                                   that->receiver_maps_.end());
    225       return true;
    226     }
    227     case kModuleExport: {
    228       return false;
    229     }
    230   }
    231 
    232   UNREACHABLE();
    233 }
    234 
    235 Handle<Cell> PropertyAccessInfo::export_cell() const {
    236   DCHECK_EQ(kModuleExport, kind_);
    237   return Handle<Cell>::cast(constant_);
    238 }
    239 
    240 AccessInfoFactory::AccessInfoFactory(JSHeapBroker* js_heap_broker,
    241                                      CompilationDependencies* dependencies,
    242                                      Handle<Context> native_context, Zone* zone)
    243     : js_heap_broker_(js_heap_broker),
    244       dependencies_(dependencies),
    245       native_context_(native_context),
    246       isolate_(native_context->GetIsolate()),
    247       type_cache_(TypeCache::Get()),
    248       zone_(zone) {
    249   DCHECK(native_context->IsNativeContext());
    250 }
    251 
    252 
    253 bool AccessInfoFactory::ComputeElementAccessInfo(
    254     Handle<Map> map, AccessMode access_mode, ElementAccessInfo* access_info) {
    255   // Check if it is safe to inline element access for the {map}.
    256   if (!CanInlineElementAccess(map)) return false;
    257   ElementsKind const elements_kind = map->elements_kind();
    258   *access_info = ElementAccessInfo(MapHandles{map}, elements_kind);
    259   return true;
    260 }
    261 
    262 bool AccessInfoFactory::ComputeElementAccessInfos(
    263     MapHandles const& maps, AccessMode access_mode,
    264     ZoneVector<ElementAccessInfo>* access_infos) {
    265   if (access_mode == AccessMode::kLoad) {
    266     // For polymorphic loads of similar elements kinds (i.e. all tagged or all
    267     // double), always use the "worst case" code without a transition.  This is
    268     // much faster than transitioning the elements to the worst case, trading a
    269     // TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
    270     ElementAccessInfo access_info;
    271     if (ConsolidateElementLoad(maps, &access_info)) {
    272       access_infos->push_back(access_info);
    273       return true;
    274     }
    275   }
    276 
    277   // Collect possible transition targets.
    278   MapHandles possible_transition_targets;
    279   possible_transition_targets.reserve(maps.size());
    280   for (Handle<Map> map : maps) {
    281     if (Map::TryUpdate(isolate(), map).ToHandle(&map)) {
    282       if (CanInlineElementAccess(map) &&
    283           IsFastElementsKind(map->elements_kind()) &&
    284           GetInitialFastElementsKind() != map->elements_kind()) {
    285         possible_transition_targets.push_back(map);
    286       }
    287     }
    288   }
    289 
    290   // Separate the actual receiver maps and the possible transition sources.
    291   MapHandles receiver_maps;
    292   receiver_maps.reserve(maps.size());
    293   MapTransitionList transitions(maps.size());
    294   for (Handle<Map> map : maps) {
    295     if (Map::TryUpdate(isolate(), map).ToHandle(&map)) {
    296       // Don't generate elements kind transitions from stable maps.
    297       Map* transition_target =
    298           map->is_stable() ? nullptr
    299                            : map->FindElementsKindTransitionedMap(
    300                                  isolate(), possible_transition_targets);
    301       if (transition_target == nullptr) {
    302         receiver_maps.push_back(map);
    303       } else {
    304         transitions.push_back(
    305             std::make_pair(map, handle(transition_target, isolate())));
    306       }
    307     }
    308   }
    309 
    310   for (Handle<Map> receiver_map : receiver_maps) {
    311     // Compute the element access information.
    312     ElementAccessInfo access_info;
    313     if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
    314       return false;
    315     }
    316 
    317     // Collect the possible transitions for the {receiver_map}.
    318     for (auto transition : transitions) {
    319       if (transition.second.is_identical_to(receiver_map)) {
    320         access_info.transitions().push_back(transition);
    321       }
    322     }
    323 
    324     // Schedule the access information.
    325     access_infos->push_back(access_info);
    326   }
    327   return true;
    328 }
    329 
    330 
    331 bool AccessInfoFactory::ComputePropertyAccessInfo(
    332     Handle<Map> map, Handle<Name> name, AccessMode access_mode,
    333     PropertyAccessInfo* access_info) {
    334   // Check if it is safe to inline property access for the {map}.
    335   if (!CanInlinePropertyAccess(map)) return false;
    336 
    337   // Compute the receiver type.
    338   Handle<Map> receiver_map = map;
    339 
    340   // Property lookups require the name to be internalized.
    341   name = isolate()->factory()->InternalizeName(name);
    342 
    343   // We support fast inline cases for certain JSObject getters.
    344   if (access_mode == AccessMode::kLoad &&
    345       LookupSpecialFieldAccessor(map, name, access_info)) {
    346     return true;
    347   }
    348 
    349   MaybeHandle<JSObject> holder;
    350   do {
    351     // Lookup the named property on the {map}.
    352     Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
    353     int const number = descriptors->Search(*name, *map);
    354     if (number != DescriptorArray::kNotFound) {
    355       PropertyDetails const details = descriptors->GetDetails(number);
    356       if (access_mode == AccessMode::kStore ||
    357           access_mode == AccessMode::kStoreInLiteral) {
    358         // Don't bother optimizing stores to read-only properties.
    359         if (details.IsReadOnly()) {
    360           return false;
    361         }
    362         // Check for store to data property on a prototype.
    363         if (details.kind() == kData && !holder.is_null()) {
    364           // Store to property not found on the receiver but on a prototype, we
    365           // need to transition to a new data property.
    366           // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
    367           return LookupTransition(receiver_map, name, holder, access_info);
    368         }
    369       }
    370       if (details.location() == kField) {
    371         if (details.kind() == kData) {
    372           int index = descriptors->GetFieldIndex(number);
    373           Representation details_representation = details.representation();
    374           FieldIndex field_index =
    375               FieldIndex::ForPropertyIndex(*map, index, details_representation);
    376           Type field_type = Type::NonInternal();
    377           MachineRepresentation field_representation =
    378               MachineRepresentation::kTagged;
    379           MaybeHandle<Map> field_map;
    380           if (details_representation.IsSmi()) {
    381             field_type = Type::SignedSmall();
    382             field_representation = MachineRepresentation::kTaggedSigned;
    383           } else if (details_representation.IsDouble()) {
    384             field_type = type_cache_.kFloat64;
    385             field_representation = MachineRepresentation::kFloat64;
    386           } else if (details_representation.IsHeapObject()) {
    387             // Extract the field type from the property details (make sure its
    388             // representation is TaggedPointer to reflect the heap object case).
    389             field_representation = MachineRepresentation::kTaggedPointer;
    390             Handle<FieldType> descriptors_field_type(
    391                 descriptors->GetFieldType(number), isolate());
    392             if (descriptors_field_type->IsNone()) {
    393               // Store is not safe if the field type was cleared.
    394               if (access_mode == AccessMode::kStore) return false;
    395 
    396               // The field type was cleared by the GC, so we don't know anything
    397               // about the contents now.
    398             } else if (descriptors_field_type->IsClass()) {
    399               dependencies()->DependOnFieldType(MapRef(js_heap_broker(), map),
    400                                                 number);
    401               // Remember the field map, and try to infer a useful type.
    402               Handle<Map> map(descriptors_field_type->AsClass(), isolate());
    403               field_type = Type::For(js_heap_broker(), map);
    404               field_map = MaybeHandle<Map>(map);
    405             }
    406           }
    407           *access_info = PropertyAccessInfo::DataField(
    408               details.constness(), MapHandles{receiver_map}, field_index,
    409               field_representation, field_type, field_map, holder);
    410           return true;
    411         } else {
    412           DCHECK_EQ(kAccessor, details.kind());
    413           // TODO(turbofan): Add support for general accessors?
    414           return false;
    415         }
    416 
    417       } else {
    418         DCHECK_EQ(kDescriptor, details.location());
    419         if (details.kind() == kData) {
    420           DCHECK(!FLAG_track_constant_fields);
    421           *access_info = PropertyAccessInfo::DataConstant(
    422               MapHandles{receiver_map},
    423               handle(descriptors->GetStrongValue(number), isolate()), holder);
    424           return true;
    425         } else {
    426           DCHECK_EQ(kAccessor, details.kind());
    427           if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
    428             DCHECK(map->is_prototype_map());
    429             Handle<PrototypeInfo> proto_info =
    430                 Map::GetOrCreatePrototypeInfo(map, isolate());
    431             Handle<JSModuleNamespace> module_namespace(
    432                 JSModuleNamespace::cast(proto_info->module_namespace()),
    433                 isolate());
    434             Handle<Cell> cell(
    435                 Cell::cast(module_namespace->module()->exports()->Lookup(
    436                     ReadOnlyRoots(isolate()), name,
    437                     Smi::ToInt(name->GetHash()))),
    438                 isolate());
    439             if (cell->value()->IsTheHole(isolate())) {
    440               // This module has not been fully initialized yet.
    441               return false;
    442             }
    443             *access_info = PropertyAccessInfo::ModuleExport(
    444                 MapHandles{receiver_map}, cell);
    445             return true;
    446           }
    447           Handle<Object> accessors(descriptors->GetStrongValue(number),
    448                                    isolate());
    449           if (!accessors->IsAccessorPair()) return false;
    450           Handle<Object> accessor(
    451               access_mode == AccessMode::kLoad
    452                   ? Handle<AccessorPair>::cast(accessors)->getter()
    453                   : Handle<AccessorPair>::cast(accessors)->setter(),
    454               isolate());
    455           if (!accessor->IsJSFunction()) {
    456             CallOptimization optimization(isolate(), accessor);
    457             if (!optimization.is_simple_api_call()) return false;
    458             if (optimization.IsCrossContextLazyAccessorPair(*native_context_,
    459                                                             *map)) {
    460               return false;
    461             }
    462 
    463             CallOptimization::HolderLookup lookup;
    464             holder =
    465                 optimization.LookupHolderOfExpectedType(receiver_map, &lookup);
    466             if (lookup == CallOptimization::kHolderNotFound) return false;
    467             DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
    468                            holder.is_null());
    469             DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound,
    470                            !holder.is_null());
    471             if (V8_UNLIKELY(FLAG_runtime_stats)) return false;
    472           }
    473           if (access_mode == AccessMode::kLoad) {
    474             Handle<Name> cached_property_name;
    475             if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(),
    476                                                                accessor)
    477                     .ToHandle(&cached_property_name)) {
    478               if (ComputePropertyAccessInfo(map, cached_property_name,
    479                                             access_mode, access_info)) {
    480                 return true;
    481               }
    482             }
    483           }
    484           *access_info = PropertyAccessInfo::AccessorConstant(
    485               MapHandles{receiver_map}, accessor, holder);
    486           return true;
    487         }
    488       }
    489       UNREACHABLE();
    490     }
    491 
    492     // Don't search on the prototype chain for special indices in case of
    493     // integer indexed exotic objects (see ES6 section 9.4.5).
    494     if (map->IsJSTypedArrayMap() && name->IsString() &&
    495         IsSpecialIndex(isolate()->unicode_cache(), String::cast(*name))) {
    496       return false;
    497     }
    498 
    499     // Don't search on the prototype when storing in literals
    500     if (access_mode == AccessMode::kStoreInLiteral) {
    501       return LookupTransition(receiver_map, name, holder, access_info);
    502     }
    503 
    504     // Don't lookup private symbols on the prototype chain.
    505     if (name->IsPrivate()) return false;
    506 
    507     // Walk up the prototype chain.
    508     if (!map->prototype()->IsJSObject()) {
    509       // Perform the implicit ToObject for primitives here.
    510       // Implemented according to ES6 section 7.3.2 GetV (V, P).
    511       Handle<JSFunction> constructor;
    512       if (Map::GetConstructorFunction(map, native_context())
    513               .ToHandle(&constructor)) {
    514         map = handle(constructor->initial_map(), isolate());
    515         DCHECK(map->prototype()->IsJSObject());
    516       } else if (map->prototype()->IsNull(isolate())) {
    517         // Store to property not found on the receiver or any prototype, we need
    518         // to transition to a new data property.
    519         // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
    520         if (access_mode == AccessMode::kStore) {
    521           return LookupTransition(receiver_map, name, holder, access_info);
    522         }
    523         // The property was not found, return undefined or throw depending
    524         // on the language mode of the load operation.
    525         // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
    526         *access_info =
    527             PropertyAccessInfo::NotFound(MapHandles{receiver_map}, holder);
    528         return true;
    529       } else {
    530         return false;
    531       }
    532     }
    533     Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
    534     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       JSObject::TryMigrateInstance(map_prototype);
    538     }
    539     map = handle(map_prototype->map(), isolate());
    540     holder = map_prototype;
    541   } while (CanInlinePropertyAccess(map));
    542   return false;
    543 }
    544 
    545 bool AccessInfoFactory::ComputePropertyAccessInfo(
    546     MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
    547     PropertyAccessInfo* access_info) {
    548   ZoneVector<PropertyAccessInfo> access_infos(zone());
    549   if (ComputePropertyAccessInfos(maps, name, access_mode, &access_infos) &&
    550       access_infos.size() == 1) {
    551     *access_info = access_infos.front();
    552     return true;
    553   }
    554   return false;
    555 }
    556 
    557 bool AccessInfoFactory::ComputePropertyAccessInfos(
    558     MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
    559     ZoneVector<PropertyAccessInfo>* access_infos) {
    560   for (Handle<Map> map : maps) {
    561     if (Map::TryUpdate(isolate(), map).ToHandle(&map)) {
    562       PropertyAccessInfo access_info;
    563       if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) {
    564         return false;
    565       }
    566       // Try to merge the {access_info} with an existing one.
    567       bool merged = false;
    568       for (PropertyAccessInfo& other_info : *access_infos) {
    569         if (other_info.Merge(&access_info, access_mode, zone())) {
    570           merged = true;
    571           break;
    572         }
    573       }
    574       if (!merged) access_infos->push_back(access_info);
    575     }
    576   }
    577   return true;
    578 }
    579 
    580 namespace {
    581 
    582 Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
    583                                            ElementsKind that_kind) {
    584   if (IsHoleyElementsKind(this_kind)) {
    585     that_kind = GetHoleyElementsKind(that_kind);
    586   } else if (IsHoleyElementsKind(that_kind)) {
    587     this_kind = GetHoleyElementsKind(this_kind);
    588   }
    589   if (this_kind == that_kind) return Just(this_kind);
    590   if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
    591     if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
    592       return Just(this_kind);
    593     }
    594     if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
    595       return Just(that_kind);
    596     }
    597   }
    598   return Nothing<ElementsKind>();
    599 }
    600 
    601 }  // namespace
    602 
    603 bool AccessInfoFactory::ConsolidateElementLoad(MapHandles const& maps,
    604                                                ElementAccessInfo* access_info) {
    605   if (maps.empty()) return false;
    606   InstanceType instance_type = maps.front()->instance_type();
    607   ElementsKind elements_kind = maps.front()->elements_kind();
    608   for (Handle<Map> map : maps) {
    609     if (!CanInlineElementAccess(map) || map->instance_type() != instance_type) {
    610       return false;
    611     }
    612     if (!GeneralizeElementsKind(elements_kind, map->elements_kind())
    613              .To(&elements_kind)) {
    614       return false;
    615     }
    616   }
    617   *access_info = ElementAccessInfo(maps, elements_kind);
    618   return true;
    619 }
    620 
    621 bool AccessInfoFactory::LookupSpecialFieldAccessor(
    622     Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) {
    623   // Check for special JSObject field accessors.
    624   FieldIndex field_index;
    625   if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
    626     Type field_type = Type::NonInternal();
    627     MachineRepresentation field_representation = MachineRepresentation::kTagged;
    628     if (map->IsStringMap()) {
    629       DCHECK(Name::Equals(isolate(), factory()->length_string(), name));
    630       // The String::length property is always a smi in the range
    631       // [0, String::kMaxLength].
    632       field_type = type_cache_.kStringLengthType;
    633       field_representation = MachineRepresentation::kTaggedSigned;
    634     } else if (map->IsJSArrayMap()) {
    635       DCHECK(Name::Equals(isolate(), factory()->length_string(), name));
    636       // The JSArray::length property is a smi in the range
    637       // [0, FixedDoubleArray::kMaxLength] in case of fast double
    638       // elements, a smi in the range [0, FixedArray::kMaxLength]
    639       // in case of other fast elements, and [0, kMaxUInt32] in
    640       // case of other arrays.
    641       if (IsDoubleElementsKind(map->elements_kind())) {
    642         field_type = type_cache_.kFixedDoubleArrayLengthType;
    643         field_representation = MachineRepresentation::kTaggedSigned;
    644       } else if (IsFastElementsKind(map->elements_kind())) {
    645         field_type = type_cache_.kFixedArrayLengthType;
    646         field_representation = MachineRepresentation::kTaggedSigned;
    647       } else {
    648         field_type = type_cache_.kJSArrayLengthType;
    649       }
    650     }
    651     // Special fields are always mutable.
    652     *access_info = PropertyAccessInfo::DataField(
    653         PropertyConstness::kMutable, MapHandles{map}, field_index,
    654         field_representation, field_type);
    655     return true;
    656   }
    657   return false;
    658 }
    659 
    660 
    661 bool AccessInfoFactory::LookupTransition(Handle<Map> map, Handle<Name> name,
    662                                          MaybeHandle<JSObject> holder,
    663                                          PropertyAccessInfo* access_info) {
    664   // Check if the {map} has a data transition with the given {name}.
    665   Map* transition =
    666       TransitionsAccessor(isolate(), map).SearchTransition(*name, kData, NONE);
    667   if (transition == nullptr) return false;
    668 
    669   Handle<Map> transition_map(transition, isolate());
    670   int const number = transition_map->LastAdded();
    671   PropertyDetails const details =
    672       transition_map->instance_descriptors()->GetDetails(number);
    673   // Don't bother optimizing stores to read-only properties.
    674   if (details.IsReadOnly()) return false;
    675   // TODO(bmeurer): Handle transition to data constant?
    676   if (details.location() != kField) return false;
    677   int const index = details.field_index();
    678   Representation details_representation = details.representation();
    679   FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index,
    680                                                         details_representation);
    681   Type field_type = Type::NonInternal();
    682   MaybeHandle<Map> field_map;
    683   MachineRepresentation field_representation = MachineRepresentation::kTagged;
    684   if (details_representation.IsSmi()) {
    685     field_type = Type::SignedSmall();
    686     field_representation = MachineRepresentation::kTaggedSigned;
    687   } else if (details_representation.IsDouble()) {
    688     field_type = type_cache_.kFloat64;
    689     field_representation = MachineRepresentation::kFloat64;
    690   } else if (details_representation.IsHeapObject()) {
    691     // Extract the field type from the property details (make sure its
    692     // representation is TaggedPointer to reflect the heap object case).
    693     field_representation = MachineRepresentation::kTaggedPointer;
    694     Handle<FieldType> descriptors_field_type(
    695         transition_map->instance_descriptors()->GetFieldType(number),
    696         isolate());
    697     if (descriptors_field_type->IsNone()) {
    698       // Store is not safe if the field type was cleared.
    699       return false;
    700     } else if (descriptors_field_type->IsClass()) {
    701       dependencies()->DependOnFieldType(
    702           MapRef(js_heap_broker(), transition_map), number);
    703       // Remember the field map, and try to infer a useful type.
    704       Handle<Map> map(descriptors_field_type->AsClass(), isolate());
    705       field_type = Type::For(js_heap_broker(), map);
    706       field_map = MaybeHandle<Map>(map);
    707     }
    708   }
    709   dependencies()->DependOnTransition(MapRef(js_heap_broker(), transition_map));
    710   // Transitioning stores are never stores to constant fields.
    711   *access_info = PropertyAccessInfo::DataField(
    712       PropertyConstness::kMutable, MapHandles{map}, field_index,
    713       field_representation, field_type, field_map, holder, transition_map);
    714   return true;
    715 }
    716 
    717 
    718 Factory* AccessInfoFactory::factory() const { return isolate()->factory(); }
    719 
    720 }  // namespace compiler
    721 }  // namespace internal
    722 }  // namespace v8
    723