1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_HEAP_STORE_BUFFER_H_ 6 #define V8_HEAP_STORE_BUFFER_H_ 7 8 #include "src/allocation.h" 9 #include "src/base/logging.h" 10 #include "src/base/platform/platform.h" 11 #include "src/cancelable-task.h" 12 #include "src/globals.h" 13 #include "src/heap/gc-tracer.h" 14 #include "src/heap/remembered-set.h" 15 #include "src/heap/slot-set.h" 16 17 namespace v8 { 18 namespace internal { 19 20 // Intermediate buffer that accumulates old-to-new stores from the generated 21 // code. Moreover, it stores invalid old-to-new slots with two entries. 22 // The first is a tagged address of the start of the invalid range, the second 23 // one is the end address of the invalid range or null if there is just one slot 24 // that needs to be removed from the remembered set. On buffer overflow the 25 // slots are moved to the remembered set. 26 class StoreBuffer { 27 public: 28 enum StoreBufferMode { IN_GC, NOT_IN_GC }; 29 30 static const int kStoreBufferSize = 1 << (11 + kPointerSizeLog2); 31 static const int kStoreBufferMask = kStoreBufferSize - 1; 32 static const int kStoreBuffers = 2; 33 static const intptr_t kDeletionTag = 1; 34 35 V8_EXPORT_PRIVATE static int StoreBufferOverflow(Isolate* isolate); 36 37 static void DeleteDuringGarbageCollection(StoreBuffer* store_buffer, 38 Address start, Address end); 39 static void InsertDuringGarbageCollection(StoreBuffer* store_buffer, 40 Address slot); 41 42 static void DeleteDuringRuntime(StoreBuffer* store_buffer, Address start, 43 Address end); 44 static void InsertDuringRuntime(StoreBuffer* store_buffer, Address slot); 45 46 explicit StoreBuffer(Heap* heap); 47 void SetUp(); 48 void TearDown(); 49 50 // Used to add entries from generated code. top_address()51 inline Address* top_address() { return reinterpret_cast<Address*>(&top_); } 52 53 // Moves entries from a specific store buffer to the remembered set. This 54 // method takes a lock. 55 void MoveEntriesToRememberedSet(int index); 56 57 // This method ensures that all used store buffer entries are transferred to 58 // the remembered set. 59 void MoveAllEntriesToRememberedSet(); 60 IsDeletionAddress(Address address)61 inline bool IsDeletionAddress(Address address) const { 62 return address & kDeletionTag; 63 } 64 MarkDeletionAddress(Address address)65 inline Address MarkDeletionAddress(Address address) { 66 return address | kDeletionTag; 67 } 68 UnmarkDeletionAddress(Address address)69 inline Address UnmarkDeletionAddress(Address address) { 70 return address & ~kDeletionTag; 71 } 72 73 inline void InsertDeletionIntoStoreBuffer(Address start, Address end); 74 inline void InsertIntoStoreBuffer(Address slot); 75 InsertEntry(Address slot)76 void InsertEntry(Address slot) { 77 // Insertions coming from the GC are directly inserted into the remembered 78 // set. Insertions coming from the runtime are added to the store buffer to 79 // allow concurrent processing. 80 insertion_callback(this, slot); 81 } 82 83 // If we only want to delete a single slot, end should be set to null which 84 // will be written into the second field. When processing the store buffer 85 // the more efficient Remove method will be called in this case. 86 void DeleteEntry(Address start, Address end = kNullAddress) { 87 // Deletions coming from the GC are directly deleted from the remembered 88 // set. Deletions coming from the runtime are added to the store buffer 89 // to allow concurrent processing. 90 deletion_callback(this, start, end); 91 } 92 93 void SetMode(StoreBufferMode mode); 94 95 // Used by the concurrent processing thread to transfer entries from the 96 // store buffer to the remembered set. 97 void ConcurrentlyProcessStoreBuffer(); 98 Empty()99 bool Empty() { 100 for (int i = 0; i < kStoreBuffers; i++) { 101 if (lazy_top_[i]) { 102 return false; 103 } 104 } 105 return top_ == start_[current_]; 106 } 107 heap()108 Heap* heap() { return heap_; } 109 110 private: 111 // There are two store buffers. If one store buffer fills up, the main thread 112 // publishes the top pointer of the store buffer that needs processing in its 113 // global lazy_top_ field. After that it start the concurrent processing 114 // thread. The concurrent processing thread uses the pointer in lazy_top_. 115 // It will grab the given mutex and transfer its entries to the remembered 116 // set. If the concurrent thread does not make progress, the main thread will 117 // perform the work. 118 // Important: there is an ordering constrained. The store buffer with the 119 // older entries has to be processed first. 120 class Task : public CancelableTask { 121 public: Task(Isolate * isolate,StoreBuffer * store_buffer)122 Task(Isolate* isolate, StoreBuffer* store_buffer) 123 : CancelableTask(isolate), 124 store_buffer_(store_buffer), 125 tracer_(isolate->heap()->tracer()) {} ~Task()126 virtual ~Task() {} 127 128 private: RunInternal()129 void RunInternal() override { 130 TRACE_BACKGROUND_GC(tracer_, 131 GCTracer::BackgroundScope::BACKGROUND_STORE_BUFFER); 132 store_buffer_->ConcurrentlyProcessStoreBuffer(); 133 } 134 StoreBuffer* store_buffer_; 135 GCTracer* tracer_; 136 DISALLOW_COPY_AND_ASSIGN(Task); 137 }; 138 mode()139 StoreBufferMode mode() const { return mode_; } 140 141 void FlipStoreBuffers(); 142 143 Heap* heap_; 144 145 Address* top_; 146 147 // The start and the limit of the buffer that contains store slots 148 // added from the generated code. We have two chunks of store buffers. 149 // Whenever one fills up, we notify a concurrent processing thread and 150 // use the other empty one in the meantime. 151 Address* start_[kStoreBuffers]; 152 Address* limit_[kStoreBuffers]; 153 154 // At most one lazy_top_ pointer is set at any time. 155 Address* lazy_top_[kStoreBuffers]; 156 base::Mutex mutex_; 157 158 // We only want to have at most one concurrent processing tas running. 159 bool task_running_; 160 161 // Points to the current buffer in use. 162 int current_; 163 164 // During GC, entries are directly added to the remembered set without 165 // going through the store buffer. This is signaled by a special 166 // IN_GC mode. 167 StoreBufferMode mode_; 168 169 VirtualMemory virtual_memory_; 170 171 // Callbacks are more efficient than reading out the gc state for every 172 // store buffer operation. 173 void (*insertion_callback)(StoreBuffer*, Address); 174 void (*deletion_callback)(StoreBuffer*, Address, Address); 175 }; 176 177 } // namespace internal 178 } // namespace v8 179 180 #endif // V8_HEAP_STORE_BUFFER_H_ 181