1c2d882ddSReid Kleckner //===- llvm/unittest/Support/AllocatorTest.cpp - BumpPtrAllocator tests ---===//
2c2d882ddSReid Kleckner //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c2d882ddSReid Kleckner //
7c2d882ddSReid Kleckner //===----------------------------------------------------------------------===//
8c2d882ddSReid Kleckner
9c2d882ddSReid Kleckner #include "llvm/Support/Allocator.h"
10c2d882ddSReid Kleckner #include "gtest/gtest.h"
114b1f2f47SReid Kleckner #include <cstdlib>
12c2d882ddSReid Kleckner
13c2d882ddSReid Kleckner using namespace llvm;
14c2d882ddSReid Kleckner
15c2d882ddSReid Kleckner namespace {
16c2d882ddSReid Kleckner
TEST(AllocatorTest,Basics)17c2d882ddSReid Kleckner TEST(AllocatorTest, Basics) {
18c2d882ddSReid Kleckner BumpPtrAllocator Alloc;
19ca9ca544SJordan Rose int *a = (int*)Alloc.Allocate(sizeof(int), alignof(int));
20ca9ca544SJordan Rose int *b = (int*)Alloc.Allocate(sizeof(int) * 10, alignof(int));
21ca9ca544SJordan Rose int *c = (int*)Alloc.Allocate(sizeof(int), alignof(int));
22c2d882ddSReid Kleckner *a = 1;
23c2d882ddSReid Kleckner b[0] = 2;
24c2d882ddSReid Kleckner b[9] = 2;
25c2d882ddSReid Kleckner *c = 3;
26c2d882ddSReid Kleckner EXPECT_EQ(1, *a);
27c2d882ddSReid Kleckner EXPECT_EQ(2, b[0]);
28c2d882ddSReid Kleckner EXPECT_EQ(2, b[9]);
29c2d882ddSReid Kleckner EXPECT_EQ(3, *c);
30c2d882ddSReid Kleckner EXPECT_EQ(1U, Alloc.GetNumSlabs());
310e31ed90SChandler Carruth
320e31ed90SChandler Carruth BumpPtrAllocator Alloc2 = std::move(Alloc);
330e31ed90SChandler Carruth EXPECT_EQ(0U, Alloc.GetNumSlabs());
340e31ed90SChandler Carruth EXPECT_EQ(1U, Alloc2.GetNumSlabs());
350e31ed90SChandler Carruth
360e31ed90SChandler Carruth // Make sure the old pointers still work. These are especially interesting
370e31ed90SChandler Carruth // under ASan or Valgrind.
380e31ed90SChandler Carruth EXPECT_EQ(1, *a);
390e31ed90SChandler Carruth EXPECT_EQ(2, b[0]);
400e31ed90SChandler Carruth EXPECT_EQ(2, b[9]);
410e31ed90SChandler Carruth EXPECT_EQ(3, *c);
420e31ed90SChandler Carruth
430e31ed90SChandler Carruth Alloc = std::move(Alloc2);
440e31ed90SChandler Carruth EXPECT_EQ(0U, Alloc2.GetNumSlabs());
450e31ed90SChandler Carruth EXPECT_EQ(1U, Alloc.GetNumSlabs());
46c2d882ddSReid Kleckner }
47c2d882ddSReid Kleckner
48c2d882ddSReid Kleckner // Allocate enough bytes to create three slabs.
TEST(AllocatorTest,ThreeSlabs)49c2d882ddSReid Kleckner TEST(AllocatorTest, ThreeSlabs) {
50a05a221eSChandler Carruth BumpPtrAllocator Alloc;
51fd1f0f17SHans Wennborg Alloc.Allocate(3000, 1);
52c2d882ddSReid Kleckner EXPECT_EQ(1U, Alloc.GetNumSlabs());
53fd1f0f17SHans Wennborg Alloc.Allocate(3000, 1);
54c2d882ddSReid Kleckner EXPECT_EQ(2U, Alloc.GetNumSlabs());
55fd1f0f17SHans Wennborg Alloc.Allocate(3000, 1);
56c2d882ddSReid Kleckner EXPECT_EQ(3U, Alloc.GetNumSlabs());
57c2d882ddSReid Kleckner }
58c2d882ddSReid Kleckner
59c2d882ddSReid Kleckner // Allocate enough bytes to create two slabs, reset the allocator, and do it
60c2d882ddSReid Kleckner // again.
TEST(AllocatorTest,TestReset)61c2d882ddSReid Kleckner TEST(AllocatorTest, TestReset) {
62a05a221eSChandler Carruth BumpPtrAllocator Alloc;
63da5ddff7SHans Wennborg
64da5ddff7SHans Wennborg // Allocate something larger than the SizeThreshold=4096.
65da5ddff7SHans Wennborg (void)Alloc.Allocate(5000, 1);
66da5ddff7SHans Wennborg Alloc.Reset();
67da5ddff7SHans Wennborg // Calling Reset should free all CustomSizedSlabs.
68da5ddff7SHans Wennborg EXPECT_EQ(0u, Alloc.GetNumSlabs());
69da5ddff7SHans Wennborg
70fd1f0f17SHans Wennborg Alloc.Allocate(3000, 1);
71c2d882ddSReid Kleckner EXPECT_EQ(1U, Alloc.GetNumSlabs());
72fd1f0f17SHans Wennborg Alloc.Allocate(3000, 1);
73c2d882ddSReid Kleckner EXPECT_EQ(2U, Alloc.GetNumSlabs());
74c2d882ddSReid Kleckner Alloc.Reset();
75c2d882ddSReid Kleckner EXPECT_EQ(1U, Alloc.GetNumSlabs());
76fd1f0f17SHans Wennborg Alloc.Allocate(3000, 1);
77c2d882ddSReid Kleckner EXPECT_EQ(1U, Alloc.GetNumSlabs());
78fd1f0f17SHans Wennborg Alloc.Allocate(3000, 1);
79c2d882ddSReid Kleckner EXPECT_EQ(2U, Alloc.GetNumSlabs());
80c2d882ddSReid Kleckner }
81c2d882ddSReid Kleckner
82c2d882ddSReid Kleckner // Test some allocations at varying alignments.
TEST(AllocatorTest,TestAlignment)83c2d882ddSReid Kleckner TEST(AllocatorTest, TestAlignment) {
84c2d882ddSReid Kleckner BumpPtrAllocator Alloc;
85c2d882ddSReid Kleckner uintptr_t a;
86c2d882ddSReid Kleckner a = (uintptr_t)Alloc.Allocate(1, 2);
87c2d882ddSReid Kleckner EXPECT_EQ(0U, a & 1);
88c2d882ddSReid Kleckner a = (uintptr_t)Alloc.Allocate(1, 4);
89c2d882ddSReid Kleckner EXPECT_EQ(0U, a & 3);
90c2d882ddSReid Kleckner a = (uintptr_t)Alloc.Allocate(1, 8);
91c2d882ddSReid Kleckner EXPECT_EQ(0U, a & 7);
92c2d882ddSReid Kleckner a = (uintptr_t)Alloc.Allocate(1, 16);
93c2d882ddSReid Kleckner EXPECT_EQ(0U, a & 15);
94c2d882ddSReid Kleckner a = (uintptr_t)Alloc.Allocate(1, 32);
95c2d882ddSReid Kleckner EXPECT_EQ(0U, a & 31);
96c2d882ddSReid Kleckner a = (uintptr_t)Alloc.Allocate(1, 64);
97c2d882ddSReid Kleckner EXPECT_EQ(0U, a & 63);
98c2d882ddSReid Kleckner a = (uintptr_t)Alloc.Allocate(1, 128);
99c2d882ddSReid Kleckner EXPECT_EQ(0U, a & 127);
100c2d882ddSReid Kleckner }
101c2d882ddSReid Kleckner
102ba0d50adSSam McCall // Test zero-sized allocations.
103ba0d50adSSam McCall // In general we don't need to allocate memory for these.
104ba0d50adSSam McCall // However Allocate never returns null, so if the first allocation is zero-sized
105ba0d50adSSam McCall // we end up creating a slab for it.
TEST(AllocatorTest,TestZero)106ba0d50adSSam McCall TEST(AllocatorTest, TestZero) {
107ba0d50adSSam McCall BumpPtrAllocator Alloc;
108*56ee5d93SSam McCall Alloc.setRedZoneSize(0); // else our arithmetic is all off
109ba0d50adSSam McCall EXPECT_EQ(0u, Alloc.GetNumSlabs());
110ba0d50adSSam McCall EXPECT_EQ(0u, Alloc.getBytesAllocated());
111ba0d50adSSam McCall
112ba0d50adSSam McCall void *Empty = Alloc.Allocate(0, 1);
113ba0d50adSSam McCall EXPECT_NE(Empty, nullptr) << "Allocate is __attribute__((returns_nonnull))";
114ba0d50adSSam McCall EXPECT_EQ(1u, Alloc.GetNumSlabs()) << "Allocated a slab to point to";
115ba0d50adSSam McCall EXPECT_EQ(0u, Alloc.getBytesAllocated());
116ba0d50adSSam McCall
117ba0d50adSSam McCall void *Large = Alloc.Allocate(4096, 1);
118ba0d50adSSam McCall EXPECT_EQ(1u, Alloc.GetNumSlabs());
119ba0d50adSSam McCall EXPECT_EQ(4096u, Alloc.getBytesAllocated());
120ba0d50adSSam McCall EXPECT_EQ(Empty, Large);
121ba0d50adSSam McCall
122ba0d50adSSam McCall void *Empty2 = Alloc.Allocate(0, 1);
123ba0d50adSSam McCall EXPECT_NE(Empty2, nullptr);
124ba0d50adSSam McCall EXPECT_EQ(1u, Alloc.GetNumSlabs());
125ba0d50adSSam McCall EXPECT_EQ(4096u, Alloc.getBytesAllocated());
126ba0d50adSSam McCall }
127ba0d50adSSam McCall
128c2d882ddSReid Kleckner // Test allocating just over the slab size. This tests a bug where before the
129c2d882ddSReid Kleckner // allocator incorrectly calculated the buffer end pointer.
TEST(AllocatorTest,TestOverflow)130c2d882ddSReid Kleckner TEST(AllocatorTest, TestOverflow) {
131a05a221eSChandler Carruth BumpPtrAllocator Alloc;
132c2d882ddSReid Kleckner
133c2d882ddSReid Kleckner // Fill the slab right up until the end pointer.
134fd1f0f17SHans Wennborg Alloc.Allocate(4096, 1);
135c2d882ddSReid Kleckner EXPECT_EQ(1U, Alloc.GetNumSlabs());
136c2d882ddSReid Kleckner
13701b443fdSDan Gohman // If we don't allocate a new slab, then we will have overflowed.
138fd1f0f17SHans Wennborg Alloc.Allocate(1, 1);
139c2d882ddSReid Kleckner EXPECT_EQ(2U, Alloc.GetNumSlabs());
140c2d882ddSReid Kleckner }
141c2d882ddSReid Kleckner
14216558f4dSArgyrios Kyrtzidis // Test allocating with a size larger than the initial slab size.
TEST(AllocatorTest,TestSmallSlabSize)14316558f4dSArgyrios Kyrtzidis TEST(AllocatorTest, TestSmallSlabSize) {
144a05a221eSChandler Carruth BumpPtrAllocator Alloc;
14516558f4dSArgyrios Kyrtzidis
146fd1f0f17SHans Wennborg Alloc.Allocate(8000, 1);
147d47b1d76SHans Wennborg EXPECT_EQ(1U, Alloc.GetNumSlabs());
14816558f4dSArgyrios Kyrtzidis }
14916558f4dSArgyrios Kyrtzidis
15044e27464SHans Wennborg // Test requesting alignment that goes past the end of the current slab.
TEST(AllocatorTest,TestAlignmentPastSlab)15144e27464SHans Wennborg TEST(AllocatorTest, TestAlignmentPastSlab) {
15244e27464SHans Wennborg BumpPtrAllocator Alloc;
153e5a96a5cSHans Wennborg Alloc.Allocate(4095, 1);
15444e27464SHans Wennborg
155e5a96a5cSHans Wennborg // Aligning the current slab pointer is likely to move it past the end of the
156e5a96a5cSHans Wennborg // slab, which would confuse any unsigned comparisons with the difference of
157572e03a3SEric Christopher // the end pointer and the aligned pointer.
15844e27464SHans Wennborg Alloc.Allocate(1024, 8192);
15944e27464SHans Wennborg
16044e27464SHans Wennborg EXPECT_EQ(2U, Alloc.GetNumSlabs());
16144e27464SHans Wennborg }
16244e27464SHans Wennborg
16346e5603cSRaphael Isemann // Test allocating with a decreased growth delay.
TEST(AllocatorTest,TestFasterSlabGrowthDelay)16446e5603cSRaphael Isemann TEST(AllocatorTest, TestFasterSlabGrowthDelay) {
16546e5603cSRaphael Isemann const size_t SlabSize = 4096;
16646e5603cSRaphael Isemann // Decrease the growth delay to double the slab size every slab.
16746e5603cSRaphael Isemann const size_t GrowthDelay = 1;
16846e5603cSRaphael Isemann BumpPtrAllocatorImpl<MallocAllocator, SlabSize, SlabSize, GrowthDelay> Alloc;
16946e5603cSRaphael Isemann // Disable the red zone for this test. The additional bytes allocated for the
17046e5603cSRaphael Isemann // red zone would change the allocation numbers we check below.
17146e5603cSRaphael Isemann Alloc.setRedZoneSize(0);
17246e5603cSRaphael Isemann
17346e5603cSRaphael Isemann Alloc.Allocate(SlabSize, 1);
17446e5603cSRaphael Isemann EXPECT_EQ(SlabSize, Alloc.getTotalMemory());
17546e5603cSRaphael Isemann // We hit our growth delay with the previous allocation so the next
17646e5603cSRaphael Isemann // allocation should get a twice as large slab.
17746e5603cSRaphael Isemann Alloc.Allocate(SlabSize, 1);
17846e5603cSRaphael Isemann EXPECT_EQ(SlabSize * 3, Alloc.getTotalMemory());
17946e5603cSRaphael Isemann Alloc.Allocate(SlabSize, 1);
18046e5603cSRaphael Isemann EXPECT_EQ(SlabSize * 3, Alloc.getTotalMemory());
18146e5603cSRaphael Isemann
18246e5603cSRaphael Isemann // Both slabs are full again and hit the growth delay again, so the
18346e5603cSRaphael Isemann // next allocation should again get a slab with four times the size of the
18446e5603cSRaphael Isemann // original slab size. In total we now should have a memory size of:
18546e5603cSRaphael Isemann // 1 + 2 + 4 * SlabSize.
18646e5603cSRaphael Isemann Alloc.Allocate(SlabSize, 1);
18746e5603cSRaphael Isemann EXPECT_EQ(SlabSize * 7, Alloc.getTotalMemory());
18846e5603cSRaphael Isemann }
18946e5603cSRaphael Isemann
19046e5603cSRaphael Isemann // Test allocating with a increased growth delay.
TEST(AllocatorTest,TestSlowerSlabGrowthDelay)19146e5603cSRaphael Isemann TEST(AllocatorTest, TestSlowerSlabGrowthDelay) {
19246e5603cSRaphael Isemann const size_t SlabSize = 16;
19346e5603cSRaphael Isemann // Increase the growth delay to only double the slab size every 256 slabs.
19446e5603cSRaphael Isemann const size_t GrowthDelay = 256;
19546e5603cSRaphael Isemann BumpPtrAllocatorImpl<MallocAllocator, SlabSize, SlabSize, GrowthDelay> Alloc;
19646e5603cSRaphael Isemann // Disable the red zone for this test. The additional bytes allocated for the
19746e5603cSRaphael Isemann // red zone would change the allocation numbers we check below.
19846e5603cSRaphael Isemann Alloc.setRedZoneSize(0);
19946e5603cSRaphael Isemann
20046e5603cSRaphael Isemann // Allocate 256 slabs. We should keep getting slabs with the original size
20146e5603cSRaphael Isemann // as we haven't hit our growth delay on the last allocation.
20246e5603cSRaphael Isemann for (std::size_t i = 0; i < GrowthDelay; ++i)
20346e5603cSRaphael Isemann Alloc.Allocate(SlabSize, 1);
20446e5603cSRaphael Isemann EXPECT_EQ(SlabSize * GrowthDelay, Alloc.getTotalMemory());
20546e5603cSRaphael Isemann // Allocate another slab. This time we should get another slab allocated
20646e5603cSRaphael Isemann // that is twice as large as the normal slab size.
20746e5603cSRaphael Isemann Alloc.Allocate(SlabSize, 1);
20846e5603cSRaphael Isemann EXPECT_EQ(SlabSize * GrowthDelay + SlabSize * 2, Alloc.getTotalMemory());
20946e5603cSRaphael Isemann }
21046e5603cSRaphael Isemann
2114b1f2f47SReid Kleckner // Mock slab allocator that returns slabs aligned on 4096 bytes. There is no
2124b1f2f47SReid Kleckner // easy portable way to do this, so this is kind of a hack.
213eed3466aSChandler Carruth class MockSlabAllocator {
214eed3466aSChandler Carruth static size_t LastSlabSize;
2154b1f2f47SReid Kleckner
2164b1f2f47SReid Kleckner public:
~MockSlabAllocator()217eed3466aSChandler Carruth ~MockSlabAllocator() { }
2184b1f2f47SReid Kleckner
Allocate(size_t Size,size_t)219785a9228SChandler Carruth void *Allocate(size_t Size, size_t /*Alignment*/) {
2204b1f2f47SReid Kleckner // Allocate space for the alignment, the slab, and a void* that goes right
2214b1f2f47SReid Kleckner // before the slab.
222ce56e1a1SGuillaume Chatelet Align Alignment(4096);
223ce56e1a1SGuillaume Chatelet void *MemBase = safe_malloc(Size + Alignment.value() - 1 + sizeof(void *));
2244b1f2f47SReid Kleckner
225f5babf97SChandler Carruth // Find the slab start.
2263b84f59bSHans Wennborg void *Slab = (void *)alignAddr((char*)MemBase + sizeof(void *), Alignment);
2274b1f2f47SReid Kleckner
2284b1f2f47SReid Kleckner // Hold a pointer to the base so we can free the whole malloced block.
2294b1f2f47SReid Kleckner ((void**)Slab)[-1] = MemBase;
2304b1f2f47SReid Kleckner
231f5babf97SChandler Carruth LastSlabSize = Size;
2324b1f2f47SReid Kleckner return Slab;
2334b1f2f47SReid Kleckner }
2344b1f2f47SReid Kleckner
Deallocate(void * Slab,size_t,size_t)235d3bc86c2SBenjamin Kramer void Deallocate(void *Slab, size_t /*Size*/, size_t /*Alignment*/) {
2364b1f2f47SReid Kleckner free(((void**)Slab)[-1]);
2374b1f2f47SReid Kleckner }
2384b1f2f47SReid Kleckner
GetLastSlabSize()239eed3466aSChandler Carruth static size_t GetLastSlabSize() { return LastSlabSize; }
2404b1f2f47SReid Kleckner };
2414b1f2f47SReid Kleckner
242eed3466aSChandler Carruth size_t MockSlabAllocator::LastSlabSize = 0;
243eed3466aSChandler Carruth
2444b1f2f47SReid Kleckner // Allocate a large-ish block with a really large alignment so that the
2454b1f2f47SReid Kleckner // allocator will think that it has space, but after it does the alignment it
2464b1f2f47SReid Kleckner // will not.
TEST(AllocatorTest,TestBigAlignment)2474b1f2f47SReid Kleckner TEST(AllocatorTest, TestBigAlignment) {
248eed3466aSChandler Carruth BumpPtrAllocatorImpl<MockSlabAllocator> Alloc;
249f5babf97SChandler Carruth
250f5babf97SChandler Carruth // First allocate a tiny bit to ensure we have to re-align things.
251fd1f0f17SHans Wennborg (void)Alloc.Allocate(1, 1);
252f5babf97SChandler Carruth
253f5babf97SChandler Carruth // Now the big chunk with a big alignment.
254f5babf97SChandler Carruth (void)Alloc.Allocate(3000, 2048);
255f5babf97SChandler Carruth
256f5babf97SChandler Carruth // We test that the last slab size is not the default 4096 byte slab, but
257f5babf97SChandler Carruth // rather a custom sized slab that is larger.
258eed3466aSChandler Carruth EXPECT_GT(MockSlabAllocator::GetLastSlabSize(), 4096u);
2594b1f2f47SReid Kleckner }
2604b1f2f47SReid Kleckner
261c2d882ddSReid Kleckner } // anonymous namespace
262