// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/compiler/compilation-dependencies.h" #include "src/handles-inl.h" #include "src/objects-inl.h" namespace v8 { namespace internal { namespace compiler { CompilationDependencies::CompilationDependencies(Isolate* isolate, Zone* zone) : zone_(zone), dependencies_(zone) {} class CompilationDependencies::Dependency : public ZoneObject { public: virtual bool IsValid() const = 0; virtual void Install(const MaybeObjectHandle& code) = 0; }; class InitialMapDependency final : public CompilationDependencies::Dependency { public: // TODO(neis): Once the concurrent compiler frontend is always-on, we no // longer need to explicitly store the initial map. InitialMapDependency(const JSFunctionRef& function, const MapRef& initial_map) : function_(function), initial_map_(initial_map) { DCHECK(function_.has_initial_map()); DCHECK(function_.initial_map().equals(initial_map_)); } bool IsValid() const override { Handle function = function_.object(); return function->has_initial_map() && function->initial_map() == *initial_map_.object(); } void Install(const MaybeObjectHandle& code) override { SLOW_DCHECK(IsValid()); DependentCode::InstallDependency(function_.isolate(), code, initial_map_.object(), DependentCode::kInitialMapChangedGroup); } private: JSFunctionRef function_; MapRef initial_map_; }; class PrototypePropertyDependency final : public CompilationDependencies::Dependency { public: // TODO(neis): Once the concurrent compiler frontend is always-on, we no // longer need to explicitly store the prototype. PrototypePropertyDependency(const JSFunctionRef& function, const ObjectRef& prototype) : function_(function), prototype_(prototype) { DCHECK(function_.has_prototype()); DCHECK(!function_.PrototypeRequiresRuntimeLookup()); DCHECK(function_.prototype().equals(prototype_)); } bool IsValid() const override { Handle function = function_.object(); return function->has_prototype_slot() && function->has_prototype() && !function->PrototypeRequiresRuntimeLookup() && function->prototype() == *prototype_.object(); } void Install(const MaybeObjectHandle& code) override { SLOW_DCHECK(IsValid()); Handle function = function_.object(); if (!function->has_initial_map()) JSFunction::EnsureHasInitialMap(function); Handle initial_map(function->initial_map(), function_.isolate()); DependentCode::InstallDependency(function_.isolate(), code, initial_map, DependentCode::kInitialMapChangedGroup); } private: JSFunctionRef function_; ObjectRef prototype_; }; class StableMapDependency final : public CompilationDependencies::Dependency { public: explicit StableMapDependency(const MapRef& map) : map_(map) { DCHECK(map_.is_stable()); } bool IsValid() const override { return map_.object()->is_stable(); } void Install(const MaybeObjectHandle& code) override { SLOW_DCHECK(IsValid()); DependentCode::InstallDependency(map_.isolate(), code, map_.object(), DependentCode::kPrototypeCheckGroup); } private: MapRef map_; }; class TransitionDependency final : public CompilationDependencies::Dependency { public: explicit TransitionDependency(const MapRef& map) : map_(map) { DCHECK(!map_.is_deprecated()); } bool IsValid() const override { return !map_.object()->is_deprecated(); } void Install(const MaybeObjectHandle& code) override { SLOW_DCHECK(IsValid()); DependentCode::InstallDependency(map_.isolate(), code, map_.object(), DependentCode::kTransitionGroup); } private: MapRef map_; }; class PretenureModeDependency final : public CompilationDependencies::Dependency { public: // TODO(neis): Once the concurrent compiler frontend is always-on, we no // longer need to explicitly store the mode. PretenureModeDependency(const AllocationSiteRef& site, PretenureFlag mode) : site_(site), mode_(mode) { DCHECK_EQ(mode_, site_.GetPretenureMode()); } bool IsValid() const override { return mode_ == site_.object()->GetPretenureMode(); } void Install(const MaybeObjectHandle& code) override { SLOW_DCHECK(IsValid()); DependentCode::InstallDependency( site_.isolate(), code, site_.object(), DependentCode::kAllocationSiteTenuringChangedGroup); } private: AllocationSiteRef site_; PretenureFlag mode_; }; class FieldTypeDependency final : public CompilationDependencies::Dependency { public: // TODO(neis): Once the concurrent compiler frontend is always-on, we no // longer need to explicitly store the type. FieldTypeDependency(const MapRef& owner, int descriptor, const ObjectRef& type) : owner_(owner), descriptor_(descriptor), type_(type) { DCHECK(owner_.equals(owner_.FindFieldOwner(descriptor_))); DCHECK(type_.equals(owner_.GetFieldType(descriptor_))); } bool IsValid() const override { DisallowHeapAllocation no_heap_allocation; Handle owner = owner_.object(); Handle type = type_.object(); return *type == owner->instance_descriptors()->GetFieldType(descriptor_); } void Install(const MaybeObjectHandle& code) override { SLOW_DCHECK(IsValid()); DependentCode::InstallDependency(owner_.isolate(), code, owner_.object(), DependentCode::kFieldOwnerGroup); } private: MapRef owner_; int descriptor_; ObjectRef type_; }; class GlobalPropertyDependency final : public CompilationDependencies::Dependency { public: // TODO(neis): Once the concurrent compiler frontend is always-on, we no // longer need to explicitly store the type and the read_only flag. GlobalPropertyDependency(const PropertyCellRef& cell, PropertyCellType type, bool read_only) : cell_(cell), type_(type), read_only_(read_only) { DCHECK_EQ(type_, cell_.property_details().cell_type()); DCHECK_EQ(read_only_, cell_.property_details().IsReadOnly()); } bool IsValid() const override { Handle cell = cell_.object(); return type_ == cell->property_details().cell_type() && read_only_ == cell->property_details().IsReadOnly(); } void Install(const MaybeObjectHandle& code) override { SLOW_DCHECK(IsValid()); DependentCode::InstallDependency(cell_.isolate(), code, cell_.object(), DependentCode::kPropertyCellChangedGroup); } private: PropertyCellRef cell_; PropertyCellType type_; bool read_only_; }; class ProtectorDependency final : public CompilationDependencies::Dependency { public: explicit ProtectorDependency(const PropertyCellRef& cell) : cell_(cell) { DCHECK_EQ(cell_.value().AsSmi(), Isolate::kProtectorValid); } bool IsValid() const override { Handle cell = cell_.object(); return cell->value() == Smi::FromInt(Isolate::kProtectorValid); } void Install(const MaybeObjectHandle& code) override { SLOW_DCHECK(IsValid()); DependentCode::InstallDependency(cell_.isolate(), code, cell_.object(), DependentCode::kPropertyCellChangedGroup); } private: PropertyCellRef cell_; }; class ElementsKindDependency final : public CompilationDependencies::Dependency { public: // TODO(neis): Once the concurrent compiler frontend is always-on, we no // longer need to explicitly store the elements kind. ElementsKindDependency(const AllocationSiteRef& site, ElementsKind kind) : site_(site), kind_(kind) { DCHECK(AllocationSite::ShouldTrack(kind_)); DCHECK_EQ(kind_, site_.PointsToLiteral() ? site_.boilerplate().value().GetElementsKind() : site_.GetElementsKind()); } bool IsValid() const override { Handle site = site_.object(); ElementsKind kind = site->PointsToLiteral() ? site->boilerplate()->GetElementsKind() : site->GetElementsKind(); return kind_ == kind; } void Install(const MaybeObjectHandle& code) override { SLOW_DCHECK(IsValid()); DependentCode::InstallDependency( site_.isolate(), code, site_.object(), DependentCode::kAllocationSiteTransitionChangedGroup); } private: AllocationSiteRef site_; ElementsKind kind_; }; class InitialMapInstanceSizePredictionDependency final : public CompilationDependencies::Dependency { 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. if (!function_.object()->has_initial_map()) return false; int instance_size = function_.object()->ComputeInstanceSizeWithMinSlack( function_.isolate()); return instance_size == instance_size_; } void Install(const MaybeObjectHandle& code) override { DCHECK(IsValid()); // Finish the slack tracking. function_.object()->CompleteInobjectSlackTrackingIfActive(); } private: JSFunctionRef function_; int instance_size_; }; MapRef CompilationDependencies::DependOnInitialMap( const JSFunctionRef& function) { MapRef map = function.initial_map(); dependencies_.push_front(new (zone_) InitialMapDependency(function, map)); return map; } ObjectRef CompilationDependencies::DependOnPrototypeProperty( const JSFunctionRef& function) { ObjectRef prototype = function.prototype(); dependencies_.push_front( new (zone_) PrototypePropertyDependency(function, prototype)); return prototype; } void CompilationDependencies::DependOnStableMap(const MapRef& map) { if (map.CanTransition()) { dependencies_.push_front(new (zone_) StableMapDependency(map)); } else { DCHECK(map.is_stable()); } } void CompilationDependencies::DependOnTransition(const MapRef& target_map) { if (target_map.CanBeDeprecated()) { dependencies_.push_front(new (zone_) TransitionDependency(target_map)); } else { DCHECK(!target_map.is_deprecated()); } } PretenureFlag CompilationDependencies::DependOnPretenureMode( const AllocationSiteRef& site) { PretenureFlag mode = site.GetPretenureMode(); dependencies_.push_front(new (zone_) PretenureModeDependency(site, mode)); return mode; } void CompilationDependencies::DependOnFieldType(const MapRef& map, int descriptor) { MapRef owner = map.FindFieldOwner(descriptor); ObjectRef type = owner.GetFieldType(descriptor); DCHECK(type.equals(map.GetFieldType(descriptor))); dependencies_.push_front(new (zone_) FieldTypeDependency(owner, descriptor, type)); } void CompilationDependencies::DependOnGlobalProperty( const PropertyCellRef& cell) { PropertyCellType type = cell.property_details().cell_type(); bool read_only = cell.property_details().IsReadOnly(); dependencies_.push_front(new (zone_) GlobalPropertyDependency(cell, type, read_only)); } void CompilationDependencies::DependOnProtector(const PropertyCellRef& cell) { dependencies_.push_front(new (zone_) ProtectorDependency(cell)); } void CompilationDependencies::DependOnElementsKind( const AllocationSiteRef& site) { // Do nothing if the object doesn't have any useful element transitions left. ElementsKind kind = site.PointsToLiteral() ? site.boilerplate().value().GetElementsKind() : site.GetElementsKind(); if (AllocationSite::ShouldTrack(kind)) { dependencies_.push_front(new (zone_) ElementsKindDependency(site, kind)); } } bool CompilationDependencies::AreValid() const { for (auto dep : dependencies_) { if (!dep->IsValid()) return false; } return true; } bool CompilationDependencies::Commit(Handle code) { // Check validity of all dependencies first, such that we can avoid installing // anything when there's already an invalid dependency. if (!AreValid()) { dependencies_.clear(); return false; } for (auto dep : dependencies_) { // Check each dependency's validity again right before installing it, // because a GC can trigger invalidation for some dependency kinds. if (!dep->IsValid()) { dependencies_.clear(); return false; } dep->Install(MaybeObjectHandle::Weak(code)); } dependencies_.clear(); return true; } namespace { void DependOnStablePrototypeChain(JSHeapBroker* broker, CompilationDependencies* deps, Handle map, MaybeHandle last_prototype) { for (PrototypeIterator i(broker->isolate(), map); !i.IsAtEnd(); i.Advance()) { Handle const current = PrototypeIterator::GetCurrent(i); deps->DependOnStableMap( MapRef(broker, handle(current->map(), broker->isolate()))); Handle last; if (last_prototype.ToHandle(&last) && last.is_identical_to(current)) { break; } } } } // namespace void CompilationDependencies::DependOnStablePrototypeChains( JSHeapBroker* broker, Handle native_context, std::vector> const& receiver_maps, Handle holder) { Isolate* isolate = holder->GetIsolate(); // Determine actual holder and perform prototype chain checks. for (auto map : receiver_maps) { // Perform the implicit ToObject for primitives here. // Implemented according to ES6 section 7.3.2 GetV (V, P). Handle constructor; if (Map::GetConstructorFunction(map, native_context) .ToHandle(&constructor)) { map = handle(constructor->initial_map(), isolate); } DependOnStablePrototypeChain(broker, this, map, holder); } } void CompilationDependencies::DependOnElementsKinds( const AllocationSiteRef& site) { AllocationSiteRef current = site; while (true) { DependOnElementsKind(current); if (!current.nested_site().IsAllocationSite()) break; current = current.nested_site().AsAllocationSite(); } CHECK_EQ(current.nested_site().AsSmi(), 0); } SlackTrackingPrediction::SlackTrackingPrediction(MapRef initial_map, int instance_size) : instance_size_(instance_size), inobject_property_count_( (instance_size >> kPointerSizeLog2) - initial_map.GetInObjectPropertiesStartInWords()) {} SlackTrackingPrediction CompilationDependencies::DependOnInitialMapInstanceSizePrediction( const JSFunctionRef& function) { MapRef initial_map = DependOnInitialMap(function); int instance_size = function.InitialMapInstanceSizeWithMinSlack(); // 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. dependencies_.push_front( new (zone_) InitialMapInstanceSizePredictionDependency(function, instance_size)); DCHECK_LE(instance_size, function.initial_map().instance_size()); return SlackTrackingPrediction(initial_map, instance_size); } } // namespace compiler } // namespace internal } // namespace v8