[heap] Fix failing DCHECK with original_top < top
The problem here was that IncrementalMarking::Step was invoking new_space()->ResetOriginalTop() which sets original_top to the current top. IncrementalMarking::Step could be invoked during InvokeAllocationObservers(), which is called right after acquiring a new LAB and allocating the first object in it. However this first allocation might be from generated code with allocation folding enabled. The generated code might not use all of the memory it allocated and in that process move top backwards again. Nevertheless InvokeAllocationObservers() could already set original_top to the current top. If the generated code later not uses all of that memory, original_top can be bigger than top. Fix this problem by ensuring that original_top always equals the LAB start. Each time LAB start is moved/accounted for, original_top is now updated as well for the new space. Also IncrementalMarking::Step() isn't allowed to move original_top anymore. Bug: chromium:1116278, v8:10315 Change-Id: Ib18a0b07e2665b8ba933555387b84329cbecdf5b Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2398519 Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Commit-Queue: Dominik Inführ <dinfuehr@chromium.org> Cr-Commit-Position: refs/heads/master@{#69840}
This commit is contained in:
parent
00efb85b9c
commit
ca4489972a
@ -249,6 +249,10 @@ class ShouldBeZeroOnReturnScope final {
|
|||||||
Object StackGuard::HandleInterrupts() {
|
Object StackGuard::HandleInterrupts() {
|
||||||
TRACE_EVENT0("v8.execute", "V8.HandleInterrupts");
|
TRACE_EVENT0("v8.execute", "V8.HandleInterrupts");
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
isolate_->heap()->VerifyNewSpaceTop();
|
||||||
|
#endif
|
||||||
|
|
||||||
if (FLAG_verify_predictable) {
|
if (FLAG_verify_predictable) {
|
||||||
// Advance synthetic time by making a time request.
|
// Advance synthetic time by making a time request.
|
||||||
isolate_->heap()->MonotonicallyIncreasingTimeInMs();
|
isolate_->heap()->MonotonicallyIncreasingTimeInMs();
|
||||||
|
@ -3741,6 +3741,7 @@ double Heap::MonotonicallyIncreasingTimeInMs() {
|
|||||||
static_cast<double>(base::Time::kMillisecondsPerSecond);
|
static_cast<double>(base::Time::kMillisecondsPerSecond);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Heap::VerifyNewSpaceTop() { new_space()->VerifyTop(); }
|
||||||
|
|
||||||
bool Heap::IdleNotification(int idle_time_in_ms) {
|
bool Heap::IdleNotification(int idle_time_in_ms) {
|
||||||
return IdleNotification(
|
return IdleNotification(
|
||||||
|
@ -699,6 +699,8 @@ class Heap {
|
|||||||
|
|
||||||
V8_EXPORT_PRIVATE double MonotonicallyIncreasingTimeInMs();
|
V8_EXPORT_PRIVATE double MonotonicallyIncreasingTimeInMs();
|
||||||
|
|
||||||
|
void VerifyNewSpaceTop();
|
||||||
|
|
||||||
void RecordStats(HeapStats* stats, bool take_snapshot = false);
|
void RecordStats(HeapStats* stats, bool take_snapshot = false);
|
||||||
|
|
||||||
bool MeasureMemory(std::unique_ptr<v8::MeasureMemoryDelegate> delegate,
|
bool MeasureMemory(std::unique_ptr<v8::MeasureMemoryDelegate> delegate,
|
||||||
|
@ -122,6 +122,9 @@ void IncrementalMarkingJob::Task::RunInternal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!incremental_marking->IsStopped()) {
|
if (!incremental_marking->IsStopped()) {
|
||||||
|
// All objects are initialized at that point.
|
||||||
|
heap->new_space()->MarkLabStartInitialized();
|
||||||
|
heap->new_lo_space()->ResetPendingObject();
|
||||||
StepResult step_result = Step(heap);
|
StepResult step_result = Step(heap);
|
||||||
if (!incremental_marking->IsStopped()) {
|
if (!incremental_marking->IsStopped()) {
|
||||||
const TaskType task_type =
|
const TaskType task_type =
|
||||||
|
@ -1041,8 +1041,6 @@ StepResult IncrementalMarking::Step(double max_step_size_in_ms,
|
|||||||
double embedder_deadline = 0.0;
|
double embedder_deadline = 0.0;
|
||||||
if (state_ == MARKING) {
|
if (state_ == MARKING) {
|
||||||
if (FLAG_concurrent_marking) {
|
if (FLAG_concurrent_marking) {
|
||||||
heap_->new_space()->ResetOriginalTop();
|
|
||||||
heap_->new_lo_space()->ResetPendingObject();
|
|
||||||
// It is safe to merge back all objects that were on hold to the shared
|
// It is safe to merge back all objects that were on hold to the shared
|
||||||
// work list at Step because we are at a safepoint where all objects
|
// work list at Step because we are at a safepoint where all objects
|
||||||
// are properly initialized.
|
// are properly initialized.
|
||||||
|
@ -36,11 +36,7 @@ class EvacuationAllocator {
|
|||||||
// Give back remaining LAB space if this EvacuationAllocator's new space LAB
|
// Give back remaining LAB space if this EvacuationAllocator's new space LAB
|
||||||
// sits right next to new space allocation top.
|
// sits right next to new space allocation top.
|
||||||
const LinearAllocationArea info = new_space_lab_.CloseAndMakeIterable();
|
const LinearAllocationArea info = new_space_lab_.CloseAndMakeIterable();
|
||||||
const Address top = new_space_->top();
|
new_space_->MaybeFreeUnusedLab(info);
|
||||||
if (info.limit() != kNullAddress && info.limit() == top) {
|
|
||||||
DCHECK_NE(info.top(), kNullAddress);
|
|
||||||
*new_space_->allocation_top_address() = info.top();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline AllocationResult Allocate(AllocationSpace space, int object_size,
|
inline AllocationResult Allocate(AllocationSpace space, int object_size,
|
||||||
|
@ -511,7 +511,6 @@ bool MarkCompactCollector::StartCompaction() {
|
|||||||
|
|
||||||
void MarkCompactCollector::StartMarking() {
|
void MarkCompactCollector::StartMarking() {
|
||||||
if (FLAG_concurrent_marking || FLAG_parallel_marking) {
|
if (FLAG_concurrent_marking || FLAG_parallel_marking) {
|
||||||
heap_->new_space()->ResetOriginalTop();
|
|
||||||
heap_->new_lo_space()->ResetPendingObject();
|
heap_->new_lo_space()->ResetPendingObject();
|
||||||
}
|
}
|
||||||
std::vector<Address> contexts =
|
std::vector<Address> contexts =
|
||||||
|
@ -87,6 +87,10 @@ HeapObject SemiSpaceObjectIterator::Next() {
|
|||||||
AllocationResult NewSpace::AllocateRaw(int size_in_bytes,
|
AllocationResult NewSpace::AllocateRaw(int size_in_bytes,
|
||||||
AllocationAlignment alignment,
|
AllocationAlignment alignment,
|
||||||
AllocationOrigin origin) {
|
AllocationOrigin origin) {
|
||||||
|
#if DEBUG
|
||||||
|
VerifyTop();
|
||||||
|
#endif
|
||||||
|
|
||||||
AllocationResult result;
|
AllocationResult result;
|
||||||
|
|
||||||
if (alignment != kWordAligned) {
|
if (alignment != kWordAligned) {
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "src/heap/memory-allocator.h"
|
#include "src/heap/memory-allocator.h"
|
||||||
#include "src/heap/paged-spaces.h"
|
#include "src/heap/paged-spaces.h"
|
||||||
#include "src/heap/spaces-inl.h"
|
#include "src/heap/spaces-inl.h"
|
||||||
|
#include "src/heap/spaces.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
@ -496,6 +497,10 @@ void NewSpace::UpdateInlineAllocationLimit(size_t min_size) {
|
|||||||
DCHECK_LE(new_limit, to_space_.page_high());
|
DCHECK_LE(new_limit, to_space_.page_high());
|
||||||
allocation_info_.set_limit(new_limit);
|
allocation_info_.set_limit(new_limit);
|
||||||
DCHECK_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
|
DCHECK_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
VerifyTop();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NewSpace::AddFreshPage() {
|
bool NewSpace::AddFreshPage() {
|
||||||
@ -550,6 +555,19 @@ bool NewSpace::EnsureAllocation(int size_in_bytes,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NewSpace::MaybeFreeUnusedLab(LinearAllocationArea info) {
|
||||||
|
if (info.limit() != kNullAddress && info.limit() == top()) {
|
||||||
|
DCHECK_NE(info.top(), kNullAddress);
|
||||||
|
allocation_info_.set_top(info.top());
|
||||||
|
allocation_info_.MoveStartToTop();
|
||||||
|
original_top_.store(info.top(), std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
VerifyTop();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<ObjectIterator> NewSpace::GetObjectIterator(Heap* heap) {
|
std::unique_ptr<ObjectIterator> NewSpace::GetObjectIterator(Heap* heap) {
|
||||||
return std::unique_ptr<ObjectIterator>(new SemiSpaceObjectIterator(this));
|
return std::unique_ptr<ObjectIterator>(new SemiSpaceObjectIterator(this));
|
||||||
}
|
}
|
||||||
@ -610,6 +628,20 @@ AllocationResult NewSpace::AllocateRawAligned(int size_in_bytes,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NewSpace::VerifyTop() {
|
||||||
|
// Ensure validity of LAB: start <= top <= limit
|
||||||
|
DCHECK_LE(allocation_info_.start(), allocation_info_.top());
|
||||||
|
DCHECK_LE(allocation_info_.top(), allocation_info_.limit());
|
||||||
|
|
||||||
|
// Ensure that original_top_ always equals LAB start.
|
||||||
|
DCHECK_EQ(original_top_, allocation_info_.start());
|
||||||
|
|
||||||
|
// Ensure that limit() is <= original_limit_, original_limit_ always needs
|
||||||
|
// to be end of curent to space page.
|
||||||
|
DCHECK_LE(allocation_info_.limit(), original_limit_);
|
||||||
|
DCHECK_EQ(original_limit_, to_space_.page_high());
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef VERIFY_HEAP
|
#ifdef VERIFY_HEAP
|
||||||
// We do not use the SemiSpaceObjectIterator because verification doesn't assume
|
// We do not use the SemiSpaceObjectIterator because verification doesn't assume
|
||||||
// that it works (it depends on the invariants we are checking).
|
// that it works (it depends on the invariants we are checking).
|
||||||
|
@ -368,11 +368,7 @@ class V8_EXPORT_PRIVATE NewSpace
|
|||||||
return to_space_.minimum_capacity();
|
return to_space_.minimum_capacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetOriginalTop() {
|
void VerifyTop();
|
||||||
DCHECK_GE(top(), original_top_);
|
|
||||||
DCHECK_LE(top(), original_limit_);
|
|
||||||
original_top_.store(top(), std::memory_order_release);
|
|
||||||
}
|
|
||||||
|
|
||||||
Address original_top_acquire() {
|
Address original_top_acquire() {
|
||||||
return original_top_.load(std::memory_order_acquire);
|
return original_top_.load(std::memory_order_acquire);
|
||||||
@ -458,6 +454,14 @@ class V8_EXPORT_PRIVATE NewSpace
|
|||||||
SemiSpace& from_space() { return from_space_; }
|
SemiSpace& from_space() { return from_space_; }
|
||||||
SemiSpace& to_space() { return to_space_; }
|
SemiSpace& to_space() { return to_space_; }
|
||||||
|
|
||||||
|
void MoveOriginalTopForward() {
|
||||||
|
DCHECK_GE(top(), original_top_);
|
||||||
|
DCHECK_LE(top(), original_limit_);
|
||||||
|
original_top_.store(top(), std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaybeFreeUnusedLab(LinearAllocationArea info);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Update linear allocation area to match the current to-space page.
|
// Update linear allocation area to match the current to-space page.
|
||||||
void UpdateLinearAllocationArea();
|
void UpdateLinearAllocationArea();
|
||||||
|
@ -365,7 +365,7 @@ void SpaceWithLinearArea::PauseAllocationObservers() {
|
|||||||
|
|
||||||
void SpaceWithLinearArea::ResumeAllocationObservers() {
|
void SpaceWithLinearArea::ResumeAllocationObservers() {
|
||||||
Space::ResumeAllocationObservers();
|
Space::ResumeAllocationObservers();
|
||||||
allocation_info_.MoveStartToTop();
|
MarkLabStartInitialized();
|
||||||
UpdateInlineAllocationLimit(0);
|
UpdateInlineAllocationLimit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,7 +374,18 @@ void SpaceWithLinearArea::AdvanceAllocationObservers() {
|
|||||||
allocation_info_.start() != allocation_info_.top()) {
|
allocation_info_.start() != allocation_info_.top()) {
|
||||||
allocation_counter_.AdvanceAllocationObservers(allocation_info_.top() -
|
allocation_counter_.AdvanceAllocationObservers(allocation_info_.top() -
|
||||||
allocation_info_.start());
|
allocation_info_.start());
|
||||||
allocation_info_.MoveStartToTop();
|
MarkLabStartInitialized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpaceWithLinearArea::MarkLabStartInitialized() {
|
||||||
|
allocation_info_.MoveStartToTop();
|
||||||
|
if (identity() == NEW_SPACE) {
|
||||||
|
heap()->new_space()->MoveOriginalTopForward();
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
heap()->VerifyNewSpaceTop();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,6 +511,8 @@ class SpaceWithLinearArea : public Space {
|
|||||||
size_t aligned_size_in_bytes,
|
size_t aligned_size_in_bytes,
|
||||||
size_t allocation_size);
|
size_t allocation_size);
|
||||||
|
|
||||||
|
void MarkLabStartInitialized();
|
||||||
|
|
||||||
// When allocation observers are active we may use a lower limit to allow the
|
// When allocation observers are active we may use a lower limit to allow the
|
||||||
// observers to 'interrupt' earlier than the natural limit. Given a linear
|
// observers to 'interrupt' earlier than the natural limit. Given a linear
|
||||||
// area bounded by [start, end), this function computes the limit to use to
|
// area bounded by [start, end), this function computes the limit to use to
|
||||||
|
Loading…
Reference in New Issue
Block a user