compilation-dependencies.cc 33.3 KB
Newer Older
1 2 3 4
// 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.

5
#include "src/compiler/compilation-dependencies.h"
6

7
#include "src/base/optional.h"
8
#include "src/compiler/compilation-dependency.h"
9
#include "src/execution/protectors.h"
10
#include "src/handles/handles-inl.h"
11
#include "src/objects/allocation-site-inl.h"
12
#include "src/objects/internal-index.h"
13
#include "src/objects/js-array-inl.h"
14
#include "src/objects/js-function-inl.h"
15
#include "src/objects/objects-inl.h"
16
#include "src/zone/zone-handle-set.h"
17 18 19

namespace v8 {
namespace internal {
20
namespace compiler {
21

22 23
CompilationDependencies::CompilationDependencies(JSHeapBroker* broker,
                                                 Zone* zone)
24 25 26
    : zone_(zone), broker_(broker), dependencies_(zone) {
  broker->set_dependencies(this);
}
27

28
class InitialMapDependency final : public CompilationDependency {
29
 public:
30 31
  InitialMapDependency(JSHeapBroker* broker, const JSFunctionRef& function,
                       const MapRef& initial_map)
32 33
      : function_(function), initial_map_(initial_map) {
  }
34

35
  bool IsValid() const override {
36
    Handle<JSFunction> function = function_.object();
37
    return function->has_initial_map() &&
38
           function->initial_map() == *initial_map_.object();
39 40
  }

41
  void Install(Handle<Code> code) const override {
42
    SLOW_DCHECK(IsValid());
43
    DependentCode::InstallDependency(function_.isolate(), code,
44
                                     initial_map_.object(),
45
                                     DependentCode::kInitialMapChangedGroup);
46
  }
47

48
 private:
49 50
  JSFunctionRef function_;
  MapRef initial_map_;
51
};
52

53
class PrototypePropertyDependency final : public CompilationDependency {
54
 public:
55 56
  PrototypePropertyDependency(JSHeapBroker* broker,
                              const JSFunctionRef& function,
57 58
                              const ObjectRef& prototype)
      : function_(function), prototype_(prototype) {
59 60 61 62
    DCHECK(function_.has_instance_prototype(broker->dependencies()));
    DCHECK(!function_.PrototypeRequiresRuntimeLookup(broker->dependencies()));
    DCHECK(function_.instance_prototype(broker->dependencies())
               .equals(prototype_));
63 64 65
  }

  bool IsValid() const override {
66
    Handle<JSFunction> function = function_.object();
67 68
    return function->has_prototype_slot() &&
           function->has_instance_prototype() &&
69
           !function->PrototypeRequiresRuntimeLookup() &&
70
           function->instance_prototype() == *prototype_.object();
71 72
  }

73
  void PrepareInstall() const override {
74
    SLOW_DCHECK(IsValid());
75
    Handle<JSFunction> function = function_.object();
76
    if (!function->has_initial_map()) JSFunction::EnsureHasInitialMap(function);
77 78
  }

79
  void Install(Handle<Code> code) const override {
80 81
    SLOW_DCHECK(IsValid());
    Handle<JSFunction> function = function_.object();
82
    CHECK(function->has_initial_map());
83 84 85 86 87 88 89 90 91 92
    Handle<Map> initial_map(function->initial_map(), function_.isolate());
    DependentCode::InstallDependency(function_.isolate(), code, initial_map,
                                     DependentCode::kInitialMapChangedGroup);
  }

 private:
  JSFunctionRef function_;
  ObjectRef prototype_;
};

93
class StableMapDependency final : public CompilationDependency {
94
 public:
95
  explicit StableMapDependency(const MapRef& map) : map_(map) {}
96

97 98 99 100 101 102
  bool IsValid() const override {
    // TODO(v8:11670): Consider turn this back into a CHECK inside the
    // constructor and DependOnStableMap, if possible in light of concurrent
    // heap state modifications.
    return !map_.object()->is_dictionary_map() && map_.object()->is_stable();
  }
103

104
  void Install(Handle<Code> code) const override {
105
    SLOW_DCHECK(IsValid());
106
    DependentCode::InstallDependency(map_.isolate(), code, map_.object(),
107
                                     DependentCode::kPrototypeCheckGroup);
108
  }
109

110
 private:
111
  MapRef map_;
112 113
};

114 115 116 117 118
class ConstantInDictionaryPrototypeChainDependency final
    : public CompilationDependency {
 public:
  explicit ConstantInDictionaryPrototypeChainDependency(
      const MapRef receiver_map, const NameRef property_name,
119
      const ObjectRef constant, PropertyKind kind)
120 121
      : receiver_map_(receiver_map),
        property_name_{property_name},
122 123
        constant_{constant},
        kind_{kind} {
124 125 126 127 128 129 130
    DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL);
  }

