7 #include "src/compiler/access-info.h" 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" 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) {
40 bool CanInlinePropertyAccess(Handle<Map> map) {
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() &&
50 !map->is_access_check_needed();
56 std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
57 switch (access_mode) {
58 case AccessMode::kLoad:
60 case AccessMode::kStore:
62 case AccessMode::kStoreInLiteral:
63 return os <<
"StoreInLiteral";
68 ElementAccessInfo::ElementAccessInfo() =
default;
70 ElementAccessInfo::ElementAccessInfo(MapHandles
const& receiver_maps,
71 ElementsKind elements_kind)
72 : elements_kind_(elements_kind), receiver_maps_(receiver_maps) {}
75 PropertyAccessInfo PropertyAccessInfo::NotFound(MapHandles
const& receiver_maps,
76 MaybeHandle<JSObject> holder) {
77 return PropertyAccessInfo(kNotFound, holder, receiver_maps);
81 PropertyAccessInfo PropertyAccessInfo::DataConstant(
82 MapHandles
const& receiver_maps, Handle<Object> constant,
83 MaybeHandle<JSObject> holder) {
84 return PropertyAccessInfo(kDataConstant, holder, constant, receiver_maps);
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) {
94 constness == PropertyConstness::kConst ? kDataConstantField : kDataField;
95 return PropertyAccessInfo(kind, holder, transition_map, field_index,
96 field_representation, field_type, field_map,
101 PropertyAccessInfo PropertyAccessInfo::AccessorConstant(
102 MapHandles
const& receiver_maps, Handle<Object> constant,
103 MaybeHandle<JSObject> holder) {
104 return PropertyAccessInfo(kAccessorConstant, holder, constant, receiver_maps);
108 PropertyAccessInfo PropertyAccessInfo::ModuleExport(
109 MapHandles
const& receiver_maps, Handle<Cell> cell) {
110 return PropertyAccessInfo(kModuleExport, MaybeHandle<JSObject>(), cell,
115 PropertyAccessInfo PropertyAccessInfo::StringLength(
116 MapHandles
const& receiver_maps) {
117 return PropertyAccessInfo(kStringLength, MaybeHandle<JSObject>(),
121 PropertyAccessInfo::PropertyAccessInfo()
123 field_representation_(MachineRepresentation::
kNone),
126 PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
127 MapHandles
const& receiver_maps)
129 receiver_maps_(receiver_maps),
131 field_representation_(MachineRepresentation::
kNone),
134 PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
135 Handle<Object> constant,
136 MapHandles
const& receiver_maps)
138 receiver_maps_(receiver_maps),
141 field_representation_(MachineRepresentation::
kNone),
142 field_type_(
Type::Any()) {}
144 PropertyAccessInfo::PropertyAccessInfo(
145 Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
146 FieldIndex field_index, MachineRepresentation field_representation,
147 Type field_type, MaybeHandle<Map> field_map,
148 MapHandles
const& receiver_maps)
150 receiver_maps_(receiver_maps),
151 transition_map_(transition_map),
153 field_index_(field_index),
154 field_representation_(field_representation),
155 field_type_(field_type),
156 field_map_(field_map) {}
158 bool PropertyAccessInfo::Merge(PropertyAccessInfo
const* that,
159 AccessMode access_mode, Zone* zone) {
160 if (this->kind_ != that->kind_)
return false;
161 if (this->holder_.address() != that->holder_.address())
return false;
163 switch (this->kind_) {
168 case kDataConstantField: {
173 if (this->field_index_.GetFieldAccessStubKey() ==
174 that->field_index_.GetFieldAccessStubKey()) {
175 switch (access_mode) {
176 case AccessMode::kLoad: {
177 if (this->field_representation_ != that->field_representation_) {
178 if (!IsAnyTagged(this->field_representation_) ||
179 !IsAnyTagged(that->field_representation_)) {
182 this->field_representation_ = MachineRepresentation::kTagged;
184 if (this->field_map_.address() != that->field_map_.address()) {
185 this->field_map_ = MaybeHandle<Map>();
189 case AccessMode::kStore:
190 case AccessMode::kStoreInLiteral: {
195 if (this->field_map_.address() != that->field_map_.address() ||
196 this->field_representation_ != that->field_representation_ ||
197 this->transition_map_.address() !=
198 that->transition_map_.address()) {
206 Type::Union(this->field_type_, that->field_type_, zone);
208 this->receiver_maps_.insert(this->receiver_maps_.end(),
209 that->receiver_maps_.begin(),
210 that->receiver_maps_.end());
217 case kAccessorConstant: {
219 if (this->constant_.address() == that->constant_.address()) {
220 this->receiver_maps_.insert(this->receiver_maps_.end(),
221 that->receiver_maps_.begin(),
222 that->receiver_maps_.end());
229 case kStringLength: {
230 this->receiver_maps_.insert(this->receiver_maps_.end(),
231 that->receiver_maps_.begin(),
232 that->receiver_maps_.end());
235 case kModuleExport: {
243 Handle<Cell> PropertyAccessInfo::export_cell()
const {
244 DCHECK_EQ(kModuleExport, kind_);
245 return Handle<Cell>::cast(constant_);
248 AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
249 CompilationDependencies* dependencies,
250 Handle<Context> native_context, Zone* zone)
252 dependencies_(dependencies),
253 native_context_(native_context),
254 isolate_(native_context->GetIsolate()),
255 type_cache_(TypeCache::Get()),
257 DCHECK(native_context->IsNativeContext());
261 bool AccessInfoFactory::ComputeElementAccessInfo(
262 Handle<Map> map, AccessMode access_mode, ElementAccessInfo* access_info) {
264 if (!CanInlineElementAccess(map))
return false;
265 ElementsKind
const elements_kind = map->elements_kind();
266 *access_info = ElementAccessInfo(MapHandles{map}, elements_kind);
270 bool AccessInfoFactory::ComputeElementAccessInfos(
271 MapHandles
const& maps, AccessMode access_mode,
272 ZoneVector<ElementAccessInfo>* access_infos) {
273 if (access_mode == AccessMode::kLoad) {
278 ElementAccessInfo access_info;
279 if (ConsolidateElementLoad(maps, &access_info)) {
280 access_infos->push_back(access_info);
286 MapHandles possible_transition_targets;
287 possible_transition_targets.reserve(maps.size());
288 for (Handle<Map> map : maps) {
289 if (Map::TryUpdate(isolate(), map).ToHandle(&map)) {
290 if (CanInlineElementAccess(map) &&
291 IsFastElementsKind(map->elements_kind()) &&
292 GetInitialFastElementsKind() != map->elements_kind()) {
293 possible_transition_targets.push_back(map);
299 MapHandles receiver_maps;
300 receiver_maps.reserve(maps.size());
301 MapTransitionList transitions(maps.size());
302 for (Handle<Map> map : maps) {
303 if (Map::TryUpdate(isolate(), map).ToHandle(&map)) {
305 Map transition_target = map->is_stable()
307 : map->FindElementsKindTransitionedMap(
308 isolate(), possible_transition_targets);
309 if (transition_target.is_null()) {
310 receiver_maps.push_back(map);
312 transitions.push_back(
313 std::make_pair(map, handle(transition_target, isolate())));
318 for (Handle<Map> receiver_map : receiver_maps) {
320 ElementAccessInfo access_info;
321 if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
326 for (
auto transition : transitions) {
327 if (transition.second.is_identical_to(receiver_map)) {
328 access_info.transitions().push_back(transition);
333 access_infos->push_back(access_info);
339 bool AccessInfoFactory::ComputePropertyAccessInfo(
340 Handle<Map> map, Handle<Name> name, AccessMode access_mode,
341 PropertyAccessInfo* access_info) {
343 if (!CanInlinePropertyAccess(map))
return false;
346 Handle<Map> receiver_map = map;
349 name = isolate()->factory()->InternalizeName(name);
352 if (access_mode == AccessMode::kLoad &&
353 LookupSpecialFieldAccessor(map, name, access_info)) {
357 MaybeHandle<JSObject> holder;
360 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
361 int const number = descriptors->Search(*name, *map);
362 if (number != DescriptorArray::kNotFound) {
363 PropertyDetails
const details = descriptors->GetDetails(number);
364 if (access_mode == AccessMode::kStore ||
365 access_mode == AccessMode::kStoreInLiteral) {
367 if (details.IsReadOnly()) {
371 if (details.kind() == kData && !holder.is_null()) {
375 return LookupTransition(receiver_map, name, holder, access_info);
378 if (details.location() == kField) {
379 if (details.kind() == kData) {
380 int index = descriptors->GetFieldIndex(number);
381 Representation details_representation = details.representation();
382 FieldIndex field_index =
383 FieldIndex::ForPropertyIndex(*map, index, details_representation);
384 Type field_type = Type::NonInternal();
385 MachineRepresentation field_representation =
386 MachineRepresentation::kTagged;
387 MaybeHandle<Map> field_map;
388 if (details_representation.IsSmi()) {
389 field_type = Type::SignedSmall();
390 field_representation = MachineRepresentation::kTaggedSigned;
391 }
else if (details_representation.IsDouble()) {
392 field_type = type_cache_.kFloat64;
393 field_representation = MachineRepresentation::kFloat64;
394 }
else if (details_representation.IsHeapObject()) {
397 field_representation = MachineRepresentation::kTaggedPointer;
398 Handle<FieldType> descriptors_field_type(
399 descriptors->GetFieldType(number), isolate());
400 if (descriptors_field_type->IsNone()) {
402 if (access_mode == AccessMode::kStore)
return false;
406 }
else if (descriptors_field_type->IsClass()) {
407 MapRef map_ref(broker(), map);
408 map_ref.SerializeOwnDescriptors();
409 dependencies()->DependOnFieldType(map_ref, number);
411 Handle<Map> map(descriptors_field_type->AsClass(), isolate());
412 field_type = Type::For(MapRef(broker(), map));
413 field_map = MaybeHandle<Map>(map);
416 *access_info = PropertyAccessInfo::DataField(
417 details.constness(), MapHandles{receiver_map}, field_index,
418 field_representation, field_type, field_map, holder);
421 DCHECK_EQ(kAccessor, details.kind());
427 DCHECK_EQ(kDescriptor, details.location());
428 if (details.kind() == kData) {
429 DCHECK(!FLAG_track_constant_fields);
430 *access_info = PropertyAccessInfo::DataConstant(
431 MapHandles{receiver_map},
432 handle(descriptors->GetStrongValue(number), isolate()), holder);
435 DCHECK_EQ(kAccessor, details.kind());
436 if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
437 DCHECK(map->is_prototype_map());
438 Handle<PrototypeInfo> proto_info =
439 Map::GetOrCreatePrototypeInfo(map, isolate());
440 Handle<JSModuleNamespace> module_namespace(
441 JSModuleNamespace::cast(proto_info->module_namespace()),
444 Cell::cast(module_namespace->module()->exports()->Lookup(
445 ReadOnlyRoots(isolate()), name,
446 Smi::ToInt(name->GetHash()))),
448 if (cell->value()->IsTheHole(isolate())) {
452 *access_info = PropertyAccessInfo::ModuleExport(
453 MapHandles{receiver_map}, cell);
456 Handle<Object> accessors(descriptors->GetStrongValue(number),
458 if (!accessors->IsAccessorPair())
return false;
459 Handle<Object> accessor(
460 access_mode == AccessMode::kLoad
461 ? Handle<AccessorPair>::cast(accessors)->getter()
462 : Handle<AccessorPair>::cast(accessors)->setter(),
464 if (!accessor->IsJSFunction()) {
465 CallOptimization optimization(isolate(), accessor);
466 if (!optimization.is_simple_api_call())
return false;
467 if (optimization.IsCrossContextLazyAccessorPair(*native_context_,
472 CallOptimization::HolderLookup lookup;
474 optimization.LookupHolderOfExpectedType(receiver_map, &lookup);
475 if (lookup == CallOptimization::kHolderNotFound)
return false;
476 DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
478 DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound,
480 if (V8_UNLIKELY(FLAG_runtime_stats))
return false;
482 if (access_mode == AccessMode::kLoad) {
483 Handle<Name> cached_property_name;
484 if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(),
486 .ToHandle(&cached_property_name)) {
487 if (ComputePropertyAccessInfo(map, cached_property_name,
488 access_mode, access_info)) {
493 *access_info = PropertyAccessInfo::AccessorConstant(
494 MapHandles{receiver_map}, accessor, holder);
503 if (map->IsJSTypedArrayMap() && name->IsString() &&
504 IsSpecialIndex(String::cast(*name))) {
509 if (access_mode == AccessMode::kStoreInLiteral) {
510 return LookupTransition(receiver_map, name, holder, access_info);
514 if (name->IsPrivate())
return false;
517 if (!map->prototype()->IsJSObject()) {
520 Handle<JSFunction> constructor;
521 if (Map::GetConstructorFunction(map, native_context())
522 .ToHandle(&constructor)) {
523 map = handle(constructor->initial_map(), isolate());
524 DCHECK(map->prototype()->IsJSObject());
525 }
else if (map->prototype()->IsNull(isolate())) {
529 if (access_mode == AccessMode::kStore) {
530 return LookupTransition(receiver_map, name, holder, access_info);
536 PropertyAccessInfo::NotFound(MapHandles{receiver_map}, holder);
542 Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
543 if (map_prototype->map()->is_deprecated()) {
546 JSObject::TryMigrateInstance(map_prototype);
548 map = handle(map_prototype->map(), isolate());
549 holder = map_prototype;
550 }
while (CanInlinePropertyAccess(map));
554 bool AccessInfoFactory::ComputePropertyAccessInfo(
555 MapHandles
const& maps, Handle<Name> name, AccessMode access_mode,
556 PropertyAccessInfo* access_info) {
557 ZoneVector<PropertyAccessInfo> access_infos(zone());
558 if (ComputePropertyAccessInfos(maps, name, access_mode, &access_infos) &&
559 access_infos.size() == 1) {
560 *access_info = access_infos.front();
566 bool AccessInfoFactory::ComputePropertyAccessInfos(
567 MapHandles
const& maps, Handle<Name> name, AccessMode access_mode,
568 ZoneVector<PropertyAccessInfo>* access_infos) {
569 for (Handle<Map> map : maps) {
570 if (Map::TryUpdate(isolate(), map).ToHandle(&map)) {
571 PropertyAccessInfo access_info;
572 if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) {
577 for (PropertyAccessInfo& other_info : *access_infos) {
578 if (other_info.Merge(&access_info, access_mode, zone())) {
583 if (!merged) access_infos->push_back(access_info);
591 Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
592 ElementsKind that_kind) {
593 if (IsHoleyElementsKind(this_kind)) {
594 that_kind = GetHoleyElementsKind(that_kind);
595 }
else if (IsHoleyElementsKind(that_kind)) {
596 this_kind = GetHoleyElementsKind(this_kind);
598 if (this_kind == that_kind)
return Just(this_kind);
599 if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
600 if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
601 return Just(this_kind);
603 if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
604 return Just(that_kind);
607 return Nothing<ElementsKind>();
612 bool AccessInfoFactory::ConsolidateElementLoad(MapHandles
const& maps,
613 ElementAccessInfo* access_info) {
614 if (maps.empty())
return false;
615 InstanceType instance_type = maps.front()->instance_type();
616 ElementsKind elements_kind = maps.front()->elements_kind();
617 for (Handle<Map> map : maps) {
618 if (!CanInlineElementAccess(map) || map->instance_type() != instance_type) {
621 if (!GeneralizeElementsKind(elements_kind, map->elements_kind())
622 .To(&elements_kind)) {
626 *access_info = ElementAccessInfo(maps, elements_kind);
630 bool AccessInfoFactory::LookupSpecialFieldAccessor(
631 Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) {
633 if (map->IsStringMap()) {
634 if (Name::Equals(isolate(), name, factory()->length_string())) {
635 *access_info = PropertyAccessInfo::StringLength(MapHandles{map});
641 FieldIndex field_index;
642 if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
643 Type field_type = Type::NonInternal();
644 MachineRepresentation field_representation = MachineRepresentation::kTagged;
645 if (map->IsJSArrayMap()) {
646 DCHECK(Name::Equals(isolate(), factory()->length_string(), name));
652 if (IsDoubleElementsKind(map->elements_kind())) {
653 field_type = type_cache_.kFixedDoubleArrayLengthType;
654 field_representation = MachineRepresentation::kTaggedSigned;
655 }
else if (IsFastElementsKind(map->elements_kind())) {
656 field_type = type_cache_.kFixedArrayLengthType;
657 field_representation = MachineRepresentation::kTaggedSigned;
659 field_type = type_cache_.kJSArrayLengthType;
663 *access_info = PropertyAccessInfo::DataField(
664 PropertyConstness::kMutable, MapHandles{map}, field_index,
665 field_representation, field_type);
672 bool AccessInfoFactory::LookupTransition(Handle<Map> map, Handle<Name> name,
673 MaybeHandle<JSObject> holder,
674 PropertyAccessInfo* access_info) {
677 TransitionsAccessor(isolate(), map).SearchTransition(*name, kData, NONE);
678 if (transition.is_null())
return false;
680 Handle<Map> transition_map(transition, isolate());
681 int const number = transition_map->LastAdded();
682 PropertyDetails
const details =
683 transition_map->instance_descriptors()->GetDetails(number);
685 if (details.IsReadOnly())
return false;
687 if (details.location() != kField)
return false;
688 int const index = details.field_index();
689 Representation details_representation = details.representation();
690 FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index,
691 details_representation);
692 Type field_type = Type::NonInternal();
693 MaybeHandle<Map> field_map;
694 MachineRepresentation field_representation = MachineRepresentation::kTagged;
695 if (details_representation.IsSmi()) {
696 field_type = Type::SignedSmall();
697 field_representation = MachineRepresentation::kTaggedSigned;
698 }
else if (details_representation.IsDouble()) {
699 field_type = type_cache_.kFloat64;
700 field_representation = MachineRepresentation::kFloat64;
701 }
else if (details_representation.IsHeapObject()) {
704 field_representation = MachineRepresentation::kTaggedPointer;
705 Handle<FieldType> descriptors_field_type(
706 transition_map->instance_descriptors()->GetFieldType(number),
708 if (descriptors_field_type->IsNone()) {
711 }
else if (descriptors_field_type->IsClass()) {
712 MapRef transition_map_ref(broker(), transition_map);
714 .SerializeOwnDescriptors();
715 dependencies()->DependOnFieldType(transition_map_ref, number);
717 Handle<Map> map(descriptors_field_type->AsClass(), isolate());
718 field_type = Type::For(MapRef(broker(), map));
719 field_map = MaybeHandle<Map>(map);
722 dependencies()->DependOnTransition(MapRef(broker(), transition_map));
724 *access_info = PropertyAccessInfo::DataField(
725 PropertyConstness::kMutable, MapHandles{map}, field_index,
726 field_representation, field_type, field_map, holder, transition_map);
731 Factory* AccessInfoFactory::factory()
const {
return isolate()->factory(); }