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 37661 : bool CanInlineElementAccess(Handle<Map> map) {
28 37661 : if (!map->IsJSObjectMap()) return false;
29 36053 : if (map->is_access_check_needed()) return false;
30 36053 : if (map->has_indexed_interceptor()) return false;
31 : ElementsKind const elements_kind = map->elements_kind();
32 36043 : if (IsFastElementsKind(elements_kind)) return true;
33 12086 : if (IsFixedTypedArrayElementsKind(elements_kind) &&
34 12086 : elements_kind != BIGUINT64_ELEMENTS &&
35 : elements_kind != BIGINT64_ELEMENTS) {
36 : return true;
37 : }
38 1628 : return false;
39 : }
40 :
41 :
42 252561 : 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 252561 : if (map->IsBooleanMap()) return true;
48 252031 : if (map->instance_type() < LAST_PRIMITIVE_TYPE) return true;
49 715574 : return map->IsJSObjectMap() && !map->is_dictionary_map() &&
50 474696 : !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 0 : ElementAccessInfo::ElementAccessInfo(MapHandles const& receiver_maps,
73 : ElementsKind elements_kind)
74 22614 : : elements_kind_(elements_kind), receiver_maps_(receiver_maps) {}
75 :
76 : // static
77 0 : PropertyAccessInfo PropertyAccessInfo::NotFound(MapHandles const& receiver_maps,
78 : MaybeHandle<JSObject> holder) {
79 0 : return PropertyAccessInfo(kNotFound, holder, receiver_maps);
80 : }
81 :
82 : // static
83 0 : PropertyAccessInfo PropertyAccessInfo::DataConstant(
84 : MapHandles const& receiver_maps, Handle<Object> constant,
85 : MaybeHandle<JSObject> holder) {
86 0 : return PropertyAccessInfo(kDataConstant, holder, constant, receiver_maps);
87 : }
88 :
89 : // static
90 0 : PropertyAccessInfo PropertyAccessInfo::DataField(
91 : PropertyConstness constness, MapHandles const& receiver_maps,
92 : FieldIndex field_index, MachineRepresentation field_representation,
93 : Type field_type, MaybeHandle<Map> field_map, MaybeHandle<JSObject> holder,
94 : MaybeHandle<Map> transition_map) {
95 : Kind kind =
96 26705 : constness == PropertyConstness::kConst ? kDataConstantField : kDataField;
97 : return PropertyAccessInfo(kind, holder, transition_map, field_index,
98 : field_representation, field_type, field_map,
99 0 : receiver_maps);
100 : }
101 :
102 : // static
103 0 : PropertyAccessInfo PropertyAccessInfo::AccessorConstant(
104 : MapHandles const& receiver_maps, Handle<Object> constant,
105 : MaybeHandle<JSObject> holder) {
106 0 : return PropertyAccessInfo(kAccessorConstant, holder, constant, receiver_maps);
107 : }
108 :
109 : // static
110 0 : PropertyAccessInfo PropertyAccessInfo::ModuleExport(
111 : MapHandles const& receiver_maps, Handle<Cell> cell) {
112 : return PropertyAccessInfo(kModuleExport, MaybeHandle<JSObject>(), cell,
113 0 : receiver_maps);
114 : }
115 :
116 : // static
117 0 : PropertyAccessInfo PropertyAccessInfo::StringLength(
118 : MapHandles const& receiver_maps) {
119 : return PropertyAccessInfo(kStringLength, MaybeHandle<JSObject>(),
120 0 : receiver_maps);
121 : }
122 :
123 2961 : PropertyAccessInfo::PropertyAccessInfo()
124 : : kind_(kInvalid),
125 : field_representation_(MachineRepresentation::kNone),
126 475869 : field_type_(Type::None()) {}
127 :
128 0 : PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
129 : MapHandles const& receiver_maps)
130 : : kind_(kind),
131 : receiver_maps_(receiver_maps),
132 : holder_(holder),
133 : field_representation_(MachineRepresentation::kNone),
134 17760 : field_type_(Type::None()) {}
135 :
136 0 : PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
137 : Handle<Object> constant,
138 : MapHandles const& receiver_maps)
139 : : kind_(kind),
140 : receiver_maps_(receiver_maps),
141 : constant_(constant),
142 : holder_(holder),
143 : field_representation_(MachineRepresentation::kNone),
144 162718 : field_type_(Type::Any()) {}
145 :
146 0 : PropertyAccessInfo::PropertyAccessInfo(
147 : Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
148 : FieldIndex field_index, MachineRepresentation field_representation,
149 : Type field_type, MaybeHandle<Map> field_map,
150 : MapHandles const& receiver_maps)
151 : : kind_(kind),
152 : receiver_maps_(receiver_maps),
153 : transition_map_(transition_map),
154 : holder_(holder),
155 : field_index_(field_index),
156 : field_representation_(field_representation),
157 : field_type_(field_type),
158 106181 : field_map_(field_map) {}
159 :
160 12205 : bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
161 : AccessMode access_mode, Zone* zone) {
162 12205 : if (this->kind_ != that->kind_) return false;
163 11863 : if (this->holder_.address() != that->holder_.address()) return false;
164 :
165 10582 : switch (this->kind_) {
166 : case kInvalid:
167 : break;
168 :
169 : case kDataField:
170 : case kDataConstantField: {
171 : // Check if we actually access the same field (we use the
172 : // GetFieldAccessStubKey method here just like the ICs do
173 : // since that way we only compare the relevant bits of the
174 : // field indices).
175 7836 : if (this->field_index_.GetFieldAccessStubKey() ==
176 : that->field_index_.GetFieldAccessStubKey()) {
177 7005 : switch (access_mode) {
178 : case AccessMode::kLoad: {
179 4418 : if (this->field_representation_ != that->field_representation_) {
180 746 : if (!IsAnyTagged(this->field_representation_) ||
181 : !IsAnyTagged(that->field_representation_)) {
182 : return false;
183 : }
184 373 : this->field_representation_ = MachineRepresentation::kTagged;
185 : }
186 4418 : if (this->field_map_.address() != that->field_map_.address()) {
187 244 : this->field_map_ = MaybeHandle<Map>();
188 : }
189 : break;
190 : }
191 : case AccessMode::kStore:
192 : case AccessMode::kStoreInLiteral: {
193 : // For stores, the field map and field representation information
194 : // must match exactly, otherwise we cannot merge the stores. We
195 : // also need to make sure that in case of transitioning stores,
196 : // the transition targets match.
197 5126 : if (this->field_map_.address() != that->field_map_.address() ||
198 5006 : this->field_representation_ != that->field_representation_ ||
199 : this->transition_map_.address() !=
200 : that->transition_map_.address()) {
201 : return false;
202 : }
203 : break;
204 : }
205 : }
206 : // Merge the field type.
207 : this->field_type_ =
208 4677 : Type::Union(this->field_type_, that->field_type_, zone);
209 : // Merge the receiver maps.
210 : this->receiver_maps_.insert(this->receiver_maps_.end(),
211 : that->receiver_maps_.begin(),
212 11891 : that->receiver_maps_.end());
213 4677 : return true;
214 : }
215 : return false;
216 : }
217 :
218 : case kDataConstant:
219 : case kAccessorConstant: {
220 : // Check if we actually access the same constant.
221 2540 : if (this->constant_.address() == that->constant_.address()) {
222 : this->receiver_maps_.insert(this->receiver_maps_.end(),
223 : that->receiver_maps_.begin(),
224 2331 : that->receiver_maps_.end());
225 2331 : return true;
226 : }
227 : return false;
228 : }
229 :
230 : case kNotFound:
231 : case kStringLength: {
232 : this->receiver_maps_.insert(this->receiver_maps_.end(),
233 : that->receiver_maps_.begin(),
234 206 : that->receiver_maps_.end());
235 206 : return true;
236 : }
237 : case kModuleExport: {
238 : return false;
239 : }
240 : }
241 :
242 0 : UNREACHABLE();
243 : }
244 :
245 7 : Handle<Cell> PropertyAccessInfo::export_cell() const {
246 : DCHECK_EQ(kModuleExport, kind_);
247 7 : return Handle<Cell>::cast(constant_);
248 : }
249 :
250 169032 : AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
251 : CompilationDependencies* dependencies,
252 : Handle<Context> native_context, Zone* zone)
253 : : broker_(broker),
254 : dependencies_(dependencies),
255 : native_context_(native_context),
256 : isolate_(native_context->GetIsolate()),
257 169032 : type_cache_(TypeCache::Get()),
258 507096 : zone_(zone) {
259 : DCHECK(native_context->IsNativeContext());
260 169032 : }
261 :
262 :
263 10412 : bool AccessInfoFactory::ComputeElementAccessInfo(
264 : Handle<Map> map, AccessMode access_mode, ElementAccessInfo* access_info) {
265 : // Check if it is safe to inline element access for the {map}.
266 10412 : if (!CanInlineElementAccess(map)) return false;
267 : ElementsKind const elements_kind = map->elements_kind();
268 28095 : *access_info = ElementAccessInfo(MapHandles{map}, elements_kind);
269 9365 : return true;
270 : }
271 :
272 22967 : bool AccessInfoFactory::ComputeElementAccessInfos(
273 29154 : MapHandles const& maps, AccessMode access_mode,
274 29162 : ZoneVector<ElementAccessInfo>* access_infos) {
275 22967 : if (access_mode == AccessMode::kLoad) {
276 : // For polymorphic loads of similar elements kinds (i.e. all tagged or all
277 : // double), always use the "worst case" code without a transition. This is
278 : // much faster than transitioning the elements to the worst case, trading a
279 : // TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
280 : ElementAccessInfo access_info;
281 14473 : if (ConsolidateElementLoad(maps, &access_info)) {
282 13249 : access_infos->push_back(access_info);
283 13249 : return true;
284 1224 : }
285 : }
286 :
287 : // Collect possible transition targets.
288 : MapHandles possible_transition_targets;
289 9718 : possible_transition_targets.reserve(maps.size());
290 30702 : for (Handle<Map> map : maps) {
291 22532 : if (Map::TryUpdate(isolate(), map).ToHandle(&map)) {
292 32515 : if (CanInlineElementAccess(map) &&
293 17715 : IsFastElementsKind(map->elements_kind()) &&
294 : GetInitialFastElementsKind() != map->elements_kind()) {
295 5149 : possible_transition_targets.push_back(map);
296 : }
297 : }
298 : }
299 :
300 : // Separate the actual receiver maps and the possible transition sources.
301 : MapHandles receiver_maps;
302 9718 : receiver_maps.reserve(maps.size());
303 9718 : MapTransitionList transitions(maps.size());
304 30702 : for (Handle<Map> map : maps) {
305 22532 : if (Map::TryUpdate(isolate(), map).ToHandle(&map)) {
306 : // Don't generate elements kind transitions from stable maps.
307 22532 : Map transition_target = map->is_stable()
308 : ? Map()
309 : : map->FindElementsKindTransitionedMap(
310 17330 : isolate(), possible_transition_targets);
311 11266 : if (transition_target.is_null()) {
312 10700 : receiver_maps.push_back(map);
313 : } else {
314 : transitions.push_back(
315 1132 : std::make_pair(map, handle(transition_target, isolate())));
316 : }
317 : }
318 : }
319 :
320 28801 : for (Handle<Map> receiver_map : receiver_maps) {
321 : // Compute the element access information.
322 : ElementAccessInfo access_info;
323 10412 : if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
324 1047 : return false;
325 : }
326 :
327 : // Collect the possible transitions for the {receiver_map}.
328 31154 : for (auto transition : transitions) {
329 12424 : if (transition.second.is_identical_to(receiver_map)) {
330 538 : access_info.transitions().push_back(transition);
331 : }
332 : }
333 :
334 : // Schedule the access information.
335 9365 : access_infos->push_back(access_info);
336 9365 : }
337 8671 : return true;
338 : }
339 :
340 :
341 157438 : bool AccessInfoFactory::ComputePropertyAccessInfo(
342 : Handle<Map> map, Handle<Name> name, AccessMode access_mode,
343 565761 : PropertyAccessInfo* access_info) {
344 157438 : CHECK(name->IsUniqueName());
345 :
346 : // Check if it is safe to inline property access for the {map}.
347 157438 : if (!CanInlinePropertyAccess(map)) return false;
348 :
349 : // Compute the receiver type.
350 : Handle<Map> receiver_map = map;
351 :
352 : // We support fast inline cases for certain JSObject getters.
353 267460 : if (access_mode == AccessMode::kLoad &&
354 117530 : LookupSpecialFieldAccessor(map, name, access_info)) {
355 : return true;
356 : }
357 :
358 : MaybeHandle<JSObject> holder;
359 95123 : do {
360 : // Lookup the named property on the {map}.
361 465527 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
362 465528 : int const number = descriptors->Search(*name, *map);
363 232764 : if (number != DescriptorArray::kNotFound) {
364 118179 : PropertyDetails const details = descriptors->GetDetails(number);
365 118179 : if (access_mode == AccessMode::kStore ||
366 : access_mode == AccessMode::kStoreInLiteral) {
367 : // Don't bother optimizing stores to read-only properties.
368 15466 : if (details.IsReadOnly()) {
369 : return false;
370 : }
371 : // Check for store to data property on a prototype.
372 12207 : if (details.kind() == kData && !holder.is_null()) {
373 : // Store to property not found on the receiver but on a prototype, we
374 : // need to transition to a new data property.
375 : // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
376 2357 : return LookupTransition(receiver_map, name, holder, access_info);
377 : }
378 : }
379 112563 : if (details.location() == kField) {
380 26706 : if (details.kind() == kData) {
381 53412 : int index = descriptors->GetFieldIndex(number);
382 : Representation details_representation = details.representation();
383 : FieldIndex field_index =
384 26706 : FieldIndex::ForPropertyIndex(*map, index, details_representation);
385 : Type field_type = Type::NonInternal();
386 : MachineRepresentation field_representation =
387 : MachineRepresentation::kTagged;
388 : MaybeHandle<Map> field_map;
389 26705 : if (details_representation.IsSmi()) {
390 : field_type = Type::SignedSmall();
391 : field_representation = MachineRepresentation::kTaggedSigned;
392 19651 : } else if (details_representation.IsDouble()) {
393 883 : field_type = type_cache_->kFloat64;
394 : field_representation = MachineRepresentation::kFloat64;
395 18768 : } else if (details_representation.IsHeapObject()) {
396 : // Extract the field type from the property details (make sure its
397 : // representation is TaggedPointer to reflect the heap object case).
398 : field_representation = MachineRepresentation::kTaggedPointer;
399 : Handle<FieldType> descriptors_field_type(
400 22728 : descriptors->GetFieldType(number), isolate());
401 11363 : if (descriptors_field_type->IsNone()) {
402 : // Store is not safe if the field type was cleared.
403 0 : if (access_mode == AccessMode::kStore) return false;
404 :
405 : // The field type was cleared by the GC, so we don't know anything
406 : // about the contents now.
407 11363 : } else if (descriptors_field_type->IsClass()) {
408 : MapRef map_ref(broker(), map);
409 1447 : map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
410 1447 : dependencies()->DependOnFieldType(map_ref, number);
411 : // Remember the field map, and try to infer a useful type.
412 2894 : Handle<Map> map(descriptors_field_type->AsClass(), isolate());
413 1447 : field_type = Type::For(MapRef(broker(), map));
414 : field_map = MaybeHandle<Map>(map);
415 : }
416 : }
417 80116 : *access_info = PropertyAccessInfo::DataField(
418 : details.constness(), MapHandles{receiver_map}, field_index,
419 26706 : field_representation, field_type, field_map, holder);
420 26706 : return true;
421 : } else {
422 : DCHECK_EQ(kAccessor, details.kind());
423 : // TODO(turbofan): Add support for general accessors?
424 : return false;
425 : }
426 :
427 : } else {
428 : DCHECK_EQ(kDescriptor, details.location());
429 85857 : if (details.kind() == kData) {
430 : DCHECK(!FLAG_track_constant_fields);
431 313156 : *access_info = PropertyAccessInfo::DataConstant(
432 : MapHandles{receiver_map},
433 78289 : handle(descriptors->GetStrongValue(number), isolate()), holder);
434 78289 : return true;
435 : } else {
436 : DCHECK_EQ(kAccessor, details.kind());
437 7568 : if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
438 : DCHECK(map->is_prototype_map());
439 : Handle<PrototypeInfo> proto_info(
440 : PrototypeInfo::cast(map->prototype_info()), isolate());
441 : Handle<JSModuleNamespace> module_namespace(
442 : JSModuleNamespace::cast(proto_info->module_namespace()),
443 14 : isolate());
444 : Handle<Cell> cell(
445 14 : Cell::cast(module_namespace->module()->exports()->Lookup(
446 : ReadOnlyRoots(isolate()), name,
447 14 : Smi::ToInt(name->GetHash()))),
448 21 : isolate());
449 14 : if (cell->value()->IsTheHole(isolate())) {
450 : // This module has not been fully initialized yet.
451 : return false;
452 : }
453 21 : *access_info = PropertyAccessInfo::ModuleExport(
454 7 : MapHandles{receiver_map}, cell);
455 7 : return true;
456 : }
457 : Handle<Object> accessors(descriptors->GetStrongValue(number),
458 15122 : isolate());
459 15122 : if (!accessors->IsAccessorPair()) return false;
460 : Handle<Object> accessor(
461 : access_mode == AccessMode::kLoad
462 12990 : ? Handle<AccessorPair>::cast(accessors)->getter()
463 14341 : : Handle<AccessorPair>::cast(accessors)->setter(),
464 16580 : isolate());
465 12546 : if (!accessor->IsJSFunction()) {
466 3338 : CallOptimization optimization(isolate(), accessor);
467 6542 : if (!optimization.is_simple_api_call()) return false;
468 172 : if (optimization.IsCrossContextLazyAccessorPair(*native_context_,
469 : *map)) {
470 : return false;
471 : }
472 :
473 : CallOptimization::HolderLookup lookup;
474 : holder =
475 166 : optimization.LookupHolderOfExpectedType(receiver_map, &lookup);
476 166 : if (lookup == CallOptimization::kHolderNotFound) return false;
477 : DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
478 : holder.is_null());
479 : DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound,
480 : !holder.is_null());
481 134 : if (V8_UNLIKELY(FLAG_runtime_stats)) return false;
482 : }
483 3069 : if (access_mode == AccessMode::kLoad) {
484 : Handle<Name> cached_property_name;
485 2213 : if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(),
486 : accessor)
487 4426 : .ToHandle(&cached_property_name)) {
488 6 : if (ComputePropertyAccessInfo(map, cached_property_name,
489 : access_mode, access_info)) {
490 6 : return true;
491 : }
492 : }
493 : }
494 9189 : *access_info = PropertyAccessInfo::AccessorConstant(
495 3063 : MapHandles{receiver_map}, accessor, holder);
496 3063 : return true;
497 : }
498 : }
499 : UNREACHABLE();
500 : }
501 :
502 : // Don't search on the prototype chain for special indices in case of
503 : // integer indexed exotic objects (see ES6 section 9.4.5).
504 117499 : if (map->IsJSTypedArrayMap() && name->IsString() &&
505 938 : IsSpecialIndex(String::cast(*name))) {
506 : return false;
507 : }
508 :
509 : // Don't search on the prototype when storing in literals
510 114578 : if (access_mode == AccessMode::kStoreInLiteral) {
511 121 : return LookupTransition(receiver_map, name, holder, access_info);
512 : }
513 :
514 : // Don't lookup private symbols on the prototype chain.
515 114457 : if (name->IsPrivate()) return false;
516 :
517 : // Walk up the prototype chain.
518 228911 : if (!map->prototype()->IsJSObject()) {
519 : // Perform the implicit ToObject for primitives here.
520 : // Implemented according to ES6 section 7.3.2 GetV (V, P).
521 : Handle<JSFunction> constructor;
522 27893 : if (Map::GetConstructorFunction(map, native_context())
523 55786 : .ToHandle(&constructor)) {
524 17120 : map = handle(constructor->initial_map(), isolate());
525 : DCHECK(map->prototype()->IsJSObject());
526 38666 : } else if (map->prototype()->IsNull(isolate())) {
527 : // Store to property not found on the receiver or any prototype, we need
528 : // to transition to a new data property.
529 : // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
530 19326 : if (access_mode == AccessMode::kStore) {
531 15445 : return LookupTransition(receiver_map, name, holder, access_info);
532 : }
533 : // The property was not found, return undefined or throw depending
534 : // on the language mode of the load operation.
535 : // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
536 11643 : *access_info =
537 3881 : PropertyAccessInfo::NotFound(MapHandles{receiver_map}, holder);
538 3881 : return true;
539 : } else {
540 : return false;
541 : }
542 : }
543 : Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
544 95123 : if (map_prototype->map()->is_deprecated()) {
545 : // Try to migrate the prototype object so we don't embed the deprecated
546 : // map into the optimized code.
547 0 : JSObject::TryMigrateInstance(map_prototype);
548 : }
549 : map = handle(map_prototype->map(), isolate());
550 : holder = map_prototype;
551 : } while (CanInlinePropertyAccess(map));
552 : return false;
553 : }
554 :
555 1191 : bool AccessInfoFactory::ComputePropertyAccessInfo(
556 : MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
557 1191 : PropertyAccessInfo* access_info) {
558 : ZoneVector<PropertyAccessInfo> access_infos(zone());
559 1735 : if (ComputePropertyAccessInfos(maps, name, access_mode, &access_infos) &&
560 544 : access_infos.size() == 1) {
561 544 : *access_info = access_infos.front();
562 544 : return true;
563 : }
564 : return false;
565 : }
566 :
567 144295 : bool AccessInfoFactory::ComputePropertyAccessInfos(
568 : MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
569 167881 : ZoneVector<PropertyAccessInfo>* access_infos) {
570 427257 : for (Handle<Map> map : maps) {
571 311352 : if (Map::TryUpdate(isolate(), map).ToHandle(&map)) {
572 : PropertyAccessInfo access_info;
573 155662 : if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) {
574 : return false;
575 : }
576 : // Try to merge the {access_info} with an existing one.
577 : bool merged = false;
578 282297 : for (PropertyAccessInfo& other_info : *access_infos) {
579 12205 : if (other_info.Merge(&access_info, access_mode, zone())) {
580 : merged = true;
581 : break;
582 : }
583 : }
584 138653 : if (!merged) access_infos->push_back(access_info);
585 : }
586 : }
587 127286 : return true;
588 : }
589 :
590 : namespace {
591 :
592 14919 : Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
593 : ElementsKind that_kind) {
594 14919 : if (IsHoleyElementsKind(this_kind)) {
595 : that_kind = GetHoleyElementsKind(that_kind);
596 12016 : } else if (IsHoleyElementsKind(that_kind)) {
597 : this_kind = GetHoleyElementsKind(this_kind);
598 : }
599 14919 : if (this_kind == that_kind) return Just(this_kind);
600 685 : if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
601 632 : if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
602 : return Just(this_kind);
603 : }
604 107 : if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
605 : return Just(that_kind);
606 : }
607 : }
608 : return Nothing<ElementsKind>();
609 : }
610 :
611 : } // namespace
612 :
613 14473 : bool AccessInfoFactory::ConsolidateElementLoad(MapHandles const& maps,
614 : ElementAccessInfo* access_info) {
615 14473 : if (maps.empty()) return false;
616 : InstanceType instance_type = maps.front()->instance_type();
617 : ElementsKind elements_kind = maps.front()->elements_kind();
618 43705 : for (Handle<Map> map : maps) {
619 31050 : if (!CanInlineElementAccess(map) || map->instance_type() != instance_type) {
620 : return false;
621 : }
622 29838 : if (!GeneralizeElementsKind(elements_kind, map->elements_kind())
623 29838 : .To(&elements_kind)) {
624 : return false;
625 : }
626 : }
627 13249 : *access_info = ElementAccessInfo(maps, elements_kind);
628 13249 : return true;
629 : }
630 :
631 117530 : bool AccessInfoFactory::LookupSpecialFieldAccessor(
632 109194 : Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) {
633 : // Check for String::length field accessor.
634 117530 : if (map->IsStringMap()) {
635 8336 : if (Name::Equals(isolate(), name, factory()->length_string())) {
636 8156 : *access_info = PropertyAccessInfo::StringLength(MapHandles{map});
637 2039 : return true;
638 : }
639 : return false;
640 : }
641 : // Check for special JSObject field accessors.
642 : FieldIndex field_index;
643 109194 : if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
644 : Type field_type = Type::NonInternal();
645 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
646 8512 : if (map->IsJSArrayMap()) {
647 : DCHECK(Name::Equals(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 8512 : if (IsDoubleElementsKind(map->elements_kind())) {
654 303 : field_type = type_cache_->kFixedDoubleArrayLengthType;
655 : field_representation = MachineRepresentation::kTaggedSigned;
656 8209 : } else if (IsFastElementsKind(map->elements_kind())) {
657 7968 : field_type = type_cache_->kFixedArrayLengthType;
658 : field_representation = MachineRepresentation::kTaggedSigned;
659 : } else {
660 241 : field_type = type_cache_->kJSArrayLengthType;
661 : }
662 : }
663 : // Special fields are always mutable.
664 25536 : *access_info = PropertyAccessInfo::DataField(
665 : PropertyConstness::kMutable, MapHandles{map}, field_index,
666 8512 : field_representation, field_type);
667 : return true;
668 : }
669 : return false;
670 : }
671 :
672 :
673 17923 : bool AccessInfoFactory::LookupTransition(Handle<Map> map, Handle<Name> name,
674 : MaybeHandle<JSObject> holder,
675 72863 : PropertyAccessInfo* access_info) {
676 : // Check if the {map} has a data transition with the given {name}.
677 : Map transition =
678 17923 : TransitionsAccessor(isolate(), map).SearchTransition(*name, kData, NONE);
679 17923 : if (transition.is_null()) return false;
680 :
681 : Handle<Map> transition_map(transition, isolate());
682 17890 : int const number = transition_map->LastAdded();
683 : PropertyDetails const details =
684 17890 : transition_map->instance_descriptors()->GetDetails(number);
685 : // Don't bother optimizing stores to read-only properties.
686 17890 : if (details.IsReadOnly()) return false;
687 : // TODO(bmeurer): Handle transition to data constant?
688 17890 : if (details.location() != kField) return false;
689 : int const index = details.field_index();
690 : Representation details_representation = details.representation();
691 : FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index,
692 17873 : details_representation);
693 : Type field_type = Type::NonInternal();
694 : MaybeHandle<Map> field_map;
695 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
696 17873 : if (details_representation.IsSmi()) {
697 : field_type = Type::SignedSmall();
698 : field_representation = MachineRepresentation::kTaggedSigned;
699 3443 : } else if (details_representation.IsDouble()) {
700 175 : field_type = type_cache_->kFloat64;
701 : field_representation = MachineRepresentation::kFloat64;
702 3268 : } 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 1672 : transition_map->instance_descriptors()->GetFieldType(number),
708 1672 : isolate());
709 836 : if (descriptors_field_type->IsNone()) {
710 : // Store is not safe if the field type was cleared.
711 : return false;
712 836 : } else if (descriptors_field_type->IsClass()) {
713 : MapRef transition_map_ref(broker(), transition_map);
714 : transition_map_ref
715 117 : .SerializeOwnDescriptors(); // TODO(neis): Remove later.
716 117 : dependencies()->DependOnFieldType(transition_map_ref, number);
717 : // Remember the field map, and try to infer a useful type.
718 234 : Handle<Map> map(descriptors_field_type->AsClass(), isolate());
719 117 : field_type = Type::For(MapRef(broker(), map));
720 : field_map = MaybeHandle<Map>(map);
721 : }
722 : }
723 17873 : dependencies()->DependOnTransition(MapRef(broker(), transition_map));
724 : // Transitioning stores are never stores to constant fields.
725 53619 : *access_info = PropertyAccessInfo::DataField(
726 : PropertyConstness::kMutable, MapHandles{map}, field_index,
727 17873 : field_representation, field_type, field_map, holder, transition_map);
728 17873 : return true;
729 : }
730 :
731 :
732 8336 : Factory* AccessInfoFactory::factory() const { return isolate()->factory(); }
733 :
734 : } // namespace compiler
735 : } // namespace internal
736 183867 : } // namespace v8
|