access-info.cc 31.4 KB
Newer Older
1 2 3 4 5 6
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <ostream>

7
#include "src/compiler/access-info.h"
8 9 10

#include "src/accessors.h"
#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 23 24 25

namespace v8 {
namespace internal {
namespace compiler {

26 27 28
namespace {

bool CanInlinePropertyAccess(Handle<Map> map) {
29 30 31 32 33 34
  // We can inline property access to prototypes of all primitives, except
  // the special Oddball ones that have no wrapper counterparts (i.e. Null,
  // Undefined and TheHole).
  STATIC_ASSERT(ODDBALL_TYPE == LAST_PRIMITIVE_TYPE);
  if (map->IsBooleanMap()) return true;
  if (map->instance_type() < LAST_PRIMITIVE_TYPE) return true;
35 36 37 38 39 40 41 42 43 44
  return map->IsJSObjectMap() && !map->is_dictionary_map() &&
         !map->has_named_interceptor() &&
         // TODO(verwaest): Whitelist contexts to which we have access.
         !map->is_access_check_needed();
}

}  // namespace


std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
45
  switch (access_mode) {
46
    case AccessMode::kLoad:
47
      return os << "Load";
48
    case AccessMode::kStore:
49
      return os << "Store";
50 51
    case AccessMode::kStoreInLiteral:
      return os << "StoreInLiteral";
52 53
    case AccessMode::kHas:
      return os << "Has";
54 55 56 57
  }
  UNREACHABLE();
}

58
ElementAccessInfo::ElementAccessInfo() = default;
59

60
ElementAccessInfo::ElementAccessInfo(MapHandles const& receiver_maps,
61
                                     ElementsKind elements_kind)
62 63 64
    : elements_kind_(elements_kind), receiver_maps_(receiver_maps) {
  CHECK(!receiver_maps.empty());
}
65

66
// static
67
PropertyAccessInfo PropertyAccessInfo::NotFound(MapHandles const& receiver_maps,
68
                                                MaybeHandle<JSObject> holder) {
69
  return PropertyAccessInfo(kNotFound, holder, receiver_maps);
70 71 72 73
}

// static
PropertyAccessInfo PropertyAccessInfo::DataField(
74
    MapHandles const& receiver_maps,
75
    std::vector<CompilationDependencies::Dependency const*>&& dependencies,
76
    FieldIndex field_index, MachineRepresentation field_representation,
77
    Type field_type, MaybeHandle<Map> field_map, MaybeHandle<JSObject> holder,
78
    MaybeHandle<Map> transition_map) {
79
  return PropertyAccessInfo(kDataField, holder, transition_map, field_index,
80
                            field_representation, field_type, field_map,
81
                            receiver_maps, std::move(dependencies));
82 83
}

84 85 86 87 88 89 90 91 92 93 94
// static
PropertyAccessInfo PropertyAccessInfo::DataConstant(
    MapHandles const& receiver_maps,
    std::vector<CompilationDependencies::Dependency const*>&& dependencies,
    FieldIndex field_index, MachineRepresentation field_representation,
    Type field_type, MaybeHandle<Map> field_map, MaybeHandle<JSObject> holder) {
  return PropertyAccessInfo(kDataConstant, holder, MaybeHandle<Map>(),
                            field_index, field_representation, field_type,
                            field_map, receiver_maps, std::move(dependencies));
}

95 96
// static
PropertyAccessInfo PropertyAccessInfo::AccessorConstant(
97
    MapHandles const& receiver_maps, Handle<Object> constant,
98 99 100 101
    MaybeHandle<JSObject> holder) {
  return PropertyAccessInfo(kAccessorConstant, holder, constant, receiver_maps);
}

102 103 104 105 106 107 108
// static
PropertyAccessInfo PropertyAccessInfo::ModuleExport(
    MapHandles const& receiver_maps, Handle<Cell> cell) {
  return PropertyAccessInfo(kModuleExport, MaybeHandle<JSObject>(), cell,
                            receiver_maps);
}

109 110 111 112 113 114 115
// static
PropertyAccessInfo PropertyAccessInfo::StringLength(
    MapHandles const& receiver_maps) {
  return PropertyAccessInfo(kStringLength, MaybeHandle<JSObject>(),
                            receiver_maps);
}

116
PropertyAccessInfo::PropertyAccessInfo()
117 118 119
    : kind_(kInvalid),
      field_representation_(MachineRepresentation::kNone),
      field_type_(Type::None()) {}
120

121
PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
122
                                       MapHandles const& receiver_maps)
123
    : kind_(kind),
124
      receiver_maps_(receiver_maps),
125
      holder_(holder),
126
      field_representation_(MachineRepresentation::kNone),
127
      field_type_(Type::None()) {}
128

129
PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
130
                                       Handle<Object> constant,
131
                                       MapHandles const& receiver_maps)
