| // Copyright 2017 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. |
| |
| #ifndef V8_HEAP_LOCAL_ALLOCATOR_H_ |
| #define V8_HEAP_LOCAL_ALLOCATOR_H_ |
| |
| #include "src/globals.h" |
| #include "src/heap/heap.h" |
| #include "src/heap/spaces.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| // Allocator encapsulating thread-local allocation. Assumes that all other |
| // allocations also go through LocalAllocator. |
| class LocalAllocator { |
| public: |
| static const int kLabSize = 32 * KB; |
| static const int kMaxLabObjectSize = 8 * KB; |
| |
| explicit LocalAllocator(Heap* heap) |
| : heap_(heap), |
| new_space_(heap->new_space()), |
| compaction_spaces_(heap), |
| new_space_lab_(LocalAllocationBuffer::InvalidBuffer()), |
| lab_allocation_will_fail_(false) {} |
| |
| // Needs to be called from the main thread to finalize this LocalAllocator. |
| void Finalize() { |
| heap_->old_space()->MergeCompactionSpace(compaction_spaces_.Get(OLD_SPACE)); |
| heap_->code_space()->MergeCompactionSpace( |
| compaction_spaces_.Get(CODE_SPACE)); |
| // Give back remaining LAB space if this LocalAllocator's new space LAB |
| // sits right next to new space allocation top. |
| const LinearAllocationArea info = new_space_lab_.Close(); |
| const Address top = new_space_->top(); |
| if (info.limit() != nullptr && info.limit() == top) { |
| DCHECK_NOT_NULL(info.top()); |
| *new_space_->allocation_top_address() = info.top(); |
| } |
| } |
| |
| AllocationResult Allocate(AllocationSpace space, int object_size, |
| AllocationAlignment alignment) { |
| switch (space) { |
| case NEW_SPACE: |
| return AllocateInNewSpace(object_size, alignment); |
| case OLD_SPACE: |
| return compaction_spaces_.Get(OLD_SPACE)->AllocateRaw(object_size, |
| alignment); |
| case CODE_SPACE: |
| return compaction_spaces_.Get(CODE_SPACE) |
| ->AllocateRaw(object_size, alignment); |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| } |
| |
| void FreeLast(AllocationSpace space, HeapObject* object, int object_size) { |
| switch (space) { |
| case NEW_SPACE: |
| FreeLastInNewSpace(object, object_size); |
| return; |
| case OLD_SPACE: |
| FreeLastInOldSpace(object, object_size); |
| return; |
| default: |
| // Only new and old space supported. |
| UNREACHABLE(); |
| break; |
| } |
| } |
| |
| private: |
| AllocationResult AllocateInNewSpace(int object_size, |
| AllocationAlignment alignment) { |
| if (object_size > kMaxLabObjectSize) { |
| return new_space_->AllocateRawSynchronized(object_size, alignment); |
| } |
| return AllocateInLAB(object_size, alignment); |
| } |
| |
| inline bool NewLocalAllocationBuffer() { |
| if (lab_allocation_will_fail_) return false; |
| LocalAllocationBuffer saved_lab_ = new_space_lab_; |
| AllocationResult result = |
| new_space_->AllocateRawSynchronized(kLabSize, kWordAligned); |
| new_space_lab_ = LocalAllocationBuffer::FromResult(heap_, result, kLabSize); |
| if (new_space_lab_.IsValid()) { |
| new_space_lab_.TryMerge(&saved_lab_); |
| return true; |
| } |
| new_space_lab_ = saved_lab_; |
| lab_allocation_will_fail_ = true; |
| return false; |
| } |
| |
| AllocationResult AllocateInLAB(int object_size, |
| AllocationAlignment alignment) { |
| AllocationResult allocation; |
| if (!new_space_lab_.IsValid() && !NewLocalAllocationBuffer()) { |
| return AllocationResult::Retry(OLD_SPACE); |
| } |
| allocation = new_space_lab_.AllocateRawAligned(object_size, alignment); |
| if (allocation.IsRetry()) { |
| if (!NewLocalAllocationBuffer()) { |
| return AllocationResult::Retry(OLD_SPACE); |
| } else { |
| allocation = new_space_lab_.AllocateRawAligned(object_size, alignment); |
| CHECK(!allocation.IsRetry()); |
| } |
| } |
| return allocation; |
| } |
| |
| void FreeLastInNewSpace(HeapObject* object, int object_size) { |
| if (!new_space_lab_.TryFreeLast(object, object_size)) { |
| // We couldn't free the last object so we have to write a proper filler. |
| heap_->CreateFillerObjectAt(object->address(), object_size, |
| ClearRecordedSlots::kNo); |
| } |
| } |
| |
| void FreeLastInOldSpace(HeapObject* object, int object_size) { |
| if (!compaction_spaces_.Get(OLD_SPACE)->TryFreeLast(object, object_size)) { |
| // We couldn't free the last object so we have to write a proper filler. |
| heap_->CreateFillerObjectAt(object->address(), object_size, |
| ClearRecordedSlots::kNo); |
| } |
| } |
| |
| Heap* const heap_; |
| NewSpace* const new_space_; |
| CompactionSpaceCollection compaction_spaces_; |
| LocalAllocationBuffer new_space_lab_; |
| bool lab_allocation_will_fail_; |
| }; |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_HEAP_LOCAL_ALLOCATOR_H_ |