Commit 660fb963 authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

[heap] Templatize worklist for arbitrary entry types

To be used with tuples of heap objects and sizes.

Bug: chromium:738865
Change-Id: I29d9cf98bb2097cc8e1616aaf19a251507ffbd9e
Reviewed-on: https://chromium-review.googlesource.com/559050Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46407}
parent 18e73287
......@@ -15,12 +15,12 @@ namespace internal {
class Heap;
class Isolate;
template <int SEGMENT_SIZE>
template <typename EntryType, int SEGMENT_SIZE>
class Worklist;
class ConcurrentMarking {
public:
using MarkingWorklist = Worklist<64 /* segment size */>;
using MarkingWorklist = Worklist<HeapObject*, 64 /* segment size */>;
ConcurrentMarking(Heap* heap, MarkingWorklist* shared_,
MarkingWorklist* bailout_);
......
......@@ -816,8 +816,8 @@ void IncrementalMarking::UpdateMarkingWorklistAfterScavenge() {
Map* filler_map = heap_->one_pointer_filler_map();
marking_worklist()->Update([this,
filler_map](HeapObject* obj) -> HeapObject* {
marking_worklist()->Update([this, filler_map](HeapObject* obj,
HeapObject** out) -> bool {
DCHECK(obj->IsHeapObject());
// Only pointers to from space have to be updated.
if (heap_->InFromSpace(obj)) {
......@@ -828,35 +828,44 @@ void IncrementalMarking::UpdateMarkingWorklistAfterScavenge() {
// If these object are dead at scavenging time, their marking deque
// entries will not point to forwarding addresses. Hence, we can discard
// them.
return nullptr;
return false;
}
HeapObject* dest = map_word.ToForwardingAddress();
DCHECK_IMPLIES(
ObjectMarking::IsWhite<kAtomicity>(obj, marking_state(obj)),
obj->IsFiller());
return dest;
*out = dest;
return true;
} else if (heap_->InToSpace(obj)) {
// The object may be on a page that was moved in new space.
DCHECK(
Page::FromAddress(obj->address())->IsFlagSet(Page::SWEEP_TO_ITERATE));
return ObjectMarking::IsGrey<kAtomicity>(obj, MarkingState::External(obj))
? obj
: nullptr;
if (ObjectMarking::IsGrey<kAtomicity>(obj, MarkingState::External(obj))) {
*out = obj;
return true;
}
return false;
} else {
// The object may be on a page that was moved from new to old space.
if (Page::FromAddress(obj->address())
->IsFlagSet(Page::SWEEP_TO_ITERATE)) {
return ObjectMarking::IsGrey<kAtomicity>(obj,
MarkingState::External(obj))
? obj
: nullptr;
if (ObjectMarking::IsGrey<kAtomicity>(obj,
MarkingState::External(obj))) {
*out = obj;
return true;
}
return false;
}
DCHECK_IMPLIES(
ObjectMarking::IsWhite<kAtomicity>(obj, marking_state(obj)),
obj->IsFiller());
// Skip one word filler objects that appear on the
// stack when we perform in place array shift.
return (obj->map() == filler_map) ? nullptr : obj;
if (obj->map() != filler_map) {
*out = obj;
return true;
}
return false;
}
});
}
......
......@@ -24,10 +24,8 @@ class ItemParallelJob;
class MigrationObserver;
class RecordMigratedSlotVisitor;
class YoungGenerationMarkingVisitor;
template <int SEGMENT_SIZE>
template <typename EntryType, int SEGMENT_SIZE>
class Worklist;
template <int SEGMENT_SIZE>
class WorklistView;
class ObjectMarking : public AllStatic {
public:
......@@ -350,7 +348,7 @@ class MinorMarkCompactCollector final : public MarkCompactCollectorBase {
void CleanupSweepToIteratePages();
private:
using MarkingWorklist = Worklist<64 /* segment size */>;
using MarkingWorklist = Worklist<HeapObject*, 64 /* segment size */>;
class RootMarkingVisitorSeedOnly;
class RootMarkingVisitor;
......@@ -396,7 +394,7 @@ class MarkCompactCollector final : public MarkCompactCollectorBase {
// Wrapper for the shared and bailout worklists.
class MarkingWorklist {
public:
using ConcurrentMarkingWorklist = Worklist<64>;
using ConcurrentMarkingWorklist = Worklist<HeapObject*, 64>;
static const int kMainThread = 0;
// The heap parameter is not used but needed to match the sequential case.
......
......@@ -87,9 +87,7 @@ class SequentialMarkingDeque {
int i = bottom_;
int new_top = bottom_;
while (i != top_) {
HeapObject* object = callback(array_[i]);
if (object) {
array_[new_top] = object;
if (callback(array_[i], &array_[new_top])) {
new_top = (new_top + 1) & mask_;
}
i = (i + 1) & mask_;
......
......@@ -16,8 +16,6 @@
namespace v8 {
namespace internal {
class HeapObject;
// A concurrent worklist based on segments. Each tasks gets private
// push and pop segments. Empty pop segments are swapped with their
// corresponding push segments. Full push segments are published to a global
......@@ -25,19 +23,19 @@ class HeapObject;
//
// Work stealing is best effort, i.e., there is no way to inform other tasks
// of the need of items.
template <int SEGMENT_SIZE>
template <typename EntryType, int SEGMENT_SIZE>
class Worklist {
public:
class View {
public:
View(Worklist<SEGMENT_SIZE>* worklist, int task_id)
View(Worklist<EntryType, SEGMENT_SIZE>* worklist, int task_id)
: worklist_(worklist), task_id_(task_id) {}
// Pushes an object onto the worklist.
bool Push(HeapObject* object) { return worklist_->Push(task_id_, object); }
// Pushes an entry onto the worklist.
bool Push(EntryType entry) { return worklist_->Push(task_id_, entry); }
// Pops an object from the worklist.
bool Pop(HeapObject** object) { return worklist_->Pop(task_id_, object); }
// Pops an entry from the worklist.
bool Pop(EntryType* entry) { return worklist_->Pop(task_id_, entry); }
// Returns true if the local portion of the worklist is empty.
bool IsLocalEmpty() { return worklist_->IsLocalEmpty(task_id_); }
......@@ -47,7 +45,7 @@ class Worklist {
bool IsGlobalEmpty() { return worklist_->IsGlobalEmpty(); }
private:
Worklist<SEGMENT_SIZE>* worklist_;
Worklist<EntryType, SEGMENT_SIZE>* worklist_;
int task_id_;
};
......@@ -71,22 +69,22 @@ class Worklist {
}
}
bool Push(int task_id, HeapObject* object) {
bool Push(int task_id, EntryType entry) {
DCHECK_LT(task_id, kMaxNumTasks);
DCHECK_NOT_NULL(private_push_segment_[task_id]);
if (!private_push_segment_[task_id]->Push(object)) {
if (!private_push_segment_[task_id]->Push(entry)) {
PublishPushSegmentToGlobal(task_id);
bool success = private_push_segment_[task_id]->Push(object);
bool success = private_push_segment_[task_id]->Push(entry);
USE(success);
DCHECK(success);
}
return true;
}
bool Pop(int task_id, HeapObject** object) {
bool Pop(int task_id, EntryType* entry) {
DCHECK_LT(task_id, kMaxNumTasks);
DCHECK_NOT_NULL(private_pop_segment_[task_id]);
if (!private_pop_segment_[task_id]->Pop(object)) {
if (!private_pop_segment_[task_id]->Pop(entry)) {
if (!private_push_segment_[task_id]->IsEmpty()) {
Segment* tmp = private_pop_segment_[task_id];
private_pop_segment_[task_id] = private_push_segment_[task_id];
......@@ -94,7 +92,7 @@ class Worklist {
} else if (!StealPopSegmentFromGlobal(task_id)) {
return false;
}
bool success = private_pop_segment_[task_id]->Pop(object);
bool success = private_pop_segment_[task_id]->Pop(entry);
USE(success);
DCHECK(success);
}
......@@ -137,9 +135,11 @@ class Worklist {
}
// Calls the specified callback on each element of the deques and replaces
// the element with the result of the callback. If the callback returns
// nullptr then the element is removed from the worklist.
// The callback must accept HeapObject* and return HeapObject*.
// the element with the result of the callback.
// The signature of the callback is
// bool Callback(EntryType old, EntryType* new).
// If the callback returns |false| then the element is removed from the
// worklist. Otherwise the |new| entry is updated.
// This function assumes that other tasks are not running.
template <typename Callback>
void Update(Callback callback) {
......@@ -165,17 +165,16 @@ class Worklist {
}
private:
using TestWorklist = Worklist<64>;
FRIEND_TEST(TestWorklist, SegmentCreate);
FRIEND_TEST(TestWorklist, SegmentPush);
FRIEND_TEST(TestWorklist, SegmentPushPop);
FRIEND_TEST(TestWorklist, SegmentIsEmpty);
FRIEND_TEST(TestWorklist, SegmentIsFull);
FRIEND_TEST(TestWorklist, SegmentClear);
FRIEND_TEST(TestWorklist, SegmentFullPushFails);
FRIEND_TEST(TestWorklist, SegmentEmptyPopFails);
FRIEND_TEST(TestWorklist, SegmentUpdateNull);
FRIEND_TEST(TestWorklist, SegmentUpdate);
FRIEND_TEST(WorkListTest, SegmentCreate);
FRIEND_TEST(WorkListTest, SegmentPush);
FRIEND_TEST(WorkListTest, SegmentPushPop);
FRIEND_TEST(WorkListTest, SegmentIsEmpty);
FRIEND_TEST(WorkListTest, SegmentIsFull);
FRIEND_TEST(WorkListTest, SegmentClear);
FRIEND_TEST(WorkListTest, SegmentFullPushFails);
FRIEND_TEST(WorkListTest, SegmentEmptyPopFails);
FRIEND_TEST(WorkListTest, SegmentUpdateFalse);
FRIEND_TEST(WorkListTest, SegmentUpdate);
class Segment {
public:
......@@ -183,15 +182,15 @@ class Worklist {
Segment() : index_(0) {}
bool Push(HeapObject* object) {
bool Push(EntryType entry) {
if (IsFull()) return false;
objects_[index_++] = object;
entries_[index_++] = entry;
return true;
}
bool Pop(HeapObject** object) {
bool Pop(EntryType* entry) {
if (IsEmpty()) return false;
*object = objects_[--index_];
*entry = entries_[--index_];
return true;
}
......@@ -204,9 +203,8 @@ class Worklist {
void Update(Callback callback) {
size_t new_index = 0;
for (size_t i = 0; i < index_; i++) {
HeapObject* object = callback(objects_[i]);
if (object) {
objects_[new_index++] = object;
if (callback(entries_[i], &entries_[new_index])) {
new_index++;
}
}
index_ = new_index;
......@@ -214,7 +212,7 @@ class Worklist {
private:
size_t index_;
HeapObject* objects_[kCapacity];
EntryType entries_[kCapacity];
};
// Do not inline the following functions as this would mean that vector fast
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment