compilation-dependencies.cc 36.7 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/execution/protectors.h"
9
#include "src/handles/handles-inl.h"
10
#include "src/objects/allocation-site-inl.h"
11
#include "src/objects/internal-index.h"
12
#include "src/objects/js-array-inl.h"
13
#include "src/objects/js-function-inl.h"
14
#include "src/objects/objects-inl.h"
15
#include "src/zone/zone-handle-set.h"
16 17 18

namespace v8 {
namespace internal {
19
namespace compiler {
20

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
#define DEPENDENCY_LIST(V)              \
  V(ConsistentJSFunctionView)           \
  V(ConstantInDictionaryPrototypeChain) \
  V(ElementsKind)                       \
  V(FieldConstness)                     \
  V(FieldRepresentation)                \
  V(FieldType)                          \
  V(GlobalProperty)                     \
  V(InitialMap)                         \
  V(InitialMapInstanceSizePrediction)   \
  V(OwnConstantDataProperty)            \
  V(OwnConstantDictionaryProperty)      \
  V(OwnConstantElement)                 \
  V(PretenureMode)                      \
  V(Protector)                          \
  V(PrototypeProperty)                  \
  V(StableMap)                          \
  V(Transition)

40 41
CompilationDependencies::CompilationDependencies(JSHeapBroker* broker,
                                                 Zone* zone)
42 43 44
    : zone_(zone), broker_(broker), dependencies_(zone) {
  broker->set_dependencies(this);
}
45

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
namespace {

enum CompilationDependencyKind {
#define V(Name) k##Name,
  DEPENDENCY_LIST(V)
#undef V
};

#define V(Name) class Name##Dependency;
DEPENDENCY_LIST(V)
#undef V

const char* CompilationDependencyKindToString(CompilationDependencyKind kind) {
#define V(Name) #Name "Dependency",
  static const char* const names[] = {DEPENDENCY_LIST(V)};
#undef V
  return names[kind];
}

}  // namespace

class CompilationDependency : public ZoneObject {
 public:
  explicit CompilationDependency(CompilationDependencyKind kind) : kind(kind) {}

  virtual bool IsValid() const = 0;
  virtual void PrepareInstall() const {}
  virtual void Install(Handle<Code> code) const = 0;

#ifdef DEBUG
#define V(Name)                                     \
  bool Is##Name() const { return kind == k##Name; } \
78
  V8_ALLOW_UNUSED const Name##Dependency* As##Name() const;
79 80 81 82 83 84 85 86 87 88 89 90 91
  DEPENDENCY_LIST(V)
#undef V
#endif

  const char* ToString() const {
    return CompilationDependencyKindToString(kind);
  }

  const CompilationDependencyKind kind;
};

namespace {

92
class InitialMapDependency final : public CompilationDependency {
93
 public:
94 95
  InitialMapDependency(JSHeapBroker* broker, const JSFunctionRef& function,
                       const MapRef& initial_map)
96 97 98
      : CompilationDependency(kInitialMap),
        function_(function),
        initial_map_(initial_map) {}
99

100
  bool IsValid() const override {
101
    Handle<JSFunction> function = function_.object();
102
    return function->has_initial_map() &&
103
           function->initial_map() == *initial_map_.object();
104 105
  }

106
  void Install(Handle<Code> code) const override {
107
    SLOW_DCHECK(IsValid());
108
    DependentCode::InstallDependency(function_.isolate(), code,
109
                                     initial_map_.object(),
110
                                     DependentCode::kInitialMapChangedGroup);
111
  }
112

113
 private:
114 115
  JSFunctionRef function_;
  MapRef initial_map_;
116
};
117

118
class PrototypePropertyDependency final : public CompilationDependency {
119
 public:
120 121
  PrototypePropertyDependency(JSHeapBroker* broker,
                              const JSFunctionRef& function,
122
                              const ObjectRef& prototype)
123 124 125
      : CompilationDependency(kPrototypeProperty),
        function_(function),
        prototype_(prototype) {
126 127 128 129
    DCHECK(function_.has_instance_prototype(broker->dependencies()));
    DCHECK(!function_.PrototypeRequiresRuntimeLookup(broker->dependencies()));
    DCHECK(function_.instance_prototype(broker->dependencies())
               .equals(prototype_));
130 131 132
  }

