blob: 89e8cfa8c7ef758972d3d9e3eb87cd4fbdb331ed [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
daniel@transgaming.combbf56f72010-04-20 18:52:13 +00009#include "compiler/Common.h"
10#include "compiler/InitializeGlobals.h"
alokp@chromium.org4e89d232010-05-14 19:37:21 +000011#include "compiler/osinclude.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000012
alokp@chromium.orgff42c632010-05-10 15:14:30 +000013OS_TLSIndex PoolIndex = OS_INVALID_TLS_INDEX;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000014
15void InitializeGlobalPools()
16{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000017 TThreadGlobalPools* globalPools= static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
18 if (globalPools)
19 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000020
alokp@chromium.org4e89d232010-05-14 19:37:21 +000021 TPoolAllocator *globalPoolAllocator = new TPoolAllocator(true);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000022
alokp@chromium.org4e89d232010-05-14 19:37:21 +000023 TThreadGlobalPools* threadData = new TThreadGlobalPools();
24
25 threadData->globalPoolAllocator = globalPoolAllocator;
26
27 OS_SetTLSValue(PoolIndex, threadData);
28 globalPoolAllocator->push();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000029}
30
31void FreeGlobalPools()
32{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000033 // Release the allocated memory for this thread.
34 TThreadGlobalPools* globalPools= static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
35 if (!globalPools)
36 return;
37
38 GlobalPoolAllocator.popAll();
39 delete &GlobalPoolAllocator;
40 delete globalPools;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000041}
42
43bool InitializePoolIndex()
44{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000045 // Allocate a TLS index.
46 if ((PoolIndex = OS_AllocTLSIndex()) == OS_INVALID_TLS_INDEX)
47 return false;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000048
alokp@chromium.org4e89d232010-05-14 19:37:21 +000049 return true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000050}
51
52void FreePoolIndex()
53{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000054 // Release the TLS index.
55 OS_FreeTLSIndex(PoolIndex);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000056}
57
58TPoolAllocator& GetGlobalPoolAllocator()
59{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000060 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000061
alokp@chromium.org4e89d232010-05-14 19:37:21 +000062 return *threadData->globalPoolAllocator;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000063}
64
65void SetGlobalPoolAllocatorPtr(TPoolAllocator* poolAllocator)
66{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000067 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000068
alokp@chromium.org4e89d232010-05-14 19:37:21 +000069 threadData->globalPoolAllocator = poolAllocator;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000070}
71
72//
73// Implement the functionality of the TPoolAllocator class, which
74// is documented in PoolAlloc.h.
75//
76TPoolAllocator::TPoolAllocator(bool g, int growthIncrement, int allocationAlignment) :
alokp@chromium.org4e89d232010-05-14 19:37:21 +000077 global(g),
78 pageSize(growthIncrement),
79 alignment(allocationAlignment),
80 freeList(0),
81 inUseList(0),
82 numCalls(0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000083{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000084 //
85 // Don't allow page sizes we know are smaller than all common
86 // OS page sizes.
87 //
88 if (pageSize < 4*1024)
89 pageSize = 4*1024;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000090
alokp@chromium.org4e89d232010-05-14 19:37:21 +000091 //
92 // A large currentPageOffset indicates a new page needs to
93 // be obtained to allocate memory.
94 //
95 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000096
alokp@chromium.org4e89d232010-05-14 19:37:21 +000097 //
98 // Adjust alignment to be at least pointer aligned and
99 // power of 2.
100 //
101 size_t minAlign = sizeof(void*);
102 alignment &= ~(minAlign - 1);
103 if (alignment < minAlign)
104 alignment = minAlign;
105 size_t a = 1;
106 while (a < alignment)
107 a <<= 1;
108 alignment = a;
109 alignmentMask = a - 1;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000110
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000111 //
112 // Align header skip
113 //
114 headerSkip = minAlign;
115 if (headerSkip < sizeof(tHeader)) {
116 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
117 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000118}
119
120TPoolAllocator::~TPoolAllocator()
121{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000122 if (!global) {
123 //
124 // Then we know that this object is not being
125 // allocated after other, globally scoped objects
126 // that depend on it. So we can delete the "in use" memory.
127 //
128 while (inUseList) {
129 tHeader* next = inUseList->nextPage;
130 inUseList->~tHeader();
131 delete [] reinterpret_cast<char*>(inUseList);
132 inUseList = next;
133 }
134 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000135
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000136 //
137 // Always delete the free list memory - it can't be being
138 // (correctly) referenced, whether the pool allocator was
139 // global or not. We should not check the guard blocks
140 // here, because we did it already when the block was
141 // placed into the free list.
142 //
143 while (freeList) {
144 tHeader* next = freeList->nextPage;
145 delete [] reinterpret_cast<char*>(freeList);
146 freeList = next;
147 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000148}
149
150// Support MSVC++ 6.0
151const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
152const unsigned char TAllocation::guardBlockEndVal = 0xfe;
153const unsigned char TAllocation::userDataFill = 0xcd;
154
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000155#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000156 const size_t TAllocation::guardBlockSize = 16;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000157#else
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000158 const size_t TAllocation::guardBlockSize = 0;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000159#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000160
161//
162// Check a single guard block for damage
163//
164void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
165{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000166 for (size_t x = 0; x < guardBlockSize; x++) {
167 if (blockMem[x] != val) {
168 char assertMsg[80];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000169
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000170 // We don't print the assert message. It's here just to be helpful.
171 sprintf(assertMsg, "PoolAlloc: Damage %s %lu byte allocation at 0x%p\n",
172 locText, size, data());
173 assert(0 && "PoolAlloc: Damage in guard block");
174 }
175 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000176}
177
178
179void TPoolAllocator::push()
180{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000181 tAllocState state = { currentPageOffset, inUseList };
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000182
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000183 stack.push_back(state);
184
185 //
186 // Indicate there is no current page to allocate from.
187 //
188 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000189}
190
191//
192// Do a mass-deallocation of all the individual allocations
193// that have occurred since the last push(), or since the
194// last pop(), or since the object's creation.
195//
196// The deallocated pages are saved for future allocations.
197//
198void TPoolAllocator::pop()
199{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000200 if (stack.size() < 1)
201 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000202
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000203 tHeader* page = stack.back().page;
204 currentPageOffset = stack.back().offset;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000205
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000206 while (inUseList != page) {
207 // invoke destructor to free allocation list
208 inUseList->~tHeader();
209
210 tHeader* nextInUse = inUseList->nextPage;
211 if (inUseList->pageCount > 1)
212 delete [] reinterpret_cast<char*>(inUseList);
213 else {
214 inUseList->nextPage = freeList;
215 freeList = inUseList;
216 }
217 inUseList = nextInUse;
218 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000219
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000220 stack.pop_back();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000221}
222
223//
224// Do a mass-deallocation of all the individual allocations
225// that have occurred.
226//
227void TPoolAllocator::popAll()
228{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000229 while (stack.size() > 0)
230 pop();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000231}
232
233void* TPoolAllocator::allocate(size_t numBytes)
234{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000235 // If we are using guard blocks, all allocations are bracketed by
236 // them: [guardblock][allocation][guardblock]. numBytes is how
237 // much memory the caller asked for. allocationSize is the total
238 // size including guard blocks. In release build,
239 // guardBlockSize=0 and this all gets optimized away.
240 size_t allocationSize = TAllocation::allocationSize(numBytes);
241
242 //
243 // Just keep some interesting statistics.
244 //
245 ++numCalls;
246 totalBytes += numBytes;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000247
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000248 //
249 // Do the allocation, most likely case first, for efficiency.
250 // This step could be moved to be inline sometime.
251 //
252 if (currentPageOffset + allocationSize <= pageSize) {
253 //
254 // Safe to allocate from currentPageOffset.
255 //
256 unsigned char* memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
257 currentPageOffset += allocationSize;
258 currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000259
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000260 return initializeAllocation(inUseList, memory, numBytes);
261 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000262
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000263 if (allocationSize + headerSkip > pageSize) {
264 //
265 // Do a multi-page allocation. Don't mix these with the others.
266 // The OS is efficient and allocating and free-ing multiple pages.
267 //
268 size_t numBytesToAlloc = allocationSize + headerSkip;
269 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
270 if (memory == 0)
271 return 0;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000272
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000273 // Use placement-new to initialize header
274 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
275 inUseList = memory;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000276
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000277 currentPageOffset = pageSize; // make next allocation come from a new page
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000278
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000279 // No guard blocks for multi-page allocations (yet)
280 return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
281 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000282
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000283 //
284 // Need a simple page to allocate from.
285 //
286 tHeader* memory;
287 if (freeList) {
288 memory = freeList;
289 freeList = freeList->nextPage;
290 } else {
291 memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
292 if (memory == 0)
293 return 0;
294 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000295
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000296 // Use placement-new to initialize header
297 new(memory) tHeader(inUseList, 1);
298 inUseList = memory;
299
300 unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
301 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000302
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000303 return initializeAllocation(inUseList, ret, numBytes);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000304}
305
306
307//
308// Check all allocations in a list for damage by calling check on each.
309//
310void TAllocation::checkAllocList() const
311{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000312 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
313 alloc->check();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000314}