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 258853 : 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 258853 : if (map->IsBooleanMap()) return true;
34 258326 : if (map->instance_type() < LAST_PRIMITIVE_TYPE) return true;
35 485344 : return map->IsJSObjectMap() && !map->is_dictionary_map() &&
36 486517 : !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 23369 : ElementAccessInfo::ElementAccessInfo(MapHandles const& receiver_maps,
61 : ElementsKind elements_kind)
62 23369 : : elements_kind_(elements_kind), receiver_maps_(receiver_maps) {
63 23369 : CHECK(!receiver_maps.empty());
64 23369 : }
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 103107 : 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 249994 : 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 15888 : 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 11175 : 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 265966 : field_map_(field_map) {}
149 :
150 17658 : bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
151 : AccessMode access_mode, Zone* zone) {
152 17658 : if (this->kind_ != that->kind_) return false;
153 16763 : if (this->holder_.address() != that->holder_.address()) return false;
154 :
155 15815 : switch (this->kind_) {
156 : case kInvalid:
157 4037 : 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 11409 : if (this->field_index_.GetFieldAccessStubKey() ==
166 : that->field_index_.GetFieldAccessStubKey()) {
167 10774 : switch (access_mode) {
168 : case AccessMode::kHas:
169 : case AccessMode::kLoad: {
170 6345 : if (this->field_representation_ != that->field_representation_) {
171 756 : if (!IsAnyTagged(this->field_representation_) ||
172 : !IsAnyTagged(that->field_representation_)) {
173 : return false;
174 : }
175 378 : this->field_representation_ = MachineRepresentation::kTagged;
176 : }
177 6345 : if (this->field_map_.address() != that->field_map_.address()) {
178 221 : 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 6443 : 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 6443 : that->receiver_maps_.end());
204 6443 : return true;
205 : }
206 : return false;
207 : }
208 :
209 : case kDataConstant:
210 : case kAccessorConstant: {
211 : // Check if we actually access the same constant.
212 294 : if (this->constant_.address() == that->constant_.address()) {
213 : this->receiver_maps_.insert(this->receiver_maps_.end(),
214 : that->receiver_maps_.begin(),
215 287 : that->receiver_maps_.end());
216 287 : 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 75 : that->receiver_maps_.end());
226 75 : 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 167666 : AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
239 : CompilationDependencies* dependencies,
240 : Zone* zone)
241 : : broker_(broker),
242 : dependencies_(dependencies),
243 167666 : type_cache_(TypeCache::Get()),
244 335332 : zone_(zone) {}
245 :
246 11172 : 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 11172 : if (!CanInlineElementAccess(map_ref)) return false;
252 9838 : ElementsKind const elements_kind = map_ref.elements_kind();
253 39352 : *access_info = ElementAccessInfo(MapHandles{map}, elements_kind);
254 9838 : return true;
255 : }
256 :
257 23589 : 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 23605 : ? broker()->GetElementAccessFeedback(FeedbackSource(nexus))
263 47178 : : broker()->ProcessFeedbackMapsForElementAccess(maps);
264 23589 : if (processed == nullptr) return false;
265 :
266 23581 : 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 1472 : ElementAccessInfo access_info;
272 15003 : if (ConsolidateElementLoad(*processed, &access_info)) {
273 13531 : access_infos->push_back(access_info);
274 13531 : return true;
275 : }
276 : }
277 :
278 19888 : for (Handle<Map> receiver_map : processed->receiver_maps) {
279 : // Compute the element access information.
280 9838 : ElementAccessInfo access_info;
281 11172 : if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
282 1334 : return false;
283 : }
284 :
285 : // Collect the possible transitions for the {receiver_map}.
286 10474 : 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 9838 : access_infos->push_back(access_info);
294 : }
295 8716 : return true;
296 : }
297 :
298 103107 : 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 103107 : PropertyDetails const details = descriptors->GetDetails(number);
304 206214 : int index = descriptors->GetFieldIndex(number);
305 : Representation details_representation = details.representation();
306 103107 : 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 103107 : 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 103107 : if (details_representation.IsSmi()) {
326 : field_type = Type::SignedSmall();
327 : field_representation = MachineRepresentation::kTaggedSigned;
328 8189 : map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
329 8189 : dependencies()->DependOnFieldRepresentation(map_ref, number);
330 94918 : } else if (details_representation.IsDouble()) {
331 917 : field_type = type_cache_->kFloat64;
332 : field_representation = MachineRepresentation::kFloat64;
333 94001 : } else if (details_representation.IsHeapObject()) {
334 : // Extract the field type from the property details (make sure its
335 : // representation is TaggedPointer to reflect the heap object case).
336 : field_representation = MachineRepresentation::kTaggedPointer;
337 : Handle<FieldType> descriptors_field_type(descriptors->GetFieldType(number),
338 168700 : isolate());
339 84350 : if (descriptors_field_type->IsNone()) {
340 : // Store is not safe if the field type was cleared.
341 55 : if (access_mode == AccessMode::kStore) return {};
342 :
343 : // The field type was cleared by the GC, so we don't know anything
344 : // about the contents now.
345 : }
346 84350 : map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
347 84350 : dependencies()->DependOnFieldRepresentation(map_ref, number);
348 84350 : if (descriptors_field_type->IsClass()) {
349 3454 : dependencies()->DependOnFieldType(map_ref, number);
350 : // Remember the field map, and try to infer a useful type.
351 6908 : Handle<Map> map(descriptors_field_type->AsClass(), isolate());
352 3454 : field_type = Type::For(MapRef(broker(), map));
353 : field_map = MaybeHandle<Map>(map);
354 : }
355 : }
356 : return PropertyAccessInfo::DataField(
357 : details.constness(), MapHandles{receiver_map}, field_index,
358 206214 : field_representation, field_type, field_map, holder);
359 : }
360 :
361 8282 : PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
362 : Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map,
363 : MaybeHandle<JSObject> holder, int number, AccessMode access_mode) const {
364 : DCHECK_NE(number, DescriptorArray::kNotFound);
365 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
366 : SLOW_DCHECK(number == descriptors->Search(*name, *map));
367 8282 : if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
368 : DCHECK(map->is_prototype_map());
369 : Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(map->prototype_info()),
370 : isolate());
371 : Handle<JSModuleNamespace> module_namespace(
372 : JSModuleNamespace::cast(proto_info->module_namespace()), isolate());
373 : Handle<Cell> cell(
374 44 : Cell::cast(module_namespace->module()->exports()->Lookup(
375 44 : ReadOnlyRoots(isolate()), name, Smi::ToInt(name->GetHash()))),
376 66 : isolate());
377 22 : if (cell->value()->IsTheHole(isolate())) {
378 : // This module has not been fully initialized yet.
379 : return {};
380 : }
381 44 : return PropertyAccessInfo::ModuleExport(MapHandles{receiver_map}, cell);
382 : }
383 8260 : if (access_mode == AccessMode::kHas) {
384 : // HasProperty checks don't call getter/setters, existence is sufficient.
385 : return PropertyAccessInfo::AccessorConstant(MapHandles{receiver_map},
386 224 : Handle<Object>(), holder);
387 : }
388 : Handle<Object> accessors(descriptors->GetStrongValue(number), isolate());
389 8148 : if (!accessors->IsAccessorPair()) return {};
390 : Handle<Object> accessor(access_mode == AccessMode::kLoad
391 : ? Handle<AccessorPair>::cast(accessors)->getter()
392 : : Handle<AccessorPair>::cast(accessors)->setter(),
393 6791 : isolate());
394 6791 : if (!accessor->IsJSFunction()) {
395 3328 : CallOptimization optimization(isolate(), accessor);
396 9984 : if (!optimization.is_simple_api_call() ||
397 172 : optimization.IsCrossContextLazyAccessorPair(
398 3672 : *broker()->native_context().object(), *map)) {
399 3194 : return {};
400 : }
401 :
402 : CallOptimization::HolderLookup lookup;
403 166 : holder = optimization.LookupHolderOfExpectedType(receiver_map, &lookup);
404 166 : if (lookup == CallOptimization::kHolderNotFound) return {};
405 : DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
406 : holder.is_null());
407 : DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound, !holder.is_null());
408 134 : if (V8_UNLIKELY(TracingFlags::is_runtime_stats_enabled())) return {};
409 : }
410 3597 : if (access_mode == AccessMode::kLoad) {
411 : Handle<Name> cached_property_name;
412 5434 : if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), accessor)
413 : .ToHandle(&cached_property_name)) {
414 : PropertyAccessInfo access_info =
415 6 : ComputePropertyAccessInfo(map, cached_property_name, access_mode);
416 6 : if (!access_info.IsInvalid()) return access_info;
417 : }
418 : }
419 : return PropertyAccessInfo::AccessorConstant(MapHandles{receiver_map},
420 7182 : accessor, holder);
421 : }
422 :
423 161076 : PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
424 : Handle<Map> map, Handle<Name> name, AccessMode access_mode) const {
425 161076 : CHECK(name->IsUniqueName());
426 :
427 161396 : if (access_mode == AccessMode::kHas && !map->IsJSReceiverMap()) return {};
428 :
429 : // Check if it is safe to inline property access for the {map}.
430 161069 : if (!CanInlinePropertyAccess(map)) return {};
431 :
432 : // We support fast inline cases for certain JSObject getters.
433 151617 : if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
434 114904 : PropertyAccessInfo access_info = LookupSpecialFieldAccessor(map, name);
435 114904 : if (!access_info.IsInvalid()) return access_info;
436 : }
437 :
438 : // Remember the receiver map. We use {map} as loop variable.
439 : Handle<Map> receiver_map = map;
440 : MaybeHandle<JSObject> holder;
441 : while (true) {
442 : // Lookup the named property on the {map}.
443 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
444 475842 : int const number = descriptors->Search(*name, *map);
445 237921 : if (number != DescriptorArray::kNotFound) {
446 116769 : PropertyDetails const details = descriptors->GetDetails(number);
447 116769 : if (access_mode == AccessMode::kStore ||
448 : access_mode == AccessMode::kStoreInLiteral) {
449 : // Don't bother optimizing stores to read-only properties.
450 15475 : if (details.IsReadOnly()) return {};
451 12274 : if (details.kind() == kData && !holder.is_null()) {
452 : // This is a store to a property not found on the receiver but on a
453 : // prototype. According to ES6 section 9.1.9 [[Set]], we need to
454 : // create a new data property on the receiver. We can still optimize
455 : // if such a transition already exists.
456 2179 : return LookupTransition(receiver_map, name, holder);
457 : }
458 : }
459 111389 : if (details.location() == kField) {
460 103107 : if (details.kind() == kData) {
461 : return ComputeDataFieldAccessInfo(receiver_map, map, holder, number,
462 103107 : access_mode);
463 : } else {
464 : DCHECK_EQ(kAccessor, details.kind());
465 : // TODO(turbofan): Add support for general accessors?
466 : return {};
467 : }
468 : } else {
469 : DCHECK_EQ(kDescriptor, details.location());
470 8282 : if (details.kind() == kData) {
471 : DCHECK(!FLAG_track_constant_fields);
472 : return PropertyAccessInfo::DataConstant(
473 : MapHandles{receiver_map},
474 0 : handle(descriptors->GetStrongValue(number), isolate()), holder);
475 : } else {
476 : DCHECK_EQ(kAccessor, details.kind());
477 : return ComputeAccessorDescriptorAccessInfo(
478 8282 : receiver_map, name, map, holder, number, access_mode);
479 : }
480 : }
481 : UNREACHABLE();
482 : }
483 :
484 : // The property wasn't found on {map}. Look on the prototype if appropriate.
485 :
486 : // Don't search on the prototype chain for special indices in case of
487 : // integer indexed exotic objects (see ES6 section 9.4.5).
488 124339 : if (map->IsJSTypedArrayMap() && name->IsString() &&
489 1573 : IsSpecialIndex(String::cast(*name))) {
490 : return {};
491 : }
492 :
493 : // Don't search on the prototype when storing in literals.
494 121145 : if (access_mode == AccessMode::kStoreInLiteral) {
495 110 : return LookupTransition(receiver_map, name, holder);
496 : }
497 :
498 : // Don't lookup private symbols on the prototype chain.
499 121035 : if (name->IsPrivate()) return {};
500 :
501 : // Walk up the prototype chain.
502 121034 : if (!map->prototype()->IsJSObject()) {
503 : // Perform the implicit ToObject for primitives here.
504 : // Implemented according to ES6 section 7.3.2 GetV (V, P).
505 : Handle<JSFunction> constructor;
506 94182 : if (Map::GetConstructorFunction(map, broker()->native_context().object())
507 : .ToHandle(&constructor)) {
508 : map = handle(constructor->initial_map(), isolate());
509 : DCHECK(map->prototype()->IsJSObject());
510 23250 : } else if (map->prototype()->IsNull(isolate())) {
511 : // Store to property not found on the receiver or any prototype, we need
512 : // to transition to a new data property.
513 : // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
514 23243 : if (access_mode == AccessMode::kStore) {
515 19771 : return LookupTransition(receiver_map, name, holder);
516 : }
517 : // The property was not found (access returns undefined or throws
518 : // depending on the language mode of the load operation.
519 : // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
520 6944 : return PropertyAccessInfo::NotFound(MapHandles{receiver_map}, holder);
521 : } else {
522 : return {};
523 : }
524 : }
525 : Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
526 97784 : if (map_prototype->map()->is_deprecated()) {
527 : // Try to migrate the prototype object so we don't embed the deprecated
528 : // map into the optimized code.
529 0 : JSObject::TryMigrateInstance(map_prototype);
530 : }
531 : map = handle(map_prototype->map(), isolate());
532 : holder = map_prototype;
533 :
534 97784 : if (!CanInlinePropertyAccess(map)) return {};
535 :
536 : // Successful lookup on prototype chain needs to guarantee that all
537 : // the prototypes up to the holder have stable maps. Let us make sure
538 : // the prototype maps are stable here.
539 95973 : CHECK(map->is_stable());
540 : }
541 : }
542 :
543 1232 : PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
544 : MapHandles const& maps, Handle<Name> name, AccessMode access_mode) const {
545 : ZoneVector<PropertyAccessInfo> raw_access_infos(zone());
546 1232 : ComputePropertyAccessInfos(maps, name, access_mode, &raw_access_infos);
547 : ZoneVector<PropertyAccessInfo> access_infos(zone());
548 2464 : if (FinalizePropertyAccessInfos(raw_access_infos, access_mode,
549 1768 : &access_infos) &&
550 : access_infos.size() == 1) {
551 536 : return access_infos.front();
552 : }
553 : return {};
554 : }
555 :
556 142925 : void AccessInfoFactory::ComputePropertyAccessInfos(
557 : MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
558 : ZoneVector<PropertyAccessInfo>* access_infos) const {
559 : DCHECK(access_infos->empty());
560 302843 : for (Handle<Map> map : maps) {
561 319836 : access_infos->push_back(ComputePropertyAccessInfo(map, name, access_mode));
562 : }
563 142925 : }
564 :
565 142925 : bool AccessInfoFactory::FinalizePropertyAccessInfos(
566 : ZoneVector<PropertyAccessInfo> infos, AccessMode access_mode,
567 : ZoneVector<PropertyAccessInfo>* result) const {
568 : DCHECK(result->empty());
569 283777 : for (auto it = infos.begin(), end = infos.end(); it != end; ++it) {
570 : bool merged = false;
571 162651 : for (auto ot = it + 1; ot != end; ++ot) {
572 17658 : if (ot->Merge(&(*it), access_mode, zone())) {
573 : merged = true;
574 : break;
575 : }
576 : }
577 155835 : if (it->IsInvalid()) return false;
578 140852 : if (!merged) result->push_back(*it);
579 : }
580 127942 : CHECK(!result->empty());
581 : return true;
582 : }
583 :
584 : namespace {
585 :
586 15397 : Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
587 : ElementsKind that_kind) {
588 15397 : if (IsHoleyElementsKind(this_kind)) {
589 : that_kind = GetHoleyElementsKind(that_kind);
590 11873 : } else if (IsHoleyElementsKind(that_kind)) {
591 : this_kind = GetHoleyElementsKind(this_kind);
592 : }
593 15397 : if (this_kind == that_kind) return Just(this_kind);
594 759 : if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
595 699 : if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
596 : return Just(this_kind);
597 : }
598 97 : if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
599 : return Just(that_kind);
600 : }
601 : }
602 : return Nothing<ElementsKind>();
603 : }
604 :
605 : } // namespace
606 :
607 15003 : bool AccessInfoFactory::ConsolidateElementLoad(
608 : ElementAccessFeedback const& processed,
609 : ElementAccessInfo* access_info) const {
610 15003 : ElementAccessFeedback::MapIterator it = processed.all_maps(broker());
611 15003 : MapRef first_map = it.current();
612 15003 : InstanceType instance_type = first_map.instance_type();
613 15003 : ElementsKind elements_kind = first_map.elements_kind();
614 : MapHandles maps;
615 45483 : for (; !it.done(); it.advance()) {
616 16712 : MapRef map = it.current();
617 16712 : if (map.instance_type() != instance_type || !CanInlineElementAccess(map)) {
618 1472 : return false;
619 : }
620 30794 : if (!GeneralizeElementsKind(elements_kind, map.elements_kind())
621 : .To(&elements_kind)) {
622 : return false;
623 : }
624 30480 : maps.push_back(map.object());
625 : }
626 :
627 27062 : *access_info = ElementAccessInfo(maps, elements_kind);
628 13531 : return true;
629 : }
630 :
631 114904 : PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor(
632 : Handle<Map> map, Handle<Name> name) const {
633 : // Check for String::length field accessor.
634 114904 : if (map->IsStringMap()) {
635 7974 : if (Name::Equals(isolate(), name, isolate()->factory()->length_string())) {
636 3648 : return PropertyAccessInfo::StringLength(MapHandles{map});
637 : }
638 : return {};
639 : }
640 : // Check for special JSObject field accessors.
641 : FieldIndex field_index;
642 106930 : if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
643 : Type field_type = Type::NonInternal();
644 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
645 7845 : if (map->IsJSArrayMap()) {
646 : DCHECK(
647 : Name::Equals(isolate(), isolate()->factory()->length_string(), name));
648 : // The JSArray::length property is a smi in the range
649 : // [0, FixedDoubleArray::kMaxLength] in case of fast double
650 : // elements, a smi in the range [0, FixedArray::kMaxLength]
651 : // in case of other fast elements, and [0, kMaxUInt32] in
652 : // case of other arrays.
653 7845 : if (IsDoubleElementsKind(map->elements_kind())) {
654 286 : field_type = type_cache_->kFixedDoubleArrayLengthType;
655 : field_representation = MachineRepresentation::kTaggedSigned;
656 7559 : } else if (IsFastElementsKind(map->elements_kind())) {
657 7272 : field_type = type_cache_->kFixedArrayLengthType;
658 : field_representation = MachineRepresentation::kTaggedSigned;
659 : } else {
660 287 : field_type = type_cache_->kJSArrayLengthType;
661 : }
662 : }
663 : // Special fields are always mutable.
664 : return PropertyAccessInfo::DataField(PropertyConstness::kMutable,
665 : MapHandles{map}, field_index,
666 15690 : field_representation, field_type);
667 : }
668 : return {};
669 : }
670 :
671 22060 : PropertyAccessInfo AccessInfoFactory::LookupTransition(
672 : Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder) const {
673 : // Check if the {map} has a data transition with the given {name}.
674 : Map transition =
675 22060 : TransitionsAccessor(isolate(), map).SearchTransition(*name, kData, NONE);
676 22060 : if (transition.is_null()) return {};
677 :
678 : Handle<Map> transition_map(transition, isolate());
679 : int const number = transition_map->LastAdded();
680 : PropertyDetails const details =
681 22031 : transition_map->instance_descriptors()->GetDetails(number);
682 : // Don't bother optimizing stores to read-only properties.
683 22031 : if (details.IsReadOnly()) return {};
684 : // TODO(bmeurer): Handle transition to data constant?
685 22031 : if (details.location() != kField) return {};
686 : int const index = details.field_index();
687 : Representation details_representation = details.representation();
688 : FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index,
689 22031 : details_representation);
690 : Type field_type = Type::NonInternal();
691 : MaybeHandle<Map> field_map;
692 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
693 : MapRef transition_map_ref(broker(), transition_map);
694 22031 : if (details_representation.IsSmi()) {
695 : field_type = Type::SignedSmall();
696 : field_representation = MachineRepresentation::kTaggedSigned;
697 18681 : transition_map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
698 18681 : dependencies()->DependOnFieldRepresentation(transition_map_ref, number);
699 3350 : } else if (details_representation.IsDouble()) {
700 150 : field_type = type_cache_->kFloat64;
701 : field_representation = MachineRepresentation::kFloat64;
702 3200 : } else if (details_representation.IsHeapObject()) {
703 : // Extract the field type from the property details (make sure its
704 : // representation is TaggedPointer to reflect the heap object case).
705 : field_representation = MachineRepresentation::kTaggedPointer;
706 : Handle<FieldType> descriptors_field_type(
707 1634 : transition_map->instance_descriptors()->GetFieldType(number),
708 817 : isolate());
709 817 : if (descriptors_field_type->IsNone()) {
710 : // Store is not safe if the field type was cleared.
711 : return {};
712 : }
713 817 : transition_map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
714 817 : dependencies()->DependOnFieldRepresentation(transition_map_ref, number);
715 817 : if (descriptors_field_type->IsClass()) {
716 139 : dependencies()->DependOnFieldType(transition_map_ref, number);
717 : // Remember the field map, and try to infer a useful type.
718 278 : Handle<Map> map(descriptors_field_type->AsClass(), isolate());
719 139 : field_type = Type::For(MapRef(broker(), map));
720 : field_map = MaybeHandle<Map>(map);
721 : }
722 : }
723 22031 : dependencies()->DependOnTransition(MapRef(broker(), transition_map));
724 : // Transitioning stores are never stores to constant fields.
725 : return PropertyAccessInfo::DataField(
726 : PropertyConstness::kMutable, MapHandles{map}, field_index,
727 44062 : field_representation, field_type, field_map, holder, transition_map);
728 : }
729 :
730 : } // namespace compiler
731 : } // namespace internal
732 122004 : } // namespace v8
|