  bool IsValid() const override {
133
    Handle<JSFunction> function = function_.object();
134 135
    return function->has_prototype_slot() &&
           function->has_instance_prototype() &&
136
           !function->PrototypeRequiresRuntimeLookup() &&
137
           function->instance_prototype() == *prototype_.object();
138 139
  }

140
  void PrepareInstall() const override {
141
    SLOW_DCHECK(IsValid());
142
    Handle<JSFunction> function = function_.object();
143
    if (!function->has_initial_map()) JSFunction::EnsureHasInitialMap(function);
144 145
  }

146
  void Install(Handle<Code> code) const override {
147 148
    SLOW_DCHECK(IsValid());
    Handle<JSFunction> function = function_.object();
149
    CHECK(function->has_initial_map());
150 151 152 153 154 155 156 157 158 159
    Handle<Map> initial_map(function->initial_map(), function_.isolate());
    DependentCode::InstallDependency(function_.isolate(), code, initial_map,
                                     DependentCode::kInitialMapChangedGroup);
  }

 private:
  JSFunctionRef function_;
  ObjectRef prototype_;
};

160
class StableMapDependency final : public CompilationDependency {
161
 public:
162 163
  explicit StableMapDependency(const MapRef& map)
      : CompilationDependency(kStableMap), map_(map) {}
164

165 166 167 168 169 170
  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();
  }
171

172
  void Install(Handle<Code> code) const override {
173
    SLOW_DCHECK(IsValid());
174
    DependentCode::InstallDependency(map_.isolate(), code, map_.object(),
175
                                     DependentCode::kPrototypeCheckGroup);
176
  }
177

178
 private:
179
  MapRef map_;
180 181
};

182 183 184 185 186
class ConstantInDictionaryPrototypeChainDependency final
    : public CompilationDependency {
 public:
  explicit ConstantInDictionaryPrototypeChainDependency(
      const MapRef receiver_map, const NameRef property_name,
187
      const ObjectRef constant, PropertyKind kind)
188 189
      : CompilationDependency(kConstantInDictionaryPrototypeChain),
        receiver_map_(receiver_map),
190
        property_name_{property_name},
191 192
        constant_{constant},
        kind_{kind} {
193 194 195 196 197 198 199
    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(); }

200
  void Install(Handle<Code> code) const override {
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
    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;
      }

245 246 247 248 249 250 251 252 253 254 255 256
      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) {
257 258 259
        if (!dictionary_value.IsAccessorPair()) {
          return ValidationResult::kFoundIncorrect;
        }
260 261 262 263 264 265 266
        // 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;
      }
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
      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 =
282
          V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
              ? 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_;
302
  PropertyKind kind_;
303 304
};

305 306 307 308 309 310 311
class OwnConstantDataPropertyDependency final : public CompilationDependency {
 public:
  OwnConstantDataPropertyDependency(JSHeapBroker* broker,
                                    const JSObjectRef& holder,
                                    const MapRef& map,
                                    Representation representation,
                                    FieldIndex index, const ObjectRef& value)
312 313
      : CompilationDependency(kOwnConstantDataProperty),
        broker_(broker),
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
        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() ||
332 333
          HeapNumber::cast(current_value).value_as_bits(kRelaxedLoad) !=
              HeapNumber::cast(used_value).value_as_bits(kRelaxedLoad)) {
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
        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_;
};

360 361 362 363 364 365 366
class OwnConstantDictionaryPropertyDependency final
    : public CompilationDependency {
 public:
  OwnConstantDictionaryPropertyDependency(JSHeapBroker* broker,
                                          const JSObjectRef& holder,
                                          InternalIndex index,
                                          const ObjectRef& value)
367 368
      : CompilationDependency(kOwnConstantDictionaryProperty),
        broker_(broker),
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
        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_;
};

416 417 418
class ConsistentJSFunctionViewDependency final : public CompilationDependency {
 public:
  explicit ConsistentJSFunctionViewDependency(const JSFunctionRef& function)
419
      : CompilationDependency(kConsistentJSFunctionView), function_(function) {}
420 421 422 423 424 425 426 427 428 429 430

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

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

 private:
  const JSFunctionRef function_;
};

431
class TransitionDependency final : public CompilationDependency {
432
 public:
433 434
  explicit TransitionDependency(const MapRef& map)
      : CompilationDependency(kTransition), map_(map) {
435
    DCHECK(map_.CanBeDeprecated());
436 437
  }

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

440
  void Install(Handle<Code> code) const override {
441
    SLOW_DCHECK(IsValid());
442
    DependentCode::InstallDependency(map_.isolate(), code, map_.object(),
443
                                     DependentCode::kTransitionGroup);
444
  }
445

446
 private:
447
  MapRef map_;
448
};
449

450
class PretenureModeDependency final : public CompilationDependency {
451
 public:
452 453
  PretenureModeDependency(const AllocationSiteRef& site,
                          AllocationType allocation)
454 455 456
      : CompilationDependency(kPretenureMode),
        site_(site),
        allocation_(allocation) {}
457 458

  bool IsValid() const override {
459
    return allocation_ == site_.object()->GetAllocationType();
460 461
  }

462
  void Install(Handle<Code> code) const override {
463
    SLOW_DCHECK(IsValid());
464
    DependentCode::InstallDependency(
465
        site_.isolate(), code, site_.object(),
466
        DependentCode::kAllocationSiteTenuringChangedGroup);
467 468 469
  }

 private:
470
  AllocationSiteRef site_;
471
  AllocationType allocation_;
472 473
};

474
class FieldRepresentationDependency final : public CompilationDependency {
475
 public:
476
  FieldRepresentationDependency(const MapRef& map, InternalIndex descriptor,
477
                                Representation representation)
478 479 480 481
      : CompilationDependency(kFieldRepresentation),
        map_(map),
        descriptor_(descriptor),
        representation_(representation) {}
482 483

  bool IsValid() const override {
484
    DisallowGarbageCollection no_heap_allocation;
485 486 487
    if (map_.object()->is_deprecated()) return false;
    return representation_.Equals(map_.object()
                                      ->instance_descriptors(map_.isolate())
488 489
                                      .GetDetails(descriptor_)
                                      .representation());
490 491
  }

492
  void Install(Handle<Code> code) const override {
493
    SLOW_DCHECK(IsValid());
494 495 496 497 498 499 500 501
    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,
502
                                     DependentCode::kFieldRepresentationGroup);
503 504
  }

505
  bool DependsOn(const Handle<Map>& receiver_map) const {
506
    return map_.object().equals(receiver_map);
507 508
  }

509
 private:
510
  MapRef map_;
511
  InternalIndex descriptor_;
512 513 514
  Representation representation_;
};

515
class FieldTypeDependency final : public CompilationDependency {
516
 public:
517
  FieldTypeDependency(const MapRef& map, InternalIndex descriptor,
518
                      const ObjectRef& type)
519 520 521 522
      : CompilationDependency(kFieldType),
        map_(map),
        descriptor_(descriptor),
        type_(type) {}
523 524

  bool IsValid() const override {
525
    DisallowGarbageCollection no_heap_allocation;
526 527 528
    if (map_.object()->is_deprecated()) return false;
    return *type_.object() == map_.object()
                                  ->instance_descriptors(map_.isolate())
529
                                  .GetFieldType(descriptor_);
530 531
  }

532
  void Install(Handle<Code> code) const override {
533
    SLOW_DCHECK(IsValid());
534 535 536 537 538 539 540
    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,
541
                                     DependentCode::kFieldTypeGroup);
542 543 544
  }

 private:
545
  MapRef map_;
546
  InternalIndex descriptor_;
547
  ObjectRef type_;
548 549
};

550
class FieldConstnessDependency final : public CompilationDependency {
551
 public:
552
  FieldConstnessDependency(const MapRef& map, InternalIndex descriptor)
553 554 555
      : CompilationDependency(kFieldConstness),
        map_(map),
        descriptor_(descriptor) {}
556 557

  bool IsValid() const override {
558
    DisallowGarbageCollection no_heap_allocation;
559
    if (map_.object()->is_deprecated()) return false;
560
    return PropertyConstness::kConst ==
561 562
           map_.object()
               ->instance_descriptors(map_.isolate())
563 564
               .GetDetails(descriptor_)
               .constness();
565 566
  }

567
  void Install(Handle<Code> code) const override {
568
    SLOW_DCHECK(IsValid());
569 570 571 572 573 574 575 576
    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,
577
                                     DependentCode::kFieldConstGroup);
578 579 580
  }

 private:
581
  MapRef map_;