132
    : kind_(kind),
133
      receiver_maps_(receiver_maps),
134 135
      constant_(constant),
      holder_(holder),
136
      field_representation_(MachineRepresentation::kNone),
137 138
      field_type_(Type::Any()) {}

139
PropertyAccessInfo::PropertyAccessInfo(
140
    Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
141
    FieldIndex field_index, MachineRepresentation field_representation,
142
    Type field_type, MaybeHandle<Map> field_map,
143 144 145
    MapHandles const& receiver_maps,
    std::vector<CompilationDependencies::Dependency const*>&&
        unrecorded_dependencies)
146
    : kind_(kind),
147
      receiver_maps_(receiver_maps),
148
      unrecorded_dependencies_(std::move(unrecorded_dependencies)),
149 150 151
      transition_map_(transition_map),
      holder_(holder),
      field_index_(field_index),
152
      field_representation_(field_representation),
153 154
      field_type_(field_type),
      field_map_(field_map) {}
155

156 157
bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
                               AccessMode access_mode, Zone* zone) {
158 159 160 161 162
  if (this->kind_ != that->kind_) return false;
  if (this->holder_.address() != that->holder_.address()) return false;

  switch (this->kind_) {
    case kInvalid:
163
      return that->kind_ == kInvalid;
164

165
    case kDataField:
166
    case kDataConstant: {
167 168 169 170 171 172
      // Check if we actually access the same field (we use the
      // GetFieldAccessStubKey method here just like the ICs do
      // since that way we only compare the relevant bits of the
      // field indices).
      if (this->field_index_.GetFieldAccessStubKey() ==
          that->field_index_.GetFieldAccessStubKey()) {
173
        switch (access_mode) {
174
          case AccessMode::kHas:
175 176
          case AccessMode::kLoad: {
            if (this->field_representation_ != that->field_representation_) {
177 178
              if (!IsAnyCompressedTagged(this->field_representation_) ||
                  !IsAnyCompressedTagged(that->field_representation_)) {
179 180
                return false;
              }
181
              this->field_representation_ = MachineType::RepCompressedTagged();
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
            }
            if (this->field_map_.address() != that->field_map_.address()) {
              this->field_map_ = MaybeHandle<Map>();
            }
            break;
          }
          case AccessMode::kStore:
          case AccessMode::kStoreInLiteral: {
            // For stores, the field map and field representation information
            // must match exactly, otherwise we cannot merge the stores. We
            // also need to make sure that in case of transitioning stores,
            // the transition targets match.
            if (this->field_map_.address() != that->field_map_.address() ||
                this->field_representation_ != that->field_representation_ ||
                this->transition_map_.address() !=
                    that->transition_map_.address()) {
              return false;
            }
            break;
          }
        }
        this->field_type_ =
            Type::Union(this->field_type_, that->field_type_, zone);
205 206 207
        this->receiver_maps_.insert(this->receiver_maps_.end(),
                                    that->receiver_maps_.begin(),
                                    that->receiver_maps_.end());
208 209 210 211
        this->unrecorded_dependencies_.insert(
            this->unrecorded_dependencies_.end(),
            that->unrecorded_dependencies_.begin(),
            that->unrecorded_dependencies_.end());
212 213 214 215 216
        return true;
      }
      return false;
    }

217
    case kAccessorConstant: {
218 219
      // Check if we actually access the same constant.
      if (this->constant_.address() == that->constant_.address()) {
220 221
        DCHECK(this->unrecorded_dependencies_.empty());
        DCHECK(that->unrecorded_dependencies_.empty());
222 223 224 225 226 227 228
        this->receiver_maps_.insert(this->receiver_maps_.end(),
                                    that->receiver_maps_.begin(),
                                    that->receiver_maps_.end());
        return true;
      }
      return false;
    }
229

230 231
    case kNotFound:
    case kStringLength: {
232 233
      DCHECK(this->unrecorded_dependencies_.empty());
      DCHECK(that->unrecorded_dependencies_.empty());
234 235 236 237 238
      this->receiver_maps_.insert(this->receiver_maps_.end(),
                                  that->receiver_maps_.begin(),
                                  that->receiver_maps_.end());
      return true;
    }
239
    case kModuleExport:
240
      return false;
241 242 243
  }
}

