blob: 27e1c06b5b83fece8636264185e22b9104a64201 [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//
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000053TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
alokp@chromium.org4e89d232010-05-14 19:37:21 +000054 pageSize(growthIncrement),
55 alignment(allocationAlignment),
56 freeList(0),
57 inUseList(0),
alokp@chromium.org18895cb2010-10-14 16:09:57 +000058 numCalls(0),
Jamie Madill438dbcf2016-06-17 14:20:05 -040059 totalBytes(0),
60 mLocked(false)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000061{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000062 //
63 // Don't allow page sizes we know are smaller than all common
64 // OS page sizes.
65 //
66 if (pageSize < 4*1024)
67 pageSize = 4*1024;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000068
alokp@chromium.org4e89d232010-05-14 19:37:21 +000069 //
70 // A large currentPageOffset indicates a new page needs to
71 // be obtained to allocate memory.
72 //
73 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000074
alokp@chromium.org4e89d232010-05-14 19:37:21 +000075 //
76 // Adjust alignment to be at least pointer aligned and
77 // power of 2.
78 //
79 size_t minAlign = sizeof(void*);
80 alignment &= ~(minAlign - 1);
81 if (alignment < minAlign)
82 alignment = minAlign;
83 size_t a = 1;
84 while (a < alignment)
85 a <<= 1;
86 alignment = a;
87 alignmentMask = a - 1;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000088
alokp@chromium.org4e89d232010-05-14 19:37:21 +000089 //
90 // Align header skip
91 //
92 headerSkip = minAlign;
93 if (headerSkip < sizeof(tHeader)) {
94 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
95 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000096}
97
98TPoolAllocator::~TPoolAllocator()
99{
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000100 while (inUseList) {
101 tHeader* next = inUseList->nextPage;
102 inUseList->~tHeader();
103 delete [] reinterpret_cast<char*>(inUseList);
104 inUseList = next;
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000105 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000106
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000107 // We should not check the guard blocks
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000108 // here, because we did it already when the block was
109 // placed into the free list.
110 //
111 while (freeList) {
112 tHeader* next = freeList->nextPage;
113 delete [] reinterpret_cast<char*>(freeList);
114 freeList = next;
115 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000116}
117
118// Support MSVC++ 6.0
119const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
120const unsigned char TAllocation::guardBlockEndVal = 0xfe;
121const unsigned char TAllocation::userDataFill = 0xcd;
122
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000123#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000124 const size_t TAllocation::guardBlockSize = 16;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000125#else
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000126 const size_t TAllocation::guardBlockSize = 0;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000127#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000128
129//
130// Check a single guard block for damage
131//
132void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
133{
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000134#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000135 for (size_t x = 0; x < guardBlockSize; x++) {
136 if (blockMem[x] != val) {
137 char assertMsg[80];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000138
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000139 // We don't print the assert message. It's here just to be helpful.
apatrick@chromium.orge4319632012-01-27 22:13:17 +0000140#if defined(_MSC_VER)
kbr@chromium.orgddb6e8e2012-04-25 00:48:13 +0000141 snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %Iu byte allocation at 0x%p\n",
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000142 locText, size, data());
apatrick@chromium.orge4319632012-01-27 22:13:17 +0000143#else
kbr@chromium.orgddb6e8e2012-04-25 00:48:13 +0000144 snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n",
apatrick@chromium.orge4319632012-01-27 22:13:17 +0000145 locText, size, data());
146#endif
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000147 assert(0 && "PoolAlloc: Damage in guard block");
148 }
149 }
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000150#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000151}
152
153
154void TPoolAllocator::push()
155{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000156 tAllocState state = { currentPageOffset, inUseList };
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000157
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000158 stack.push_back(state);
159
160 //
161 // Indicate there is no current page to allocate from.
162 //
163 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000164}
165
166//
167// Do a mass-deallocation of all the individual allocations
168// that have occurred since the last push(), or since the
169// last pop(), or since the object's creation.
170//
171// The deallocated pages are saved for future allocations.
172//
173void TPoolAllocator::pop()
174{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000175 if (stack.size() < 1)
176 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000177
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000178 tHeader* page = stack.back().page;
179 currentPageOffset = stack.back().offset;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000180
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000181 while (inUseList != page) {
182 // invoke destructor to free allocation list
183 inUseList->~tHeader();
184
185 tHeader* nextInUse = inUseList->nextPage;
186 if (inUseList->pageCount > 1)
187 delete [] reinterpret_cast<char*>(inUseList);
188 else {
189 inUseList->nextPage = freeList;
190 freeList = inUseList;
191 }
192 inUseList = nextInUse;
193 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000194
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000195 stack.pop_back();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000196}
197
198//
199// Do a mass-deallocation of all the individual allocations
200// that have occurred.
201//
202void TPoolAllocator::popAll()
203{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000204 while (stack.size() > 0)
205 pop();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000206}
207
208void* TPoolAllocator::allocate(size_t numBytes)
209{
Jamie Madill438dbcf2016-06-17 14:20:05 -0400210 ASSERT(!mLocked);
211
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000212 //
213 // Just keep some interesting statistics.
214 //
215 ++numCalls;
216 totalBytes += numBytes;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000217
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000218 // If we are using guard blocks, all allocations are bracketed by
219 // them: [guardblock][allocation][guardblock]. numBytes is how
220 // much memory the caller asked for. allocationSize is the total
221 // size including guard blocks. In release build,
222 // guardBlockSize=0 and this all gets optimized away.
223 size_t allocationSize = TAllocation::allocationSize(numBytes);
224 // Detect integer overflow.
225 if (allocationSize < numBytes)
226 return 0;
227
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000228 //
229 // Do the allocation, most likely case first, for efficiency.
230 // This step could be moved to be inline sometime.
231 //
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000232 if (allocationSize <= pageSize - currentPageOffset) {
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000233 //
234 // Safe to allocate from currentPageOffset.
235 //
236 unsigned char* memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
237 currentPageOffset += allocationSize;
238 currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000239
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000240 return initializeAllocation(inUseList, memory, numBytes);
241 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000242
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000243 if (allocationSize > pageSize - headerSkip) {
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000244 //
245 // Do a multi-page allocation. Don't mix these with the others.
246 // The OS is efficient and allocating and free-ing multiple pages.
247 //
248 size_t numBytesToAlloc = allocationSize + headerSkip;
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000249 // Detect integer overflow.
250 if (numBytesToAlloc < allocationSize)
251 return 0;
252
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000253 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
254 if (memory == 0)
255 return 0;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000256
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000257 // Use placement-new to initialize header
258 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
259 inUseList = memory;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000260
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000261 currentPageOffset = pageSize; // make next allocation come from a new page
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000262
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000263 // No guard blocks for multi-page allocations (yet)
264 return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
265 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000266
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000267 //
268 // Need a simple page to allocate from.
269 //
270 tHeader* memory;
271 if (freeList) {
272 memory = freeList;
273 freeList = freeList->nextPage;
274 } else {
275 memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
276 if (memory == 0)
277 return 0;
278 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000279
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000280 // Use placement-new to initialize header
281 new(memory) tHeader(inUseList, 1);
282 inUseList = memory;
283
284 unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
285 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000286
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000287 return initializeAllocation(inUseList, ret, numBytes);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000288}
289
Jamie Madill438dbcf2016-06-17 14:20:05 -0400290void TPoolAllocator::lock()
291{
292 ASSERT(!mLocked);
293 mLocked = true;
294}
295
296void TPoolAllocator::unlock()
297{
298 ASSERT(mLocked);
299 mLocked = false;
300}
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000301
302//
303// Check all allocations in a list for damage by calling check on each.
304//
305void TAllocation::checkAllocList() const
306{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000307 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
308 alloc->check();
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000309}