582
  InternalIndex descriptor_;
583 584
};

585
class GlobalPropertyDependency final : public CompilationDependency {
586
 public:
587
  GlobalPropertyDependency(const PropertyCellRef& cell, PropertyCellType type,
588
                           bool read_only)
589 590 591 592
      : CompilationDependency(kGlobalProperty),
        cell_(cell),
        type_(type),
        read_only_(read_only) {
593 594
    DCHECK_EQ(type_, cell_.property_details().cell_type());
    DCHECK_EQ(read_only_, cell_.property_details().IsReadOnly());
595 596 597
  }

  bool IsValid() const override {
598
    Handle<PropertyCell> cell = cell_.object();
599 600 601 602 603
    // 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;
    }
604 605
    return type_ == cell->property_details().cell_type() &&
           read_only_ == cell->property_details().IsReadOnly();
606 607
  }

608
  void Install(Handle<Code> code) const override {
609
    SLOW_DCHECK(IsValid());
610
    DependentCode::InstallDependency(cell_.isolate(), code, cell_.object(),
611
                                     DependentCode::kPropertyCellChangedGroup);
612 613 614
  }

 private:
615
  PropertyCellRef cell_;
616 617 618 619
  PropertyCellType type_;
  bool read_only_;
};

620
class ProtectorDependency final : public CompilationDependency {
621
 public:
622 623
  explicit ProtectorDependency(const PropertyCellRef& cell)
      : CompilationDependency(kProtector), cell_(cell) {}
624 625

  bool IsValid() const override {
626
    Handle<PropertyCell> cell = cell_.object();
627
    return cell->value() == Smi::FromInt(Protectors::kProtectorValid);
628 629
  }

630
  void Install(Handle<Code> code) const override {
631
    SLOW_DCHECK(IsValid());
632
    DependentCode::InstallDependency(cell_.isolate(), code, cell_.object(),
633
                                     DependentCode::kPropertyCellChangedGroup);
634 635 636
  }

 private:
637
  PropertyCellRef cell_;
638 639
};

640
class ElementsKindDependency final : public CompilationDependency {
641
 public:
642
  ElementsKindDependency(const AllocationSiteRef& site, ElementsKind kind)
643
      : CompilationDependency(kElementsKind), site_(site), kind_(kind) {
644
    DCHECK(AllocationSite::ShouldTrack(kind_));
645 646 647
  }

  bool IsValid() const override {
648
    Handle<AllocationSite> site = site_.object();
649 650 651 652
    ElementsKind kind =
        site->PointsToLiteral()
            ? site->boilerplate(kAcquireLoad).map().elements_kind()
            : site->GetElementsKind();
653 654 655
    return kind_ == kind;
  }

656
  void Install(Handle<Code> code) const override {
657
    SLOW_DCHECK(IsValid());
658
    DependentCode::InstallDependency(
659
        site_.isolate(), code, site_.object(),
660
        DependentCode::kAllocationSiteTransitionChangedGroup);
661
  }
662 663

 private:
664
  AllocationSiteRef site_;
665 666 667
  ElementsKind kind_;
};

668 669 670 671 672 673
// 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)
674 675 676 677
      : CompilationDependency(kOwnConstantElement),
        holder_(holder),
        index_(index),
        element_(element) {}
678 679 680 681 682 683 684 685 686 687 688 689

  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();
  }

690
  void Install(Handle<Code> code) const override {
691 692 693 694 695 696 697 698 699
    // This dependency has no effect after code finalization.
  }

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

700
class InitialMapInstanceSizePredictionDependency final
701
    : public CompilationDependency {
702 703 704
 public:
  InitialMapInstanceSizePredictionDependency(const JSFunctionRef& function,
                                             int instance_size)
705 706 707
      : CompilationDependency(kInitialMapInstanceSizePrediction),
        function_(function),
        instance_size_(instance_size) {}
708 709 710 711

  bool IsValid() const override {
    // The dependency is valid if the prediction is the same as the current
    // slack tracking result.
712 713 714
    if (!function_.object()->has_initial_map()) return false;
    int instance_size = function_.object()->ComputeInstanceSizeWithMinSlack(
        function_.isolate());
715 716 717
    return instance_size == instance_size_;
  }

718
  void PrepareInstall() const override {
719
    SLOW_DCHECK(IsValid());
720
    function_.object()->CompleteInobjectSlackTrackingIfActive();
721 722
  }

723
  void Install(Handle<Code> code) const override {
724
    SLOW_DCHECK(IsValid());
725 726
    DCHECK(
        !function_.object()->initial_map().IsInobjectSlackTrackingInProgress());
727 728
  }

729 730 731 732 733
 private:
  JSFunctionRef function_;
  int instance_size_;
};

