blob: 9ef4f59f5cfbbea742b187262fe16137140cced3 [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
alokp@chromium.org79fb1012012-04-26 21:07:39 +000014#include "common/angleutils.h"
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000015#include "compiler/InitializeGlobals.h"
alokp@chromium.org4e89d232010-05-14 19:37:21 +000016#include "compiler/osinclude.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000017
alokp@chromium.orgff42c632010-05-10 15:14:30 +000018OS_TLSIndex PoolIndex = OS_INVALID_TLS_INDEX;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000019
20void InitializeGlobalPools()
21{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000022 TThreadGlobalPools* globalPools= static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
23 if (globalPools)
24 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000025
alokp@chromium.org4e89d232010-05-14 19:37:21 +000026 TThreadGlobalPools* threadData = new TThreadGlobalPools();
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000027 threadData->globalPoolAllocator = 0;
28
29 OS_SetTLSValue(PoolIndex, threadData);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000030}
31
32void FreeGlobalPools()
33{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000034 // Release the allocated memory for this thread.
35 TThreadGlobalPools* globalPools= static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
36 if (!globalPools)
37 return;
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000038
alokp@chromium.org4e89d232010-05-14 19:37:21 +000039 delete globalPools;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000040}
41
42bool InitializePoolIndex()
43{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000044 // Allocate a TLS index.
45 if ((PoolIndex = OS_AllocTLSIndex()) == OS_INVALID_TLS_INDEX)
46 return false;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000047
alokp@chromium.org4e89d232010-05-14 19:37:21 +000048 return true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000049}
50
51void FreePoolIndex()
52{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000053 // Release the TLS index.
54 OS_FreeTLSIndex(PoolIndex);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000055}
56
57TPoolAllocator& GetGlobalPoolAllocator()
58{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000059 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000060
alokp@chromium.org4e89d232010-05-14 19:37:21 +000061 return *threadData->globalPoolAllocator;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000062}
63
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000064void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000065{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000066 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000067
alokp@chromium.org4e89d232010-05-14 19:37:21 +000068 threadData->globalPoolAllocator = poolAllocator;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000069}
70
71//
72// Implement the functionality of the TPoolAllocator class, which
73// is documented in PoolAlloc.h.
74//
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000075TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
alokp@chromium.org4e89d232010-05-14 19:37:21 +000076 pageSize(growthIncrement),
77 alignment(allocationAlignment),
78 freeList(0),
79 inUseList(0),
alokp@chromium.org18895cb2010-10-14 16:09:57 +000080 numCalls(0),
81 totalBytes(0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000082{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000083 //
84 // Don't allow page sizes we know are smaller than all common
85 // OS page sizes.
86 //
87 if (pageSize < 4*1024)
88 pageSize = 4*1024;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000089
alokp@chromium.org4e89d232010-05-14 19:37:21 +000090 //
91 // A large currentPageOffset indicates a new page needs to
92 // be obtained to allocate memory.
93 //
94 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000095
alokp@chromium.org4e89d232010-05-14 19:37:21 +000096 //
97 // Adjust alignment to be at least pointer aligned and
98 // power of 2.
99 //
100 size_t minAlign = sizeof(void*);
101 alignment &= ~(minAlign - 1);
102 if (alignment < minAlign)
103 alignment = minAlign;
104 size_t a = 1;
105 while (a < alignment)
106 a <<= 1;
107 alignment = a;
108 alignmentMask = a - 1;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000109
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000110 //
111 // Align header skip
112 //
113 headerSkip = minAlign;
114 if (headerSkip < sizeof(tHeader)) {
115 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
116 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000117}
118
119TPoolAllocator::~TPoolAllocator()
120{
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000121 while (inUseList) {
122 tHeader* next = inUseList->nextPage;
123 inUseList->~tHeader();
124 delete [] reinterpret_cast<char*>(inUseList);
125 inUseList = next;
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000126 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000127
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000128 // We should not check the guard blocks
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000129 // here, because we did it already when the block was
130 // placed into the free list.
131 //
132 while (freeList) {
133 tHeader* next = freeList->nextPage;
134 delete [] reinterpret_cast<char*>(freeList);
135 freeList = next;
136 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000137}
138
139// Support MSVC++ 6.0
140const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
141const unsigned char TAllocation::guardBlockEndVal = 0xfe;
142const unsigned char TAllocation::userDataFill = 0xcd;
143
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000144#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000145 const size_t TAllocation::guardBlockSize = 16;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000146#else
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000147 const size_t TAllocation::guardBlockSize = 0;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000148#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000149
150//
151// Check a single guard block for damage
152//
153void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
154{
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000155#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000156 for (size_t x = 0; x < guardBlockSize; x++) {
157 if (blockMem[x] != val) {
158 char assertMsg[80];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000159
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000160 // We don't print the assert message. It's here just to be helpful.
apatrick@chromium.orge4319632012-01-27 22:13:17 +0000161#if defined(_MSC_VER)
kbr@chromium.orgddb6e8e2012-04-25 00:48:13 +0000162 snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %Iu byte allocation at 0x%p\n",
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000163 locText, size, data());
apatrick@chromium.orge4319632012-01-27 22:13:17 +0000164#else
kbr@chromium.orgddb6e8e2012-04-25 00:48:13 +0000165 snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n",
apatrick@chromium.orge4319632012-01-27 22:13:17 +0000166 locText, size, data());
167#endif
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000168 assert(0 && "PoolAlloc: Damage in guard block");
169 }
170 }
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000171#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000172}
173
174
175void TPoolAllocator::push()
176{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000177 tAllocState state = { currentPageOffset, inUseList };
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000178
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000179 stack.push_back(state);
180
181 //
182 // Indicate there is no current page to allocate from.
183 //
184 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000185}
186
187//
188// Do a mass-deallocation of all the individual allocations
189// that have occurred since the last push(), or since the
190// last pop(), or since the object's creation.
191//
192// The deallocated pages are saved for future allocations.
193//
194void TPoolAllocator::pop()
195{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000196 if (stack.size() < 1)
197 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000198
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000199 tHeader* page = stack.back().page;
200 currentPageOffset = stack.back().offset;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000201
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000202 while (inUseList != page) {
203 // invoke destructor to free allocation list
204 inUseList->~tHeader();
205
206 tHeader* nextInUse = inUseList->nextPage;
207 if (inUseList->pageCount > 1)
208 delete [] reinterpret_cast<char*>(inUseList);
209 else {
210 inUseList->nextPage = freeList;
211 freeList = inUseList;
212 }
213 inUseList = nextInUse;
214 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000215
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000216 stack.pop_back();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000217}
218
219//
220// Do a mass-deallocation of all the individual allocations
221// that have occurred.
222//
223void TPoolAllocator::popAll()
224{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000225 while (stack.size() > 0)
226 pop();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000227}
228
229void* TPoolAllocator::allocate(size_t numBytes)
230{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000231 // If we are using guard blocks, all allocations are bracketed by
232 // them: [guardblock][allocation][guardblock]. numBytes is how
233 // much memory the caller asked for. allocationSize is the total
234 // size including guard blocks. In release build,
235 // guardBlockSize=0 and this all gets optimized away.
236 size_t allocationSize = TAllocation::allocationSize(numBytes);
237
238 //
239 // Just keep some interesting statistics.
240 //
241 ++numCalls;
242 totalBytes += numBytes;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000243
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000244 //
245 // Do the allocation, most likely case first, for efficiency.
246 // This step could be moved to be inline sometime.
247 //
248 if (currentPageOffset + allocationSize <= pageSize) {
249 //
250 // Safe to allocate from currentPageOffset.
251 //
252 unsigned char* memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
253 currentPageOffset += allocationSize;
254 currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000255
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000256 return initializeAllocation(inUseList, memory, numBytes);
257 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000258
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000259 if (allocationSize + headerSkip > pageSize) {
260 //
261 // Do a multi-page allocation. Don't mix these with the others.
262 // The OS is efficient and allocating and free-ing multiple pages.
263 //
264 size_t numBytesToAlloc = allocationSize + headerSkip;
265 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
266 if (memory == 0)
267 return 0;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000268
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000269 // Use placement-new to initialize header
270 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
271 inUseList = memory;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000272
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000273 currentPageOffset = pageSize; // make next allocation come from a new page
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000274
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000275 // No guard blocks for multi-page allocations (yet)
276 return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
277 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000278
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000279 //
280 // Need a simple page to allocate from.
281 //
282 tHeader* memory;
283 if (freeList) {
284 memory = freeList;
285 freeList = freeList->nextPage;
286 } else {
287 memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
288 if (memory == 0)
289 return 0;
290 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000291
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000292 // Use placement-new to initialize header
293 new(memory) tHeader(inUseList, 1);
294 inUseList = memory;
295
296 unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
297 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000298
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000299 return initializeAllocation(inUseList, ret, numBytes);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000300}
301
302
303//
304// Check all allocations in a list for damage by calling check on each.
305//
306void TAllocation::checkAllocList() const
307{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000308 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
309 alloc->check();
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000310}