// Copyright 2018 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. #ifndef V8_OBJECTS_ALLOCATION_SITE_INL_H_ #define V8_OBJECTS_ALLOCATION_SITE_INL_H_ #include "src/objects/allocation-site.h" #include "src/heap/heap-write-barrier-inl.h" #include "src/objects/js-objects-inl.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" namespace v8 { namespace internal { OBJECT_CONSTRUCTORS_IMPL(AllocationMemento, Struct) OBJECT_CONSTRUCTORS_IMPL(AllocationSite, Struct) NEVER_READ_ONLY_SPACE_IMPL(AllocationSite) CAST_ACCESSOR(AllocationMemento) CAST_ACCESSOR(AllocationSite) ACCESSORS(AllocationSite, transition_info_or_boilerplate, Object, kTransitionInfoOrBoilerplateOffset) ACCESSORS(AllocationSite, nested_site, Object, kNestedSiteOffset) INT32_ACCESSORS(AllocationSite, pretenure_data, kPretenureDataOffset) INT32_ACCESSORS(AllocationSite, pretenure_create_count, kPretenureCreateCountOffset) ACCESSORS(AllocationSite, dependent_code, DependentCode, kDependentCodeOffset) ACCESSORS_CHECKED(AllocationSite, weak_next, Object, kWeakNextOffset, HasWeakNext()) ACCESSORS(AllocationMemento, allocation_site, Object, kAllocationSiteOffset) JSObject AllocationSite::boilerplate() const { DCHECK(PointsToLiteral()); return JSObject::cast(transition_info_or_boilerplate()); } void AllocationSite::set_boilerplate(JSObject object, WriteBarrierMode mode) { set_transition_info_or_boilerplate(object, mode); } int AllocationSite::transition_info() const { DCHECK(!PointsToLiteral()); return Smi::cast(transition_info_or_boilerplate()).value(); } void AllocationSite::set_transition_info(int value) { DCHECK(!PointsToLiteral()); set_transition_info_or_boilerplate(Smi::FromInt(value), SKIP_WRITE_BARRIER); } bool AllocationSite::HasWeakNext() const { return map() == GetReadOnlyRoots().allocation_site_map(); } void AllocationSite::Initialize() { set_transition_info_or_boilerplate(Smi::kZero); SetElementsKind(GetInitialFastElementsKind()); set_nested_site(Smi::kZero); set_pretenure_data(0); set_pretenure_create_count(0); set_dependent_code( DependentCode::cast(GetReadOnlyRoots().empty_weak_fixed_array()), SKIP_WRITE_BARRIER); } bool AllocationSite::IsZombie() const { return pretenure_decision() == kZombie; } bool AllocationSite::IsMaybeTenure() const { return pretenure_decision() == kMaybeTenure; } bool AllocationSite::PretenuringDecisionMade() const { return pretenure_decision() != kUndecided; } void AllocationSite::MarkZombie() { DCHECK(!IsZombie()); Initialize(); set_pretenure_decision(kZombie); } ElementsKind AllocationSite::GetElementsKind() const { return ElementsKindBits::decode(transition_info()); } void AllocationSite::SetElementsKind(ElementsKind kind) { set_transition_info(ElementsKindBits::update(transition_info(), kind)); } bool AllocationSite::CanInlineCall() const { return DoNotInlineBit::decode(transition_info()) == 0; } void AllocationSite::SetDoNotInlineCall() { set_transition_info(DoNotInlineBit::update(transition_info(), true)); } bool AllocationSite::PointsToLiteral() const { Object raw_value = transition_info_or_boilerplate(); DCHECK_EQ(!raw_value.IsSmi(), raw_value.IsJSArray() || raw_value.IsJSObject()); return !raw_value.IsSmi(); } // Heuristic: We only need to create allocation site info if the boilerplate // elements kind is the initial elements kind. bool AllocationSite::ShouldTrack(ElementsKind boilerplate_elements_kind) { return IsSmiElementsKind(boilerplate_elements_kind); } inline bool AllocationSite::CanTrack(InstanceType type) { if (FLAG_allocation_site_pretenuring) { // TurboFan doesn't care at all about String pretenuring feedback, // so don't bother even trying to track that. return type == JS_ARRAY_TYPE || type == JS_OBJECT_TYPE; } return type == JS_ARRAY_TYPE; } AllocationSite::PretenureDecision AllocationSite::pretenure_decision() const { return PretenureDecisionBits::decode(pretenure_data()); } void AllocationSite::set_pretenure_decision(PretenureDecision decision) { int32_t value = pretenure_data(); set_pretenure_data(PretenureDecisionBits::update(value, decision)); } bool AllocationSite::deopt_dependent_code() const { return DeoptDependentCodeBit::decode(pretenure_data()); } void AllocationSite::set_deopt_dependent_code(bool deopt) { int32_t value = pretenure_data(); set_pretenure_data(DeoptDependentCodeBit::update(value, deopt)); } int AllocationSite::memento_found_count() const { return MementoFoundCountBits::decode(pretenure_data()); } inline void AllocationSite::set_memento_found_count(int count) { int32_t value = pretenure_data(); // Verify that we can count more mementos than we can possibly find in one // new space collection. DCHECK((GetHeap()->MaxSemiSpaceSize() / (Heap::kMinObjectSizeInTaggedWords * kTaggedSize + AllocationMemento::kSize)) < MementoFoundCountBits::kMax); DCHECK_LT(count, MementoFoundCountBits::kMax); set_pretenure_data(MementoFoundCountBits::update(value, count)); } int AllocationSite::memento_create_count() const { return pretenure_create_count(); } void AllocationSite::set_memento_create_count(int count) { set_pretenure_create_count(count); } bool AllocationSite::IncrementMementoFoundCount(int increment) { if (IsZombie()) return false; int value = memento_found_count(); set_memento_found_count(value + increment); return memento_found_count() >= kPretenureMinimumCreated; } inline void AllocationSite::IncrementMementoCreateCount() { DCHECK(FLAG_allocation_site_pretenuring); int value = memento_create_count(); set_memento_create_count(value + 1); } bool AllocationMemento::IsValid() const { return allocation_site().IsAllocationSite() && !AllocationSite::cast(allocation_site()).IsZombie(); } AllocationSite AllocationMemento::GetAllocationSite() const { DCHECK(IsValid()); return AllocationSite::cast(allocation_site()); } Address AllocationMemento::GetAllocationSiteUnchecked() const { return allocation_site().ptr(); } template bool AllocationSite::DigestTransitionFeedback(Handle site, ElementsKind to_kind) { Isolate* isolate = site->GetIsolate(); bool result = false; if (site->PointsToLiteral() && site->boilerplate().IsJSArray()) { Handle boilerplate(JSArray::cast(site->boilerplate()), isolate); ElementsKind kind = boilerplate->GetElementsKind(); // if kind is holey ensure that to_kind is as well. if (IsHoleyElementsKind(kind)) { to_kind = GetHoleyElementsKind(to_kind); } if (IsMoreGeneralElementsKindTransition(kind, to_kind)) { // If the array is huge, it's not likely to be defined in a local // function, so we shouldn't make new instances of it very often. uint32_t length = 0; CHECK(boilerplate->length().ToArrayLength(&length)); if (length <= kMaximumArrayBytesToPretransition) { if (update_or_check == AllocationSiteUpdateMode::kCheckOnly) { return true; } if (FLAG_trace_track_allocation_sites) { bool is_nested = site->IsNested(); PrintF("AllocationSite: JSArray %p boilerplate %supdated %s->%s\n", reinterpret_cast(site->ptr()), is_nested ? "(nested)" : " ", ElementsKindToString(kind), ElementsKindToString(to_kind)); } JSObject::TransitionElementsKind(boilerplate, to_kind); site->dependent_code().DeoptimizeDependentCodeGroup( isolate, DependentCode::kAllocationSiteTransitionChangedGroup); result = true; } } } else { // The AllocationSite is for a constructed Array. ElementsKind kind = site->GetElementsKind(); // if kind is holey ensure that to_kind is as well. if (IsHoleyElementsKind(kind)) { to_kind = GetHoleyElementsKind(to_kind); } if (IsMoreGeneralElementsKindTransition(kind, to_kind)) { if (update_or_check == AllocationSiteUpdateMode::kCheckOnly) return true; if (FLAG_trace_track_allocation_sites) { PrintF("AllocationSite: JSArray %p site updated %s->%s\n", reinterpret_cast(site->ptr()), ElementsKindToString(kind), ElementsKindToString(to_kind)); } site->SetElementsKind(to_kind); site->dependent_code().DeoptimizeDependentCodeGroup( isolate, DependentCode::kAllocationSiteTransitionChangedGroup); result = true; } } return result; } } // namespace internal } // namespace v8 #include "src/objects/object-macros-undef.h" #endif // V8_OBJECTS_ALLOCATION_SITE_INL_H_