blob: 5ad10c200b193b8f981e72508da3361d3816a138 [file] [log] [blame]
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
Geoff Lang17732822013-08-29 13:46:49 -04007#include "compiler/translator/PoolAlloc.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00008
Jamie Madillb980c562018-11-27 11:34:27 -05009#include <assert.h>
Geoff Lang44fa7592014-05-30 11:50:07 -040010#include <stdint.h>
11#include <stdio.h>
Geoff Lang44fa7592014-05-30 11:50:07 -040012
Jamie Madill438dbcf2016-06-17 14:20:05 -040013#include "common/angleutils.h"
14#include "common/debug.h"
15#include "common/platform.h"
16#include "common/tls.h"
17#include "compiler/translator/InitializeGlobals.h"
18
Geoff Lang44fa7592014-05-30 11:50:07 -040019TLSIndex PoolIndex = TLS_INVALID_INDEX;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000020
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000021bool InitializePoolIndex()
22{
Geoff Lang44fa7592014-05-30 11:50:07 -040023 assert(PoolIndex == TLS_INVALID_INDEX);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000024
Geoff Lang44fa7592014-05-30 11:50:07 -040025 PoolIndex = CreateTLSIndex();
26 return PoolIndex != TLS_INVALID_INDEX;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000027}
28
29void FreePoolIndex()
30{
Geoff Lang44fa7592014-05-30 11:50:07 -040031 assert(PoolIndex != TLS_INVALID_INDEX);
Alok Priyadarshi8156b6b2013-09-23 14:56:58 -040032
Geoff Lang44fa7592014-05-30 11:50:07 -040033 DestroyTLSIndex(PoolIndex);
34 PoolIndex = TLS_INVALID_INDEX;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000035}
36
Jamie Madilld7b1ab52016-12-12 14:42:19 -050037TPoolAllocator *GetGlobalPoolAllocator()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000038{
Geoff Lang44fa7592014-05-30 11:50:07 -040039 assert(PoolIndex != TLS_INVALID_INDEX);
Jamie Madilld7b1ab52016-12-12 14:42:19 -050040 return static_cast<TPoolAllocator *>(GetTLSValue(PoolIndex));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000041}
42
Jamie Madilld7b1ab52016-12-12 14:42:19 -050043void SetGlobalPoolAllocator(TPoolAllocator *poolAllocator)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000044{
Geoff Lang44fa7592014-05-30 11:50:07 -040045 assert(PoolIndex != TLS_INVALID_INDEX);
46 SetTLSValue(PoolIndex, poolAllocator);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000047}
48
49//
50// Implement the functionality of the TPoolAllocator class, which
51// is documented in PoolAlloc.h.
52//
Corentin Wallez28b65282016-06-16 07:24:50 -070053TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment)
54 : alignment(allocationAlignment),
55#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
56 pageSize(growthIncrement),
57 freeList(0),
58 inUseList(0),
59 numCalls(0),
60 totalBytes(0),
61#endif
62 mLocked(false)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000063{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000064 //
alokp@chromium.org4e89d232010-05-14 19:37:21 +000065 // Adjust alignment to be at least pointer aligned and
66 // power of 2.
67 //
Jamie Madilld7b1ab52016-12-12 14:42:19 -050068 size_t minAlign = sizeof(void *);
alokp@chromium.org4e89d232010-05-14 19:37:21 +000069 alignment &= ~(minAlign - 1);
70 if (alignment < minAlign)
71 alignment = minAlign;
Jamie Madillb980c562018-11-27 11:34:27 -050072 size_t a = 1;
alokp@chromium.org4e89d232010-05-14 19:37:21 +000073 while (a < alignment)
74 a <<= 1;
Jamie Madilld7b1ab52016-12-12 14:42:19 -050075 alignment = a;
alokp@chromium.org4e89d232010-05-14 19:37:21 +000076 alignmentMask = a - 1;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000077
Corentin Wallez28b65282016-06-16 07:24:50 -070078#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
79 //
80 // Don't allow page sizes we know are smaller than all common
81 // OS page sizes.
82 //
83 if (pageSize < 4 * 1024)
84 pageSize = 4 * 1024;
85
86 //
87 // A large currentPageOffset indicates a new page needs to
88 // be obtained to allocate memory.
89 //
90 currentPageOffset = pageSize;
91
alokp@chromium.org4e89d232010-05-14 19:37:21 +000092 //
93 // Align header skip
94 //
95 headerSkip = minAlign;
Jamie Madilld7b1ab52016-12-12 14:42:19 -050096 if (headerSkip < sizeof(tHeader))
97 {
alokp@chromium.org4e89d232010-05-14 19:37:21 +000098 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
99 }
Corentin Wallez28b65282016-06-16 07:24:50 -0700100#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
101 mStack.push_back({});
102#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000103}
104
105TPoolAllocator::~TPoolAllocator()
106{
Corentin Wallez28b65282016-06-16 07:24:50 -0700107#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500108 while (inUseList)
109 {
110 tHeader *next = inUseList->nextPage;
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000111 inUseList->~tHeader();
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500112 delete[] reinterpret_cast<char *>(inUseList);
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000113 inUseList = next;
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000114 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000115
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000116 // We should not check the guard blocks
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000117 // here, because we did it already when the block was
118 // placed into the free list.
119 //
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500120 while (freeList)
121 {
122 tHeader *next = freeList->nextPage;
123 delete[] reinterpret_cast<char *>(freeList);
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000124 freeList = next;
125 }
Corentin Wallez28b65282016-06-16 07:24:50 -0700126#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
127 for (auto &allocs : mStack)
128 {
129 for (auto alloc : allocs)
130 {
131 free(alloc);
132 }
133 }
134 mStack.clear();
135#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000136}
137
138// Support MSVC++ 6.0
139const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
140const unsigned char TAllocation::guardBlockEndVal = 0xfe;
141const unsigned char TAllocation::userDataFill = 0xcd;
142
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000143#ifdef GUARD_BLOCKS
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500144const size_t TAllocation::guardBlockSize = 16;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000145#else
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500146const size_t TAllocation::guardBlockSize = 0;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000147#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000148
149//
150// Check a single guard block for damage
151//
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500152void TAllocation::checkGuardBlock(unsigned char *blockMem,
153 unsigned char val,
154 const char *locText) const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000155{
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000156#ifdef GUARD_BLOCKS
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500157 for (size_t x = 0; x < guardBlockSize; x++)
158 {
159 if (blockMem[x] != val)
160 {
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000161 char assertMsg[80];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000162
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500163// We don't print the assert message. It's here just to be helpful.
Jamie Madillb980c562018-11-27 11:34:27 -0500164# if defined(_MSC_VER)
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500165 snprintf(assertMsg, sizeof(assertMsg),
166 "PoolAlloc: Damage %s %Iu byte allocation at 0x%p\n", locText, size, data());
Jamie Madillb980c562018-11-27 11:34:27 -0500167# else
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500168 snprintf(assertMsg, sizeof(assertMsg),
169 "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n", locText, size, data());
Jamie Madillb980c562018-11-27 11:34:27 -0500170# endif
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000171 assert(0 && "PoolAlloc: Damage in guard block");
172 }
173 }
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000174#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000175}
176
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000177void TPoolAllocator::push()
178{
Corentin Wallez28b65282016-06-16 07:24:50 -0700179#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500180 tAllocState state = {currentPageOffset, inUseList};
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000181
Corentin Wallez28b65282016-06-16 07:24:50 -0700182 mStack.push_back(state);
183
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000184 //
185 // Indicate there is no current page to allocate from.
186 //
187 currentPageOffset = pageSize;
Corentin Wallez28b65282016-06-16 07:24:50 -0700188#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
189 mStack.push_back({});
190#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000191}
192
193//
194// Do a mass-deallocation of all the individual allocations
195// that have occurred since the last push(), or since the
196// last pop(), or since the object's creation.
197//
198// The deallocated pages are saved for future allocations.
199//
200void TPoolAllocator::pop()
201{
Corentin Wallez28b65282016-06-16 07:24:50 -0700202 if (mStack.size() < 1)
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000203 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000204
Corentin Wallez28b65282016-06-16 07:24:50 -0700205#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
206 tHeader *page = mStack.back().page;
207 currentPageOffset = mStack.back().offset;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000208
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500209 while (inUseList != page)
210 {
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000211 // invoke destructor to free allocation list
212 inUseList->~tHeader();
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500213
214 tHeader *nextInUse = inUseList->nextPage;
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000215 if (inUseList->pageCount > 1)
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500216 delete[] reinterpret_cast<char *>(inUseList);
217 else
218 {
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000219 inUseList->nextPage = freeList;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500220 freeList = inUseList;
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000221 }
222 inUseList = nextInUse;
223 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000224
Corentin Wallez28b65282016-06-16 07:24:50 -0700225 mStack.pop_back();
226#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
227 for (auto &alloc : mStack.back())
228 {
229 free(alloc);
230 }
231 mStack.pop_back();
232#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000233}
234
235//
236// Do a mass-deallocation of all the individual allocations
237// that have occurred.
238//
239void TPoolAllocator::popAll()
240{
Corentin Wallez28b65282016-06-16 07:24:50 -0700241 while (mStack.size() > 0)
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000242 pop();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000243}
244
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500245void *TPoolAllocator::allocate(size_t numBytes)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000246{
Jamie Madill438dbcf2016-06-17 14:20:05 -0400247 ASSERT(!mLocked);
248
Corentin Wallez28b65282016-06-16 07:24:50 -0700249#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000250 //
251 // Just keep some interesting statistics.
252 //
253 ++numCalls;
254 totalBytes += numBytes;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000255
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000256 // If we are using guard blocks, all allocations are bracketed by
257 // them: [guardblock][allocation][guardblock]. numBytes is how
258 // much memory the caller asked for. allocationSize is the total
259 // size including guard blocks. In release build,
260 // guardBlockSize=0 and this all gets optimized away.
261 size_t allocationSize = TAllocation::allocationSize(numBytes);
262 // Detect integer overflow.
263 if (allocationSize < numBytes)
264 return 0;
265
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000266 //
267 // Do the allocation, most likely case first, for efficiency.
268 // This step could be moved to be inline sometime.
269 //
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500270 if (allocationSize <= pageSize - currentPageOffset)
271 {
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000272 //
273 // Safe to allocate from currentPageOffset.
274 //
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500275 unsigned char *memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000276 currentPageOffset += allocationSize;
277 currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000278
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000279 return initializeAllocation(inUseList, memory, numBytes);
280 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000281
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500282 if (allocationSize > pageSize - headerSkip)
283 {
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000284 //
285 // Do a multi-page allocation. Don't mix these with the others.
286 // The OS is efficient and allocating and free-ing multiple pages.
287 //
288 size_t numBytesToAlloc = allocationSize + headerSkip;
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000289 // Detect integer overflow.
290 if (numBytesToAlloc < allocationSize)
291 return 0;
292
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500293 tHeader *memory = reinterpret_cast<tHeader *>(::new char[numBytesToAlloc]);
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000294 if (memory == 0)
295 return 0;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000296
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000297 // Use placement-new to initialize header
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500298 new (memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000299 inUseList = memory;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000300
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000301 currentPageOffset = pageSize; // make next allocation come from a new page
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000302
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000303 // No guard blocks for multi-page allocations (yet)
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500304 return reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000305 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000306
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000307 //
308 // Need a simple page to allocate from.
309 //
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500310 tHeader *memory;
311 if (freeList)
312 {
313 memory = freeList;
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000314 freeList = freeList->nextPage;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500315 }
316 else
317 {
318 memory = reinterpret_cast<tHeader *>(::new char[pageSize]);
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000319 if (memory == 0)
320 return 0;
321 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000322
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000323 // Use placement-new to initialize header
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500324 new (memory) tHeader(inUseList, 1);
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000325 inUseList = memory;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500326
327 unsigned char *ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
328 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000329
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000330 return initializeAllocation(inUseList, ret, numBytes);
Corentin Wallez28b65282016-06-16 07:24:50 -0700331#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
332 void *alloc = malloc(numBytes + alignmentMask);
333 mStack.back().push_back(alloc);
334
335 intptr_t intAlloc = reinterpret_cast<intptr_t>(alloc);
Jamie Madillb980c562018-11-27 11:34:27 -0500336 intAlloc = (intAlloc + alignmentMask) & ~alignmentMask;
Corentin Wallez28b65282016-06-16 07:24:50 -0700337 return reinterpret_cast<void *>(intAlloc);
338#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000339}
340
Jamie Madill438dbcf2016-06-17 14:20:05 -0400341void TPoolAllocator::lock()
342{
343 ASSERT(!mLocked);
344 mLocked = true;
345}
346
347void TPoolAllocator::unlock()
348{
349 ASSERT(mLocked);
350 mLocked = false;
351}
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000352
353//
354// Check all allocations in a list for damage by calling check on each.
355//
356void TAllocation::checkAllocList() const
357{
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500358 for (const TAllocation *alloc = this; alloc != 0; alloc = alloc->prevAlloc)
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000359 alloc->check();
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000360}