|  | // Copyright 2015 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. | 
|  |  | 
|  | #include <vector> | 
|  |  | 
|  | #include "src/common/globals.h" | 
|  | #include "src/heap/heap-inl.h" | 
|  | #include "src/heap/spaces-inl.h" | 
|  | #include "src/objects/objects.h" | 
|  | #include "test/cctest/cctest.h" | 
|  |  | 
|  | namespace v8 { | 
|  | namespace internal { | 
|  | namespace heap { | 
|  |  | 
|  | static Address AllocateLabBackingStore(Heap* heap, intptr_t size_in_bytes) { | 
|  | AllocationResult result = heap->old_space()->AllocateRaw( | 
|  | static_cast<int>(size_in_bytes), kDoubleAligned); | 
|  | Address adr = result.ToObjectChecked().address(); | 
|  | return adr; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void VerifyIterable(v8::internal::Address base, | 
|  | v8::internal::Address limit, | 
|  | std::vector<intptr_t> expected_size) { | 
|  | CHECK_LE(base, limit); | 
|  | HeapObject object; | 
|  | size_t counter = 0; | 
|  | while (base < limit) { | 
|  | object = HeapObject::FromAddress(base); | 
|  | CHECK(object.IsFreeSpaceOrFiller()); | 
|  | CHECK_LT(counter, expected_size.size()); | 
|  | CHECK_EQ(expected_size[counter], object.Size()); | 
|  | base += object.Size(); | 
|  | counter++; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static bool AllocateFromLab(Heap* heap, LocalAllocationBuffer* lab, | 
|  | intptr_t size_in_bytes, | 
|  | AllocationAlignment alignment = kWordAligned) { | 
|  | HeapObject obj; | 
|  | AllocationResult result = | 
|  | lab->AllocateRawAligned(static_cast<int>(size_in_bytes), alignment); | 
|  | if (result.To(&obj)) { | 
|  | heap->CreateFillerObjectAt(obj.address(), static_cast<int>(size_in_bytes), | 
|  | ClearRecordedSlots::kNo); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  |  | 
|  | TEST(InvalidLab) { | 
|  | LocalAllocationBuffer lab = LocalAllocationBuffer::InvalidBuffer(); | 
|  | CHECK(!lab.IsValid()); | 
|  | } | 
|  |  | 
|  |  | 
|  | TEST(UnusedLabImplicitClose) { | 
|  | CcTest::InitializeVM(); | 
|  | Heap* heap = CcTest::heap(); | 
|  | const int kLabSize = 4 * KB; | 
|  | Address base = AllocateLabBackingStore(heap, kLabSize); | 
|  | Address limit = base + kLabSize; | 
|  | intptr_t expected_sizes_raw[1] = {kLabSize}; | 
|  | std::vector<intptr_t> expected_sizes(expected_sizes_raw, | 
|  | expected_sizes_raw + 1); | 
|  | { | 
|  | AllocationResult lab_backing_store(HeapObject::FromAddress(base)); | 
|  | LocalAllocationBuffer lab = | 
|  | LocalAllocationBuffer::FromResult(heap, lab_backing_store, kLabSize); | 
|  | CHECK(lab.IsValid()); | 
|  | } | 
|  | VerifyIterable(base, limit, expected_sizes); | 
|  | } | 
|  |  | 
|  |  | 
|  | TEST(SimpleAllocate) { | 
|  | CcTest::InitializeVM(); | 
|  | Heap* heap = CcTest::heap(); | 
|  | const int kLabSize = 4 * KB; | 
|  | Address base = AllocateLabBackingStore(heap, kLabSize); | 
|  | Address limit = base + kLabSize; | 
|  | intptr_t sizes_raw[1] = {128}; | 
|  | intptr_t expected_sizes_raw[2] = {128, kLabSize - 128}; | 
|  | std::vector<intptr_t> sizes(sizes_raw, sizes_raw + 1); | 
|  | std::vector<intptr_t> expected_sizes(expected_sizes_raw, | 
|  | expected_sizes_raw + 2); | 
|  | { | 
|  | AllocationResult lab_backing_store(HeapObject::FromAddress(base)); | 
|  | LocalAllocationBuffer lab = | 
|  | LocalAllocationBuffer::FromResult(heap, lab_backing_store, kLabSize); | 
|  | CHECK(lab.IsValid()); | 
|  | intptr_t sum = 0; | 
|  | for (auto size : sizes) { | 
|  | if (AllocateFromLab(heap, &lab, size)) { | 
|  | sum += size; | 
|  | } | 
|  | } | 
|  | } | 
|  | VerifyIterable(base, limit, expected_sizes); | 
|  | } | 
|  |  | 
|  |  | 
|  | TEST(AllocateUntilLabOOM) { | 
|  | CcTest::InitializeVM(); | 
|  | Heap* heap = CcTest::heap(); | 
|  | const int kLabSize = 2 * KB; | 
|  | Address base = AllocateLabBackingStore(heap, kLabSize); | 
|  | Address limit = base + kLabSize; | 
|  | // The following objects won't fit in {kLabSize}. | 
|  | intptr_t sizes_raw[5] = {512, 512, 128, 512, 512}; | 
|  | intptr_t expected_sizes_raw[5] = {512, 512, 128, 512, 384 /* left over */}; | 
|  | std::vector<intptr_t> sizes(sizes_raw, sizes_raw + 5); | 
|  | std::vector<intptr_t> expected_sizes(expected_sizes_raw, | 
|  | expected_sizes_raw + 5); | 
|  | intptr_t sum = 0; | 
|  | { | 
|  | AllocationResult lab_backing_store(HeapObject::FromAddress(base)); | 
|  | LocalAllocationBuffer lab = | 
|  | LocalAllocationBuffer::FromResult(heap, lab_backing_store, kLabSize); | 
|  | CHECK(lab.IsValid()); | 
|  | for (auto size : sizes) { | 
|  | if (AllocateFromLab(heap, &lab, size)) { | 
|  | sum += size; | 
|  | } | 
|  | } | 
|  | CHECK_EQ(kLabSize - sum, 384); | 
|  | } | 
|  | VerifyIterable(base, limit, expected_sizes); | 
|  | } | 
|  |  | 
|  |  | 
|  | TEST(AllocateExactlyUntilLimit) { | 
|  | CcTest::InitializeVM(); | 
|  | Heap* heap = CcTest::heap(); | 
|  | const int kLabSize = 2 * KB; | 
|  | Address base = AllocateLabBackingStore(heap, kLabSize); | 
|  | Address limit = base + kLabSize; | 
|  | intptr_t sizes_raw[4] = {512, 512, 512, 512}; | 
|  | intptr_t expected_sizes_raw[5] = {512, 512, 512, 512, 0}; | 
|  | std::vector<intptr_t> sizes(sizes_raw, sizes_raw + 4); | 
|  | std::vector<intptr_t> expected_sizes(expected_sizes_raw, | 
|  | expected_sizes_raw + 5); | 
|  | { | 
|  | AllocationResult lab_backing_store(HeapObject::FromAddress(base)); | 
|  | LocalAllocationBuffer lab = | 
|  | LocalAllocationBuffer::FromResult(heap, lab_backing_store, kLabSize); | 
|  | CHECK(lab.IsValid()); | 
|  | intptr_t sum = 0; | 
|  | for (auto size : sizes) { | 
|  | if (AllocateFromLab(heap, &lab, size)) { | 
|  | sum += size; | 
|  | } else { | 
|  | break; | 
|  | } | 
|  | } | 
|  | CHECK_EQ(kLabSize - sum, 0); | 
|  | } | 
|  | VerifyIterable(base, limit, expected_sizes); | 
|  | } | 
|  |  | 
|  |  | 
|  | TEST(MergeSuccessful) { | 
|  | CcTest::InitializeVM(); | 
|  | Heap* heap = CcTest::heap(); | 
|  | const int kLabSize = 2 * KB; | 
|  | Address base1 = AllocateLabBackingStore(heap, 2 * kLabSize); | 
|  | Address limit1 = base1 + kLabSize; | 
|  | Address base2 = limit1; | 
|  | Address limit2 = base2 + kLabSize; | 
|  |  | 
|  | intptr_t sizes1_raw[4] = {512, 512, 512, 256}; | 
|  | intptr_t expected_sizes1_raw[5] = {512, 512, 512, 256, 256}; | 
|  | std::vector<intptr_t> sizes1(sizes1_raw, sizes1_raw + 4); | 
|  | std::vector<intptr_t> expected_sizes1(expected_sizes1_raw, | 
|  | expected_sizes1_raw + 5); | 
|  |  | 
|  | intptr_t sizes2_raw[5] = {256, 512, 512, 512, 512}; | 
|  | intptr_t expected_sizes2_raw[10] = {512, 512, 512, 256, 256, | 
|  | 512, 512, 512, 512, 0}; | 
|  | std::vector<intptr_t> sizes2(sizes2_raw, sizes2_raw + 5); | 
|  | std::vector<intptr_t> expected_sizes2(expected_sizes2_raw, | 
|  | expected_sizes2_raw + 10); | 
|  |  | 
|  | { | 
|  | AllocationResult lab_backing_store1(HeapObject::FromAddress(base1)); | 
|  | LocalAllocationBuffer lab1 = | 
|  | LocalAllocationBuffer::FromResult(heap, lab_backing_store1, kLabSize); | 
|  | CHECK(lab1.IsValid()); | 
|  | intptr_t sum = 0; | 
|  | for (auto size : sizes1) { | 
|  | if (AllocateFromLab(heap, &lab1, size)) { | 
|  | sum += size; | 
|  | } else { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | AllocationResult lab_backing_store2(HeapObject::FromAddress(base2)); | 
|  | LocalAllocationBuffer lab2 = | 
|  | LocalAllocationBuffer::FromResult(heap, lab_backing_store2, kLabSize); | 
|  | CHECK(lab2.IsValid()); | 
|  | CHECK(lab2.TryMerge(&lab1)); | 
|  | CHECK(!lab1.IsValid()); | 
|  | for (auto size : sizes2) { | 
|  | if (AllocateFromLab(heap, &lab2, size)) { | 
|  | sum += size; | 
|  | } else { | 
|  | break; | 
|  | } | 
|  | } | 
|  | CHECK_EQ(2 * kLabSize - sum, 0); | 
|  | } | 
|  | VerifyIterable(base1, limit1, expected_sizes1); | 
|  | VerifyIterable(base1, limit2, expected_sizes2); | 
|  | } | 
|  |  | 
|  |  | 
|  | TEST(MergeFailed) { | 
|  | CcTest::InitializeVM(); | 
|  | Heap* heap = CcTest::heap(); | 
|  | const int kLabSize = 2 * KB; | 
|  | Address base1 = AllocateLabBackingStore(heap, 3 * kLabSize); | 
|  | Address base2 = base1 + kLabSize; | 
|  | Address base3 = base2 + kLabSize; | 
|  |  | 
|  | { | 
|  | AllocationResult lab_backing_store1(HeapObject::FromAddress(base1)); | 
|  | LocalAllocationBuffer lab1 = | 
|  | LocalAllocationBuffer::FromResult(heap, lab_backing_store1, kLabSize); | 
|  | CHECK(lab1.IsValid()); | 
|  |  | 
|  | AllocationResult lab_backing_store2(HeapObject::FromAddress(base2)); | 
|  | LocalAllocationBuffer lab2 = | 
|  | LocalAllocationBuffer::FromResult(heap, lab_backing_store2, kLabSize); | 
|  | CHECK(lab2.IsValid()); | 
|  |  | 
|  | AllocationResult lab_backing_store3(HeapObject::FromAddress(base3)); | 
|  | LocalAllocationBuffer lab3 = | 
|  | LocalAllocationBuffer::FromResult(heap, lab_backing_store3, kLabSize); | 
|  | CHECK(lab3.IsValid()); | 
|  |  | 
|  | CHECK(!lab3.TryMerge(&lab1)); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | #ifdef V8_HOST_ARCH_32_BIT | 
|  | TEST(AllocateAligned) { | 
|  | CcTest::InitializeVM(); | 
|  | Heap* heap = CcTest::heap(); | 
|  | const int kLabSize = 2 * KB; | 
|  | Address base = AllocateLabBackingStore(heap, kLabSize); | 
|  | Address limit = base + kLabSize; | 
|  | std::pair<intptr_t, AllocationAlignment> sizes_raw[2] = { | 
|  | std::make_pair(116, kWordAligned), std::make_pair(64, kDoubleAligned)}; | 
|  | std::vector<std::pair<intptr_t, AllocationAlignment>> sizes(sizes_raw, | 
|  | sizes_raw + 2); | 
|  | intptr_t expected_sizes_raw[4] = {116, 4, 64, 1864}; | 
|  | std::vector<intptr_t> expected_sizes(expected_sizes_raw, | 
|  | expected_sizes_raw + 4); | 
|  |  | 
|  | { | 
|  | AllocationResult lab_backing_store(HeapObject::FromAddress(base)); | 
|  | LocalAllocationBuffer lab = | 
|  | LocalAllocationBuffer::FromResult(heap, lab_backing_store, kLabSize); | 
|  | CHECK(lab.IsValid()); | 
|  | for (auto pair : sizes) { | 
|  | if (!AllocateFromLab(heap, &lab, pair.first, pair.second)) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | VerifyIterable(base, limit, expected_sizes); | 
|  | } | 
|  | #endif  // V8_HOST_ARCH_32_BIT | 
|  |  | 
|  | }  // namespace heap | 
|  | }  // namespace internal | 
|  | }  // namespace v8 |