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/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/counters.h"
13 : #include "src/field-index-inl.h"
14 : #include "src/field-type.h"
15 : #include "src/ic/call-optimization.h"
16 : #include "src/objects-inl.h"
17 : #include "src/objects/cell-inl.h"
18 : #include "src/objects/module-inl.h"
19 : #include "src/objects/struct-inl.h"
20 : #include "src/objects/templates.h"
21 :
22 : namespace v8 {
23 : namespace internal {
24 : namespace compiler {
25 :
26 : namespace {
27 :
28 256532 : bool CanInlinePropertyAccess(Handle<Map> map) {
29 : // We can inline property access to prototypes of all primitives, except
30 : // the special Oddball ones that have no wrapper counterparts (i.e. Null,
31 : // Undefined and TheHole).
32 : STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_TYPE);
33 256532 : if (map->IsBooleanMap()) return true;
34 256005 : if (map->instance_type() < LAST_PRIMITIVE_TYPE) return true;
35 480738 : return map->IsJSObjectMap() && !map->is_dictionary_map() &&
36 481912 : !map->has_named_interceptor() &&
37 : // TODO(verwaest): Whitelist contexts to which we have access.
38 : !map->is_access_check_needed();
39 : }
40 :
41 : } // namespace
42 :
43 :
44 0 : std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
45 0 : switch (access_mode) {
46 : case AccessMode::kLoad:
47 0 : return os << "Load";
48 : case AccessMode::kStore:
49 0 : return os << "Store";
50 : case AccessMode::kStoreInLiteral:
51 0 : return os << "StoreInLiteral";
52 : case AccessMode::kHas:
53 0 : return os << "Has";
54 : }
55 0 : UNREACHABLE();
56 : }
57 :
58 : ElementAccessInfo::ElementAccessInfo() = default;
59 :
60 23163 : ElementAccessInfo::ElementAccessInfo(MapHandles const& receiver_maps,
61 : ElementsKind elements_kind)
62 23163 : : elements_kind_(elements_kind), receiver_maps_(receiver_maps) {
63 23163 : CHECK(!receiver_maps.empty());
64 23163 : }
65 :
66 : // static
67 0 : PropertyAccessInfo PropertyAccessInfo::NotFound(MapHandles const& receiver_maps,
68 : MaybeHandle<JSObject> holder) {
69 0 : return PropertyAccessInfo(kNotFound, holder, receiver_maps);
70 : }
71 :
72 : // static
73 0 : PropertyAccessInfo PropertyAccessInfo::DataConstant(
74 : MapHandles const& receiver_maps, Handle<Object> constant,
75 : MaybeHandle<JSObject> holder) {
76 0 : return PropertyAccessInfo(kDataConstant, holder, constant, receiver_maps);
77 : }
78 :
79 : // static
80 0 : PropertyAccessInfo PropertyAccessInfo::DataField(
81 : PropertyConstness constness, MapHandles const& receiver_maps,
82 : FieldIndex field_index, MachineRepresentation field_representation,
83 : Type field_type, MaybeHandle<Map> field_map, MaybeHandle<JSObject> holder,
84 : MaybeHandle<Map> transition_map) {
85 : Kind kind =
86 101596 : constness == PropertyConstness::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 : // static
107 0 : PropertyAccessInfo PropertyAccessInfo::StringLength(
108 : MapHandles const& receiver_maps) {
109 : return PropertyAccessInfo(kStringLength, MaybeHandle<JSObject>(),
110 0 : receiver_maps);
111 : }
112 :
113 0 : PropertyAccessInfo::PropertyAccessInfo()
114 : : kind_(kInvalid),
115 : field_representation_(MachineRepresentation::kNone),
116 247096 : field_type_(Type::None()) {}
117 :
118 0 : PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
119 : MapHandles const& receiver_maps)
120 : : kind_(kind),
121 : receiver_maps_(receiver_maps),
122 : holder_(holder),
123 : field_representation_(MachineRepresentation::kNone),
124 15852 : field_type_(Type::None()) {}
125 :
126 0 : PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
127 : Handle<Object> constant,
128 : MapHandles const& receiver_maps)
129 : : kind_(kind),
130 : receiver_maps_(receiver_maps),
131 : constant_(constant),
132 : holder_(holder),
133 : field_representation_(MachineRepresentation::kNone),
134 10506 : field_type_(Type::Any()) {}
135 :
136 0 : PropertyAccessInfo::PropertyAccessInfo(
137 : Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
138 : FieldIndex field_index, MachineRepresentation field_representation,
139 : Type field_type, MaybeHandle<Map> field_map,
140 : MapHandles const& receiver_maps)
141 : : kind_(kind),
142 : receiver_maps_(receiver_maps),
143 : transition_map_(transition_map),
144 : holder_(holder),
145 : field_index_(field_index),
146 : field_representation_(field_representation),
147 : field_type_(field_type),
148 262626 : field_map_(field_map) {}
149 :
150 17348 : bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
151 : AccessMode access_mode, Zone* zone) {
152 17348 : if (this->kind_ != that->kind_) return false;
153 16581 : if (this->holder_.address() != that->holder_.address()) return false;
154 :
155 15657 : switch (this->kind_) {
156 : case kInvalid:
157 4038 : return that->kind_ == kInvalid;
158 :
159 : case kDataField:
160 : case kDataConstantField: {
161 : // Check if we actually access the same field (we use the
162 : // GetFieldAccessStubKey method here just like the ICs do
163 : // since that way we only compare the relevant bits of the
164 : // field indices).
165 11288 : if (this->field_index_.GetFieldAccessStubKey() ==
166 : that->field_index_.GetFieldAccessStubKey()) {
167 10701 : switch (access_mode) {
168 : case AccessMode::kHas:
169 : case AccessMode::kLoad: {
170 6272 : if (this->field_representation_ != that->field_representation_) {
171 748 : if (!IsAnyTagged(this->field_representation_) ||
172 : !IsAnyTagged(that->field_representation_)) {
173 : return false;
174 : }
175 374 : this->field_representation_ = MachineRepresentation::kTagged;
176 : }
177 6272 : if (this->field_map_.address() != that->field_map_.address()) {
178 211 : 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 8810 : if (this->field_map_.address() != that->field_map_.address() ||
189 8702 : 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 6370 : 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 6370 : that->receiver_maps_.end());
204 6370 : return true;
205 : }
206 : return false;
207 : }
208 :
209 : case kDataConstant:
210 : case kAccessorConstant: {
211 : // Check if we actually access the same constant.
212 254 : if (this->constant_.address() == that->constant_.address()) {
213 : this->receiver_maps_.insert(this->receiver_maps_.end(),
214 : that->receiver_maps_.begin(),
215 247 : that->receiver_maps_.end());
216 247 : return true;
217 : }
218 : return false;
219 : }
220 :
221 : case kNotFound:
222 : case kStringLength: {
223 : this->receiver_maps_.insert(this->receiver_maps_.end(),
224 : that->receiver_maps_.begin(),
225 77 : that->receiver_maps_.end());
226 77 : return true;
227 : }
228 : case kModuleExport:
229 : return false;
230 : }
231 0 : }
232 :
233 7 : Handle<Cell> PropertyAccessInfo::export_cell() const {
234 : DCHECK_EQ(kModuleExport, kind_);
235 7 : return Handle<Cell>::cast(constant_);
236 : }
237 :
238 165941 : AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
239 : CompilationDependencies* dependencies,
240 : Zone* zone)
241 : : broker_(broker),
242 : dependencies_(dependencies),
243 165941 : type_cache_(TypeCache::Get()),
244 331882 : zone_(zone) {}
245 :
246 11039 : bool AccessInfoFactory::ComputeElementAccessInfo(
247 : Handle<Map> map, AccessMode access_mode,
248 : ElementAccessInfo* access_info) const {
249 : // Check if it is safe to inline element access for the {map}.
250 : MapRef map_ref(broker(), map);
251 11039 : if (!CanInlineElementAccess(map_ref)) return false;
252 9707 : ElementsKind const elements_kind = map_ref.elements_kind();
253 38828 : *access_info = ElementAccessInfo(MapHandles{map}, elements_kind);
254 9707 : return true;
255 : }
256 :
257 23399 : bool AccessInfoFactory::ComputeElementAccessInfos(
258 : FeedbackNexus nexus, MapHandles const& maps, AccessMode access_mode,
259 : ZoneVector<ElementAccessInfo>* access_infos) const {
260 : ElementAccessFeedback const* processed =
261 : FLAG_concurrent_inlining
262 23415 : ? broker()->GetElementAccessFeedback(FeedbackSource(nexus))
263 46798 : : broker()->ProcessFeedbackMapsForElementAccess(maps);
264 23399 : if (processed == nullptr) return false;
265 :
266 23391 : if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
267 : // For polymorphic loads of similar elements kinds (i.e. all tagged or all
268 : // double), always use the "worst case" code without a transition. This is
269 : // much faster than transitioning the elements to the worst case, trading a
270 : // TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
271 1458 : ElementAccessInfo access_info;
272 14914 : if (ConsolidateElementLoad(*processed, &access_info)) {
273 13456 : access_infos->push_back(access_info);
274 13456 : return true;
275 : }
276 : }
277 :
278 19642 : for (Handle<Map> receiver_map : processed->receiver_maps) {
279 : // Compute the element access information.
280 9707 : ElementAccessInfo access_info;
281 11039 : if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
282 1332 : return false;
283 : }
284 :
285 : // Collect the possible transitions for the {receiver_map}.
286 10343 : for (auto transition : processed->transitions) {
287 636 : if (transition.second.equals(receiver_map)) {
288 560 : access_info.AddTransitionSource(transition.first);
289 : }
290 : }
291 :
292 : // Schedule the access information.
293 9707 : access_infos->push_back(access_info);
294 : }
295 8603 : return true;
296 : }
297 :
298 101596 : PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
299 : Handle<Map> receiver_map, Handle<Map> map, MaybeHandle<JSObject> holder,
300 : int number, AccessMode access_mode) const {
301 : DCHECK_NE(number, DescriptorArray::kNotFound);
302 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
303 101596 : PropertyDetails const details = descriptors->GetDetails(number);
304 203192 : int index = descriptors->GetFieldIndex(number);
305 : Representation details_representation = details.representation();
306 101596 : if (details_representation.IsNone()) {
307 : // The ICs collect feedback in PREMONOMORPHIC state already,
308 : // but at this point the {receiver_map} might still contain
309 : // fields for which the representation has not yet been
310 : // determined by the runtime. So we need to catch this case
311 : // here and fall back to use the regular IC logic instead.
312 : return {};
313 : }
314 : FieldIndex field_index =
315 101596 : FieldIndex::ForPropertyIndex(*map, index, details_representation);
316 : Type field_type = Type::NonInternal();
317 : #ifdef V8_COMPRESS_POINTERS
318 : MachineRepresentation field_representation =
319 : MachineRepresentation::kCompressed;
320 : #else
321 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
322 : #endif
323 : MaybeHandle<Map> field_map;
324 : MapRef map_ref(broker(), map);
325 101596 : if (details_representation.IsSmi()) {
326 : field_type = Type::SignedSmall();
327 : #ifdef V8_COMPRESS_POINTERS
328 : field_representation = MachineRepresentation::kCompressedSigned;
329 : #else
330 : field_representation = MachineRepresentation::kTaggedSigned;
331 : #endif
332 7824 : map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
333 7824 : dependencies()->DependOnFieldRepresentation(map_ref, number);
334 93772 : } else if (details_representation.IsDouble()) {
335 909 : field_type = type_cache_->kFloat64;
336 : field_representation = MachineRepresentation::kFloat64;
337 92863 : } else if (details_representation.IsHeapObject()) {
338 : // Extract the field type from the property details (make sure its
339 : // representation is TaggedPointer to reflect the heap object case).
340 : #ifdef V8_COMPRESS_POINTERS
341 : field_representation = MachineRepresentation::kCompressedPointer;
342 : #else
343 : field_representation = MachineRepresentation::kTaggedPointer;
344 : #endif
345 : Handle<FieldType> descriptors_field_type(descriptors->GetFieldType(number),
346 167382 : isolate());
347 83691 : if (descriptors_field_type->IsNone()) {
348 : // Store is not safe if the field type was cleared.
349 55 : if (access_mode == AccessMode::kStore) return {};
350 :
351 : // The field type was cleared by the GC, so we don't know anything
352 : // about the contents now.
353 : }
354 83691 : map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
355 83691 : dependencies()->DependOnFieldRepresentation(map_ref, number);
356 83691 : if (descriptors_field_type->IsClass()) {
357 3231 : dependencies()->DependOnFieldType(map_ref, number);
358 : // Remember the field map, and try to infer a useful type.
359 6462 : Handle<Map> map(descriptors_field_type->AsClass(), isolate());
360 3231 : field_type = Type::For(MapRef(broker(), map));
361 : field_map = MaybeHandle<Map>(map);
362 : }
363 : }
364 : return PropertyAccessInfo::DataField(
365 : details.constness(), MapHandles{receiver_map}, field_index,
366 203192 : field_representation, field_type, field_map, holder);
367 : }
368 :
369 8109 : PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
370 : Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map,
371 : MaybeHandle<JSObject> holder, int number, AccessMode access_mode) const {
372 : DCHECK_NE(number, DescriptorArray::kNotFound);
373 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
374 : SLOW_DCHECK(number == descriptors->Search(*name, *map));
375 8109 : if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
376 : DCHECK(map->is_prototype_map());
377 : Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(map->prototype_info()),
378 : isolate());
379 : Handle<JSModuleNamespace> module_namespace(
380 : JSModuleNamespace::cast(proto_info->module_namespace()), isolate());
381 : Handle<Cell> cell(
382 44 : Cell::cast(module_namespace->module()->exports()->Lookup(
383 44 : ReadOnlyRoots(isolate()), name, Smi::ToInt(name->GetHash()))),
384 66 : isolate());
385 22 : if (cell->value()->IsTheHole(isolate())) {
386 : // This module has not been fully initialized yet.
387 : return {};
388 : }
389 44 : return PropertyAccessInfo::ModuleExport(MapHandles{receiver_map}, cell);
390 : }
391 8087 : if (access_mode == AccessMode::kHas) {
392 : // HasProperty checks don't call getter/setters, existence is sufficient.
393 : return PropertyAccessInfo::AccessorConstant(MapHandles{receiver_map},
394 224 : Handle<Object>(), holder);
395 : }
396 : Handle<Object> accessors(descriptors->GetStrongValue(number), isolate());
397 7975 : if (!accessors->IsAccessorPair()) return {};
398 : Handle<Object> accessor(access_mode == AccessMode::kLoad
399 : ? Handle<AccessorPair>::cast(accessors)->getter()
400 : : Handle<AccessorPair>::cast(accessors)->setter(),
401 6618 : isolate());
402 6618 : if (!accessor->IsJSFunction()) {
403 3378 : CallOptimization optimization(isolate(), accessor);
404 10134 : if (!optimization.is_simple_api_call() ||
405 172 : optimization.IsCrossContextLazyAccessorPair(
406 3722 : *broker()->native_context().object(), *map)) {
407 3244 : return {};
408 : }
409 :
410 : CallOptimization::HolderLookup lookup;
411 166 : holder = optimization.LookupHolderOfExpectedType(receiver_map, &lookup);
412 166 : if (lookup == CallOptimization::kHolderNotFound) return {};
413 : DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
414 : holder.is_null());
415 : DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound, !holder.is_null());
416 134 : if (V8_UNLIKELY(TracingFlags::is_runtime_stats_enabled())) return {};
417 : }
418 3374 : if (access_mode == AccessMode::kLoad) {
419 : Handle<Name> cached_property_name;
420 4986 : if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), accessor)
421 : .ToHandle(&cached_property_name)) {
422 : PropertyAccessInfo access_info =
423 6 : ComputePropertyAccessInfo(map, cached_property_name, access_mode);
424 6 : if (!access_info.IsInvalid()) return access_info;
425 : }
426 : }
427 : return PropertyAccessInfo::AccessorConstant(MapHandles{receiver_map},
428 6736 : accessor, holder);
429 : }
430 :
431 159292 : PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
432 : Handle<Map> map, Handle<Name> name, AccessMode access_mode) const {
433 159292 : CHECK(name->IsUniqueName());
434 :
435 159612 : if (access_mode == AccessMode::kHas && !map->IsJSReceiverMap()) return {};
436 :
437 : // Check if it is safe to inline property access for the {map}.
438 159285 : if (!CanInlinePropertyAccess(map)) return {};
439 :
440 : // We support fast inline cases for certain JSObject getters.
441 149835 : if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
442 113152 : PropertyAccessInfo access_info = LookupSpecialFieldAccessor(map, name);
443 113152 : if (!access_info.IsInvalid()) return access_info;
444 : }
445 :
446 : // Remember the receiver map. We use {map} as loop variable.
447 : Handle<Map> receiver_map = map;
448 : MaybeHandle<JSObject> holder;
449 : while (true) {
450 : // Lookup the named property on the {map}.
451 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
452 471502 : int const number = descriptors->Search(*name, *map);
453 235751 : if (number != DescriptorArray::kNotFound) {
454 115149 : PropertyDetails const details = descriptors->GetDetails(number);
455 115149 : if (access_mode == AccessMode::kStore ||
456 : access_mode == AccessMode::kStoreInLiteral) {
457 : // Don't bother optimizing stores to read-only properties.
458 15468 : if (details.IsReadOnly()) return {};
459 12228 : if (details.kind() == kData && !holder.is_null()) {
460 : // This is a store to a property not found on the receiver but on a
461 : // prototype. According to ES6 section 9.1.9 [[Set]], we need to
462 : // create a new data property on the receiver. We can still optimize
463 : // if such a transition already exists.
464 2204 : return LookupTransition(receiver_map, name, holder);
465 : }
466 : }
467 109705 : if (details.location() == kField) {
468 101596 : if (details.kind() == kData) {
469 : return ComputeDataFieldAccessInfo(receiver_map, map, holder, number,
470 101596 : access_mode);
471 : } else {
472 : DCHECK_EQ(kAccessor, details.kind());
473 : // TODO(turbofan): Add support for general accessors?
474 : return {};
475 : }
476 : } else {
477 : DCHECK_EQ(kDescriptor, details.location());
478 8109 : if (details.kind() == kData) {
479 : DCHECK(!FLAG_track_constant_fields);
480 : return PropertyAccessInfo::DataConstant(
481 : MapHandles{receiver_map},
482 0 : handle(descriptors->GetStrongValue(number), isolate()), holder);
483 : } else {
484 : DCHECK_EQ(kAccessor, details.kind());
485 : return ComputeAccessorDescriptorAccessInfo(
486 8109 : receiver_map, name, map, holder, number, access_mode);
487 : }
488 : }
489 : UNREACHABLE();
490 : }
491 :
492 : // The property wasn't found on {map}. Look on the prototype if appropriate.
493 :
494 : // Don't search on the prototype chain for special indices in case of
495 : // integer indexed exotic objects (see ES6 section 9.4.5).
496 123373 : if (map->IsJSTypedArrayMap() && name->IsString() &&
497 1365 : IsSpecialIndex(String::cast(*name))) {
498 : return {};
499 : }
500 :
501 : // Don't search on the prototype when storing in literals.
502 120595 : if (access_mode == AccessMode::kStoreInLiteral) {
503 110 : return LookupTransition(receiver_map, name, holder);
504 : }
505 :
506 : // Don't lookup private symbols on the prototype chain.
507 120485 : if (name->IsPrivate()) return {};
508 :
509 : // Walk up the prototype chain.
510 120484 : if (!map->prototype()->IsJSObject()) {
511 : // Perform the implicit ToObject for primitives here.
512 : // Implemented according to ES6 section 7.3.2 GetV (V, P).
513 : Handle<JSFunction> constructor;
514 94125 : if (Map::GetConstructorFunction(map, broker()->native_context().object())
515 : .ToHandle(&constructor)) {
516 : map = handle(constructor->initial_map(), isolate());
517 : DCHECK(map->prototype()->IsJSObject());
518 23237 : } else if (map->prototype()->IsNull(isolate())) {
519 : // Store to property not found on the receiver or any prototype, we need
520 : // to transition to a new data property.
521 : // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
522 23230 : if (access_mode == AccessMode::kStore) {
523 19742 : return LookupTransition(receiver_map, name, holder);
524 : }
525 : // The property was not found (access returns undefined or throws
526 : // depending on the language mode of the load operation.
527 : // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
528 6976 : return PropertyAccessInfo::NotFound(MapHandles{receiver_map}, holder);
529 : } else {
530 : return {};
531 : }
532 : }
533 : Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
534 97247 : 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 0 : JSObject::TryMigrateInstance(map_prototype);
538 : }
539 : map = handle(map_prototype->map(), isolate());
540 : holder = map_prototype;
541 :
542 97247 : if (!CanInlinePropertyAccess(map)) return {};
543 :
544 : // Successful lookup on prototype chain needs to guarantee that all
545 : // the prototypes up to the holder have stable maps. Let us make sure
546 : // the prototype maps are stable here.
547 95403 : CHECK(map->is_stable());
548 : }
549 : }
550 :
551 1217 : PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
552 : MapHandles const& maps, Handle<Name> name, AccessMode access_mode) const {
553 : ZoneVector<PropertyAccessInfo> raw_access_infos(zone());
554 1217 : ComputePropertyAccessInfos(maps, name, access_mode, &raw_access_infos);
555 : ZoneVector<PropertyAccessInfo> access_infos(zone());
556 2434 : if (FinalizePropertyAccessInfos(raw_access_infos, access_mode,
557 1738 : &access_infos) &&
558 : access_infos.size() == 1) {
559 521 : return access_infos.front();
560 : }
561 : return {};
562 : }
563 :
564 141413 : void AccessInfoFactory::ComputePropertyAccessInfos(
565 : MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
566 : ZoneVector<PropertyAccessInfo>* access_infos) const {
567 : DCHECK(access_infos->empty());
568 299570 : for (Handle<Map> map : maps) {
569 316314 : access_infos->push_back(ComputePropertyAccessInfo(map, name, access_mode));
570 : }
571 141413 : }
572 :
573 141413 : bool AccessInfoFactory::FinalizePropertyAccessInfos(
574 : ZoneVector<PropertyAccessInfo> infos, AccessMode access_mode,
575 : ZoneVector<PropertyAccessInfo>* result) const {
576 : DCHECK(result->empty());
577 280383 : for (auto it = infos.begin(), end = infos.end(); it != end; ++it) {
578 : bool merged = false;
579 160689 : for (auto ot = it + 1; ot != end; ++ot) {
580 17348 : if (ot->Merge(&(*it), access_mode, zone())) {
581 : merged = true;
582 : break;
583 : }
584 : }
585 154073 : if (it->IsInvalid()) return false;
586 138970 : if (!merged) result->push_back(*it);
587 : }
588 126310 : CHECK(!result->empty());
589 : return true;
590 : }
591 :
592 : namespace {
593 :
594 15307 : Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
595 : ElementsKind that_kind) {
596 15307 : if (IsHoleyElementsKind(this_kind)) {
597 : that_kind = GetHoleyElementsKind(that_kind);
598 11800 : } else if (IsHoleyElementsKind(that_kind)) {
599 : this_kind = GetHoleyElementsKind(this_kind);
600 : }
601 15307 : if (this_kind == that_kind) return Just(this_kind);
602 756 : if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
603 696 : if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
604 : return Just(this_kind);
605 : }
606 96 : if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
607 : return Just(that_kind);
608 : }
609 : }
610 : return Nothing<ElementsKind>();
611 : }
612 :
613 : } // namespace
614 :
615 14914 : bool AccessInfoFactory::ConsolidateElementLoad(
616 : ElementAccessFeedback const& processed,
617 : ElementAccessInfo* access_info) const {
618 14914 : ElementAccessFeedback::MapIterator it = processed.all_maps(broker());
619 14914 : MapRef first_map = it.current();
620 14914 : InstanceType instance_type = first_map.instance_type();
621 14914 : ElementsKind elements_kind = first_map.elements_kind();
622 : MapHandles maps;
623 45216 : for (; !it.done(); it.advance()) {
624 16609 : MapRef map = it.current();
625 16609 : if (map.instance_type() != instance_type || !CanInlineElementAccess(map)) {
626 1458 : return false;
627 : }
628 30614 : if (!GeneralizeElementsKind(elements_kind, map.elements_kind())
629 : .To(&elements_kind)) {
630 : return false;
631 : }
632 30302 : maps.push_back(map.object());
633 : }
634 :
635 26912 : *access_info = ElementAccessInfo(maps, elements_kind);
636 13456 : return true;
637 : }
638 :
639 113152 : PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor(
640 : Handle<Map> map, Handle<Name> name) const {
641 : // Check for String::length field accessor.
642 113152 : if (map->IsStringMap()) {
643 7940 : if (Name::Equals(isolate(), name, isolate()->factory()->length_string())) {
644 3592 : return PropertyAccessInfo::StringLength(MapHandles{map});
645 : }
646 : return {};
647 : }
648 : // Check for special JSObject field accessors.
649 : FieldIndex field_index;
650 105212 : if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
651 : Type field_type = Type::NonInternal();
652 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
653 7691 : if (map->IsJSArrayMap()) {
654 : DCHECK(
655 : Name::Equals(isolate(), isolate()->factory()->length_string(), name));
656 : // The JSArray::length property is a smi in the range
657 : // [0, FixedDoubleArray::kMaxLength] in case of fast double
658 : // elements, a smi in the range [0, FixedArray::kMaxLength]
659 : // in case of other fast elements, and [0, kMaxUInt32] in
660 : // case of other arrays.
661 7691 : if (IsDoubleElementsKind(map->elements_kind())) {
662 284 : field_type = type_cache_->kFixedDoubleArrayLengthType;
663 : field_representation = MachineRepresentation::kTaggedSigned;
664 7407 : } else if (IsFastElementsKind(map->elements_kind())) {
665 7122 : field_type = type_cache_->kFixedArrayLengthType;
666 : field_representation = MachineRepresentation::kTaggedSigned;
667 : } else {
668 285 : field_type = type_cache_->kJSArrayLengthType;
669 : }
670 : }
671 : // Special fields are always mutable.
672 : return PropertyAccessInfo::DataField(PropertyConstness::kMutable,
673 : MapHandles{map}, field_index,
674 15382 : field_representation, field_type);
675 : }
676 : return {};
677 : }
678 :
679 22056 : PropertyAccessInfo AccessInfoFactory::LookupTransition(
680 : Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder) const {
681 : // Check if the {map} has a data transition with the given {name}.
682 : Map transition =
683 22056 : TransitionsAccessor(isolate(), map).SearchTransition(*name, kData, NONE);
684 22056 : if (transition.is_null()) return {};
685 :
686 : Handle<Map> transition_map(transition, isolate());
687 : int const number = transition_map->LastAdded();
688 : PropertyDetails const details =
689 22026 : transition_map->instance_descriptors()->GetDetails(number);
690 : // Don't bother optimizing stores to read-only properties.
691 22026 : if (details.IsReadOnly()) return {};
692 : // TODO(bmeurer): Handle transition to data constant?
693 22026 : if (details.location() != kField) return {};
694 : int const index = details.field_index();
695 : Representation details_representation = details.representation();
696 : FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index,
697 22026 : details_representation);
698 : Type field_type = Type::NonInternal();
699 : MaybeHandle<Map> field_map;
700 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
701 : MapRef transition_map_ref(broker(), transition_map);
702 22026 : if (details_representation.IsSmi()) {
703 : field_type = Type::SignedSmall();
704 : field_representation = MachineRepresentation::kTaggedSigned;
705 18733 : transition_map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
706 18733 : dependencies()->DependOnFieldRepresentation(transition_map_ref, number);
707 3293 : } else if (details_representation.IsDouble()) {
708 152 : field_type = type_cache_->kFloat64;
709 : field_representation = MachineRepresentation::kFloat64;
710 3141 : } else if (details_representation.IsHeapObject()) {
711 : // Extract the field type from the property details (make sure its
712 : // representation is TaggedPointer to reflect the heap object case).
713 : field_representation = MachineRepresentation::kTaggedPointer;
714 : Handle<FieldType> descriptors_field_type(
715 1518 : transition_map->instance_descriptors()->GetFieldType(number),
716 759 : isolate());
717 759 : if (descriptors_field_type->IsNone()) {
718 : // Store is not safe if the field type was cleared.
719 : return {};
720 : }
721 759 : transition_map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
722 759 : dependencies()->DependOnFieldRepresentation(transition_map_ref, number);
723 759 : if (descriptors_field_type->IsClass()) {
724 126 : dependencies()->DependOnFieldType(transition_map_ref, number);
725 : // Remember the field map, and try to infer a useful type.
726 252 : Handle<Map> map(descriptors_field_type->AsClass(), isolate());
727 126 : field_type = Type::For(MapRef(broker(), map));
728 : field_map = MaybeHandle<Map>(map);
729 : }
730 : }
731 22026 : dependencies()->DependOnTransition(MapRef(broker(), transition_map));
732 : // Transitioning stores are never stores to constant fields.
733 : return PropertyAccessInfo::DataField(
734 : PropertyConstness::kMutable, MapHandles{map}, field_index,
735 44052 : field_representation, field_type, field_map, holder, transition_map);
736 : }
737 :
738 : } // namespace compiler
739 : } // namespace internal
740 122036 : } // namespace v8
|