Add SIMD 128 alignment support to Heap.
authorbbudge <bbudge@chromium.org>
Tue, 2 Jun 2015 22:56:00 +0000 (15:56 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 2 Jun 2015 22:56:14 +0000 (22:56 +0000)
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

Committed: https://crrev.com/4347d56a6919ae06a70e4a4a8b2f1179cf47bc7e
Cr-Commit-Position: refs/heads/master@{#28767}

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

Cr-Commit-Position: refs/heads/master@{#28771}

src/globals.h
src/heap/heap.cc
src/heap/spaces-inl.h
test/cctest/test-heap.cc

index 23cdbedb6ebcbe98ba49399d619581270fe4da44..1a20909f0c3f04aa27d71a6a9fb6bd679024261e 100644 (file)
@@ -202,6 +202,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))
@@ -314,6 +316,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;
@@ -454,7 +460,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
index 47fef8c5a2edbb296612a38e08afe7afea7a9e94..4a508fb078f3a68ef965ddc664834964e07898dd 100644 (file)
@@ -1991,6 +1991,8 @@ int Heap::GetMaximumFillToAlign(AllocationAlignment alignment) {
     case kDoubleAligned:
     case kDoubleUnaligned:
       return kDoubleSize - kPointerSize;
+    case kSimd128Unaligned:
+      return kSimd128Size - kPointerSize;
     default:
       UNREACHABLE();
   }
@@ -2004,6 +2006,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;
 }
 
index 0cbabbaf683f9327c8fc4b6eae2ddf0adb65b190..c2c4d126976336dd4348d0e76c4575b3f9748218 100644 (file)
@@ -311,6 +311,9 @@ AllocationResult PagedSpace::AllocateRawAligned(int size_in_bytes,
     if (object != NULL && filler_size != 0) {
       object = heap()->AlignWithFiller(object, size_in_bytes, allocation_size,
                                        alignment);
+      // Filler objects are initialized, so mark only the aligned object memory
+      // as uninitialized.
+      allocation_size = size_in_bytes;
     }
   }
 
index a9cb60e985d3c96c2d99e6d2cacdcca6ab764c23..9e3c80ec39b766ec35892dba25cbe134d2b21cc2 100644 (file)
@@ -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,
 }
 
 
+// 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;
+}
+
+
 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) {
-    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.
+    // 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);
-    // 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);
-  }
-}
 
+    // 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);
 
-// 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;
+    // 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);
+  }
 }
 
 
@@ -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);
   }
 }