blob: 7e348ca97039ca47766aff2050d44e4cd16fba7c [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),
86 numCalls(0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000087{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000088 //
89 // Don't allow page sizes we know are smaller than all common
90 // OS page sizes.
91 //
92 if (pageSize < 4*1024)
93 pageSize = 4*1024;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000094
alokp@chromium.org4e89d232010-05-14 19:37:21 +000095 //
96 // A large currentPageOffset indicates a new page needs to
97 // be obtained to allocate memory.
98 //
99 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000100
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000101 //
102 // Adjust alignment to be at least pointer aligned and
103 // power of 2.
104 //
105 size_t minAlign = sizeof(void*);
106 alignment &= ~(minAlign - 1);
107 if (alignment < minAlign)
108 alignment = minAlign;
109 size_t a = 1;
110 while (a < alignment)
111 a <<= 1;
112 alignment = a;
113 alignmentMask = a - 1;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000114
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000115 //
116 // Align header skip
117 //
118 headerSkip = minAlign;
119 if (headerSkip < sizeof(tHeader)) {
120 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
121 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000122}
123
124TPoolAllocator::~TPoolAllocator()
125{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000126 if (!global) {
127 //
128 // Then we know that this object is not being
129 // allocated after other, globally scoped objects
130 // that depend on it. So we can delete the "in use" memory.
131 //
132 while (inUseList) {
133 tHeader* next = inUseList->nextPage;
134 inUseList->~tHeader();
135 delete [] reinterpret_cast<char*>(inUseList);
136 inUseList = next;
137 }
138 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000139
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000140 //
141 // Always delete the free list memory - it can't be being
142 // (correctly) referenced, whether the pool allocator was
143 // global or not. We should not check the guard blocks
144 // here, because we did it already when the block was
145 // placed into the free list.
146 //
147 while (freeList) {
148 tHeader* next = freeList->nextPage;
149 delete [] reinterpret_cast<char*>(freeList);
150 freeList = next;
151 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000152}
153
154// Support MSVC++ 6.0
155const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
156const unsigned char TAllocation::guardBlockEndVal = 0xfe;
157const unsigned char TAllocation::userDataFill = 0xcd;
158
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000159#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000160 const size_t TAllocation::guardBlockSize = 16;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000161#else
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000162 const size_t TAllocation::guardBlockSize = 0;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000163#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000164
165//
166// Check a single guard block for damage
167//
168void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
169{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000170 for (size_t x = 0; x < guardBlockSize; x++) {
171 if (blockMem[x] != val) {
172 char assertMsg[80];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000173
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000174 // We don't print the assert message. It's here just to be helpful.
alokp@chromium.org1bcc3fd2010-05-19 17:08:44 +0000175 sprintf(assertMsg, "PoolAlloc: Damage %s %u byte allocation at 0x%p\n",
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000176 locText, size, data());
177 assert(0 && "PoolAlloc: Damage in guard block");
178 }
179 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000180}
181
182
183void TPoolAllocator::push()
184{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000185 tAllocState state = { currentPageOffset, inUseList };
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000186
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000187 stack.push_back(state);
188
189 //
190 // Indicate there is no current page to allocate from.
191 //
192 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000193}
194
195//
196// Do a mass-deallocation of all the individual allocations
197// that have occurred since the last push(), or since the
198// last pop(), or since the object's creation.
199//
200// The deallocated pages are saved for future allocations.
201//
202void TPoolAllocator::pop()
203{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000204 if (stack.size() < 1)
205 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000206
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000207 tHeader* page = stack.back().page;
208 currentPageOffset = stack.back().offset;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000209
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000210 while (inUseList != page) {
211 // invoke destructor to free allocation list
212 inUseList->~tHeader();
213
214 tHeader* nextInUse = inUseList->nextPage;
215 if (inUseList->pageCount > 1)
216 delete [] reinterpret_cast<char*>(inUseList);
217 else {
218 inUseList->nextPage = freeList;
219 freeList = inUseList;
220 }
221 inUseList = nextInUse;
222 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000223
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000224 stack.pop_back();
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{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000233 while (stack.size() > 0)
234 pop();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000235}
236
237void* TPoolAllocator::allocate(size_t numBytes)
238{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000239 // If we are using guard blocks, all allocations are bracketed by
240 // them: [guardblock][allocation][guardblock]. numBytes is how
241 // much memory the caller asked for. allocationSize is the total
242 // size including guard blocks. In release build,
243 // guardBlockSize=0 and this all gets optimized away.
244 size_t allocationSize = TAllocation::allocationSize(numBytes);
245
246 //
247 // Just keep some interesting statistics.
248 //
249 ++numCalls;
250 totalBytes += numBytes;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000251
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000252 //
253 // Do the allocation, most likely case first, for efficiency.
254 // This step could be moved to be inline sometime.
255 //
256 if (currentPageOffset + allocationSize <= pageSize) {
257 //
258 // Safe to allocate from currentPageOffset.
259 //
260 unsigned char* memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
261 currentPageOffset += allocationSize;
262 currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000263
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000264 return initializeAllocation(inUseList, memory, numBytes);
265 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000266
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000267 if (allocationSize + headerSkip > pageSize) {
268 //
269 // Do a multi-page allocation. Don't mix these with the others.
270 // The OS is efficient and allocating and free-ing multiple pages.
271 //
272 size_t numBytesToAlloc = allocationSize + headerSkip;
273 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
274 if (memory == 0)
275 return 0;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000276
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000277 // Use placement-new to initialize header
278 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
279 inUseList = memory;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000280
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000281 currentPageOffset = pageSize; // make next allocation come from a new page
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000282
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000283 // No guard blocks for multi-page allocations (yet)
284 return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
285 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000286
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000287 //
288 // Need a simple page to allocate from.
289 //
290 tHeader* memory;
291 if (freeList) {
292 memory = freeList;
293 freeList = freeList->nextPage;
294 } else {
295 memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
296 if (memory == 0)
297 return 0;
298 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000299
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000300 // Use placement-new to initialize header
301 new(memory) tHeader(inUseList, 1);
302 inUseList = memory;
303
304 unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
305 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000306
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000307 return initializeAllocation(inUseList, ret, numBytes);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000308}
309
310
311//
312// Check all allocations in a list for damage by calling check on each.
313//
314void TAllocation::checkAllocList() const
315{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000316 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
317 alloc->check();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000318}