Clean up aligned allocation code in preparation for SIMD alignments.

Moves alignment fill calculations into two static Heap methods.
Adds a Heap method to handle the complex case where filler is potentially needed before and after a heap object.
Makes DoubleAlignForDeserialization explicitly fill after an already
aligned object.

LOG=N
BUG=v8:4124

Review URL: https://codereview.chromium.org/1150593003

Cr-Commit-Position: refs/heads/master@{#28687}
This commit is contained in:
bbudge 2015-05-28 10:19:39 -07:00 committed by Commit bot
parent 62b61129f2
commit fcfb080eb9
6 changed files with 245 additions and 80 deletions

View File

@ -1986,31 +1986,54 @@ STATIC_ASSERT((HeapNumber::kValueOffset & kDoubleAlignmentMask) !=
#endif
HeapObject* Heap::EnsureAligned(HeapObject* object, int size,
AllocationAlignment alignment) {
if (alignment == kDoubleAligned &&
(OffsetFrom(object->address()) & kDoubleAlignmentMask) != 0) {
CreateFillerObjectAt(object->address(), kPointerSize);
return HeapObject::FromAddress(object->address() + kPointerSize);
} else if (alignment == kDoubleUnaligned &&
(OffsetFrom(object->address()) & kDoubleAlignmentMask) == 0) {
CreateFillerObjectAt(object->address(), kPointerSize);
return HeapObject::FromAddress(object->address() + kPointerSize);
} else {
CreateFillerObjectAt(object->address() + size - kPointerSize, kPointerSize);
return object;
int Heap::GetMaximumFillToAlign(AllocationAlignment alignment) {
switch (alignment) {
case kWordAligned:
return 0;
case kDoubleAligned:
case kDoubleUnaligned:
return kDoubleSize - kPointerSize;
default:
UNREACHABLE();
}
return 0;
}
HeapObject* Heap::PrecedeWithFiller(HeapObject* object) {
CreateFillerObjectAt(object->address(), kPointerSize);
return HeapObject::FromAddress(object->address() + kPointerSize);
int Heap::GetFillToAlign(Address address, AllocationAlignment alignment) {
intptr_t offset = OffsetFrom(address);
if (alignment == kDoubleAligned && (offset & kDoubleAlignmentMask) != 0)
return kPointerSize;
if (alignment == kDoubleUnaligned && (offset & kDoubleAlignmentMask) == 0)
return kDoubleSize - kPointerSize; // No fill if double is always aligned.
return 0;
}
HeapObject* Heap::PrecedeWithFiller(HeapObject* object, int filler_size) {
CreateFillerObjectAt(object->address(), filler_size);
return HeapObject::FromAddress(object->address() + filler_size);
}
HeapObject* Heap::AlignWithFiller(HeapObject* object, int object_size,
int allocation_size,
AllocationAlignment alignment) {
int filler_size = allocation_size - object_size;
DCHECK(filler_size > 0);
int pre_filler = GetFillToAlign(object->address(), alignment);
if (pre_filler) {
object = PrecedeWithFiller(object, pre_filler);
filler_size -= pre_filler;
}
if (filler_size)
CreateFillerObjectAt(object->address() + object_size, filler_size);
return object;
}
HeapObject* Heap::DoubleAlignForDeserialization(HeapObject* object, int size) {
return EnsureAligned(object, size, kDoubleAligned);
return AlignWithFiller(object, size, size + kPointerSize, kDoubleAligned);
}

View File

@ -716,13 +716,23 @@ class Heap {
MUST_USE_RESULT AllocationResult
CopyJSObject(JSObject* source, AllocationSite* site = NULL);
// This method assumes overallocation of one word. It will store a filler
// before the object if the given object is not double aligned, otherwise
// it will place the filler after the object.
MUST_USE_RESULT HeapObject* EnsureAligned(HeapObject* object, int size,
AllocationAlignment alignment);
// Calculates the maximum amount of filler that could be required by the
// given alignment.
static int GetMaximumFillToAlign(AllocationAlignment alignment);
// Calculates the actual amount of filler required for a given address at the
// given alignment.
static int GetFillToAlign(Address address, AllocationAlignment alignment);
MUST_USE_RESULT HeapObject* PrecedeWithFiller(HeapObject* object);
// Creates a filler object and returns a heap object immediately after it.
MUST_USE_RESULT HeapObject* PrecedeWithFiller(HeapObject* object,
int filler_size);
// Creates a filler object if needed for alignment and returns a heap object
// immediately after it. If any space is left after the returned object,
// another filler object is created so the over allocated memory is iterable.
MUST_USE_RESULT HeapObject* AlignWithFiller(HeapObject* object,
int object_size,
int allocation_size,
AllocationAlignment alignment);
// Clear the Instanceof cache (used when a prototype changes).
inline void ClearInstanceofCache();

View File

@ -250,28 +250,21 @@ HeapObject* PagedSpace::AllocateLinearly(int size_in_bytes) {
}
HeapObject* PagedSpace::AllocateLinearlyAligned(int size_in_bytes,
HeapObject* PagedSpace::AllocateLinearlyAligned(int* size_in_bytes,
AllocationAlignment alignment) {
Address current_top = allocation_info_.top();
int alignment_size = 0;
int filler_size = Heap::GetFillToAlign(current_top, alignment);
if (alignment == kDoubleAligned &&
(OffsetFrom(current_top) & kDoubleAlignmentMask) != 0) {
alignment_size = kPointerSize;
size_in_bytes += alignment_size;
} else if (alignment == kDoubleUnaligned &&
(OffsetFrom(current_top) & kDoubleAlignmentMask) == 0) {
alignment_size = kPointerSize;
size_in_bytes += alignment_size;
}
Address new_top = current_top + size_in_bytes;
Address new_top = current_top + filler_size + *size_in_bytes;
if (new_top > allocation_info_.limit()) return NULL;
allocation_info_.set_top(new_top);
if (alignment_size > 0) {
return heap()->EnsureAligned(HeapObject::FromAddress(current_top),
size_in_bytes, alignment);
if (filler_size > 0) {
*size_in_bytes += filler_size;
return heap()->PrecedeWithFiller(HeapObject::FromAddress(current_top),
filler_size);
}
return HeapObject::FromAddress(current_top);
}
@ -303,21 +296,26 @@ AllocationResult PagedSpace::AllocateRawUnaligned(int size_in_bytes) {
AllocationResult PagedSpace::AllocateRawAligned(int size_in_bytes,
AllocationAlignment alignment) {
DCHECK(identity() == OLD_SPACE);
HeapObject* object = AllocateLinearlyAligned(size_in_bytes, alignment);
int aligned_size_in_bytes = size_in_bytes + kPointerSize;
int allocation_size = size_in_bytes;
HeapObject* object = AllocateLinearlyAligned(&allocation_size, alignment);
if (object == NULL) {
object = free_list_.Allocate(aligned_size_in_bytes);
// We don't know exactly how much filler we need to align until space is
// allocated, so assume the worst case.
int filler_size = Heap::GetMaximumFillToAlign(alignment);
allocation_size += filler_size;
object = free_list_.Allocate(allocation_size);
if (object == NULL) {
object = SlowAllocateRaw(aligned_size_in_bytes);
object = SlowAllocateRaw(allocation_size);
}
if (object != NULL) {
object = heap()->EnsureAligned(object, aligned_size_in_bytes, alignment);
if (object != NULL && filler_size != 0) {
object = heap()->AlignWithFiller(object, size_in_bytes, allocation_size,
alignment);
}
}
if (object != NULL) {
MSAN_ALLOCATED_UNINITIALIZED_MEMORY(object->address(), size_in_bytes);
MSAN_ALLOCATED_UNINITIALIZED_MEMORY(object->address(), allocation_size);
return object;
}
@ -344,19 +342,8 @@ AllocationResult PagedSpace::AllocateRaw(int size_in_bytes,
AllocationResult NewSpace::AllocateRawAligned(int size_in_bytes,
AllocationAlignment alignment) {
Address old_top = allocation_info_.top();
int alignment_size = 0;
int aligned_size_in_bytes = 0;
// If double alignment is required and top pointer is not aligned, we allocate
// additional memory to take care of the alignment.
if (alignment == kDoubleAligned &&
(OffsetFrom(old_top) & kDoubleAlignmentMask) != 0) {
alignment_size += kPointerSize;
} else if (alignment == kDoubleUnaligned &&
(OffsetFrom(old_top) & kDoubleAlignmentMask) == 0) {
alignment_size += kPointerSize;
}
aligned_size_in_bytes = size_in_bytes + alignment_size;
int filler_size = Heap::GetFillToAlign(old_top, alignment);
int aligned_size_in_bytes = size_in_bytes + filler_size;
if (allocation_info_.limit() - old_top < aligned_size_in_bytes) {
return SlowAllocateRaw(size_in_bytes, alignment);
@ -366,16 +353,13 @@ AllocationResult NewSpace::AllocateRawAligned(int size_in_bytes,
allocation_info_.set_top(allocation_info_.top() + aligned_size_in_bytes);
DCHECK_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
if (alignment_size > 0) {
obj = heap()->PrecedeWithFiller(obj);
if (filler_size > 0) {
obj = heap()->PrecedeWithFiller(obj, filler_size);
}
// The slow path above ultimately goes through AllocateRaw, so this suffices.
MSAN_ALLOCATED_UNINITIALIZED_MEMORY(obj->address(), size_in_bytes);
DCHECK((kDoubleAligned && (OffsetFrom(obj) & kDoubleAlignmentMask) == 0) ||
(kDoubleUnaligned && (OffsetFrom(obj) & kDoubleAlignmentMask) != 0));
return obj;
}

View File

@ -1455,33 +1455,28 @@ AllocationResult NewSpace::SlowAllocateRaw(int size_in_bytes,
Address old_top = allocation_info_.top();
Address high = to_space_.page_high();
if (allocation_info_.limit() < high) {
int alignment_size = Heap::GetFillToAlign(old_top, alignment);
int aligned_size_in_bytes = size_in_bytes + alignment_size;
// Either the limit has been lowered because linear allocation was disabled
// or because incremental marking wants to get a chance to do a step. Set
// the new limit accordingly.
int aligned_size = size_in_bytes;
aligned_size += (alignment != kWordAligned) ? kPointerSize : 0;
Address new_top = old_top + aligned_size;
Address new_top = old_top + aligned_size_in_bytes;
int bytes_allocated = static_cast<int>(new_top - top_on_previous_step_);
heap()->incremental_marking()->Step(bytes_allocated,
IncrementalMarking::GC_VIA_STACK_GUARD);
UpdateInlineAllocationLimit(aligned_size);
UpdateInlineAllocationLimit(aligned_size_in_bytes);
top_on_previous_step_ = new_top;
if (alignment == kDoubleAligned)
return AllocateRawAligned(size_in_bytes, kDoubleAligned);
else if (alignment == kDoubleUnaligned)
return AllocateRawAligned(size_in_bytes, kDoubleUnaligned);
return AllocateRawUnaligned(size_in_bytes);
if (alignment == kWordAligned) return AllocateRawUnaligned(size_in_bytes);
return AllocateRawAligned(size_in_bytes, alignment);
} else if (AddFreshPage()) {
// Switched to new page. Try allocating again.
int bytes_allocated = static_cast<int>(old_top - top_on_previous_step_);
heap()->incremental_marking()->Step(bytes_allocated,
IncrementalMarking::GC_VIA_STACK_GUARD);
top_on_previous_step_ = to_space_.page_low();
if (alignment == kDoubleAligned)
return AllocateRawAligned(size_in_bytes, kDoubleAligned);
else if (alignment == kDoubleUnaligned)
return AllocateRawAligned(size_in_bytes, kDoubleUnaligned);
return AllocateRawUnaligned(size_in_bytes);
if (alignment == kWordAligned) return AllocateRawUnaligned(size_in_bytes);
return AllocateRawAligned(size_in_bytes, alignment);
} else {
return AllocationResult::Retry();
}

View File

@ -1931,9 +1931,10 @@ class PagedSpace : public Space {
// address denoted by top in allocation_info_.
inline HeapObject* AllocateLinearly(int size_in_bytes);
// Generic fast case allocation function that tries double aligned linear
// allocation at the address denoted by top in allocation_info_.
inline HeapObject* AllocateLinearlyAligned(int size_in_bytes,
// Generic fast case allocation function that tries aligned linear allocation
// at the address denoted by top in allocation_info_. Writes the aligned
// allocation size, which includes the filler size, to size_in_bytes.
inline HeapObject* AllocateLinearlyAligned(int* size_in_bytes,
AllocationAlignment alignment);
// If sweeping is still in progress try to sweep unswept pages. If that is

View File

@ -1784,6 +1784,158 @@ TEST(TestSizeOfObjects) {
}
TEST(TestAlignmentCalculations) {
// Maximum fill amounts should be consistent.
int maximum_double_misalignment = kDoubleSize - kPointerSize;
int max_word_fill = Heap::GetMaximumFillToAlign(kWordAligned);
CHECK_EQ(0, max_word_fill);
int max_double_fill = Heap::GetMaximumFillToAlign(kDoubleAligned);
CHECK_EQ(maximum_double_misalignment, max_double_fill);
int max_double_unaligned_fill = Heap::GetMaximumFillToAlign(kDoubleUnaligned);
CHECK_EQ(maximum_double_misalignment, max_double_unaligned_fill);
Address base = reinterpret_cast<Address>(NULL);
int fill = 0;
// Word alignment never requires fill.
fill = Heap::GetFillToAlign(base, kWordAligned);
CHECK_EQ(0, fill);
fill = Heap::GetFillToAlign(base + kPointerSize, kWordAligned);
CHECK_EQ(0, fill);
// No fill is required when address is double aligned.
fill = Heap::GetFillToAlign(base, kDoubleAligned);
CHECK_EQ(0, fill);
// Fill is required if address is not double aligned.
fill = Heap::GetFillToAlign(base + kPointerSize, kDoubleAligned);
CHECK_EQ(maximum_double_misalignment, fill);
// kDoubleUnaligned has the opposite fill amounts.
fill = Heap::GetFillToAlign(base, kDoubleUnaligned);
CHECK_EQ(maximum_double_misalignment, fill);
fill = Heap::GetFillToAlign(base + kPointerSize, kDoubleUnaligned);
CHECK_EQ(0, fill);
}
static HeapObject* NewSpaceAllocateAligned(int size,
AllocationAlignment alignment) {
Heap* heap = CcTest::heap();
AllocationResult allocation =
heap->new_space()->AllocateRawAligned(size, alignment);
HeapObject* obj = NULL;
allocation.To(&obj);
heap->CreateFillerObjectAt(obj->address(), size);
return obj;
}
TEST(TestAlignedAllocation) {
// Double misalignment is 4 on 32-bit platforms, 0 on 64-bit ones.
const intptr_t double_misalignment = kDoubleSize - kPointerSize;
if (double_misalignment) {
// Allocate a pointer sized object that must be double aligned.
Address* top_addr = CcTest::heap()->new_space()->allocation_top_address();
Address start = *top_addr;
HeapObject* obj1 = NewSpaceAllocateAligned(kPointerSize, kDoubleAligned);
CHECK(IsAddressAligned(obj1->address(), kDoubleAlignment));
// Allocate a second pointer sized object. These two allocations should
// cause exactly one filler object to be created.
HeapObject* obj2 = NewSpaceAllocateAligned(kPointerSize, kDoubleAligned);
CHECK(IsAddressAligned(obj2->address(), kDoubleAlignment));
CHECK_EQ(2 * kPointerSize + double_misalignment, *top_addr - start);
// There should be 3 filler objects now (the two HeapObjects we created and
// the filler.)
CHECK(HeapObject::FromAddress(start)->IsFiller() &&
HeapObject::FromAddress(start + kPointerSize)->IsFiller() &&
HeapObject::FromAddress(start + 2 * kPointerSize)->IsFiller());
// Similarly for kDoubleUnaligned.
start = *top_addr;
obj1 = NewSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
CHECK(IsAddressAligned(obj1->address(), kDoubleAlignment, kPointerSize));
obj2 = NewSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
CHECK(IsAddressAligned(obj2->address(), kDoubleAlignment, kPointerSize));
CHECK_EQ(2 * kPointerSize + double_misalignment, *top_addr - start);
CHECK(HeapObject::FromAddress(start)->IsFiller() &&
HeapObject::FromAddress(start + kPointerSize)->IsFiller() &&
HeapObject::FromAddress(start + 2 * kPointerSize)->IsFiller());
}
}
// Force allocation to happen from the free list, at a desired misalignment.
static Address SetUpFreeListAllocation(int misalignment) {
Heap* heap = CcTest::heap();
OldSpace* old_space = heap->old_space();
Address top = old_space->top();
// First, allocate enough filler to get the linear area into the desired
// misalignment.
const intptr_t maximum_misalignment = 2 * kPointerSize;
const intptr_t maximum_misalignment_mask = maximum_misalignment - 1;
intptr_t top_alignment = OffsetFrom(top) & maximum_misalignment_mask;
int filler_size = misalignment - static_cast<int>(top_alignment);
if (filler_size < 0) filler_size += maximum_misalignment;
if (filler_size) {
// Create the filler object.
AllocationResult allocation = old_space->AllocateRawUnaligned(filler_size);
HeapObject* obj = NULL;
allocation.To(&obj);
heap->CreateFillerObjectAt(obj->address(), filler_size);
}
top = old_space->top();
old_space->EmptyAllocationInfo();
return top;
}
static HeapObject* OldSpaceAllocateAligned(int size,
AllocationAlignment alignment) {
Heap* heap = CcTest::heap();
AllocationResult allocation =
heap->old_space()->AllocateRawAligned(size, alignment);
HeapObject* obj = NULL;
allocation.To(&obj);
heap->CreateFillerObjectAt(obj->address(), size);
return obj;
}
// Test the case where allocation must be done from the free list, so filler
// may precede or follow the object.
TEST(TestAlignedOverAllocation) {
// Double misalignment is 4 on 32-bit platforms, 0 on 64-bit ones.
const intptr_t double_misalignment = kDoubleSize - kPointerSize;
if (double_misalignment) {
Address start = SetUpFreeListAllocation(0);
HeapObject* obj1 = OldSpaceAllocateAligned(kPointerSize, kDoubleAligned);
// The object should be aligned, and a filler object should be created.
CHECK(IsAddressAligned(obj1->address(), kDoubleAlignment));
CHECK(HeapObject::FromAddress(start)->IsFiller() &&
HeapObject::FromAddress(start + kPointerSize)->IsFiller());
// Try the opposite alignment case.
start = SetUpFreeListAllocation(kPointerSize);
HeapObject* obj2 = OldSpaceAllocateAligned(kPointerSize, kDoubleAligned);
CHECK(IsAddressAligned(obj2->address(), kDoubleAlignment));
CHECK(HeapObject::FromAddress(start)->IsFiller() &&
HeapObject::FromAddress(start + kPointerSize)->IsFiller());
// Similarly for kDoubleUnaligned.
start = SetUpFreeListAllocation(0);
obj1 = OldSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
// The object should be aligned, and a filler object should be created.
CHECK(IsAddressAligned(obj1->address(), kDoubleAlignment, kPointerSize));
CHECK(HeapObject::FromAddress(start)->IsFiller() &&
HeapObject::FromAddress(start + kPointerSize)->IsFiller());
// Try the opposite alignment case.
start = SetUpFreeListAllocation(kPointerSize);
obj2 = OldSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
CHECK(IsAddressAligned(obj2->address(), kDoubleAlignment, kPointerSize));
CHECK(HeapObject::FromAddress(start)->IsFiller() &&
HeapObject::FromAddress(start + kPointerSize)->IsFiller());
}
}
TEST(TestSizeOfObjectsVsHeapIteratorPrecision) {
CcTest::InitializeVM();
HeapIterator iterator(CcTest::heap());