734 735
}  // namespace

736 737
void CompilationDependencies::RecordDependency(
    CompilationDependency const* dependency) {
738 739 740
  if (dependency != nullptr) dependencies_.push_front(dependency);
}

741 742
MapRef CompilationDependencies::DependOnInitialMap(
    const JSFunctionRef& function) {
743 744
  MapRef map = function.initial_map(this);
  RecordDependency(zone_->New<InitialMapDependency>(broker_, function, map));
745
  return map;
746 747
}

748 749
ObjectRef CompilationDependencies::DependOnPrototypeProperty(
    const JSFunctionRef& function) {
750
  ObjectRef prototype = function.instance_prototype(this);
751
  RecordDependency(
752
      zone_->New<PrototypePropertyDependency>(broker_, function, prototype));
753 754 755
  return prototype;
}

756 757
void CompilationDependencies::DependOnStableMap(const MapRef& map) {
  if (map.CanTransition()) {
758
    RecordDependency(zone_->New<StableMapDependency>(map));
759 760
  }
}
761

762 763
void CompilationDependencies::DependOnConstantInDictionaryPrototypeChain(
    const MapRef& receiver_map, const NameRef& property_name,
764
    const ObjectRef& constant, PropertyKind kind) {
765
  RecordDependency(zone_->New<ConstantInDictionaryPrototypeChainDependency>(
766
      receiver_map, property_name, constant, kind));
767 768
}

769
AllocationType CompilationDependencies::DependOnPretenureMode(
770
    const AllocationSiteRef& site) {
771
  if (!FLAG_allocation_site_pretenuring) return AllocationType::kYoung;
772
  AllocationType allocation = site.GetAllocationType();
773
  RecordDependency(zone_->New<PretenureModeDependency>(site, allocation));
774
  return allocation;
775
}
776

777
PropertyConstness CompilationDependencies::DependOnFieldConstness(
778
    const MapRef& map, InternalIndex descriptor) {
779
  PropertyConstness constness = map.GetPropertyDetails(descriptor).constness();
780 781 782 783 784 785 786 787 788 789 790 791 792 793
  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);
794
  RecordDependency(zone_->New<FieldConstnessDependency>(map, descriptor));
795 796 797
  return PropertyConstness::kConst;
}

798
void CompilationDependencies::DependOnGlobalProperty(
799 800 801
    const PropertyCellRef& cell) {
  PropertyCellType type = cell.property_details().cell_type();
  bool read_only = cell.property_details().IsReadOnly();
802
  RecordDependency(zone_->New<GlobalPropertyDependency>(cell, type, read_only));
803 804
}

805
bool CompilationDependencies::DependOnProtector(const PropertyCellRef& cell) {
806
  cell.CacheAsProtector();
807
  if (cell.value().AsSmi() != Protectors::kProtectorValid) return false;
808
  RecordDependency(zone_->New<ProtectorDependency>(cell));
809 810 811 812
  return true;
}

bool CompilationDependencies::DependOnArrayBufferDetachingProtector() {
813
  return DependOnProtector(MakeRef(
814 815 816 817 818
      broker_,
      broker_->isolate()->factory()->array_buffer_detaching_protector()));
}

bool CompilationDependencies::DependOnArrayIteratorProtector() {
819
  return DependOnProtector(MakeRef(
820 821 822 823
      broker_, broker_->isolate()->factory()->array_iterator_protector()));
}

bool CompilationDependencies::DependOnArraySpeciesProtector() {
824
  return DependOnProtector(MakeRef(
825 826 827 828
      broker_, broker_->isolate()->factory()->array_species_protector()));
}

bool CompilationDependencies::DependOnNoElementsProtector() {
829 830
  return DependOnProtector(
      MakeRef(broker_, broker_->isolate()->factory()->no_elements_protector()));
831 832 833
}

