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 "src/compiler/access-info.h"
6 
7 #include <ostream>
8 
9 #include "src/builtins/accessors.h"
10 #include "src/compiler/compilation-dependencies.h"
11 #include "src/compiler/simplified-operator.h"
12 #include "src/compiler/type-cache.h"
13 #include "src/ic/call-optimization.h"
14 #include "src/ic/handler-configuration.h"
15 #include "src/logging/counters.h"
16 #include "src/objects/cell-inl.h"
17 #include "src/objects/field-index-inl.h"
18 #include "src/objects/field-type.h"
19 #include "src/objects/module-inl.h"
20 #include "src/objects/objects-inl.h"
21 #include "src/objects/struct-inl.h"
22 #include "src/objects/templates.h"
23 
24 namespace v8 {
25 namespace internal {
26 namespace compiler {
27 
28 namespace {
29 
CanInlinePropertyAccess(MapRef map,AccessMode access_mode)30 bool CanInlinePropertyAccess(MapRef map, AccessMode access_mode) {
31   // We can inline property access to prototypes of all primitives, except
32   // the special Oddball ones that have no wrapper counterparts (i.e. Null,
33   // Undefined and TheHole).
34   // We can only inline accesses to dictionary mode holders if the access is a
35   // load and the holder is a prototype. The latter ensures a 1:1
36   // relationship between the map and the object (and therefore the property
37   // dictionary).
38   STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_HEAP_OBJECT_TYPE);
39   if (map.object()->IsBooleanMap()) return true;
40   if (map.instance_type() < LAST_PRIMITIVE_HEAP_OBJECT_TYPE) return true;
41   if (map.object()->IsJSObjectMap()) {
42     if (map.is_dictionary_map()) {
43       if (!V8_DICT_PROPERTY_CONST_TRACKING_BOOL) return false;
44       return access_mode == AccessMode::kLoad &&
45              map.object()->is_prototype_map();
46     }
47     return !map.object()->has_named_interceptor() &&
48            // TODO(verwaest): Allowlist contexts to which we have access.
49            !map.is_access_check_needed();
50   }
51   return false;
52 }
53 
54 #ifdef DEBUG
HasFieldRepresentationDependenciesOnMap(ZoneVector<CompilationDependency const * > & dependencies,Handle<Map> const & field_owner_map)55 bool HasFieldRepresentationDependenciesOnMap(
56     ZoneVector<CompilationDependency const*>& dependencies,
57     Handle<Map> const& field_owner_map) {
58   for (auto dep : dependencies) {
59     if (CompilationDependencies::IsFieldRepresentationDependencyOnMap(
60             dep, field_owner_map)) {
61       return true;
62     }
63   }
64   return false;
65 }
66 #endif
67 
68 }  // namespace
69 
70 
operator <<(std::ostream & os,AccessMode access_mode)71 std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
72   switch (access_mode) {
73     case AccessMode::kLoad:
74       return os << "Load";
75     case AccessMode::kStore:
76       return os << "Store";
77     case AccessMode::kStoreInLiteral:
78       return os << "StoreInLiteral";
79     case AccessMode::kHas:
80       return os << "Has";
81   }
82   UNREACHABLE();
83 }
84 
ElementAccessInfo(ZoneVector<MapRef> && lookup_start_object_maps,ElementsKind elements_kind,Zone * zone)85 ElementAccessInfo::ElementAccessInfo(
86     ZoneVector<MapRef>&& lookup_start_object_maps, ElementsKind elements_kind,
87     Zone* zone)
88     : elements_kind_(elements_kind),
89       lookup_start_object_maps_(lookup_start_object_maps),
90       transition_sources_(zone) {
91   CHECK(!lookup_start_object_maps.empty());
92 }
93 
94 // static
Invalid(Zone * zone)95 PropertyAccessInfo PropertyAccessInfo::Invalid(Zone* zone) {
96   return PropertyAccessInfo(zone);
97 }
98 
99 // static
NotFound(Zone * zone,MapRef receiver_map,base::Optional<JSObjectRef> holder)100 PropertyAccessInfo PropertyAccessInfo::NotFound(
101     Zone* zone, MapRef receiver_map, base::Optional<JSObjectRef> holder) {
102   return PropertyAccessInfo(zone, kNotFound, holder, {{receiver_map}, zone});
103 }
104 
105 // static
DataField(Zone * zone,MapRef receiver_map,ZoneVector<CompilationDependency const * > && dependencies,FieldIndex field_index,Representation field_representation,Type field_type,MapRef field_owner_map,base::Optional<MapRef> field_map,base::Optional<JSObjectRef> holder,base::Optional<MapRef> transition_map)106 PropertyAccessInfo PropertyAccessInfo::DataField(
107     Zone* zone, MapRef receiver_map,
108     ZoneVector<CompilationDependency const*>&& dependencies,
109     FieldIndex field_index, Representation field_representation,
110     Type field_type, MapRef field_owner_map, base::Optional<MapRef> field_map,
111     base::Optional<JSObjectRef> holder, base::Optional<MapRef> transition_map) {
112   DCHECK(!field_representation.IsNone());
113   DCHECK_IMPLIES(
114       field_representation.IsDouble(),
115       HasFieldRepresentationDependenciesOnMap(
116           dependencies, transition_map.has_value()
117                             ? transition_map->object()
118                             : holder.has_value() ? holder->map().object()
119                                                  : receiver_map.object()));
120   return PropertyAccessInfo(kDataField, holder, transition_map, field_index,
121                             field_representation, field_type, field_owner_map,
122                             field_map, {{receiver_map}, zone},
123                             std::move(dependencies));
124 }
125 
126 // static
FastDataConstant(Zone * zone,MapRef receiver_map,ZoneVector<CompilationDependency const * > && dependencies,FieldIndex field_index,Representation field_representation,Type field_type,MapRef field_owner_map,base::Optional<MapRef> field_map,base::Optional<JSObjectRef> holder,base::Optional<MapRef> transition_map)127 PropertyAccessInfo PropertyAccessInfo::FastDataConstant(
128     Zone* zone, MapRef receiver_map,
129     ZoneVector<CompilationDependency const*>&& dependencies,
130     FieldIndex field_index, Representation field_representation,
131     Type field_type, MapRef field_owner_map, base::Optional<MapRef> field_map,
132     base::Optional<JSObjectRef> holder, base::Optional<MapRef> transition_map) {
133   DCHECK(!field_representation.IsNone());
134   return PropertyAccessInfo(kFastDataConstant, holder, transition_map,
135                             field_index, field_representation, field_type,
136                             field_owner_map, field_map, {{receiver_map}, zone},
137                             std::move(dependencies));
138 }
139 
140 // static
FastAccessorConstant(Zone * zone,MapRef receiver_map,base::Optional<ObjectRef> constant,base::Optional<JSObjectRef> holder)141 PropertyAccessInfo PropertyAccessInfo::FastAccessorConstant(
142     Zone* zone, MapRef receiver_map, base::Optional<ObjectRef> constant,
143     base::Optional<JSObjectRef> holder) {
144   return PropertyAccessInfo(zone, kFastAccessorConstant, holder, constant, {},
145                             {{receiver_map}, zone});
146 }
147 
148 // static
ModuleExport(Zone * zone,MapRef receiver_map,CellRef cell)149 PropertyAccessInfo PropertyAccessInfo::ModuleExport(Zone* zone,
150                                                     MapRef receiver_map,
151                                                     CellRef cell) {
152   return PropertyAccessInfo(zone, kModuleExport, {}, cell, {},
153                             {{receiver_map}, zone});
154 }
155 
156 // static
StringLength(Zone * zone,MapRef receiver_map)157 PropertyAccessInfo PropertyAccessInfo::StringLength(Zone* zone,
158                                                     MapRef receiver_map) {
159   return PropertyAccessInfo(zone, kStringLength, {}, {{receiver_map}, zone});
160 }
161 
162 // static
DictionaryProtoDataConstant(Zone * zone,MapRef receiver_map,JSObjectRef holder,InternalIndex dictionary_index,NameRef name)163 PropertyAccessInfo PropertyAccessInfo::DictionaryProtoDataConstant(
164     Zone* zone, MapRef receiver_map, JSObjectRef holder,
165     InternalIndex dictionary_index, NameRef name) {
166   return PropertyAccessInfo(zone, kDictionaryProtoDataConstant, holder,
167                             {{receiver_map}, zone}, dictionary_index, name);
168 }
169 
170 // static
DictionaryProtoAccessorConstant(Zone * zone,MapRef receiver_map,base::Optional<JSObjectRef> holder,ObjectRef constant,NameRef property_name)171 PropertyAccessInfo PropertyAccessInfo::DictionaryProtoAccessorConstant(
172     Zone* zone, MapRef receiver_map, base::Optional<JSObjectRef> holder,
173     ObjectRef constant, NameRef property_name) {
174   return PropertyAccessInfo(zone, kDictionaryProtoAccessorConstant, holder,
175                             constant, property_name, {{receiver_map}, zone});
176 }
177 
178 // static
DataField(int offset,bool is_inobject,Representation field_representation,Type field_type)179 MinimorphicLoadPropertyAccessInfo MinimorphicLoadPropertyAccessInfo::DataField(
180     int offset, bool is_inobject, Representation field_representation,
181     Type field_type) {
182   return MinimorphicLoadPropertyAccessInfo(kDataField, offset, is_inobject,
183                                            field_representation, field_type);
184 }
185 
186 // static
Invalid()187 MinimorphicLoadPropertyAccessInfo MinimorphicLoadPropertyAccessInfo::Invalid() {
188   return MinimorphicLoadPropertyAccessInfo(
189       kInvalid, -1, false, Representation::None(), Type::None());
190 }
191 
PropertyAccessInfo(Zone * zone)192 PropertyAccessInfo::PropertyAccessInfo(Zone* zone)
193     : kind_(kInvalid),
194       lookup_start_object_maps_(zone),
195       unrecorded_dependencies_(zone),
196       field_representation_(Representation::None()),
197       field_type_(Type::None()),
198       dictionary_index_(InternalIndex::NotFound()) {}
199 
PropertyAccessInfo(Zone * zone,Kind kind,base::Optional<JSObjectRef> holder,ZoneVector<MapRef> && lookup_start_object_maps)200 PropertyAccessInfo::PropertyAccessInfo(
201     Zone* zone, Kind kind, base::Optional<JSObjectRef> holder,
202     ZoneVector<MapRef>&& lookup_start_object_maps)
203     : kind_(kind),
204       lookup_start_object_maps_(lookup_start_object_maps),
205       holder_(holder),
206       unrecorded_dependencies_(zone),
207       field_representation_(Representation::None()),
208       field_type_(Type::None()),
209       dictionary_index_(InternalIndex::NotFound()) {}
210 
PropertyAccessInfo(Zone * zone,Kind kind,base::Optional<JSObjectRef> holder,base::Optional<ObjectRef> constant,base::Optional<NameRef> name,ZoneVector<MapRef> && lookup_start_object_maps)211 PropertyAccessInfo::PropertyAccessInfo(
212     Zone* zone, Kind kind, base::Optional<JSObjectRef> holder,
213     base::Optional<ObjectRef> constant, base::Optional<NameRef> name,
214     ZoneVector<MapRef>&& lookup_start_object_maps)
215     : kind_(kind),
216       lookup_start_object_maps_(lookup_start_object_maps),
217       constant_(constant),
218       holder_(holder),
219       unrecorded_dependencies_(zone),
220       field_representation_(Representation::None()),
221       field_type_(Type::Any()),
222       dictionary_index_(InternalIndex::NotFound()),
223       name_(name) {
224   DCHECK_IMPLIES(kind == kDictionaryProtoAccessorConstant, name.has_value());
225 }
226 
PropertyAccessInfo(Kind kind,base::Optional<JSObjectRef> holder,base::Optional<MapRef> transition_map,FieldIndex field_index,Representation field_representation,Type field_type,MapRef field_owner_map,base::Optional<MapRef> field_map,ZoneVector<MapRef> && lookup_start_object_maps,ZoneVector<CompilationDependency const * > && unrecorded_dependencies)227 PropertyAccessInfo::PropertyAccessInfo(
228     Kind kind, base::Optional<JSObjectRef> holder,
229     base::Optional<MapRef> transition_map, FieldIndex field_index,
230     Representation field_representation, Type field_type,
231     MapRef field_owner_map, base::Optional<MapRef> field_map,
232     ZoneVector<MapRef>&& lookup_start_object_maps,
233     ZoneVector<CompilationDependency const*>&& unrecorded_dependencies)
234     : kind_(kind),
235       lookup_start_object_maps_(lookup_start_object_maps),
236       holder_(holder),
237       unrecorded_dependencies_(std::move(unrecorded_dependencies)),
238       transition_map_(transition_map),
239       field_index_(field_index),
240       field_representation_(field_representation),
241       field_type_(field_type),
242       field_owner_map_(field_owner_map),
243       field_map_(field_map),
244       dictionary_index_(InternalIndex::NotFound()) {
245   DCHECK_IMPLIES(transition_map.has_value(),
246                  field_owner_map.equals(transition_map.value()));
247 }
248 
PropertyAccessInfo(Zone * zone,Kind kind,base::Optional<JSObjectRef> holder,ZoneVector<MapRef> && lookup_start_object_maps,InternalIndex dictionary_index,NameRef name)249 PropertyAccessInfo::PropertyAccessInfo(
250     Zone* zone, Kind kind, base::Optional<JSObjectRef> holder,
251     ZoneVector<MapRef>&& lookup_start_object_maps,
252     InternalIndex dictionary_index, NameRef name)
253     : kind_(kind),
254       lookup_start_object_maps_(lookup_start_object_maps),
255       holder_(holder),
256       unrecorded_dependencies_(zone),
257       field_representation_(Representation::None()),
258       field_type_(Type::Any()),
259       dictionary_index_(dictionary_index),
260       name_{name} {}
261 
MinimorphicLoadPropertyAccessInfo(Kind kind,int offset,bool is_inobject,Representation field_representation,Type field_type)262 MinimorphicLoadPropertyAccessInfo::MinimorphicLoadPropertyAccessInfo(
263     Kind kind, int offset, bool is_inobject,
264     Representation field_representation, Type field_type)
265     : kind_(kind),
266       is_inobject_(is_inobject),
267       offset_(offset),
268       field_representation_(field_representation),
269       field_type_(field_type) {}
270 
271 namespace {
272 
273 template <class RefT>
OptionalRefEquals(base::Optional<RefT> lhs,base::Optional<RefT> rhs)274 bool OptionalRefEquals(base::Optional<RefT> lhs, base::Optional<RefT> rhs) {
275   if (!lhs.has_value()) return !rhs.has_value();
276   if (!rhs.has_value()) return false;
277   return lhs->equals(rhs.value());
278 }
279 
280 template <class T>
AppendVector(ZoneVector<T> * dst,const ZoneVector<T> & src)281 void AppendVector(ZoneVector<T>* dst, const ZoneVector<T>& src) {
282   dst->insert(dst->end(), src.begin(), src.end());
283 }
284 
285 }  // namespace
286 
Merge(PropertyAccessInfo const * that,AccessMode access_mode,Zone * zone)287 bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
288                                AccessMode access_mode, Zone* zone) {
289   if (kind_ != that->kind_) return false;
290   if (!OptionalRefEquals(holder_, that->holder_)) return false;
291 
292   switch (kind_) {
293     case kInvalid:
294       DCHECK_EQ(that->kind_, kInvalid);
295       return true;
296 
297     case kDataField:
298     case kFastDataConstant: {
299       // Check if we actually access the same field (we use the
300       // GetFieldAccessStubKey method here just like the ICs do
301       // since that way we only compare the relevant bits of the
302       // field indices).
303       if (field_index_.GetFieldAccessStubKey() !=
304           that->field_index_.GetFieldAccessStubKey()) {
305         return false;
306       }
307 
308       switch (access_mode) {
309         case AccessMode::kHas:
310         case AccessMode::kLoad: {
311           if (!field_representation_.Equals(that->field_representation_)) {
312             if (field_representation_.IsDouble() ||
313                 that->field_representation_.IsDouble()) {
314               return false;
315             }
316             field_representation_ = Representation::Tagged();
317           }
318           if (!OptionalRefEquals(field_map_, that->field_map_)) {
319             field_map_ = {};
320           }
321           break;
322         }
323         case AccessMode::kStore:
324         case AccessMode::kStoreInLiteral: {
325           // For stores, the field map and field representation information
326           // must match exactly, otherwise we cannot merge the stores. We
327           // also need to make sure that in case of transitioning stores,
328           // the transition targets match.
329           if (!OptionalRefEquals(field_map_, that->field_map_) ||
330               !field_representation_.Equals(that->field_representation_) ||
331               !OptionalRefEquals(transition_map_, that->transition_map_)) {
332             return false;
333           }
334           break;
335         }
336       }
337 
338       field_type_ = Type::Union(field_type_, that->field_type_, zone);
339       AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_);
340       AppendVector(&unrecorded_dependencies_, that->unrecorded_dependencies_);
341       return true;
342     }
343 
344     case kDictionaryProtoAccessorConstant:
345     case kFastAccessorConstant: {
346       // Check if we actually access the same constant.
347       if (!OptionalRefEquals(constant_, that->constant_)) return false;
348 
349       DCHECK(unrecorded_dependencies_.empty());
350       DCHECK(that->unrecorded_dependencies_.empty());
351       AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_);
352       return true;
353     }
354 
355     case kDictionaryProtoDataConstant: {
356       DCHECK_EQ(AccessMode::kLoad, access_mode);
357       if (dictionary_index_ != that->dictionary_index_) return false;
358       AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_);
359       return true;
360     }
361 
362     case kNotFound:
363     case kStringLength: {
364       DCHECK(unrecorded_dependencies_.empty());
365       DCHECK(that->unrecorded_dependencies_.empty());
366       AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_);
367       return true;
368     }
369     case kModuleExport:
370       return false;
371   }
372 }
373 
GetConstFieldInfo() const374 ConstFieldInfo PropertyAccessInfo::GetConstFieldInfo() const {
375   return IsFastDataConstant() ? ConstFieldInfo(field_owner_map_->object())
376                               : ConstFieldInfo::None();
377 }
378 
AccessInfoFactory(JSHeapBroker * broker,CompilationDependencies * dependencies,Zone * zone)379 AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
380                                      CompilationDependencies* dependencies,
381                                      Zone* zone)
382     : broker_(broker),
383       dependencies_(dependencies),
384       type_cache_(TypeCache::Get()),
385       zone_(zone) {}
386 
ComputeElementAccessInfo(MapRef map,AccessMode access_mode) const387 base::Optional<ElementAccessInfo> AccessInfoFactory::ComputeElementAccessInfo(
388     MapRef map, AccessMode access_mode) const {
389   if (!map.CanInlineElementAccess()) return {};
390   return ElementAccessInfo({{map}, zone()}, map.elements_kind(), zone());
391 }
392 
ComputeElementAccessInfos(ElementAccessFeedback const & feedback,ZoneVector<ElementAccessInfo> * access_infos) const393 bool AccessInfoFactory::ComputeElementAccessInfos(
394     ElementAccessFeedback const& feedback,
395     ZoneVector<ElementAccessInfo>* access_infos) const {
396   AccessMode access_mode = feedback.keyed_mode().access_mode();
397   if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
398     // For polymorphic loads of similar elements kinds (i.e. all tagged or all
399     // double), always use the "worst case" code without a transition.  This is
400     // much faster than transitioning the elements to the worst case, trading a
401     // TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
402     base::Optional<ElementAccessInfo> access_info =
403         ConsolidateElementLoad(feedback);
404     if (access_info.has_value()) {
405       access_infos->push_back(*access_info);
406       return true;
407     }
408   }
409 
410   for (auto const& group : feedback.transition_groups()) {
411     DCHECK(!group.empty());
412     base::Optional<MapRef> target =
413         MakeRefAssumeMemoryFence(broker(), group.front());
414     base::Optional<ElementAccessInfo> access_info =
415         ComputeElementAccessInfo(target.value(), access_mode);
416     if (!access_info.has_value()) return false;
417 
418     for (size_t i = 1; i < group.size(); ++i) {
419       base::Optional<MapRef> map_ref =
420           MakeRefAssumeMemoryFence(broker(), group[i]);
421       if (!map_ref.has_value()) continue;
422       access_info->AddTransitionSource(map_ref.value());
423     }
424     access_infos->push_back(*access_info);
425   }
426   return true;
427 }
428 
ComputeDataFieldAccessInfo(MapRef receiver_map,MapRef map,base::Optional<JSObjectRef> holder,InternalIndex descriptor,AccessMode access_mode) const429 PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
430     MapRef receiver_map, MapRef map, base::Optional<JSObjectRef> holder,
431     InternalIndex descriptor, AccessMode access_mode) const {
432   DCHECK(descriptor.is_found());
433   // TODO(jgruber,v8:7790): Use DescriptorArrayRef instead.
434   Handle<DescriptorArray> descriptors = map.instance_descriptors().object();
435   PropertyDetails const details = descriptors->GetDetails(descriptor);
436   int index = descriptors->GetFieldIndex(descriptor);
437   Representation details_representation = details.representation();
438   if (details_representation.IsNone()) {
439     // The ICs collect feedback in PREMONOMORPHIC state already,
440     // but at this point the {receiver_map} might still contain
441     // fields for which the representation has not yet been
442     // determined by the runtime. So we need to catch this case
443     // here and fall back to use the regular IC logic instead.
444     return Invalid();
445   }
446   FieldIndex field_index = FieldIndex::ForPropertyIndex(*map.object(), index,
447                                                         details_representation);
448   Type field_type = Type::NonInternal();
449   base::Optional<MapRef> field_map;
450 
451   ZoneVector<CompilationDependency const*> unrecorded_dependencies(zone());
452 
453   Handle<FieldType> descriptors_field_type =
454       broker()->CanonicalPersistentHandle(
455           descriptors->GetFieldType(descriptor));
456   base::Optional<ObjectRef> descriptors_field_type_ref =
457       TryMakeRef<Object>(broker(), descriptors_field_type);
458   if (!descriptors_field_type_ref.has_value()) return Invalid();
459 
460   if (details_representation.IsSmi()) {
461     field_type = Type::SignedSmall();
462     unrecorded_dependencies.push_back(
463         dependencies()->FieldRepresentationDependencyOffTheRecord(
464             map, descriptor, details_representation));
465   } else if (details_representation.IsDouble()) {
466     field_type = type_cache_->kFloat64;
467     unrecorded_dependencies.push_back(
468         dependencies()->FieldRepresentationDependencyOffTheRecord(
469             map, descriptor, details_representation));
470   } else if (details_representation.IsHeapObject()) {
471     if (descriptors_field_type->IsNone()) {
472       // Store is not safe if the field type was cleared.
473       if (access_mode == AccessMode::kStore) {
474         return Invalid();
475       }
476 
477       // The field type was cleared by the GC, so we don't know anything
478       // about the contents now.
479     }
480     unrecorded_dependencies.push_back(
481         dependencies()->FieldRepresentationDependencyOffTheRecord(
482             map, descriptor, details_representation));
483     if (descriptors_field_type->IsClass()) {
484       // Remember the field map, and try to infer a useful type.
485       base::Optional<MapRef> maybe_field_map =
486           TryMakeRef(broker(), descriptors_field_type->AsClass());
487       if (!maybe_field_map.has_value()) return Invalid();
488       field_type = Type::For(maybe_field_map.value());
489       field_map = maybe_field_map;
490     }
491   } else {
492     CHECK(details_representation.IsTagged());
493   }
494   // TODO(turbofan): We may want to do this only depending on the use
495   // of the access info.
496   unrecorded_dependencies.push_back(
497       dependencies()->FieldTypeDependencyOffTheRecord(
498           map, descriptor, descriptors_field_type_ref.value()));
499 
500   PropertyConstness constness;
501   if (details.IsReadOnly() && !details.IsConfigurable()) {
502     constness = PropertyConstness::kConst;
503   } else {
504     constness = dependencies()->DependOnFieldConstness(map, descriptor);
505   }
506 
507   // Note: FindFieldOwner may be called multiple times throughout one
508   // compilation. This is safe since its result is fixed for a given map and
509   // descriptor.
510   MapRef field_owner_map = map.FindFieldOwner(descriptor);
511 
512   switch (constness) {
513     case PropertyConstness::kMutable:
514       return PropertyAccessInfo::DataField(
515           zone(), receiver_map, std::move(unrecorded_dependencies), field_index,
516           details_representation, field_type, field_owner_map, field_map,
517           holder, {});
518 
519     case PropertyConstness::kConst:
520       return PropertyAccessInfo::FastDataConstant(
521           zone(), receiver_map, std::move(unrecorded_dependencies), field_index,
522           details_representation, field_type, field_owner_map, field_map,
523           holder, {});
524   }
525   UNREACHABLE();
526 }
527 
528 namespace {
529 
530 using AccessorsObjectGetter = std::function<Handle<Object>()>;
531 
AccessorAccessInfoHelper(Isolate * isolate,Zone * zone,JSHeapBroker * broker,const AccessInfoFactory * ai_factory,MapRef receiver_map,NameRef name,MapRef map,base::Optional<JSObjectRef> holder,AccessMode access_mode,AccessorsObjectGetter get_accessors)532 PropertyAccessInfo AccessorAccessInfoHelper(
533     Isolate* isolate, Zone* zone, JSHeapBroker* broker,
534     const AccessInfoFactory* ai_factory, MapRef receiver_map, NameRef name,
535     MapRef map, base::Optional<JSObjectRef> holder, AccessMode access_mode,
536     AccessorsObjectGetter get_accessors) {
537   if (map.instance_type() == JS_MODULE_NAMESPACE_TYPE) {
538     DCHECK(map.object()->is_prototype_map());
539     Handle<PrototypeInfo> proto_info = broker->CanonicalPersistentHandle(
540         PrototypeInfo::cast(map.object()->prototype_info()));
541     Handle<JSModuleNamespace> module_namespace =
542         broker->CanonicalPersistentHandle(
543             JSModuleNamespace::cast(proto_info->module_namespace()));
544     Handle<Cell> cell = broker->CanonicalPersistentHandle(
545         Cell::cast(module_namespace->module().exports().Lookup(
546             isolate, name.object(), Smi::ToInt(name.object()->GetHash()))));
547     if (cell->value(kRelaxedLoad).IsTheHole(isolate)) {
548       // This module has not been fully initialized yet.
549       return PropertyAccessInfo::Invalid(zone);
550     }
551     base::Optional<CellRef> cell_ref = TryMakeRef(broker, cell);
552     if (!cell_ref.has_value()) {
553       return PropertyAccessInfo::Invalid(zone);
554     }
555     return PropertyAccessInfo::ModuleExport(zone, receiver_map,
556                                             cell_ref.value());
557   }
558   if (access_mode == AccessMode::kHas) {
559     // kHas is not supported for dictionary mode objects.
560     DCHECK(!map.is_dictionary_map());
561 
562     // HasProperty checks don't call getter/setters, existence is sufficient.
563     return PropertyAccessInfo::FastAccessorConstant(zone, receiver_map, {},
564                                                     holder);
565   }
566   Handle<Object> maybe_accessors = get_accessors();
567   if (!maybe_accessors->IsAccessorPair()) {
568     return PropertyAccessInfo::Invalid(zone);
569   }
570   Handle<AccessorPair> accessors = Handle<AccessorPair>::cast(maybe_accessors);
571   Handle<Object> accessor = broker->CanonicalPersistentHandle(
572       access_mode == AccessMode::kLoad ? accessors->getter(kAcquireLoad)
573                                        : accessors->setter(kAcquireLoad));
574 
575   base::Optional<ObjectRef> accessor_ref = TryMakeRef(broker, accessor);
576   if (!accessor_ref.has_value()) return PropertyAccessInfo::Invalid(zone);
577 
578   if (!accessor->IsJSFunction()) {
579     CallOptimization optimization(broker->local_isolate_or_isolate(), accessor);
580     if (!optimization.is_simple_api_call() ||
581         optimization.IsCrossContextLazyAccessorPair(
582             *broker->target_native_context().object(), *map.object())) {
583       return PropertyAccessInfo::Invalid(zone);
584     }
585 
586     CallOptimization::HolderLookup lookup;
587     Handle<JSObject> holder_handle = broker->CanonicalPersistentHandle(
588         optimization.LookupHolderOfExpectedType(
589             broker->local_isolate_or_isolate(), receiver_map.object(),
590             &lookup));
591     if (lookup == CallOptimization::kHolderNotFound) {
592       return PropertyAccessInfo::Invalid(zone);
593     }
594     DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
595                    holder_handle.is_null());
596     DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound,
597                    !holder_handle.is_null());
598 
599     if (holder_handle.is_null()) {
600       holder = {};
601     } else {
602       holder = TryMakeRef(broker, holder_handle);
603       if (!holder.has_value()) return PropertyAccessInfo::Invalid(zone);
604     }
605   }
606   if (access_mode == AccessMode::kLoad) {
607     base::Optional<Name> cached_property_name =
608         FunctionTemplateInfo::TryGetCachedPropertyName(isolate, *accessor);
609     if (cached_property_name.has_value()) {
610       base::Optional<NameRef> cached_property_name_ref =
611           TryMakeRef(broker, cached_property_name.value());
612       if (cached_property_name_ref.has_value()) {
613         PropertyAccessInfo access_info = ai_factory->ComputePropertyAccessInfo(
614             map, cached_property_name_ref.value(), access_mode);
615         if (!access_info.IsInvalid()) return access_info;
616       }
617     }
618   }
619 
620   if (map.is_dictionary_map()) {
621     return PropertyAccessInfo::DictionaryProtoAccessorConstant(
622         zone, receiver_map, holder, accessor_ref.value(), name);
623   } else {
624     return PropertyAccessInfo::FastAccessorConstant(
625         zone, receiver_map, accessor_ref.value(), holder);
626   }
627 }
628 
629 }  // namespace
630 
ComputeAccessorDescriptorAccessInfo(MapRef receiver_map,NameRef name,MapRef holder_map,base::Optional<JSObjectRef> holder,InternalIndex descriptor,AccessMode access_mode) const631 PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
632     MapRef receiver_map, NameRef name, MapRef holder_map,
633     base::Optional<JSObjectRef> holder, InternalIndex descriptor,
634     AccessMode access_mode) const {
635   DCHECK(descriptor.is_found());
636   Handle<DescriptorArray> descriptors = broker()->CanonicalPersistentHandle(
637       holder_map.object()->instance_descriptors(kRelaxedLoad));
638   SLOW_DCHECK(descriptor ==
639               descriptors->Search(*name.object(), *holder_map.object()));
640 
641   auto get_accessors = [&]() {
642     return broker()->CanonicalPersistentHandle(
643         descriptors->GetStrongValue(descriptor));
644   };
645   return AccessorAccessInfoHelper(isolate(), zone(), broker(), this,
646                                   receiver_map, name, holder_map, holder,
647                                   access_mode, get_accessors);
648 }
649 
ComputeDictionaryProtoAccessInfo(MapRef receiver_map,NameRef name,JSObjectRef holder,InternalIndex dictionary_index,AccessMode access_mode,PropertyDetails details) const650 PropertyAccessInfo AccessInfoFactory::ComputeDictionaryProtoAccessInfo(
651     MapRef receiver_map, NameRef name, JSObjectRef holder,
652     InternalIndex dictionary_index, AccessMode access_mode,
653     PropertyDetails details) const {
654   CHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL);
655   DCHECK(holder.map().object()->is_prototype_map());
656   DCHECK_EQ(access_mode, AccessMode::kLoad);
657 
658   // We can only inline accesses to constant properties.
659   if (details.constness() != PropertyConstness::kConst) {
660     return Invalid();
661   }
662 
663   if (details.kind() == PropertyKind::kData) {
664     return PropertyAccessInfo::DictionaryProtoDataConstant(
665         zone(), receiver_map, holder, dictionary_index, name);
666   }
667 
668   auto get_accessors = [&]() {
669     return JSObject::DictionaryPropertyAt(isolate(), holder.object(),
670                                           dictionary_index);
671   };
672   return AccessorAccessInfoHelper(isolate(), zone(), broker(), this,
673                                   receiver_map, name, holder.map(), holder,
674                                   access_mode, get_accessors);
675 }
676 
ComputePropertyAccessInfo(MinimorphicLoadPropertyAccessFeedback const & feedback) const677 MinimorphicLoadPropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
678     MinimorphicLoadPropertyAccessFeedback const& feedback) const {
679   DCHECK(feedback.handler()->IsSmi());
680   int handler = Smi::cast(*feedback.handler()).value();
681   bool is_inobject = LoadHandler::IsInobjectBits::decode(handler);
682   bool is_double = LoadHandler::IsDoubleBits::decode(handler);
683   int offset = LoadHandler::FieldIndexBits::decode(handler) * kTaggedSize;
684   Representation field_rep =
685       is_double ? Representation::Double() : Representation::Tagged();
686   Type field_type = is_double ? Type::Number() : Type::Any();
687   return MinimorphicLoadPropertyAccessInfo::DataField(offset, is_inobject,
688                                                       field_rep, field_type);
689 }
690 
TryLoadPropertyDetails(MapRef map,base::Optional<JSObjectRef> maybe_holder,NameRef name,InternalIndex * index_out,PropertyDetails * details_out) const691 bool AccessInfoFactory::TryLoadPropertyDetails(
692     MapRef map, base::Optional<JSObjectRef> maybe_holder, NameRef name,
693     InternalIndex* index_out, PropertyDetails* details_out) const {
694   if (map.is_dictionary_map()) {
695     DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL);
696     DCHECK(map.object()->is_prototype_map());
697 
698     DisallowGarbageCollection no_gc;
699 
700     if (!maybe_holder.has_value()) {
701       // TODO(v8:11457) In this situation, we have a dictionary mode prototype
702       // as a receiver. Consider other means of obtaining the holder in this
703       // situation.
704 
705       // Without the holder, we can't get the property details.
706       return false;
707     }
708 
709     Handle<JSObject> holder = maybe_holder->object();
710     if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
711       SwissNameDictionary dict = holder->property_dictionary_swiss();
712       *index_out = dict.FindEntry(isolate(), name.object());
713       if (index_out->is_found()) {
714         *details_out = dict.DetailsAt(*index_out);
715       }
716     } else {
717       NameDictionary dict = holder->property_dictionary();
718       *index_out = dict.FindEntry(isolate(), name.object());
719       if (index_out->is_found()) {
720         *details_out = dict.DetailsAt(*index_out);
721       }
722     }
723   } else {
724     DescriptorArray descriptors = *map.instance_descriptors().object();
725     *index_out = descriptors.Search(*name.object(), *map.object(),
726                                     broker()->is_concurrent_inlining());
727     if (index_out->is_found()) {
728       *details_out = descriptors.GetDetails(*index_out);
729     }
730   }
731 
732   return true;
733 }
734 
ComputePropertyAccessInfo(MapRef map,NameRef name,AccessMode access_mode) const735 PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
736     MapRef map, NameRef name, AccessMode access_mode) const {
737   CHECK(name.IsUniqueName());
738 
739   // Dictionary property const tracking is unsupported when concurrent inlining
740   // is enabled.
741   CHECK_IMPLIES(V8_DICT_PROPERTY_CONST_TRACKING_BOOL,
742                 !broker()->is_concurrent_inlining());
743 
744   JSHeapBroker::MapUpdaterGuardIfNeeded mumd_scope(broker());
745 
746   if (access_mode == AccessMode::kHas && !map.object()->IsJSReceiverMap()) {
747     return Invalid();
748   }
749 
750   // Check if it is safe to inline property access for the {map}.
751   if (!CanInlinePropertyAccess(map, access_mode)) {
752     return Invalid();
753   }
754 
755   // We support fast inline cases for certain JSObject getters.
756   if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
757     PropertyAccessInfo access_info = LookupSpecialFieldAccessor(map, name);
758     if (!access_info.IsInvalid()) return access_info;
759   }
760 
761   // Only relevant if V8_DICT_PROPERTY_CONST_TRACKING enabled.
762   bool dictionary_prototype_on_chain = false;
763   bool fast_mode_prototype_on_chain = false;
764 
765   // Remember the receiver map. We use {map} as loop variable.
766   MapRef receiver_map = map;
767   base::Optional<JSObjectRef> holder;
768 
769   // Perform the implicit ToObject for primitives here.
770   // Implemented according to ES6 section 7.3.2 GetV (V, P).
771   // Note: Keep sync'd with
772   // CompilationDependencies::DependOnStablePrototypeChains.
773   if (receiver_map.IsPrimitiveMap()) {
774     base::Optional<JSFunctionRef> constructor =
775         broker()->target_native_context().GetConstructorFunction(receiver_map);
776     if (!constructor.has_value()) return Invalid();
777     map = constructor->initial_map(broker()->dependencies());
778     DCHECK(!map.IsPrimitiveMap());
779   }
780 
781   while (true) {
782     PropertyDetails details = PropertyDetails::Empty();
783     InternalIndex index = InternalIndex::NotFound();
784     if (!TryLoadPropertyDetails(map, holder, name, &index, &details)) {
785       return Invalid();
786     }
787 
788     if (index.is_found()) {
789       if (access_mode == AccessMode::kStore ||
790           access_mode == AccessMode::kStoreInLiteral) {
791         DCHECK(!map.is_dictionary_map());
792 
793         // Don't bother optimizing stores to read-only properties.
794         if (details.IsReadOnly()) return Invalid();
795 
796         if (details.kind() == kData && holder.has_value()) {
797           // This is a store to a property not found on the receiver but on a
798           // prototype. According to ES6 section 9.1.9 [[Set]], we need to
799           // create a new data property on the receiver. We can still optimize
800           // if such a transition already exists.
801           return LookupTransition(receiver_map, name, holder);
802         }
803       }
804 
805       if (map.is_dictionary_map()) {
806         DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL);
807 
808         if (fast_mode_prototype_on_chain) {
809           // TODO(v8:11248) While the work on dictionary mode prototypes is in
810           // progress, we may still see fast mode objects on the chain prior to
811           // reaching a dictionary mode prototype holding the property . Due to
812           // this only being an intermediate state, we don't stupport these kind
813           // of heterogenous prototype chains.
814           return Invalid();
815         }
816 
817         // TryLoadPropertyDetails only succeeds if we know the holder.
818         return ComputeDictionaryProtoAccessInfo(
819             receiver_map, name, holder.value(), index, access_mode, details);
820       }
821 
822       if (dictionary_prototype_on_chain) {
823         // If V8_DICT_PROPERTY_CONST_TRACKING_BOOL was disabled, then a
824         // dictionary prototype would have caused a bailout earlier.
825         DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL);
826 
827         // TODO(v8:11248) We have a fast mode holder, but there was a dictionary
828         // mode prototype earlier on the chain. Note that seeing a fast mode
829         // prototype even though V8_DICT_PROPERTY_CONST_TRACKING is enabled
830         // should only be possible while the implementation of dictionary mode
831         // prototypes is work in progress. Eventually, enabling
832         // V8_DICT_PROPERTY_CONST_TRACKING will guarantee that all prototypes
833         // are always in dictionary mode, making this case unreachable. However,
834         // due to the complications of checking dictionary mode prototypes for
835         // modification, we don't attempt to support dictionary mode prototypes
836         // occuring before a fast mode holder on the chain.
837         return Invalid();
838       }
839       if (details.location() == PropertyLocation::kField) {
840         if (details.kind() == kData) {
841           return ComputeDataFieldAccessInfo(receiver_map, map, holder, index,
842                                             access_mode);
843         } else {
844           DCHECK_EQ(kAccessor, details.kind());
845           // TODO(turbofan): Add support for general accessors?
846           return Invalid();
847         }
848       } else {
849         DCHECK_EQ(PropertyLocation::kDescriptor, details.location());
850         DCHECK_EQ(kAccessor, details.kind());
851         return ComputeAccessorDescriptorAccessInfo(receiver_map, name, map,
852                                                    holder, index, access_mode);
853       }
854 
855       UNREACHABLE();
856     }
857 
858     // The property wasn't found on {map}. Look on the prototype if appropriate.
859     DCHECK(!index.is_found());
860 
861     // Don't search on the prototype chain for special indices in case of
862     // integer indexed exotic objects (see ES6 section 9.4.5).
863     if (map.object()->IsJSTypedArrayMap() && name.IsString()) {
864       if (broker()->IsMainThread()) {
865         if (IsSpecialIndex(String::cast(*name.object()))) {
866           return Invalid();
867         }
868       } else {
869         // TODO(jgruber): We are being conservative here since we can't access
870         // string contents from background threads. Should that become possible
871         // in the future, remove this bailout.
872         return Invalid();
873       }
874     }
875 
876     // Don't search on the prototype when storing in literals.
877     if (access_mode == AccessMode::kStoreInLiteral) {
878       return LookupTransition(receiver_map, name, holder);
879     }
880 
881     // Don't lookup private symbols on the prototype chain.
882     if (name.object()->IsPrivate()) {
883       return Invalid();
884     }
885 
886     if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL && holder.has_value()) {
887       // At this point, we are past the first loop iteration.
888       DCHECK(holder->object()->map().is_prototype_map());
889       DCHECK(!holder->map().equals(receiver_map));
890 
891       fast_mode_prototype_on_chain =
892           fast_mode_prototype_on_chain || !map.is_dictionary_map();
893       dictionary_prototype_on_chain =
894           dictionary_prototype_on_chain || map.is_dictionary_map();
895     }
896 
897     // Walk up the prototype chain.
898     if (!broker()->is_concurrent_inlining()) {
899       if (!map.TrySerializePrototype(NotConcurrentInliningTag{broker()})) {
900         return Invalid();
901       }
902     }
903 
904     // Load the map's prototype's map to guarantee that every time we use it,
905     // we use the same Map.
906     base::Optional<HeapObjectRef> prototype = map.prototype();
907     if (!prototype.has_value()) return Invalid();
908 
909     MapRef map_prototype_map = prototype->map();
910     if (!map_prototype_map.object()->IsJSObjectMap()) {
911       // Don't allow proxies on the prototype chain.
912       if (!prototype->IsNull()) {
913         DCHECK(prototype->object()->IsJSProxy());
914         return Invalid();
915       }
916 
917       DCHECK(prototype->IsNull());
918 
919       if (dictionary_prototype_on_chain) {
920         // TODO(v8:11248) See earlier comment about
921         // dictionary_prototype_on_chain. We don't support absent properties
922         // with dictionary mode prototypes on the chain, either. This is again
923         // just due to how we currently deal with dependencies for dictionary
924         // properties during finalization.
925         return Invalid();
926       }
927 
928       // Store to property not found on the receiver or any prototype, we need
929       // to transition to a new data property.
930       // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
931       if (access_mode == AccessMode::kStore) {
932         return LookupTransition(receiver_map, name, holder);
933       }
934 
935       // The property was not found (access returns undefined or throws
936       // depending on the language mode of the load operation.
937       // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
938       return PropertyAccessInfo::NotFound(zone(), receiver_map, holder);
939     }
940 
941     holder = prototype->AsJSObject();
942     map = map_prototype_map;
943 
944     if (!CanInlinePropertyAccess(map, access_mode)) {
945       return Invalid();
946     }
947 
948     // Successful lookup on prototype chain needs to guarantee that all the
949     // prototypes up to the holder have stable maps, except for dictionary-mode
950     // prototypes. We currently do this by taking a
951     // DependOnStablePrototypeChains dependency in the caller.
952     //
953     // TODO(jgruber): This is brittle and easy to miss. Consider a refactor
954     // that moves the responsibility of taking the dependency into
955     // AccessInfoFactory.
956   }
957   UNREACHABLE();
958 }
959 
FinalizePropertyAccessInfosAsOne(ZoneVector<PropertyAccessInfo> access_infos,AccessMode access_mode) const960 PropertyAccessInfo AccessInfoFactory::FinalizePropertyAccessInfosAsOne(
961     ZoneVector<PropertyAccessInfo> access_infos, AccessMode access_mode) const {
962   ZoneVector<PropertyAccessInfo> merged_access_infos(zone());
963   MergePropertyAccessInfos(access_infos, access_mode, &merged_access_infos);
964   if (merged_access_infos.size() == 1) {
965     PropertyAccessInfo& result = merged_access_infos.front();
966     if (!result.IsInvalid()) {
967       result.RecordDependencies(dependencies());
968       return result;
969     }
970   }
971   return Invalid();
972 }
973 
RecordDependencies(CompilationDependencies * dependencies)974 void PropertyAccessInfo::RecordDependencies(
975     CompilationDependencies* dependencies) {
976   for (CompilationDependency const* d : unrecorded_dependencies_) {
977     dependencies->RecordDependency(d);
978   }
979   unrecorded_dependencies_.clear();
980 }
981 
FinalizePropertyAccessInfos(ZoneVector<PropertyAccessInfo> access_infos,AccessMode access_mode,ZoneVector<PropertyAccessInfo> * result) const982 bool AccessInfoFactory::FinalizePropertyAccessInfos(
983     ZoneVector<PropertyAccessInfo> access_infos, AccessMode access_mode,
984     ZoneVector<PropertyAccessInfo>* result) const {
985   if (access_infos.empty()) return false;
986   MergePropertyAccessInfos(access_infos, access_mode, result);
987   for (PropertyAccessInfo const& info : *result) {
988     if (info.IsInvalid()) return false;
989   }
990   for (PropertyAccessInfo& info : *result) {
991     info.RecordDependencies(dependencies());
992   }
993   return true;
994 }
995 
MergePropertyAccessInfos(ZoneVector<PropertyAccessInfo> infos,AccessMode access_mode,ZoneVector<PropertyAccessInfo> * result) const996 void AccessInfoFactory::MergePropertyAccessInfos(
997     ZoneVector<PropertyAccessInfo> infos, AccessMode access_mode,
998     ZoneVector<PropertyAccessInfo>* result) const {
999   DCHECK(result->empty());
1000   for (auto it = infos.begin(), end = infos.end(); it != end; ++it) {
1001     bool merged = false;
1002     for (auto ot = it + 1; ot != end; ++ot) {
1003       if (ot->Merge(&(*it), access_mode, zone())) {
1004         merged = true;
1005         break;
1006       }
1007     }
1008     if (!merged) result->push_back(*it);
1009   }
1010   CHECK(!result->empty());
1011 }
1012 
isolate() const1013 Isolate* AccessInfoFactory::isolate() const { return broker()->isolate(); }
1014 
1015 namespace {
1016 
GeneralizeElementsKind(ElementsKind this_kind,ElementsKind that_kind)1017 Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
1018                                            ElementsKind that_kind) {
1019   if (IsHoleyElementsKind(this_kind)) {
1020     that_kind = GetHoleyElementsKind(that_kind);
1021   } else if (IsHoleyElementsKind(that_kind)) {
1022     this_kind = GetHoleyElementsKind(this_kind);
1023   }
1024   if (this_kind == that_kind) return Just(this_kind);
1025   if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
1026     if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
1027       return Just(this_kind);
1028     }
1029     if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
1030       return Just(that_kind);
1031     }
1032   }
1033   return Nothing<ElementsKind>();
1034 }
1035 
1036 }  // namespace
1037 
ConsolidateElementLoad(ElementAccessFeedback const & feedback) const1038 base::Optional<ElementAccessInfo> AccessInfoFactory::ConsolidateElementLoad(
1039     ElementAccessFeedback const& feedback) const {
1040   if (feedback.transition_groups().empty()) return {};
1041 
1042   DCHECK(!feedback.transition_groups().front().empty());
1043   Handle<Map> first_map = feedback.transition_groups().front().front();
1044   base::Optional<MapRef> first_map_ref = TryMakeRef(broker(), first_map);
1045   if (!first_map_ref.has_value()) return {};
1046   InstanceType instance_type = first_map_ref->instance_type();
1047   ElementsKind elements_kind = first_map_ref->elements_kind();
1048 
1049   ZoneVector<MapRef> maps(zone());
1050   for (auto const& group : feedback.transition_groups()) {
1051     for (Handle<Map> map_handle : group) {
1052       base::Optional<MapRef> map = TryMakeRef(broker(), map_handle);
1053       if (!map.has_value()) return {};
1054       if (map->instance_type() != instance_type ||
1055           !map->CanInlineElementAccess()) {
1056         return {};
1057       }
1058       if (!GeneralizeElementsKind(elements_kind, map->elements_kind())
1059                .To(&elements_kind)) {
1060         return {};
1061       }
1062       maps.push_back(map.value());
1063     }
1064   }
1065 
1066   return ElementAccessInfo(std::move(maps), elements_kind, zone());
1067 }
1068 
LookupSpecialFieldAccessor(MapRef map,NameRef name) const1069 PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor(
1070     MapRef map, NameRef name) const {
1071   // Check for String::length field accessor.
1072   if (map.object()->IsStringMap()) {
1073     if (Name::Equals(isolate(), name.object(),
1074                      isolate()->factory()->length_string())) {
1075       return PropertyAccessInfo::StringLength(zone(), map);
1076     }
1077     return Invalid();
1078   }
1079   // Check for special JSObject field accessors.
1080   FieldIndex field_index;
1081   if (Accessors::IsJSObjectFieldAccessor(isolate(), map.object(), name.object(),
1082                                          &field_index)) {
1083     Type field_type = Type::NonInternal();
1084     Representation field_representation = Representation::Tagged();
1085     if (map.object()->IsJSArrayMap()) {
1086       DCHECK(Name::Equals(isolate(), isolate()->factory()->length_string(),
1087                           name.object()));
1088       // The JSArray::length property is a smi in the range
1089       // [0, FixedDoubleArray::kMaxLength] in case of fast double
1090       // elements, a smi in the range [0, FixedArray::kMaxLength]
1091       // in case of other fast elements, and [0, kMaxUInt32] in
1092       // case of other arrays.
1093       if (IsDoubleElementsKind(map.elements_kind())) {
1094         field_type = type_cache_->kFixedDoubleArrayLengthType;
1095         field_representation = Representation::Smi();
1096       } else if (IsFastElementsKind(map.elements_kind())) {
1097         field_type = type_cache_->kFixedArrayLengthType;
1098         field_representation = Representation::Smi();
1099       } else {
1100         field_type = type_cache_->kJSArrayLengthType;
1101       }
1102     }
1103     // Special fields are always mutable.
1104     return PropertyAccessInfo::DataField(zone(), map, {{}, zone()}, field_index,
1105                                          field_representation, field_type, map,
1106                                          {}, {}, {});
1107   }
1108   return Invalid();
1109 }
1110 
LookupTransition(MapRef map,NameRef name,base::Optional<JSObjectRef> holder) const1111 PropertyAccessInfo AccessInfoFactory::LookupTransition(
1112     MapRef map, NameRef name, base::Optional<JSObjectRef> holder) const {
1113   // Check if the {map} has a data transition with the given {name}.
1114   Map transition = TransitionsAccessor(isolate(), map.object(),
1115                                        broker()->is_concurrent_inlining())
1116                        .SearchTransition(*name.object(), kData, NONE);
1117   if (transition.is_null()) return Invalid();
1118 
1119   base::Optional<MapRef> maybe_transition_map =
1120       TryMakeRef(broker(), transition);
1121   if (!maybe_transition_map.has_value()) return Invalid();
1122   MapRef transition_map = maybe_transition_map.value();
1123 
1124   InternalIndex const number = transition_map.object()->LastAdded();
1125   Handle<DescriptorArray> descriptors =
1126       transition_map.instance_descriptors().object();
1127   PropertyDetails const details = descriptors->GetDetails(number);
1128 
1129   // Don't bother optimizing stores to read-only properties.
1130   if (details.IsReadOnly()) return Invalid();
1131 
1132   // TODO(bmeurer): Handle transition to data constant?
1133   if (details.location() != PropertyLocation::kField) return Invalid();
1134 
1135   int const index = details.field_index();
1136   Representation details_representation = details.representation();
1137   if (details_representation.IsNone()) return Invalid();
1138 
1139   FieldIndex field_index = FieldIndex::ForPropertyIndex(
1140       *transition_map.object(), index, details_representation);
1141   Type field_type = Type::NonInternal();
1142   base::Optional<MapRef> field_map;
1143 
1144   ZoneVector<CompilationDependency const*> unrecorded_dependencies(zone());
1145   if (details_representation.IsSmi()) {
1146     field_type = Type::SignedSmall();
1147     unrecorded_dependencies.push_back(
1148         dependencies()->FieldRepresentationDependencyOffTheRecord(
1149             transition_map, number, details_representation));
1150   } else if (details_representation.IsDouble()) {
1151     field_type = type_cache_->kFloat64;
1152     unrecorded_dependencies.push_back(
1153         dependencies()->FieldRepresentationDependencyOffTheRecord(
1154             transition_map, number, details_representation));
1155   } else if (details_representation.IsHeapObject()) {
1156     // Extract the field type from the property details (make sure its
1157     // representation is TaggedPointer to reflect the heap object case).
1158     // TODO(jgruber,v8:7790): Use DescriptorArrayRef instead.
1159     Handle<FieldType> descriptors_field_type =
1160         broker()->CanonicalPersistentHandle(descriptors->GetFieldType(number));
1161     base::Optional<ObjectRef> descriptors_field_type_ref =
1162         TryMakeRef<Object>(broker(), descriptors_field_type);
1163     if (!descriptors_field_type_ref.has_value()) return Invalid();
1164 
1165     if (descriptors_field_type->IsNone()) {
1166       // Store is not safe if the field type was cleared.
1167       return Invalid();
1168     }
1169     unrecorded_dependencies.push_back(
1170         dependencies()->FieldRepresentationDependencyOffTheRecord(
1171             transition_map, number, details_representation));
1172     if (descriptors_field_type->IsClass()) {
1173       unrecorded_dependencies.push_back(
1174           dependencies()->FieldTypeDependencyOffTheRecord(
1175               transition_map, number, *descriptors_field_type_ref));
1176       // Remember the field map, and try to infer a useful type.
1177       base::Optional<MapRef> maybe_field_map =
1178           TryMakeRef(broker(), descriptors_field_type->AsClass());
1179       if (!maybe_field_map.has_value()) return Invalid();
1180       field_type = Type::For(maybe_field_map.value());
1181       field_map = maybe_field_map;
1182     }
1183   }
1184 
1185   unrecorded_dependencies.push_back(
1186       dependencies()->TransitionDependencyOffTheRecord(transition_map));
1187   if (!broker()->is_concurrent_inlining()) {
1188     transition_map.SerializeBackPointer(
1189         NotConcurrentInliningTag{broker()});  // For BuildPropertyStore.
1190   }
1191 
1192   // Transitioning stores *may* store to const fields. The resulting
1193   // DataConstant access infos can be distinguished from later, i.e. redundant,
1194   // stores to the same constant field by the presence of a transition map.
1195   switch (dependencies()->DependOnFieldConstness(transition_map, number)) {
1196     case PropertyConstness::kMutable:
1197       return PropertyAccessInfo::DataField(
1198           zone(), map, std::move(unrecorded_dependencies), field_index,
1199           details_representation, field_type, transition_map, field_map, holder,
1200           transition_map);
1201     case PropertyConstness::kConst:
1202       return PropertyAccessInfo::FastDataConstant(
1203           zone(), map, std::move(unrecorded_dependencies), field_index,
1204           details_representation, field_type, transition_map, field_map, holder,
1205           transition_map);
1206   }
1207   UNREACHABLE();
1208 }
1209 
1210 }  // namespace compiler
1211 }  // namespace internal
1212 }  // namespace v8
1213