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