• 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.h"
10 
11 namespace v8 {
12 namespace internal {
13 
14 class MarkBit {
15  public:
16   typedef uint32_t CellType;
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   *cell_ = old_value | mask_;
62   return (old_value & mask_) == 0;
63 }
64 
65 template <>
66 inline bool MarkBit::Set<AccessMode::ATOMIC>() {
67   return base::AsAtomic32::SetBits(cell_, mask_, mask_);
68 }
69 
70 template <>
71 inline bool MarkBit::Get<AccessMode::NON_ATOMIC>() {
72   return (*cell_ & mask_) != 0;
73 }
74 
75 template <>
76 inline bool MarkBit::Get<AccessMode::ATOMIC>() {
77   return (base::AsAtomic32::Acquire_Load(cell_) & mask_) != 0;
78 }
79 
80 template <>
81 inline bool MarkBit::Clear<AccessMode::NON_ATOMIC>() {
82   CellType old_value = *cell_;
83   *cell_ = old_value & ~mask_;
84   return (old_value & mask_) == mask_;
85 }
86 
87 template <>
88 inline bool MarkBit::Clear<AccessMode::ATOMIC>() {
89   return base::AsAtomic32::SetBits(cell_, 0u, mask_);
90 }
91 
92 // Bitmap is a sequence of cells each containing fixed number of bits.
93 class V8_EXPORT_PRIVATE Bitmap {
94  public:
95   static const uint32_t kBitsPerCell = 32;
96   static const uint32_t kBitsPerCellLog2 = 5;
97   static const uint32_t kBitIndexMask = kBitsPerCell - 1;
98   static const uint32_t kBytesPerCell = kBitsPerCell / kBitsPerByte;
99   static const uint32_t kBytesPerCellLog2 = kBitsPerCellLog2 - kBitsPerByteLog2;
100 
101   static const size_t kLength = (1 << kPageSizeBits) >> (kPointerSizeLog2);
102 
103   static const size_t kSize = (1 << kPageSizeBits) >>
104                               (kPointerSizeLog2 + kBitsPerByteLog2);
105 
CellsForLength(int length)106   static int CellsForLength(int length) {
107     return (length + kBitsPerCell - 1) >> kBitsPerCellLog2;
108   }
109 
CellsCount()110   int CellsCount() { return CellsForLength(kLength); }
111 
IndexToCell(uint32_t index)112   V8_INLINE static uint32_t IndexToCell(uint32_t index) {
113     return index >> kBitsPerCellLog2;
114   }
115 
IndexInCell(uint32_t index)116   V8_INLINE static uint32_t IndexInCell(uint32_t index) {
117     return index & kBitIndexMask;
118   }
119 
120   // Retrieves the cell containing the provided markbit index.
CellAlignIndex(uint32_t index)121   V8_INLINE static uint32_t CellAlignIndex(uint32_t index) {
122     return index & ~kBitIndexMask;
123   }
124 
IsCellAligned(uint32_t index)125   V8_INLINE static bool IsCellAligned(uint32_t index) {
126     return (index & kBitIndexMask) == 0;
127   }
128 
cells()129   V8_INLINE MarkBit::CellType* cells() {
130     return reinterpret_cast<MarkBit::CellType*>(this);
131   }
132 
FromAddress(Address addr)133   V8_INLINE static Bitmap* FromAddress(Address addr) {
134     return reinterpret_cast<Bitmap*>(addr);
135   }
136 
MarkBitFromIndex(uint32_t index)137   inline MarkBit MarkBitFromIndex(uint32_t index) {
138     MarkBit::CellType mask = 1u << IndexInCell(index);
139     MarkBit::CellType* cell = this->cells() + (index >> kBitsPerCellLog2);
140     return MarkBit(cell, mask);
141   }
142 
143   void Clear();
144 
145   void MarkAllBits();
146 
147   // Clears bits in the given cell. The mask specifies bits to clear: if a
148   // bit is set in the mask then the corresponding bit is cleared in the cell.
149   template <AccessMode mode = AccessMode::NON_ATOMIC>
150   void ClearBitsInCell(uint32_t cell_index, uint32_t mask);
151 
152   // Sets bits in the given cell. The mask specifies bits to set: if a
153   // bit is set in the mask then the corresponding bit is set in the cell.
154   template <AccessMode mode = AccessMode::NON_ATOMIC>
155   void SetBitsInCell(uint32_t cell_index, uint32_t mask);
156 
157   // Sets all bits in the range [start_index, end_index). The cells at the
158   // boundary of the range are updated with atomic compare and swap operation.
159   // The inner cells are updated with relaxed write.
160   void SetRange(uint32_t start_index, uint32_t end_index);
161 
162   // Clears all bits in the range [start_index, end_index). The cells at the
163   // boundary of the range are updated with atomic compare and swap operation.
164   // The inner cells are updated with relaxed write.
165   void ClearRange(uint32_t start_index, uint32_t end_index);
166 
167   // Returns true if all bits in the range [start_index, end_index) are set.
168   bool AllBitsSetInRange(uint32_t start_index, uint32_t end_index);
169 
170   // Returns true if all bits in the range [start_index, end_index) are cleared.
171   bool AllBitsClearInRange(uint32_t start_index, uint32_t end_index);
172 
173   void Print();
174 
175   bool IsClean();
176 };
177 
178 template <>
179 inline void Bitmap::SetBitsInCell<AccessMode::NON_ATOMIC>(uint32_t cell_index,
180                                                           uint32_t mask) {
181   cells()[cell_index] |= mask;
182 }
183 
184 template <>
185 inline void Bitmap::SetBitsInCell<AccessMode::ATOMIC>(uint32_t cell_index,
186                                                       uint32_t mask) {
187   base::AsAtomic32::SetBits(cells() + cell_index, mask, mask);
188 }
189 
190 template <>
191 inline void Bitmap::ClearBitsInCell<AccessMode::NON_ATOMIC>(uint32_t cell_index,
192                                                             uint32_t mask) {
193   cells()[cell_index] &= ~mask;
194 }
195 
196 template <>
197 inline void Bitmap::ClearBitsInCell<AccessMode::ATOMIC>(uint32_t cell_index,
198                                                         uint32_t mask) {
199   base::AsAtomic32::SetBits(cells() + cell_index, 0u, mask);
200 }
201 
202 class Marking : public AllStatic {
203  public:
204   // TODO(hpayer): The current mark bit operations use as default NON_ATOMIC
205   // mode for access. We should remove the default value or switch it with
206   // ATOMIC as soon we add concurrency.
207 
208   // Impossible markbits: 01
209   static const char* kImpossibleBitPattern;
210   template <AccessMode mode = AccessMode::NON_ATOMIC>
IsImpossible(MarkBit mark_bit)211   V8_INLINE static bool IsImpossible(MarkBit mark_bit) {
212     if (mode == AccessMode::NON_ATOMIC) {
213       return !mark_bit.Get<mode>() && mark_bit.Next().Get<mode>();
214     }
215     // If we are in concurrent mode we can only tell if an object has the
216     // impossible bit pattern if we read the first bit again after reading
217     // the first and the second bit. If the first bit is till zero and the
218     // second bit is one then the object has the impossible bit pattern.
219     bool is_impossible = !mark_bit.Get<mode>() && mark_bit.Next().Get<mode>();
220     if (is_impossible) {
221       return !mark_bit.Get<mode>();
222     }
223     return false;
224   }
225 
226   // Black markbits: 11
227   static const char* kBlackBitPattern;
228   template <AccessMode mode = AccessMode::NON_ATOMIC>
IsBlack(MarkBit mark_bit)229   V8_INLINE static bool IsBlack(MarkBit mark_bit) {
230     return mark_bit.Get<mode>() && mark_bit.Next().Get<mode>();
231   }
232 
233   // White markbits: 00 - this is required by the mark bit clearer.
234   static const char* kWhiteBitPattern;
235   template <AccessMode mode = AccessMode::NON_ATOMIC>
IsWhite(MarkBit mark_bit)236   V8_INLINE static bool IsWhite(MarkBit mark_bit) {
237     DCHECK(!IsImpossible<mode>(mark_bit));
238     return !mark_bit.Get<mode>();
239   }
240 
241   // Grey markbits: 10
242   static const char* kGreyBitPattern;
243   template <AccessMode mode = AccessMode::NON_ATOMIC>
IsGrey(MarkBit mark_bit)244   V8_INLINE static bool IsGrey(MarkBit mark_bit) {
245     return mark_bit.Get<mode>() && !mark_bit.Next().Get<mode>();
246   }
247 
248   // IsBlackOrGrey assumes that the first bit is set for black or grey
249   // objects.
250   template <AccessMode mode = AccessMode::NON_ATOMIC>
IsBlackOrGrey(MarkBit mark_bit)251   V8_INLINE static bool IsBlackOrGrey(MarkBit mark_bit) {
252     return mark_bit.Get<mode>();
253   }
254 
255   template <AccessMode mode = AccessMode::NON_ATOMIC>
MarkWhite(MarkBit markbit)256   V8_INLINE static void MarkWhite(MarkBit markbit) {
257     STATIC_ASSERT(mode == AccessMode::NON_ATOMIC);
258     markbit.Clear<mode>();
259     markbit.Next().Clear<mode>();
260   }
261 
262   // Warning: this method is not safe in general in concurrent scenarios.
263   // If you know that nobody else will change the bits on the given location
264   // then you may use it.
265   template <AccessMode mode = AccessMode::NON_ATOMIC>
MarkBlack(MarkBit markbit)266   V8_INLINE static void MarkBlack(MarkBit markbit) {
267     markbit.Set<mode>();
268     markbit.Next().Set<mode>();
269   }
270 
271   template <AccessMode mode = AccessMode::NON_ATOMIC>
WhiteToGrey(MarkBit markbit)272   V8_INLINE static bool WhiteToGrey(MarkBit markbit) {
273     return markbit.Set<mode>();
274   }
275 
276   template <AccessMode mode = AccessMode::NON_ATOMIC>
WhiteToBlack(MarkBit markbit)277   V8_INLINE static bool WhiteToBlack(MarkBit markbit) {
278     return markbit.Set<mode>() && markbit.Next().Set<mode>();
279   }
280 
281   template <AccessMode mode = AccessMode::NON_ATOMIC>
GreyToBlack(MarkBit markbit)282   V8_INLINE static bool GreyToBlack(MarkBit markbit) {
283     return markbit.Get<mode>() && markbit.Next().Set<mode>();
284   }
285 
286   enum ObjectColor {
287     BLACK_OBJECT,
288     WHITE_OBJECT,
289     GREY_OBJECT,
290     IMPOSSIBLE_COLOR
291   };
292 
ColorName(ObjectColor color)293   static const char* ColorName(ObjectColor color) {
294     switch (color) {
295       case BLACK_OBJECT:
296         return "black";
297       case WHITE_OBJECT:
298         return "white";
299       case GREY_OBJECT:
300         return "grey";
301       case IMPOSSIBLE_COLOR:
302         return "impossible";
303     }
304     return "error";
305   }
306 
Color(MarkBit mark_bit)307   static ObjectColor Color(MarkBit mark_bit) {
308     if (IsBlack(mark_bit)) return BLACK_OBJECT;
309     if (IsWhite(mark_bit)) return WHITE_OBJECT;
310     if (IsGrey(mark_bit)) return GREY_OBJECT;
311     UNREACHABLE();
312   }
313 
314  private:
315   DISALLOW_IMPLICIT_CONSTRUCTORS(Marking);
316 };
317 
318 }  // namespace internal
319 }  // namespace v8
320 
321 #endif  // V8_HEAP_MARKING_H_
322