blob: e286c7d206dd02c3e8f86f43b861e201922f1767 [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 TThreadGlobalPools* threadData = new TThreadGlobalPools();
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000026 threadData->globalPoolAllocator = 0;
27
28 OS_SetTLSValue(PoolIndex, threadData);
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;
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000037
alokp@chromium.org4e89d232010-05-14 19:37:21 +000038 delete globalPools;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000039}
40
41bool InitializePoolIndex()
42{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000043 // Allocate a TLS index.
44 if ((PoolIndex = OS_AllocTLSIndex()) == OS_INVALID_TLS_INDEX)
45 return false;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000046
alokp@chromium.org4e89d232010-05-14 19:37:21 +000047 return true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000048}
49
50void FreePoolIndex()
51{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000052 // Release the TLS index.
53 OS_FreeTLSIndex(PoolIndex);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000054}
55
56TPoolAllocator& GetGlobalPoolAllocator()
57{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000058 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000059
alokp@chromium.org4e89d232010-05-14 19:37:21 +000060 return *threadData->globalPoolAllocator;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000061}
62
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000063void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000064{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000065 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000066
alokp@chromium.org4e89d232010-05-14 19:37:21 +000067 threadData->globalPoolAllocator = poolAllocator;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000068}
69
70//
71// Implement the functionality of the TPoolAllocator class, which
72// is documented in PoolAlloc.h.
73//
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000074TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
alokp@chromium.org4e89d232010-05-14 19:37:21 +000075 pageSize(growthIncrement),
76 alignment(allocationAlignment),
77 freeList(0),
78 inUseList(0),
alokp@chromium.org18895cb2010-10-14 16:09:57 +000079 numCalls(0),
80 totalBytes(0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000081{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000082 //
83 // Don't allow page sizes we know are smaller than all common
84 // OS page sizes.
85 //
86 if (pageSize < 4*1024)
87 pageSize = 4*1024;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000088
alokp@chromium.org4e89d232010-05-14 19:37:21 +000089 //
90 // A large currentPageOffset indicates a new page needs to
91 // be obtained to allocate memory.
92 //
93 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000094
alokp@chromium.org4e89d232010-05-14 19:37:21 +000095 //
96 // Adjust alignment to be at least pointer aligned and
97 // power of 2.
98 //
99 size_t minAlign = sizeof(void*);
100 alignment &= ~(minAlign - 1);
101 if (alignment < minAlign)
102 alignment = minAlign;
103 size_t a = 1;
104 while (a < alignment)
105 a <<= 1;
106 alignment = a;
107 alignmentMask = a - 1;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000108
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000109 //
110 // Align header skip
111 //
112 headerSkip = minAlign;
113 if (headerSkip < sizeof(tHeader)) {
114 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
115 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000116}
117
118TPoolAllocator::~TPoolAllocator()
119{
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000120 while (inUseList) {
121 tHeader* next = inUseList->nextPage;
122 inUseList->~tHeader();
123 delete [] reinterpret_cast<char*>(inUseList);
124 inUseList = next;
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000125 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000126
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000127 // We should not check the guard blocks
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000128 // here, because we did it already when the block was
129 // placed into the free list.
130 //
131 while (freeList) {
132 tHeader* next = freeList->nextPage;
133 delete [] reinterpret_cast<char*>(freeList);
134 freeList = next;
135 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000136}
137
138// Support MSVC++ 6.0
139const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
140const unsigned char TAllocation::guardBlockEndVal = 0xfe;
141const unsigned char TAllocation::userDataFill = 0xcd;
142
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000143#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000144 const size_t TAllocation::guardBlockSize = 16;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000145#else
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000146 const size_t TAllocation::guardBlockSize = 0;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000147#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000148
149//
150// Check a single guard block for damage
151//
152void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
153{
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000154#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000155 for (size_t x = 0; x < guardBlockSize; x++) {
156 if (blockMem[x] != val) {
157 char assertMsg[80];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000158
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000159 // We don't print the assert message. It's here just to be helpful.
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000160 sprintf(assertMsg, "PoolAlloc: Damage %s %lu byte allocation at 0x%p\n",
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000161 locText, size, data());
162 assert(0 && "PoolAlloc: Damage in guard block");
163 }
164 }
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000165#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000166}
167
168
169void TPoolAllocator::push()
170{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000171 tAllocState state = { currentPageOffset, inUseList };
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000172
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000173 stack.push_back(state);
174
175 //
176 // Indicate there is no current page to allocate from.
177 //
178 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000179}
180
181//
182// Do a mass-deallocation of all the individual allocations
183// that have occurred since the last push(), or since the
184// last pop(), or since the object's creation.
185//
186// The deallocated pages are saved for future allocations.
187//
188void TPoolAllocator::pop()
189{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000190 if (stack.size() < 1)
191 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000192
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000193 tHeader* page = stack.back().page;
194 currentPageOffset = stack.back().offset;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000195
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000196 while (inUseList != page) {
197 // invoke destructor to free allocation list
198 inUseList->~tHeader();
199
200 tHeader* nextInUse = inUseList->nextPage;
201 if (inUseList->pageCount > 1)
202 delete [] reinterpret_cast<char*>(inUseList);
203 else {
204 inUseList->nextPage = freeList;
205 freeList = inUseList;
206 }
207 inUseList = nextInUse;
208 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000209
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000210 stack.pop_back();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000211}
212
213//
214// Do a mass-deallocation of all the individual allocations
215// that have occurred.
216//
217void TPoolAllocator::popAll()
218{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000219 while (stack.size() > 0)
220 pop();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000221}
222
223void* TPoolAllocator::allocate(size_t numBytes)
224{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000225 // If we are using guard blocks, all allocations are bracketed by
226 // them: [guardblock][allocation][guardblock]. numBytes is how
227 // much memory the caller asked for. allocationSize is the total
228 // size including guard blocks. In release build,
229 // guardBlockSize=0 and this all gets optimized away.
230 size_t allocationSize = TAllocation::allocationSize(numBytes);
231
232 //
233 // Just keep some interesting statistics.
234 //
235 ++numCalls;
236 totalBytes += numBytes;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000237
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000238 //
239 // Do the allocation, most likely case first, for efficiency.
240 // This step could be moved to be inline sometime.
241 //
242 if (currentPageOffset + allocationSize <= pageSize) {
243 //
244 // Safe to allocate from currentPageOffset.
245 //
246 unsigned char* memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
247 currentPageOffset += allocationSize;
248 currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000249
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000250 return initializeAllocation(inUseList, memory, numBytes);
251 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000252
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000253 if (allocationSize + headerSkip > pageSize) {
254 //
255 // Do a multi-page allocation. Don't mix these with the others.
256 // The OS is efficient and allocating and free-ing multiple pages.
257 //
258 size_t numBytesToAlloc = allocationSize + headerSkip;
259 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
260 if (memory == 0)
261 return 0;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000262
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000263 // Use placement-new to initialize header
264 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
265 inUseList = memory;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000266
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000267 currentPageOffset = pageSize; // make next allocation come from a new page
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000268
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000269 // No guard blocks for multi-page allocations (yet)
270 return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
271 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000272
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000273 //
274 // Need a simple page to allocate from.
275 //
276 tHeader* memory;
277 if (freeList) {
278 memory = freeList;
279 freeList = freeList->nextPage;
280 } else {
281 memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
282 if (memory == 0)
283 return 0;
284 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000285
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000286 // Use placement-new to initialize header
287 new(memory) tHeader(inUseList, 1);
288 inUseList = memory;
289
290 unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
291 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000292
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000293 return initializeAllocation(inUseList, ret, numBytes);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000294}
295
296
297//
298// Check all allocations in a list for damage by calling check on each.
299//
300void TAllocation::checkAllocList() const
301{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000302 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
303 alloc->check();
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000304}