| OLD | NEW |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #ifndef V8_SLOT_SET_H | 5 #ifndef V8_SLOT_SET_H |
| 6 #define V8_SLOT_SET_H | 6 #define V8_SLOT_SET_H |
| 7 | 7 |
| 8 #include "src/allocation.h" | 8 #include "src/allocation.h" |
| 9 #include "src/base/atomic-utils.h" |
| 9 #include "src/base/bits.h" | 10 #include "src/base/bits.h" |
| 10 #include "src/utils.h" | 11 #include "src/utils.h" |
| 11 | 12 |
| 12 namespace v8 { | 13 namespace v8 { |
| 13 namespace internal { | 14 namespace internal { |
| 14 | 15 |
| 15 enum SlotCallbackResult { KEEP_SLOT, REMOVE_SLOT }; | 16 enum SlotCallbackResult { KEEP_SLOT, REMOVE_SLOT }; |
| 16 | 17 |
| 17 // Data structure for maintaining a set of slots in a standard (non-large) | 18 // Data structure for maintaining a set of slots in a standard (non-large) |
| 18 // page. The base address of the page must be set with SetPageStart before any | 19 // page. The base address of the page must be set with SetPageStart before any |
| 19 // operation. | 20 // operation. |
| 20 // The data structure assumes that the slots are pointer size aligned and | 21 // The data structure assumes that the slots are pointer size aligned and |
| 21 // splits the valid slot offset range into kBuckets buckets. | 22 // splits the valid slot offset range into kBuckets buckets. |
| 22 // Each bucket is a bitmap with a bit corresponding to a single slot offset. | 23 // Each bucket is a bitmap with a bit corresponding to a single slot offset. |
| 23 class SlotSet : public Malloced { | 24 class SlotSet : public Malloced { |
| 24 public: | 25 public: |
| 25 SlotSet() { | 26 SlotSet() { |
| 26 for (int i = 0; i < kBuckets; i++) { | 27 for (int i = 0; i < kBuckets; i++) { |
| 27 bucket[i] = nullptr; | 28 bucket[i].SetValue(nullptr); |
| 28 } | 29 } |
| 29 } | 30 } |
| 30 | 31 |
| 31 ~SlotSet() { | 32 ~SlotSet() { |
| 32 for (int i = 0; i < kBuckets; i++) { | 33 for (int i = 0; i < kBuckets; i++) { |
| 33 ReleaseBucket(i); | 34 ReleaseBucket(i); |
| 34 } | 35 } |
| 35 } | 36 } |
| 36 | 37 |
| 37 void SetPageStart(Address page_start) { page_start_ = page_start; } | 38 void SetPageStart(Address page_start) { page_start_ = page_start; } |
| 38 | 39 |
| 39 // The slot offset specifies a slot at address page_start_ + slot_offset. | 40 // The slot offset specifies a slot at address page_start_ + slot_offset. |
| 41 // This method should only be called on the main thread because concurrent |
| 42 // allocation of the bucket is not thread-safe. |
| 40 void Insert(int slot_offset) { | 43 void Insert(int slot_offset) { |
| 41 int bucket_index, cell_index, bit_index; | 44 int bucket_index, cell_index, bit_index; |
| 42 SlotToIndices(slot_offset, &bucket_index, &cell_index, &bit_index); | 45 SlotToIndices(slot_offset, &bucket_index, &cell_index, &bit_index); |
| 43 if (bucket[bucket_index] == nullptr) { | 46 base::AtomicValue<uint32_t>* current_bucket = bucket[bucket_index].Value(); |
| 44 bucket[bucket_index] = AllocateBucket(); | 47 if (current_bucket == nullptr) { |
| 48 current_bucket = AllocateBucket(); |
| 49 bucket[bucket_index].SetValue(current_bucket); |
| 45 } | 50 } |
| 46 bucket[bucket_index][cell_index] |= 1u << bit_index; | 51 current_bucket[cell_index].SetBit(bit_index); |
| 47 } | 52 } |
| 48 | 53 |
| 49 // The slot offset specifies a slot at address page_start_ + slot_offset. | 54 // The slot offset specifies a slot at address page_start_ + slot_offset. |
| 50 void Remove(int slot_offset) { | 55 void Remove(int slot_offset) { |
| 51 int bucket_index, cell_index, bit_index; | 56 int bucket_index, cell_index, bit_index; |
| 52 SlotToIndices(slot_offset, &bucket_index, &cell_index, &bit_index); | 57 SlotToIndices(slot_offset, &bucket_index, &cell_index, &bit_index); |
| 53 if (bucket[bucket_index] != nullptr) { | 58 base::AtomicValue<uint32_t>* current_bucket = bucket[bucket_index].Value(); |
| 54 uint32_t cell = bucket[bucket_index][cell_index]; | 59 if (current_bucket != nullptr) { |
| 60 uint32_t cell = current_bucket[cell_index].Value(); |
| 55 if (cell) { | 61 if (cell) { |
| 56 uint32_t bit_mask = 1u << bit_index; | 62 uint32_t bit_mask = 1u << bit_index; |
| 57 if (cell & bit_mask) { | 63 if (cell & bit_mask) { |
| 58 bucket[bucket_index][cell_index] ^= bit_mask; | 64 current_bucket[cell_index].ClearBit(bit_index); |
| 59 } | 65 } |
| 60 } | 66 } |
| 61 } | 67 } |
| 62 } | 68 } |
| 63 | 69 |
| 64 // The slot offsets specify a range of slots at addresses: | 70 // The slot offsets specify a range of slots at addresses: |
| 65 // [page_start_ + start_offset ... page_start_ + end_offset). | 71 // [page_start_ + start_offset ... page_start_ + end_offset). |
| 66 void RemoveRange(int start_offset, int end_offset) { | 72 void RemoveRange(int start_offset, int end_offset) { |
| 67 CHECK_LE(end_offset, 1 << kPageSizeBits); | 73 CHECK_LE(end_offset, 1 << kPageSizeBits); |
| 68 DCHECK_LE(start_offset, end_offset); | 74 DCHECK_LE(start_offset, end_offset); |
| 69 int start_bucket, start_cell, start_bit; | 75 int start_bucket, start_cell, start_bit; |
| 70 SlotToIndices(start_offset, &start_bucket, &start_cell, &start_bit); | 76 SlotToIndices(start_offset, &start_bucket, &start_cell, &start_bit); |
| 71 int end_bucket, end_cell, end_bit; | 77 int end_bucket, end_cell, end_bit; |
| 72 SlotToIndices(end_offset, &end_bucket, &end_cell, &end_bit); | 78 SlotToIndices(end_offset, &end_bucket, &end_cell, &end_bit); |
| 73 uint32_t start_mask = (1u << start_bit) - 1; | 79 uint32_t start_mask = (1u << start_bit) - 1; |
| 74 uint32_t end_mask = ~((1u << end_bit) - 1); | 80 uint32_t end_mask = ~((1u << end_bit) - 1); |
| 75 if (start_bucket == end_bucket && start_cell == end_cell) { | 81 if (start_bucket == end_bucket && start_cell == end_cell) { |
| 76 MaskCell(start_bucket, start_cell, start_mask | end_mask); | 82 ClearCell(start_bucket, start_cell, ~(start_mask | end_mask)); |
| 77 return; | 83 return; |
| 78 } | 84 } |
| 79 int current_bucket = start_bucket; | 85 int current_bucket = start_bucket; |
| 80 int current_cell = start_cell; | 86 int current_cell = start_cell; |
| 81 MaskCell(current_bucket, current_cell, start_mask); | 87 ClearCell(current_bucket, current_cell, ~start_mask); |
| 82 current_cell++; | 88 current_cell++; |
| 83 if (current_bucket < end_bucket) { | 89 if (current_bucket < end_bucket) { |
| 84 if (bucket[current_bucket] != nullptr) { | 90 if (bucket[current_bucket].Value() != nullptr) { |
| 85 while (current_cell < kCellsPerBucket) { | 91 while (current_cell < kCellsPerBucket) { |
| 86 bucket[current_bucket][current_cell] = 0; | 92 bucket[current_bucket].Value()[current_cell].SetValue(0); |
| 87 current_cell++; | 93 current_cell++; |
| 88 } | 94 } |
| 89 } | 95 } |
| 90 // The rest of the current bucket is cleared. | 96 // The rest of the current bucket is cleared. |
| 91 // Move on to the next bucket. | 97 // Move on to the next bucket. |
| 92 current_bucket++; | 98 current_bucket++; |
| 93 current_cell = 0; | 99 current_cell = 0; |
| 94 } | 100 } |
| 95 DCHECK(current_bucket == end_bucket || | 101 DCHECK(current_bucket == end_bucket || |
| 96 (current_bucket < end_bucket && current_cell == 0)); | 102 (current_bucket < end_bucket && current_cell == 0)); |
| 97 while (current_bucket < end_bucket) { | 103 while (current_bucket < end_bucket) { |
| 98 ReleaseBucket(current_bucket); | 104 ReleaseBucket(current_bucket); |
| 99 current_bucket++; | 105 current_bucket++; |
| 100 } | 106 } |
| 101 // All buckets between start_bucket and end_bucket are cleared. | 107 // All buckets between start_bucket and end_bucket are cleared. |
| 102 DCHECK(current_bucket == end_bucket && current_cell <= end_cell); | 108 DCHECK(current_bucket == end_bucket && current_cell <= end_cell); |
| 103 if (current_bucket == kBuckets || bucket[current_bucket] == nullptr) { | 109 if (current_bucket == kBuckets || |
| 110 bucket[current_bucket].Value() == nullptr) { |
| 104 return; | 111 return; |
| 105 } | 112 } |
| 106 while (current_cell < end_cell) { | 113 while (current_cell < end_cell) { |
| 107 bucket[current_bucket][current_cell] = 0; | 114 bucket[current_bucket].Value()[current_cell].SetValue(0); |
| 108 current_cell++; | 115 current_cell++; |
| 109 } | 116 } |
| 110 // All cells between start_cell and end_cell are cleared. | 117 // All cells between start_cell and end_cell are cleared. |
| 111 DCHECK(current_bucket == end_bucket && current_cell == end_cell); | 118 DCHECK(current_bucket == end_bucket && current_cell == end_cell); |
| 112 MaskCell(end_bucket, end_cell, end_mask); | 119 ClearCell(end_bucket, end_cell, ~end_mask); |
| 113 } | 120 } |
| 114 | 121 |
| 115 // The slot offset specifies a slot at address page_start_ + slot_offset. | 122 // The slot offset specifies a slot at address page_start_ + slot_offset. |
| 116 bool Lookup(int slot_offset) { | 123 bool Lookup(int slot_offset) { |
| 117 int bucket_index, cell_index, bit_index; | 124 int bucket_index, cell_index, bit_index; |
| 118 SlotToIndices(slot_offset, &bucket_index, &cell_index, &bit_index); | 125 SlotToIndices(slot_offset, &bucket_index, &cell_index, &bit_index); |
| 119 if (bucket[bucket_index] != nullptr) { | 126 if (bucket[bucket_index].Value() != nullptr) { |
| 120 uint32_t cell = bucket[bucket_index][cell_index]; | 127 uint32_t cell = bucket[bucket_index].Value()[cell_index].Value(); |
| 121 return (cell & (1u << bit_index)) != 0; | 128 return (cell & (1u << bit_index)) != 0; |
| 122 } | 129 } |
| 123 return false; | 130 return false; |
| 124 } | 131 } |
| 125 | 132 |
| 126 // Iterate over all slots in the set and for each slot invoke the callback. | 133 // Iterate over all slots in the set and for each slot invoke the callback. |
| 127 // If the callback returns REMOVE_SLOT then the slot is removed from the set. | 134 // If the callback returns REMOVE_SLOT then the slot is removed from the set. |
| 128 // Returns the new number of slots. | 135 // Returns the new number of slots. |
| 136 // This method should only be called on the main thread. |
| 129 // | 137 // |
| 130 // Sample usage: | 138 // Sample usage: |
| 131 // Iterate([](Address slot_address) { | 139 // Iterate([](Address slot_address) { |
| 132 // if (good(slot_address)) return KEEP_SLOT; | 140 // if (good(slot_address)) return KEEP_SLOT; |
| 133 // else return REMOVE_SLOT; | 141 // else return REMOVE_SLOT; |
| 134 // }); | 142 // }); |
| 135 template <typename Callback> | 143 template <typename Callback> |
| 136 int Iterate(Callback callback) { | 144 int Iterate(Callback callback) { |
| 137 int new_count = 0; | 145 int new_count = 0; |
| 138 for (int bucket_index = 0; bucket_index < kBuckets; bucket_index++) { | 146 for (int bucket_index = 0; bucket_index < kBuckets; bucket_index++) { |
| 139 if (bucket[bucket_index] != nullptr) { | 147 if (bucket[bucket_index].Value() != nullptr) { |
| 140 int in_bucket_count = 0; | 148 int in_bucket_count = 0; |
| 141 uint32_t* current_bucket = bucket[bucket_index]; | 149 base::AtomicValue<uint32_t>* current_bucket = |
| 150 bucket[bucket_index].Value(); |
| 142 int cell_offset = bucket_index * kBitsPerBucket; | 151 int cell_offset = bucket_index * kBitsPerBucket; |
| 143 for (int i = 0; i < kCellsPerBucket; i++, cell_offset += kBitsPerCell) { | 152 for (int i = 0; i < kCellsPerBucket; i++, cell_offset += kBitsPerCell) { |
| 144 if (current_bucket[i]) { | 153 if (current_bucket[i].Value()) { |
| 145 uint32_t cell = current_bucket[i]; | 154 uint32_t cell = current_bucket[i].Value(); |
| 146 uint32_t old_cell = cell; | 155 uint32_t old_cell = cell; |
| 147 uint32_t new_cell = cell; | 156 uint32_t new_cell = cell; |
| 148 while (cell) { | 157 while (cell) { |
| 149 int bit_offset = base::bits::CountTrailingZeros32(cell); | 158 int bit_offset = base::bits::CountTrailingZeros32(cell); |
| 150 uint32_t bit_mask = 1u << bit_offset; | 159 uint32_t bit_mask = 1u << bit_offset; |
| 151 uint32_t slot = (cell_offset + bit_offset) << kPointerSizeLog2; | 160 uint32_t slot = (cell_offset + bit_offset) << kPointerSizeLog2; |
| 152 if (callback(page_start_ + slot) == KEEP_SLOT) { | 161 if (callback(page_start_ + slot) == KEEP_SLOT) { |
| 153 ++in_bucket_count; | 162 ++in_bucket_count; |
| 154 } else { | 163 } else { |
| 155 new_cell ^= bit_mask; | 164 new_cell ^= bit_mask; |
| 156 } | 165 } |
| 157 cell ^= bit_mask; | 166 cell ^= bit_mask; |
| 158 } | 167 } |
| 159 if (old_cell != new_cell) { | 168 if (old_cell != new_cell) { |
| 160 current_bucket[i] = new_cell; | 169 while (!current_bucket[i].TrySetValue(old_cell, new_cell)) { |
| 170 // If TrySetValue fails, the cell must have changed. We just |
| 171 // have to read the current value of the cell, & it with the |
| 172 // computed value, and retry. We can do this, because this |
| 173 // method will only be called on the main thread and filtering |
| 174 // threads will only remove slots. |
| 175 old_cell = current_bucket[i].Value(); |
| 176 new_cell &= old_cell; |
| 177 } |
| 161 } | 178 } |
| 162 } | 179 } |
| 163 } | 180 } |
| 164 if (in_bucket_count == 0) { | 181 if (in_bucket_count == 0) { |
| 165 ReleaseBucket(bucket_index); | 182 ReleaseBucket(bucket_index); |
| 166 } | 183 } |
| 167 new_count += in_bucket_count; | 184 new_count += in_bucket_count; |
| 168 } | 185 } |
| 169 } | 186 } |
| 170 return new_count; | 187 return new_count; |
| 171 } | 188 } |
| 172 | 189 |
| 173 private: | 190 private: |
| 174 static const int kMaxSlots = (1 << kPageSizeBits) / kPointerSize; | 191 static const int kMaxSlots = (1 << kPageSizeBits) / kPointerSize; |
| 175 static const int kCellsPerBucket = 32; | 192 static const int kCellsPerBucket = 32; |
| 176 static const int kCellsPerBucketLog2 = 5; | 193 static const int kCellsPerBucketLog2 = 5; |
| 177 static const int kBitsPerCell = 32; | 194 static const int kBitsPerCell = 32; |
| 178 static const int kBitsPerCellLog2 = 5; | 195 static const int kBitsPerCellLog2 = 5; |
| 179 static const int kBitsPerBucket = kCellsPerBucket * kBitsPerCell; | 196 static const int kBitsPerBucket = kCellsPerBucket * kBitsPerCell; |
| 180 static const int kBitsPerBucketLog2 = kCellsPerBucketLog2 + kBitsPerCellLog2; | 197 static const int kBitsPerBucketLog2 = kCellsPerBucketLog2 + kBitsPerCellLog2; |
| 181 static const int kBuckets = kMaxSlots / kCellsPerBucket / kBitsPerCell; | 198 static const int kBuckets = kMaxSlots / kCellsPerBucket / kBitsPerCell; |
| 182 | 199 |
| 183 uint32_t* AllocateBucket() { | 200 base::AtomicValue<uint32_t>* AllocateBucket() { |
| 184 uint32_t* result = NewArray<uint32_t>(kCellsPerBucket); | 201 base::AtomicValue<uint32_t>* result = |
| 202 NewArray<base::AtomicValue<uint32_t>>(kCellsPerBucket); |
| 185 for (int i = 0; i < kCellsPerBucket; i++) { | 203 for (int i = 0; i < kCellsPerBucket; i++) { |
| 186 result[i] = 0; | 204 result[i].SetValue(0); |
| 187 } | 205 } |
| 188 return result; | 206 return result; |
| 189 } | 207 } |
| 190 | 208 |
| 191 void ReleaseBucket(int bucket_index) { | 209 void ReleaseBucket(int bucket_index) { |
| 192 DeleteArray<uint32_t>(bucket[bucket_index]); | 210 DeleteArray<base::AtomicValue<uint32_t>>(bucket[bucket_index].Value()); |
| 193 bucket[bucket_index] = nullptr; | 211 bucket[bucket_index].SetValue(nullptr); |
| 194 } | 212 } |
| 195 | 213 |
| 196 void MaskCell(int bucket_index, int cell_index, uint32_t mask) { | 214 void ClearCell(int bucket_index, int cell_index, uint32_t mask) { |
| 197 if (bucket_index < kBuckets) { | 215 if (bucket_index < kBuckets) { |
| 198 uint32_t* cells = bucket[bucket_index]; | 216 base::AtomicValue<uint32_t>* cells = bucket[bucket_index].Value(); |
| 199 if (cells != nullptr && cells[cell_index] != 0) { | 217 if (cells != nullptr) { |
| 200 cells[cell_index] &= mask; | 218 uint32_t cell = cells[cell_index].Value(); |
| 219 if (cell) cells[cell_index].SetBits(0, mask); |
| 201 } | 220 } |
| 202 } else { | 221 } else { |
| 203 // GCC bug 59124: Emits wrong warnings | 222 // GCC bug 59124: Emits wrong warnings |
| 204 // "array subscript is above array bounds" | 223 // "array subscript is above array bounds" |
| 205 UNREACHABLE(); | 224 UNREACHABLE(); |
| 206 } | 225 } |
| 207 } | 226 } |
| 208 | 227 |
| 209 // Converts the slot offset into bucket/cell/bit index. | 228 // Converts the slot offset into bucket/cell/bit index. |
| 210 void SlotToIndices(int slot_offset, int* bucket_index, int* cell_index, | 229 void SlotToIndices(int slot_offset, int* bucket_index, int* cell_index, |
| 211 int* bit_index) { | 230 int* bit_index) { |
| 212 DCHECK_EQ(slot_offset % kPointerSize, 0); | 231 DCHECK_EQ(slot_offset % kPointerSize, 0); |
| 213 int slot = slot_offset >> kPointerSizeLog2; | 232 int slot = slot_offset >> kPointerSizeLog2; |
| 214 DCHECK(slot >= 0 && slot <= kMaxSlots); | 233 DCHECK(slot >= 0 && slot <= kMaxSlots); |
| 215 *bucket_index = slot >> kBitsPerBucketLog2; | 234 *bucket_index = slot >> kBitsPerBucketLog2; |
| 216 *cell_index = (slot >> kBitsPerCellLog2) & (kCellsPerBucket - 1); | 235 *cell_index = (slot >> kBitsPerCellLog2) & (kCellsPerBucket - 1); |
| 217 *bit_index = slot & (kBitsPerCell - 1); | 236 *bit_index = slot & (kBitsPerCell - 1); |
| 218 } | 237 } |
| 219 | 238 |
| 220 uint32_t* bucket[kBuckets]; | 239 base::AtomicValue<base::AtomicValue<uint32_t>*> bucket[kBuckets]; |
| 221 Address page_start_; | 240 Address page_start_; |
| 222 }; | 241 }; |
| 223 | 242 |
| 224 enum SlotType { | 243 enum SlotType { |
| 225 EMBEDDED_OBJECT_SLOT, | 244 EMBEDDED_OBJECT_SLOT, |
| 226 OBJECT_SLOT, | 245 OBJECT_SLOT, |
| 227 CELL_TARGET_SLOT, | 246 CELL_TARGET_SLOT, |
| 228 CODE_TARGET_SLOT, | 247 CODE_TARGET_SLOT, |
| 229 CODE_ENTRY_SLOT, | 248 CODE_ENTRY_SLOT, |
| 230 DEBUG_TARGET_SLOT, | 249 DEBUG_TARGET_SLOT, |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 355 }; | 374 }; |
| 356 | 375 |
| 357 Address page_start_; | 376 Address page_start_; |
| 358 Chunk* chunk_; | 377 Chunk* chunk_; |
| 359 }; | 378 }; |
| 360 | 379 |
| 361 } // namespace internal | 380 } // namespace internal |
| 362 } // namespace v8 | 381 } // namespace v8 |
| 363 | 382 |
| 364 #endif // V8_SLOT_SET_H | 383 #endif // V8_SLOT_SET_H |
| OLD | NEW |