244 245 246 247 248
Handle<Cell> PropertyAccessInfo::export_cell() const {
  DCHECK_EQ(kModuleExport, kind_);
  return Handle<Cell>::cast(constant_);
}

249
AccessInfoFactory::AccessInfoFactory(JSHeapBroker* broker,
250
                                     CompilationDependencies* dependencies,
251
                                     Zone* zone)
252
    : broker_(broker),
253
      dependencies_(dependencies),
254
      type_cache_(TypeCache::Get()),
255
      zone_(zone) {}
256

257
bool AccessInfoFactory::ComputeElementAccessInfo(
258 259
    Handle<Map> map, AccessMode access_mode,
    ElementAccessInfo* access_info) const {
260
  // Check if it is safe to inline element access for the {map}.
261 262 263
  MapRef map_ref(broker(), map);
  if (!CanInlineElementAccess(map_ref)) return false;
  ElementsKind const elements_kind = map_ref.elements_kind();
264
  *access_info = ElementAccessInfo(MapHandles{map}, elements_kind);
265
  return true;
266 267
}

268 269 270
bool AccessInfoFactory::ComputeElementAccessInfos(
    FeedbackNexus nexus, MapHandles const& maps, AccessMode access_mode,
    ZoneVector<ElementAccessInfo>* access_infos) const {
271 272 273 274
  ElementAccessFeedback const* processed =
      FLAG_concurrent_inlining
          ? broker()->GetElementAccessFeedback(FeedbackSource(nexus))
          : broker()->ProcessFeedbackMapsForElementAccess(maps);
275
  if (processed == nullptr) return false;
276

277
  if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
278 279 280 281 282
    // For polymorphic loads of similar elements kinds (i.e. all tagged or all
    // double), always use the "worst case" code without a transition.  This is
    // much faster than transitioning the elements to the worst case, trading a
    // TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
    ElementAccessInfo access_info;
283
    if (ConsolidateElementLoad(*processed, &access_info)) {
284 285
      access_infos->push_back(access_info);
      return true;
286 287 288
    }
  }

289
  for (Handle<Map> receiver_map : processed->receiver_maps) {
290 291 292 293 294 295 296
    // Compute the element access information.
    ElementAccessInfo access_info;
    if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
      return false;
    }

    // Collect the possible transitions for the {receiver_map}.
297
    for (auto transition : processed->transitions) {
298
      if (transition.second.equals(receiver_map)) {
299
        access_info.AddTransitionSource(transition.first);
300 301
      }
    }
302 303 304

    // Schedule the access information.
    access_infos->push_back(access_info);
305 306 307
  }
  return true;
}
308

309
PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
310
    Handle<Map> receiver_map, Handle<Map> map, MaybeHandle<JSObject> holder,
