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 247575 : 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 247575 : if (map->IsBooleanMap()) return true;
34 247050 : if (map->instance_type() < LAST_PRIMITIVE_TYPE) return true;
35 467152 : return map->IsJSObjectMap() && !map->is_dictionary_map() &&
36 468306 : !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 22734 : ElementAccessInfo::ElementAccessInfo(MapHandles const& receiver_maps,
61 : ElementsKind elements_kind)
62 22734 : : elements_kind_(elements_kind), receiver_maps_(receiver_maps) {
63 22734 : CHECK(!receiver_maps.empty());
64 22734 : }
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 100724 : 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 2343 : PropertyAccessInfo::PropertyAccessInfo()
114 : : kind_(kInvalid),
115 : field_representation_(MachineRepresentation::kNone),
116 311881 : 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 15798 : 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 10965 : 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 259992 : field_map_(field_map) {}
149 :
150 13662 : bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
151 : AccessMode access_mode, Zone* zone) {
152 13662 : if (this->kind_ != that->kind_) return false;
153 12945 : if (this->holder_.address() != that->holder_.address()) return false;
154 :
155 11871 : switch (this->kind_) {
156 : case kInvalid:
157 : break;
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 11512 : if (this->field_index_.GetFieldAccessStubKey() ==
166 : that->field_index_.GetFieldAccessStubKey()) {
167 10665 : switch (access_mode) {
168 : case AccessMode::kHas:
169 : case AccessMode::kLoad: {
170 6098 : if (this->field_representation_ != that->field_representation_) {
171 696 : if (!IsAnyTagged(this->field_representation_) ||
172 : !IsAnyTagged(that->field_representation_)) {
173 : return false;
174 : }
175 348 : this->field_representation_ = MachineRepresentation::kTagged;
176 : }
177 6098 : if (this->field_map_.address() != that->field_map_.address()) {
178 211 : this->field_map_ = MaybeHandle<Map>();
179 : }
180 : break;
181 : }
182 : case AccessMode::kStore:
183 : case AccessMode::kStoreInLiteral: {
184 : // For stores, the field map and field representation information
185 : // must match exactly, otherwise we cannot merge the stores. We
186 : // also need to make sure that in case of transitioning stores,
187 : // the transition targets match.
188 9086 : if (this->field_map_.address() != that->field_map_.address() ||
189 8978 : 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 6334 : 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 6334 : that->receiver_maps_.end());
204 6334 : return true;
205 : }
206 : return false;
207 : }
208 :
209 : case kDataConstant:
210 : case kAccessorConstant: {
211 : // Check if we actually access the same constant.
212 248 : if (this->constant_.address() == that->constant_.address()) {
213 : this->receiver_maps_.insert(this->receiver_maps_.end(),
214 : that->receiver_maps_.begin(),
215 241 : that->receiver_maps_.end());
216 241 : 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 111 : that->receiver_maps_.end());
226 111 : return true;
227 : }
228 : case kModuleExport: {
229 : return false;
230 : }
231 : }
232 :
233 0 : UNREACHABLE();
234 : }
235 :
236 7 : Handle<Cell> PropertyAccessInfo::export_cell() const {
237 : DCHECK_EQ(kModuleExport, kind_);
238 7 : return Handle<Cell>::cast(constant_);
239 : }
240 :
241 163848 : AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
242 : CompilationDependencies* dependencies,
243 : Zone* zone)
244 : : broker_(broker),
245 : dependencies_(dependencies),
246 163848 : type_cache_(TypeCache::Get()),
247 327696 : zone_(zone) {}
248 :
249 10491 : bool AccessInfoFactory::ComputeElementAccessInfo(
250 : Handle<Map> map, AccessMode access_mode,
251 : ElementAccessInfo* access_info) const {
252 : // Check if it is safe to inline element access for the {map}.
253 : MapRef map_ref(broker(), map);
254 10491 : if (!CanInlineElementAccess(map_ref)) return false;
255 9431 : ElementsKind const elements_kind = map_ref.elements_kind();
256 37724 : *access_info = ElementAccessInfo(MapHandles{map}, elements_kind);
257 9431 : return true;
258 : }
259 :
260 23147 : bool AccessInfoFactory::ComputeElementAccessInfos(
261 : FeedbackNexus nexus, MapHandles const& maps, AccessMode access_mode,
262 : ZoneVector<ElementAccessInfo>* access_infos) const {
263 : ElementAccessFeedback const* processed;
264 23147 : if (FLAG_concurrent_inlining) {
265 8 : TRACE_BROKER(broker(),
266 : "ComputeElementAccessInfos: using preprocessed feedback "
267 : << "(slot " << nexus.slot() << " of "
268 : << "feedback vector handle "
269 : << nexus.vector_handle().address() << ").\n");
270 8 : processed = broker()->GetElementAccessFeedback(FeedbackSource(nexus));
271 : } else {
272 23139 : processed = broker()->ProcessFeedbackMapsForElementAccess(maps);
273 : }
274 :
275 23147 : if (processed == nullptr) return false;
276 :
277 23139 : if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
278 : // For polymorphic loads of similar elements kinds (i.e. all tagged or all
279 : // double), always use the "worst case" code without a transition. This is
280 : // much faster than transitioning the elements to the worst case, trading a
281 : // TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
282 1249 : ElementAccessInfo access_info;
283 14552 : if (ConsolidateElementLoad(*processed, &access_info)) {
284 13303 : access_infos->push_back(access_info);
285 13303 : return true;
286 : }
287 : }
288 :
289 19267 : for (Handle<Map> receiver_map : processed->receiver_maps) {
290 : // Compute the element access information.
291 9431 : ElementAccessInfo access_info;
292 10491 : if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
293 1060 : return false;
294 : }
295 :
296 : // Collect the possible transitions for the {receiver_map}.
297 10087 : for (auto transition : processed->transitions) {
298 656 : if (transition.second.equals(receiver_map)) {
299 570 : access_info.AddTransitionSource(transition.first);
300 : }
301 : }
302 :
303 : // Schedule the access information.
304 9431 : access_infos->push_back(access_info);
305 : }
306 8776 : return true;
307 : }
308 :
309 100724 : bool AccessInfoFactory::ComputeDataFieldAccessInfo(
310 : Handle<Map> receiver_map, Handle<Map> map, MaybeHandle<JSObject> holder,
311 : int number, AccessMode access_mode, PropertyAccessInfo* access_info) const {
312 : DCHECK_NE(number, DescriptorArray::kNotFound);
313 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
314 100724 : PropertyDetails const details = descriptors->GetDetails(number);
315 201448 : int index = descriptors->GetFieldIndex(number);
316 : Representation details_representation = details.representation();
317 : FieldIndex field_index =
318 100724 : FieldIndex::ForPropertyIndex(*map, index, details_representation);
319 : Type field_type = Type::NonInternal();
320 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
321 : MaybeHandle<Map> field_map;
322 100724 : if (details_representation.IsSmi()) {
323 : field_type = Type::SignedSmall();
324 : field_representation = MachineRepresentation::kTaggedSigned;
325 92412 : } else if (details_representation.IsDouble()) {
326 937 : field_type = type_cache_->kFloat64;
327 : field_representation = MachineRepresentation::kFloat64;
328 91475 : } else if (details_representation.IsHeapObject()) {
329 : // Extract the field type from the property details (make sure its
330 : // representation is TaggedPointer to reflect the heap object case).
331 : field_representation = MachineRepresentation::kTaggedPointer;
332 : Handle<FieldType> descriptors_field_type(descriptors->GetFieldType(number),
333 163196 : isolate());
334 81598 : if (descriptors_field_type->IsNone()) {
335 : // Store is not safe if the field type was cleared.
336 7 : if (access_mode == AccessMode::kStore) return false;
337 :
338 : // The field type was cleared by the GC, so we don't know anything
339 : // about the contents now.
340 81591 : } else if (descriptors_field_type->IsClass()) {
341 : MapRef map_ref(broker(), map);
342 3491 : map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
343 3491 : dependencies()->DependOnFieldType(map_ref, number);
344 : // Remember the field map, and try to infer a useful type.
345 6982 : Handle<Map> map(descriptors_field_type->AsClass(), isolate());
346 3491 : field_type = Type::For(MapRef(broker(), map));
347 : field_map = MaybeHandle<Map>(map);
348 : }
349 : }
350 302172 : *access_info = PropertyAccessInfo::DataField(
351 : details.constness(), MapHandles{receiver_map}, field_index,
352 100724 : field_representation, field_type, field_map, holder);
353 100724 : return true;
354 : }
355 :
356 7962 : bool AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
357 : Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map,
358 : MaybeHandle<JSObject> holder, int number, AccessMode access_mode,
359 : PropertyAccessInfo* access_info) const {
360 : DCHECK_NE(number, DescriptorArray::kNotFound);
361 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
362 : SLOW_DCHECK(number == descriptors->Search(*name, *map));
363 7962 : if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
364 : DCHECK(map->is_prototype_map());
365 : Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(map->prototype_info()),
366 : isolate());
367 : Handle<JSModuleNamespace> module_namespace(
368 : JSModuleNamespace::cast(proto_info->module_namespace()), isolate());
369 : Handle<Cell> cell(
370 44 : Cell::cast(module_namespace->module()->exports()->Lookup(
371 44 : ReadOnlyRoots(isolate()), name, Smi::ToInt(name->GetHash()))),
372 66 : isolate());
373 22 : if (cell->value()->IsTheHole(isolate())) {
374 : // This module has not been fully initialized yet.
375 : return false;
376 : }
377 : *access_info =
378 88 : PropertyAccessInfo::ModuleExport(MapHandles{receiver_map}, cell);
379 22 : return true;
380 : }
381 7940 : if (access_mode == AccessMode::kHas) {
382 : // HasProperty checks don't call getter/setters, existence is sufficient.
383 336 : *access_info = PropertyAccessInfo::AccessorConstant(
384 112 : MapHandles{receiver_map}, Handle<Object>(), holder);
385 112 : return true;
386 : }
387 15656 : Handle<Object> accessors(descriptors->GetStrongValue(number), isolate());
388 7828 : if (!accessors->IsAccessorPair()) return false;
389 : Handle<Object> accessor(access_mode == AccessMode::kLoad
390 : ? Handle<AccessorPair>::cast(accessors)->getter()
391 : : Handle<AccessorPair>::cast(accessors)->setter(),
392 6575 : isolate());
393 6575 : if (!accessor->IsJSFunction()) {
394 3182 : CallOptimization optimization(isolate(), accessor);
395 6230 : if (!optimization.is_simple_api_call()) return false;
396 344 : if (optimization.IsCrossContextLazyAccessorPair(
397 344 : *broker()->native_context().object(), *map)) {
398 : return false;
399 : }
400 :
401 : CallOptimization::HolderLookup lookup;
402 166 : holder = optimization.LookupHolderOfExpectedType(receiver_map, &lookup);
403 166 : if (lookup == CallOptimization::kHolderNotFound) return false;
404 : DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
405 : holder.is_null());
406 : DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound, !holder.is_null());
407 134 : if (V8_UNLIKELY(TracingFlags::is_runtime_stats_enabled())) return false;
408 : }
409 3527 : if (access_mode == AccessMode::kLoad) {
410 : Handle<Name> cached_property_name;
411 5352 : if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), accessor)
412 : .ToHandle(&cached_property_name)) {
413 6 : if (ComputePropertyAccessInfo(map, cached_property_name, access_mode,
414 : access_info)) {
415 6 : return true;
416 : }
417 : }
418 : }
419 10563 : *access_info = PropertyAccessInfo::AccessorConstant(MapHandles{receiver_map},
420 3521 : accessor, holder);
421 3521 : return true;
422 : }
423 :
424 153563 : bool AccessInfoFactory::ComputePropertyAccessInfo(
425 : Handle<Map> map, Handle<Name> name, AccessMode access_mode,
426 : PropertyAccessInfo* access_info) const {
427 153563 : CHECK(name->IsUniqueName());
428 :
429 153875 : if (access_mode == AccessMode::kHas && !map->IsJSReceiverMap()) return false;
430 :
431 : // Check if it is safe to inline property access for the {map}.
432 153556 : if (!CanInlinePropertyAccess(map)) return false;
433 :
434 : // We support fast inline cases for certain JSObject getters.
435 260216 : if ((access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) &&
436 112172 : LookupSpecialFieldAccessor(map, name, access_info)) {
437 : return true;
438 : }
439 :
440 : // Remember the receiver map. We use {map} as loop variable.
441 : Handle<Map> receiver_map = map;
442 : MaybeHandle<JSObject> holder;
443 94019 : do {
444 : // Lookup the named property on the {map}.
445 : Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
446 461288 : int const number = descriptors->Search(*name, *map);
447 230644 : if (number != DescriptorArray::kNotFound) {
448 113819 : PropertyDetails const details = descriptors->GetDetails(number);
449 113819 : if (access_mode == AccessMode::kStore ||
450 : access_mode == AccessMode::kStoreInLiteral) {
451 : // Don't bother optimizing stores to read-only properties.
452 15175 : if (details.IsReadOnly()) {
453 : return false;
454 : }
455 12128 : 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 2086 : return LookupTransition(receiver_map, name, holder, access_info);
461 : }
462 : }
463 108686 : if (details.location() == kField) {
464 100724 : if (details.kind() == kData) {
465 : return ComputeDataFieldAccessInfo(receiver_map, map, holder, number,
466 100724 : access_mode, access_info);
467 : } else {
468 : DCHECK_EQ(kAccessor, details.kind());
469 : // TODO(turbofan): Add support for general accessors?
470 : return false;
471 : }
472 : } else {
473 : DCHECK_EQ(kDescriptor, details.location());
474 7962 : if (details.kind() == kData) {
475 : DCHECK(!FLAG_track_constant_fields);
476 0 : *access_info = PropertyAccessInfo::DataConstant(
477 : MapHandles{receiver_map},
478 0 : handle(descriptors->GetStrongValue(number), isolate()), holder);
479 0 : return true;
480 : } else {
481 : DCHECK_EQ(kAccessor, details.kind());
482 : return ComputeAccessorDescriptorAccessInfo(receiver_map, name, map,
483 : holder, number,
484 7962 : access_mode, access_info);
485 : }
486 : }
487 : UNREACHABLE();
488 : }
489 :
490 : // The property wasn't found on {map}. Look on the prototype if appropriate.
491 :
492 : // Don't search on the prototype chain for special indices in case of
493 : // integer indexed exotic objects (see ES6 section 9.4.5).
494 119911 : if (map->IsJSTypedArrayMap() && name->IsString() &&
495 1522 : IsSpecialIndex(String::cast(*name))) {
496 : return false;
497 : }
498 :
499 : // Don't search on the prototype when storing in literals.
500 116818 : if (access_mode == AccessMode::kStoreInLiteral) {
501 111 : return LookupTransition(receiver_map, name, holder, access_info);
502 : }
503 :
504 : // Don't lookup private symbols on the prototype chain.
505 116707 : if (name->IsPrivate()) return false;
506 :
507 : // Walk up the prototype chain.
508 116706 : if (!map->prototype()->IsJSObject()) {
509 : // Perform the implicit ToObject for primitives here.
510 : // Implemented according to ES6 section 7.3.2 GetV (V, P).
511 : Handle<JSFunction> constructor;
512 91749 : if (Map::GetConstructorFunction(map, broker()->native_context().object())
513 : .ToHandle(&constructor)) {
514 : map = handle(constructor->initial_map(), isolate());
515 : DCHECK(map->prototype()->IsJSObject());
516 22687 : } else if (map->prototype()->IsNull(isolate())) {
517 : // Store to property not found on the receiver or any prototype, we need
518 : // to transition to a new data property.
519 : // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
520 22680 : if (access_mode == AccessMode::kStore) {
521 19327 : return LookupTransition(receiver_map, name, holder, access_info);
522 : }
523 : // The property was not found, return undefined or throw depending
524 : // on the language mode of the load operation.
525 : // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
526 : *access_info =
527 13412 : PropertyAccessInfo::NotFound(MapHandles{receiver_map}, holder);
528 3353 : return true;
529 : } else {
530 : return false;
531 : }
532 : }
533 : Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
534 94019 : if (map_prototype->map()->is_deprecated()) {
535 : // Try to migrate the prototype object so we don't embed the deprecated
536 : // map into the optimized code.
537 0 : JSObject::TryMigrateInstance(map_prototype);
538 : }
539 : map = handle(map_prototype->map(), isolate());
540 : holder = map_prototype;
541 : } while (CanInlinePropertyAccess(map));
542 : return false;
543 : }
544 :
545 1212 : bool AccessInfoFactory::ComputePropertyAccessInfo(
546 : MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
547 : PropertyAccessInfo* access_info) const {
548 : ZoneVector<PropertyAccessInfo> access_infos(zone());
549 1734 : if (ComputePropertyAccessInfos(maps, name, access_mode, &access_infos) &&
550 : access_infos.size() == 1) {
551 522 : *access_info = access_infos.front();
552 522 : return true;
553 : }
554 : return false;
555 : }
556 :
557 139570 : bool AccessInfoFactory::ComputePropertyAccessInfos(
558 : MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
559 : ZoneVector<PropertyAccessInfo>* access_infos) const {
560 : ZoneVector<PropertyAccessInfo> infos(zone());
561 139570 : infos.reserve(maps.size());
562 277403 : for (Handle<Map> map : maps) {
563 : PropertyAccessInfo access_info;
564 152426 : if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) {
565 : return false;
566 : }
567 137833 : infos.push_back(access_info);
568 : }
569 :
570 : // Merge as many as possible and push into {access_infos}.
571 262689 : for (auto it = infos.begin(), end = infos.end(); it != end; ++it) {
572 : bool merged = false;
573 144688 : for (auto ot = it + 1; ot != end; ++ot) {
574 13662 : if (ot->Merge(&(*it), access_mode, zone())) {
575 : merged = true;
576 : break;
577 : }
578 : }
579 137712 : if (!merged) access_infos->push_back(*it);
580 : }
581 : return true;
582 : }
583 :
584 : namespace {
585 :
586 15015 : Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
587 : ElementsKind that_kind) {
588 15015 : if (IsHoleyElementsKind(this_kind)) {
589 : that_kind = GetHoleyElementsKind(that_kind);
590 11825 : } else if (IsHoleyElementsKind(that_kind)) {
591 : this_kind = GetHoleyElementsKind(this_kind);
592 : }
593 15015 : if (this_kind == that_kind) return Just(this_kind);
594 762 : if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
595 685 : if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
596 : return Just(this_kind);
597 : }
598 87 : if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
599 : return Just(that_kind);
600 : }
601 : }
602 : return Nothing<ElementsKind>();
603 : }
604 :
605 : } // namespace
606 :
607 14552 : bool AccessInfoFactory::ConsolidateElementLoad(
608 : ElementAccessFeedback const& processed,
609 : ElementAccessInfo* access_info) const {
610 14552 : ElementAccessFeedback::MapIterator it = processed.all_maps(broker());
611 14552 : MapRef first_map = it.current();
612 14552 : InstanceType instance_type = first_map.instance_type();
613 14552 : ElementsKind elements_kind = first_map.elements_kind();
614 : MapHandles maps;
615 44254 : for (; !it.done(); it.advance()) {
616 16100 : MapRef map = it.current();
617 16100 : if (map.instance_type() != instance_type || !CanInlineElementAccess(map)) {
618 1249 : return false;
619 : }
620 30030 : if (!GeneralizeElementsKind(elements_kind, map.elements_kind())
621 : .To(&elements_kind)) {
622 : return false;
623 : }
624 29702 : maps.push_back(map.object());
625 : }
626 :
627 26606 : *access_info = ElementAccessInfo(maps, elements_kind);
628 13303 : return true;
629 : }
630 :
631 112172 : bool AccessInfoFactory::LookupSpecialFieldAccessor(
632 : Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) const {
633 : // Check for String::length field accessor.
634 112172 : if (map->IsStringMap()) {
635 7820 : if (Name::Equals(isolate(), name, isolate()->factory()->length_string())) {
636 7652 : *access_info = PropertyAccessInfo::StringLength(MapHandles{map});
637 1913 : return true;
638 : }
639 : return false;
640 : }
641 : // Check for special JSObject field accessors.
642 : FieldIndex field_index;
643 104352 : if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
644 : Type field_type = Type::NonInternal();
645 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
646 7782 : if (map->IsJSArrayMap()) {
647 : DCHECK(
648 : Name::Equals(isolate(), isolate()->factory()->length_string(), name));
649 : // The JSArray::length property is a smi in the range
650 : // [0, FixedDoubleArray::kMaxLength] in case of fast double
651 : // elements, a smi in the range [0, FixedArray::kMaxLength]
652 : // in case of other fast elements, and [0, kMaxUInt32] in
653 : // case of other arrays.
654 7782 : if (IsDoubleElementsKind(map->elements_kind())) {
655 325 : field_type = type_cache_->kFixedDoubleArrayLengthType;
656 : field_representation = MachineRepresentation::kTaggedSigned;
657 7457 : } else if (IsFastElementsKind(map->elements_kind())) {
658 7217 : field_type = type_cache_->kFixedArrayLengthType;
659 : field_representation = MachineRepresentation::kTaggedSigned;
660 : } else {
661 240 : field_type = type_cache_->kJSArrayLengthType;
662 : }
663 : }
664 : // Special fields are always mutable.
665 23346 : *access_info = PropertyAccessInfo::DataField(
666 : PropertyConstness::kMutable, MapHandles{map}, field_index,
667 7782 : field_representation, field_type);
668 : return true;
669 : }
670 : return false;
671 : }
672 :
673 21524 : bool AccessInfoFactory::LookupTransition(
674 : Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder,
675 : PropertyAccessInfo* access_info) const {
676 : // Check if the {map} has a data transition with the given {name}.
677 : Map transition =
678 21524 : TransitionsAccessor(isolate(), map).SearchTransition(*name, kData, NONE);
679 21524 : if (transition.is_null()) return false;
680 :
681 : Handle<Map> transition_map(transition, isolate());
682 : int const number = transition_map->LastAdded();
683 : PropertyDetails const details =
684 21490 : transition_map->instance_descriptors()->GetDetails(number);
685 : // Don't bother optimizing stores to read-only properties.
686 21490 : if (details.IsReadOnly()) return false;
687 : // TODO(bmeurer): Handle transition to data constant?
688 21490 : 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 21490 : details_representation);
693 : Type field_type = Type::NonInternal();
694 : MaybeHandle<Map> field_map;
695 : MachineRepresentation field_representation = MachineRepresentation::kTagged;
696 21490 : if (details_representation.IsSmi()) {
697 : field_type = Type::SignedSmall();
698 : field_representation = MachineRepresentation::kTaggedSigned;
699 3373 : } else if (details_representation.IsDouble()) {
700 155 : field_type = type_cache_->kFloat64;
701 : field_representation = MachineRepresentation::kFloat64;
702 3218 : } 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 1660 : transition_map->instance_descriptors()->GetFieldType(number),
708 : isolate());
709 830 : if (descriptors_field_type->IsNone()) {
710 : // Store is not safe if the field type was cleared.
711 : return false;
712 830 : } else if (descriptors_field_type->IsClass()) {
713 : MapRef transition_map_ref(broker(), transition_map);
714 : transition_map_ref
715 145 : .SerializeOwnDescriptors(); // TODO(neis): Remove later.
716 145 : dependencies()->DependOnFieldType(transition_map_ref, number);
717 : // Remember the field map, and try to infer a useful type.
718 290 : Handle<Map> map(descriptors_field_type->AsClass(), isolate());
719 145 : field_type = Type::For(MapRef(broker(), map));
720 : field_map = MaybeHandle<Map>(map);
721 : }
722 : }
723 21490 : dependencies()->DependOnTransition(MapRef(broker(), transition_map));
724 : // Transitioning stores are never stores to constant fields.
725 64470 : *access_info = PropertyAccessInfo::DataField(
726 : PropertyConstness::kMutable, MapHandles{map}, field_index,
727 21490 : field_representation, field_type, field_map, holder, transition_map);
728 21490 : return true;
729 : }
730 :
731 :
732 : } // namespace compiler
733 : } // namespace internal
734 120216 : } // namespace v8
|