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