• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 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_MARKING_H_
6 #define V8_HEAP_MARKING_H_
7 
8 #include "src/base/atomic-utils.h"
9 #include "src/utils/utils.h"
10 
11 namespace v8 {
12 namespace internal {
13 
14 class MarkBit {
15  public:
16   using CellType = uint32_t;
17   STATIC_ASSERT(sizeof(CellType) == sizeof(base::Atomic32));
18 
MarkBit(CellType * cell,CellType mask)19   inline MarkBit(CellType* cell, CellType mask) : cell_(cell), mask_(mask) {}
20 
21 #ifdef DEBUG
22   bool operator==(const MarkBit& other) {
23     return cell_ == other.cell_ && mask_ == other.mask_;
24   }
25 #endif
26 
27  private:
Next()28   inline MarkBit Next() {
29     CellType new_mask = mask_ << 1;
30     if (new_mask == 0) {
31       return MarkBit(cell_ + 1, 1);
32     } else {
33       return MarkBit(cell_, new_mask);
34     }
35   }
36 
37   // The function returns true if it succeeded to
38   // transition the bit from 0 to 1.
39   template <AccessMode mode = AccessMode::NON_ATOMIC>
40   inline bool Set();
41 
42   template <AccessMode mode = AccessMode::NON_ATOMIC>
43   inline bool Get();
44 
45   // The function returns true if it succeeded to
46   // transition the bit from 1 to 0.
47   template <AccessMode mode = AccessMode::NON_ATOMIC>
48   inline bool Clear();
49 
50   CellType* cell_;
51   CellType mask_;
52 
53   friend class IncrementalMarking;
54   friend class ConcurrentMarkingMarkbits;
55   friend class Marking;
56 };
57 
58 template <>
59 inline bool MarkBit::Set<AccessMode::NON_ATOMIC>() {
60   CellType old_value = *cell_;
61   if ((old_value & mask_) == mask_) return false;
62   *cell_ = old_value | mask_;
63   return true;
64 }
65 
66 template <>
67 inline bool MarkBit::Set<AccessMode::ATOMIC>() {
68   return base::AsAtomic32::SetBits(cell_, mask_, mask_);
69 }
70 
71 template <>
72 inline bool MarkBit::Get<AccessMode::NON_ATOMIC>() {
73   return (*cell_ & mask_) != 0;
74 }
75 
76 template <>
77 inline bool MarkBit::Get<AccessMode::ATOMIC>() {
78   return (base::AsAtomic32::Acquire_Load(cell_) & mask_) != 0;
79 }
80 
81 template <>
82 inline bool MarkBit::Clear<AccessMode::NON_ATOMIC>() {
83   CellType old_value = *cell_;
84   *cell_ = old_value & ~mask_;
85   return (old_value & mask_) == mask_;
86 }
87 
88 template <>
89 inline bool MarkBit::Clear<AccessMode::ATOMIC>() {
90   return base::AsAtomic32::SetBits(cell_, 0u, mask_);
91 }
92 
93 // Bitmap is a sequence of cells each containing fixed number of bits.
94 class V8_EXPORT_PRIVATE Bitmap {
95  public:
96   static const uint32_t kBitsPerCell = 32;
97   static const uint32_t kBitsPerCellLog2 = 5;
98   static const uint32_t kBitIndexMask = kBitsPerCell - 1;
99   static const uint32_t kBytesPerCell = kBitsPerCell / kBitsPerByte;
100   static const uint32_t kBytesPerCellLog2 = kBitsPerCellLog2 - kBitsPerByteLog2;
101 
102   // The length is the number of bits in this bitmap. (+1) accounts for
103   // the case where the markbits are queried for a one-word filler at the
104   // end of the page.
105   static const size_t kLength = ((1 << kPageSizeBits) >> kTaggedSizeLog2) + 1;
106   // The size of the bitmap in bytes is CellsCount() * kBytesPerCell.
107   static const size_t kSize;
108 
CellsForLength(int length)109   static constexpr size_t CellsForLength(int length) {
110     return (length + kBitsPerCell - 1) >> kBitsPerCellLog2;
111   }
112 
CellsCount()113   static constexpr size_t CellsCount() { return CellsForLength(kLength); }
114 
IndexToCell(uint32_t index)115   V8_INLINE static uint32_t IndexToCell(uint32_t index) {
116     return index >> kBitsPerCellLog2;
117   }
118 
IndexInCell(uint32_t index)119   V8_INLINE static uint32_t IndexInCell(uint32_t index) {
120     return index & kBitIndexMask;
121   }
122 
123   // Retrieves the cell containing the provided markbit index.
CellAlignIndex(uint32_t index)124   V8_INLINE static uint32_t CellAlignIndex(uint32_t index) {
125     return index & ~kBitIndexMask;
126   }
127 
cells()128   V8_INLINE MarkBit::CellType* cells() {
129     return reinterpret_cast<MarkBit::CellType*>(this);
130   }
131 
FromAddress(Address addr)132   V8_INLINE static Bitmap* FromAddress(Address addr) {
133     return reinterpret_cast<Bitmap*>(addr);
134   }
135 
MarkBitFromIndex(uint32_t index)136   inline MarkBit MarkBitFromIndex(uint32_t index) {
137     MarkBit::CellType mask = 1u << IndexInCell(index);
138     MarkBit::CellType* cell = this->cells() + (index >> kBitsPerCellLog2);
139     return MarkBit(cell, mask);
140   }
141 };
142 
143 template <AccessMode mode>
144 class ConcurrentBitmap : public Bitmap {
145  public:
146   void Clear();
147 
148   void MarkAllBits();
149 
150   // Clears bits in the given cell. The mask specifies bits to clear: if a
151   // bit is set in the mask then the corresponding bit is cleared in the cell.
152   void ClearBitsInCell(uint32_t cell_index, uint32_t mask);
153 
154   // Sets bits in the given cell. The mask specifies bits to set: if a
155   // bit is set in the mask then the corresponding bit is set in the cell.
156   void SetBitsInCell(uint32_t cell_index, uint32_t mask);
157 
158   // Sets all bits in the range [start_index, end_index). If the access is
159   // atomic, the cells at the boundary of the range are updated with atomic
160   // compare and swap operation. The inner cells are updated with relaxed write.
161   void SetRange(uint32_t start_index, uint32_t end_index);
162 
163   // Clears all bits in the range [start_index, end_index). If the access is
164   // atomic, the cells at the boundary of the range are updated with atomic
165   // compare and swap operation. The inner cells are updated with relaxed write.
166   void ClearRange(uint32_t start_index, uint32_t end_index);
167 
168   // The following methods are *not* safe to use in a concurrent context so they
169   // are not implemented for `AccessMode::ATOMIC`.
170 
171   // Returns true if all bits in the range [start_index, end_index) are set.
172   bool AllBitsSetInRange(uint32_t start_index, uint32_t end_index);
173 
174   // Returns true if all bits in the range [start_index, end_index) are cleared.
175   bool AllBitsClearInRange(uint32_t start_index, uint32_t end_index);
176 
177   void Print();
178 
179   bool IsClean();
180 
181  private:
182   // Clear all bits in the cell range [start_cell_index, end_cell_index). If the
183   // access is atomic then *still* use a relaxed memory ordering.
184   void ClearCellRangeRelaxed(uint32_t start_cell_index,
185                              uint32_t end_cell_index);
186 
187   // Set all bits in the cell range [start_cell_index, end_cell_index). If the
188   // access is atomic then *still* use a relaxed memory ordering.
189   void SetCellRangeRelaxed(uint32_t start_cell_index, uint32_t end_cell_index);
190 };
191 
192 template <>
ClearCellRangeRelaxed(uint32_t start_cell_index,uint32_t end_cell_index)193 inline void ConcurrentBitmap<AccessMode::ATOMIC>::ClearCellRangeRelaxed(
194     uint32_t start_cell_index, uint32_t end_cell_index) {
195   base::Atomic32* cell_base = reinterpret_cast<base::Atomic32*>(cells());
196   for (uint32_t i = start_cell_index; i < end_cell_index; i++) {
197     base::Relaxed_Store(cell_base + i, 0);
198   }
199 }
200 
201 template <>
ClearCellRangeRelaxed(uint32_t start_cell_index,uint32_t end_cell_index)202 inline void ConcurrentBitmap<AccessMode::NON_ATOMIC>::ClearCellRangeRelaxed(
203     uint32_t start_cell_index, uint32_t end_cell_index) {
204   for (uint32_t i = start_cell_index; i < end_cell_index; i++) {
205     cells()[i] = 0;
206   }
207 }
208 
209 template <>
SetCellRangeRelaxed(uint32_t start_cell_index,uint32_t end_cell_index)210 inline void ConcurrentBitmap<AccessMode::ATOMIC>::SetCellRangeRelaxed(
211     uint32_t start_cell_index, uint32_t end_cell_index) {
212   base::Atomic32* cell_base = reinterpret_cast<base::Atomic32*>(cells());
213   for (uint32_t i = start_cell_index; i < end_cell_index; i++) {
214     base::Relaxed_Store(cell_base + i, 0xffffffff);
215   }
216 }
217 
218 template <>
SetCellRangeRelaxed(uint32_t start_cell_index,uint32_t end_cell_index)219 inline void ConcurrentBitmap<AccessMode::NON_ATOMIC>::SetCellRangeRelaxed(
220     uint32_t start_cell_index, uint32_t end_cell_index) {
221   for (uint32_t i = start_cell_index; i < end_cell_index; i++) {
222     cells()[i] = 0xffffffff;
223   }
224 }
225 
226 template <AccessMode mode>
Clear()227 inline void ConcurrentBitmap<mode>::Clear() {
228   ClearCellRangeRelaxed(0, CellsCount());
229   if (mode == AccessMode::ATOMIC) {
230     // This fence prevents re-ordering of publishing stores with the mark-bit
231     // setting stores.
232     base::SeqCst_MemoryFence();
233   }
234 }
235 
236 template <AccessMode mode>
MarkAllBits()237 inline void ConcurrentBitmap<mode>::MarkAllBits() {
238   SetCellRangeRelaxed(0, CellsCount());
239   if (mode == AccessMode::ATOMIC) {
240     // This fence prevents re-ordering of publishing stores with the mark-bit
241     // setting stores.
242     base::SeqCst_MemoryFence();
243   }
244 }
245 
246 template <>
SetBitsInCell(uint32_t cell_index,uint32_t mask)247 inline void ConcurrentBitmap<AccessMode::NON_ATOMIC>::SetBitsInCell(
248     uint32_t cell_index, uint32_t mask) {
249   cells()[cell_index] |= mask;
250 }
251 
252 template <>
SetBitsInCell(uint32_t cell_index,uint32_t mask)253 inline void ConcurrentBitmap<AccessMode::ATOMIC>::SetBitsInCell(
254     uint32_t cell_index, uint32_t mask) {
255   base::AsAtomic32::SetBits(cells() + cell_index, mask, mask);
256 }
257 
258 template <>
ClearBitsInCell(uint32_t cell_index,uint32_t mask)259 inline void ConcurrentBitmap<AccessMode::NON_ATOMIC>::ClearBitsInCell(
260     uint32_t cell_index, uint32_t mask) {
261   cells()[cell_index] &= ~mask;
262 }
263 
264 template <>
ClearBitsInCell(uint32_t cell_index,uint32_t mask)265 inline void ConcurrentBitmap<AccessMode::ATOMIC>::ClearBitsInCell(
266     uint32_t cell_index, uint32_t mask) {
267   base::AsAtomic32::SetBits(cells() + cell_index, 0u, mask);
268 }
269 
270 template <AccessMode mode>
SetRange(uint32_t start_index,uint32_t end_index)271 void ConcurrentBitmap<mode>::SetRange(uint32_t start_index,
272                                       uint32_t end_index) {
273   if (start_index >= end_index) return;
274   end_index--;
275 
276   unsigned int start_cell_index = start_index >> Bitmap::kBitsPerCellLog2;
277   MarkBit::CellType start_index_mask = 1u << Bitmap::IndexInCell(start_index);
278 
279   unsigned int end_cell_index = end_index >> Bitmap::kBitsPerCellLog2;
280   MarkBit::CellType end_index_mask = 1u << Bitmap::IndexInCell(end_index);
281 
282   if (start_cell_index != end_cell_index) {
283     // Firstly, fill all bits from the start address to the end of the first
284     // cell with 1s.
285     SetBitsInCell(start_cell_index, ~(start_index_mask - 1));
286     // Then fill all in between cells with 1s.
287     SetCellRangeRelaxed(start_cell_index + 1, end_cell_index);
288     // Finally, fill all bits until the end address in the last cell with 1s.
289     SetBitsInCell(end_cell_index, end_index_mask | (end_index_mask - 1));
290   } else {
291     SetBitsInCell(start_cell_index,
292                   end_index_mask | (end_index_mask - start_index_mask));
293   }
294   if (mode == AccessMode::ATOMIC) {
295     // This fence prevents re-ordering of publishing stores with the mark-bit
296     // setting stores.
297     base::SeqCst_MemoryFence();
298   }
299 }
300 
301 template <AccessMode mode>
ClearRange(uint32_t start_index,uint32_t end_index)302 void ConcurrentBitmap<mode>::ClearRange(uint32_t start_index,
303                                         uint32_t end_index) {
304   if (start_index >= end_index) return;
305   end_index--;
306 
307   unsigned int start_cell_index = start_index >> Bitmap::kBitsPerCellLog2;
308   MarkBit::CellType start_index_mask = 1u << Bitmap::IndexInCell(start_index);
309 
310   unsigned int end_cell_index = end_index >> Bitmap::kBitsPerCellLog2;
311   MarkBit::CellType end_index_mask = 1u << Bitmap::IndexInCell(end_index);
312 
313   if (start_cell_index != end_cell_index) {
314     // Firstly, fill all bits from the start address to the end of the first
315     // cell with 0s.
316     ClearBitsInCell(start_cell_index, ~(start_index_mask - 1));
317     // Then fill all in between cells with 0s.
318     ClearCellRangeRelaxed(start_cell_index + 1, end_cell_index);
319     // Finally, set all bits until the end address in the last cell with 0s.
320     ClearBitsInCell(end_cell_index, end_index_mask | (end_index_mask - 1));
321   } else {
322     ClearBitsInCell(start_cell_index,
323                     end_index_mask | (end_index_mask - start_index_mask));
324   }
325   if (mode == AccessMode::ATOMIC) {
326     // This fence prevents re-ordering of publishing stores with the mark-bit
327     // clearing stores.
328     base::SeqCst_MemoryFence();
329   }
330 }
331 
332 template <>
333 V8_EXPORT_PRIVATE bool
334 ConcurrentBitmap<AccessMode::NON_ATOMIC>::AllBitsSetInRange(
335     uint32_t start_index, uint32_t end_index);
336 
337 template <>
338 V8_EXPORT_PRIVATE bool
339 ConcurrentBitmap<AccessMode::NON_ATOMIC>::AllBitsClearInRange(
340     uint32_t start_index, uint32_t end_index);
341 
342 template <>
343 void ConcurrentBitmap<AccessMode::NON_ATOMIC>::Print();
344 
345 template <>
346 V8_EXPORT_PRIVATE bool ConcurrentBitmap<AccessMode::NON_ATOMIC>::IsClean();
347 
348 class Marking : public AllStatic {
349  public:
350   // TODO(hpayer): The current mark bit operations use as default NON_ATOMIC
351   // mode for access. We should remove the default value or switch it with
352   // ATOMIC as soon we add concurrency.
353 
354   // Impossible markbits: 01
355   static const char* kImpossibleBitPattern;
356   template <AccessMode mode = AccessMode::NON_ATOMIC>
IsImpossible(MarkBit mark_bit)357   V8_INLINE static bool IsImpossible(MarkBit mark_bit) {
358     if (mode == AccessMode::NON_ATOMIC) {
359       return !mark_bit.Get<mode>() && mark_bit.Next().Get<mode>();
360     }
361     // If we are in concurrent mode we can only tell if an object has the
362     // impossible bit pattern if we read the first bit again after reading
363     // the first and the second bit. If the first bit is till zero and the
364     // second bit is one then the object has the impossible bit pattern.
365     bool is_impossible = !mark_bit.Get<mode>() && mark_bit.Next().Get<mode>();
366     if (is_impossible) {
367       return !mark_bit.Get<mode>();
368     }
369     return false;
370   }
371 
372   // Black markbits: 11
373   static const char* kBlackBitPattern;
374   template <AccessMode mode = AccessMode::NON_ATOMIC>
IsBlack(MarkBit mark_bit)375   V8_INLINE static bool IsBlack(MarkBit mark_bit) {
376     return mark_bit.Get<mode>() && mark_bit.Next().Get<mode>();
377   }
378 
379   // White markbits: 00 - this is required by the mark bit clearer.
380   static const char* kWhiteBitPattern;
381   template <AccessMode mode = AccessMode::NON_ATOMIC>
IsWhite(MarkBit mark_bit)382   V8_INLINE static bool IsWhite(MarkBit mark_bit) {
383     DCHECK(!IsImpossible<mode>(mark_bit));
384     return !mark_bit.Get<mode>();
385   }
386 
387   // Grey markbits: 10
388   static const char* kGreyBitPattern;
389   template <AccessMode mode = AccessMode::NON_ATOMIC>
IsGrey(MarkBit mark_bit)390   V8_INLINE static bool IsGrey(MarkBit mark_bit) {
391     return mark_bit.Get<mode>() && !mark_bit.Next().Get<mode>();
392   }
393 
394   // IsBlackOrGrey assumes that the first bit is set for black or grey
395   // objects.
396   template <AccessMode mode = AccessMode::NON_ATOMIC>
IsBlackOrGrey(MarkBit mark_bit)397   V8_INLINE static bool IsBlackOrGrey(MarkBit mark_bit) {
398     return mark_bit.Get<mode>();
399   }
400 
401   template <AccessMode mode = AccessMode::NON_ATOMIC>
MarkWhite(MarkBit markbit)402   V8_INLINE static void MarkWhite(MarkBit markbit) {
403     STATIC_ASSERT(mode == AccessMode::NON_ATOMIC);
404     markbit.Clear<mode>();
405     markbit.Next().Clear<mode>();
406   }
407 
408   // Warning: this method is not safe in general in concurrent scenarios.
409   // If you know that nobody else will change the bits on the given location
410   // then you may use it.
411   template <AccessMode mode = AccessMode::NON_ATOMIC>
MarkBlack(MarkBit markbit)412   V8_INLINE static void MarkBlack(MarkBit markbit) {
413     markbit.Set<mode>();
414     markbit.Next().Set<mode>();
415   }
416 
417   template <AccessMode mode = AccessMode::NON_ATOMIC>
WhiteToGrey(MarkBit markbit)418   V8_INLINE static bool WhiteToGrey(MarkBit markbit) {
419     return markbit.Set<mode>();
420   }
421 
422   template <AccessMode mode = AccessMode::NON_ATOMIC>
WhiteToBlack(MarkBit markbit)423   V8_INLINE static bool WhiteToBlack(MarkBit markbit) {
424     return markbit.Set<mode>() && markbit.Next().Set<mode>();
425   }
426 
427   template <AccessMode mode = AccessMode::NON_ATOMIC>
GreyToBlack(MarkBit markbit)428   V8_INLINE static bool GreyToBlack(MarkBit markbit) {
429     return markbit.Get<mode>() && markbit.Next().Set<mode>();
430   }
431 
432   enum ObjectColor {
433     BLACK_OBJECT,
434     WHITE_OBJECT,
435     GREY_OBJECT,
436     IMPOSSIBLE_COLOR
437   };
438 
ColorName(ObjectColor color)439   static const char* ColorName(ObjectColor color) {
440     switch (color) {
441       case BLACK_OBJECT:
442         return "black";
443       case WHITE_OBJECT:
444         return "white";
445       case GREY_OBJECT:
446         return "grey";
447       case IMPOSSIBLE_COLOR:
448         return "impossible";
449     }
450     return "error";
451   }
452 
Color(MarkBit mark_bit)453   static ObjectColor Color(MarkBit mark_bit) {
454     if (IsBlack(mark_bit)) return BLACK_OBJECT;
455     if (IsWhite(mark_bit)) return WHITE_OBJECT;
456     if (IsGrey(mark_bit)) return GREY_OBJECT;
457     UNREACHABLE();
458   }
459 
460  private:
461   DISALLOW_IMPLICIT_CONSTRUCTORS(Marking);
462 };
463 
464 }  // namespace internal
465 }  // namespace v8
466 
467 #endif  // V8_HEAP_MARKING_H_
468