311
    int number, AccessMode access_mode) const {
312 313 314 315 316
  DCHECK_NE(number, DescriptorArray::kNotFound);
  Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
  PropertyDetails const details = descriptors->GetDetails(number);
  int index = descriptors->GetFieldIndex(number);
  Representation details_representation = details.representation();
317 318 319 320 321 322
  if (details_representation.IsNone()) {
    // The ICs collect feedback in PREMONOMORPHIC state already,
    // but at this point the {receiver_map} might still contain
    // fields for which the representation has not yet been
    // determined by the runtime. So we need to catch this case
    // here and fall back to use the regular IC logic instead.
323
    return {};
324
  }
325 326 327
  FieldIndex field_index =
      FieldIndex::ForPropertyIndex(*map, index, details_representation);
  Type field_type = Type::NonInternal();
328
  MachineRepresentation field_representation =
329
      MachineType::RepCompressedTagged();
330
  MaybeHandle<Map> field_map;
331
  MapRef map_ref(broker(), map);
332 333
  std::vector<CompilationDependencies::Dependency const*>
      unrecorded_dependencies;
334 335
  if (details_representation.IsSmi()) {
    field_type = Type::SignedSmall();
336
    field_representation = MachineType::RepCompressedTaggedSigned();
337
    map_ref.SerializeOwnDescriptors();  // TODO(neis): Remove later.
338 339 340
    unrecorded_dependencies.push_back(
        dependencies()->FieldRepresentationDependencyOffTheRecord(map_ref,
                                                                  number));
341 342 343 344 345 346
  } else if (details_representation.IsDouble()) {
    field_type = type_cache_->kFloat64;
    field_representation = MachineRepresentation::kFloat64;
  } else if (details_representation.IsHeapObject()) {
    // Extract the field type from the property details (make sure its
    // representation is TaggedPointer to reflect the heap object case).
347
    field_representation = MachineType::RepCompressedTaggedPointer();
348 349 350 351
    Handle<FieldType> descriptors_field_type(descriptors->GetFieldType(number),
                                             isolate());
    if (descriptors_field_type->IsNone()) {
      // Store is not safe if the field type was cleared.
352
      if (access_mode == AccessMode::kStore) return {};
353 354 355

      // The field type was cleared by the GC, so we don't know anything
      // about the contents now.
356 357
    }
    map_ref.SerializeOwnDescriptors();  // TODO(neis): Remove later.
358 359 360
    unrecorded_dependencies.push_back(
        dependencies()->FieldRepresentationDependencyOffTheRecord(map_ref,
                                                                  number));
361
    if (descriptors_field_type->IsClass()) {
362 363
      unrecorded_dependencies.push_back(
          dependencies()->FieldTypeDependencyOffTheRecord(map_ref, number));
364 365 366 367 368 369
      // Remember the field map, and try to infer a useful type.
      Handle<Map> map(descriptors_field_type->AsClass(), isolate());
      field_type = Type::For(MapRef(broker(), map));
      field_map = MaybeHandle<Map>(map);
    }
  }
370 371 372 373 374 375 376 377 378 379 380
  switch (details.constness()) {
    case PropertyConstness::kMutable:
      return PropertyAccessInfo::DataField(
          MapHandles{receiver_map}, std::move(unrecorded_dependencies),
          field_index, field_representation, field_type, field_map, holder);
    case PropertyConstness::kConst:
      return PropertyAccessInfo::DataConstant(
          MapHandles{receiver_map}, std::move(unrecorded_dependencies),
          field_index, field_representation, field_type, field_map, holder);
  }
  UNREACHABLE();
381 382
}

383
PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
384
    Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map,
385
    MaybeHandle<JSObject> holder, int number, AccessMode access_mode) const {
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
  DCHECK_NE(number, DescriptorArray::kNotFound);
  Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
  SLOW_DCHECK(number == descriptors->Search(*name, *map));
  if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
    DCHECK(map->is_prototype_map());
    Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(map->prototype_info()),
                                     isolate());
    Handle<JSModuleNamespace> module_namespace(
        JSModuleNamespace::cast(proto_info->module_namespace()), isolate());
    Handle<Cell> cell(
        Cell::cast(module_namespace->module()->exports()->Lookup(
            ReadOnlyRoots(isolate()), name, Smi::ToInt(name->GetHash()))),
        isolate());
    if (cell->value()->IsTheHole(isolate())) {
      // This module has not been fully initialized yet.
401
      return {};
402
    }
403
    return PropertyAccessInfo::ModuleExport(MapHandles{receiver_map}, cell);
404
  }
405 406
  if (access_mode == AccessMode::kHas) {
    // HasProperty checks don't call getter/setters, existence is sufficient.
407 408
    return PropertyAccessInfo::AccessorConstant(MapHandles{receiver_map},
                                                Handle<Object>(), holder);
409
  }
410
  Handle<Object> accessors(descriptors->GetStrongValue(number), isolate());