  // Checks that |constant_| is still the value of accessing |property_name_|
  // starting at |receiver_map_|.
  bool IsValid() const override { return !GetHolderIfValid().is_null(); }

131
  void Install(Handle<Code> code) const override {
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
    SLOW_DCHECK(IsValid());
    Isolate* isolate = receiver_map_.isolate();
    Handle<JSObject> holder = GetHolderIfValid().ToHandleChecked();
    Handle<Map> map = receiver_map_.object();

    while (map->prototype() != *holder) {
      map = handle(map->prototype().map(), isolate);
      DCHECK(map->IsJSObjectMap());  // Due to IsValid holding.
      DependentCode::InstallDependency(isolate, code, map,
                                       DependentCode::kPrototypeCheckGroup);
    }

    DCHECK(map->prototype().map().IsJSObjectMap());  // Due to IsValid holding.
    DependentCode::InstallDependency(isolate, code,
                                     handle(map->prototype().map(), isolate),
                                     DependentCode::kPrototypeCheckGroup);
  }

 private:
  // If the dependency is still valid, returns holder of the constant. Otherwise
  // returns null.
  // TODO(neis) Currently, invoking IsValid and then Install duplicates the call
  // to GetHolderIfValid. Instead, consider letting IsValid change the state
  // (and store the holder), or merge IsValid and Install.
  MaybeHandle<JSObject> GetHolderIfValid() const {
    DisallowGarbageCollection no_gc;
    Isolate* isolate = receiver_map_.isolate();

    Handle<Object> holder;
    HeapObject prototype = receiver_map_.object()->prototype();

    enum class ValidationResult { kFoundCorrect, kFoundIncorrect, kNotFound };
    auto try_load = [&](auto dictionary) -> ValidationResult {
      InternalIndex entry =
          dictionary.FindEntry(isolate, property_name_.object());
      if (entry.is_not_found()) {
        return ValidationResult::kNotFound;
      }

      PropertyDetails details = dictionary.DetailsAt(entry);
      if (details.constness() != PropertyConstness::kConst) {
        return ValidationResult::kFoundIncorrect;
      }

176 177 178 179 180 181 182 183 184 185 186 187
      Object dictionary_value = dictionary.ValueAt(entry);
      Object value;
      // We must be able to detect the case that the property |property_name_|
      // of |holder_| was originally a plain function |constant_| (when creating
      // this dependency) and has since become an accessor whose getter is
      // |constant_|. Therefore, we cannot just look at the property kind of
      // |details|, because that reflects the current situation, not the one
      // when creating this dependency.
      if (details.kind() != kind_) {
        return ValidationResult::kFoundIncorrect;
      }
      if (kind_ == PropertyKind::kAccessor) {
188 189 190
        if (!dictionary_value.IsAccessorPair()) {
          return ValidationResult::kFoundIncorrect;
        }
191 192 193 194 195 196 197
        // Only supporting loading at the moment, so we only ever want the
        // getter.
        value = AccessorPair::cast(dictionary_value)
                    .get(AccessorComponent::ACCESSOR_GETTER);
      } else {
        value = dictionary_value;
      }
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
      return value == *constant_.object() ? ValidationResult::kFoundCorrect
                                          : ValidationResult::kFoundIncorrect;
    };

    while (prototype.IsJSObject()) {
      // We only care about JSObjects because that's the only type of holder
      // (and types of prototypes on the chain to the holder) that
      // AccessInfoFactory::ComputePropertyAccessInfo allows.
      JSObject object = JSObject::cast(prototype);

      // We only support dictionary mode prototypes on the chain for this kind
      // of dependency.
      CHECK(!object.HasFastProperties());

      ValidationResult result =
213
          V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
              ? try_load(object.property_dictionary_swiss())
              : try_load(object.property_dictionary());

      if (result == ValidationResult::kFoundCorrect) {
        return handle(object, isolate);
      } else if (result == ValidationResult::kFoundIncorrect) {
        return MaybeHandle<JSObject>();
      }

      // In case of kNotFound, continue walking up the chain.
      prototype = object.map().prototype();
    }

    return MaybeHandle<JSObject>();
  }

  MapRef receiver_map_;
  NameRef property_name_;
  ObjectRef constant_;
233
  PropertyKind kind_;
234 235
};

236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
class OwnConstantDataPropertyDependency final : public CompilationDependency {
 public:
  OwnConstantDataPropertyDependency(JSHeapBroker* broker,
                                    const JSObjectRef& holder,
                                    const MapRef& map,
                                    Representation representation,
                                    FieldIndex index, const ObjectRef& value)
      : broker_(broker),
        holder_(holder),
        map_(map),
        representation_(representation),
        index_(index),
        value_(value) {}

