Add SIMD 128 alignment support to Heap.
Adds SIMD 128 alignment sizes and masks. Adds support in Heap for SIMD alignments and fills. Reworks cctest so that each test independently aligns its allocation address, rather than depending on the previous tests ending state. Adds test cases for SIMD. LOG=N BUG=v8:4124 Review URL: https://codereview.chromium.org/1159453004 Cr-Commit-Position: refs/heads/master@{#28767}
This commit is contained in:
parent
4f9df26b09
commit
4347d56a69
@ -197,6 +197,8 @@ typedef int32_t uc32;
|
||||
const int kOneByteSize = kCharSize;
|
||||
const int kUC16Size = sizeof(uc16); // NOLINT
|
||||
|
||||
// 128 bit SIMD value size.
|
||||
const int kSimd128Size = 16;
|
||||
|
||||
// Round up n to be a multiple of sz, where sz is a power of 2.
|
||||
#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1))
|
||||
@ -309,6 +311,10 @@ const intptr_t kPointerAlignmentMask = kPointerAlignment - 1;
|
||||
const intptr_t kDoubleAlignment = 8;
|
||||
const intptr_t kDoubleAlignmentMask = kDoubleAlignment - 1;
|
||||
|
||||
// Desired alignment for 128 bit SIMD values.
|
||||
const intptr_t kSimd128Alignment = 16;
|
||||
const intptr_t kSimd128AlignmentMask = kSimd128Alignment - 1;
|
||||
|
||||
// Desired alignment for generated code is 32 bytes (to improve cache line
|
||||
// utilization).
|
||||
const int kCodeAlignmentBits = 5;
|
||||
@ -449,7 +455,12 @@ enum AllocationSpace {
|
||||
const int kSpaceTagSize = 3;
|
||||
const int kSpaceTagMask = (1 << kSpaceTagSize) - 1;
|
||||
|
||||
enum AllocationAlignment { kWordAligned, kDoubleAligned, kDoubleUnaligned };
|
||||
enum AllocationAlignment {
|
||||
kWordAligned,
|
||||
kDoubleAligned,
|
||||
kDoubleUnaligned,
|
||||
kSimd128Unaligned
|
||||
};
|
||||
|
||||
// A flag that indicates whether objects should be pretenured when
|
||||
// allocated (allocated directly into the old generation) or not
|
||||
|
@ -1995,6 +1995,8 @@ int Heap::GetMaximumFillToAlign(AllocationAlignment alignment) {
|
||||
case kDoubleAligned:
|
||||
case kDoubleUnaligned:
|
||||
return kDoubleSize - kPointerSize;
|
||||
case kSimd128Unaligned:
|
||||
return kSimd128Size - kPointerSize;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -2008,6 +2010,10 @@ int Heap::GetFillToAlign(Address address, AllocationAlignment alignment) {
|
||||
return kPointerSize;
|
||||
if (alignment == kDoubleUnaligned && (offset & kDoubleAlignmentMask) == 0)
|
||||
return kDoubleSize - kPointerSize; // No fill if double is always aligned.
|
||||
if (alignment == kSimd128Unaligned) {
|
||||
return (kSimd128Size - (static_cast<int>(offset) + kPointerSize)) &
|
||||
kSimd128AlignmentMask;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1784,14 +1784,18 @@ TEST(TestSizeOfObjects) {
|
||||
|
||||
|
||||
TEST(TestAlignmentCalculations) {
|
||||
// Maximum fill amounts should be consistent.
|
||||
// Maximum fill amounts are consistent.
|
||||
int maximum_double_misalignment = kDoubleSize - kPointerSize;
|
||||
int maximum_simd128_misalignment = kSimd128Size - 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);
|
||||
int max_simd128_unaligned_fill =
|
||||
Heap::GetMaximumFillToAlign(kSimd128Unaligned);
|
||||
CHECK_EQ(maximum_simd128_misalignment, max_simd128_unaligned_fill);
|
||||
|
||||
Address base = reinterpret_cast<Address>(NULL);
|
||||
int fill = 0;
|
||||
@ -1813,6 +1817,16 @@ TEST(TestAlignmentCalculations) {
|
||||
CHECK_EQ(maximum_double_misalignment, fill);
|
||||
fill = Heap::GetFillToAlign(base + kPointerSize, kDoubleUnaligned);
|
||||
CHECK_EQ(0, fill);
|
||||
|
||||
// 128 bit SIMD types have 2 or 4 possible alignments, depending on platform.
|
||||
fill = Heap::GetFillToAlign(base, kSimd128Unaligned);
|
||||
CHECK_EQ((3 * kPointerSize) & kSimd128AlignmentMask, fill);
|
||||
fill = Heap::GetFillToAlign(base + kPointerSize, kSimd128Unaligned);
|
||||
CHECK_EQ((2 * kPointerSize) & kSimd128AlignmentMask, fill);
|
||||
fill = Heap::GetFillToAlign(base + 2 * kPointerSize, kSimd128Unaligned);
|
||||
CHECK_EQ(kPointerSize, fill);
|
||||
fill = Heap::GetFillToAlign(base + 3 * kPointerSize, kSimd128Unaligned);
|
||||
CHECK_EQ(0, fill);
|
||||
}
|
||||
|
||||
|
||||
@ -1828,65 +1842,94 @@ static HeapObject* NewSpaceAllocateAligned(int size,
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
Address* top_addr = CcTest::heap()->new_space()->allocation_top_address();
|
||||
// Align the top for the first test.
|
||||
if (!IsAddressAligned(*top_addr, kDoubleAlignment))
|
||||
NewSpaceAllocateAligned(kPointerSize, kWordAligned);
|
||||
|
||||
// Allocate a pointer sized object that must be double aligned.
|
||||
Address start = *top_addr;
|
||||
HeapObject* obj1 = NewSpaceAllocateAligned(kPointerSize, kDoubleAligned);
|
||||
CHECK(IsAddressAligned(obj1->address(), kDoubleAlignment));
|
||||
// Only the object was allocated.
|
||||
CHECK_EQ(kPointerSize, *top_addr - start);
|
||||
// top is now misaligned.
|
||||
// Allocate a second pointer sized object that must be double aligned.
|
||||
HeapObject* obj2 = NewSpaceAllocateAligned(kPointerSize, kDoubleAligned);
|
||||
CHECK(IsAddressAligned(obj2->address(), kDoubleAlignment));
|
||||
// There should be a filler object in between the two objects.
|
||||
CHECK(HeapObject::FromAddress(start + kPointerSize)->IsFiller());
|
||||
// Two objects and a filler object were allocated.
|
||||
CHECK_EQ(2 * kPointerSize + double_misalignment, *top_addr - start);
|
||||
|
||||
// Similarly for kDoubleUnaligned. top is misaligned.
|
||||
start = *top_addr;
|
||||
obj1 = NewSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
|
||||
CHECK(IsAddressAligned(obj1->address(), kDoubleAlignment, kPointerSize));
|
||||
CHECK_EQ(kPointerSize, *top_addr - start);
|
||||
obj2 = NewSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
|
||||
CHECK(IsAddressAligned(obj2->address(), kDoubleAlignment, kPointerSize));
|
||||
CHECK(HeapObject::FromAddress(start + kPointerSize)->IsFiller());
|
||||
CHECK_EQ(2 * kPointerSize + double_misalignment, *top_addr - start);
|
||||
// Get new space allocation into the desired alignment.
|
||||
static Address AlignNewSpace(AllocationAlignment alignment, int offset) {
|
||||
Address* top_addr = CcTest::heap()->new_space()->allocation_top_address();
|
||||
int fill = Heap::GetFillToAlign(*top_addr, alignment);
|
||||
if (fill) {
|
||||
NewSpaceAllocateAligned(fill + offset, kWordAligned);
|
||||
}
|
||||
return *top_addr;
|
||||
}
|
||||
|
||||
|
||||
// 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);
|
||||
TEST(TestAlignedAllocation) {
|
||||
// Double misalignment is 4 on 32-bit platforms, 0 on 64-bit ones.
|
||||
const intptr_t double_misalignment = kDoubleSize - kPointerSize;
|
||||
Address* top_addr = CcTest::heap()->new_space()->allocation_top_address();
|
||||
Address start;
|
||||
HeapObject* obj;
|
||||
HeapObject* filler;
|
||||
if (double_misalignment) {
|
||||
// Allocate a pointer sized object that must be double aligned at an
|
||||
// aligned address.
|
||||
start = AlignNewSpace(kDoubleAligned, 0);
|
||||
obj = NewSpaceAllocateAligned(kPointerSize, kDoubleAligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kDoubleAlignment));
|
||||
// There is no filler.
|
||||
CHECK_EQ(kPointerSize, *top_addr - start);
|
||||
|
||||
// Allocate a second pointer sized object that must be double aligned at an
|
||||
// unaligned address.
|
||||
start = AlignNewSpace(kDoubleAligned, kPointerSize);
|
||||
obj = NewSpaceAllocateAligned(kPointerSize, kDoubleAligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kDoubleAlignment));
|
||||
// There is a filler object before the object.
|
||||
filler = HeapObject::FromAddress(start);
|
||||
CHECK(obj != filler && filler->IsFiller() &&
|
||||
filler->Size() == kPointerSize);
|
||||
CHECK_EQ(kPointerSize + double_misalignment, *top_addr - start);
|
||||
|
||||
// Similarly for kDoubleUnaligned.
|
||||
start = AlignNewSpace(kDoubleUnaligned, 0);
|
||||
obj = NewSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kDoubleAlignment, kPointerSize));
|
||||
CHECK_EQ(kPointerSize, *top_addr - start);
|
||||
start = AlignNewSpace(kDoubleUnaligned, kPointerSize);
|
||||
obj = NewSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kDoubleAlignment, kPointerSize));
|
||||
// There is a filler object before the object.
|
||||
filler = HeapObject::FromAddress(start);
|
||||
CHECK(obj != filler && filler->IsFiller() &&
|
||||
filler->Size() == kPointerSize);
|
||||
CHECK_EQ(kPointerSize + double_misalignment, *top_addr - start);
|
||||
}
|
||||
|
||||
// Now test SIMD alignment. There are 2 or 4 possible alignments, depending
|
||||
// on platform.
|
||||
start = AlignNewSpace(kSimd128Unaligned, 0);
|
||||
obj = NewSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
|
||||
// There is no filler.
|
||||
CHECK_EQ(kPointerSize, *top_addr - start);
|
||||
start = AlignNewSpace(kSimd128Unaligned, kPointerSize);
|
||||
obj = NewSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
|
||||
// There is a filler object before the object.
|
||||
filler = HeapObject::FromAddress(start);
|
||||
CHECK(obj != filler && filler->IsFiller() &&
|
||||
filler->Size() == kSimd128Size - kPointerSize);
|
||||
CHECK_EQ(kPointerSize + kSimd128Size - kPointerSize, *top_addr - start);
|
||||
|
||||
if (double_misalignment) {
|
||||
// Test the 2 other alignments possible on 32 bit platforms.
|
||||
start = AlignNewSpace(kSimd128Unaligned, 2 * kPointerSize);
|
||||
obj = NewSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
|
||||
// There is a filler object before the object.
|
||||
filler = HeapObject::FromAddress(start);
|
||||
CHECK(obj != filler && filler->IsFiller() &&
|
||||
filler->Size() == 2 * kPointerSize);
|
||||
CHECK_EQ(kPointerSize + 2 * kPointerSize, *top_addr - start);
|
||||
start = AlignNewSpace(kSimd128Unaligned, 3 * kPointerSize);
|
||||
obj = NewSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
|
||||
// There is a filler object before the object.
|
||||
filler = HeapObject::FromAddress(start);
|
||||
CHECK(obj != filler && filler->IsFiller() &&
|
||||
filler->Size() == kPointerSize);
|
||||
CHECK_EQ(kPointerSize + kPointerSize, *top_addr - start);
|
||||
}
|
||||
top = old_space->top();
|
||||
old_space->EmptyAllocationInfo();
|
||||
return top;
|
||||
}
|
||||
|
||||
|
||||
@ -1902,38 +1945,105 @@ static HeapObject* OldSpaceAllocateAligned(int size,
|
||||
}
|
||||
|
||||
|
||||
// Get old space allocation into the desired alignment.
|
||||
static Address AlignOldSpace(AllocationAlignment alignment, int offset) {
|
||||
Address* top_addr = CcTest::heap()->old_space()->allocation_top_address();
|
||||
int fill = Heap::GetFillToAlign(*top_addr, alignment);
|
||||
int allocation = fill + offset;
|
||||
if (allocation) {
|
||||
OldSpaceAllocateAligned(allocation, kWordAligned);
|
||||
}
|
||||
Address top = *top_addr;
|
||||
// Now force the remaining allocation onto the free list.
|
||||
CcTest::heap()->old_space()->EmptyAllocationInfo();
|
||||
return top;
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
Address start;
|
||||
HeapObject* obj;
|
||||
HeapObject* filler1;
|
||||
HeapObject* filler2;
|
||||
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());
|
||||
start = AlignOldSpace(kDoubleAligned, 0);
|
||||
obj = OldSpaceAllocateAligned(kPointerSize, kDoubleAligned);
|
||||
// The object is aligned, and a filler object is created after.
|
||||
CHECK(IsAddressAligned(obj->address(), kDoubleAlignment));
|
||||
filler1 = HeapObject::FromAddress(start + kPointerSize);
|
||||
CHECK(obj != filler1 && filler1->IsFiller() &&
|
||||
filler1->Size() == kPointerSize);
|
||||
// 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());
|
||||
start = AlignOldSpace(kDoubleAligned, kPointerSize);
|
||||
obj = OldSpaceAllocateAligned(kPointerSize, kDoubleAligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kDoubleAlignment));
|
||||
filler1 = HeapObject::FromAddress(start);
|
||||
CHECK(obj != filler1);
|
||||
CHECK(filler1->IsFiller());
|
||||
CHECK(filler1->Size() == kPointerSize);
|
||||
CHECK(obj != filler1 && filler1->IsFiller() &&
|
||||
filler1->Size() == kPointerSize);
|
||||
|
||||
// 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());
|
||||
start = AlignOldSpace(kDoubleUnaligned, 0);
|
||||
obj = OldSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
|
||||
// The object is aligned, and a filler object is created after.
|
||||
CHECK(IsAddressAligned(obj->address(), kDoubleAlignment, kPointerSize));
|
||||
filler1 = HeapObject::FromAddress(start + kPointerSize);
|
||||
CHECK(obj != filler1 && filler1->IsFiller() &&
|
||||
filler1->Size() == kPointerSize);
|
||||
// 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());
|
||||
start = AlignOldSpace(kDoubleUnaligned, kPointerSize);
|
||||
obj = OldSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kDoubleAlignment, kPointerSize));
|
||||
filler1 = HeapObject::FromAddress(start);
|
||||
CHECK(obj != filler1 && filler1->IsFiller() &&
|
||||
filler1->Size() == kPointerSize);
|
||||
}
|
||||
|
||||
// Now test SIMD alignment. There are 2 or 4 possible alignments, depending
|
||||
// on platform.
|
||||
start = AlignOldSpace(kSimd128Unaligned, 0);
|
||||
obj = OldSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
|
||||
// There is a filler object after the object.
|
||||
filler1 = HeapObject::FromAddress(start + kPointerSize);
|
||||
CHECK(obj != filler1 && filler1->IsFiller() &&
|
||||
filler1->Size() == kSimd128Size - kPointerSize);
|
||||
start = AlignOldSpace(kSimd128Unaligned, kPointerSize);
|
||||
obj = OldSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
|
||||
// There is a filler object before the object.
|
||||
filler1 = HeapObject::FromAddress(start);
|
||||
CHECK(obj != filler1 && filler1->IsFiller() &&
|
||||
filler1->Size() == kSimd128Size - kPointerSize);
|
||||
|
||||
if (double_misalignment) {
|
||||
// Test the 2 other alignments possible on 32 bit platforms.
|
||||
start = AlignOldSpace(kSimd128Unaligned, 2 * kPointerSize);
|
||||
obj = OldSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
|
||||
// There are filler objects before and after the object.
|
||||
filler1 = HeapObject::FromAddress(start);
|
||||
CHECK(obj != filler1 && filler1->IsFiller() &&
|
||||
filler1->Size() == 2 * kPointerSize);
|
||||
filler2 = HeapObject::FromAddress(start + 3 * kPointerSize);
|
||||
CHECK(obj != filler2 && filler2->IsFiller() &&
|
||||
filler2->Size() == kPointerSize);
|
||||
start = AlignOldSpace(kSimd128Unaligned, 3 * kPointerSize);
|
||||
obj = OldSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
|
||||
CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
|
||||
// There are filler objects before and after the object.
|
||||
filler1 = HeapObject::FromAddress(start);
|
||||
CHECK(obj != filler1 && filler1->IsFiller() &&
|
||||
filler1->Size() == kPointerSize);
|
||||
filler2 = HeapObject::FromAddress(start + 2 * kPointerSize);
|
||||
CHECK(obj != filler2 && filler2->IsFiller() &&
|
||||
filler2->Size() == 2 * kPointerSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user