411
  if (!accessors->IsAccessorPair()) return {};
412 413 414 415 416 417
  Handle<Object> accessor(access_mode == AccessMode::kLoad
                              ? Handle<AccessorPair>::cast(accessors)->getter()
                              : Handle<AccessorPair>::cast(accessors)->setter(),
                          isolate());
  if (!accessor->IsJSFunction()) {
    CallOptimization optimization(isolate(), accessor);
418 419
    if (!optimization.is_simple_api_call() ||
        optimization.IsCrossContextLazyAccessorPair(
420
            *broker()->native_context().object(), *map)) {
421
      return {};
422 423 424 425
    }

    CallOptimization::HolderLookup lookup;
    holder = optimization.LookupHolderOfExpectedType(receiver_map, &lookup);
426
    if (lookup == CallOptimization::kHolderNotFound) return {};
427 428 429
    DCHECK_IMPLIES(lookup == CallOptimization::kHolderIsReceiver,
                   holder.is_null());
    DCHECK_IMPLIES(lookup == CallOptimization::kHolderFound, !holder.is_null());
430
    if (V8_UNLIKELY(TracingFlags::is_runtime_stats_enabled())) return {};
431 432 433 434 435
  }
  if (access_mode == AccessMode::kLoad) {
    Handle<Name> cached_property_name;
    if (FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), accessor)
            .ToHandle(&cached_property_name)) {
436 437 438
      PropertyAccessInfo access_info =
          ComputePropertyAccessInfo(map, cached_property_name, access_mode);
      if (!access_info.IsInvalid()) return access_info;
439 440
    }
  }
441 442
  return PropertyAccessInfo::AccessorConstant(MapHandles{receiver_map},
                                              accessor, holder);
443
}
444

445 446
PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
    Handle<Map> map, Handle<Name> name, AccessMode access_mode) const {
447 448
  CHECK(name->IsUniqueName());

449
  if (access_mode == AccessMode::kHas && !map->IsJSReceiverMap()) return {};
450

451
  // Check if it is safe to inline property access for the {map}.
452
  if (!CanInlinePropertyAccess(map)) return {};
453 454

  // We support fast inline cases for certain JSObject getters.
455 456 457
  if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
    PropertyAccessInfo access_info = LookupSpecialFieldAccessor(map, name);
    if (!access_info.IsInvalid()) return access_info;
458 459
  }

460 461
  // Remember the receiver map. We use {map} as loop variable.
  Handle<Map> receiver_map = map;
462
  MaybeHandle<JSObject> holder;