  bool IsValid() const override {
    if (holder_.object()->map() != *map_.object()) {
      TRACE_BROKER_MISSING(broker_,
                           "Map change detected in " << holder_.object());
      return false;
    }
    DisallowGarbageCollection no_heap_allocation;
    Object current_value = holder_.object()->RawFastPropertyAt(index_);
    Object used_value = *value_.object();
    if (representation_.IsDouble()) {
      // Compare doubles by bit pattern.
      if (!current_value.IsHeapNumber() || !used_value.IsHeapNumber() ||
262 263
          HeapNumber::cast(current_value).value_as_bits(kRelaxedLoad) !=
              HeapNumber::cast(used_value).value_as_bits(kRelaxedLoad)) {
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
        TRACE_BROKER_MISSING(broker_,
                             "Constant Double property value changed in "
                                 << holder_.object() << " at FieldIndex "
                                 << index_.property_index());
        return false;
      }
    } else if (current_value != used_value) {
      TRACE_BROKER_MISSING(broker_, "Constant property value changed in "
                                        << holder_.object() << " at FieldIndex "
                                        << index_.property_index());
      return false;
    }
    return true;
  }

  void Install(Handle<Code> code) const override {}

 private:
  JSHeapBroker* const broker_;
  JSObjectRef const holder_;
  MapRef const map_;
  Representation const representation_;
  FieldIndex const index_;
  ObjectRef const value_;
};

290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
class OwnConstantDictionaryPropertyDependency final
    : public CompilationDependency {
 public:
  OwnConstantDictionaryPropertyDependency(JSHeapBroker* broker,
                                          const JSObjectRef& holder,
                                          InternalIndex index,
                                          const ObjectRef& value)
      : broker_(broker),
        holder_(holder),
        map_(holder.map()),
        index_(index),
        value_(value) {
    // We depend on map() being cached.
    STATIC_ASSERT(ref_traits<JSObject>::ref_serialization_kind !=
                  RefSerializationKind::kNeverSerialized);
  }

  bool IsValid() const override {
    if (holder_.object()->map() != *map_.object()) {
      TRACE_BROKER_MISSING(broker_,
                           "Map change detected in " << holder_.object());
      return false;
    }

    base::Optional<Object> maybe_value = JSObject::DictionaryPropertyAt(
        holder_.object(), index_, broker_->isolate()->heap());

    if (!maybe_value) {
      TRACE_BROKER_MISSING(
          broker_, holder_.object()
                       << "has a value that might not safe to read at index "
                       << index_.as_int());
      return false;
    }

    if (*maybe_value != *value_.object()) {
      TRACE_BROKER_MISSING(broker_, "Constant property value changed in "
                                        << holder_.object()
                                        << " at InternalIndex "
                                        << index_.as_int());
      return false;
    }
    return true;
  }

  void Install(Handle<Code> code) const override {}

 private:
  JSHeapBroker* const broker_;
  JSObjectRef const holder_;
  MapRef const map_;
  InternalIndex const index_;
  ObjectRef const value_;
};

345 346 347 348 349 350 351 352 353 354 355
class ConsistentJSFunctionViewDependency final : public CompilationDependency {
 public:
  explicit ConsistentJSFunctionViewDependency(const JSFunctionRef& function)
      : function_(function) {}

  bool IsValid() const override {
    return function_.IsConsistentWithHeapState();
  }

  void Install(Handle<Code> code) const override {}

356 357 358 359
#ifdef DEBUG
  bool IsConsistentJSFunctionViewDependency() const override { return true; }
#endif

360 361 362 363
 private:
  const JSFunctionRef function_;
};

364
class TransitionDependency final : public CompilationDependency {
365
 public:
366
  explicit TransitionDependency(const MapRef& map) : map_(map) {
367
    DCHECK(map_.CanBeDeprecated());
368 369
  }

370
  bool IsValid() const override { return !map_.object()->is_deprecated(); }
371

372
  void Install(Handle<Code> code) const override {
373
    SLOW_DCHECK(IsValid());
374
    DependentCode::InstallDependency(map_.isolate(), code, map_.object(),
375
                                     DependentCode::kTransitionGroup);
376
  }
377

378
 private:
379
  MapRef map_;
380
};
381

382
class PretenureModeDependency final : public CompilationDependency {
383
 public:
384 385
  PretenureModeDependency(const AllocationSiteRef& site,
                          AllocationType allocation)
386
      : site_(site), allocation_(allocation) {}
387 388

