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/field-index-inl.h"
13 : #include "src/field-type.h"
14 : #include "src/ic/call-optimization.h"
15 : #include "src/objects-inl.h"
16 : #include "src/objects/cell-inl.h"
17 : #include "src/objects/module-inl.h"
18 : #include "src/objects/struct-inl.h"
19 : #include "src/objects/templates.h"
20 :
21 : namespace v8 {
22 : namespace internal {
23 : namespace compiler {
24 :
25 : namespace {
26 :
27 37805 : bool CanInlineElementAccess(Handle<Map> map) {
28 37805 : if (!map->IsJSObjectMap()) return false;
29 36222 : if (map->is_access_check_needed()) return false;
30 36222 : if (map->has_indexed_interceptor()) return false;
31 : ElementsKind const elements_kind = map->elements_kind();
32 36214 : if (IsFastElementsKind(elements_kind)) return true;
33 12066 : if (IsFixedTypedArrayElementsKind(elements_kind) &&
34 12066 : elements_kind != BIGUINT64_ELEMENTS &&
35 : elements_kind != BIGINT64_ELEMENTS) {
36 : return true;
37 : }
38 1606 : return false;
39 : }
40 :
41 :
42 258695 : bool CanInlinePropertyAccess(Handle<Map> map) {
43 : // We can inline property access to prototypes of all primitives, except
44 : // the special Oddball ones that have no wrapper counterparts (i.e. Null,
45 : // Undefined and TheHole).
46 : STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_TYPE);
47 258695 : if (map->IsBooleanMap()) return true;
48 258171 : if (map->instance_type() < LAST_PRIMITIVE_TYPE) return true;
49 737407 : return map->IsJSObjectMap() && !map->is_dictionary_map() &&
50 489937 : !map->has_named_interceptor() &&
51 : // TODO(verwaest): Whitelist contexts to which we have access.
52 : !map->is_access_check_needed();
53 : }
54 :
55 : } // namespace
56 :
57 :
58 0 : std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
59 0 : switch (access_mode) {
60 : case AccessMode::kLoad:
61 0 : return os << "Load";
62 : case AccessMode::kStore:
63 0 : return os << "Store";
64 : case AccessMode::kStoreInLiteral:
65 0 : return os << "StoreInLiteral";
66 : }
67 0 : UNREACHABLE();
68 : }
69 :
70 : ElementAccessInfo::ElementAccessInfo() = default;
71 :
72 22699 : ElementAccessInfo::ElementAccessInfo(MapHandles const& receiver_maps,
73 : ElementsKind elements_kind)
74 22699 : : elements_kind_(elements_kind), receiver_maps_(receiver_maps) {
75 22699 : CHECK(!receiver_maps.empty());
76 22699 : }
77 :
78 : // static
79 0 : PropertyAccessInfo PropertyAccessInfo::NotFound(MapHandles const& receiver_maps,
80 : MaybeHandle<JSObject> holder) {
81 0 : return PropertyAccessInfo(kNotFound, holder, receiver_maps);
82 : }
83 :
84 : // static
85 0 : PropertyAccessInfo PropertyAccessInfo::DataConstant(
86 : MapHandles const& receiver_maps, Handle<Object> constant,
87 : MaybeHandle<JSObject> holder) {
88 0 : return PropertyAccessInfo(kDataConstant, holder, constant, receiver_maps);
89 : }
90 :
91 : // static
92 0 : PropertyAccessInfo PropertyAccessInfo::DataField(
93 : PropertyConstness constness, MapHandles const& receiver_maps,
94 : FieldIndex field_index, MachineRepresentation field_representation,
95 : Type field_type, MaybeHandle<Map> field_map, MaybeHandle<JSObject> holder,
96 : MaybeHandle<Map> transition_map) {
97 : Kind kind =
98 103570 : constness == PropertyConstness::kConst ? kDataConstantField : kDataField;
99 : return PropertyAccessInfo(kind, holder, transition_map, field_index,
100 : field_representation, field_type, field_map,
101 0 : receiver_maps);
102 : }
103 :
104 : // static
105 0 : PropertyAccessInfo PropertyAccessInfo::AccessorConstant(
106 : MapHandles const& receiver_maps, Handle<Object> constant,
107 : MaybeHandle<JSObject> holder) {
108 0 : return PropertyAccessInfo(kAccessorConstant, holder, constant, receiver_maps);
109 : }
110 :
111 : // static
112 0 : PropertyAccessInfo PropertyAccessInfo::ModuleExport(
113 : MapHandles const& receiver_maps, Handle<Cell> cell) {
114 : return PropertyAccessInfo(kModuleExport, MaybeHandle<JSObject>(), cell,
115 0 : receiver_maps);
116 : }
117 :
118 : // static
119 0 : PropertyAccessInfo PropertyAccessInfo::StringLength(
120 : MapHandles const& receiver_maps) {
121 : return PropertyAccessInfo(kStringLength, MaybeHandle<JSObject>(),
122 0 : receiver_maps);
123 : }
124 :
125 2372 : PropertyAccessInfo::PropertyAccessInfo()
126 : : kind_(kInvalid),
127 : field_representation_(MachineRepresentation::kNone),
128 476613 : field_type_(Type::None()) {}
129 :
130 0 : PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
131 : MapHandles const& receiver_maps)
132 : : kind_(kind),
133 : receiver_maps_(receiver_maps),
134 : holder_(holder),
135 : field_representation_(MachineRepresentation::kNone),
136 16461 : field_type_(Type::None()) {}
137 :
138 0 : PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
139 : Handle<Object> constant,
140 : MapHandles const& receiver_maps)
141 : : kind_(kind),
142 : receiver_maps_(receiver_maps),
143 : constant_(constant),
144 : holder_(holder),
145 : field_representation_(MachineRepresentation::kNone),
146 6184 : field_type_(Type::Any()) {}
147 :
148 0 : PropertyAccessInfo::PropertyAccessInfo(
149 : Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
150 : FieldIndex field_index, MachineRepresentation field_representation,
151 : Type field_type, MaybeHandle<Map> field_map,
152 : MapHandles const& receiver_maps)
153 : : kind_(kind),
154 : receiver_maps_(receiver_maps),
155 : transition_map_(transition_map),
156 : holder_(holder),
157 : field_index_(field_index),
158 : field_representation_(field_representation),
159 : field_type_(field_type),
160 268660 : field_map_(field_map) {}
161 :
162 14415 : bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
163 : AccessMode access_mode, Zone* zone) {
164 14415 : if (this->kind_ != that->kind_) return false;
165 13872 : if (this->holder_.address() != that->holder_.address()) return false;
166 :
167 12827 : switch (this->kind_) {
168 : case kInvalid:
169 : break;
170 :
171 : case kDataField:
172 : case kDataConstantField: {
173 : // Check if we actually access the same field (we use the
174 : // GetFieldAccessStubKey method here just like the ICs do
175 : // since that way we only compare the relevant bits of the
176 : // field indices).
177 12335 : if (this->field_index_.GetFieldAccessStubKey() ==
178 : that->field_index_.GetFieldAccessStubKey()) {
179 11533 : switch (access_mode) {
180 : case AccessMode::kLoad: {
181 6968 : if (this->field_representation_ != that->field_representation_) {
182 690 : if (!IsAnyTagged(this->field_representation_) ||
183 : !IsAnyTagged(that->field_representation_)) {
184 : return false;
185 : }
186 345 : this->field_representation_ = MachineRepresentation::kTagged;
187 : }
188 6968 : if (this->field_map_.address() != that->field_map_.address()) {
189 233 : this->field_map_ = MaybeHandle<Map>();
190 : }
191 : break;
192 : }
193 : case AccessMode::kStore:
194 : case AccessMode::kStoreInLiteral: {
195 : // For stores, the field map and field representation information
196 : // must match exactly, otherwise we cannot merge the stores. We
197 : // also need to make sure that in case of transitioning stores,
198 : // the transition targets match.
199 9082 : if (this->field_map_.address() != that->field_map_.address() ||
200 8974 : this->field_representation_ != that->field_representation_ ||
201 : this->transition_map_.address() !=
202 : that->transition_map_.address()) {
203 : return false;
204 : }
205 : break;
206 : }
207 : }
208 : // Merge the field type.
209 : this->field_type_ =
210 7201 : Type::Union(this->field_type_, that->field_type_, zone);
211 : // Merge the receiver maps.
212 : this->receiver_maps_.insert(this->receiver_maps_.end(),
213 : that->receiver_maps_.begin(),
214 14887 : that->receiver_maps_.end());
215 7201 : return true;
216 : }
217 : return false;
218 : }
219 :
220 : case kDataConstant:
221 : case kAccessorConstant: {
222 : // Check if we actually access the same constant.
223 288 : if (this->constant_.address() == that->constant_.address()) {
224 : this->receiver_maps_.insert(this->receiver_maps_.end(),
225 : that->receiver_maps_.begin(),
226 281 : that->receiver_maps_.end());
227 281 : return true;
228 : }
229 : return false;
230 : }
231 :
232 : case kNotFound:
233 : case kStringLength: {
234 : this->receiver_maps_.insert(this->receiver_maps_.end(),
235 : that->receiver_maps_.begin(),
236 204 : that->receiver_maps_.end());
237 204 : return true;
238 : }
239 : case kModuleExport: {
240 : return false;
241 : }
242 : }
243 :
244 0 : UNREACHABLE();
245 : }
246 :
247 7 : Handle<Cell> PropertyAccessInfo::export_cell() const {
248 : DCHECK_EQ(kModuleExport, kind_);
249 7 : return Handle<Cell>::cast(constant_);
250 : }
251 :
252 167109 : AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
253 : CompilationDependencies* dependencies,
254 : Handle<Context> native_context, Zone* zone)
255 : : broker_(broker),
256 : dependencies_(dependencies),
257 : native_context_(native_context),
258 : isolate_(native_context->GetIsolate()),
259 167109 : type_cache_(TypeCache::Get()),
260 501327 : zone_(zone) {
261 : DCHECK(native_context->IsNativeContext());
262 167109 : }
263 :
264 10183 : bool AccessInfoFactory::ComputeElementAccessInfo(
265 : Handle<Map> map, AccessMode access_mode,
266 : ElementAccessInfo* access_info) const {
267 : // Check if it is safe to inline element access for the {map}.
268 10183 : if (!CanInlineElementAccess(map)) return false;
269 : ElementsKind const elements_kind = map->elements_kind();
270 27459 : *access_info = ElementAccessInfo(MapHandles{map}, elements_kind);
271 9153 : return true;
272 : }
273 :
274 : typedef std::vector<std::pair<Handle<Map>, Handle<Map>>> TransitionList;
275 :
276 : namespace {
277 18952 : void ProcessFeedbackMaps(Isolate* isolate, MapHandles const& maps,
278 : MapHandles* receiver_maps,
279 : TransitionList* transitions) {
280 : DCHECK(receiver_maps->empty());
281 : DCHECK(transitions->empty());
282 :
283 : // Collect possible transition targets.
284 : MapHandles possible_transition_targets;
285 9476 : possible_transition_targets.reserve(maps.size());
286 29995 : for (Handle<Map> map : maps) {
287 31866 : if (CanInlineElementAccess(map) &&
288 17294 : IsFastElementsKind(map->elements_kind()) &&
289 : GetInitialFastElementsKind() != map->elements_kind()) {
290 4903 : possible_transition_targets.push_back(map);
291 : }
292 : }
293 :
294 : // Separate the actual receiver maps and the possible transition sources.
295 29995 : for (Handle<Map> map : maps) {
296 : // Don't generate elements kind transitions from stable maps.
297 22086 : Map transition_target = map->is_stable()
298 : ? Map()
299 : : map->FindElementsKindTransitionedMap(
300 16954 : isolate, possible_transition_targets);
301 11043 : if (transition_target.is_null()) {
302 10470 : receiver_maps->push_back(map);
303 : } else {
304 573 : transitions->emplace_back(map, handle(transition_target, isolate));
305 : }
306 : }
307 9476 : }
308 : } // namespace
309 :
310 23022 : bool AccessInfoFactory::ComputeElementAccessInfos(
311 : MapHandles const& maps, AccessMode access_mode,
312 9476 : ZoneVector<ElementAccessInfo>* access_infos) const {
313 23022 : if (access_mode == AccessMode::kLoad) {
314 : // For polymorphic loads of similar elements kinds (i.e. all tagged or all
315 : // double), always use the "worst case" code without a transition. This is
316 : // much faster than transitioning the elements to the worst case, trading a
317 : // TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
318 : ElementAccessInfo access_info;
319 14779 : if (ConsolidateElementLoad(maps, &access_info)) {
320 13546 : access_infos->push_back(access_info);
321 13546 : return true;
322 1233 : }
323 : }
324 :
325 : MapHandles receiver_maps;
326 : TransitionList transitions;
327 9476 : ProcessFeedbackMaps(isolate(), maps, &receiver_maps, &transitions);
328 :
329 28105 : for (Handle<Map> receiver_map : receiver_maps) {
330 : // Compute the element access information.
331 : ElementAccessInfo access_info;
332 10183 : if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
333 1030 : return false;
334 : }
335 :
336 : // Collect the possible transitions for the {receiver_map}.
337 18931 : for (auto transition : transitions) {
338 625 : if (transition.second.is_identical_to(receiver_map)) {
339 545 : access_info.AddTransitionSource(transition.first);
340 : }
341 : }
342 :
343 : // Schedule the access information.
344 9153 : access_infos->push_back(access_info);
345 9153 : }
346 8446 : return true;
347 : }
348 :
349 103570 : bool AccessInfoFactory::ComputeDataFieldAccessInfo(
350 : Handle<Map> receiver_map, Handle<Map> map, MaybeHandle<JSObject> holder,
351 195812 : int number, AccessMode access_mode, PropertyAccessInfo* access_info) const {
352 : DCHECK_NE(number, DescriptorArray::kNotFound);
353 207140 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
354 103570 : PropertyDetails const details = descriptors->GetDetails(number);
355 207140 : int index = descriptors->GetFieldIndex(number);
356 : Representation details_representation = details.representation();
357 : FieldIndex field_index =
358 103570 : FieldIndex::ForPropertyIndex(*map, index, details_representation);
359 : Type field_type = Type::NonInternal();
360 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
361 : MaybeHandle<Map> field_map;
362 103570 : if (details_representation.IsSmi()) {
363 : field_type = Type::SignedSmall();
364 : field_representation = MachineRepresentation::kTaggedSigned;
365 96461 : } else if (details_representation.IsDouble()) {
366 897 : field_type = type_cache_->kFloat64;
367 : field_representation = MachineRepresentation::kFloat64;
368 95564 : } else if (details_representation.IsHeapObject()) {
369 : // Extract the field type from the property details (make sure its
370 : // representation is TaggedPointer to reflect the heap object case).
371 : field_representation = MachineRepresentation::kTaggedPointer;
372 : Handle<FieldType> descriptors_field_type(descriptors->GetFieldType(number),
373 165572 : isolate());
374 82786 : if (descriptors_field_type->IsNone()) {
375 : // Store is not safe if the field type was cleared.
376 7 : if (access_mode == AccessMode::kStore) return false;
377 :
378 : // The field type was cleared by the GC, so we don't know anything
379 : // about the contents now.
380 82779 : } else if (descriptors_field_type->IsClass()) {
381 : MapRef map_ref(broker(), map);
382 2364 : map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
383 2364 : dependencies()->DependOnFieldType(map_ref, number);
384 : // Remember the field map, and try to infer a useful type.
385 4728 : Handle<Map> map(descriptors_field_type->AsClass(), isolate());
386 2364 : field_type = Type::For(MapRef(broker(), map));
387 : field_map = MaybeHandle<Map>(map);
388 : }
389 : }
390 310710 : *access_info = PropertyAccessInfo::DataField(
391 : details.constness(), MapHandles{receiver_map}, field_index,
392 103570 : field_representation, field_type, field_map, holder);
393 103570 : return true;
394 : }
395 :
396 7519 : bool AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
397 : Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map,
398 : MaybeHandle<JSObject> holder, int number, AccessMode access_mode,
399 26938 : PropertyAccessInfo* access_info) const {
400 : DCHECK_NE(number, DescriptorArray::kNotFound);
401 15038 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
402 : SLOW_DCHECK(number == descriptors->Search(*name, *map));
403 7519 : if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
404 : DCHECK(map->is_prototype_map());
405 : Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(map->prototype_info()),
406 : isolate());
407 : Handle<JSModuleNamespace> module_namespace(
408 : JSModuleNamespace::cast(proto_info->module_namespace()), isolate());
409 : Handle<Cell> cell(
410 14 : Cell::cast(module_namespace->module()->exports()->Lookup(
411 14 : ReadOnlyRoots(isolate()), name, Smi::ToInt(name->GetHash()))),
412 21 : isolate());
413 14 : if (cell->value()->IsTheHole(isolate())) {
414 : // This module has not been fully initialized yet.
415 : return false;
416 : }
417 21 : *access_info =
418 7 : PropertyAccessInfo::ModuleExport(MapHandles{receiver_map}, cell);
419 7 : return true;
420 : }
421 15024 : Handle<Object> accessors(descriptors->GetStrongValue(number), isolate());
422 15024 : if (!accessors->IsAccessorPair()) return false;
423 : Handle<Object> accessor(access_mode == AccessMode::kLoad
424 10822 : ? Handle<AccessorPair>::cast(accessors)->getter()
425 10329 : : Handle<AccessorPair>::cast(accessors)->setter(),
426 10329 : isolate());
427 12592 : if (!accessor->IsJSFunction()) {
428 3339 : CallOptimization optimization(isolate(), accessor);
429 6544 : if (!optimization.is_simple_api_call()) return false;
430 172 : if (optimization.IsCrossContextLazyAccessorPair(*native_context_, *map)) {
431 : return false;
432 : }
433 :
434 : CallOptimization::HolderLookup lookup;
435 166 : holder = optimization.LookupHolderOfExpectedType(receiver_map, &lookup);
436 166 : if (lookup == CallOptimization::kHolderNotFound) return false;
437 : DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
438 : holder.is_null());
439 : DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound, !holder.is_null());
440 134 : if (V8_UNLIKELY(FLAG_runtime_stats)) return false;
441 : }
442 3091 : if (access_mode == AccessMode::kLoad) {
443 : Handle<Name> cached_property_name;
444 2237 : if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), accessor)
445 4474 : .ToHandle(&cached_property_name)) {
446 6 : if (ComputePropertyAccessInfo(map, cached_property_name, access_mode,
447 : access_info)) {
448 6 : return true;
449 : }
450 : }
451 : }
452 9255 : *access_info = PropertyAccessInfo::AccessorConstant(MapHandles{receiver_map},
453 3085 : accessor, holder);
454 3085 : return true;
455 : }
456 :
457 157712 : bool AccessInfoFactory::ComputePropertyAccessInfo(
458 : Handle<Map> map, Handle<Name> name, AccessMode access_mode,
459 473088 : PropertyAccessInfo* access_info) const {
460 157712 : CHECK(name->IsUniqueName());
461 :
462 : // Check if it is safe to inline property access for the {map}.
463 157712 : if (!CanInlinePropertyAccess(map)) return false;
464 :
465 : // We support fast inline cases for certain JSObject getters.
466 268656 : if (access_mode == AccessMode::kLoad &&
467 116371 : LookupSpecialFieldAccessor(map, name, access_info)) {
468 : return true;
469 : }
470 :
471 : // Remember the receiver map. We use {map} as loop variable.
472 : Handle<Map> receiver_map = map;
473 : MaybeHandle<JSObject> holder;
474 100983 : do {
475 : // Lookup the named property on the {map}.
476 480806 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
477 480806 : int const number = descriptors->Search(*name, *map);
478 240403 : if (number != DescriptorArray::kNotFound) {
479 116588 : PropertyDetails const details = descriptors->GetDetails(number);
480 116588 : if (access_mode == AccessMode::kStore ||
481 : access_mode == AccessMode::kStoreInLiteral) {
482 : // Don't bother optimizing stores to read-only properties.
483 15112 : if (details.IsReadOnly()) {
484 : return false;
485 : }
486 11931 : if (details.kind() == kData && !holder.is_null()) {
487 : // This is a store to a property not found on the receiver but on a
488 : // prototype. According to ES6 section 9.1.9 [[Set]], we need to
489 : // create a new data property on the receiver. We can still optimize
490 : // if such a transition already exists.
491 2318 : return LookupTransition(receiver_map, name, holder, access_info);
492 : }
493 : }
494 111089 : if (details.location() == kField) {
495 103570 : if (details.kind() == kData) {
496 : return ComputeDataFieldAccessInfo(receiver_map, map, holder, number,
497 103570 : access_mode, access_info);
498 : } else {
499 : DCHECK_EQ(kAccessor, details.kind());
500 : // TODO(turbofan): Add support for general accessors?
501 : return false;
502 : }
503 : } else {
504 : DCHECK_EQ(kDescriptor, details.location());
505 7519 : if (details.kind() == kData) {
506 : DCHECK(!FLAG_track_constant_fields);
507 0 : *access_info = PropertyAccessInfo::DataConstant(
508 : MapHandles{receiver_map},
509 0 : handle(descriptors->GetStrongValue(number), isolate()), holder);
510 0 : return true;
511 : } else {
512 : DCHECK_EQ(kAccessor, details.kind());
513 : return ComputeAccessorDescriptorAccessInfo(receiver_map, name, map,
514 : holder, number,
515 7519 : access_mode, access_info);
516 : }
517 : }
518 : UNREACHABLE();
519 : }
520 :
521 : // The property wasn't found on {map}. Look on the prototype if appropriate.
522 :
523 : // Don't search on the prototype chain for special indices in case of
524 : // integer indexed exotic objects (see ES6 section 9.4.5).
525 126597 : if (map->IsJSTypedArrayMap() && name->IsString() &&
526 900 : IsSpecialIndex(String::cast(*name))) {
527 : return false;
528 : }
529 :
530 : // Don't search on the prototype when storing in literals.
531 123808 : if (access_mode == AccessMode::kStoreInLiteral) {
532 110 : return LookupTransition(receiver_map, name, holder, access_info);
533 : }
534 :
535 : // Don't lookup private symbols on the prototype chain.
536 123698 : if (name->IsPrivate()) return false;
537 :
538 : // Walk up the prototype chain.
539 247394 : if (!map->prototype()->IsJSObject()) {
540 : // Perform the implicit ToObject for primitives here.
541 : // Implemented according to ES6 section 7.3.2 GetV (V, P).
542 : Handle<JSFunction> constructor;
543 30719 : if (Map::GetConstructorFunction(map, native_context())
544 61438 : .ToHandle(&constructor)) {
545 16010 : map = handle(constructor->initial_map(), isolate());
546 : DCHECK(map->prototype()->IsJSObject());
547 45428 : } else if (map->prototype()->IsNull(isolate())) {
548 : // Store to property not found on the receiver or any prototype, we need
549 : // to transition to a new data property.
550 : // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
551 22707 : if (access_mode == AccessMode::kStore) {
552 19377 : return LookupTransition(receiver_map, name, holder, access_info);
553 : }
554 : // The property was not found, return undefined or throw depending
555 : // on the language mode of the load operation.
556 : // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
557 9990 : *access_info =
558 3330 : PropertyAccessInfo::NotFound(MapHandles{receiver_map}, holder);
559 3330 : return true;
560 : } else {
561 : return false;
562 : }
563 : }
564 : Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
565 100983 : if (map_prototype->map()->is_deprecated()) {
566 : // Try to migrate the prototype object so we don't embed the deprecated
567 : // map into the optimized code.
568 0 : JSObject::TryMigrateInstance(map_prototype);
569 : }
570 : map = handle(map_prototype->map(), isolate());
571 : holder = map_prototype;
572 : } while (CanInlinePropertyAccess(map));
573 : return false;
574 : }
575 :
576 1165 : bool AccessInfoFactory::ComputePropertyAccessInfo(
577 : MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
578 1165 : PropertyAccessInfo* access_info) const {
579 : ZoneVector<PropertyAccessInfo> access_infos(zone());
580 1685 : if (ComputePropertyAccessInfos(maps, name, access_mode, &access_infos) &&
581 520 : access_infos.size() == 1) {
582 520 : *access_info = access_infos.front();
583 520 : return true;
584 : }
585 : return false;
586 : }
587 :
588 142880 : bool AccessInfoFactory::ComputePropertyAccessInfos(
589 142880 : MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
590 157295 : ZoneVector<PropertyAccessInfo>* access_infos) const {
591 : ZoneVector<PropertyAccessInfo> infos(zone());
592 142880 : infos.reserve(maps.size());
593 427509 : for (Handle<Map> map : maps) {
594 : PropertyAccessInfo access_info;
595 156499 : if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) {
596 : return false;
597 : }
598 141749 : infos.push_back(access_info);
599 : }
600 :
601 : // Merge as many as possible and push into {access_infos}.
602 397906 : for (auto it = infos.begin(), end = infos.end(); it != end; ++it) {
603 : bool merged = false;
604 290021 : for (auto ot = it + 1; ot != end; ++ot) {
605 14415 : if (ot->Merge(&(*it), access_mode, zone())) {
606 : merged = true;
607 : break;
608 : }
609 : }
610 141646 : if (!merged) access_infos->push_back(*it);
611 : }
612 : return true;
613 : }
614 :
615 : namespace {
616 :
617 15527 : Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
618 : ElementsKind that_kind) {
619 15527 : if (IsHoleyElementsKind(this_kind)) {
620 : that_kind = GetHoleyElementsKind(that_kind);
621 12224 : } else if (IsHoleyElementsKind(that_kind)) {
622 : this_kind = GetHoleyElementsKind(this_kind);
623 : }
624 15527 : if (this_kind == that_kind) return Just(this_kind);
625 790 : if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
626 718 : if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
627 : return Just(this_kind);
628 : }
629 109 : if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
630 : return Just(that_kind);
631 : }
632 : }
633 : return Nothing<ElementsKind>();
634 : }
635 :
636 : } // namespace
637 :
638 14779 : bool AccessInfoFactory::ConsolidateElementLoad(
639 : MapHandles const& maps, ElementAccessInfo* access_info) const {
640 14779 : CHECK(!maps.empty());
641 : InstanceType instance_type = maps.front()->instance_type();
642 : ElementsKind elements_kind = maps.front()->elements_kind();
643 44904 : for (Handle<Map> map : maps) {
644 32254 : if (!CanInlineElementAccess(map) || map->instance_type() != instance_type) {
645 : return false;
646 : }
647 31054 : if (!GeneralizeElementsKind(elements_kind, map->elements_kind())
648 31054 : .To(&elements_kind)) {
649 : return false;
650 : }
651 : }
652 13546 : *access_info = ElementAccessInfo(maps, elements_kind);
653 13546 : return true;
654 : }
655 :
656 116371 : bool AccessInfoFactory::LookupSpecialFieldAccessor(
657 108196 : Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) const {
658 : // Check for String::length field accessor.
659 116371 : if (map->IsStringMap()) {
660 8175 : if (Name::Equals(isolate(), name, factory()->length_string())) {
661 8628 : *access_info = PropertyAccessInfo::StringLength(MapHandles{map});
662 2157 : return true;
663 : }
664 : return false;
665 : }
666 : // Check for special JSObject field accessors.
667 : FieldIndex field_index;
668 108196 : if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
669 : Type field_type = Type::NonInternal();
670 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
671 8989 : if (map->IsJSArrayMap()) {
672 : DCHECK(Name::Equals(isolate(), factory()->length_string(), name));
673 : // The JSArray::length property is a smi in the range
674 : // [0, FixedDoubleArray::kMaxLength] in case of fast double
675 : // elements, a smi in the range [0, FixedArray::kMaxLength]
676 : // in case of other fast elements, and [0, kMaxUInt32] in
677 : // case of other arrays.
678 8989 : if (IsDoubleElementsKind(map->elements_kind())) {
679 290 : field_type = type_cache_->kFixedDoubleArrayLengthType;
680 : field_representation = MachineRepresentation::kTaggedSigned;
681 8699 : } else if (IsFastElementsKind(map->elements_kind())) {
682 8456 : field_type = type_cache_->kFixedArrayLengthType;
683 : field_representation = MachineRepresentation::kTaggedSigned;
684 : } else {
685 243 : field_type = type_cache_->kJSArrayLengthType;
686 : }
687 : }
688 : // Special fields are always mutable.
689 26967 : *access_info = PropertyAccessInfo::DataField(
690 : PropertyConstness::kMutable, MapHandles{map}, field_index,
691 8989 : field_representation, field_type);
692 : return true;
693 : }
694 : return false;
695 : }
696 :
697 21805 : bool AccessInfoFactory::LookupTransition(
698 : Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder,
699 88472 : PropertyAccessInfo* access_info) const {
700 : // Check if the {map} has a data transition with the given {name}.
701 : Map transition =
702 21805 : TransitionsAccessor(isolate(), map).SearchTransition(*name, kData, NONE);
703 21805 : if (transition.is_null()) return false;
704 :
705 : Handle<Map> transition_map(transition, isolate());
706 21771 : int const number = transition_map->LastAdded();
707 : PropertyDetails const details =
708 21771 : transition_map->instance_descriptors()->GetDetails(number);
709 : // Don't bother optimizing stores to read-only properties.
710 21771 : if (details.IsReadOnly()) return false;
711 : // TODO(bmeurer): Handle transition to data constant?
712 21771 : if (details.location() != kField) return false;
713 : int const index = details.field_index();
714 : Representation details_representation = details.representation();
715 : FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index,
716 21771 : details_representation);
717 : Type field_type = Type::NonInternal();
718 : MaybeHandle<Map> field_map;
719 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
720 21771 : if (details_representation.IsSmi()) {
721 : field_type = Type::SignedSmall();
722 : field_representation = MachineRepresentation::kTaggedSigned;
723 3388 : } else if (details_representation.IsDouble()) {
724 147 : field_type = type_cache_->kFloat64;
725 : field_representation = MachineRepresentation::kFloat64;
726 3241 : } else if (details_representation.IsHeapObject()) {
727 : // Extract the field type from the property details (make sure its
728 : // representation is TaggedPointer to reflect the heap object case).
729 : field_representation = MachineRepresentation::kTaggedPointer;
730 : Handle<FieldType> descriptors_field_type(
731 1708 : transition_map->instance_descriptors()->GetFieldType(number),
732 1708 : isolate());
733 854 : if (descriptors_field_type->IsNone()) {
734 : // Store is not safe if the field type was cleared.
735 : return false;
736 854 : } else if (descriptors_field_type->IsClass()) {
737 : MapRef transition_map_ref(broker(), transition_map);
738 : transition_map_ref
739 125 : .SerializeOwnDescriptors(); // TODO(neis): Remove later.
740 125 : dependencies()->DependOnFieldType(transition_map_ref, number);
741 : // Remember the field map, and try to infer a useful type.
742 250 : Handle<Map> map(descriptors_field_type->AsClass(), isolate());
743 125 : field_type = Type::For(MapRef(broker(), map));
744 : field_map = MaybeHandle<Map>(map);
745 : }
746 : }
747 21771 : dependencies()->DependOnTransition(MapRef(broker(), transition_map));
748 : // Transitioning stores are never stores to constant fields.
749 65313 : *access_info = PropertyAccessInfo::DataField(
750 : PropertyConstness::kMutable, MapHandles{map}, field_index,
751 21771 : field_representation, field_type, field_map, holder, transition_map);
752 21771 : return true;
753 : }
754 :
755 8175 : Factory* AccessInfoFactory::factory() const { return isolate()->factory(); }
756 :
757 : } // namespace compiler
758 : } // namespace internal
759 178779 : } // namespace v8
|