463
  while (true) {
464 465
    // Lookup the named property on the {map}.
    Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
466
    int const number = descriptors->Search(*name, *map);
467 468
    if (number != DescriptorArray::kNotFound) {
      PropertyDetails const details = descriptors->GetDetails(number);
469 470
      if (access_mode == AccessMode::kStore ||
          access_mode == AccessMode::kStoreInLiteral) {
471
        // Don't bother optimizing stores to read-only properties.
472
        if (details.IsReadOnly()) return {};
473
        if (details.kind() == kData && !holder.is_null()) {
474 475 476 477
          // This is a store to a property not found on the receiver but on a
          // prototype. According to ES6 section 9.1.9 [[Set]], we need to
          // create a new data property on the receiver. We can still optimize
          // if such a transition already exists.
478
          return LookupTransition(receiver_map, name, holder);
479 480
        }
      }
481 482
      if (details.location() == kField) {
        if (details.kind() == kData) {
483
          return ComputeDataFieldAccessInfo(receiver_map, map, holder, number,
484
                                            access_mode);
485 486 487
        } else {
          DCHECK_EQ(kAccessor, details.kind());
          // TODO(turbofan): Add support for general accessors?
488
          return {};
489
        }
490 491
      } else {
        DCHECK_EQ(kDescriptor, details.location());
492 493 494
        DCHECK_EQ(kAccessor, details.kind());
        return ComputeAccessorDescriptorAccessInfo(receiver_map, name, map,
                                                   holder, number, access_mode);
495
      }
496
      UNREACHABLE();
497 498
    }

499 500
    // The property wasn't found on {map}. Look on the prototype if appropriate.

501 502 503
    // Don't search on the prototype chain for special indices in case of
    // integer indexed exotic objects (see ES6 section 9.4.5).
    if (map->IsJSTypedArrayMap() && name->IsString() &&
504
        IsSpecialIndex(String::cast(*name))) {
505
      return {};
506 507
    }

508
    // Don't search on the prototype when storing in literals.
509
    if (access_mode == AccessMode::kStoreInLiteral) {
510
      return LookupTransition(receiver_map, name, holder);
511 512
    }

513
    // Don't lookup private symbols on the prototype chain.
514
    if (name->IsPrivate()) return {};
515 516 517 518 519 520

    // Walk up the prototype chain.
    if (!map->prototype()->IsJSObject()) {
      // Perform the implicit ToObject for primitives here.
      // Implemented according to ES6 section 7.3.2 GetV (V, P).
      Handle<JSFunction> constructor;
521
      if (Map::GetConstructorFunction(map, broker()->native_context().object())
522 523 524
              .ToHandle(&constructor)) {
        map = handle(constructor->initial_map(), isolate());
        DCHECK(map->prototype()->IsJSObject());
525
      } else if (map->prototype()->IsNull(isolate())) {
526 527 528
        // Store to property not found on the receiver or any prototype, we need
        // to transition to a new data property.
        // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
529
        if (access_mode == AccessMode::kStore) {
530
          return LookupTransition(receiver_map, name, holder);
531
        }
532 533
        // The property was not found (access returns undefined or throws
        // depending on the language mode of the load operation.
534
        // Implemented according to ES6 section 9.1.8 [[Get]] (P, Receiver)
535
        return PropertyAccessInfo::NotFound(MapHandles{receiver_map}, holder);
536
      } else {
537
        return {};
538 539 540 541 542 543 544 545 546 547
      }
    }
    Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
    if (map_prototype->map()->is_deprecated()) {
      // Try to migrate the prototype object so we don't embed the deprecated
      // map into the optimized code.
      JSObject::TryMigrateInstance(map_prototype);
    }
    map = handle(map_prototype->map(), isolate());
    holder = map_prototype;
548

549
    if (!CanInlinePropertyAccess(map)) return {};
550 551 552 553 554 555

    // Successful lookup on prototype chain needs to guarantee that all
    // the prototypes up to the holder have stable maps. Let us make sure
    // the prototype maps are stable here.
    CHECK(map->is_stable());
  }
556
}
557

558 559 560 561 562 563 564 565 566 567
PropertyAccessInfo AccessInfoFactory::FinalizePropertyAccessInfosAsOne(
    ZoneVector<PropertyAccessInfo> access_infos, AccessMode access_mode) const {
  ZoneVector<PropertyAccessInfo> merged_access_infos(zone());
  MergePropertyAccessInfos(access_infos, access_mode, &merged_access_infos);
  if (merged_access_infos.size() == 1) {
    PropertyAccessInfo& result = merged_access_infos.front();
    if (!result.IsInvalid()) {
      result.RecordDependencies(dependencies());
      return result;
    }
568
  }
569
  return {};
570 571
}

572
void AccessInfoFactory::ComputePropertyAccessInfos(
573
    MapHandles const& maps, Handle<Name> name, AccessMode access_mode,
574
    ZoneVector<PropertyAccessInfo>* access_infos) const {
575
  DCHECK(access_infos->empty());
576
  for (Handle<Map> map : maps) {
577
    access_infos->push_back(ComputePropertyAccessInfo(map, name, access_mode));
578
  }
579
}
580

581 582 583 584 585 586 587 588 589
void PropertyAccessInfo::RecordDependencies(
    CompilationDependencies* dependencies) {
  for (CompilationDependencies::Dependency const* d :
       unrecorded_dependencies_) {
    dependencies->RecordDependency(d);
  }
  unrecorded_dependencies_.clear();
}

590
bool AccessInfoFactory::FinalizePropertyAccessInfos(
591 592 593 594 595 596 597 598 599 600 601 602 603
    ZoneVector<PropertyAccessInfo> access_infos, AccessMode access_mode,
    ZoneVector<PropertyAccessInfo>* result) const {
  MergePropertyAccessInfos(access_infos, access_mode, result);
  for (PropertyAccessInfo const& info : *result) {
    if (info.IsInvalid()) return false;
  }
  for (PropertyAccessInfo& info : *result) {
    info.RecordDependencies(dependencies());
  }
  return true;
}

