blob: 8cbe560a8578d449e5ea00f176a17f2cb0acc2e9 [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
daniel@transgaming.combbf56f72010-04-20 18:52:13 +00007#include "compiler/PoolAlloc.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00008
alokp@chromium.org1bcc3fd2010-05-19 17:08:44 +00009#ifndef _MSC_VER
10#include <stdint.h>
11#endif
alokp@chromium.org89b05432010-05-19 20:13:10 +000012#include <stdio.h>
alokp@chromium.org1bcc3fd2010-05-19 17:08:44 +000013
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000014#include "compiler/InitializeGlobals.h"
alokp@chromium.org4e89d232010-05-14 19:37:21 +000015#include "compiler/osinclude.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000016
alokp@chromium.orgff42c632010-05-10 15:14:30 +000017OS_TLSIndex PoolIndex = OS_INVALID_TLS_INDEX;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000018
19void InitializeGlobalPools()
20{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000021 TThreadGlobalPools* globalPools= static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
22 if (globalPools)
23 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000024
alokp@chromium.org4e89d232010-05-14 19:37:21 +000025 TPoolAllocator *globalPoolAllocator = new TPoolAllocator(true);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000026
alokp@chromium.org4e89d232010-05-14 19:37:21 +000027 TThreadGlobalPools* threadData = new TThreadGlobalPools();
28
29 threadData->globalPoolAllocator = globalPoolAllocator;
30
31 OS_SetTLSValue(PoolIndex, threadData);
32 globalPoolAllocator->push();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000033}
34
35void FreeGlobalPools()
36{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000037 // Release the allocated memory for this thread.
38 TThreadGlobalPools* globalPools= static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
39 if (!globalPools)
40 return;
41
42 GlobalPoolAllocator.popAll();
43 delete &GlobalPoolAllocator;
44 delete globalPools;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000045}
46
47bool InitializePoolIndex()
48{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000049 // Allocate a TLS index.
50 if ((PoolIndex = OS_AllocTLSIndex()) == OS_INVALID_TLS_INDEX)
51 return false;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000052
alokp@chromium.org4e89d232010-05-14 19:37:21 +000053 return true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000054}
55
56void FreePoolIndex()
57{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000058 // Release the TLS index.
59 OS_FreeTLSIndex(PoolIndex);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000060}
61
62TPoolAllocator& GetGlobalPoolAllocator()
63{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000064 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000065
alokp@chromium.org4e89d232010-05-14 19:37:21 +000066 return *threadData->globalPoolAllocator;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000067}
68
69void SetGlobalPoolAllocatorPtr(TPoolAllocator* poolAllocator)
70{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000071 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000072
alokp@chromium.org4e89d232010-05-14 19:37:21 +000073 threadData->globalPoolAllocator = poolAllocator;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000074}
75
76//
77// Implement the functionality of the TPoolAllocator class, which
78// is documented in PoolAlloc.h.
79//
80TPoolAllocator::TPoolAllocator(bool g, int growthIncrement, int allocationAlignment) :
alokp@chromium.org4e89d232010-05-14 19:37:21 +000081 global(g),
82 pageSize(growthIncrement),
83 alignment(allocationAlignment),
84 freeList(0),
85 inUseList(0),
alokp@chromium.org18895cb2010-10-14 16:09:57 +000086 numCalls(0),
87 totalBytes(0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000088{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000089 //
90 // Don't allow page sizes we know are smaller than all common
91 // OS page sizes.
92 //
93 if (pageSize < 4*1024)
94 pageSize = 4*1024;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000095
alokp@chromium.org4e89d232010-05-14 19:37:21 +000096 //
97 // A large currentPageOffset indicates a new page needs to
98 // be obtained to allocate memory.
99 //
100 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000101
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000102 //
103 // Adjust alignment to be at least pointer aligned and
104 // power of 2.
105 //
106 size_t minAlign = sizeof(void*);
107 alignment &= ~(minAlign - 1);
108 if (alignment < minAlign)
109 alignment = minAlign;
110 size_t a = 1;
111 while (a < alignment)
112 a <<= 1;
113 alignment = a;
114 alignmentMask = a - 1;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000115
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000116 //
117 // Align header skip
118 //
119 headerSkip = minAlign;
120 if (headerSkip < sizeof(tHeader)) {
121 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
122 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000123}
124
125TPoolAllocator::~TPoolAllocator()
126{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000127 if (!global) {
128 //
129 // Then we know that this object is not being
130 // allocated after other, globally scoped objects
131 // that depend on it. So we can delete the "in use" memory.
132 //
133 while (inUseList) {
134 tHeader* next = inUseList->nextPage;
135 inUseList->~tHeader();
136 delete [] reinterpret_cast<char*>(inUseList);
137 inUseList = next;
138 }
139 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000140
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000141 //
142 // Always delete the free list memory - it can't be being
143 // (correctly) referenced, whether the pool allocator was
144 // global or not. We should not check the guard blocks
145 // here, because we did it already when the block was
146 // placed into the free list.
147 //
148 while (freeList) {
149 tHeader* next = freeList->nextPage;
150 delete [] reinterpret_cast<char*>(freeList);
151 freeList = next;
152 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000153}
154
155// Support MSVC++ 6.0
156const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
157const unsigned char TAllocation::guardBlockEndVal = 0xfe;
158const unsigned char TAllocation::userDataFill = 0xcd;
159
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000160#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000161 const size_t TAllocation::guardBlockSize = 16;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000162#else
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000163 const size_t TAllocation::guardBlockSize = 0;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000164#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000165
166//
167// Check a single guard block for damage
168//
169void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
170{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000171 for (size_t x = 0; x < guardBlockSize; x++) {
172 if (blockMem[x] != val) {
173 char assertMsg[80];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000174
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000175 // We don't print the assert message. It's here just to be helpful.
alokp@chromium.org1bcc3fd2010-05-19 17:08:44 +0000176 sprintf(assertMsg, "PoolAlloc: Damage %s %u byte allocation at 0x%p\n",
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000177 locText, size, data());
178 assert(0 && "PoolAlloc: Damage in guard block");
179 }
180 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000181}
182
183
184void TPoolAllocator::push()
185{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000186 tAllocState state = { currentPageOffset, inUseList };
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000187
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000188 stack.push_back(state);
189
190 //
191 // Indicate there is no current page to allocate from.
192 //
193 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000194}
195
196//
197// Do a mass-deallocation of all the individual allocations
198// that have occurred since the last push(), or since the
199// last pop(), or since the object's creation.
200//
201// The deallocated pages are saved for future allocations.
202//
203void TPoolAllocator::pop()
204{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000205 if (stack.size() < 1)
206 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000207
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000208 tHeader* page = stack.back().page;
209 currentPageOffset = stack.back().offset;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000210
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000211 while (inUseList != page) {
212 // invoke destructor to free allocation list
213 inUseList->~tHeader();
214
215 tHeader* nextInUse = inUseList->nextPage;
216 if (inUseList->pageCount > 1)
217 delete [] reinterpret_cast<char*>(inUseList);
218 else {
219 inUseList->nextPage = freeList;
220 freeList = inUseList;
221 }
222 inUseList = nextInUse;
223 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000224
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000225 stack.pop_back();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000226}
227
228//
229// Do a mass-deallocation of all the individual allocations
230// that have occurred.
231//
232void TPoolAllocator::popAll()
233{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000234 while (stack.size() > 0)
235 pop();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000236}
237
238void* TPoolAllocator::allocate(size_t numBytes)
239{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000240 // If we are using guard blocks, all allocations are bracketed by
241 // them: [guardblock][allocation][guardblock]. numBytes is how
242 // much memory the caller asked for. allocationSize is the total
243 // size including guard blocks. In release build,
244 // guardBlockSize=0 and this all gets optimized away.
245 size_t allocationSize = TAllocation::allocationSize(numBytes);
246
247 //
248 // Just keep some interesting statistics.
249 //
250 ++numCalls;
251 totalBytes += numBytes;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000252
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000253 //
254 // Do the allocation, most likely case first, for efficiency.
255 // This step could be moved to be inline sometime.
256 //
257 if (currentPageOffset + allocationSize <= pageSize) {
258 //
259 // Safe to allocate from currentPageOffset.
260 //
261 unsigned char* memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
262 currentPageOffset += allocationSize;
263 currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000264
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000265 return initializeAllocation(inUseList, memory, numBytes);
266 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000267
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000268 if (allocationSize + headerSkip > pageSize) {
269 //
270 // Do a multi-page allocation. Don't mix these with the others.
271 // The OS is efficient and allocating and free-ing multiple pages.
272 //
273 size_t numBytesToAlloc = allocationSize + headerSkip;
274 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
275 if (memory == 0)
276 return 0;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000277
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000278 // Use placement-new to initialize header
279 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
280 inUseList = memory;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000281
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000282 currentPageOffset = pageSize; // make next allocation come from a new page
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000283
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000284 // No guard blocks for multi-page allocations (yet)
285 return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
286 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000287
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000288 //
289 // Need a simple page to allocate from.
290 //
291 tHeader* memory;
292 if (freeList) {
293 memory = freeList;
294 freeList = freeList->nextPage;
295 } else {
296 memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
297 if (memory == 0)
298 return 0;
299 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000300
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000301 // Use placement-new to initialize header
302 new(memory) tHeader(inUseList, 1);
303 inUseList = memory;
304
305 unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
306 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000307
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000308 return initializeAllocation(inUseList, ret, numBytes);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000309}
310
311
312//
313// Check all allocations in a list for damage by calling check on each.
314//
315void TAllocation::checkAllocList() const
316{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000317 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
318 alloc->check();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000319}