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 256518 : 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 256518 : if (map->IsBooleanMap()) return true;
34 255991 : if (map->instance_type() < LAST_PRIMITIVE_TYPE) return true;
35 480786 : return map->IsJSObjectMap() && !map->is_dictionary_map() &&
36 481960 : !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 23294 : ElementAccessInfo::ElementAccessInfo(MapHandles const& receiver_maps,
61 : ElementsKind elements_kind)
62 23294 : : elements_kind_(elements_kind), receiver_maps_(receiver_maps) {
63 23294 : CHECK(!receiver_maps.empty());
64 23294 : }
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 101944 : 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 247418 : 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 15819 : 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 10620 : 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 263420 : field_map_(field_map) {}
149 :
150 17470 : bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
151 : AccessMode access_mode, Zone* zone) {
152 17470 : if (this->kind_ != that->kind_) return false;
153 16654 : if (this->holder_.address() != that->holder_.address()) return false;
154 :
155 15706 : 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 11351 : if (this->field_index_.GetFieldAccessStubKey() ==
166 : that->field_index_.GetFieldAccessStubKey()) {
167 10726 : switch (access_mode) {
168 : case AccessMode::kHas:
169 : case AccessMode::kLoad: {
170 6297 : if (this->field_representation_ != that->field_representation_) {
171 740 : if (!IsAnyTagged(this->field_representation_) ||
172 : !IsAnyTagged(that->field_representation_)) {
173 : return false;
174 : }
175 370 : this->field_representation_ = MachineRepresentation::kTagged;
176 : }
177 6297 : if (this->field_map_.address() != that->field_map_.address()) {
178 235 : 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 6395 : 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 6395 : that->receiver_maps_.end());
204 6395 : return true;
205 : }
206 : return false;
207 : }
208 :
209 : case kDataConstant:
210 : case kAccessorConstant: {
211 : // Check if we actually access the same constant.
212 243 : if (this->constant_.address() == that->constant_.address()) {
213 : this->receiver_maps_.insert(this->receiver_maps_.end(),
214 : that->receiver_maps_.begin(),
215 236 : that->receiver_maps_.end());
216 236 : 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 166271 : AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
239 : CompilationDependencies* dependencies,
240 : Zone* zone)
241 : : broker_(broker),
242 : dependencies_(dependencies),
243 166271 : type_cache_(TypeCache::Get()),
244 332542 : zone_(zone) {}
245 :
246 11077 : 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 11077 : if (!CanInlineElementAccess(map_ref)) return false;
252 9746 : ElementsKind const elements_kind = map_ref.elements_kind();
253 38984 : *access_info = ElementAccessInfo(MapHandles{map}, elements_kind);
254 9746 : return true;
255 : }
256 :
257 23544 : 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 23560 : ? broker()->GetElementAccessFeedback(FeedbackSource(nexus))
263 47088 : : broker()->ProcessFeedbackMapsForElementAccess(maps);
264 23544 : if (processed == nullptr) return false;
265 :
266 23536 : 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 1448 : ElementAccessInfo access_info;
272 14996 : if (ConsolidateElementLoad(*processed, &access_info)) {
273 13548 : access_infos->push_back(access_info);
274 13548 : return true;
275 : }
276 : }
277 :
278 19734 : for (Handle<Map> receiver_map : processed->receiver_maps) {
279 : // Compute the element access information.
280 9746 : ElementAccessInfo access_info;
281 11077 : if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
282 1331 : return false;
283 : }
284 :
285 : // Collect the possible transitions for the {receiver_map}.
286 10362 : for (auto transition : processed->transitions) {
287 616 : if (transition.second.equals(receiver_map)) {
288 550 : access_info.AddTransitionSource(transition.first);
289 : }
290 : }
291 :
292 : // Schedule the access information.
293 9746 : access_infos->push_back(access_info);
294 : }
295 8657 : return true;
296 : }
297 :
298 101944 : 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 101944 : PropertyDetails const details = descriptors->GetDetails(number);
304 203888 : int index = descriptors->GetFieldIndex(number);
305 : Representation details_representation = details.representation();
306 101944 : 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 101944 : 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 101944 : 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 7913 : map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
333 7913 : dependencies()->DependOnFieldRepresentation(map_ref, number);
334 94031 : } else if (details_representation.IsDouble()) {
335 913 : field_type = type_cache_->kFloat64;
336 : field_representation = MachineRepresentation::kFloat64;
337 93118 : } 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 : field_representation = MachineRepresentation::kTaggedPointer;
341 : Handle<FieldType> descriptors_field_type(descriptors->GetFieldType(number),
342 167740 : isolate());
343 83870 : if (descriptors_field_type->IsNone()) {
344 : // Store is not safe if the field type was cleared.
345 55 : if (access_mode == AccessMode::kStore) return {};
346 :
347 : // The field type was cleared by the GC, so we don't know anything
348 : // about the contents now.
349 : }
350 83870 : map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
351 83870 : dependencies()->DependOnFieldRepresentation(map_ref, number);
352 83870 : if (descriptors_field_type->IsClass()) {
353 3272 : dependencies()->DependOnFieldType(map_ref, number);
354 : // Remember the field map, and try to infer a useful type.
355 6544 : Handle<Map> map(descriptors_field_type->AsClass(), isolate());
356 3272 : field_type = Type::For(MapRef(broker(), map));
357 : field_map = MaybeHandle<Map>(map);
358 : }
359 : }
360 : return PropertyAccessInfo::DataField(
361 : details.constness(), MapHandles{receiver_map}, field_index,
362 203888 : field_representation, field_type, field_map, holder);
363 : }
364 :
365 8101 : PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
366 : Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map,
367 : MaybeHandle<JSObject> holder, int number, AccessMode access_mode) const {
368 : DCHECK_NE(number, DescriptorArray::kNotFound);
369 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
370 : SLOW_DCHECK(number == descriptors->Search(*name, *map));
371 8101 : if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
372 : DCHECK(map->is_prototype_map());
373 : Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(map->prototype_info()),
374 : isolate());
375 : Handle<JSModuleNamespace> module_namespace(
376 : JSModuleNamespace::cast(proto_info->module_namespace()), isolate());
377 : Handle<Cell> cell(
378 44 : Cell::cast(module_namespace->module()->exports()->Lookup(
379 44 : ReadOnlyRoots(isolate()), name, Smi::ToInt(name->GetHash()))),
380 66 : isolate());
381 22 : if (cell->value()->IsTheHole(isolate())) {
382 : // This module has not been fully initialized yet.
383 : return {};
384 : }
385 44 : return PropertyAccessInfo::ModuleExport(MapHandles{receiver_map}, cell);
386 : }
387 8079 : if (access_mode == AccessMode::kHas) {
388 : // HasProperty checks don't call getter/setters, existence is sufficient.
389 : return PropertyAccessInfo::AccessorConstant(MapHandles{receiver_map},
390 224 : Handle<Object>(), holder);
391 : }
392 : Handle<Object> accessors(descriptors->GetStrongValue(number), isolate());
393 7967 : if (!accessors->IsAccessorPair()) return {};
394 : Handle<Object> accessor(access_mode == AccessMode::kLoad
395 : ? Handle<AccessorPair>::cast(accessors)->getter()
396 : : Handle<AccessorPair>::cast(accessors)->setter(),
397 6614 : isolate());
398 6614 : if (!accessor->IsJSFunction()) {
399 3336 : CallOptimization optimization(isolate(), accessor);
400 10008 : if (!optimization.is_simple_api_call() ||
401 172 : optimization.IsCrossContextLazyAccessorPair(
402 3680 : *broker()->native_context().object(), *map)) {
403 3202 : return {};
404 : }
405 :
406 : CallOptimization::HolderLookup lookup;
407 166 : holder = optimization.LookupHolderOfExpectedType(receiver_map, &lookup);
408 166 : if (lookup == CallOptimization::kHolderNotFound) return {};
409 : DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
410 : holder.is_null());
411 : DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound, !holder.is_null());
412 134 : if (V8_UNLIKELY(TracingFlags::is_runtime_stats_enabled())) return {};
413 : }
414 3412 : if (access_mode == AccessMode::kLoad) {
415 : Handle<Name> cached_property_name;
416 5070 : if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), accessor)
417 : .ToHandle(&cached_property_name)) {
418 : PropertyAccessInfo access_info =
419 6 : ComputePropertyAccessInfo(map, cached_property_name, access_mode);
420 6 : if (!access_info.IsInvalid()) return access_info;
421 : }
422 : }
423 : return PropertyAccessInfo::AccessorConstant(MapHandles{receiver_map},
424 6812 : accessor, holder);
425 : }
426 :
427 159568 : PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
428 : Handle<Map> map, Handle<Name> name, AccessMode access_mode) const {
429 159568 : CHECK(name->IsUniqueName());
430 :
431 159888 : if (access_mode == AccessMode::kHas && !map->IsJSReceiverMap()) return {};
432 :
433 : // Check if it is safe to inline property access for the {map}.
434 159561 : if (!CanInlinePropertyAccess(map)) return {};
435 :
436 : // We support fast inline cases for certain JSObject getters.
437 150108 : if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
438 113496 : PropertyAccessInfo access_info = LookupSpecialFieldAccessor(map, name);
439 113496 : if (!access_info.IsInvalid()) return access_info;
440 : }
441 :
442 : // Remember the receiver map. We use {map} as loop variable.
443 : Handle<Map> receiver_map = map;
444 : MaybeHandle<JSObject> holder;
445 : while (true) {
446 : // Lookup the named property on the {map}.
447 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
448 471512 : int const number = descriptors->Search(*name, *map);
449 235756 : if (number != DescriptorArray::kNotFound) {
450 115488 : PropertyDetails const details = descriptors->GetDetails(number);
451 115488 : if (access_mode == AccessMode::kStore ||
452 : access_mode == AccessMode::kStoreInLiteral) {
453 : // Don't bother optimizing stores to read-only properties.
454 15440 : if (details.IsReadOnly()) return {};
455 12247 : if (details.kind() == kData && !holder.is_null()) {
456 : // This is a store to a property not found on the receiver but on a
457 : // prototype. According to ES6 section 9.1.9 [[Set]], we need to
458 : // create a new data property on the receiver. We can still optimize
459 : // if such a transition already exists.
460 2250 : return LookupTransition(receiver_map, name, holder);
461 : }
462 : }
463 110045 : if (details.location() == kField) {
464 101944 : if (details.kind() == kData) {
465 : return ComputeDataFieldAccessInfo(receiver_map, map, holder, number,
466 101944 : access_mode);
467 : } else {
468 : DCHECK_EQ(kAccessor, details.kind());
469 : // TODO(turbofan): Add support for general accessors?
470 : return {};
471 : }
472 : } else {
473 : DCHECK_EQ(kDescriptor, details.location());
474 8101 : if (details.kind() == kData) {
475 : DCHECK(!FLAG_track_constant_fields);
476 : return PropertyAccessInfo::DataConstant(
477 : MapHandles{receiver_map},
478 0 : handle(descriptors->GetStrongValue(number), isolate()), holder);
479 : } else {
480 : DCHECK_EQ(kAccessor, details.kind());
481 : return ComputeAccessorDescriptorAccessInfo(
482 8101 : receiver_map, name, map, holder, number, access_mode);
483 : }
484 : }
485 : UNREACHABLE();
486 : }
487 :
488 : // The property wasn't found on {map}. Look on the prototype if appropriate.
489 :
490 : // Don't search on the prototype chain for special indices in case of
491 : // integer indexed exotic objects (see ES6 section 9.4.5).
492 123036 : if (map->IsJSTypedArrayMap() && name->IsString() &&
493 1363 : IsSpecialIndex(String::cast(*name))) {
494 : return {};
495 : }
496 :
497 : // Don't search on the prototype when storing in literals.
498 120261 : if (access_mode == AccessMode::kStoreInLiteral) {
499 111 : return LookupTransition(receiver_map, name, holder);
500 : }
501 :
502 : // Don't lookup private symbols on the prototype chain.
503 120150 : if (name->IsPrivate()) return {};
504 :
505 : // Walk up the prototype chain.
506 120149 : if (!map->prototype()->IsJSObject()) {
507 : // Perform the implicit ToObject for primitives here.
508 : // Implemented according to ES6 section 7.3.2 GetV (V, P).
509 : Handle<JSFunction> constructor;
510 93987 : if (Map::GetConstructorFunction(map, broker()->native_context().object())
511 : .ToHandle(&constructor)) {
512 : map = handle(constructor->initial_map(), isolate());
513 : DCHECK(map->prototype()->IsJSObject());
514 23192 : } else if (map->prototype()->IsNull(isolate())) {
515 : // Store to property not found on the receiver or any prototype, we need
516 : // to transition to a new data property.
517 : // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
518 23185 : if (access_mode == AccessMode::kStore) {
519 19698 : return LookupTransition(receiver_map, name, holder);
520 : }
521 : // The property was not found (access returns undefined or throws
522 : // depending on the language mode of the load operation.
523 : // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
524 6974 : return PropertyAccessInfo::NotFound(MapHandles{receiver_map}, holder);
525 : } else {
526 : return {};
527 : }
528 : }
529 : Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
530 96957 : if (map_prototype->map()->is_deprecated()) {
531 : // Try to migrate the prototype object so we don't embed the deprecated
532 : // map into the optimized code.
533 0 : JSObject::TryMigrateInstance(map_prototype);
534 : }
535 : map = handle(map_prototype->map(), isolate());
536 : holder = map_prototype;
537 :
538 96957 : if (!CanInlinePropertyAccess(map)) return {};
539 :
540 : // Successful lookup on prototype chain needs to guarantee that all
541 : // the prototypes up to the holder have stable maps. Let us make sure
542 : // the prototype maps are stable here.
543 95169 : CHECK(map->is_stable());
544 : }
545 : }
546 :
547 1222 : PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
548 : MapHandles const& maps, Handle<Name> name, AccessMode access_mode) const {
549 : ZoneVector<PropertyAccessInfo> raw_access_infos(zone());
550 1222 : ComputePropertyAccessInfos(maps, name, access_mode, &raw_access_infos);
551 : ZoneVector<PropertyAccessInfo> access_infos(zone());
552 2444 : if (FinalizePropertyAccessInfos(raw_access_infos, access_mode,
553 1748 : &access_infos) &&
554 : access_infos.size() == 1) {
555 526 : return access_infos.front();
556 : }
557 : return {};
558 : }
559 :
560 141601 : void AccessInfoFactory::ComputePropertyAccessInfos(
561 : MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
562 : ZoneVector<PropertyAccessInfo>* access_infos) const {
563 : DCHECK(access_infos->empty());
564 300037 : for (Handle<Map> map : maps) {
565 316872 : access_infos->push_back(ComputePropertyAccessInfo(map, name, access_mode));
566 : }
567 141601 : }
568 :
569 141601 : bool AccessInfoFactory::FinalizePropertyAccessInfos(
570 : ZoneVector<PropertyAccessInfo> infos, AccessMode access_mode,
571 : ZoneVector<PropertyAccessInfo>* result) const {
572 : DCHECK(result->empty());
573 280998 : for (auto it = infos.begin(), end = infos.end(); it != end; ++it) {
574 : bool merged = false;
575 161080 : for (auto ot = it + 1; ot != end; ++ot) {
576 17470 : if (ot->Merge(&(*it), access_mode, zone())) {
577 : merged = true;
578 : break;
579 : }
580 : }
581 154353 : if (it->IsInvalid()) return false;
582 139397 : if (!merged) result->push_back(*it);
583 : }
584 126645 : CHECK(!result->empty());
585 : return true;
586 : }
587 :
588 : namespace {
589 :
590 15377 : Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
591 : ElementsKind that_kind) {
592 15377 : if (IsHoleyElementsKind(this_kind)) {
593 : that_kind = GetHoleyElementsKind(that_kind);
594 11868 : } else if (IsHoleyElementsKind(that_kind)) {
595 : this_kind = GetHoleyElementsKind(this_kind);
596 : }
597 15377 : if (this_kind == that_kind) return Just(this_kind);
598 743 : if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
599 689 : if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
600 : return Just(this_kind);
601 : }
602 93 : if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
603 : return Just(that_kind);
604 : }
605 : }
606 : return Nothing<ElementsKind>();
607 : }
608 :
609 : } // namespace
610 :
611 14996 : bool AccessInfoFactory::ConsolidateElementLoad(
612 : ElementAccessFeedback const& processed,
613 : ElementAccessInfo* access_info) const {
614 14996 : ElementAccessFeedback::MapIterator it = processed.all_maps(broker());
615 14996 : MapRef first_map = it.current();
616 14996 : InstanceType instance_type = first_map.instance_type();
617 14996 : ElementsKind elements_kind = first_map.elements_kind();
618 : MapHandles maps;
619 45456 : for (; !it.done(); it.advance()) {
620 16678 : MapRef map = it.current();
621 16678 : if (map.instance_type() != instance_type || !CanInlineElementAccess(map)) {
622 1448 : return false;
623 : }
624 30754 : if (!GeneralizeElementsKind(elements_kind, map.elements_kind())
625 : .To(&elements_kind)) {
626 : return false;
627 : }
628 30460 : maps.push_back(map.object());
629 : }
630 :
631 27096 : *access_info = ElementAccessInfo(maps, elements_kind);
632 13548 : return true;
633 : }
634 :
635 113496 : PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor(
636 : Handle<Map> map, Handle<Name> name) const {
637 : // Check for String::length field accessor.
638 113496 : if (map->IsStringMap()) {
639 7931 : if (Name::Equals(isolate(), name, isolate()->factory()->length_string())) {
640 3572 : return PropertyAccessInfo::StringLength(MapHandles{map});
641 : }
642 : return {};
643 : }
644 : // Check for special JSObject field accessors.
645 : FieldIndex field_index;
646 105565 : if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
647 : Type field_type = Type::NonInternal();
648 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
649 7736 : if (map->IsJSArrayMap()) {
650 : DCHECK(
651 : Name::Equals(isolate(), isolate()->factory()->length_string(), name));
652 : // The JSArray::length property is a smi in the range
653 : // [0, FixedDoubleArray::kMaxLength] in case of fast double
654 : // elements, a smi in the range [0, FixedArray::kMaxLength]
655 : // in case of other fast elements, and [0, kMaxUInt32] in
656 : // case of other arrays.
657 7736 : if (IsDoubleElementsKind(map->elements_kind())) {
658 275 : field_type = type_cache_->kFixedDoubleArrayLengthType;
659 : field_representation = MachineRepresentation::kTaggedSigned;
660 7461 : } else if (IsFastElementsKind(map->elements_kind())) {
661 7178 : field_type = type_cache_->kFixedArrayLengthType;
662 : field_representation = MachineRepresentation::kTaggedSigned;
663 : } else {
664 283 : field_type = type_cache_->kJSArrayLengthType;
665 : }
666 : }
667 : // Special fields are always mutable.
668 : return PropertyAccessInfo::DataField(PropertyConstness::kMutable,
669 : MapHandles{map}, field_index,
670 15472 : field_representation, field_type);
671 : }
672 : return {};
673 : }
674 :
675 22059 : PropertyAccessInfo AccessInfoFactory::LookupTransition(
676 : Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder) const {
677 : // Check if the {map} has a data transition with the given {name}.
678 : Map transition =
679 22059 : TransitionsAccessor(isolate(), map).SearchTransition(*name, kData, NONE);
680 22059 : if (transition.is_null()) return {};
681 :
682 : Handle<Map> transition_map(transition, isolate());
683 : int const number = transition_map->LastAdded();
684 : PropertyDetails const details =
685 22030 : transition_map->instance_descriptors()->GetDetails(number);
686 : // Don't bother optimizing stores to read-only properties.
687 22030 : if (details.IsReadOnly()) return {};
688 : // TODO(bmeurer): Handle transition to data constant?
689 22030 : if (details.location() != kField) return {};
690 : int const index = details.field_index();
691 : Representation details_representation = details.representation();
692 : FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index,
693 22030 : details_representation);
694 : Type field_type = Type::NonInternal();
695 : MaybeHandle<Map> field_map;
696 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
697 : MapRef transition_map_ref(broker(), transition_map);
698 22030 : if (details_representation.IsSmi()) {
699 : field_type = Type::SignedSmall();
700 : field_representation = MachineRepresentation::kTaggedSigned;
701 18703 : transition_map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
702 18703 : dependencies()->DependOnFieldRepresentation(transition_map_ref, number);
703 3327 : } else if (details_representation.IsDouble()) {
704 150 : field_type = type_cache_->kFloat64;
705 : field_representation = MachineRepresentation::kFloat64;
706 3177 : } else if (details_representation.IsHeapObject()) {
707 : // Extract the field type from the property details (make sure its
708 : // representation is TaggedPointer to reflect the heap object case).
709 : field_representation = MachineRepresentation::kTaggedPointer;
710 : Handle<FieldType> descriptors_field_type(
711 1588 : transition_map->instance_descriptors()->GetFieldType(number),
712 794 : isolate());
713 794 : if (descriptors_field_type->IsNone()) {
714 : // Store is not safe if the field type was cleared.
715 : return {};
716 : }
717 794 : transition_map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
718 794 : dependencies()->DependOnFieldRepresentation(transition_map_ref, number);
719 794 : if (descriptors_field_type->IsClass()) {
720 132 : dependencies()->DependOnFieldType(transition_map_ref, number);
721 : // Remember the field map, and try to infer a useful type.
722 264 : Handle<Map> map(descriptors_field_type->AsClass(), isolate());
723 132 : field_type = Type::For(MapRef(broker(), map));
724 : field_map = MaybeHandle<Map>(map);
725 : }
726 : }
727 22030 : dependencies()->DependOnTransition(MapRef(broker(), transition_map));
728 : // Transitioning stores are never stores to constant fields.
729 : return PropertyAccessInfo::DataField(
730 : PropertyConstness::kMutable, MapHandles{map}, field_index,
731 44060 : field_representation, field_type, field_map, holder, transition_map);
732 : }
733 :
734 : } // namespace compiler
735 : } // namespace internal
736 121996 : } // namespace v8
|