void AccessInfoFactory::MergePropertyAccessInfos(
604 605 606
    ZoneVector<PropertyAccessInfo> infos, AccessMode access_mode,
    ZoneVector<PropertyAccessInfo>* result) const {
  DCHECK(result->empty());
607 608 609 610 611 612
  for (auto it = infos.begin(), end = infos.end(); it != end; ++it) {
    bool merged = false;
    for (auto ot = it + 1; ot != end; ++ot) {
      if (ot->Merge(&(*it), access_mode, zone())) {
        merged = true;
        break;
613
      }
614
    }
615
    if (!merged) result->push_back(*it);
616
  }
617
  CHECK(!result->empty());
618 619
}

620 621 622 623
namespace {

Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
                                           ElementsKind that_kind) {
624
  if (IsHoleyElementsKind(this_kind)) {
625
    that_kind = GetHoleyElementsKind(that_kind);
626
  } else if (IsHoleyElementsKind(that_kind)) {
627 628 629
    this_kind = GetHoleyElementsKind(this_kind);
  }
  if (this_kind == that_kind) return Just(this_kind);
630
  if (IsDoubleElementsKind(that_kind) == IsDoubleElementsKind(this_kind)) {
631 632 633 634 635 636 637 638 639 640 641 642
    if (IsMoreGeneralElementsKindTransition(that_kind, this_kind)) {
      return Just(this_kind);
    }
    if (IsMoreGeneralElementsKindTransition(this_kind, that_kind)) {
      return Just(that_kind);
    }
  }
  return Nothing<ElementsKind>();
}

}  // namespace

643
bool AccessInfoFactory::ConsolidateElementLoad(
644 645 646
    ElementAccessFeedback const& processed,
    ElementAccessInfo* access_info) const {
  ElementAccessFeedback::MapIterator it = processed.all_maps(broker());
647 648 649 650 651 652 653
  MapRef first_map = it.current();
  InstanceType instance_type = first_map.instance_type();
  ElementsKind elements_kind = first_map.elements_kind();
  MapHandles maps;
  for (; !it.done(); it.advance()) {
    MapRef map = it.current();
    if (map.instance_type() != instance_type || !CanInlineElementAccess(map)) {
654 655
      return false;
    }
656
    if (!GeneralizeElementsKind(elements_kind, map.elements_kind())
657 658
             .To(&elements_kind)) {
      return false;
659
    }
660
    maps.push_back(map.object());
661 662
  }

663
  *access_info = ElementAccessInfo(maps, elements_kind);
664 665
  return true;
}
666

667 668
PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor(
    Handle<Map> map, Handle<Name> name) const {
669 670
  // Check for String::length field accessor.
  if (map->IsStringMap()) {
671
    if (Name::Equals(isolate(), name, isolate()->factory()->length_string())) {
672
      return PropertyAccessInfo::StringLength(MapHandles{map});
673
    }
674
    return {};
675
  }
676
  // Check for special JSObject field accessors.
677
  FieldIndex field_index;
678
  if (Accessors::IsJSObjectFieldAccessor(isolate(), map, name, &field_index)) {
679
    Type field_type = Type::NonInternal();
680 681
    MachineRepresentation field_representation =
        MachineType::RepCompressedTagged();
682
    if (map->IsJSArrayMap()) {
683 684
      DCHECK(
          Name::Equals(isolate(), isolate()->factory()->length_string(), name));
685 686 687 688 689
      // The JSArray::length property is a smi in the range
      // [0, FixedDoubleArray::kMaxLength] in case of fast double
      // elements, a smi in the range [0, FixedArray::kMaxLength]
      // in case of other fast elements, and [0, kMaxUInt32] in
      // case of other arrays.
690
      if (IsDoubleElementsKind(map->elements_kind())) {
691
        field_type = type_cache_->kFixedDoubleArrayLengthType;
692
        field_representation = MachineType::RepCompressedTaggedSigned();
693
      } else if (IsFastElementsKind(map->elements_kind())) {
694
        field_type = type_cache_->kFixedArrayLengthType;
695
        field_representation = MachineType::RepCompressedTaggedSigned();
696
      } else {
697
        field_type = type_cache_->kJSArrayLengthType;
698 699
      }
    }
700
    // Special fields are always mutable.
701
    return PropertyAccessInfo::DataField(MapHandles{map}, {}, field_index,
702
                                         field_representation, field_type);
703
  }
704
  return {};
705 706
}