bool CompilationDependencies::DependOnPromiseHookProtector() {
834
  return DependOnProtector(MakeRef(
835 836 837 838
      broker_, broker_->isolate()->factory()->promise_hook_protector()));
}

bool CompilationDependencies::DependOnPromiseSpeciesProtector() {
839
  return DependOnProtector(MakeRef(
840 841 842 843
      broker_, broker_->isolate()->factory()->promise_species_protector()));
}

bool CompilationDependencies::DependOnPromiseThenProtector() {
844
  return DependOnProtector(MakeRef(
845
      broker_, broker_->isolate()->factory()->promise_then_protector()));
846 847 848
}

void CompilationDependencies::DependOnElementsKind(
849 850
    const AllocationSiteRef& site) {
  ElementsKind kind = site.PointsToLiteral()
851
                          ? site.boilerplate().value().map().elements_kind()
852
                          : site.GetElementsKind();
853
  if (AllocationSite::ShouldTrack(kind)) {
854
    RecordDependency(zone_->New<ElementsKindDependency>(site, kind));
855 856 857
  }
}

858 859 860 861 862 863 864 865 866
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));
}

867 868 869 870 871 872 873
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));
}

874 875 876 877 878 879
void CompilationDependencies::DependOnOwnConstantDictionaryProperty(
    const JSObjectRef& holder, InternalIndex index, const ObjectRef& value) {
  RecordDependency(zone_->New<OwnConstantDictionaryPropertyDependency>(
      broker_, holder, index, value));
}

880 881 882 883 884 885 886
V8_INLINE void TraceInvalidCompilationDependency(
    const CompilationDependency* d) {
  DCHECK(FLAG_trace_compilation_dependencies);
  DCHECK(!d->IsValid());
  PrintF("Compilation aborted due to invalid dependency: %s\n", d->ToString());
}

887
bool CompilationDependencies::Commit(Handle<Code> code) {
888 889
  for (auto dep : dependencies_) {
    if (!dep->IsValid()) {
890 891 892
      if (FLAG_trace_compilation_dependencies) {
        TraceInvalidCompilationDependency(dep);
      }
893 894 895 896
      dependencies_.clear();
      return false;
    }
    dep->PrepareInstall();
897 898
  }

899
  DisallowCodeDependencyChange no_dependency_change;
900
  for (auto dep : dependencies_) {
901
    // Check each dependency's validity again right before installing it,
902 903 904 905
    // 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.
906
    if (!dep->IsValid()) {
907 908 909
      if (FLAG_trace_compilation_dependencies) {
        TraceInvalidCompilationDependency(dep);
      }
910 911 912
      dependencies_.clear();
      return false;
    }
913
    dep->Install(code);
914
  }
915

916
  // It is even possible that a GC during the above installations invalidated
917 918 919 920 921 922 923 924 925 926 927 928
  // 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.
929
  if (FLAG_stress_gc_during_compilation) {
930
    broker_->isolate()->heap()->PreciseCollectAllGarbage(
931
        Heap::kForcedGC, GarbageCollectionReason::kTesting, kNoGCCallbackFlags);
932
  }
933 934
#ifdef DEBUG
  for (auto dep : dependencies_) {
935
    CHECK_IMPLIES(!dep->IsValid(),
936
                  dep->IsPretenureMode() || dep->IsConsistentJSFunctionView());
937 938
  }
#endif
939

940 941 942 943 944
  dependencies_.clear();
  return true;
}

namespace {
945

946
// This function expects to never see a JSProxy.
947
void DependOnStablePrototypeChain(CompilationDependencies* deps, MapRef map,
948
                                  base::Optional<JSObjectRef> last_prototype) {
949
  while (true) {
950
    HeapObjectRef proto = map.prototype().value();
951 952 953 954
    if (!proto.IsJSObject()) {
      CHECK_EQ(proto.map().oddball_type(), OddballType::kNull);
      break;
    }
955 956
    map = proto.map();
    deps->DependOnStableMap(map);
957
    if (last_prototype.has_value() && proto.equals(*last_prototype)) break;
958 959
  }
}
960

961
}  // namespace
962

963 964 965 966 967 968 969 970 971 972
#ifdef DEBUG
#define V(Name)                                                     \
  const Name##Dependency* CompilationDependency::As##Name() const { \
    DCHECK(Is##Name());                                             \
    return static_cast<const Name##Dependency*>(this);              \
  }