  bool IsValid() const override {
389
    return allocation_ == site_.object()->GetAllocationType();
390 391
  }

392
  void Install(Handle<Code> code) const override {
393
    SLOW_DCHECK(IsValid());
394
    DependentCode::InstallDependency(
395
        site_.isolate(), code, site_.object(),
396
        DependentCode::kAllocationSiteTenuringChangedGroup);
397 398
  }

399 400 401 402
#ifdef DEBUG
  bool IsPretenureModeDependency() const override { return true; }
#endif

403
 private:
404
  AllocationSiteRef site_;
405
  AllocationType allocation_;
406 407
};

408
class FieldRepresentationDependency final : public CompilationDependency {
409
 public:
410
  FieldRepresentationDependency(const MapRef& map, InternalIndex descriptor,
411
                                Representation representation)
412
      : map_(map), descriptor_(descriptor), representation_(representation) {}
413 414

  bool IsValid() const override {
415
    DisallowGarbageCollection no_heap_allocation;
416 417 418
    if (map_.object()->is_deprecated()) return false;
    return representation_.Equals(map_.object()
                                      ->instance_descriptors(map_.isolate())
419 420
                                      .GetDetails(descriptor_)
                                      .representation());
421 422
  }

423
  void Install(Handle<Code> code) const override {
424
    SLOW_DCHECK(IsValid());
425 426 427 428 429 430 431 432
    Isolate* isolate = map_.isolate();
    Handle<Map> owner(map_.object()->FindFieldOwner(isolate, descriptor_),
                      isolate);
    CHECK(!owner->is_deprecated());
    CHECK(representation_.Equals(owner->instance_descriptors(isolate)
                                     .GetDetails(descriptor_)
                                     .representation()));
    DependentCode::InstallDependency(isolate, code, owner,
433
                                     DependentCode::kFieldRepresentationGroup);
434 435
  }

436 437 438
#ifdef DEBUG
  bool IsFieldRepresentationDependencyOnMap(
      Handle<Map> const& receiver_map) const override {
439
    return map_.object().equals(receiver_map);
440 441 442
  }
#endif

443
 private:
444
  MapRef map_;
445
  InternalIndex descriptor_;
446 447 448
  Representation representation_;
};

449
class FieldTypeDependency final : public CompilationDependency {
450
 public:
451
  FieldTypeDependency(const MapRef& map, InternalIndex descriptor,
452
                      const ObjectRef& type)
453
      : map_(map), descriptor_(descriptor), type_(type) {}
454 455

  bool IsValid() const override {
456
    DisallowGarbageCollection no_heap_allocation;
457 458 459
    if (map_.object()->is_deprecated()) return false;
    return *type_.object() == map_.object()
                                  ->instance_descriptors(map_.isolate())
460
                                  .GetFieldType(descriptor_);
461 462
  }

463
  void Install(Handle<Code> code) const override {
464
    SLOW_DCHECK(IsValid());
465 466 467 468 469 470 471
    Isolate* isolate = map_.isolate();
    Handle<Map> owner(map_.object()->FindFieldOwner(isolate, descriptor_),
                      isolate);
    CHECK(!owner->is_deprecated());
    CHECK_EQ(*type_.object(),
             owner->instance_descriptors(isolate).GetFieldType(descriptor_));
    DependentCode::InstallDependency(isolate, code, owner,
472
                                     DependentCode::kFieldTypeGroup);
473 474 475
  }

 private:
476
  MapRef map_;
477
  InternalIndex descriptor_;
478
  ObjectRef type_;
479 480
};

481
class FieldConstnessDependency final : public CompilationDependency {
482
 public:
483 484
  FieldConstnessDependency(const MapRef& map, InternalIndex descriptor)
      : map_(map), descriptor_(descriptor) {}
485 486

  bool IsValid() const override {
487
    DisallowGarbageCollection no_heap_allocation;
488
    if (map_.object()->is_deprecated()) return false;
489
    return PropertyConstness::kConst ==
490 491
           map_.object()
               ->instance_descriptors(map_.isolate())
492 493
               .GetDetails(descriptor_)
               .constness();
494 495
  }

496
  void Install(Handle<Code> code) const override {
497
    SLOW_DCHECK(IsValid());
498 499 500 501 502 503 504 505
    Isolate* isolate = map_.isolate();
    Handle<Map> owner(map_.object()->FindFieldOwner(isolate, descriptor_),
                      isolate);
    CHECK(!owner->is_deprecated());
    CHECK_EQ(PropertyConstness::kConst, owner->instance_descriptors(isolate)
                                            .GetDetails(descriptor_)
                                            .constness());
    DependentCode::InstallDependency(isolate, code, owner,
506
                                     DependentCode::kFieldConstGroup);
507 508 509
  }

