diff --git a/src/compilation-dependencies.cc b/src/compilation-dependencies.cc index 67550858a4..d20e7f46f0 100644 --- a/src/compilation-dependencies.cc +++ b/src/compilation-dependencies.cc @@ -5,14 +5,15 @@ #include "src/compilation-dependencies.h" #include "src/handles-inl.h" +#include "src/heap/factory.h" +#include "src/isolate.h" #include "src/objects-inl.h" +#include "src/zone/zone.h" namespace v8 { namespace internal { -// TODO(neis): Move these to the DependentCode class. -namespace { -DependentCode* GetDependentCode(Handle object) { +DependentCode* CompilationDependencies::Get(Handle object) const { if (object->IsMap()) { return Handle::cast(object)->dependent_code(); } else if (object->IsPropertyCell()) { @@ -23,7 +24,9 @@ DependentCode* GetDependentCode(Handle object) { UNREACHABLE(); } -void SetDependentCode(Handle object, Handle dep) { + +void CompilationDependencies::Set(Handle object, + Handle dep) { if (object->IsMap()) { Handle::cast(object)->set_dependent_code(*dep); } else if (object->IsPropertyCell()) { @@ -35,332 +38,106 @@ void SetDependentCode(Handle object, Handle dep) { } } -void InstallDependency(Isolate* isolate, Handle source, - Handle target, - DependentCode::DependencyGroup group) { - Handle old_deps(GetDependentCode(target), isolate); - Handle new_deps = - DependentCode::InsertWeakCode(old_deps, group, source); - // Update the list head if necessary. - if (!new_deps.is_identical_to(old_deps)) SetDependentCode(target, new_deps); -} -} // namespace -CompilationDependencies::CompilationDependencies(Isolate* isolate, Zone* zone) - : isolate_(isolate), zone_(zone), dependencies_(zone) {} +void CompilationDependencies::Insert(DependentCode::DependencyGroup group, + Handle object) { + if (groups_[group] == nullptr) { + groups_[group] = new (zone_->New(sizeof(ZoneVector>))) + ZoneVector>(zone_); + } + groups_[group]->push_back(object); -class CompilationDependencies::Dependency : public ZoneObject { - public: - virtual bool IsValid() const = 0; - virtual void Install(Isolate* isolate, Handle code) = 0; -}; - -class InitialMapDependency final : public CompilationDependencies::Dependency { - public: - InitialMapDependency(Handle function, Handle initial_map) - : function_(function), initial_map_(initial_map) { - DCHECK(IsValid()); + if (object_wrapper_.is_null()) { + // Allocate the wrapper if necessary. + object_wrapper_ = + isolate_->factory()->NewForeign(reinterpret_cast
(this)); } - bool IsValid() const override { - DisallowHeapAllocation no_heap_allocation; - DCHECK(function_->has_initial_map()); - return *initial_map_ == function_->initial_map(); - } + // Get the old dependent code list. + Handle old_dependent_code = + Handle(Get(object), isolate_); + Handle new_dependent_code = + DependentCode::InsertCompilationDependencies(old_dependent_code, group, + object_wrapper_); - void Install(Isolate* isolate, Handle code) override { - DCHECK(IsValid()); - InstallDependency(isolate, code, initial_map_, - DependentCode::kInitialMapChangedGroup); - } - - private: - Handle function_; - Handle initial_map_; -}; - -class StableMapDependency final : public CompilationDependencies::Dependency { - public: - explicit StableMapDependency(Handle map) : map_(map) { - DCHECK(IsValid()); - } - - bool IsValid() const override { - DisallowHeapAllocation no_heap_allocation; - return map_->is_stable(); - } - - void Install(Isolate* isolate, Handle code) override { - DCHECK(IsValid()); - InstallDependency(isolate, code, map_, DependentCode::kPrototypeCheckGroup); - } - - private: - Handle map_; -}; - -class TransitionDependency final : public CompilationDependencies::Dependency { - public: - explicit TransitionDependency(Handle map) : map_(map) { - DCHECK(IsValid()); - } - - bool IsValid() const override { - DisallowHeapAllocation no_heap_allocation; - return !map_->is_deprecated(); - } - - void Install(Isolate* isolate, Handle code) override { - DCHECK(IsValid()); - InstallDependency(isolate, code, map_, DependentCode::kTransitionGroup); - } - - private: - Handle map_; -}; - -class PretenureModeDependency final - : public CompilationDependencies::Dependency { - public: - PretenureModeDependency(Handle site, PretenureFlag mode) - : site_(site), mode_(mode) { - DCHECK(IsValid()); - } - - bool IsValid() const override { - DisallowHeapAllocation no_heap_allocation; - return mode_ == site_->GetPretenureMode(); - } - - void Install(Isolate* isolate, Handle code) override { - DCHECK(IsValid()); - InstallDependency(isolate, code, site_, - DependentCode::kAllocationSiteTenuringChangedGroup); - } - - private: - Handle site_; - PretenureFlag mode_; -}; - -class FieldTypeDependency final : public CompilationDependencies::Dependency { - public: - FieldTypeDependency(Isolate* isolate, Handle owner, - Handle type) - : isolate_(isolate), owner_(owner), type_(type) { - DCHECK(IsValid()); - } - - bool IsValid() const override { - DisallowHeapAllocation no_heap_allocation; - int descriptor = owner_->LastAdded(); - CHECK_EQ(*owner_, owner_->FindFieldOwner(isolate_, descriptor)); - return *type_ == owner_->instance_descriptors()->GetFieldType(descriptor); - } - - void Install(Isolate* isolate, Handle code) override { - DCHECK(IsValid()); - InstallDependency(isolate, code, owner_, DependentCode::kFieldOwnerGroup); - } - - private: - Isolate* isolate_; - Handle owner_; - Handle type_; -}; - -class GlobalPropertyDependency final - : public CompilationDependencies::Dependency { - public: - GlobalPropertyDependency(Handle cell, PropertyCellType type, - bool read_only) - : cell_(cell), type_(type), read_only_(read_only) { - DCHECK(IsValid()); - } - - bool IsValid() const override { - DisallowHeapAllocation no_heap_allocation; - return type_ == cell_->property_details().cell_type() && - read_only_ == cell_->property_details().IsReadOnly(); - } - - void Install(Isolate* isolate, Handle code) override { - DCHECK(IsValid()); - InstallDependency(isolate, code, cell_, - DependentCode::kPropertyCellChangedGroup); - } - - private: - Handle cell_; - PropertyCellType type_; - bool read_only_; -}; - -class ProtectorDependency final : public CompilationDependencies::Dependency { - public: - explicit ProtectorDependency(Handle cell) : cell_(cell) { - DCHECK(IsValid()); - } - - bool IsValid() const override { - DisallowHeapAllocation no_heap_allocation; - return cell_->value() == Smi::FromInt(Isolate::kProtectorValid); - } - - void Install(Isolate* isolate, Handle code) override { - DCHECK(IsValid()); - InstallDependency(isolate, code, cell_, - DependentCode::kPropertyCellChangedGroup); - } - - private: - Handle cell_; -}; - -class ElementsKindDependency final - : public CompilationDependencies::Dependency { - public: - ElementsKindDependency(Handle site, ElementsKind kind) - : site_(site), kind_(kind) { - DCHECK(IsValid()); - } - - bool IsValid() const override { - DisallowHeapAllocation no_heap_allocation; - DCHECK(AllocationSite::ShouldTrack(kind_)); - ElementsKind kind = site_->PointsToLiteral() - ? site_->boilerplate()->GetElementsKind() - : site_->GetElementsKind(); - return kind_ == kind; - } - - void Install(Isolate* isolate, Handle code) override { - DCHECK(IsValid()); - InstallDependency(isolate, code, site_, - DependentCode::kAllocationSiteTransitionChangedGroup); - } - - private: - Handle site_; - ElementsKind kind_; -}; - -Handle CompilationDependencies::DependOnInitialMap( - Handle function) { - Handle map(function->initial_map(), function->GetIsolate()); - dependencies_.push_front(new (zone_) InitialMapDependency(function, map)); - return map; -} - -void CompilationDependencies::DependOnStableMap(Handle map) { - if (map->CanTransition()) { - dependencies_.push_front(new (zone_) StableMapDependency(map)); - } else { - DCHECK(map->is_stable()); + // Set the new dependent code list if the head of the list changed. + if (!new_dependent_code.is_identical_to(old_dependent_code)) { + Set(object, new_dependent_code); } } -void CompilationDependencies::DependOnTransition(Handle target_map) { - if (target_map->CanBeDeprecated()) { - dependencies_.push_front(new (zone_) TransitionDependency(target_map)); - } else { - DCHECK(!target_map->is_deprecated()); - } -} -PretenureFlag CompilationDependencies::DependOnPretenureMode( - Handle site) { - PretenureFlag mode = site->GetPretenureMode(); - dependencies_.push_front(new (zone_) PretenureModeDependency(site, mode)); - return mode; -} - -void CompilationDependencies::DependOnFieldType(Handle map, - int descriptor) { - Handle owner(map->FindFieldOwner(isolate_, descriptor), isolate_); - DCHECK_EQ(descriptor, owner->LastAdded()); - Handle type( - owner->instance_descriptors()->GetFieldType(descriptor), isolate_); - DCHECK_EQ(*type, map->instance_descriptors()->GetFieldType(descriptor)); - dependencies_.push_front(new (zone_) - FieldTypeDependency(isolate_, owner, type)); -} - -void CompilationDependencies::DependOnFieldType(const LookupIterator* it) { - Handle owner = it->GetFieldOwnerMap(); - int descriptor = it->GetFieldDescriptorIndex(); - DCHECK_EQ(descriptor, owner->LastAdded()); - Handle type = it->GetFieldType(); - CHECK_EQ(*type, - it->GetHolder()->map()->instance_descriptors()->GetFieldType( - descriptor)); - dependencies_.push_front(new (zone_) - FieldTypeDependency(isolate_, owner, type)); -} - -void CompilationDependencies::DependOnGlobalProperty( - Handle 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(Handle cell) { - dependencies_.push_front(new (zone_) ProtectorDependency(cell)); -} - -void CompilationDependencies::DependOnElementsKind( - Handle site) { - // Do nothing if the object doesn't have any useful element transitions left. - ElementsKind kind = site->PointsToLiteral() - ? site->boilerplate()->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 abort before - // installing anything. - if (!AreValid()) { - dependencies_.clear(); - return false; - } +void CompilationDependencies::Commit(Handle code) { + if (IsEmpty()) return; + DCHECK(!object_wrapper_.is_null()); Handle cell = Code::WeakCellFor(code); - for (auto dep : dependencies_) { - dep->Install(isolate_, cell); + AllowDeferredHandleDereference get_wrapper; + for (int i = 0; i < DependentCode::kGroupCount; i++) { + ZoneVector>* group_objects = groups_[i]; + if (group_objects == nullptr) continue; + DependentCode::DependencyGroup group = + static_cast(i); + for (size_t j = 0; j < group_objects->size(); j++) { + DependentCode* dependent_code = Get(group_objects->at(j)); + dependent_code->UpdateToFinishedCode(group, *object_wrapper_, *cell); + } + groups_[i] = nullptr; // Zone-allocated, no need to delete. } - dependencies_.clear(); - return true; } -namespace { -void DependOnStablePrototypeChain(CompilationDependencies* deps, - Handle map, - MaybeHandle last_prototype) { + +void CompilationDependencies::Rollback() { + if (IsEmpty()) return; + + AllowDeferredHandleDereference get_wrapper; + // Unregister from all dependent maps if not yet committed. + for (int i = 0; i < DependentCode::kGroupCount; i++) { + ZoneVector>* group_objects = groups_[i]; + if (group_objects == nullptr) continue; + DependentCode::DependencyGroup group = + static_cast(i); + for (size_t j = 0; j < group_objects->size(); j++) { + DependentCode* dependent_code = Get(group_objects->at(j)); + dependent_code->RemoveCompilationDependencies(group, *object_wrapper_); + } + groups_[i] = nullptr; // Zone-allocated, no need to delete. + } +} + + +void CompilationDependencies::AssumeMapNotDeprecated(Handle map) { + DCHECK(!map->is_deprecated()); + // Do nothing if the map cannot be deprecated. + if (map->CanBeDeprecated()) { + Insert(DependentCode::kTransitionGroup, map); + } +} + + +void CompilationDependencies::AssumeMapStable(Handle map) { + DCHECK(map->is_stable()); + // Do nothing if the map cannot transition. + if (map->CanTransition()) { + Insert(DependentCode::kPrototypeCheckGroup, map); + } +} + + +void CompilationDependencies::AssumePrototypeMapsStable( + Handle map, MaybeHandle prototype) { for (PrototypeIterator i(map); !i.IsAtEnd(); i.Advance()) { Handle const current = PrototypeIterator::GetCurrent(i); - deps->DependOnStableMap(handle(current->map(), current->GetIsolate())); + AssumeMapStable(handle(current->map(), isolate_)); Handle last; - if (last_prototype.ToHandle(&last) && last.is_identical_to(current)) { + if (prototype.ToHandle(&last) && last.is_identical_to(current)) { break; } } } -} // namespace -void CompilationDependencies::DependOnStablePrototypeChains( +void CompilationDependencies::AssumePrototypesStable( Handle native_context, std::vector> const& receiver_maps, Handle holder) { // Determine actual holder and perform prototype chain checks. @@ -370,20 +147,21 @@ void CompilationDependencies::DependOnStablePrototypeChains( Handle constructor; if (Map::GetConstructorFunction(map, native_context) .ToHandle(&constructor)) { - map = handle(constructor->initial_map(), isolate_); + map = handle(constructor->initial_map(), holder->GetIsolate()); } - DependOnStablePrototypeChain(this, map, holder); + AssumePrototypeMapsStable(map, holder); } } -void CompilationDependencies::DependOnElementsKinds( +void CompilationDependencies::AssumeTransitionStable( Handle site) { - while (true) { - DependOnElementsKind(site); - if (!site->nested_site()->IsAllocationSite()) break; - site = handle(AllocationSite::cast(site->nested_site()), isolate_); + // Do nothing if the object doesn't have any useful element transitions left. + ElementsKind kind = site->PointsToLiteral() + ? site->boilerplate()->GetElementsKind() + : site->GetElementsKind(); + if (AllocationSite::ShouldTrack(kind)) { + Insert(DependentCode::kAllocationSiteTransitionChangedGroup, site); } - CHECK_EQ(site->nested_site(), Smi::kZero); } } // namespace internal diff --git a/src/compilation-dependencies.h b/src/compilation-dependencies.h index 0a78650389..db098835c8 100644 --- a/src/compilation-dependencies.h +++ b/src/compilation-dependencies.h @@ -5,70 +5,75 @@ #ifndef V8_COMPILATION_DEPENDENCIES_H_ #define V8_COMPILATION_DEPENDENCIES_H_ +#include "src/handles.h" #include "src/objects.h" +#include "src/objects/map.h" #include "src/zone/zone-containers.h" namespace v8 { namespace internal { -// Collects and installs dependencies of the code that is being generated. +// Collects dependencies for this compilation, e.g. assumptions about +// stable maps, constant globals, etc. class V8_EXPORT_PRIVATE CompilationDependencies { public: - CompilationDependencies(Isolate* isolate, Zone* zone); + CompilationDependencies(Isolate* isolate, Zone* zone) + : isolate_(isolate), + zone_(zone), + object_wrapper_(Handle::null()), + aborted_(false) { + std::fill_n(groups_, DependentCode::kGroupCount, nullptr); + } - V8_WARN_UNUSED_RESULT bool Commit(Handle code); + void Insert(DependentCode::DependencyGroup group, Handle handle); - // Return the initial map of {function} and record the assumption that it - // stays the intial map. - Handle DependOnInitialMap(Handle function); + void AssumeInitialMapCantChange(Handle map) { + Insert(DependentCode::kInitialMapChangedGroup, map); + } + void AssumeFieldOwner(Handle map) { + Insert(DependentCode::kFieldOwnerGroup, map); + } + void AssumeMapStable(Handle map); + void AssumePrototypeMapsStable( + Handle map, + MaybeHandle prototype = MaybeHandle()); + void AssumeMapNotDeprecated(Handle map); + void AssumePropertyCell(Handle cell) { + Insert(DependentCode::kPropertyCellChangedGroup, cell); + } + void AssumeTenuringDecision(Handle site) { + Insert(DependentCode::kAllocationSiteTenuringChangedGroup, site); + } + void AssumeTransitionStable(Handle site); - // Record the assumption that {map} stays stable. - void DependOnStableMap(Handle map); - - // Record the assumption that {target_map} can be transitioned to, i.e., that - // it does not become deprecated. - void DependOnTransition(Handle target_map); - - // Return the pretenure mode of {site} and record the assumption that it does - // not change. - PretenureFlag DependOnPretenureMode(Handle site); - - // Record the assumption that the field type of a field does not change. The - // field is identified by the argument(s). - void DependOnFieldType(Handle map, int descriptor); - void DependOnFieldType(const LookupIterator* it); - - // Record the assumption that neither {cell}'s {CellType} changes, nor the - // {IsReadOnly()} flag of {cell}'s {PropertyDetails}. - void DependOnGlobalProperty(Handle cell); - - // Record the assumption that the protector remains valid. - void DependOnProtector(Handle cell); - - // Record the assumption that {site}'s {ElementsKind} doesn't change. - void DependOnElementsKind(Handle site); - - // Depend on the stability of (the maps of) all prototypes of every class in + // Adds stability dependencies on all prototypes of every class in // {receiver_type} up to (and including) the {holder}. - void DependOnStablePrototypeChains( - Handle native_context, - std::vector> const& receiver_maps, Handle holder); + void AssumePrototypesStable(Handle native_context, + std::vector> const& receiver_maps, + Handle holder); - // Like DependOnElementsKind but also applies to all nested allocation sites. - void DependOnElementsKinds(Handle site); + void Commit(Handle code); + void Rollback(); + void Abort() { aborted_ = true; } + bool HasAborted() const { return aborted_; } - // Exposed only for testing purposes. - bool AreValid() const; - - // Exposed only because C++. - class Dependency; + bool IsEmpty() const { + for (int i = 0; i < DependentCode::kGroupCount; i++) { + if (groups_[i]) return false; + } + return true; + } private: Isolate* isolate_; Zone* zone_; - ZoneForwardList dependencies_; -}; + Handle object_wrapper_; + bool aborted_; + ZoneVector >* groups_[DependentCode::kGroupCount]; + DependentCode* Get(Handle object) const; + void Set(Handle object, Handle dep); +}; } // namespace internal } // namespace v8 diff --git a/src/compiler.cc b/src/compiler.cc index 2a81337f8a..98e13edb7b 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -227,6 +227,8 @@ CompilationJob::Status OptimizedCompilationJob::FinalizeJob(Isolate* isolate) { DCHECK(ThreadId::Current().Equals(isolate->thread_id())); DisallowCodeDependencyChange no_dependency_change; DisallowJavascriptExecution no_js(isolate); + CHECK(!compilation_info()->dependencies() || + !compilation_info()->dependencies()->HasAborted()); // Delegate to the underlying implementation. DCHECK_EQ(state(), State::kReadyToFinalize); @@ -800,6 +802,8 @@ CompilationJob::Status FinalizeOptimizedCompilationJob( if (job->state() == CompilationJob::State::kReadyToFinalize) { if (shared->optimization_disabled()) { job->RetryOptimization(BailoutReason::kOptimizationDisabled); + } else if (compilation_info->dependencies()->HasAborted()) { + job->RetryOptimization(BailoutReason::kBailedOutDueToDependencyChange); } else if (job->FinalizeJob(isolate) == CompilationJob::SUCCEEDED) { job->RecordCompilationStats(); job->RecordFunctionCompilation(CodeEventListener::LAZY_COMPILE_TAG, diff --git a/src/compiler/access-info.cc b/src/compiler/access-info.cc index 9a77b8a9b0..ba6767f299 100644 --- a/src/compiler/access-info.cc +++ b/src/compiler/access-info.cc @@ -395,7 +395,11 @@ bool AccessInfoFactory::ComputePropertyAccessInfo( // The field type was cleared by the GC, so we don't know anything // about the contents now. } else if (descriptors_field_type->IsClass()) { - dependencies()->DependOnFieldType(map, number); + // Add proper code dependencies in case of stable field map(s). + Handle field_owner_map( + map->FindFieldOwner(isolate(), number), isolate()); + dependencies()->AssumeFieldOwner(field_owner_map); + // Remember the field map, and try to infer a useful type. field_type = Type::For(js_heap_broker(), descriptors_field_type->AsClass()); @@ -697,14 +701,18 @@ bool AccessInfoFactory::LookupTransition(Handle map, Handle name, // Store is not safe if the field type was cleared. return false; } else if (descriptors_field_type->IsClass()) { - dependencies()->DependOnFieldType(transition_map, number); + // Add proper code dependencies in case of stable field map(s). + Handle field_owner_map( + transition_map->FindFieldOwner(isolate(), number), isolate()); + dependencies()->AssumeFieldOwner(field_owner_map); + // Remember the field map, and try to infer a useful type. field_type = Type::For(js_heap_broker(), descriptors_field_type->AsClass()); field_map = descriptors_field_type->AsClass(); } } - dependencies()->DependOnTransition(transition_map); + dependencies()->AssumeMapNotDeprecated(transition_map); // Transitioning stores are never stores to constant fields. *access_info = PropertyAccessInfo::DataField( PropertyConstness::kMutable, MapHandles{map}, field_index, diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc index 622b32d632..674af1e286 100644 --- a/src/compiler/js-call-reducer.cc +++ b/src/compiler/js-call-reducer.cc @@ -594,7 +594,7 @@ Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) { } if (result == NodeProperties::kUnreliableReceiverMaps) { for (size_t i = 0; i < object_maps.size(); ++i) { - dependencies()->DependOnStableMap(object_maps[i]); + dependencies()->AssumeMapStable(object_maps[i]); } } Node* value = jsgraph()->Constant(candidate_prototype); @@ -1050,7 +1050,7 @@ Reduction JSCallReducer::ReduceArrayForEach(Node* node, // Install code dependencies on the {receiver} prototype maps and the // global array protector cell. - dependencies()->DependOnProtector(factory()->no_elements_protector()); + dependencies()->AssumePropertyCell(factory()->no_elements_protector()); // If we have unreliable maps, we need a map check. if (result == NodeProperties::kUnreliableReceiverMaps) { @@ -1234,7 +1234,7 @@ Reduction JSCallReducer::ReduceArrayReduce(Node* node, // Install code dependencies on the {receiver} prototype maps and the // global array protector cell. - dependencies()->DependOnProtector(factory()->no_elements_protector()); + dependencies()->AssumePropertyCell(factory()->no_elements_protector()); // If we have unreliable maps, we need a map check. if (result == NodeProperties::kUnreliableReceiverMaps) { @@ -1504,7 +1504,7 @@ Reduction JSCallReducer::ReduceArrayMap(Node* node, if (receiver_map->elements_kind() != kind) return NoChange(); } - dependencies()->DependOnProtector(factory()->array_species_protector()); + dependencies()->AssumePropertyCell(factory()->array_species_protector()); Handle handle_constructor( JSFunction::cast( @@ -1711,7 +1711,7 @@ Reduction JSCallReducer::ReduceArrayFilter(Node* node, if (receiver_map->elements_kind() != kind) return NoChange(); } - dependencies()->DependOnProtector(factory()->array_species_protector()); + dependencies()->AssumePropertyCell(factory()->array_species_protector()); Handle initial_map( Map::cast(native_context()->GetInitialJSArrayMap(packed_kind)), @@ -1988,7 +1988,7 @@ Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant, // Install code dependencies on the {receiver} prototype maps and the // global array protector cell. - dependencies()->DependOnProtector(factory()->no_elements_protector()); + dependencies()->AssumePropertyCell(factory()->no_elements_protector()); // If we have unreliable maps, we need a map check. if (result == NodeProperties::kUnreliableReceiverMaps) { @@ -2303,7 +2303,7 @@ Reduction JSCallReducer::ReduceArrayEvery(Node* node, if (receiver_map->elements_kind() != kind) return NoChange(); } - dependencies()->DependOnProtector(factory()->array_species_protector()); + dependencies()->AssumePropertyCell(factory()->array_species_protector()); // If we have unreliable maps, we need a map check. if (result == NodeProperties::kUnreliableReceiverMaps) { @@ -2643,7 +2643,7 @@ Reduction JSCallReducer::ReduceArraySome(Node* node, if (receiver_map->elements_kind() != kind) return NoChange(); } - dependencies()->DependOnProtector(factory()->array_species_protector()); + dependencies()->AssumePropertyCell(factory()->array_species_protector()); Node* k = jsgraph()->ZeroConstant(); @@ -2891,7 +2891,7 @@ Reduction JSCallReducer::ReduceCallApiFunction( // Install stability dependencies for unreliable {receiver_maps}. if (result == NodeProperties::kUnreliableReceiverMaps) { for (size_t i = 0; i < receiver_maps.size(); ++i) { - dependencies()->DependOnStableMap(receiver_maps[i]); + dependencies()->AssumeMapStable(receiver_maps[i]); } } @@ -3061,7 +3061,7 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( // that no one messed with the %ArrayIteratorPrototype%.next method. if (node->opcode() == IrOpcode::kJSCallWithSpread || node->opcode() == IrOpcode::kJSConstructWithSpread) { - dependencies()->DependOnProtector(factory()->array_iterator_protector()); + dependencies()->AssumePropertyCell(factory()->array_iterator_protector()); } // Remove the {arguments_list} input from the {node}. @@ -4295,7 +4295,7 @@ Reduction JSCallReducer::ReduceArrayPrototypePush(Node* node) { } // Install code dependencies on the {receiver} global array protector cell. - dependencies()->DependOnProtector(factory()->no_elements_protector()); + dependencies()->AssumePropertyCell(factory()->no_elements_protector()); // If the {receiver_maps} information is not reliable, we need // to check that the {receiver} still has one of these maps. @@ -4409,7 +4409,7 @@ Reduction JSCallReducer::ReduceArrayPrototypePop(Node* node) { } // Install code dependencies on the {receiver} global array protector cell. - dependencies()->DependOnProtector(factory()->no_elements_protector()); + dependencies()->AssumePropertyCell(factory()->no_elements_protector()); // If the {receiver_maps} information is not reliable, we need // to check that the {receiver} still has one of these maps. @@ -4527,7 +4527,7 @@ Reduction JSCallReducer::ReduceArrayPrototypeShift(Node* node) { } // Install code dependencies on the {receiver} global array protector cell. - dependencies()->DependOnProtector(factory()->no_elements_protector()); + dependencies()->AssumePropertyCell(factory()->no_elements_protector()); // If the {receiver_maps} information is not reliable, we need // to check that the {receiver} still has one of these maps. @@ -4808,7 +4808,7 @@ Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) { // Install code dependency on the array protector for holey arrays. if (IsHoleyElementsKind(elements_kind)) { - dependencies()->DependOnProtector(factory()->no_elements_protector()); + dependencies()->AssumePropertyCell(factory()->no_elements_protector()); } // Load the (current) {iterated_object} from the {iterator}; this might be @@ -4832,7 +4832,7 @@ Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) { if (isolate()->IsArrayBufferNeuteringIntact()) { // Add a code dependency so we are deoptimized in case an ArrayBuffer // gets neutered. - dependencies()->DependOnProtector( + dependencies()->AssumePropertyCell( factory()->array_buffer_neutering_protector()); } else { // Deoptimize if the array buffer was neutered. @@ -5318,7 +5318,7 @@ Reduction JSCallReducer::ReduceAsyncFunctionPromiseCreate(Node* node) { if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange(); // Install a code dependency on the promise hook protector cell. - dependencies()->DependOnProtector(factory()->promise_hook_protector()); + dependencies()->AssumePropertyCell(factory()->promise_hook_protector()); // Morph this {node} into a JSCreatePromise node. RelaxControls(node); @@ -5333,7 +5333,8 @@ Reduction JSCallReducer::ReduceAsyncFunctionPromiseRelease(Node* node) { DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange(); - dependencies()->DependOnProtector(factory()->promise_hook_protector()); + // Install a code dependency on the promise hook protector cell. + dependencies()->AssumePropertyCell(factory()->promise_hook_protector()); // The AsyncFunctionPromiseRelease builtin is a no-op as long as neither // the debugger is active nor any promise hook has been installed (ever). @@ -5502,7 +5503,8 @@ Reduction JSCallReducer::ReducePromiseConstructor(Node* node) { // Only handle builtins Promises, not subclasses. if (target != new_target) return NoChange(); - dependencies()->DependOnProtector(factory()->promise_hook_protector()); + // Add a code dependency on the promise hook protector. + dependencies()->AssumePropertyCell(factory()->promise_hook_protector()); Handle promise_shared( handle(native_context()->promise_function()->shared(), isolate())); @@ -5655,7 +5657,8 @@ Reduction JSCallReducer::ReducePromiseInternalConstructor(Node* node) { // Check that promises aren't being observed through (debug) hooks. if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange(); - dependencies()->DependOnProtector(factory()->promise_hook_protector()); + // Install a code dependency on the promise hook protector cell. + dependencies()->AssumePropertyCell(factory()->promise_hook_protector()); // Create a new pending promise. Node* value = effect = @@ -5747,7 +5750,8 @@ Reduction JSCallReducer::ReducePromisePrototypeCatch(Node* node) { } } - dependencies()->DependOnProtector(factory()->promise_then_protector()); + // Add a code dependency on the necessary protectors. + dependencies()->AssumePropertyCell(factory()->promise_then_protector()); // If the {receiver_maps} aren't reliable, we need to repeat the // map check here, guarded by the CALL_IC. @@ -5823,9 +5827,10 @@ Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) { } } - dependencies()->DependOnProtector(factory()->promise_hook_protector()); - dependencies()->DependOnProtector(factory()->promise_then_protector()); - dependencies()->DependOnProtector(factory()->promise_species_protector()); + // Add a code dependency on the necessary protectors. + dependencies()->AssumePropertyCell(factory()->promise_hook_protector()); + dependencies()->AssumePropertyCell(factory()->promise_then_protector()); + dependencies()->AssumePropertyCell(factory()->promise_species_protector()); // If the {receiver_maps} aren't reliable, we need to repeat the // map check here, guarded by the CALL_IC. @@ -5975,8 +5980,9 @@ Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) { } } - dependencies()->DependOnProtector(factory()->promise_hook_protector()); - dependencies()->DependOnProtector(factory()->promise_species_protector()); + // Add a code dependency on the necessary protectors. + dependencies()->AssumePropertyCell(factory()->promise_hook_protector()); + dependencies()->AssumePropertyCell(factory()->promise_species_protector()); // If the {receiver_maps} aren't reliable, we need to repeat the // map check here, guarded by the CALL_IC. @@ -6653,7 +6659,7 @@ Reduction JSCallReducer::ReduceArrayBufferViewAccessor( if (isolate()->IsArrayBufferNeuteringIntact()) { // Add a code dependency so we are deoptimized in case an ArrayBuffer // gets neutered. - dependencies()->DependOnProtector( + dependencies()->AssumePropertyCell( factory()->array_buffer_neutering_protector()); } else { // Check if the {receiver}s buffer was neutered. @@ -6997,8 +7003,8 @@ Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) { // Add proper dependencies on the {regexp}s [[Prototype]]s. Handle holder; if (ai_exec.holder().ToHandle(&holder)) { - dependencies()->DependOnStablePrototypeChains( - native_context(), ai_exec.receiver_maps(), holder); + dependencies()->AssumePrototypesStable(native_context(), + ai_exec.receiver_maps(), holder); } if (need_map_check) { diff --git a/src/compiler/js-create-lowering.cc b/src/compiler/js-create-lowering.cc index 8dfab6aeb0..125aee9c39 100644 --- a/src/compiler/js-create-lowering.cc +++ b/src/compiler/js-create-lowering.cc @@ -133,12 +133,12 @@ Reduction JSCreateLowering::ReduceJSCreate(Node* node) { // Force completion of inobject slack tracking before // generating code to finalize the instance size. original_constructor->CompleteInobjectSlackTrackingIfActive(); + Handle initial_map(original_constructor->initial_map(), isolate()); + int const instance_size = initial_map->instance_size(); // Add a dependency on the {initial_map} to make sure that this code is // deoptimized whenever the {initial_map} changes. - Handle initial_map = - dependencies()->DependOnInitialMap(original_constructor); - int const instance_size = initial_map->instance_size(); + dependencies()->AssumeInitialMapCantChange(initial_map); // Emit code to allocate the JSObject instance for the // {original_constructor}. @@ -425,12 +425,13 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) { // Force completion of inobject slack tracking before // generating code to finalize the instance size. js_function->CompleteInobjectSlackTrackingIfActive(); + Handle initial_map(js_function->initial_map(), isolate()); + DCHECK(initial_map->instance_type() == JS_GENERATOR_OBJECT_TYPE || + initial_map->instance_type() == JS_ASYNC_GENERATOR_OBJECT_TYPE); // Add a dependency on the {initial_map} to make sure that this code is // deoptimized whenever the {initial_map} changes. - Handle initial_map = dependencies()->DependOnInitialMap(js_function); - DCHECK(initial_map->instance_type() == JS_GENERATOR_OBJECT_TYPE || - initial_map->instance_type() == JS_ASYNC_GENERATOR_OBJECT_TYPE); + dependencies()->AssumeInitialMapCantChange(initial_map); // Allocate a register file. DCHECK(js_function->shared()->HasBytecodeArray()); @@ -724,11 +725,11 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) { // Force completion of inobject slack tracking before // generating code to finalize the instance size. original_constructor->CompleteInobjectSlackTrackingIfActive(); + Handle initial_map(original_constructor->initial_map(), isolate()); // Add a dependency on the {initial_map} to make sure that this code is // deoptimized whenever the {initial_map} changes. - Handle initial_map = - dependencies()->DependOnInitialMap(original_constructor); + dependencies()->AssumeInitialMapCantChange(initial_map); // Tells whether we are protected by either the {site} or a // protector cell to do certain speculative optimizations. @@ -742,8 +743,10 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) { Map::AsElementsKind(isolate(), initial_map, elements_kind); } can_inline_call = site->CanInlineCall(); - pretenure = dependencies()->DependOnPretenureMode(site); - dependencies()->DependOnElementsKind(site); + pretenure = site->GetPretenureMode(); + + dependencies()->AssumeTransitionStable(site); + dependencies()->AssumeTenuringDecision(site); } else { can_inline_call = isolate()->IsArrayConstructorIntact(); } @@ -1133,6 +1136,17 @@ Reduction JSCreateLowering::ReduceJSCreatePromise(Node* node) { return Changed(node); } +void AssumeAllocationSiteTransitionDeepDependencies( + CompilationDependencies* dependencies, Isolate* isolate, + Handle site) { + while (true) { + dependencies->AssumeTransitionStable(site); + if (!site->nested_site()->IsAllocationSite()) break; + site = handle(AllocationSite::cast(site->nested_site()), isolate); + } + CHECK_EQ(site->nested_site(), Smi::kZero); +} + Reduction JSCreateLowering::ReduceJSCreateLiteralArrayOrObject(Node* node) { DCHECK(node->opcode() == IrOpcode::kJSCreateLiteralArray || node->opcode() == IrOpcode::kJSCreateLiteralObject); @@ -1148,11 +1162,12 @@ Reduction JSCreateLowering::ReduceJSCreateLiteralArrayOrObject(Node* node) { if (site.IsFastLiteral(js_heap_broker())) { PretenureFlag pretenure = NOT_TENURED; if (FLAG_allocation_site_pretenuring) { - pretenure = dependencies()->DependOnPretenureMode( - site.object()); + pretenure = site.GetPretenureMode(); + dependencies()->AssumeTenuringDecision(site.object()); } - dependencies()->DependOnElementsKinds(site.object()); JSObjectRef boilerplate = site.boilerplate(js_heap_broker()); + AssumeAllocationSiteTransitionDeepDependencies( + dependencies(), isolate(), site.object()); Node* value = effect = AllocateFastLiteral(effect, control, boilerplate, pretenure); ReplaceWithValue(node, value, effect, control); @@ -1173,8 +1188,9 @@ Reduction JSCreateLowering::ReduceJSCreateEmptyLiteralArray(Node* node) { Handle const initial_map( native_context()->GetInitialJSArrayMap(site->GetElementsKind()), isolate()); - PretenureFlag const pretenure = dependencies()->DependOnPretenureMode(site); - dependencies()->DependOnElementsKind(site); + PretenureFlag const pretenure = site->GetPretenureMode(); + dependencies()->AssumeTransitionStable(site); + dependencies()->AssumeTenuringDecision(site); Node* length = jsgraph()->ZeroConstant(); return ReduceNewArray(node, length, 0, initial_map, pretenure); } diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc index 220bbe3133..f4fd316371 100644 --- a/src/compiler/js-native-context-specialization.cc +++ b/src/compiler/js-native-context-specialization.cc @@ -155,7 +155,7 @@ Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor( // {function}s map is stable, i.e. we can use a code dependency // to guard against [[Prototype]] changes of {function}. if (function_map->is_stable() && function_prototype->IsConstructor()) { - dependencies()->DependOnStableMap(function_map); + dependencies()->AssumeMapStable(function_map); Node* value = jsgraph()->Constant(function_prototype); ReplaceWithValue(node, value); return Replace(value); @@ -208,7 +208,7 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { // Determine actual holder and perform prototype chain checks. Handle holder; if (access_info.holder().ToHandle(&holder)) { - dependencies()->DependOnStablePrototypeChains( + dependencies()->AssumePrototypesStable( native_context().object(), access_info.receiver_maps(), holder); } @@ -234,9 +234,9 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { // Determine actual holder and perform prototype chain checks. Handle holder; if (access_info.holder().ToHandle(&holder)) { - dependencies()->DependOnStablePrototypeChains( - native_context().object(), access_info.receiver_maps(), - holder); + dependencies()->AssumePrototypesStable(native_context().object(), + access_info.receiver_maps(), + holder); } else { holder = receiver; } @@ -411,7 +411,9 @@ Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance( // depend on that for the prototype constant-folding below. JSFunction::EnsureHasInitialMap(function); - Handle initial_map = dependencies()->DependOnInitialMap(function); + // Install a code dependency on the {function}s initial map. + Handle initial_map(function->initial_map(), isolate()); + dependencies()->AssumeInitialMapCantChange(initial_map); Node* prototype = jsgraph()->Constant(handle(initial_map->prototype(), isolate())); @@ -500,9 +502,8 @@ Reduction JSNativeContextSpecialization::ReduceJSResolvePromise(Node* node) { // Add proper dependencies on the {resolution}s [[Prototype]]s. Handle holder; if (access_info.holder().ToHandle(&holder)) { - dependencies()->DependOnStablePrototypeChains( - native_context().object(), access_info.receiver_maps(), - holder); + dependencies()->AssumePrototypesStable(native_context().object(), + access_info.receiver_maps(), holder); } // Simply fulfill the {promise} with the {resolution}. @@ -608,7 +609,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( // can be deleted or reconfigured to an accessor property). if (property_details.cell_type() != PropertyCellType::kMutable || property_details.IsConfigurable()) { - dependencies()->DependOnGlobalProperty(property_cell); + dependencies()->AssumePropertyCell(property_cell); } // Load from constant/undefined global property can be constant-folded. @@ -640,7 +641,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( // elimination if it's stable, i.e. the HeapObject wasn't // mutated without the cell state being updated. if (property_cell_value_map->is_stable()) { - dependencies()->DependOnStableMap(property_cell_value_map); + dependencies()->AssumeMapStable(property_cell_value_map); map = property_cell_value_map; } } @@ -662,7 +663,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( case PropertyCellType::kConstant: { // Record a code dependency on the cell, and just deoptimize if the new // value doesn't match the previous value stored inside the cell. - dependencies()->DependOnGlobalProperty(property_cell); + dependencies()->AssumePropertyCell(property_cell); Node* check = graph()->NewNode(simplified()->ReferenceEqual(), value, jsgraph()->Constant(property_cell_value)); @@ -675,7 +676,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( // Record a code dependency on the cell, and just deoptimize if the new // values' type doesn't match the type of the previous value in the // cell. - dependencies()->DependOnGlobalProperty(property_cell); + dependencies()->AssumePropertyCell(property_cell); Type property_cell_value_type; MachineRepresentation representation = MachineRepresentation::kTagged; if (property_cell_value->IsHeapObject()) { @@ -684,7 +685,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( Handle property_cell_value_map( Handle::cast(property_cell_value)->map(), isolate()); DCHECK(property_cell_value_map->is_stable()); - dependencies()->DependOnStableMap(property_cell_value_map); + dependencies()->AssumeMapStable(property_cell_value_map); // Check that the {value} is a HeapObject. value = effect = graph()->NewNode(simplified()->CheckHeapObject(), @@ -715,7 +716,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( case PropertyCellType::kMutable: { // Record a code dependency on the cell, and just deoptimize if the // property ever becomes read-only. - dependencies()->DependOnGlobalProperty(property_cell); + dependencies()->AssumePropertyCell(property_cell); effect = graph()->NewNode( simplified()->StoreField(ForPropertyCellValue( MachineRepresentation::kTagged, Type::NonInternal(), @@ -1096,7 +1097,8 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { // {function} in order to be notified about changes to the // "prototype" of {function}. JSFunction::EnsureHasInitialMap(function); - dependencies()->DependOnInitialMap(function); + Handle initial_map(function->initial_map(), isolate()); + dependencies()->AssumeInitialMapCantChange(initial_map); Handle prototype(function->prototype(), isolate()); Node* value = jsgraph()->Constant(prototype); ReplaceWithValue(node, value); @@ -1230,7 +1232,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( // Install dependencies on the relevant prototype maps. for (Handle prototype_map : prototype_maps) { - dependencies()->DependOnStableMap(prototype_map); + dependencies()->AssumeMapStable(prototype_map); } } @@ -1800,9 +1802,8 @@ JSNativeContextSpecialization::BuildPropertyLoad( Handle holder; PropertyAccessBuilder access_builder(jsgraph(), dependencies()); if (access_info.holder().ToHandle(&holder)) { - dependencies()->DependOnStablePrototypeChains( - native_context().object(), access_info.receiver_maps(), - holder); + dependencies()->AssumePrototypesStable(native_context().object(), + access_info.receiver_maps(), holder); } // Generate the actual property access. @@ -1858,9 +1859,8 @@ JSNativeContextSpecialization::BuildPropertyStore( PropertyAccessBuilder access_builder(jsgraph(), dependencies()); if (access_info.holder().ToHandle(&holder)) { DCHECK_NE(AccessMode::kStoreInLiteral, access_mode); - dependencies()->DependOnStablePrototypeChains( - native_context().object(), access_info.receiver_maps(), - holder); + dependencies()->AssumePrototypesStable(native_context().object(), + access_info.receiver_maps(), holder); } DCHECK(!access_info.IsNotFound()); @@ -2258,7 +2258,7 @@ JSNativeContextSpecialization::BuildElementAccess( if (isolate()->IsArrayBufferNeuteringIntact()) { // Add a code dependency so we are deoptimized in case an ArrayBuffer // gets neutered. - dependencies()->DependOnProtector( + dependencies()->AssumePropertyCell( factory()->array_buffer_neutering_protector()); } else { // Default to zero if the {receiver}s buffer was neutered. @@ -2661,7 +2661,8 @@ Node* JSNativeContextSpecialization::BuildIndexedStringLoad( KeyedAccessLoadMode load_mode) { if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS && isolate()->IsNoElementsProtectorIntact()) { - dependencies()->DependOnProtector(factory()->no_elements_protector()); + // Add a code dependency on the "no elements" protector. + dependencies()->AssumePropertyCell(factory()->no_elements_protector()); // Ensure that the {index} is a valid String length. index = *effect = graph()->NewNode( @@ -2810,7 +2811,8 @@ bool JSNativeContextSpecialization::CanTreatHoleAsUndefined( // Check if the array prototype chain is intact. if (!isolate()->IsNoElementsProtectorIntact()) return false; - dependencies()->DependOnProtector(factory()->no_elements_protector()); + // Install code dependency on the array protector cell. + dependencies()->AssumePropertyCell(factory()->no_elements_protector()); return true; } diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc index 3af19b9193..9a330c95ae 100644 --- a/src/compiler/pipeline.cc +++ b/src/compiler/pipeline.cc @@ -918,9 +918,7 @@ PipelineCompilationJob::Status PipelineCompilationJob::FinalizeJobImpl( } return FAILED; } - if (!compilation_info()->dependencies()->Commit(code)) { - return AbortOptimization(BailoutReason::kBailedOutDueToDependencyChange); - } + compilation_info()->dependencies()->Commit(code); compilation_info()->SetCode(code); compilation_info()->context()->native_context()->AddOptimizedCode(*code); diff --git a/src/compiler/property-access-builder.cc b/src/compiler/property-access-builder.cc index f97974c1fb..8fbc10413a 100644 --- a/src/compiler/property-access-builder.cc +++ b/src/compiler/property-access-builder.cc @@ -136,7 +136,7 @@ void PropertyAccessBuilder::BuildCheckMaps( if (receiver_map->is_stable()) { for (Handle map : receiver_maps) { if (map.is_identical_to(receiver_map)) { - dependencies()->DependOnStableMap(receiver_map); + dependencies()->AssumeMapStable(receiver_map); return; } } @@ -195,17 +195,18 @@ Node* PropertyAccessBuilder::TryBuildLoadConstantDataField( // but for now let's just do what Crankshaft does. LookupIterator it(m.Value(), name, LookupIterator::OWN_SKIP_INTERCEPTOR); if (it.state() == LookupIterator::DATA) { - bool is_readonly_non_configurable = + bool is_reaonly_non_configurable = it.IsReadOnly() && !it.IsConfigurable(); - if (is_readonly_non_configurable || + if (is_reaonly_non_configurable || (FLAG_track_constant_fields && access_info.IsDataConstantField())) { Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it)); - if (!is_readonly_non_configurable) { + if (!is_reaonly_non_configurable) { // It's necessary to add dependency on the map that introduced // the field. DCHECK(access_info.IsDataConstantField()); DCHECK(!it.is_dictionary_holder()); - dependencies()->DependOnFieldType(&it); + Handle field_owner_map = it.GetFieldOwnerMap(); + dependencies()->AssumeFieldOwner(field_owner_map); } return value; } @@ -263,7 +264,7 @@ Node* PropertyAccessBuilder::BuildLoadDataField( Handle field_map; if (access_info.field_map().ToHandle(&field_map)) { if (field_map->is_stable()) { - dependencies()->DependOnStableMap(field_map); + dependencies()->AssumeMapStable(field_map); field_access.map = field_map; } } diff --git a/src/compiler/typed-optimization.cc b/src/compiler/typed-optimization.cc index a7859389fc..3a681ecc64 100644 --- a/src/compiler/typed-optimization.cc +++ b/src/compiler/typed-optimization.cc @@ -148,7 +148,7 @@ Reduction TypedOptimization::ReduceCheckMaps(Node* node) { if (map_type.IsHeapConstant() && map_type.AsHeapConstant()->Value().is_identical_to(object_map)) { if (object_map->CanTransition()) { - dependencies()->DependOnStableMap(object_map); + dependencies()->AssumeMapStable(object_map); } return Replace(effect); } @@ -213,7 +213,7 @@ Reduction TypedOptimization::ReduceLoadField(Node* node) { Handle object_map; if (GetStableMapFromObjectType(object_type).ToHandle(&object_map)) { if (object_map->CanTransition()) { - dependencies()->DependOnStableMap(object_map); + dependencies()->AssumeMapStable(object_map); } Node* const value = jsgraph()->HeapConstant(object_map); ReplaceWithValue(node, value); diff --git a/src/heap/factory.cc b/src/heap/factory.cc index c310aa55a1..57e8bc1e60 100644 --- a/src/heap/factory.cc +++ b/src/heap/factory.cc @@ -2553,7 +2553,7 @@ MaybeHandle Factory::TryNewCode( Heap* heap = isolate()->heap(); CodePageCollectionMemoryModificationScope code_allocation(heap); HeapObject* result = - heap->AllocateRawWithLightRetry(object_size, CODE_SPACE); + heap->AllocateRawWithLigthRetry(object_size, CODE_SPACE); // Return an empty handle if we cannot allocate the code object. if (!result) return MaybeHandle(); diff --git a/src/heap/heap.cc b/src/heap/heap.cc index b5e55397e6..392afbf384 100644 --- a/src/heap/heap.cc +++ b/src/heap/heap.cc @@ -4473,7 +4473,7 @@ HeapObject* Heap::EnsureImmovableCode(HeapObject* heap_object, return heap_object; } -HeapObject* Heap::AllocateRawWithLightRetry(int size, AllocationSpace space, +HeapObject* Heap::AllocateRawWithLigthRetry(int size, AllocationSpace space, AllocationAlignment alignment) { HeapObject* result; AllocationResult alloc = AllocateRaw(size, space, alignment); @@ -4497,7 +4497,7 @@ HeapObject* Heap::AllocateRawWithLightRetry(int size, AllocationSpace space, HeapObject* Heap::AllocateRawWithRetryOrFail(int size, AllocationSpace space, AllocationAlignment alignment) { AllocationResult alloc; - HeapObject* result = AllocateRawWithLightRetry(size, space, alignment); + HeapObject* result = AllocateRawWithLigthRetry(size, space, alignment); if (result) return result; isolate()->counters()->gc_last_resort_from_handles()->Increment(); diff --git a/src/heap/heap.h b/src/heap/heap.h index 76163f7652..9c1e5e606e 100644 --- a/src/heap/heap.h +++ b/src/heap/heap.h @@ -1910,7 +1910,7 @@ class Heap { // triggered and the allocation is retried. This is performed multiple times. // If after that retry procedure the allocation still fails nullptr is // returned. - HeapObject* AllocateRawWithLightRetry( + HeapObject* AllocateRawWithLigthRetry( int size, AllocationSpace space, AllocationAlignment alignment = kWordAligned); diff --git a/src/objects.cc b/src/objects.cc index 5dfd8f861f..b9602427d8 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -25,6 +25,7 @@ #include "src/bootstrapper.h" #include "src/builtins/builtins.h" #include "src/code-stubs.h" +#include "src/compilation-dependencies.h" #include "src/compiler.h" #include "src/counters-inl.h" #include "src/counters.h" @@ -15017,6 +15018,34 @@ bool DependentCode::Compact() { } +void DependentCode::UpdateToFinishedCode(DependencyGroup group, Foreign* info, + WeakCell* code_cell) { + if (this->length() == 0 || this->group() > group) { + // There is no such group. + return; + } + if (this->group() < group) { + // The group comes later in the list. + next_link()->UpdateToFinishedCode(group, info, code_cell); + return; + } + DCHECK_EQ(group, this->group()); + DisallowHeapAllocation no_gc; + int count = this->count(); + for (int i = 0; i < count; i++) { + if (object_at(i) == info) { + set_object_at(i, code_cell); + break; + } + } +#ifdef DEBUG + for (int i = 0; i < count; i++) { + DCHECK(object_at(i) != info); + } +#endif +} + + void DependentCode::RemoveCompilationDependencies( DependentCode::DependencyGroup group, Foreign* info) { if (this->length() == 0 || this->group() > group) { @@ -15105,12 +15134,20 @@ bool DependentCode::MarkCodeForDeoptimization( int count = this->count(); for (int i = 0; i < count; i++) { Object* obj = object_at(i); - WeakCell* cell = WeakCell::cast(obj); - if (cell->cleared()) continue; - Code* code = Code::cast(cell->value()); - if (!code->marked_for_deoptimization()) { - code->SetMarkedForDeoptimization(DependencyGroupName(group)); - marked = true; + if (obj->IsWeakCell()) { + WeakCell* cell = WeakCell::cast(obj); + if (cell->cleared()) continue; + Code* code = Code::cast(cell->value()); + if (!code->marked_for_deoptimization()) { + code->SetMarkedForDeoptimization(DependencyGroupName(group)); + marked = true; + } + } else { + DCHECK(obj->IsForeign()); + CompilationDependencies* info = + reinterpret_cast( + Foreign::cast(obj)->foreign_address()); + info->Abort(); } } for (int i = 0; i < count; i++) { diff --git a/src/objects/code.h b/src/objects/code.h index 17d5d50ae0..8518d70aee 100644 --- a/src/objects/code.h +++ b/src/objects/code.h @@ -624,6 +624,9 @@ class DependentCode : public FixedArray { DependencyGroup group, Handle code_cell); + void UpdateToFinishedCode(DependencyGroup group, Foreign* info, + WeakCell* code_cell); + void RemoveCompilationDependencies(DependentCode::DependencyGroup group, Foreign* info); diff --git a/src/optimized-compilation-info.cc b/src/optimized-compilation-info.cc index e6a97eb7ac..c3889d8177 100644 --- a/src/optimized-compilation-info.cc +++ b/src/optimized-compilation-info.cc @@ -87,6 +87,9 @@ OptimizedCompilationInfo::~OptimizedCompilationInfo() { if (GetFlag(kDisableFutureOptimization) && has_shared_info()) { shared_info()->DisableOptimization(bailout_reason()); } + if (dependencies()) { + dependencies()->Rollback(); + } } void OptimizedCompilationInfo::set_deferred_handles( diff --git a/src/optimized-compilation-info.h b/src/optimized-compilation-info.h index b11b493054..8096f1d97b 100644 --- a/src/optimized-compilation-info.h +++ b/src/optimized-compilation-info.h @@ -309,7 +309,6 @@ class V8_EXPORT_PRIVATE OptimizedCompilationInfo final { std::shared_ptr deferred_handles_; // Dependencies for this compilation, e.g. stable maps. - // TODO(neis): Move this to PipelineData. std::unique_ptr dependencies_; BailoutReason bailout_reason_; diff --git a/test/cctest/compiler/function-tester.cc b/test/cctest/compiler/function-tester.cc index 44438df98d..6e77b9a1a6 100644 --- a/test/cctest/compiler/function-tester.cc +++ b/test/cctest/compiler/function-tester.cc @@ -153,7 +153,7 @@ Handle FunctionTester::Compile(Handle function) { Handle code = Pipeline::GenerateCodeForTesting(&info, isolate).ToHandleChecked(); - CHECK(info.dependencies()->Commit(code)); + info.dependencies()->Commit(code); info.context()->native_context()->AddOptimizedCode(*code); function->set_code(*code); return function; diff --git a/test/cctest/test-field-type-tracking.cc b/test/cctest/test-field-type-tracking.cc index e8b4e3146a..50ee39d035 100644 --- a/test/cctest/test-field-type-tracking.cc +++ b/test/cctest/test-field-type-tracking.cc @@ -477,7 +477,6 @@ TEST(ReconfigureAccessorToNonExistingDataField) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle none_type = FieldType::None(isolate); Handle pair = CreateAccessorPair(true, true); @@ -541,9 +540,9 @@ TEST(ReconfigureAccessorToNonExistingDataField) { // to a map with a property with None representation. TEST(ReconfigureAccessorToNonExistingDataFieldHeavy) { CcTest::InitializeVM(); - v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); Factory* factory = isolate->factory(); + v8::HandleScope scope(CcTest::isolate()); CompileRun( "function getter() { return 1; };" @@ -654,7 +653,9 @@ static void TestGeneralizeField(int detach_property_at_index, Handle field_owner(map->FindFieldOwner(isolate, property_index), isolate); CompilationDependencies dependencies(isolate, &zone); - dependencies.DependOnFieldType(map, property_index); + CHECK(!dependencies.HasAborted()); + + dependencies.AssumeFieldOwner(field_owner); Handle new_map = Map::ReconfigureProperty( isolate, map, property_index, kData, NONE, to.representation, to.type); @@ -670,21 +671,21 @@ static void TestGeneralizeField(int detach_property_at_index, CHECK(map->is_deprecated()); CHECK_NE(*map, *new_map); CHECK_EQ(expected_field_type_dependency && !field_owner->is_deprecated(), - !dependencies.AreValid()); + dependencies.HasAborted()); } else if (expected_deprecation) { CHECK(!map->is_stable()); CHECK(map->is_deprecated()); CHECK(field_owner->is_deprecated()); CHECK_NE(*map, *new_map); - CHECK(dependencies.AreValid()); + CHECK(!dependencies.HasAborted()); } else { CHECK(!field_owner->is_deprecated()); CHECK(map->is_stable()); // Map did not change, must be left stable. CHECK_EQ(*map, *new_map); - CHECK_EQ(expected_field_type_dependency, !dependencies.AreValid()); + CHECK_EQ(expected_field_type_dependency, dependencies.HasAborted()); } { @@ -698,6 +699,8 @@ static void TestGeneralizeField(int detach_property_at_index, } } + dependencies.Rollback(); // Properly cleanup compilation info. + // Update all deprecated maps and check that they are now the same. Handle updated_map = Map::Update(isolate, map); CHECK_EQ(*new_map, *updated_map); @@ -754,7 +757,6 @@ TEST(GeneralizeSmiFieldToDouble) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); TestGeneralizeField( @@ -767,7 +769,6 @@ TEST(GeneralizeSmiFieldToTagged) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle value_type = FieldType::Class(Map::Create(isolate, 0), isolate); @@ -782,7 +783,6 @@ TEST(GeneralizeDoubleFieldToTagged) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle value_type = FieldType::Class(Map::Create(isolate, 0), isolate); @@ -797,7 +797,6 @@ TEST(GeneralizeHeapObjectFieldToTagged) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle value_type = FieldType::Class(Map::Create(isolate, 0), isolate); @@ -812,7 +811,6 @@ TEST(GeneralizeHeapObjectFieldToHeapObject) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle current_type = @@ -843,7 +841,6 @@ TEST(GeneralizeNoneFieldToSmi) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle none_type = FieldType::None(isolate); Handle any_type = FieldType::Any(isolate); @@ -858,7 +855,6 @@ TEST(GeneralizeNoneFieldToDouble) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle none_type = FieldType::None(isolate); Handle any_type = FieldType::Any(isolate); @@ -873,7 +869,6 @@ TEST(GeneralizeNoneFieldToHeapObject) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle none_type = FieldType::None(isolate); Handle value_type = FieldType::Class(Map::Create(isolate, 0), isolate); @@ -889,7 +884,6 @@ TEST(GeneralizeNoneFieldToTagged) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle none_type = FieldType::None(isolate); Handle any_type = FieldType::Any(isolate); @@ -909,7 +903,6 @@ TEST(GeneralizeFieldWithAccessorProperties) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle pair = CreateAccessorPair(true, true); @@ -1021,8 +1014,10 @@ static void TestReconfigureDataFieldAttribute_GeneralizeField( CHECK(expectations2.Check(*map2)); Zone zone(isolate->allocator(), ZONE_NAME); + Handle field_owner(map->FindFieldOwner(isolate, kSplitProp), isolate); CompilationDependencies dependencies(isolate, &zone); - dependencies.DependOnFieldType(map, kSplitProp); + CHECK(!dependencies.HasAborted()); + dependencies.AssumeFieldOwner(field_owner); // Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which // should generalize representations in |map1|. @@ -1041,7 +1036,8 @@ static void TestReconfigureDataFieldAttribute_GeneralizeField( expected.type); } CHECK(map->is_deprecated()); - CHECK(dependencies.AreValid()); + CHECK(!dependencies.HasAborted()); + dependencies.Rollback(); // Properly cleanup compilation info. CHECK_NE(*map, *new_map); CHECK(!new_map->is_deprecated()); @@ -1103,8 +1099,10 @@ static void TestReconfigureDataFieldAttribute_GeneralizeFieldTrivial( CHECK(expectations2.Check(*map2)); Zone zone(isolate->allocator(), ZONE_NAME); + Handle field_owner(map->FindFieldOwner(isolate, kSplitProp), isolate); CompilationDependencies dependencies(isolate, &zone); - dependencies.DependOnFieldType(map, kSplitProp); + CHECK(!dependencies.HasAborted()); + dependencies.AssumeFieldOwner(field_owner); // Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which // should generalize representations in |map1|. @@ -1127,7 +1125,8 @@ static void TestReconfigureDataFieldAttribute_GeneralizeFieldTrivial( } CHECK(!map->is_deprecated()); CHECK_EQ(*map, *new_map); - CHECK_EQ(expected_field_type_dependency, !dependencies.AreValid()); + CHECK_EQ(expected_field_type_dependency, dependencies.HasAborted()); + dependencies.Rollback(); // Properly cleanup compilation info. CHECK(!new_map->is_deprecated()); CHECK(expectations.Check(*new_map)); @@ -1140,7 +1139,6 @@ TEST(ReconfigureDataFieldAttribute_GeneralizeSmiFieldToDouble) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); if (FLAG_track_constant_fields) { @@ -1170,7 +1168,6 @@ TEST(ReconfigureDataFieldAttribute_GeneralizeSmiFieldToTagged) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle value_type = FieldType::Class(Map::Create(isolate, 0), isolate); @@ -1202,7 +1199,6 @@ TEST(ReconfigureDataFieldAttribute_GeneralizeDoubleFieldToTagged) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle value_type = FieldType::Class(Map::Create(isolate, 0), isolate); @@ -1234,7 +1230,6 @@ TEST(ReconfigureDataFieldAttribute_GeneralizeHeapObjFieldToHeapObj) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle current_type = @@ -1330,7 +1325,6 @@ TEST(ReconfigureDataFieldAttribute_GeneralizeHeapObjectFieldToTagged) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle value_type = FieldType::Class(Map::Create(isolate, 0), isolate); @@ -1782,8 +1776,10 @@ static void TestReconfigureElementsKind_GeneralizeField( CHECK(expectations2.Check(*map2)); Zone zone(isolate->allocator(), ZONE_NAME); + Handle field_owner(map->FindFieldOwner(isolate, kDiffProp), isolate); CompilationDependencies dependencies(isolate, &zone); - dependencies.DependOnFieldType(map, kDiffProp); + CHECK(!dependencies.HasAborted()); + dependencies.AssumeFieldOwner(field_owner); // Reconfigure elements kinds of |map2|, which should generalize // representations in |map|. @@ -1801,7 +1797,8 @@ static void TestReconfigureElementsKind_GeneralizeField( expected.representation, expected.type); CHECK(map->is_deprecated()); - CHECK(dependencies.AreValid()); + CHECK(!dependencies.HasAborted()); + dependencies.Rollback(); // Properly cleanup compilation info. CHECK_NE(*map, *new_map); CHECK(!new_map->is_deprecated()); @@ -1875,8 +1872,10 @@ static void TestReconfigureElementsKind_GeneralizeFieldTrivial( CHECK(expectations2.Check(*map2)); Zone zone(isolate->allocator(), ZONE_NAME); + Handle field_owner(map->FindFieldOwner(isolate, kDiffProp), isolate); CompilationDependencies dependencies(isolate, &zone); - dependencies.DependOnFieldType(map, kDiffProp); + CHECK(!dependencies.HasAborted()); + dependencies.AssumeFieldOwner(field_owner); // Reconfigure elements kinds of |map2|, which should generalize // representations in |map|. @@ -1897,7 +1896,8 @@ static void TestReconfigureElementsKind_GeneralizeFieldTrivial( expected.representation, expected.type); CHECK(!map->is_deprecated()); CHECK_EQ(*map, *new_map); - CHECK(dependencies.AreValid()); + CHECK(!dependencies.HasAborted()); + dependencies.Rollback(); // Properly cleanup compilation info. CHECK(!new_map->is_deprecated()); CHECK(expectations.Check(*new_map)); @@ -1920,7 +1920,6 @@ TEST(ReconfigureElementsKind_GeneralizeSmiFieldToDouble) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); if (FLAG_track_constant_fields) { @@ -1949,7 +1948,6 @@ TEST(ReconfigureElementsKind_GeneralizeSmiFieldToTagged) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle value_type = FieldType::Class(Map::Create(isolate, 0), isolate); @@ -1980,7 +1978,6 @@ TEST(ReconfigureElementsKind_GeneralizeDoubleFieldToTagged) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle value_type = FieldType::Class(Map::Create(isolate, 0), isolate); @@ -2011,7 +2008,6 @@ TEST(ReconfigureElementsKind_GeneralizeHeapObjFieldToHeapObj) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle current_type = @@ -2104,7 +2100,6 @@ TEST(ReconfigureElementsKind_GeneralizeHeapObjectFieldToTagged) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle value_type = FieldType::Class(Map::Create(isolate, 0), isolate); @@ -2139,7 +2134,6 @@ TEST(ReconfigurePropertySplitMapTransitionsOverflow) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Expectations expectations(isolate); @@ -2328,7 +2322,6 @@ TEST(ElementsKindTransitionFromMapOwningDescriptor) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle value_type = FieldType::Class(Map::Create(isolate, 0), isolate); @@ -2358,7 +2351,6 @@ TEST(ElementsKindTransitionFromMapNotOwningDescriptor) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle value_type = FieldType::Class(Map::Create(isolate, 0), isolate); @@ -2680,7 +2672,6 @@ TEST(TransitionDataFieldToDataField) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); - Handle any_type = FieldType::Any(isolate); Handle value1 = handle(Smi::kZero, isolate); @@ -2717,7 +2708,6 @@ TEST(TransitionDataConstantToAnotherDataConstant) { v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); Factory* factory = isolate->factory(); - Handle name = factory->empty_string(); Handle sloppy_map = Map::CopyInitialMap(isolate, isolate->sloppy_function_map()); @@ -2752,7 +2742,6 @@ TEST(TransitionDataConstantToDataField) { v8::HandleScope scope(CcTest::isolate()); Isolate* isolate = CcTest::i_isolate(); Factory* factory = isolate->factory(); - Handle any_type = FieldType::Any(isolate); Handle js_func1 = diff --git a/test/unittests/compiler/js-call-reducer-unittest.cc b/test/unittests/compiler/js-call-reducer-unittest.cc index 4bace1c1d8..18d01df5c3 100644 --- a/test/unittests/compiler/js-call-reducer-unittest.cc +++ b/test/unittests/compiler/js-call-reducer-unittest.cc @@ -57,6 +57,11 @@ class JSCallReducerTest : public TypedGraphTest { i::FLAG_lazy_handler_deserialization = old_flag_lazy_handler_; } + // Ensure uncommitted compilation dependencies are discarded after each test. + // This prevents use-after-free accesses through invalidation of compilation + // dependencies. + void TearDown() override { deps_.Rollback(); } + Node* GlobalFunction(const char* name) { Handle f = Handle::cast( Object::GetProperty(