marking.h 9.72 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
#ifndef V8_HEAP_MARKING_H_
#define V8_HEAP_MARKING_H_
7

8
#include "src/base/atomic-utils.h"
9 10 11 12 13 14 15 16
#include "src/utils.h"

namespace v8 {
namespace internal {

class MarkBit {
 public:
  typedef uint32_t CellType;
17
  STATIC_ASSERT(sizeof(CellType) == sizeof(base::Atomic32));
18

19
  inline MarkBit(CellType* cell, CellType mask) : cell_(cell), mask_(mask) {}
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

#ifdef DEBUG
  bool operator==(const MarkBit& other) {
    return cell_ == other.cell_ && mask_ == other.mask_;
  }
#endif

 private:
  inline MarkBit Next() {
    CellType new_mask = mask_ << 1;
    if (new_mask == 0) {
      return MarkBit(cell_ + 1, 1);
    } else {
      return MarkBit(cell_, new_mask);
    }
  }

37 38
  // The function returns true if it succeeded to
  // 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 46
  // The function returns true if it succeeded to
  // transition the bit from 1 to 0.
47
  template <AccessMode mode = AccessMode::NON_ATOMIC>
48
  inline bool Clear();
49

50 51
  CellType* cell_;
  CellType mask_;
52 53

  friend class IncrementalMarking;
54
  friend class ConcurrentMarkingMarkbits;
55 56 57
  friend class Marking;
};

58
template <>
59
inline bool MarkBit::Set<AccessMode::NON_ATOMIC>() {
60
  CellType old_value = *cell_;
61 62
  *cell_ = old_value | mask_;
  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 84
  *cell_ = old_value & ~mask_;
  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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
 public:
  static const uint32_t kBitsPerCell = 32;
  static const uint32_t kBitsPerCellLog2 = 5;
  static const uint32_t kBitIndexMask = kBitsPerCell - 1;
  static const uint32_t kBytesPerCell = kBitsPerCell / kBitsPerByte;
  static const uint32_t kBytesPerCellLog2 = kBitsPerCellLog2 - kBitsPerByteLog2;

  static const size_t kLength = (1 << kPageSizeBits) >> (kPointerSizeLog2);

  static const size_t kSize = (1 << kPageSizeBits) >>
                              (kPointerSizeLog2 + kBitsPerByteLog2);

  static int CellsForLength(int length) {
    return (length + kBitsPerCell - 1) >> kBitsPerCellLog2;
  }

  int CellsCount() { return CellsForLength(kLength); }

112
  V8_INLINE static uint32_t IndexToCell(uint32_t index) {
113 114 115 116 117 118 119
    return index >> kBitsPerCellLog2;
  }

  V8_INLINE static uint32_t IndexInCell(uint32_t index) {
    return index & kBitIndexMask;
  }

120 121 122
  // Retrieves the cell containing the provided markbit index.
  V8_INLINE static uint32_t CellAlignIndex(uint32_t index) {
    return index & ~kBitIndexMask;
123 124
  }

125 126
  V8_INLINE static bool IsCellAligned(uint32_t index) {
    return (index & kBitIndexMask) == 0;
127 128
  }

129
  V8_INLINE MarkBit::CellType* cells() {
130 131 132
    return reinterpret_cast<MarkBit::CellType*>(this);
  }

133
  V8_INLINE static Bitmap* FromAddress(Address addr) {
134 135 136 137 138 139
    return reinterpret_cast<Bitmap*>(addr);
  }

  inline MarkBit MarkBitFromIndex(uint32_t index) {
    MarkBit::CellType mask = 1u << IndexInCell(index);
    MarkBit::CellType* cell = this->cells() + (index >> kBitsPerCellLog2);
140
    return MarkBit(cell, mask);
141 142
  }

143
  void Clear();
144

145 146
  void MarkAllBits();

147 148
  // Clears bits in the given cell. The mask specifies bits to clear: if a
  // bit is set in the mask then the corresponding bit is cleared in the cell.
149
  template <AccessMode mode = AccessMode::NON_ATOMIC>
150 151 152 153
  void ClearBitsInCell(uint32_t cell_index, uint32_t mask);

  // Sets bits in the given cell. The mask specifies bits to set: if a
  // bit is set in the mask then the corresponding bit is set in the cell.
154
  template <AccessMode mode = AccessMode::NON_ATOMIC>
155 156
  void SetBitsInCell(uint32_t cell_index, uint32_t mask);

157 158 159 160
  // Sets all bits in the range [start_index, end_index). The cells at the
  // boundary of the range are updated with atomic compare and swap operation.
  // The inner cells are updated with relaxed write.
  void SetRange(uint32_t start_index, uint32_t end_index);
161

162 163 164 165
  // Clears all bits in the range [start_index, end_index). The cells at the
  // boundary of the range are updated with atomic compare and swap operation.
  // The inner cells are updated with relaxed write.
  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 180
inline void Bitmap::SetBitsInCell<AccessMode::NON_ATOMIC>(uint32_t cell_index,
                                                          uint32_t mask) {
181 182 183 184
  cells()[cell_index] |= mask;
}

template <>
185 186
inline void Bitmap::SetBitsInCell<AccessMode::ATOMIC>(uint32_t cell_index,
                                                      uint32_t mask) {
187
  base::AsAtomic32::SetBits(cells() + cell_index, mask, mask);
188 189 190
}

template <>
191 192
inline void Bitmap::ClearBitsInCell<AccessMode::NON_ATOMIC>(uint32_t cell_index,
                                                            uint32_t mask) {
193 194 195 196
  cells()[cell_index] &= ~mask;
}

template <>
197 198
inline void Bitmap::ClearBitsInCell<AccessMode::ATOMIC>(uint32_t cell_index,
                                                        uint32_t mask) {
199
  base::AsAtomic32::SetBits(cells() + cell_index, 0u, mask);
200 201
}

202 203
class Marking : public AllStatic {
 public:
204 205 206 207
  // TODO(hpayer): The current mark bit operations use as default NON_ATOMIC
  // mode for access. We should remove the default value or switch it with
  // ATOMIC as soon we add concurrency.

208 209
  // Impossible markbits: 01
  static const char* kImpossibleBitPattern;
210
  template <AccessMode mode = AccessMode::NON_ATOMIC>
211
  INLINE(static bool IsImpossible(MarkBit mark_bit)) {
212
    if (mode == AccessMode::NON_ATOMIC) {
213 214 215 216 217 218 219 220 221 222 223
      return !mark_bit.Get<mode>() && mark_bit.Next().Get<mode>();
    }
    // If we are in concurrent mode we can only tell if an object has the
    // impossible bit pattern if we read the first bit again after reading
    // the first and the second bit. If the first bit is till zero and the
    // second bit is one then the object has the impossible bit pattern.
    bool is_impossible = !mark_bit.Get<mode>() && mark_bit.Next().Get<mode>();
    if (is_impossible) {
      return !mark_bit.Get<mode>();
    }
    return false;
224 225 226 227
  }

  // Black markbits: 11
  static const char* kBlackBitPattern;
228
  template <AccessMode mode = AccessMode::NON_ATOMIC>
229
  INLINE(static bool IsBlack(MarkBit mark_bit)) {
230
    return mark_bit.Get<mode>() && mark_bit.Next().Get<mode>();
231 232 233 234
  }

  // White markbits: 00 - this is required by the mark bit clearer.
  static const char* kWhiteBitPattern;
235
  template <AccessMode mode = AccessMode::NON_ATOMIC>
236
  INLINE(static bool IsWhite(MarkBit mark_bit)) {
237
    DCHECK(!IsImpossible<mode>(mark_bit));
238
    return !mark_bit.Get<mode>();
239 240 241 242
  }

  // Grey markbits: 10
  static const char* kGreyBitPattern;
243
  template <AccessMode mode = AccessMode::NON_ATOMIC>
244
  INLINE(static bool IsGrey(MarkBit mark_bit)) {
245
    return mark_bit.Get<mode>() && !mark_bit.Next().Get<mode>();
246 247 248 249
  }

  // IsBlackOrGrey assumes that the first bit is set for black or grey
  // objects.
250
  template <AccessMode mode = AccessMode::NON_ATOMIC>
251 252 253
  INLINE(static bool IsBlackOrGrey(MarkBit mark_bit)) {
    return mark_bit.Get<mode>();
  }
254

255
  template <AccessMode mode = AccessMode::NON_ATOMIC>
256
  INLINE(static void MarkWhite(MarkBit markbit)) {
257
    STATIC_ASSERT(mode == AccessMode::NON_ATOMIC);
258 259
    markbit.Clear<mode>();
    markbit.Next().Clear<mode>();
260 261
  }

262 263 264
  // Warning: this method is not safe in general in concurrent scenarios.
  // If you know that nobody else will change the bits on the given location
  // then you may use it.
265
  template <AccessMode mode = AccessMode::NON_ATOMIC>
266
  INLINE(static void MarkBlack(MarkBit markbit)) {
267 268
    markbit.Set<mode>();
    markbit.Next().Set<mode>();
269 270
  }

271
  template <AccessMode mode = AccessMode::NON_ATOMIC>
272 273
  INLINE(static bool WhiteToGrey(MarkBit markbit)) {
    return markbit.Set<mode>();
274 275
  }

276
  template <AccessMode mode = AccessMode::NON_ATOMIC>
277 278
  INLINE(static bool WhiteToBlack(MarkBit markbit)) {
    return markbit.Set<mode>() && markbit.Next().Set<mode>();
279 280
  }

281
  template <AccessMode mode = AccessMode::NON_ATOMIC>
282
  INLINE(static bool GreyToBlack(MarkBit markbit)) {
283
    return markbit.Get<mode>() && markbit.Next().Set<mode>();
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
  }

  enum ObjectColor {
    BLACK_OBJECT,
    WHITE_OBJECT,
    GREY_OBJECT,
    IMPOSSIBLE_COLOR
  };

  static const char* ColorName(ObjectColor color) {
    switch (color) {
      case BLACK_OBJECT:
        return "black";
      case WHITE_OBJECT:
        return "white";
      case GREY_OBJECT:
        return "grey";
      case IMPOSSIBLE_COLOR:
        return "impossible";
    }
    return "error";
  }

  static ObjectColor Color(MarkBit mark_bit) {
    if (IsBlack(mark_bit)) return BLACK_OBJECT;
    if (IsWhite(mark_bit)) return WHITE_OBJECT;
    if (IsGrey(mark_bit)) return GREY_OBJECT;
    UNREACHABLE();
  }

 private:
  DISALLOW_IMPLICIT_CONSTRUCTORS(Marking);
};

}  // namespace internal
}  // namespace v8

321
#endif  // V8_HEAP_MARKING_H_