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/atomic-utils.h" |
10 #include "src/base/bits.h" | 10 #include "src/base/bits.h" |
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
240 Address page_start_; | 240 Address page_start_; |
241 }; | 241 }; |
242 | 242 |
243 enum SlotType { | 243 enum SlotType { |
244 EMBEDDED_OBJECT_SLOT, | 244 EMBEDDED_OBJECT_SLOT, |
245 OBJECT_SLOT, | 245 OBJECT_SLOT, |
246 CELL_TARGET_SLOT, | 246 CELL_TARGET_SLOT, |
247 CODE_TARGET_SLOT, | 247 CODE_TARGET_SLOT, |
248 CODE_ENTRY_SLOT, | 248 CODE_ENTRY_SLOT, |
249 DEBUG_TARGET_SLOT, | 249 DEBUG_TARGET_SLOT, |
250 NUMBER_OF_SLOT_TYPES | 250 CLEARED_SLOT |
251 }; | 251 }; |
252 | 252 |
253 // Data structure for maintaining a multiset of typed slots in a page. | 253 // Data structure for maintaining a multiset of typed slots in a page. |
254 // Typed slots can only appear in Code and JSFunction objects, so | 254 // Typed slots can only appear in Code and JSFunction objects, so |
255 // the maximum possible offset is limited by the LargePage::kMaxCodePageSize. | 255 // the maximum possible offset is limited by the LargePage::kMaxCodePageSize. |
256 // The implementation is a chain of chunks, where each chunks is an array of | 256 // The implementation is a chain of chunks, where each chunks is an array of |
257 // encoded (slot type, slot offset) pairs. | 257 // encoded (slot type, slot offset) pairs. |
258 // There is no duplicate detection and we do not expect many duplicates because | 258 // There is no duplicate detection and we do not expect many duplicates because |
259 // typed slots contain V8 internal pointers that are not directly exposed to JS. | 259 // typed slots contain V8 internal pointers that are not directly exposed to JS. |
260 class TypedSlotSet { | 260 class TypedSlotSet { |
261 public: | 261 public: |
| 262 typedef std::pair<SlotType, uint32_t> TypeAndOffset; |
| 263 |
262 struct TypedSlot { | 264 struct TypedSlot { |
263 TypedSlot() : type_and_offset_(0), host_offset_(0) {} | 265 TypedSlot() { |
| 266 type_and_offset_.SetValue(0); |
| 267 host_offset_.SetValue(0); |
| 268 } |
264 | 269 |
265 TypedSlot(SlotType type, uint32_t host_offset, uint32_t offset) | 270 TypedSlot(SlotType type, uint32_t host_offset, uint32_t offset) { |
266 : type_and_offset_(TypeField::encode(type) | | 271 type_and_offset_.SetValue(TypeField::encode(type) | |
267 OffsetField::encode(offset)), | 272 OffsetField::encode(offset)); |
268 host_offset_(host_offset) {} | 273 host_offset_.SetValue(host_offset); |
| 274 } |
269 | 275 |
270 bool operator==(const TypedSlot other) { | 276 bool operator==(const TypedSlot other) { |
271 return type_and_offset_ == other.type_and_offset_ && | 277 return type_and_offset_.Value() == other.type_and_offset_.Value() && |
272 host_offset_ == other.host_offset_; | 278 host_offset_.Value() == other.host_offset_.Value(); |
273 } | 279 } |
274 | 280 |
275 bool operator!=(const TypedSlot other) { return !(*this == other); } | 281 bool operator!=(const TypedSlot other) { return !(*this == other); } |
276 | 282 |
277 SlotType type() { return TypeField::decode(type_and_offset_); } | 283 SlotType type() { return TypeField::decode(type_and_offset_.Value()); } |
278 | 284 |
279 uint32_t offset() { return OffsetField::decode(type_and_offset_); } | 285 uint32_t offset() { return OffsetField::decode(type_and_offset_.Value()); } |
280 | 286 |
281 uint32_t host_offset() { return host_offset_; } | 287 TypeAndOffset GetTypeAndOffset() { |
| 288 uint32_t type_and_offset = type_and_offset_.Value(); |
| 289 return std::make_pair(TypeField::decode(type_and_offset), |
| 290 OffsetField::decode(type_and_offset)); |
| 291 } |
282 | 292 |
283 uint32_t type_and_offset_; | 293 uint32_t host_offset() { return host_offset_.Value(); } |
284 uint32_t host_offset_; | 294 |
| 295 void Set(TypedSlot slot) { |
| 296 type_and_offset_.SetValue(slot.type_and_offset_.Value()); |
| 297 host_offset_.SetValue(slot.host_offset_.Value()); |
| 298 } |
| 299 |
| 300 void Clear() { |
| 301 type_and_offset_.SetValue(TypeField::encode(CLEARED_SLOT) | |
| 302 OffsetField::encode(0)); |
| 303 host_offset_.SetValue(0); |
| 304 } |
| 305 |
| 306 base::AtomicValue<uint32_t> type_and_offset_; |
| 307 base::AtomicValue<uint32_t> host_offset_; |
285 }; | 308 }; |
286 static const int kMaxOffset = 1 << 29; | 309 static const int kMaxOffset = 1 << 29; |
287 | 310 |
288 explicit TypedSlotSet(Address page_start) : page_start_(page_start) { | 311 explicit TypedSlotSet(Address page_start) : page_start_(page_start) { |
289 chunk_ = new Chunk(nullptr, kInitialBufferSize); | 312 chunk_.SetValue(new Chunk(nullptr, kInitialBufferSize)); |
290 } | 313 } |
291 | 314 |
292 ~TypedSlotSet() { | 315 ~TypedSlotSet() { |
293 Chunk* chunk = chunk_; | 316 Chunk* chunk = chunk_.Value(); |
294 while (chunk != nullptr) { | 317 while (chunk != nullptr) { |
295 Chunk* next = chunk->next; | 318 Chunk* next = chunk->next.Value(); |
296 delete chunk; | 319 delete chunk; |
297 chunk = next; | 320 chunk = next; |
298 } | 321 } |
299 } | 322 } |
300 | 323 |
301 // The slot offset specifies a slot at address page_start_ + offset. | 324 // The slot offset specifies a slot at address page_start_ + offset. |
| 325 // This method can only be called on the main thread. |
302 void Insert(SlotType type, uint32_t host_offset, uint32_t offset) { | 326 void Insert(SlotType type, uint32_t host_offset, uint32_t offset) { |
303 TypedSlot slot(type, host_offset, offset); | 327 TypedSlot slot(type, host_offset, offset); |
304 if (!chunk_->AddSlot(slot)) { | 328 Chunk* top_chunk = chunk_.Value(); |
305 chunk_ = new Chunk(chunk_, NextCapacity(chunk_->capacity)); | 329 if (!top_chunk->AddSlot(slot)) { |
306 bool added = chunk_->AddSlot(slot); | 330 Chunk* new_top_chunk = |
| 331 new Chunk(top_chunk, NextCapacity(top_chunk->capacity.Value())); |
| 332 bool added = new_top_chunk->AddSlot(slot); |
| 333 chunk_.SetValue(new_top_chunk); |
307 DCHECK(added); | 334 DCHECK(added); |
308 USE(added); | 335 USE(added); |
309 } | 336 } |
310 } | 337 } |
311 | 338 |
312 // Iterate over all slots in the set and for each slot invoke the callback. | 339 // Iterate over all slots in the set and for each slot invoke the callback. |
313 // If the callback returns REMOVE_SLOT then the slot is removed from the set. | 340 // If the callback returns REMOVE_SLOT then the slot is removed from the set. |
314 // Returns the new number of slots. | 341 // Returns the new number of slots. |
315 // | 342 // |
316 // Sample usage: | 343 // Sample usage: |
317 // Iterate([](SlotType slot_type, Address slot_address) { | 344 // Iterate([](SlotType slot_type, Address slot_address) { |
318 // if (good(slot_type, slot_address)) return KEEP_SLOT; | 345 // if (good(slot_type, slot_address)) return KEEP_SLOT; |
319 // else return REMOVE_SLOT; | 346 // else return REMOVE_SLOT; |
320 // }); | 347 // }); |
321 template <typename Callback> | 348 template <typename Callback> |
322 int Iterate(Callback callback) { | 349 int Iterate(Callback callback) { |
323 STATIC_ASSERT(NUMBER_OF_SLOT_TYPES < 8); | 350 STATIC_ASSERT(CLEARED_SLOT < 8); |
324 const TypedSlot kRemovedSlot(NUMBER_OF_SLOT_TYPES, 0, 0); | 351 Chunk* chunk = chunk_.Value(); |
325 Chunk* chunk = chunk_; | |
326 int new_count = 0; | 352 int new_count = 0; |
327 while (chunk != nullptr) { | 353 while (chunk != nullptr) { |
328 TypedSlot* buffer = chunk->buffer; | 354 TypedSlot* buffer = chunk->buffer.Value(); |
329 int count = chunk->count; | 355 int count = chunk->count.Value(); |
330 for (int i = 0; i < count; i++) { | 356 for (int i = 0; i < count; i++) { |
331 TypedSlot slot = buffer[i]; | 357 // Order is important here. We have to read out the slot type last to |
332 if (slot != kRemovedSlot) { | 358 // observe the concurrent removal case consistently. |
333 SlotType type = slot.type(); | 359 Address host_addr = page_start_ + buffer[i].host_offset(); |
334 Address addr = page_start_ + slot.offset(); | 360 TypeAndOffset type_and_offset = buffer[i].GetTypeAndOffset(); |
335 Address host_addr = page_start_ + slot.host_offset(); | 361 SlotType type = type_and_offset.first; |
| 362 if (type != CLEARED_SLOT) { |
| 363 Address addr = page_start_ + type_and_offset.second; |
336 if (callback(type, host_addr, addr) == KEEP_SLOT) { | 364 if (callback(type, host_addr, addr) == KEEP_SLOT) { |
337 new_count++; | 365 new_count++; |
338 } else { | 366 } else { |
339 buffer[i] = kRemovedSlot; | 367 buffer[i].Clear(); |
340 } | 368 } |
341 } | 369 } |
342 } | 370 } |
343 chunk = chunk->next; | 371 chunk = chunk->next.Value(); |
344 } | 372 } |
345 return new_count; | 373 return new_count; |
346 } | 374 } |
347 | 375 |
348 private: | 376 private: |
349 static const int kInitialBufferSize = 100; | 377 static const int kInitialBufferSize = 100; |
350 static const int kMaxBufferSize = 16 * KB; | 378 static const int kMaxBufferSize = 16 * KB; |
351 | 379 |
352 static int NextCapacity(int capacity) { | 380 static int NextCapacity(int capacity) { |
353 return Min(kMaxBufferSize, capacity * 2); | 381 return Min(kMaxBufferSize, capacity * 2); |
354 } | 382 } |
355 | 383 |
356 class OffsetField : public BitField<int, 0, 29> {}; | 384 class OffsetField : public BitField<int, 0, 29> {}; |
357 class TypeField : public BitField<SlotType, 29, 3> {}; | 385 class TypeField : public BitField<SlotType, 29, 3> {}; |
358 | 386 |
359 struct Chunk : Malloced { | 387 struct Chunk : Malloced { |
360 explicit Chunk(Chunk* next_chunk, int capacity) | 388 explicit Chunk(Chunk* next_chunk, int chunk_capacity) { |
361 : next(next_chunk), count(0), capacity(capacity) { | 389 count.SetValue(0); |
362 buffer = NewArray<TypedSlot>(capacity); | 390 capacity.SetValue(chunk_capacity); |
| 391 buffer.SetValue(NewArray<TypedSlot>(chunk_capacity)); |
| 392 next.SetValue(next_chunk); |
363 } | 393 } |
364 bool AddSlot(TypedSlot slot) { | 394 bool AddSlot(TypedSlot slot) { |
365 if (count == capacity) return false; | 395 int current_count = count.Value(); |
366 buffer[count++] = slot; | 396 if (current_count == capacity.Value()) return false; |
| 397 TypedSlot* current_buffer = buffer.Value(); |
| 398 // Order is important here. We have to write the slot first before |
| 399 // increasing the counter to guarantee that a consistent state is |
| 400 // observed by concurrent threads. |
| 401 current_buffer[current_count].Set(slot); |
| 402 count.SetValue(current_count + 1); |
367 return true; | 403 return true; |
368 } | 404 } |
369 ~Chunk() { DeleteArray(buffer); } | 405 ~Chunk() { DeleteArray(buffer.Value()); } |
370 Chunk* next; | 406 base::AtomicValue<Chunk*> next; |
371 int count; | 407 base::AtomicValue<int> count; |
372 int capacity; | 408 base::AtomicValue<int> capacity; |
373 TypedSlot* buffer; | 409 base::AtomicValue<TypedSlot*> buffer; |
374 }; | 410 }; |
375 | 411 |
376 Address page_start_; | 412 Address page_start_; |
377 Chunk* chunk_; | 413 base::AtomicValue<Chunk*> chunk_; |
378 }; | 414 }; |
379 | 415 |
380 } // namespace internal | 416 } // namespace internal |
381 } // namespace v8 | 417 } // namespace v8 |
382 | 418 |
383 #endif // V8_SLOT_SET_H | 419 #endif // V8_SLOT_SET_H |
OLD | NEW |