Home | History | Annotate | Download | only in compiler
      1 // Copyright 2015 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/compiler/compilation-dependencies.h"
      6 
      7 #include "src/handles-inl.h"
      8 #include "src/objects-inl.h"
      9 
     10 namespace v8 {
     11 namespace internal {
     12 namespace compiler {
     13 
     14 CompilationDependencies::CompilationDependencies(Isolate* isolate, Zone* zone)
     15     : zone_(zone), dependencies_(zone) {}
     16 
     17 class CompilationDependencies::Dependency : public ZoneObject {
     18  public:
     19   virtual bool IsValid() const = 0;
     20   virtual void Install(MaybeObjectHandle code) = 0;
     21 };
     22 
     23 class InitialMapDependency final : public CompilationDependencies::Dependency {
     24  public:
     25   // TODO(neis): Once the concurrent compiler frontend is always-on, we no
     26   // longer need to explicitly store the initial map.
     27   InitialMapDependency(const JSFunctionRef& function, const MapRef& initial_map)
     28       : function_(function), initial_map_(initial_map) {
     29     DCHECK(function_.has_initial_map());
     30     DCHECK(function_.initial_map().equals(initial_map_));
     31   }
     32 
     33   bool IsValid() const override {
     34     Handle<JSFunction> function = function_.object<JSFunction>();
     35     return function->has_initial_map() &&
     36            function->initial_map() == *initial_map_.object<Map>();
     37   }
     38 
     39   void Install(MaybeObjectHandle code) override {
     40     SLOW_DCHECK(IsValid());
     41     DependentCode::InstallDependency(function_.isolate(), code,
     42                                      initial_map_.object<Map>(),
     43                                      DependentCode::kInitialMapChangedGroup);
     44   }
     45 
     46  private:
     47   JSFunctionRef function_;
     48   MapRef initial_map_;
     49 };
     50 
     51 class PrototypePropertyDependency final
     52     : public CompilationDependencies::Dependency {
     53  public:
     54   // TODO(neis): Once the concurrent compiler frontend is always-on, we no
     55   // longer need to explicitly store the prototype.
     56   PrototypePropertyDependency(const JSFunctionRef& function,
     57                               const ObjectRef& prototype)
     58       : function_(function), prototype_(prototype) {
     59     DCHECK(function_.has_prototype());
     60     DCHECK(!function_.PrototypeRequiresRuntimeLookup());
     61     DCHECK(function_.prototype().equals(prototype_));
     62   }
     63 
     64   bool IsValid() const override {
     65     Handle<JSFunction> function = function_.object<JSFunction>();
     66     return function->has_prototype_slot() && function->has_prototype() &&
     67            !function->PrototypeRequiresRuntimeLookup() &&
     68            function->prototype() == *prototype_.object();
     69   }
     70 
     71   void Install(MaybeObjectHandle code) override {
     72     SLOW_DCHECK(IsValid());
     73     Handle<JSFunction> function = function_.object<JSFunction>();
     74     if (!function->has_initial_map()) JSFunction::EnsureHasInitialMap(function);
     75     Handle<Map> initial_map(function->initial_map(), function_.isolate());
     76     DependentCode::InstallDependency(function_.isolate(), code, initial_map,
     77                                      DependentCode::kInitialMapChangedGroup);
     78   }
     79 
     80  private:
     81   JSFunctionRef function_;
     82   ObjectRef prototype_;
     83 };
     84 
     85 class StableMapDependency final : public CompilationDependencies::Dependency {
     86  public:
     87   explicit StableMapDependency(const MapRef& map) : map_(map) {
     88     DCHECK(map_.is_stable());
     89   }
     90 
     91   bool IsValid() const override { return map_.object<Map>()->is_stable(); }
     92 
     93   void Install(MaybeObjectHandle code) override {
     94     SLOW_DCHECK(IsValid());
     95     DependentCode::InstallDependency(map_.isolate(), code, map_.object<Map>(),
     96                                      DependentCode::kPrototypeCheckGroup);
     97   }
     98 
     99  private:
    100   MapRef map_;
    101 };
    102 
    103 class TransitionDependency final : public CompilationDependencies::Dependency {
    104  public:
    105   explicit TransitionDependency(const MapRef& map) : map_(map) {
    106     DCHECK(!map_.is_deprecated());
    107   }
    108 
    109   bool IsValid() const override { return !map_.object<Map>()->is_deprecated(); }
    110 
    111   void Install(MaybeObjectHandle code) override {
    112     SLOW_DCHECK(IsValid());
    113     DependentCode::InstallDependency(map_.isolate(), code, map_.object<Map>(),
    114                                      DependentCode::kTransitionGroup);
    115   }
    116 
    117  private:
    118   MapRef map_;
    119 };
    120 
    121 class PretenureModeDependency final
    122     : public CompilationDependencies::Dependency {
    123  public:
    124   // TODO(neis): Once the concurrent compiler frontend is always-on, we no
    125   // longer need to explicitly store the mode.
    126   PretenureModeDependency(const AllocationSiteRef& site, PretenureFlag mode)
    127       : site_(site), mode_(mode) {
    128     DCHECK_EQ(mode_, site_.GetPretenureMode());
    129   }
    130 
    131   bool IsValid() const override {
    132     return mode_ == site_.object<AllocationSite>()->GetPretenureMode();
    133   }
    134 
    135   void Install(MaybeObjectHandle code) override {
    136     SLOW_DCHECK(IsValid());
    137     DependentCode::InstallDependency(
    138         site_.isolate(), code, site_.object<AllocationSite>(),
    139         DependentCode::kAllocationSiteTenuringChangedGroup);
    140   }
    141 
    142  private:
    143   AllocationSiteRef site_;
    144   PretenureFlag mode_;
    145 };
    146 
    147 class FieldTypeDependency final : public CompilationDependencies::Dependency {
    148  public:
    149   // TODO(neis): Once the concurrent compiler frontend is always-on, we no
    150   // longer need to explicitly store the type.
    151   FieldTypeDependency(const MapRef& owner, int descriptor,
    152                       const ObjectRef& type)
    153       : owner_(owner), descriptor_(descriptor), type_(type) {
    154     DCHECK(owner_.equals(owner_.FindFieldOwner(descriptor_)));
    155     DCHECK(type_.equals(owner_.GetFieldType(descriptor_)));
    156   }
    157 
    158   bool IsValid() const override {
    159     DisallowHeapAllocation no_heap_allocation;
    160     Handle<Map> owner = owner_.object<Map>();
    161     Handle<FieldType> type = type_.object<FieldType>();
    162     return *type == owner->instance_descriptors()->GetFieldType(descriptor_);
    163   }
    164 
    165   void Install(MaybeObjectHandle code) override {
    166     SLOW_DCHECK(IsValid());
    167     DependentCode::InstallDependency(owner_.isolate(), code,
    168                                      owner_.object<Map>(),
    169                                      DependentCode::kFieldOwnerGroup);
    170   }
    171 
    172  private:
    173   MapRef owner_;
    174   int descriptor_;
    175   ObjectRef type_;
    176 };
    177 
    178 class GlobalPropertyDependency final
    179     : public CompilationDependencies::Dependency {
    180  public:
    181   // TODO(neis): Once the concurrent compiler frontend is always-on, we no
    182   // longer need to explicitly store the type and the read_only flag.
    183   GlobalPropertyDependency(const PropertyCellRef& cell, PropertyCellType type,
    184                            bool read_only)
    185       : cell_(cell), type_(type), read_only_(read_only) {
    186     DCHECK_EQ(type_, cell_.property_details().cell_type());
    187     DCHECK_EQ(read_only_, cell_.property_details().IsReadOnly());
    188   }
    189 
    190   bool IsValid() const override {
    191     Handle<PropertyCell> cell = cell_.object<PropertyCell>();
    192     return type_ == cell->property_details().cell_type() &&
    193            read_only_ == cell->property_details().IsReadOnly();
    194   }
    195 
    196   void Install(MaybeObjectHandle code) override {
    197     SLOW_DCHECK(IsValid());
    198     DependentCode::InstallDependency(cell_.isolate(), code,
    199                                      cell_.object<PropertyCell>(),
    200                                      DependentCode::kPropertyCellChangedGroup);
    201   }
    202 
    203  private:
    204   PropertyCellRef cell_;
    205   PropertyCellType type_;
    206   bool read_only_;
    207 };
    208 
    209 class ProtectorDependency final : public CompilationDependencies::Dependency {
    210  public:
    211   explicit ProtectorDependency(const PropertyCellRef& cell) : cell_(cell) {
    212     DCHECK_EQ(cell_.value().AsSmi(), Isolate::kProtectorValid);
    213   }
    214 
    215   bool IsValid() const override {
    216     Handle<PropertyCell> cell = cell_.object<PropertyCell>();
    217     return cell->value() == Smi::FromInt(Isolate::kProtectorValid);
    218   }
    219 
    220   void Install(MaybeObjectHandle code) override {
    221     SLOW_DCHECK(IsValid());
    222     DependentCode::InstallDependency(cell_.isolate(), code,
    223                                      cell_.object<PropertyCell>(),
    224                                      DependentCode::kPropertyCellChangedGroup);
    225   }
    226 
    227  private:
    228   PropertyCellRef cell_;
    229 };
    230 
    231 class ElementsKindDependency final
    232     : public CompilationDependencies::Dependency {
    233  public:
    234   // TODO(neis): Once the concurrent compiler frontend is always-on, we no
    235   // longer need to explicitly store the elements kind.
    236   ElementsKindDependency(const AllocationSiteRef& site, ElementsKind kind)
    237       : site_(site), kind_(kind) {
    238     DCHECK(AllocationSite::ShouldTrack(kind_));
    239     DCHECK_EQ(kind_, site_.PointsToLiteral()
    240                          ? site_.boilerplate().value().GetElementsKind()
    241                          : site_.GetElementsKind());
    242   }
    243 
    244   bool IsValid() const override {
    245     Handle<AllocationSite> site = site_.object<AllocationSite>();
    246     ElementsKind kind = site->PointsToLiteral()
    247                             ? site->boilerplate()->GetElementsKind()
    248                             : site->GetElementsKind();
    249     return kind_ == kind;
    250   }
    251 
    252   void Install(MaybeObjectHandle code) override {
    253     SLOW_DCHECK(IsValid());
    254     DependentCode::InstallDependency(
    255         site_.isolate(), code, site_.object<AllocationSite>(),
    256         DependentCode::kAllocationSiteTransitionChangedGroup);
    257   }
    258 
    259  private:
    260   AllocationSiteRef site_;
    261   ElementsKind kind_;
    262 };
    263 
    264 class InitialMapInstanceSizePredictionDependency final
    265     : public CompilationDependencies::Dependency {
    266  public:
    267   InitialMapInstanceSizePredictionDependency(const JSFunctionRef& function,
    268                                              int instance_size)
    269       : function_(function), instance_size_(instance_size) {}
    270 
    271   bool IsValid() const override {
    272     // The dependency is valid if the prediction is the same as the current
    273     // slack tracking result.
    274     int instance_size =
    275         function_.object<JSFunction>()->ComputeInstanceSizeWithMinSlack(
    276             function_.isolate());
    277     return instance_size == instance_size_;
    278   }
    279 
    280   void Install(MaybeObjectHandle code) override {
    281     DCHECK(IsValid());
    282     // Finish the slack tracking.
    283     function_.object<JSFunction>()->CompleteInobjectSlackTrackingIfActive();
    284   }
    285 
    286  private:
    287   JSFunctionRef function_;
    288   int instance_size_;
    289 };
    290 
    291 MapRef CompilationDependencies::DependOnInitialMap(
    292     const JSFunctionRef& function) {
    293   MapRef map = function.initial_map();
    294   dependencies_.push_front(new (zone_) InitialMapDependency(function, map));
    295   return map;
    296 }
    297 
    298 ObjectRef CompilationDependencies::DependOnPrototypeProperty(
    299     const JSFunctionRef& function) {
    300   ObjectRef prototype = function.prototype();
    301   dependencies_.push_front(
    302       new (zone_) PrototypePropertyDependency(function, prototype));
    303   return prototype;
    304 }
    305 
    306 void CompilationDependencies::DependOnStableMap(const MapRef& map) {
    307   if (map.CanTransition()) {
    308     dependencies_.push_front(new (zone_) StableMapDependency(map));
    309   } else {
    310     DCHECK(map.is_stable());
    311   }
    312 }
    313 
    314 void CompilationDependencies::DependOnTransition(const MapRef& target_map) {
    315   if (target_map.CanBeDeprecated()) {
    316     dependencies_.push_front(new (zone_) TransitionDependency(target_map));
    317   } else {
    318     DCHECK(!target_map.is_deprecated());
    319   }
    320 }
    321 
    322 PretenureFlag CompilationDependencies::DependOnPretenureMode(
    323     const AllocationSiteRef& site) {
    324   PretenureFlag mode = site.GetPretenureMode();
    325   dependencies_.push_front(new (zone_) PretenureModeDependency(site, mode));
    326   return mode;
    327 }
    328 
    329 void CompilationDependencies::DependOnFieldType(const MapRef& map,
    330                                                 int descriptor) {
    331   MapRef owner = map.FindFieldOwner(descriptor);
    332   ObjectRef type = owner.GetFieldType(descriptor);
    333   DCHECK(type.equals(map.GetFieldType(descriptor)));
    334   dependencies_.push_front(new (zone_)
    335                                FieldTypeDependency(owner, descriptor, type));
    336 }
    337 
    338 void CompilationDependencies::DependOnGlobalProperty(
    339     const PropertyCellRef& cell) {
    340   PropertyCellType type = cell.property_details().cell_type();
    341   bool read_only = cell.property_details().IsReadOnly();
    342   dependencies_.push_front(new (zone_)
    343                                GlobalPropertyDependency(cell, type, read_only));
    344 }
    345 
    346 void CompilationDependencies::DependOnProtector(const PropertyCellRef& cell) {
    347   dependencies_.push_front(new (zone_) ProtectorDependency(cell));
    348 }
    349 
    350 void CompilationDependencies::DependOnElementsKind(
    351     const AllocationSiteRef& site) {
    352   // Do nothing if the object doesn't have any useful element transitions left.
    353   ElementsKind kind = site.PointsToLiteral()
    354                           ? site.boilerplate().value().GetElementsKind()
    355                           : site.GetElementsKind();
    356   if (AllocationSite::ShouldTrack(kind)) {
    357     dependencies_.push_front(new (zone_) ElementsKindDependency(site, kind));
    358   }
    359 }
    360 
    361 bool CompilationDependencies::AreValid() const {
    362   for (auto dep : dependencies_) {
    363     if (!dep->IsValid()) return false;
    364   }
    365   return true;
    366 }
    367 
    368 bool CompilationDependencies::Commit(Handle<Code> code) {
    369   // Check validity of all dependencies first, such that we can avoid installing
    370   // anything when there's already an invalid dependency.
    371   if (!AreValid()) {
    372     dependencies_.clear();
    373     return false;
    374   }
    375 
    376   for (auto dep : dependencies_) {
    377     // Check each dependency's validity again right before installing it,
    378     // because a GC can trigger invalidation for some dependency kinds.
    379     if (!dep->IsValid()) {
    380       dependencies_.clear();
    381       return false;
    382     }
    383     dep->Install(MaybeObjectHandle::Weak(code));
    384   }
    385   dependencies_.clear();
    386   return true;
    387 }
    388 
    389 namespace {
    390 void DependOnStablePrototypeChain(JSHeapBroker* broker,
    391                                   CompilationDependencies* deps,
    392                                   Handle<Map> map,
    393                                   MaybeHandle<JSReceiver> last_prototype) {
    394   for (PrototypeIterator i(broker->isolate(), map); !i.IsAtEnd(); i.Advance()) {
    395     Handle<JSReceiver> const current =
    396         PrototypeIterator::GetCurrent<JSReceiver>(i);
    397     deps->DependOnStableMap(
    398         MapRef(broker, handle(current->map(), broker->isolate())));
    399     Handle<JSReceiver> last;
    400     if (last_prototype.ToHandle(&last) && last.is_identical_to(current)) {
    401       break;
    402     }
    403   }
    404 }
    405 }  // namespace
    406 
    407 void CompilationDependencies::DependOnStablePrototypeChains(
    408     JSHeapBroker* broker, Handle<Context> native_context,
    409     std::vector<Handle<Map>> const& receiver_maps, Handle<JSObject> holder) {
    410   Isolate* isolate = holder->GetIsolate();
    411   // Determine actual holder and perform prototype chain checks.
    412   for (auto map : receiver_maps) {
    413     // Perform the implicit ToObject for primitives here.
    414     // Implemented according to ES6 section 7.3.2 GetV (V, P).
    415     Handle<JSFunction> constructor;
    416     if (Map::GetConstructorFunction(map, native_context)
    417             .ToHandle(&constructor)) {
    418       map = handle(constructor->initial_map(), isolate);
    419     }
    420     DependOnStablePrototypeChain(broker, this, map, holder);
    421   }
    422 }
    423 
    424 void CompilationDependencies::DependOnElementsKinds(
    425     const AllocationSiteRef& site) {
    426   AllocationSiteRef current = site;
    427   while (true) {
    428     DependOnElementsKind(current);
    429     if (!current.nested_site().IsAllocationSite()) break;
    430     current = current.nested_site().AsAllocationSite();
    431   }
    432   CHECK_EQ(current.nested_site().AsSmi(), 0);
    433 }
    434 
    435 SlackTrackingPrediction::SlackTrackingPrediction(MapRef initial_map,
    436                                                  int instance_size)
    437     : instance_size_(instance_size),
    438       inobject_property_count_(
    439           (instance_size >> kPointerSizeLog2) -
    440           initial_map.GetInObjectPropertiesStartInWords()) {}
    441 
    442 SlackTrackingPrediction
    443 CompilationDependencies::DependOnInitialMapInstanceSizePrediction(
    444     const JSFunctionRef& function) {
    445   MapRef initial_map = DependOnInitialMap(function);
    446   int instance_size = function.InitialMapInstanceSizeWithMinSlack();
    447   // Currently, we always install the prediction dependency. If this turns out
    448   // to be too expensive, we can only install the dependency if slack
    449   // tracking is active.
    450   dependencies_.push_front(
    451       new (zone_)
    452           InitialMapInstanceSizePredictionDependency(function, instance_size));
    453   DCHECK_LE(instance_size, function.initial_map().instance_size());
    454   return SlackTrackingPrediction(initial_map, instance_size);
    455 }
    456 
    457 }  // namespace compiler
    458 }  // namespace internal
    459 }  // namespace v8
    460