 private:
510
  MapRef map_;
511
  InternalIndex descriptor_;
512 513
};

514
class GlobalPropertyDependency final : public CompilationDependency {
515
 public:
516
  GlobalPropertyDependency(const PropertyCellRef& cell, PropertyCellType type,
517 518
                           bool read_only)
      : cell_(cell), type_(type), read_only_(read_only) {
519 520
    DCHECK_EQ(type_, cell_.property_details().cell_type());
    DCHECK_EQ(read_only_, cell_.property_details().IsReadOnly());
521 522 523
  }

  bool IsValid() const override {
524
    Handle<PropertyCell> cell = cell_.object();
525 526 527 528 529
    // The dependency is never valid if the cell is 'invalidated'. This is
    // marked by setting the value to the hole.
    if (cell->value() == *(cell_.isolate()->factory()->the_hole_value())) {
      return false;
    }
530 531
    return type_ == cell->property_details().cell_type() &&
           read_only_ == cell->property_details().IsReadOnly();
532 533
  }

534
  void Install(Handle<Code> code) const override {
535
    SLOW_DCHECK(IsValid());
536
    DependentCode::InstallDependency(cell_.isolate(), code, cell_.object(),
537
                                     DependentCode::kPropertyCellChangedGroup);
538 539 540
  }

 private:
541
  PropertyCellRef cell_;
542 543 544 545
  PropertyCellType type_;
  bool read_only_;
};

546
class ProtectorDependency final : public CompilationDependency {
547
 public:
548
  explicit ProtectorDependency(const PropertyCellRef& cell) : cell_(cell) {}
549 550

  bool IsValid() const override {
551
    Handle<PropertyCell> cell = cell_.object();
552
    return cell->value() == Smi::FromInt(Protectors::kProtectorValid);
553 554
  }

555
  void Install(Handle<Code> code) const override {
556
    SLOW_DCHECK(IsValid());
557
    DependentCode::InstallDependency(cell_.isolate(), code, cell_.object(),
558
                                     DependentCode::kPropertyCellChangedGroup);
559 560 561
  }

 private:
562
  PropertyCellRef cell_;
563 564
};

565
class ElementsKindDependency final : public CompilationDependency {
566
 public:
567
  ElementsKindDependency(const AllocationSiteRef& site, ElementsKind kind)
568 569
      : site_(site), kind_(kind) {
    DCHECK(AllocationSite::ShouldTrack(kind_));
570 571 572
  }

  bool IsValid() const override {
573
    Handle<AllocationSite> site = site_.object();
574 575 576 577
    ElementsKind kind =
        site->PointsToLiteral()
            ? site->boilerplate(kAcquireLoad).map().elements_kind()
            : site->GetElementsKind();
578 579 580
    return kind_ == kind;
  }

581
  void Install(Handle<Code> code) const override {
582
    SLOW_DCHECK(IsValid());
583
    DependentCode::InstallDependency(
584
        site_.isolate(), code, site_.object(),
585
        DependentCode::kAllocationSiteTransitionChangedGroup);
586
  }
587 588

 private:
589
  AllocationSiteRef site_;
590 591 592
  ElementsKind kind_;
};

593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
// Only valid if the holder can use direct reads, since validation uses
// GetOwnConstantElementFromHeap.
class OwnConstantElementDependency final : public CompilationDependency {
 public:
  OwnConstantElementDependency(const JSObjectRef& holder, uint32_t index,
                               const ObjectRef& element)
      : holder_(holder), index_(index), element_(element) {}

  bool IsValid() const override {
    DisallowGarbageCollection no_gc;
    JSObject holder = *holder_.object();
    base::Optional<Object> maybe_element =
        holder_.GetOwnConstantElementFromHeap(holder.elements(),
                                              holder.GetElementsKind(), index_);
    if (!maybe_element.has_value()) return false;

    return maybe_element.value() == *element_.object();
  }

612
  void Install(Handle<Code> code) const override {
613 614 615 616 617 618 619 620 621
    // This dependency has no effect after code finalization.
  }

