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