707 708
PropertyAccessInfo AccessInfoFactory::LookupTransition(
    Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder) const {
709
  // Check if the {map} has a data transition with the given {name}.
710
  Map transition =
711
      TransitionsAccessor(isolate(), map).SearchTransition(*name, kData, NONE);
712
  if (transition.is_null()) return {};
713

714
  Handle<Map> transition_map(transition, isolate());
715 716 717 718
  int const number = transition_map->LastAdded();
  PropertyDetails const details =
      transition_map->instance_descriptors()->GetDetails(number);
  // Don't bother optimizing stores to read-only properties.
719
  if (details.IsReadOnly()) return {};
720
  // TODO(bmeurer): Handle transition to data constant?
721
  if (details.location() != kField) return {};
722 723
  int const index = details.field_index();
  Representation details_representation = details.representation();
724 725
  FieldIndex field_index = FieldIndex::ForPropertyIndex(*transition_map, index,
                                                        details_representation);
726
  Type field_type = Type::NonInternal();
727
  MaybeHandle<Map> field_map;
728 729
  MachineRepresentation field_representation =
      MachineType::RepCompressedTagged();
730
  MapRef transition_map_ref(broker(), transition_map);
731 732
  std::vector<CompilationDependencies::Dependency const*>
      unrecorded_dependencies;
733 734
  if (details_representation.IsSmi()) {
    field_type = Type::SignedSmall();
735
    field_representation = MachineType::RepCompressedTaggedSigned();
736
    transition_map_ref.SerializeOwnDescriptors();  // TODO(neis): Remove later.
737 738 739
    unrecorded_dependencies.push_back(
        dependencies()->FieldRepresentationDependencyOffTheRecord(
            transition_map_ref, number));
740
  } else if (details_representation.IsDouble()) {
741
    field_type = type_cache_->kFloat64;
742 743 744 745
    field_representation = MachineRepresentation::kFloat64;
  } else if (details_representation.IsHeapObject()) {
    // Extract the field type from the property details (make sure its
    // representation is TaggedPointer to reflect the heap object case).
746
    field_representation = MachineType::RepCompressedTaggedPointer();
747 748 749 750 751
    Handle<FieldType> descriptors_field_type(
        transition_map->instance_descriptors()->GetFieldType(number),
        isolate());
    if (descriptors_field_type->IsNone()) {
      // Store is not safe if the field type was cleared.
752
      return {};
753 754
    }
    transition_map_ref.SerializeOwnDescriptors();  // TODO(neis): Remove later.
755 756 757
    unrecorded_dependencies.push_back(
        dependencies()->FieldRepresentationDependencyOffTheRecord(
            transition_map_ref, number));
758
    if (descriptors_field_type->IsClass()) {
759 760 761
      unrecorded_dependencies.push_back(
          dependencies()->FieldTypeDependencyOffTheRecord(transition_map_ref,
                                                          number));
762
      // Remember the field map, and try to infer a useful type.
763
      Handle<Map> map(descriptors_field_type->AsClass(), isolate());
764
      field_type = Type::For(MapRef(broker(), map));
765
      field_map = MaybeHandle<Map>(map);
766 767
    }
  }
768 769 770
  unrecorded_dependencies.push_back(
      dependencies()->TransitionDependencyOffTheRecord(
          MapRef(broker(), transition_map)));
771
  // Transitioning stores are never stores to constant fields.
772
  return PropertyAccessInfo::DataField(
773 774
      MapHandles{map}, std::move(unrecorded_dependencies), field_index,
      field_representation, field_type, field_map, holder, transition_map);
775 776 777 778 779
}

}  // namespace compiler
}  // namespace internal
}  // namespace v8