 private:
  const JSObjectRef holder_;
  const uint32_t index_;
  const ObjectRef element_;
};

622
class InitialMapInstanceSizePredictionDependency final
623
    : public CompilationDependency {
624 625 626 627 628 629 630 631
 public:
  InitialMapInstanceSizePredictionDependency(const JSFunctionRef& function,
                                             int instance_size)
      : function_(function), instance_size_(instance_size) {}

  bool IsValid() const override {
    // The dependency is valid if the prediction is the same as the current
    // slack tracking result.
632 633 634
    if (!function_.object()->has_initial_map()) return false;
    int instance_size = function_.object()->ComputeInstanceSizeWithMinSlack(
        function_.isolate());
635 636 637
    return instance_size == instance_size_;
  }

638
  void PrepareInstall() const override {
639
    SLOW_DCHECK(IsValid());
640
    function_.object()->CompleteInobjectSlackTrackingIfActive();
641 642
  }

643
  void Install(Handle<Code> code) const override {
644
    SLOW_DCHECK(IsValid());
645 646
    DCHECK(
        !function_.object()->initial_map().IsInobjectSlackTrackingInProgress());
647 648
  }

649 650 651 652 653
 private:
  JSFunctionRef function_;
  int instance_size_;
};

654 655
void CompilationDependencies::RecordDependency(
    CompilationDependency const* dependency) {
656 657 658
  if (dependency != nullptr) dependencies_.push_front(dependency);
}

659 660
MapRef CompilationDependencies::DependOnInitialMap(
    const JSFunctionRef& function) {
661 662
  MapRef map = function.initial_map(this);
  RecordDependency(zone_->New<InitialMapDependency>(broker_, function, map));
663
  return map;
664 665
}

666 667
ObjectRef CompilationDependencies::DependOnPrototypeProperty(
    const JSFunctionRef& function) {
668
  ObjectRef prototype = function.instance_prototype(this);
669
  RecordDependency(
670
      zone_->New<PrototypePropertyDependency>(broker_, function, prototype));
671 672 673
  return prototype;
}

674 675
void CompilationDependencies::DependOnStableMap(const MapRef& map) {
  if (map.CanTransition()) {
676
    RecordDependency(zone_->New<StableMapDependency>(map));
677 678
  }
}
679

680 681
void CompilationDependencies::DependOnConstantInDictionaryPrototypeChain(
    const MapRef& receiver_map, const NameRef& property_name,
682
    const ObjectRef& constant, PropertyKind kind) {
683
  RecordDependency(zone_->New<ConstantInDictionaryPrototypeChainDependency>(
684
      receiver_map, property_name, constant, kind));
685 686
}

687
AllocationType CompilationDependencies::DependOnPretenureMode(
688
    const AllocationSiteRef& site) {
689
  if (!FLAG_allocation_site_pretenuring) return AllocationType::kYoung;
690
  AllocationType allocation = site.GetAllocationType();
691
  RecordDependency(zone_->New<PretenureModeDependency>(site, allocation));
692
  return allocation;
693
}
694

695
PropertyConstness CompilationDependencies::DependOnFieldConstness(
696
    const MapRef& map, InternalIndex descriptor) {
697
  PropertyConstness constness = map.GetPropertyDetails(descriptor).constness();
698 699 700 701 702 703 704 705 706 707 708 709 710 711
  if (constness == PropertyConstness::kMutable) return constness;

  // If the map can have fast elements transitions, then the field can be only
  // considered constant if the map does not transition.
  if (Map::CanHaveFastTransitionableElementsKind(map.instance_type())) {
    // If the map can already transition away, let us report the field as
    // mutable.
    if (!map.is_stable()) {
      return PropertyConstness::kMutable;
    }
    DependOnStableMap(map);
  }

  DCHECK_EQ(constness, PropertyConstness::kConst);
712
  RecordDependency(zone_->New<FieldConstnessDependency>(map, descriptor));
713 714 715
  return PropertyConstness::kConst;
}

716
void CompilationDependencies::DependOnGlobalProperty(
717 718 719
    const PropertyCellRef& cell) {
  PropertyCellType type = cell.property_details().cell_type();
  bool read_only = cell.property_details().IsReadOnly();
720
  RecordDependency(zone_->New<GlobalPropertyDependency>(cell, type, read_only));
721 722
}

723
bool CompilationDependencies::DependOnProtector(const PropertyCellRef& cell) {
724
  cell.CacheAsProtector();
725
  if (cell.value().AsSmi() != Protectors::kProtectorValid) return false;
726
  RecordDependency(zone_->New<ProtectorDependency>(cell));
727 728 729 730
  return true;
}

bool CompilationDependencies::DependOnArrayBufferDetachingProtector() {
731
  return DependOnProtector(MakeRef(
732 733 734 735 736
      broker_,
      broker_->isolate()->factory()->array_buffer_detaching_protector()));
}

bool CompilationDependencies::DependOnArrayIteratorProtector() {
737
  return DependOnProtector(MakeRef(
738 739 740 741
      broker_, broker_->isolate()->factory()->array_iterator_protector()));
}

bool CompilationDependencies::DependOnArraySpeciesProtector() {
742
  return DependOnProtector(MakeRef(
743 744 745 746
      broker_, broker_->isolate()->factory()->array_species_protector()));
}

bool CompilationDependencies::DependOnNoElementsProtector() {
747 748
  return DependOnProtector(
      MakeRef(broker_, broker_->isolate()->factory()->no_elements_protector()));
749 750 751
}

bool CompilationDependencies::DependOnPromiseHookProtector() {
752
  return DependOnProtector(MakeRef(
753 754 755 756
      broker_, broker_->isolate()->factory()->promise_hook_protector()));
}

bool CompilationDependencies::DependOnPromiseSpeciesProtector() {
757
  return DependOnProtector(MakeRef(
758 759 760 761
      broker_, broker_->isolate()->factory()->promise_species_protector()));
}

bool CompilationDependencies::DependOnPromiseThenProtector() {
762
  return DependOnProtector(MakeRef(
763
      broker_, broker_->isolate()->factory()->promise_then_protector()));
764 765 766
}

void CompilationDependencies::DependOnElementsKind(
767 768
    const AllocationSiteRef& site) {
  ElementsKind kind = site.PointsToLiteral()
769
                          ? site.boilerplate().value().map().elements_kind()
770
                          : site.GetElementsKind();
771
  if (AllocationSite::ShouldTrack(kind)) {
772
    RecordDependency(zone_->New<ElementsKindDependency>(site, kind));
773 774 775
  }
}

776 777 778 779 780 781 782 783 784
void CompilationDependencies::DependOnOwnConstantElement(
    const JSObjectRef& holder, uint32_t index, const ObjectRef& element) {
  // Only valid if the holder can use direct reads, since validation uses
  // GetOwnConstantElementFromHeap.
  DCHECK(holder.should_access_heap() || broker_->is_concurrent_inlining());
  RecordDependency(
      zone_->New<OwnConstantElementDependency>(holder, index, element));
}

785 786 787 788 789 790 791
void CompilationDependencies::DependOnOwnConstantDataProperty(
    const JSObjectRef& holder, const MapRef& map, Representation representation,
    FieldIndex index, const ObjectRef& value) {
  RecordDependency(zone_->New<OwnConstantDataPropertyDependency>(
      broker_, holder, map, representation, index, value));
}

792 793 794 795 796 797
void CompilationDependencies::DependOnOwnConstantDictionaryProperty(
    const JSObjectRef& holder, InternalIndex index, const ObjectRef& value) {
  RecordDependency(zone_->New<OwnConstantDictionaryPropertyDependency>(
      broker_, holder, index, value));
}

798
bool CompilationDependencies::Commit(Handle<Code> code) {
799 800 801 802 803 804
  for (auto dep : dependencies_) {
    if (!dep->IsValid()) {
      dependencies_.clear();
      return false;
    }
    dep->PrepareInstall();
805 806
  }

807
  DisallowCodeDependencyChange no_dependency_change;
808
  for (auto dep : dependencies_) {
809
    // Check each dependency's validity again right before installing it,
810 811 812 813
    // because the first iteration above might have invalidated some
    // dependencies. For example, PrototypePropertyDependency::PrepareInstall
    // can call EnsureHasInitialMap, which can invalidate a StableMapDependency
    // on the prototype object's map.
814 815 816 817
    if (!dep->IsValid()) {
      dependencies_.clear();
      return false;
    }
818
    dep->Install(code);
819
  }
820

821
  // It is even possible that a GC during the above installations invalidated
822 823 824 825 826 827 828 829 830 831 832 833
  // one of the dependencies. However, this should only affect
  //
  // 1. pretenure mode dependencies, or
  // 2. function consistency dependencies,
  //
  // which we assert below. It is safe to return successfully in these cases,
  // because
  //
  // 1. once the code gets executed it will do a stack check that triggers its
  //    deoptimization.
  // 2. since the function state was deemed consistent above, that means the
  //    compilation saw a self-consistent state of the jsfunction.
834
  if (FLAG_stress_gc_during_compilation) {
835
    broker_->isolate()->heap()->PreciseCollectAllGarbage(
836
        Heap::kForcedGC, GarbageCollectionReason::kTesting, kNoGCCallbackFlags);
837
  }
838 839
#ifdef DEBUG
  for (auto dep : dependencies_) {
840 841 842
    CHECK_IMPLIES(!dep->IsValid(),
                  dep->IsPretenureModeDependency() ||
                      dep->IsConsistentJSFunctionViewDependency());
843 844
  }
#endif
845

846 847 848 849 850
  dependencies_.clear();
  return true;
}

namespace {
851
// This function expects to never see a JSProxy.
852
void DependOnStablePrototypeChain(CompilationDependencies* deps, MapRef map,
853
                                  base::Optional<JSObjectRef> last_prototype) {
854
  while (true) {
855
    HeapObjectRef proto = map.prototype().value();
856 857 858 859
    if (!proto.IsJSObject()) {
      CHECK_EQ(proto.map().oddball_type(), OddballType::kNull);
      break;
    }
860 861
    map = proto.map();
    deps->DependOnStableMap(map);
862
    if (last_prototype.has_value() && proto.equals(*last_prototype)) break;
863 864
  }
}
865
}  // namespace
866

867
void CompilationDependencies::DependOnStablePrototypeChains(
868
    ZoneVector<MapRef> const& receiver_maps, WhereToStart start,
869
    base::Optional<JSObjectRef> last_prototype) {
870
  for (MapRef receiver_map : receiver_maps) {
871 872 873
    if (receiver_map.IsPrimitiveMap()) {
      // Perform the implicit ToObject for primitives here.
      // Implemented according to ES6 section 7.3.2 GetV (V, P).
874
      // Note: Keep sync'd with AccessInfoFactory::ComputePropertyAccessInfo.
875
      base::Optional<JSFunctionRef> constructor =
876
          broker_->target_native_context().GetConstructorFunction(receiver_map);
877
      receiver_map = constructor.value().initial_map(this);
878
    }
879
    if (start == kStartAtReceiver) DependOnStableMap(receiver_map);
880
    DependOnStablePrototypeChain(this, receiver_map, last_prototype);
881 882
  }
}
883

884
void CompilationDependencies::DependOnElementsKinds(
885 886
    const AllocationSiteRef& site) {
  AllocationSiteRef current = site;
887
  while (true) {
888 889 890
    DependOnElementsKind(current);
    if (!current.nested_site().IsAllocationSite()) break;
    current = current.nested_site().AsAllocationSite();
891
  }
892
  CHECK_EQ(current.nested_site().AsSmi(), 0);
893
}
894

895 896 897 898 899 900
void CompilationDependencies::DependOnConsistentJSFunctionView(
    const JSFunctionRef& function) {
  DCHECK(broker_->is_concurrent_inlining());
  RecordDependency(zone_->New<ConsistentJSFunctionViewDependency>(function));
}

901 902 903 904
SlackTrackingPrediction::SlackTrackingPrediction(MapRef initial_map,
                                                 int instance_size)
    : instance_size_(instance_size),
      inobject_property_count_(
905
          (instance_size >> kTaggedSizeLog2) -
906 907 908 909 910 911
          initial_map.GetInObjectPropertiesStartInWords()) {}

SlackTrackingPrediction
CompilationDependencies::DependOnInitialMapInstanceSizePrediction(
    const JSFunctionRef& function) {
  MapRef initial_map = DependOnInitialMap(function);
912
  int instance_size = function.InitialMapInstanceSizeWithMinSlack(this);
913 914 915
  // Currently, we always install the prediction dependency. If this turns out
  // to be too expensive, we can only install the dependency if slack
  // tracking is active.
916
  RecordDependency(zone_->New<InitialMapInstanceSizePredictionDependency>(
917
      function, instance_size));
918
  CHECK_LE(instance_size, function.initial_map(this).instance_size());
919 920 921
  return SlackTrackingPrediction(initial_map, instance_size);
}

922
CompilationDependency const*
923 924 925
CompilationDependencies::TransitionDependencyOffTheRecord(
    const MapRef& target_map) const {
  if (target_map.CanBeDeprecated()) {
926
    return zone_->New<TransitionDependency>(target_map);
927 928 929 930 931 932
  } else {
    DCHECK(!target_map.is_deprecated());
    return nullptr;
  }
}

933
CompilationDependency const*
934
CompilationDependencies::FieldRepresentationDependencyOffTheRecord(
935 936
    const MapRef& map, InternalIndex descriptor,
    Representation representation) const {
937 938
  return zone_->New<FieldRepresentationDependency>(map, descriptor,
                                                   representation);
939 940
}

941
CompilationDependency const*
942
CompilationDependencies::FieldTypeDependencyOffTheRecord(
943
    const MapRef& map, InternalIndex descriptor, const ObjectRef& type) const {
944
  return zone_->New<FieldTypeDependency>(map, descriptor, type);
945 946
}

947
}  // namespace compiler
948 949
}  // namespace internal
}  // namespace v8