blob: abe70262f2d0d9ef0150255a33e65fdd0e933f57 [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
Geoff Lang17732822013-08-29 13:46:49 -04007#include "compiler/translator/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"
Geoff Lang17732822013-08-29 13:46:49 -040015#include "compiler/translator/InitializeGlobals.h"
16#include "compiler/translator/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
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000020bool InitializePoolIndex()
21{
Alok Priyadarshi8156b6b2013-09-23 14:56:58 -040022 assert(PoolIndex == OS_INVALID_TLS_INDEX);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000023
Alok Priyadarshi8156b6b2013-09-23 14:56:58 -040024 PoolIndex = OS_AllocTLSIndex();
25 return PoolIndex != OS_INVALID_TLS_INDEX;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000026}
27
28void FreePoolIndex()
29{
Alok Priyadarshi8156b6b2013-09-23 14:56:58 -040030 assert(PoolIndex != OS_INVALID_TLS_INDEX);
31
alokp@chromium.org4e89d232010-05-14 19:37:21 +000032 OS_FreeTLSIndex(PoolIndex);
Alok Priyadarshi8156b6b2013-09-23 14:56:58 -040033 PoolIndex = OS_INVALID_TLS_INDEX;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000034}
35
Alok Priyadarshi8156b6b2013-09-23 14:56:58 -040036TPoolAllocator* GetGlobalPoolAllocator()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000037{
Alok Priyadarshi8156b6b2013-09-23 14:56:58 -040038 assert(PoolIndex != OS_INVALID_TLS_INDEX);
39 return static_cast<TPoolAllocator*>(OS_GetTLSValue(PoolIndex));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000040}
41
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000042void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000043{
Alok Priyadarshi8156b6b2013-09-23 14:56:58 -040044 assert(PoolIndex != OS_INVALID_TLS_INDEX);
45 OS_SetTLSValue(PoolIndex, poolAllocator);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000046}
47
48//
49// Implement the functionality of the TPoolAllocator class, which
50// is documented in PoolAlloc.h.
51//
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000052TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
alokp@chromium.org4e89d232010-05-14 19:37:21 +000053 pageSize(growthIncrement),
54 alignment(allocationAlignment),
55 freeList(0),
56 inUseList(0),
alokp@chromium.org18895cb2010-10-14 16:09:57 +000057 numCalls(0),
58 totalBytes(0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000059{
alokp@chromium.org4e89d232010-05-14 19:37:21 +000060 //
61 // Don't allow page sizes we know are smaller than all common
62 // OS page sizes.
63 //
64 if (pageSize < 4*1024)
65 pageSize = 4*1024;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000066
alokp@chromium.org4e89d232010-05-14 19:37:21 +000067 //
68 // A large currentPageOffset indicates a new page needs to
69 // be obtained to allocate memory.
70 //
71 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000072
alokp@chromium.org4e89d232010-05-14 19:37:21 +000073 //
74 // Adjust alignment to be at least pointer aligned and
75 // power of 2.
76 //
77 size_t minAlign = sizeof(void*);
78 alignment &= ~(minAlign - 1);
79 if (alignment < minAlign)
80 alignment = minAlign;
81 size_t a = 1;
82 while (a < alignment)
83 a <<= 1;
84 alignment = a;
85 alignmentMask = a - 1;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000086
alokp@chromium.org4e89d232010-05-14 19:37:21 +000087 //
88 // Align header skip
89 //
90 headerSkip = minAlign;
91 if (headerSkip < sizeof(tHeader)) {
92 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
93 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000094}
95
96TPoolAllocator::~TPoolAllocator()
97{
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000098 while (inUseList) {
99 tHeader* next = inUseList->nextPage;
100 inUseList->~tHeader();
101 delete [] reinterpret_cast<char*>(inUseList);
102 inUseList = next;
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000103 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000104
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000105 // We should not check the guard blocks
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000106 // here, because we did it already when the block was
107 // placed into the free list.
108 //
109 while (freeList) {
110 tHeader* next = freeList->nextPage;
111 delete [] reinterpret_cast<char*>(freeList);
112 freeList = next;
113 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000114}
115
116// Support MSVC++ 6.0
117const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
118const unsigned char TAllocation::guardBlockEndVal = 0xfe;
119const unsigned char TAllocation::userDataFill = 0xcd;
120
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000121#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000122 const size_t TAllocation::guardBlockSize = 16;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000123#else
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000124 const size_t TAllocation::guardBlockSize = 0;
alokp@chromium.orgb1e8c6f2010-05-12 18:35:53 +0000125#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000126
127//
128// Check a single guard block for damage
129//
130void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
131{
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000132#ifdef GUARD_BLOCKS
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000133 for (size_t x = 0; x < guardBlockSize; x++) {
134 if (blockMem[x] != val) {
135 char assertMsg[80];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000136
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000137 // We don't print the assert message. It's here just to be helpful.
apatrick@chromium.orge4319632012-01-27 22:13:17 +0000138#if defined(_MSC_VER)
kbr@chromium.orgddb6e8e2012-04-25 00:48:13 +0000139 snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %Iu byte allocation at 0x%p\n",
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000140 locText, size, data());
apatrick@chromium.orge4319632012-01-27 22:13:17 +0000141#else
kbr@chromium.orgddb6e8e2012-04-25 00:48:13 +0000142 snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n",
apatrick@chromium.orge4319632012-01-27 22:13:17 +0000143 locText, size, data());
144#endif
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000145 assert(0 && "PoolAlloc: Damage in guard block");
146 }
147 }
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000148#endif
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000149}
150
151
152void TPoolAllocator::push()
153{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000154 tAllocState state = { currentPageOffset, inUseList };
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000155
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000156 stack.push_back(state);
157
158 //
159 // Indicate there is no current page to allocate from.
160 //
161 currentPageOffset = pageSize;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000162}
163
164//
165// Do a mass-deallocation of all the individual allocations
166// that have occurred since the last push(), or since the
167// last pop(), or since the object's creation.
168//
169// The deallocated pages are saved for future allocations.
170//
171void TPoolAllocator::pop()
172{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000173 if (stack.size() < 1)
174 return;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000175
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000176 tHeader* page = stack.back().page;
177 currentPageOffset = stack.back().offset;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000178
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000179 while (inUseList != page) {
180 // invoke destructor to free allocation list
181 inUseList->~tHeader();
182
183 tHeader* nextInUse = inUseList->nextPage;
184 if (inUseList->pageCount > 1)
185 delete [] reinterpret_cast<char*>(inUseList);
186 else {
187 inUseList->nextPage = freeList;
188 freeList = inUseList;
189 }
190 inUseList = nextInUse;
191 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000192
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000193 stack.pop_back();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000194}
195
196//
197// Do a mass-deallocation of all the individual allocations
198// that have occurred.
199//
200void TPoolAllocator::popAll()
201{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000202 while (stack.size() > 0)
203 pop();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000204}
205
206void* TPoolAllocator::allocate(size_t numBytes)
207{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000208 //
209 // Just keep some interesting statistics.
210 //
211 ++numCalls;
212 totalBytes += numBytes;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000213
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000214 // If we are using guard blocks, all allocations are bracketed by
215 // them: [guardblock][allocation][guardblock]. numBytes is how
216 // much memory the caller asked for. allocationSize is the total
217 // size including guard blocks. In release build,
218 // guardBlockSize=0 and this all gets optimized away.
219 size_t allocationSize = TAllocation::allocationSize(numBytes);
220 // Detect integer overflow.
221 if (allocationSize < numBytes)
222 return 0;
223
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000224 //
225 // Do the allocation, most likely case first, for efficiency.
226 // This step could be moved to be inline sometime.
227 //
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000228 if (allocationSize <= pageSize - currentPageOffset) {
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000229 //
230 // Safe to allocate from currentPageOffset.
231 //
232 unsigned char* memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
233 currentPageOffset += allocationSize;
234 currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000235
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000236 return initializeAllocation(inUseList, memory, numBytes);
237 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000238
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000239 if (allocationSize > pageSize - headerSkip) {
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000240 //
241 // Do a multi-page allocation. Don't mix these with the others.
242 // The OS is efficient and allocating and free-ing multiple pages.
243 //
244 size_t numBytesToAlloc = allocationSize + headerSkip;
shannonwoods@chromium.orgc7964842013-05-30 00:10:41 +0000245 // Detect integer overflow.
246 if (numBytesToAlloc < allocationSize)
247 return 0;
248
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000249 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
250 if (memory == 0)
251 return 0;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000252
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000253 // Use placement-new to initialize header
254 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
255 inUseList = memory;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000256
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000257 currentPageOffset = pageSize; // make next allocation come from a new page
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000258
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000259 // No guard blocks for multi-page allocations (yet)
260 return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
261 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000262
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000263 //
264 // Need a simple page to allocate from.
265 //
266 tHeader* memory;
267 if (freeList) {
268 memory = freeList;
269 freeList = freeList->nextPage;
270 } else {
271 memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
272 if (memory == 0)
273 return 0;
274 }
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, 1);
278 inUseList = memory;
279
280 unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
281 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000282
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000283 return initializeAllocation(inUseList, ret, numBytes);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000284}
285
286
287//
288// Check all allocations in a list for damage by calling check on each.
289//
290void TAllocation::checkAllocList() const
291{
alokp@chromium.org4e89d232010-05-14 19:37:21 +0000292 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
293 alloc->check();
daniel@transgaming.comb969cc52011-03-15 18:23:59 +0000294}