[heap] Use callbacks to dispatch store buffer operations.
BUG=chromium:648568, chromium:669920 Review-Url: https://codereview.chromium.org/2548213004 Cr-Commit-Position: refs/heads/master@{#41592}
This commit is contained in:
parent
dcbd3758d9
commit
9c191a0cda
@ -286,6 +286,10 @@ GarbageCollector Heap::SelectGarbageCollector(AllocationSpace space,
|
||||
return YoungGenerationCollector();
|
||||
}
|
||||
|
||||
void Heap::SetGCState(HeapState state) {
|
||||
gc_state_ = state;
|
||||
store_buffer_->SetMode(gc_state_);
|
||||
}
|
||||
|
||||
// TODO(1238405): Combine the infrastructure for --heap-stats and
|
||||
// --log-gc to avoid the complicated preprocessor and flag testing.
|
||||
@ -1430,7 +1434,8 @@ void Heap::CallGCEpilogueCallbacks(GCType gc_type,
|
||||
void Heap::MarkCompact() {
|
||||
PauseAllocationObserversScope pause_observers(this);
|
||||
|
||||
gc_state_ = MARK_COMPACT;
|
||||
SetGCState(MARK_COMPACT);
|
||||
|
||||
LOG(isolate_, ResourceEvent("markcompact", "begin"));
|
||||
|
||||
uint64_t size_of_objects_before_gc = SizeOfObjects();
|
||||
@ -1456,7 +1461,7 @@ void Heap::MinorMarkCompact() { UNREACHABLE(); }
|
||||
|
||||
void Heap::MarkCompactEpilogue() {
|
||||
TRACE_GC(tracer(), GCTracer::Scope::MC_EPILOGUE);
|
||||
gc_state_ = NOT_IN_GC;
|
||||
SetGCState(NOT_IN_GC);
|
||||
|
||||
isolate_->counters()->objs_since_last_full()->Set(0);
|
||||
|
||||
@ -1587,7 +1592,7 @@ void Heap::Scavenge() {
|
||||
|
||||
mark_compact_collector()->sweeper().EnsureNewSpaceCompleted();
|
||||
|
||||
gc_state_ = SCAVENGE;
|
||||
SetGCState(SCAVENGE);
|
||||
|
||||
// Implements Cheney's copying algorithm
|
||||
LOG(isolate_, ResourceEvent("scavenge", "begin"));
|
||||
@ -1713,7 +1718,7 @@ void Heap::Scavenge() {
|
||||
|
||||
LOG(isolate_, ResourceEvent("scavenge", "end"));
|
||||
|
||||
gc_state_ = NOT_IN_GC;
|
||||
SetGCState(NOT_IN_GC);
|
||||
}
|
||||
|
||||
|
||||
|
@ -818,6 +818,7 @@ class Heap {
|
||||
void PrintShortHeapStatistics();
|
||||
|
||||
inline HeapState gc_state() { return gc_state_; }
|
||||
void SetGCState(HeapState state);
|
||||
|
||||
inline bool IsInGCPostProcessing() { return gc_post_processing_depth_ > 0; }
|
||||
|
||||
|
@ -23,6 +23,8 @@ StoreBuffer::StoreBuffer(Heap* heap)
|
||||
lazy_top_[i] = nullptr;
|
||||
}
|
||||
task_running_ = false;
|
||||
insertion_callback = &InsertDuringRuntime;
|
||||
deletion_callback = &DeleteDuringRuntime;
|
||||
}
|
||||
|
||||
void StoreBuffer::SetUp() {
|
||||
@ -137,29 +139,5 @@ void StoreBuffer::ConcurrentlyProcessStoreBuffer() {
|
||||
task_running_ = false;
|
||||
}
|
||||
|
||||
void StoreBuffer::DeleteEntry(Address start, Address end) {
|
||||
// Deletions coming from the GC are directly deleted from the remembered
|
||||
// set. Deletions coming from the runtime are added to the store buffer
|
||||
// to allow concurrent processing.
|
||||
if (heap_->gc_state() == Heap::NOT_IN_GC) {
|
||||
if (top_ + sizeof(Address) * 2 > limit_[current_]) {
|
||||
StoreBufferOverflow(heap_->isolate());
|
||||
}
|
||||
*top_ = MarkDeletionAddress(start);
|
||||
top_++;
|
||||
*top_ = end;
|
||||
top_++;
|
||||
} else {
|
||||
// In GC the store buffer has to be empty at any time.
|
||||
DCHECK(Empty());
|
||||
Page* page = Page::FromAddress(start);
|
||||
if (end) {
|
||||
RememberedSet<OLD_TO_NEW>::RemoveRange(page, start, end,
|
||||
SlotSet::PREFREE_EMPTY_BUCKETS);
|
||||
} else {
|
||||
RememberedSet<OLD_TO_NEW>::Remove(page, start);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -63,22 +63,76 @@ class StoreBuffer {
|
||||
// If we only want to delete a single slot, end should be set to null which
|
||||
// will be written into the second field. When processing the store buffer
|
||||
// the more efficient Remove method will be called in this case.
|
||||
void DeleteEntry(Address start, Address end = nullptr);
|
||||
void DeleteEntry(Address start, Address end = nullptr) {
|
||||
// Deletions coming from the GC are directly deleted from the remembered
|
||||
// set. Deletions coming from the runtime are added to the store buffer
|
||||
// to allow concurrent processing.
|
||||
deletion_callback(this, start, end);
|
||||
}
|
||||
|
||||
static void DeleteDuringGarbageCollection(StoreBuffer* store_buffer,
|
||||
Address start, Address end) {
|
||||
// In GC the store buffer has to be empty at any time.
|
||||
DCHECK(store_buffer->Empty());
|
||||
DCHECK(store_buffer->heap()->gc_state() != Heap::NOT_IN_GC);
|
||||
Page* page = Page::FromAddress(start);
|
||||
if (end) {
|
||||
RememberedSet<OLD_TO_NEW>::RemoveRange(page, start, end,
|
||||
SlotSet::PREFREE_EMPTY_BUCKETS);
|
||||
} else {
|
||||
RememberedSet<OLD_TO_NEW>::Remove(page, start);
|
||||
}
|
||||
}
|
||||
|
||||
static void DeleteDuringRuntime(StoreBuffer* store_buffer, Address start,
|
||||
Address end) {
|
||||
DCHECK(store_buffer->heap()->gc_state() == Heap::NOT_IN_GC);
|
||||
store_buffer->InsertDeletionIntoStoreBuffer(start, end);
|
||||
}
|
||||
|
||||
void InsertDeletionIntoStoreBuffer(Address start, Address end) {
|
||||
if (top_ + sizeof(Address) * 2 > limit_[current_]) {
|
||||
StoreBufferOverflow(heap_->isolate());
|
||||
}
|
||||
*top_ = MarkDeletionAddress(start);
|
||||
top_++;
|
||||
*top_ = end;
|
||||
top_++;
|
||||
}
|
||||
|
||||
static void InsertDuringGarbageCollection(StoreBuffer* store_buffer,
|
||||
Address slot) {
|
||||
DCHECK(store_buffer->heap()->gc_state() != Heap::NOT_IN_GC);
|
||||
RememberedSet<OLD_TO_NEW>::Insert(Page::FromAddress(slot), slot);
|
||||
}
|
||||
|
||||
static void InsertDuringRuntime(StoreBuffer* store_buffer, Address slot) {
|
||||
DCHECK(store_buffer->heap()->gc_state() == Heap::NOT_IN_GC);
|
||||
store_buffer->InsertIntoStoreBuffer(slot);
|
||||
}
|
||||
|
||||
void InsertIntoStoreBuffer(Address slot) {
|
||||
if (top_ + sizeof(Address) > limit_[current_]) {
|
||||
StoreBufferOverflow(heap_->isolate());
|
||||
}
|
||||
*top_ = slot;
|
||||
top_++;
|
||||
}
|
||||
|
||||
void InsertEntry(Address slot) {
|
||||
// Insertions coming from the GC are directly inserted into the remembered
|
||||
// set. Insertions coming from the runtime are added to the store buffer to
|
||||
// allow concurrent processing.
|
||||
if (heap_->gc_state() == Heap::NOT_IN_GC) {
|
||||
if (top_ + sizeof(Address) > limit_[current_]) {
|
||||
StoreBufferOverflow(heap_->isolate());
|
||||
}
|
||||
*top_ = slot;
|
||||
top_++;
|
||||
insertion_callback(this, slot);
|
||||
}
|
||||
|
||||
void SetMode(Heap::HeapState state) {
|
||||
if (state == Heap::NOT_IN_GC) {
|
||||
insertion_callback = &InsertDuringRuntime;
|
||||
deletion_callback = &DeleteDuringRuntime;
|
||||
} else {
|
||||
// In GC the store buffer has to be empty at any time.
|
||||
DCHECK(Empty());
|
||||
RememberedSet<OLD_TO_NEW>::Insert(Page::FromAddress(slot), slot);
|
||||
insertion_callback = &InsertDuringGarbageCollection;
|
||||
deletion_callback = &DeleteDuringGarbageCollection;
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,6 +149,8 @@ class StoreBuffer {
|
||||
return top_ == start_[current_];
|
||||
}
|
||||
|
||||
Heap* heap() { return heap_; }
|
||||
|
||||
private:
|
||||
// There are two store buffers. If one store buffer fills up, the main thread
|
||||
// publishes the top pointer of the store buffer that needs processing in its
|
||||
@ -143,6 +199,11 @@ class StoreBuffer {
|
||||
int current_;
|
||||
|
||||
base::VirtualMemory* virtual_memory_;
|
||||
|
||||
// Callbacks are more efficient than reading out the gc state for every
|
||||
// store buffer operation.
|
||||
std::function<void(StoreBuffer*, Address)> insertion_callback;
|
||||
std::function<void(StoreBuffer*, Address, Address)> deletion_callback;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
Loading…
Reference in New Issue
Block a user