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