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