blob: 3b44afe335882f67474005d03538e0024946e683 [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
Geoff Lang44fa7592014-05-30 11:50:07 -04009#include <stdint.h>
10#include <stdio.h>
11#include <assert.h>
12
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
Alok Priyadarshi8156b6b2013-09-23 14:56:58 -040037TPoolAllocator* GetGlobalPoolAllocator()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000038{
Geoff Lang44fa7592014-05-30 11:50:07 -040039 assert(PoolIndex != TLS_INVALID_INDEX);
40 return static_cast<TPoolAllocator*>(GetTLSValue(PoolIndex));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000041}
42
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000043void 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 //
68 size_t minAlign = sizeof(void*);
69 alignment &= ~(minAlign - 1);
70 if (alignment < minAlign)
71 alignment = minAlign;
72 size_t a = 1;
73 while (a < alignment)
74 a <<= 1;
75 alignment = a;
76 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;
96 if (headerSkip < sizeof(tHeader)) {
97 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
98 }
Corentin Wallez28b65282016-06-16 07:24:50 -070099#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
100 mStack.push_back({});
101#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000102}
103
104TPoolAllocator::~TPoolAllocator()
105{
Corentin Wallez28b65282016-06-16 07:24:50 -0700106#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000107 while (inUseList) {
108 tHeader* next = inUseList->nextPage;
109 inUseList->~tHeader();
110 delete [] reinterpret_cast<char*>(inUseList);
111 inUseList = next;
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000112 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000113
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000114 // We should not check the guard blocks
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000115 // here, because we did it already when the block was
116 // placed into the free list.
117 //
118 while (freeList) {
119 tHeader* next = freeList->nextPage;
120 delete [] reinterpret_cast<char*>(freeList);
121 freeList = next;
122 }
Corentin Wallez28b65282016-06-16 07:24:50 -0700123#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
124 for (auto &allocs : mStack)
125 {
126 for (auto alloc : allocs)
127 {
128 free(alloc);
129 }
130 }
131 mStack.clear();
132#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000133}
134
135// Support MSVC++ 6.0
136const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
137const unsigned char TAllocation::guardBlockEndVal = 0xfe;
138const unsigned char TAllocation::userDataFill = 0xcd;
139
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000140#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000141 const size_t TAllocation::guardBlockSize = 16;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000142#else
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000143 const size_t TAllocation::guardBlockSize = 0;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000144#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000145
146//
147// Check a single guard block for damage
148//
149void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
150{
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000151#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000152 for (size_t x = 0; x < guardBlockSize; x++) {
153 if (blockMem[x] != val) {
154 char assertMsg[80];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000155
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000156 // We don't print the assert message. It's here just to be helpful.
apatrick@chromium.orge4319632012-01-27 22:13:17 +0000157#if defined(_MSC_VER)
kbr@chromium.orgddb6e8e2012-04-25 00:48:13 +0000158 snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %Iu byte allocation at 0x%p\n",
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000159 locText, size, data());
apatrick@chromium.orge4319632012-01-27 22:13:17 +0000160#else
kbr@chromium.orgddb6e8e2012-04-25 00:48:13 +0000161 snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n",
apatrick@chromium.orge4319632012-01-27 22:13:17 +0000162 locText, size, data());
163#endif
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000164 assert(0 && "PoolAlloc: Damage in guard block");
165 }
166 }
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000167#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000168}
169
170
171void TPoolAllocator::push()
172{
Corentin Wallez28b65282016-06-16 07:24:50 -0700173#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000174 tAllocState state = { currentPageOffset, inUseList };
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000175
Corentin Wallez28b65282016-06-16 07:24:50 -0700176 mStack.push_back(state);
177
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000178 //
179 // Indicate there is no current page to allocate from.
180 //
181 currentPageOffset = pageSize;
Corentin Wallez28b65282016-06-16 07:24:50 -0700182#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
183 mStack.push_back({});
184#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000185}
186
187//
188// Do a mass-deallocation of all the individual allocations
189// that have occurred since the last push(), or since the
190// last pop(), or since the object's creation.
191//
192// The deallocated pages are saved for future allocations.
193//
194void TPoolAllocator::pop()
195{
Corentin Wallez28b65282016-06-16 07:24:50 -0700196 if (mStack.size() < 1)
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000197 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000198
Corentin Wallez28b65282016-06-16 07:24:50 -0700199#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
200 tHeader *page = mStack.back().page;
201 currentPageOffset = mStack.back().offset;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000202
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000203 while (inUseList != page) {
204 // invoke destructor to free allocation list
205 inUseList->~tHeader();
206
207 tHeader* nextInUse = inUseList->nextPage;
208 if (inUseList->pageCount > 1)
209 delete [] reinterpret_cast<char*>(inUseList);
210 else {
211 inUseList->nextPage = freeList;
212 freeList = inUseList;
213 }
214 inUseList = nextInUse;
215 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000216
Corentin Wallez28b65282016-06-16 07:24:50 -0700217 mStack.pop_back();
218#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
219 for (auto &alloc : mStack.back())
220 {
221 free(alloc);
222 }
223 mStack.pop_back();
224#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000225}
226
227//
228// Do a mass-deallocation of all the individual allocations
229// that have occurred.
230//
231void TPoolAllocator::popAll()
232{
Corentin Wallez28b65282016-06-16 07:24:50 -0700233 while (mStack.size() > 0)
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000234 pop();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000235}
236
237void* TPoolAllocator::allocate(size_t numBytes)
238{
Jamie Madill438dbcf2016-06-17 14:20:05 -0400239 ASSERT(!mLocked);
240
Corentin Wallez28b65282016-06-16 07:24:50 -0700241#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000242 //
243 // Just keep some interesting statistics.
244 //
245 ++numCalls;
246 totalBytes += numBytes;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000247
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000248 // If we are using guard blocks, all allocations are bracketed by
249 // them: [guardblock][allocation][guardblock]. numBytes is how
250 // much memory the caller asked for. allocationSize is the total
251 // size including guard blocks. In release build,
252 // guardBlockSize=0 and this all gets optimized away.
253 size_t allocationSize = TAllocation::allocationSize(numBytes);
254 // Detect integer overflow.
255 if (allocationSize < numBytes)
256 return 0;
257
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000258 //
259 // Do the allocation, most likely case first, for efficiency.
260 // This step could be moved to be inline sometime.
261 //
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000262 if (allocationSize <= pageSize - currentPageOffset) {
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000263 //
264 // Safe to allocate from currentPageOffset.
265 //
266 unsigned char* memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
267 currentPageOffset += allocationSize;
268 currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000269
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000270 return initializeAllocation(inUseList, memory, numBytes);
271 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000272
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000273 if (allocationSize > pageSize - headerSkip) {
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000274 //
275 // Do a multi-page allocation. Don't mix these with the others.
276 // The OS is efficient and allocating and free-ing multiple pages.
277 //
278 size_t numBytesToAlloc = allocationSize + headerSkip;
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000279 // Detect integer overflow.
280 if (numBytesToAlloc < allocationSize)
281 return 0;
282
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000283 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
284 if (memory == 0)
285 return 0;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000286
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000287 // Use placement-new to initialize header
288 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
289 inUseList = memory;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000290
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000291 currentPageOffset = pageSize; // make next allocation come from a new page
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000292
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000293 // No guard blocks for multi-page allocations (yet)
294 return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
295 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000296
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000297 //
298 // Need a simple page to allocate from.
299 //
300 tHeader* memory;
301 if (freeList) {
302 memory = freeList;
303 freeList = freeList->nextPage;
304 } else {
305 memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
306 if (memory == 0)
307 return 0;
308 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000309
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000310 // Use placement-new to initialize header
311 new(memory) tHeader(inUseList, 1);
312 inUseList = memory;
313
314 unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
315 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000316
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000317 return initializeAllocation(inUseList, ret, numBytes);
Corentin Wallez28b65282016-06-16 07:24:50 -0700318#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
319 void *alloc = malloc(numBytes + alignmentMask);
320 mStack.back().push_back(alloc);
321
322 intptr_t intAlloc = reinterpret_cast<intptr_t>(alloc);
323 intAlloc = (intAlloc + alignmentMask) & ~alignmentMask;
324 return reinterpret_cast<void *>(intAlloc);
325#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000326}
327
Jamie Madill438dbcf2016-06-17 14:20:05 -0400328void TPoolAllocator::lock()
329{
330 ASSERT(!mLocked);
331 mLocked = true;
332}
333
334void TPoolAllocator::unlock()
335{
336 ASSERT(mLocked);
337 mLocked = false;
338}
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000339
340//
341// Check all allocations in a list for damage by calling check on each.
342//
343void TAllocation::checkAllocList() const
344{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000345 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
346 alloc->check();
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000347}