blob: 39567de0d00d3fafbd83d9772af7b729445942eb [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
12
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000013#include "compiler/InitializeGlobals.h"
alokp@chromium.org4e89d232010-05-14 19:37:21 +000014#include "compiler/osinclude.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000015
alokp@chromium.orgff42c632010-05-10 15:14:30 +000016OS_TLSIndex PoolIndex = OS_INVALID_TLS_INDEX;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000017
18void InitializeGlobalPools()
19{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000020 TThreadGlobalPools* globalPools= static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
21 if (globalPools)
22 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000023
alokp@chromium.org4e89d232010-05-14 19:37:21 +000024 TPoolAllocator *globalPoolAllocator = new TPoolAllocator(true);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000025
alokp@chromium.org4e89d232010-05-14 19:37:21 +000026 TThreadGlobalPools* threadData = new TThreadGlobalPools();
27
28 threadData->globalPoolAllocator = globalPoolAllocator;
29
30 OS_SetTLSValue(PoolIndex, threadData);
31 globalPoolAllocator->push();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000032}
33
34void FreeGlobalPools()
35{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000036 // Release the allocated memory for this thread.
37 TThreadGlobalPools* globalPools= static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
38 if (!globalPools)
39 return;
40
41 GlobalPoolAllocator.popAll();
42 delete &GlobalPoolAllocator;
43 delete globalPools;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000044}
45
46bool InitializePoolIndex()
47{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000048 // Allocate a TLS index.
49 if ((PoolIndex = OS_AllocTLSIndex()) == OS_INVALID_TLS_INDEX)
50 return false;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000051
alokp@chromium.org4e89d232010-05-14 19:37:21 +000052 return true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000053}
54
55void FreePoolIndex()
56{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000057 // Release the TLS index.
58 OS_FreeTLSIndex(PoolIndex);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000059}
60
61TPoolAllocator& GetGlobalPoolAllocator()
62{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000063 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000064
alokp@chromium.org4e89d232010-05-14 19:37:21 +000065 return *threadData->globalPoolAllocator;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000066}
67
68void SetGlobalPoolAllocatorPtr(TPoolAllocator* poolAllocator)
69{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000070 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000071
alokp@chromium.org4e89d232010-05-14 19:37:21 +000072 threadData->globalPoolAllocator = poolAllocator;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000073}
74
75//
76// Implement the functionality of the TPoolAllocator class, which
77// is documented in PoolAlloc.h.
78//
79TPoolAllocator::TPoolAllocator(bool g, int growthIncrement, int allocationAlignment) :
alokp@chromium.org4e89d232010-05-14 19:37:21 +000080 global(g),
81 pageSize(growthIncrement),
82 alignment(allocationAlignment),
83 freeList(0),
84 inUseList(0),
85 numCalls(0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000086{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000087 //
88 // Don't allow page sizes we know are smaller than all common
89 // OS page sizes.
90 //
91 if (pageSize < 4*1024)
92 pageSize = 4*1024;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000093
alokp@chromium.org4e89d232010-05-14 19:37:21 +000094 //
95 // A large currentPageOffset indicates a new page needs to
96 // be obtained to allocate memory.
97 //
98 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000099
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000100 //
101 // Adjust alignment to be at least pointer aligned and
102 // power of 2.
103 //
104 size_t minAlign = sizeof(void*);
105 alignment &= ~(minAlign - 1);
106 if (alignment < minAlign)
107 alignment = minAlign;
108 size_t a = 1;
109 while (a < alignment)
110 a <<= 1;
111 alignment = a;
112 alignmentMask = a - 1;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000113
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000114 //
115 // Align header skip
116 //
117 headerSkip = minAlign;
118 if (headerSkip < sizeof(tHeader)) {
119 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
120 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000121}
122
123TPoolAllocator::~TPoolAllocator()
124{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000125 if (!global) {
126 //
127 // Then we know that this object is not being
128 // allocated after other, globally scoped objects
129 // that depend on it. So we can delete the "in use" memory.
130 //
131 while (inUseList) {
132 tHeader* next = inUseList->nextPage;
133 inUseList->~tHeader();
134 delete [] reinterpret_cast<char*>(inUseList);
135 inUseList = next;
136 }
137 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000138
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000139 //
140 // Always delete the free list memory - it can't be being
141 // (correctly) referenced, whether the pool allocator was
142 // global or not. We should not check the guard blocks
143 // here, because we did it already when the block was
144 // placed into the free list.
145 //
146 while (freeList) {
147 tHeader* next = freeList->nextPage;
148 delete [] reinterpret_cast<char*>(freeList);
149 freeList = next;
150 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000151}
152
153// Support MSVC++ 6.0
154const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
155const unsigned char TAllocation::guardBlockEndVal = 0xfe;
156const unsigned char TAllocation::userDataFill = 0xcd;
157
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000158#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000159 const size_t TAllocation::guardBlockSize = 16;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000160#else
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000161 const size_t TAllocation::guardBlockSize = 0;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000162#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000163
164//
165// Check a single guard block for damage
166//
167void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
168{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000169 for (size_t x = 0; x < guardBlockSize; x++) {
170 if (blockMem[x] != val) {
171 char assertMsg[80];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000172
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000173 // We don't print the assert message. It's here just to be helpful.
alokp@chromium.org1bcc3fd2010-05-19 17:08:44 +0000174 sprintf(assertMsg, "PoolAlloc: Damage %s %u byte allocation at 0x%p\n",
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000175 locText, size, data());
176 assert(0 && "PoolAlloc: Damage in guard block");
177 }
178 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000179}
180
181
182void TPoolAllocator::push()
183{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000184 tAllocState state = { currentPageOffset, inUseList };
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000185
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000186 stack.push_back(state);
187
188 //
189 // Indicate there is no current page to allocate from.
190 //
191 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000192}
193
194//
195// Do a mass-deallocation of all the individual allocations
196// that have occurred since the last push(), or since the
197// last pop(), or since the object's creation.
198//
199// The deallocated pages are saved for future allocations.
200//
201void TPoolAllocator::pop()
202{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000203 if (stack.size() < 1)
204 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000205
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000206 tHeader* page = stack.back().page;
207 currentPageOffset = stack.back().offset;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000208
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000209 while (inUseList != page) {
210 // invoke destructor to free allocation list
211 inUseList->~tHeader();
212
213 tHeader* nextInUse = inUseList->nextPage;
214 if (inUseList->pageCount > 1)
215 delete [] reinterpret_cast<char*>(inUseList);
216 else {
217 inUseList->nextPage = freeList;
218 freeList = inUseList;
219 }
220 inUseList = nextInUse;
221 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000222
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000223 stack.pop_back();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000224}
225
226//
227// Do a mass-deallocation of all the individual allocations
228// that have occurred.
229//
230void TPoolAllocator::popAll()
231{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000232 while (stack.size() > 0)
233 pop();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000234}
235
236void* TPoolAllocator::allocate(size_t numBytes)
237{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000238 // If we are using guard blocks, all allocations are bracketed by
239 // them: [guardblock][allocation][guardblock]. numBytes is how
240 // much memory the caller asked for. allocationSize is the total
241 // size including guard blocks. In release build,
242 // guardBlockSize=0 and this all gets optimized away.
243 size_t allocationSize = TAllocation::allocationSize(numBytes);
244
245 //
246 // Just keep some interesting statistics.
247 //
248 ++numCalls;
249 totalBytes += numBytes;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000250
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000251 //
252 // Do the allocation, most likely case first, for efficiency.
253 // This step could be moved to be inline sometime.
254 //
255 if (currentPageOffset + allocationSize <= pageSize) {
256 //
257 // Safe to allocate from currentPageOffset.
258 //
259 unsigned char* memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
260 currentPageOffset += allocationSize;
261 currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000262
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000263 return initializeAllocation(inUseList, memory, numBytes);
264 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000265
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000266 if (allocationSize + headerSkip > pageSize) {
267 //
268 // Do a multi-page allocation. Don't mix these with the others.
269 // The OS is efficient and allocating and free-ing multiple pages.
270 //
271 size_t numBytesToAlloc = allocationSize + headerSkip;
272 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
273 if (memory == 0)
274 return 0;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000275
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000276 // Use placement-new to initialize header
277 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
278 inUseList = memory;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000279
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000280 currentPageOffset = pageSize; // make next allocation come from a new page
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000281
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000282 // No guard blocks for multi-page allocations (yet)
283 return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
284 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000285
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000286 //
287 // Need a simple page to allocate from.
288 //
289 tHeader* memory;
290 if (freeList) {
291 memory = freeList;
292 freeList = freeList->nextPage;
293 } else {
294 memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
295 if (memory == 0)
296 return 0;
297 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000298
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000299 // Use placement-new to initialize header
300 new(memory) tHeader(inUseList, 1);
301 inUseList = memory;
302
303 unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
304 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000305
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000306 return initializeAllocation(inUseList, ret, numBytes);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000307}
308
309
310//
311// Check all allocations in a list for damage by calling check on each.
312//
313void TAllocation::checkAllocList() const
314{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000315 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
316 alloc->check();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000317}