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