• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/builtins/accessors.h"
10 #include "src/compiler/compilation-dependencies.h"
11 #include "src/compiler/compilation-dependency.h"
12 #include "src/compiler/simplified-operator.h"
13 #include "src/compiler/type-cache.h"
14 #include "src/ic/call-optimization.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(Handle<Map> map)30 bool CanInlinePropertyAccess(Handle<Map> map) {
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   STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_HEAP_OBJECT_TYPE);
35   if (map->IsBooleanMap()) return true;
36   if (map->instance_type() < LAST_PRIMITIVE_HEAP_OBJECT_TYPE) return true;
37   return map->IsJSObjectMap() && !map->is_dictionary_map() &&
38          !map->has_named_interceptor() &&
39          // TODO(verwaest): Allowlist contexts to which we have access.
40          !map->is_access_check_needed();
41 }
42 
43 #ifdef DEBUG
HasFieldRepresentationDependenciesOnMap(ZoneVector<CompilationDependency const * > & dependencies,Handle<Map> const & field_owner_map)44 bool HasFieldRepresentationDependenciesOnMap(
45     ZoneVector<CompilationDependency const*>& dependencies,
46     Handle<Map> const& field_owner_map) {
47   for (auto dep : dependencies) {
48     if (dep->IsFieldRepresentationDependencyOnMap(field_owner_map)) {
49       return true;
50     }
51   }
52   return false;
53 }
54 #endif
55 
56 }  // namespace
57 
58 
operator <<(std::ostream & os,AccessMode access_mode)59 std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
60   switch (access_mode) {
61     case AccessMode::kLoad:
62       return os << "Load";
63     case AccessMode::kStore:
64       return os << "Store";
65     case AccessMode::kStoreInLiteral:
66       return os << "StoreInLiteral";
67     case AccessMode::kHas:
68       return os << "Has";
69   }
70   UNREACHABLE();
71 }
72 
ElementAccessInfo(ZoneVector<Handle<Map>> && lookup_start_object_maps,ElementsKind elements_kind,Zone * zone)73 ElementAccessInfo::ElementAccessInfo(
74     ZoneVector<Handle<Map>>&& lookup_start_object_maps,
75     ElementsKind elements_kind, Zone* zone)
76     : elements_kind_(elements_kind),
77       lookup_start_object_maps_(lookup_start_object_maps),
78       transition_sources_(zone) {
79   CHECK(!lookup_start_object_maps.empty());
80 }
81 
82 // static
Invalid(Zone * zone)83 PropertyAccessInfo PropertyAccessInfo::Invalid(Zone* zone) {
84   return PropertyAccessInfo(zone);
85 }
86 
87 // static
NotFound(Zone * zone,Handle<Map> receiver_map,MaybeHandle<JSObject> holder)88 PropertyAccessInfo PropertyAccessInfo::NotFound(Zone* zone,
89                                                 Handle<Map> receiver_map,
90                                                 MaybeHandle<JSObject> holder) {
91   return PropertyAccessInfo(zone, kNotFound, holder, {{receiver_map}, zone});
92 }
93 
94 // static
DataField(Zone * zone,Handle<Map> receiver_map,ZoneVector<CompilationDependency const * > && dependencies,FieldIndex field_index,Representation field_representation,Type field_type,Handle<Map> field_owner_map,MaybeHandle<Map> field_map,MaybeHandle<JSObject> holder,MaybeHandle<Map> transition_map)95 PropertyAccessInfo PropertyAccessInfo::DataField(
96     Zone* zone, Handle<Map> receiver_map,
97     ZoneVector<CompilationDependency const*>&& dependencies,
98     FieldIndex field_index, Representation field_representation,
99     Type field_type, Handle<Map> field_owner_map, MaybeHandle<Map> field_map,
100     MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map) {
101   DCHECK_IMPLIES(
102       field_representation.IsDouble(),
103       HasFieldRepresentationDependenciesOnMap(dependencies, field_owner_map));
104   return PropertyAccessInfo(kDataField, holder, transition_map, field_index,
105                             field_representation, field_type, field_owner_map,
106                             field_map, {{receiver_map}, zone},
107                             std::move(dependencies));
108 }
109 
110 // static
DataConstant(Zone * zone,Handle<Map> receiver_map,ZoneVector<CompilationDependency const * > && dependencies,FieldIndex field_index,Representation field_representation,Type field_type,Handle<Map> field_owner_map,MaybeHandle<Map> field_map,MaybeHandle<JSObject> holder,MaybeHandle<Map> transition_map)111 PropertyAccessInfo PropertyAccessInfo::DataConstant(
112     Zone* zone, Handle<Map> receiver_map,
113     ZoneVector<CompilationDependency const*>&& dependencies,
114     FieldIndex field_index, Representation field_representation,
115     Type field_type, Handle<Map> field_owner_map, MaybeHandle<Map> field_map,
116     MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map) {
117   return PropertyAccessInfo(kDataConstant, holder, transition_map, field_index,
118                             field_representation, field_type, field_owner_map,
119                             field_map, {{receiver_map}, zone},
120                             std::move(dependencies));
121 }
122 
123 // static
AccessorConstant(Zone * zone,Handle<Map> receiver_map,Handle<Object> constant,MaybeHandle<JSObject> holder)124 PropertyAccessInfo PropertyAccessInfo::AccessorConstant(
125     Zone* zone, Handle<Map> receiver_map, Handle<Object> constant,
126     MaybeHandle<JSObject> holder) {
127   return PropertyAccessInfo(zone, kAccessorConstant, holder, constant,
128                             {{receiver_map}, zone});
129 }
130 
131 // static
ModuleExport(Zone * zone,Handle<Map> receiver_map,Handle<Cell> cell)132 PropertyAccessInfo PropertyAccessInfo::ModuleExport(Zone* zone,
133                                                     Handle<Map> receiver_map,
134                                                     Handle<Cell> cell) {
135   return PropertyAccessInfo(zone, kModuleExport, MaybeHandle<JSObject>(), cell,
136                             {{receiver_map}, zone});
137 }
138 
139 // static
StringLength(Zone * zone,Handle<Map> receiver_map)140 PropertyAccessInfo PropertyAccessInfo::StringLength(Zone* zone,
141                                                     Handle<Map> receiver_map) {
142   return PropertyAccessInfo(zone, kStringLength, MaybeHandle<JSObject>(),
143                             {{receiver_map}, zone});
144 }
145 
146 // static
DataField(int offset,bool is_inobject,Representation field_representation,Type field_type)147 MinimorphicLoadPropertyAccessInfo MinimorphicLoadPropertyAccessInfo::DataField(
148     int offset, bool is_inobject, Representation field_representation,
149     Type field_type) {
150   return MinimorphicLoadPropertyAccessInfo(kDataField, offset, is_inobject,
151                                            field_representation, field_type);
152 }
153 
154 // static
Invalid()155 MinimorphicLoadPropertyAccessInfo MinimorphicLoadPropertyAccessInfo::Invalid() {
156   return MinimorphicLoadPropertyAccessInfo(
157       kInvalid, -1, false, Representation::None(), Type::None());
158 }
159 
PropertyAccessInfo(Zone * zone)160 PropertyAccessInfo::PropertyAccessInfo(Zone* zone)
161     : kind_(kInvalid),
162       lookup_start_object_maps_(zone),
163       unrecorded_dependencies_(zone),
164       field_representation_(Representation::None()),
165       field_type_(Type::None()) {}
166 
PropertyAccessInfo(Zone * zone,Kind kind,MaybeHandle<JSObject> holder,ZoneVector<Handle<Map>> && lookup_start_object_maps)167 PropertyAccessInfo::PropertyAccessInfo(
168     Zone* zone, Kind kind, MaybeHandle<JSObject> holder,
169     ZoneVector<Handle<Map>>&& lookup_start_object_maps)
170     : kind_(kind),
171       lookup_start_object_maps_(lookup_start_object_maps),
172       unrecorded_dependencies_(zone),
173       holder_(holder),
174       field_representation_(Representation::None()),
175       field_type_(Type::None()) {}
176 
PropertyAccessInfo(Zone * zone,Kind kind,MaybeHandle<JSObject> holder,Handle<Object> constant,ZoneVector<Handle<Map>> && lookup_start_object_maps)177 PropertyAccessInfo::PropertyAccessInfo(
178     Zone* zone, Kind kind, MaybeHandle<JSObject> holder,
179     Handle<Object> constant, ZoneVector<Handle<Map>>&& lookup_start_object_maps)
180     : kind_(kind),
181       lookup_start_object_maps_(lookup_start_object_maps),
182       unrecorded_dependencies_(zone),
183       constant_(constant),
184       holder_(holder),
185       field_representation_(Representation::None()),
186       field_type_(Type::Any()) {}
187 
PropertyAccessInfo(Kind kind,MaybeHandle<JSObject> holder,MaybeHandle<Map> transition_map,FieldIndex field_index,Representation field_representation,Type field_type,Handle<Map> field_owner_map,MaybeHandle<Map> field_map,ZoneVector<Handle<Map>> && lookup_start_object_maps,ZoneVector<CompilationDependency const * > && unrecorded_dependencies)188 PropertyAccessInfo::PropertyAccessInfo(
189     Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
190     FieldIndex field_index, Representation field_representation,
191     Type field_type, Handle<Map> field_owner_map, MaybeHandle<Map> field_map,
192     ZoneVector<Handle<Map>>&& lookup_start_object_maps,
193     ZoneVector<CompilationDependency const*>&& unrecorded_dependencies)
194     : kind_(kind),
195       lookup_start_object_maps_(lookup_start_object_maps),
196       unrecorded_dependencies_(std::move(unrecorded_dependencies)),
197       transition_map_(transition_map),
198       holder_(holder),
199       field_index_(field_index),
200       field_representation_(field_representation),
201       field_type_(field_type),
202       field_owner_map_(field_owner_map),
203       field_map_(field_map) {
204   DCHECK_IMPLIES(!transition_map.is_null(),
205                  field_owner_map.address() == transition_map.address());
206 }
207 
MinimorphicLoadPropertyAccessInfo(Kind kind,int offset,bool is_inobject,Representation field_representation,Type field_type)208 MinimorphicLoadPropertyAccessInfo::MinimorphicLoadPropertyAccessInfo(
209     Kind kind, int offset, bool is_inobject,
210     Representation field_representation, Type field_type)
211     : kind_(kind),
212       is_inobject_(is_inobject),
213       offset_(offset),
214       field_representation_(field_representation),
215       field_type_(field_type) {}
216 
Merge(PropertyAccessInfo const * that,AccessMode access_mode,Zone * zone)217 bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
218                                AccessMode access_mode, Zone* zone) {
219   if (this->kind_ != that->kind_) return false;
220   if (this->holder_.address() != that->holder_.address()) return false;
221 
222   switch (this->kind_) {
223     case kInvalid:
224       return that->kind_ == kInvalid;
225 
226     case kDataField:
227     case kDataConstant: {
228       // Check if we actually access the same field (we use the
229       // GetFieldAccessStubKey method here just like the ICs do
230       // since that way we only compare the relevant bits of the
231       // field indices).
232       if (this->field_index_.GetFieldAccessStubKey() ==
233           that->field_index_.GetFieldAccessStubKey()) {
234         switch (access_mode) {
235           case AccessMode::kHas:
236           case AccessMode::kLoad: {
237             if (!this->field_representation_.Equals(
238                     that->field_representation_)) {
239               if (this->field_representation_.IsDouble() ||
240                   that->field_representation_.IsDouble()) {
241                 return false;
242               }
243               this->field_representation_ = Representation::Tagged();
244             }
245             if (this->field_map_.address() != that->field_map_.address()) {
246               this->field_map_ = MaybeHandle<Map>();
247             }
248             break;
249           }
250           case AccessMode::kStore:
251           case AccessMode::kStoreInLiteral: {
252             // For stores, the field map and field representation information
253             // must match exactly, otherwise we cannot merge the stores. We
254             // also need to make sure that in case of transitioning stores,
255             // the transition targets match.
256             if (this->field_map_.address() != that->field_map_.address() ||
257                 !this->field_representation_.Equals(
258                     that->field_representation_) ||
259                 this->transition_map_.address() !=
260                     that->transition_map_.address()) {
261               return false;
262             }
263             break;
264           }
265         }
266         this->field_type_ =
267             Type::Union(this->field_type_, that->field_type_, zone);
268         this->lookup_start_object_maps_.insert(
269             this->lookup_start_object_maps_.end(),
270             that->lookup_start_object_maps_.begin(),
271             that->lookup_start_object_maps_.end());
272         this->unrecorded_dependencies_.insert(
273             this->unrecorded_dependencies_.end(),
274             that->unrecorded_dependencies_.begin(),
275             that->unrecorded_dependencies_.end());
276         return true;
277       }
278       return false;
279     }
280 
281     case kAccessorConstant: {
282       // Check if we actually access the same constant.
283       if (this->constant_.address() == that->constant_.address()) {
284         DCHECK(this->unrecorded_dependencies_.empty());
285         DCHECK(that->unrecorded_dependencies_.empty());
286         this->lookup_start_object_maps_.insert(
287             this->lookup_start_object_maps_.end(),
288             that->lookup_start_object_maps_.begin(),
289             that->lookup_start_object_maps_.end());
290         return true;
291       }
292       return false;
293     }
294 
295     case kNotFound:
296     case kStringLength: {
297       DCHECK(this->unrecorded_dependencies_.empty());
298       DCHECK(that->unrecorded_dependencies_.empty());
299       this->lookup_start_object_maps_.insert(
300           this->lookup_start_object_maps_.end(),
301           that->lookup_start_object_maps_.begin(),
302           that->lookup_start_object_maps_.end());
303       return true;
304     }
305     case kModuleExport:
306       return false;
307   }
308 }
309 
GetConstFieldInfo() const310 ConstFieldInfo PropertyAccessInfo::GetConstFieldInfo() const {
311   if (IsDataConstant()) {
312     return ConstFieldInfo(field_owner_map_.ToHandleChecked());
313   }
314   return ConstFieldInfo::None();
315 }
316 
AccessInfoFactory(JSHeapBroker * broker,CompilationDependencies * dependencies,Zone * zone)317 AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
318                                      CompilationDependencies* dependencies,
319                                      Zone* zone)
320     : broker_(broker),
321       dependencies_(dependencies),
322       type_cache_(TypeCache::Get()),
323       zone_(zone) {}
324 
ComputeElementAccessInfo(Handle<Map> map,AccessMode access_mode) const325 base::Optional<ElementAccessInfo> AccessInfoFactory::ComputeElementAccessInfo(
326     Handle<Map> map, AccessMode access_mode) const {
327   // Check if it is safe to inline element access for the {map}.
328   MapRef map_ref(broker(), map);
329   if (!CanInlineElementAccess(map_ref)) return base::nullopt;
330   ElementsKind const elements_kind = map_ref.elements_kind();
331   return ElementAccessInfo({{map}, zone()}, elements_kind, zone());
332 }
333 
ComputeElementAccessInfos(ElementAccessFeedback const & feedback,ZoneVector<ElementAccessInfo> * access_infos) const334 bool AccessInfoFactory::ComputeElementAccessInfos(
335     ElementAccessFeedback const& feedback,
336     ZoneVector<ElementAccessInfo>* access_infos) const {
337   AccessMode access_mode = feedback.keyed_mode().access_mode();
338   if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
339     // For polymorphic loads of similar elements kinds (i.e. all tagged or all
340     // double), always use the "worst case" code without a transition.  This is
341     // much faster than transitioning the elements to the worst case, trading a
342     // TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
343     base::Optional<ElementAccessInfo> access_info =
344         ConsolidateElementLoad(feedback);
345     if (access_info.has_value()) {
346       access_infos->push_back(*access_info);
347       return true;
348     }
349   }
350 
351   for (auto const& group : feedback.transition_groups()) {
352     DCHECK(!group.empty());
353     Handle<Map> target = group.front();
354     base::Optional<ElementAccessInfo> access_info =
355         ComputeElementAccessInfo(target, access_mode);
356     if (!access_info.has_value()) return false;
357 
358     for (size_t i = 1; i < group.size(); ++i) {
359       access_info->AddTransitionSource(group[i]);
360     }
361     access_infos->push_back(*access_info);
362   }
363   return true;
364 }
365 
ComputeDataFieldAccessInfo(Handle<Map> receiver_map,Handle<Map> map,MaybeHandle<JSObject> holder,InternalIndex descriptor,AccessMode access_mode) const366 PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
367     Handle<Map> receiver_map, Handle<Map> map, MaybeHandle<JSObject> holder,
368     InternalIndex descriptor, AccessMode access_mode) const {
369   DCHECK(descriptor.is_found());
370   Handle<DescriptorArray> descriptors(map->instance_descriptors(kRelaxedLoad),
371                                       isolate());
372   PropertyDetails const details = descriptors->GetDetails(descriptor);
373   int index = descriptors->GetFieldIndex(descriptor);
374   Representation details_representation = details.representation();
375   if (details_representation.IsNone()) {
376     // The ICs collect feedback in PREMONOMORPHIC state already,
377     // but at this point the {receiver_map} might still contain
378     // fields for which the representation has not yet been
379     // determined by the runtime. So we need to catch this case
380     // here and fall back to use the regular IC logic instead.
381     return PropertyAccessInfo::Invalid(zone());
382   }
383   FieldIndex field_index =
384       FieldIndex::ForPropertyIndex(*map, index, details_representation);
385   Type field_type = Type::NonInternal();
386   MaybeHandle<Map> field_map;
387   MapRef map_ref(broker(), map);
388   ZoneVector<CompilationDependency const*> unrecorded_dependencies(zone());
389   map_ref.SerializeOwnDescriptor(descriptor);
390   if (details_representation.IsSmi()) {
391     field_type = Type::SignedSmall();
392     unrecorded_dependencies.push_back(
393         dependencies()->FieldRepresentationDependencyOffTheRecord(map_ref,
394                                                                   descriptor));
395   } else if (details_representation.IsDouble()) {
396     field_type = type_cache_->kFloat64;
397     if (!FLAG_unbox_double_fields) {
398       unrecorded_dependencies.push_back(
399           dependencies()->FieldRepresentationDependencyOffTheRecord(
400               map_ref, descriptor));
401     }
402   } else if (details_representation.IsHeapObject()) {
403     // Extract the field type from the property details (make sure its
404     // representation is TaggedPointer to reflect the heap object case).
405     Handle<FieldType> descriptors_field_type(
406         descriptors->GetFieldType(descriptor), isolate());
407     if (descriptors_field_type->IsNone()) {
408       // Store is not safe if the field type was cleared.
409       if (access_mode == AccessMode::kStore) {
410         return PropertyAccessInfo::Invalid(zone());
411       }
412 
413       // The field type was cleared by the GC, so we don't know anything
414       // about the contents now.
415     }
416     unrecorded_dependencies.push_back(
417         dependencies()->FieldRepresentationDependencyOffTheRecord(map_ref,
418                                                                   descriptor));
419     if (descriptors_field_type->IsClass()) {
420       // Remember the field map, and try to infer a useful type.
421       Handle<Map> map(descriptors_field_type->AsClass(), isolate());
422       field_type = Type::For(MapRef(broker(), map));
423       field_map = MaybeHandle<Map>(map);
424     }
425   } else {
426     CHECK(details_representation.IsTagged());
427   }
428   // TODO(turbofan): We may want to do this only depending on the use
429   // of the access info.
430   unrecorded_dependencies.push_back(
431       dependencies()->FieldTypeDependencyOffTheRecord(map_ref, descriptor));
432 
433   PropertyConstness constness;
434   if (details.IsReadOnly() && !details.IsConfigurable()) {
435     constness = PropertyConstness::kConst;
436   } else if (broker()->is_turboprop() && !map->is_prototype_map()) {
437     // The constness feedback is too unstable for the aggresive compilation
438     // of turboprop.
439     constness = PropertyConstness::kMutable;
440   } else {
441     map_ref.SerializeOwnDescriptor(descriptor);
442     constness = dependencies()->DependOnFieldConstness(map_ref, descriptor);
443   }
444   Handle<Map> field_owner_map(map->FindFieldOwner(isolate(), descriptor),
445                               isolate());
446   switch (constness) {
447     case PropertyConstness::kMutable:
448       return PropertyAccessInfo::DataField(
449           zone(), receiver_map, std::move(unrecorded_dependencies), field_index,
450           details_representation, field_type, field_owner_map, field_map,
451           holder);
452     case PropertyConstness::kConst:
453       return PropertyAccessInfo::DataConstant(
454           zone(), receiver_map, std::move(unrecorded_dependencies), field_index,
455           details_representation, field_type, field_owner_map, field_map,
456           holder);
457   }
458   UNREACHABLE();
459 }
460 
ComputeAccessorDescriptorAccessInfo(Handle<Map> receiver_map,Handle<Name> name,Handle<Map> map,MaybeHandle<JSObject> holder,InternalIndex descriptor,AccessMode access_mode) const461 PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
462     Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map,
463     MaybeHandle<JSObject> holder, InternalIndex descriptor,
464     AccessMode access_mode) const {
465   DCHECK(descriptor.is_found());
466   Handle<DescriptorArray> descriptors(map->instance_descriptors(kRelaxedLoad),
467                                       isolate());
468   SLOW_DCHECK(descriptor == descriptors->Search(*name, *map));
469   if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
470     DCHECK(map->is_prototype_map());
471     Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(map->prototype_info()),
472                                      isolate());
473     Handle<JSModuleNamespace> module_namespace(
474         JSModuleNamespace::cast(proto_info->module_namespace()), isolate());
475     Handle<Cell> cell(Cell::cast(module_namespace->module().exports().Lookup(
476                           isolate(), name, Smi::ToInt(name->GetHash()))),
477                       isolate());
478     if (cell->value().IsTheHole(isolate())) {
479       // This module has not been fully initialized yet.
480       return PropertyAccessInfo::Invalid(zone());
481     }
482     return PropertyAccessInfo::ModuleExport(zone(), receiver_map, cell);
483   }
484   if (access_mode == AccessMode::kHas) {
485     // HasProperty checks don't call getter/setters, existence is sufficient.
486     return PropertyAccessInfo::AccessorConstant(zone(), receiver_map,
487                                                 Handle<Object>(), holder);
488   }
489   Handle<Object> accessors(descriptors->GetStrongValue(descriptor), isolate());
490   if (!accessors->IsAccessorPair()) {
491     return PropertyAccessInfo::Invalid(zone());
492   }
493   Handle<Object> accessor(access_mode == AccessMode::kLoad
494                               ? Handle<AccessorPair>::cast(accessors)->getter()
495                               : Handle<AccessorPair>::cast(accessors)->setter(),
496                           isolate());
497   if (!accessor->IsJSFunction()) {
498     CallOptimization optimization(isolate(), accessor);
499     if (!optimization.is_simple_api_call() ||
500         optimization.IsCrossContextLazyAccessorPair(
501             *broker()->target_native_context().object(), *map)) {
502       return PropertyAccessInfo::Invalid(zone());
503     }
504 
505     CallOptimization::HolderLookup lookup;
506     holder = optimization.LookupHolderOfExpectedType(receiver_map, &lookup);
507     if (lookup == CallOptimization::kHolderNotFound) {
508       return PropertyAccessInfo::Invalid(zone());
509     }
510     DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
511                    holder.is_null());
512     DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound, !holder.is_null());
513   }
514   if (access_mode == AccessMode::kLoad) {
515     Handle<Name> cached_property_name;
516     if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), accessor)
517             .ToHandle(&cached_property_name)) {
518       PropertyAccessInfo access_info =
519           ComputePropertyAccessInfo(map, cached_property_name, access_mode);
520       if (!access_info.IsInvalid()) return access_info;
521     }
522   }
523   return PropertyAccessInfo::AccessorConstant(zone(), receiver_map, accessor,
524                                               holder);
525 }
526 
ComputePropertyAccessInfo(MinimorphicLoadPropertyAccessFeedback const & feedback) const527 MinimorphicLoadPropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
528     MinimorphicLoadPropertyAccessFeedback const& feedback) const {
529   DCHECK(feedback.handler()->IsSmi());
530   int handler = Smi::cast(*feedback.handler()).value();
531   bool is_inobject = LoadHandler::IsInobjectBits::decode(handler);
532   bool is_double = LoadHandler::IsDoubleBits::decode(handler);
533   int offset = LoadHandler::FieldIndexBits::decode(handler) * kTaggedSize;
534   Representation field_rep =
535       is_double ? Representation::Double() : Representation::Tagged();
536   Type field_type = is_double ? Type::Number() : Type::Any();
537   return MinimorphicLoadPropertyAccessInfo::DataField(offset, is_inobject,
538                                                       field_rep, field_type);
539 }
540 
ComputePropertyAccessInfo(Handle<Map> map,Handle<Name> name,AccessMode access_mode) const541 PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
542     Handle<Map> map, Handle<Name> name, AccessMode access_mode) const {
543   CHECK(name->IsUniqueName());
544 
545   if (access_mode == AccessMode::kHas && !map->IsJSReceiverMap()) {
546     return PropertyAccessInfo::Invalid(zone());
547   }
548 
549   // Check if it is safe to inline property access for the {map}.
550   if (!CanInlinePropertyAccess(map)) {
551     return PropertyAccessInfo::Invalid(zone());
552   }
553 
554   // We support fast inline cases for certain JSObject getters.
555   if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
556     PropertyAccessInfo access_info = LookupSpecialFieldAccessor(map, name);
557     if (!access_info.IsInvalid()) return access_info;
558   }
559 
560   // Remember the receiver map. We use {map} as loop variable.
561   Handle<Map> receiver_map = map;
562   MaybeHandle<JSObject> holder;
563   while (true) {
564     // Lookup the named property on the {map}.
565     Handle<DescriptorArray> descriptors(map->instance_descriptors(kAcquireLoad),
566                                         isolate());
567     InternalIndex const number =
568         descriptors->Search(*name, *map, broker()->is_concurrent_inlining());
569     if (number.is_found()) {
570       PropertyDetails const details = descriptors->GetDetails(number);
571       if (access_mode == AccessMode::kStore ||
572           access_mode == AccessMode::kStoreInLiteral) {
573         // Don't bother optimizing stores to read-only properties.
574         if (details.IsReadOnly()) {
575           return PropertyAccessInfo::Invalid(zone());
576         }
577         if (details.kind() == kData && !holder.is_null()) {
578           // This is a store to a property not found on the receiver but on a
579           // prototype. According to ES6 section 9.1.9 [[Set]], we need to
580           // create a new data property on the receiver. We can still optimize
581           // if such a transition already exists.
582           return LookupTransition(receiver_map, name, holder);
583         }
584       }
585       if (details.location() == kField) {
586         if (details.kind() == kData) {
587           return ComputeDataFieldAccessInfo(receiver_map, map, holder, number,
588                                             access_mode);
589         } else {
590           DCHECK_EQ(kAccessor, details.kind());
591           // TODO(turbofan): Add support for general accessors?
592           return PropertyAccessInfo::Invalid(zone());
593         }
594       } else {
595         DCHECK_EQ(kDescriptor, details.location());
596         DCHECK_EQ(kAccessor, details.kind());
597         return ComputeAccessorDescriptorAccessInfo(receiver_map, name, map,
598                                                    holder, number, access_mode);
599       }
600       UNREACHABLE();
601     }
602 
603     // The property wasn't found on {map}. Look on the prototype if appropriate.
604 
605     // Don't search on the prototype chain for special indices in case of
606     // integer indexed exotic objects (see ES6 section 9.4.5).
607     if (map->IsJSTypedArrayMap() && name->IsString() &&
608         IsSpecialIndex(String::cast(*name))) {
609       return PropertyAccessInfo::Invalid(zone());
610     }
611 
612     // Don't search on the prototype when storing in literals.
613     if (access_mode == AccessMode::kStoreInLiteral) {
614       return LookupTransition(receiver_map, name, holder);
615     }
616 
617     // Don't lookup private symbols on the prototype chain.
618     if (name->IsPrivate()) {
619       return PropertyAccessInfo::Invalid(zone());
620     }
621 
622     // Walk up the prototype chain.
623     MapRef(broker(), map).SerializePrototype();
624     // Acquire synchronously the map's prototype's map to guarantee that every
625     // time we use it, we use the same Map.
626     Handle<Map> map_prototype_map(map->prototype().synchronized_map(),
627                                   isolate());
628     if (!map_prototype_map->IsJSObjectMap()) {
629       // Perform the implicit ToObject for primitives here.
630       // Implemented according to ES6 section 7.3.2 GetV (V, P).
631       Handle<JSFunction> constructor;
632       if (Map::GetConstructorFunction(
633               map, broker()->target_native_context().object())
634               .ToHandle(&constructor)) {
635         map = handle(constructor->initial_map(), isolate());
636         map_prototype_map =
637             handle(map->prototype().synchronized_map(), isolate());
638         DCHECK(map_prototype_map->IsJSObjectMap());
639       } else if (map->prototype().IsNull()) {
640         // Store to property not found on the receiver or any prototype, we need
641         // to transition to a new data property.
642         // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
643         if (access_mode == AccessMode::kStore) {
644           return LookupTransition(receiver_map, name, holder);
645         }
646         // The property was not found (access returns undefined or throws
647         // depending on the language mode of the load operation.
648         // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
649         return PropertyAccessInfo::NotFound(zone(), receiver_map, holder);
650       } else {
651         return PropertyAccessInfo::Invalid(zone());
652       }
653     }
654 
655     holder = handle(JSObject::cast(map->prototype()), isolate());
656     map = map_prototype_map;
657     CHECK(!map->is_deprecated());
658 
659     if (!CanInlinePropertyAccess(map)) {
660       return PropertyAccessInfo::Invalid(zone());
661     }
662 
663     // Successful lookup on prototype chain needs to guarantee that all
664     // the prototypes up to the holder have stable maps. Let us make sure
665     // the prototype maps are stable here.
666     CHECK(map->is_stable());
667   }
668   UNREACHABLE();
669 }
670 
FinalizePropertyAccessInfosAsOne(ZoneVector<PropertyAccessInfo> access_infos,AccessMode access_mode) const671 PropertyAccessInfo AccessInfoFactory::FinalizePropertyAccessInfosAsOne(
672     ZoneVector<PropertyAccessInfo> access_infos, AccessMode access_mode) const {
673   ZoneVector<PropertyAccessInfo> merged_access_infos(zone());
674   MergePropertyAccessInfos(access_infos, access_mode, &merged_access_infos);
675   if (merged_access_infos.size() == 1) {
676     PropertyAccessInfo& result = merged_access_infos.front();
677     if (!result.IsInvalid()) {
678       result.RecordDependencies(dependencies());
679       return result;
680     }
681   }
682   return PropertyAccessInfo::Invalid(zone());
683 }
684 
ComputePropertyAccessInfos(MapHandles const & maps,Handle<Name> name,AccessMode access_mode,ZoneVector<PropertyAccessInfo> * access_infos) const685 void AccessInfoFactory::ComputePropertyAccessInfos(
686     MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
687     ZoneVector<PropertyAccessInfo>* access_infos) const {
688   DCHECK(access_infos->empty());
689   for (Handle<Map> map : maps) {
690     access_infos->push_back(ComputePropertyAccessInfo(map, name, access_mode));
691   }
692 }
693 
RecordDependencies(CompilationDependencies * dependencies)694 void PropertyAccessInfo::RecordDependencies(
695     CompilationDependencies* dependencies) {
696   for (CompilationDependency const* d : unrecorded_dependencies_) {
697     dependencies->RecordDependency(d);
698   }
699   unrecorded_dependencies_.clear();
700 }
701 
FinalizePropertyAccessInfos(ZoneVector<PropertyAccessInfo> access_infos,AccessMode access_mode,ZoneVector<PropertyAccessInfo> * result) const702 bool AccessInfoFactory::FinalizePropertyAccessInfos(
703     ZoneVector<PropertyAccessInfo> access_infos, AccessMode access_mode,
704     ZoneVector<PropertyAccessInfo>* result) const {
705   if (access_infos.empty()) return false;
706   MergePropertyAccessInfos(access_infos, access_mode, result);
707   for (PropertyAccessInfo const& info : *result) {
708     if (info.IsInvalid()) return false;
709   }
710   for (PropertyAccessInfo& info : *result) {
711     info.RecordDependencies(dependencies());
712   }
713   return true;
714 }
715 
MergePropertyAccessInfos(ZoneVector<PropertyAccessInfo> infos,AccessMode access_mode,ZoneVector<PropertyAccessInfo> * result) const716 void AccessInfoFactory::MergePropertyAccessInfos(
717     ZoneVector<PropertyAccessInfo> infos, AccessMode access_mode,
718     ZoneVector<PropertyAccessInfo>* result) const {
719   DCHECK(result->empty());
720   for (auto it = infos.begin(), end = infos.end(); it != end; ++it) {
721     bool merged = false;
722     for (auto ot = it + 1; ot != end; ++ot) {
723       if (ot->Merge(&(*it), access_mode, zone())) {
724         merged = true;
725         break;
726       }
727     }
728     if (!merged) result->push_back(*it);
729   }
730   CHECK(!result->empty());
731 }
732 
isolate() const733 Isolate* AccessInfoFactory::isolate() const { return broker()->isolate(); }
734 
735 namespace {
736 
GeneralizeElementsKind(ElementsKind this_kind,ElementsKind that_kind)737 Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
738                                            ElementsKind that_kind) {
739   if (IsHoleyElementsKind(this_kind)) {
740     that_kind = GetHoleyElementsKind(that_kind);
741   } else if (IsHoleyElementsKind(that_kind)) {
742     this_kind = GetHoleyElementsKind(this_kind);
743   }
744   if (this_kind == that_kind) return Just(this_kind);
745   if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
746     if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
747       return Just(this_kind);
748     }
749     if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
750       return Just(that_kind);
751     }
752   }
753   return Nothing<ElementsKind>();
754 }
755 
756 }  // namespace
757 
ConsolidateElementLoad(ElementAccessFeedback const & feedback) const758 base::Optional<ElementAccessInfo> AccessInfoFactory::ConsolidateElementLoad(
759     ElementAccessFeedback const& feedback) const {
760   if (feedback.transition_groups().empty()) return base::nullopt;
761 
762   DCHECK(!feedback.transition_groups().front().empty());
763   MapRef first_map(broker(), feedback.transition_groups().front().front());
764   InstanceType instance_type = first_map.instance_type();
765   ElementsKind elements_kind = first_map.elements_kind();
766 
767   ZoneVector<Handle<Map>> maps(zone());
768   for (auto const& group : feedback.transition_groups()) {
769     for (Handle<Map> map_handle : group) {
770       MapRef map(broker(), map_handle);
771       if (map.instance_type() != instance_type ||
772           !CanInlineElementAccess(map)) {
773         return base::nullopt;
774       }
775       if (!GeneralizeElementsKind(elements_kind, map.elements_kind())
776                .To(&elements_kind)) {
777         return base::nullopt;
778       }
779       maps.push_back(map.object());
780     }
781   }
782 
783   return ElementAccessInfo(std::move(maps), elements_kind, zone());
784 }
785 
LookupSpecialFieldAccessor(Handle<Map> map,Handle<Name> name) const786 PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor(
787     Handle<Map> map, Handle<Name> name) const {
788   // Check for String::length field accessor.
789   if (map->IsStringMap()) {
790     if (Name::Equals(isolate(), name, isolate()->factory()->length_string())) {
791       return PropertyAccessInfo::StringLength(zone(), map);
792     }
793     return PropertyAccessInfo::Invalid(zone());
794   }
795   // Check for special JSObject field accessors.
796   FieldIndex field_index;
797   if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
798     Type field_type = Type::NonInternal();
799     Representation field_representation = Representation::Tagged();
800     if (map->IsJSArrayMap()) {
801       DCHECK(
802           Name::Equals(isolate(), isolate()->factory()->length_string(), name));
803       // The JSArray::length property is a smi in the range
804       // [0, FixedDoubleArray::kMaxLength] in case of fast double
805       // elements, a smi in the range [0, FixedArray::kMaxLength]
806       // in case of other fast elements, and [0, kMaxUInt32] in
807       // case of other arrays.
808       if (IsDoubleElementsKind(map->elements_kind())) {
809         field_type = type_cache_->kFixedDoubleArrayLengthType;
810         field_representation = Representation::Smi();
811       } else if (IsFastElementsKind(map->elements_kind())) {
812         field_type = type_cache_->kFixedArrayLengthType;
813         field_representation = Representation::Smi();
814       } else {
815         field_type = type_cache_->kJSArrayLengthType;
816       }
817     }
818     // Special fields are always mutable.
819     return PropertyAccessInfo::DataField(zone(), map, {{}, zone()}, field_index,
820                                          field_representation, field_type, map);
821   }
822   return PropertyAccessInfo::Invalid(zone());
823 }
824 
LookupTransition(Handle<Map> map,Handle<Name> name,MaybeHandle<JSObject> holder) const825 PropertyAccessInfo AccessInfoFactory::LookupTransition(
826     Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder) const {
827   // Check if the {map} has a data transition with the given {name}.
828   Map transition =
829       TransitionsAccessor(isolate(), map, broker()->is_concurrent_inlining())
830           .SearchTransition(*name, kData, NONE);
831   if (transition.is_null()) {
832     return PropertyAccessInfo::Invalid(zone());
833   }
834 
835   Handle<Map> transition_map(transition, isolate());
836   InternalIndex const number = transition_map->LastAdded();
837   Handle<DescriptorArray> descriptors(
838       transition_map->instance_descriptors(kAcquireLoad), isolate());
839   PropertyDetails const details = descriptors->GetDetails(number);
840   // Don't bother optimizing stores to read-only properties.
841   if (details.IsReadOnly()) {
842     return PropertyAccessInfo::Invalid(zone());
843   }
844   // TODO(bmeurer): Handle transition to data constant?
845   if (details.location() != kField) {
846     return PropertyAccessInfo::Invalid(zone());
847   }
848   int const index = details.field_index();
849   Representation details_representation = details.representation();
850   FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index,
851                                                         details_representation);
852   Type field_type = Type::NonInternal();
853   MaybeHandle<Map> field_map;
854   MapRef transition_map_ref(broker(), transition_map);
855   ZoneVector<CompilationDependency const*> unrecorded_dependencies(zone());
856   if (details_representation.IsSmi()) {
857     field_type = Type::SignedSmall();
858     transition_map_ref.SerializeOwnDescriptor(number);
859     unrecorded_dependencies.push_back(
860         dependencies()->FieldRepresentationDependencyOffTheRecord(
861             transition_map_ref, number));
862   } else if (details_representation.IsDouble()) {
863     field_type = type_cache_->kFloat64;
864     if (!FLAG_unbox_double_fields) {
865       transition_map_ref.SerializeOwnDescriptor(number);
866       unrecorded_dependencies.push_back(
867           dependencies()->FieldRepresentationDependencyOffTheRecord(
868               transition_map_ref, number));
869     }
870   } else if (details_representation.IsHeapObject()) {
871     // Extract the field type from the property details (make sure its
872     // representation is TaggedPointer to reflect the heap object case).
873     Handle<FieldType> descriptors_field_type(descriptors->GetFieldType(number),
874                                              isolate());
875     if (descriptors_field_type->IsNone()) {
876       // Store is not safe if the field type was cleared.
877       return PropertyAccessInfo::Invalid(zone());
878     }
879     transition_map_ref.SerializeOwnDescriptor(number);
880     unrecorded_dependencies.push_back(
881         dependencies()->FieldRepresentationDependencyOffTheRecord(
882             transition_map_ref, number));
883     if (descriptors_field_type->IsClass()) {
884       unrecorded_dependencies.push_back(
885           dependencies()->FieldTypeDependencyOffTheRecord(transition_map_ref,
886                                                           number));
887       // Remember the field map, and try to infer a useful type.
888       Handle<Map> map(descriptors_field_type->AsClass(), isolate());
889       field_type = Type::For(MapRef(broker(), map));
890       field_map = MaybeHandle<Map>(map);
891     }
892   }
893   unrecorded_dependencies.push_back(
894       dependencies()->TransitionDependencyOffTheRecord(
895           MapRef(broker(), transition_map)));
896   transition_map_ref.SerializeBackPointer();  // For BuildPropertyStore.
897   // Transitioning stores *may* store to const fields. The resulting
898   // DataConstant access infos can be distinguished from later, i.e. redundant,
899   // stores to the same constant field by the presence of a transition map.
900   switch (details.constness()) {
901     case PropertyConstness::kMutable:
902       return PropertyAccessInfo::DataField(
903           zone(), map, std::move(unrecorded_dependencies), field_index,
904           details_representation, field_type, transition_map, field_map, holder,
905           transition_map);
906     case PropertyConstness::kConst:
907       return PropertyAccessInfo::DataConstant(
908           zone(), map, std::move(unrecorded_dependencies), field_index,
909           details_representation, field_type, transition_map, field_map, holder,
910           transition_map);
911   }
912   UNREACHABLE();
913 }
914 
915 }  // namespace compiler
916 }  // namespace internal
917 }  // namespace v8
918