DEPENDENCY_LIST(V)
#undef V
#endif  // DEBUG

973
void CompilationDependencies::DependOnStablePrototypeChains(
974
    ZoneVector<MapRef> const& receiver_maps, WhereToStart start,
975
    base::Optional<JSObjectRef> last_prototype) {
976
  for (MapRef receiver_map : receiver_maps) {
977 978 979
    if (receiver_map.IsPrimitiveMap()) {
      // Perform the implicit ToObject for primitives here.
      // Implemented according to ES6 section 7.3.2 GetV (V, P).
980
      // Note: Keep sync'd with AccessInfoFactory::ComputePropertyAccessInfo.
981
      base::Optional<JSFunctionRef> constructor =
982
          broker_->target_native_context().GetConstructorFunction(receiver_map);
983
      receiver_map = constructor.value().initial_map(this);
984
    }
985
    if (start == kStartAtReceiver) DependOnStableMap(receiver_map);
986
    DependOnStablePrototypeChain(this, receiver_map, last_prototype);
987 988
  }
}
989

990
void CompilationDependencies::DependOnElementsKinds(
991 992
    const AllocationSiteRef& site) {
  AllocationSiteRef current = site;
993
  while (true) {
994 995 996
    DependOnElementsKind(current);
    if (!current.nested_site().IsAllocationSite()) break;
    current = current.nested_site().AsAllocationSite();
997
  }
998
  CHECK_EQ(current.nested_site().AsSmi(), 0);
999
}
1000

1001 1002 1003 1004 1005 1006
void CompilationDependencies::DependOnConsistentJSFunctionView(
    const JSFunctionRef& function) {
  DCHECK(broker_->is_concurrent_inlining());
  RecordDependency(zone_->New<ConsistentJSFunctionViewDependency>(function));
}

1007 1008 1009 1010
SlackTrackingPrediction::SlackTrackingPrediction(MapRef initial_map,
                                                 int instance_size)
    : instance_size_(instance_size),
      inobject_property_count_(
1011
          (instance_size >> kTaggedSizeLog2) -
1012 1013 1014 1015 1016 1017
          initial_map.GetInObjectPropertiesStartInWords()) {}

SlackTrackingPrediction
CompilationDependencies::DependOnInitialMapInstanceSizePrediction(
    const JSFunctionRef& function) {
  MapRef initial_map = DependOnInitialMap(function);
1018
  int instance_size = function.InitialMapInstanceSizeWithMinSlack(this);
1019 1020 1021
  // 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.
1022
  RecordDependency(zone_->New<InitialMapInstanceSizePredictionDependency>(
1023
      function, instance_size));
1024
  CHECK_LE(instance_size, function.initial_map(this).instance_size());
1025 1026 1027
  return SlackTrackingPrediction(initial_map, instance_size);
}

1028
CompilationDependency const*
1029 1030 1031
CompilationDependencies::TransitionDependencyOffTheRecord(
    const MapRef& target_map) const {
  if (target_map.CanBeDeprecated()) {
1032
    return zone_->New<TransitionDependency>(target_map);
1033 1034 1035 1036 1037 1038
  } else {
    DCHECK(!target_map.is_deprecated());
    return nullptr;
  }
}

1039
CompilationDependency const*
1040
CompilationDependencies::FieldRepresentationDependencyOffTheRecord(
1041 1042
    const MapRef& map, InternalIndex descriptor,
    Representation representation) const {
1043 1044
  return zone_->New<FieldRepresentationDependency>(map, descriptor,
                                                   representation);
1045 1046
}

1047
CompilationDependency const*
1048
CompilationDependencies::FieldTypeDependencyOffTheRecord(
1049
    const MapRef& map, InternalIndex descriptor, const ObjectRef& type) const {
1050
  return zone_->New<FieldTypeDependency>(map, descriptor, type);
1051 1052
}

1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
#ifdef DEBUG
// static
bool CompilationDependencies::IsFieldRepresentationDependencyOnMap(
    const CompilationDependency* dep, const Handle<Map>& receiver_map) {
  return dep->IsFieldRepresentation() &&
         dep->AsFieldRepresentation()->DependsOn(receiver_map);
}
#endif  // DEBUG

#undef DEPENDENCY_LIST

1064
}  // namespace compiler
1065 1066
}  // namespace internal
}  // namespace v8