1 //
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
7 #include "compiler/PoolAlloc.h"
8
9 #ifndef _MSC_VER
10 #include <stdint.h>
11 #endif
12 #include <stdio.h>
13
14 #include "compiler/InitializeGlobals.h"
15 #include "compiler/osinclude.h"
16
17 OS_TLSIndex PoolIndex = OS_INVALID_TLS_INDEX;
18
InitializeGlobalPools()19 void InitializeGlobalPools()
20 {
21 TThreadGlobalPools* globalPools= static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
22 if (globalPools)
23 return;
24
25 TThreadGlobalPools* threadData = new TThreadGlobalPools();
26 threadData->globalPoolAllocator = 0;
27
28 OS_SetTLSValue(PoolIndex, threadData);
29 }
30
FreeGlobalPools()31 void FreeGlobalPools()
32 {
33 // Release the allocated memory for this thread.
34 TThreadGlobalPools* globalPools= static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
35 if (!globalPools)
36 return;
37
38 delete globalPools;
39 }
40
InitializePoolIndex()41 bool InitializePoolIndex()
42 {
43 // Allocate a TLS index.
44 if ((PoolIndex = OS_AllocTLSIndex()) == OS_INVALID_TLS_INDEX)
45 return false;
46
47 return true;
48 }
49
FreePoolIndex()50 void FreePoolIndex()
51 {
52 // Release the TLS index.
53 OS_FreeTLSIndex(PoolIndex);
54 }
55
GetGlobalPoolAllocator()56 TPoolAllocator& GetGlobalPoolAllocator()
57 {
58 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
59
60 return *threadData->globalPoolAllocator;
61 }
62
SetGlobalPoolAllocator(TPoolAllocator * poolAllocator)63 void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator)
64 {
65 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
66
67 threadData->globalPoolAllocator = poolAllocator;
68 }
69
70 //
71 // Implement the functionality of the TPoolAllocator class, which
72 // is documented in PoolAlloc.h.
73 //
TPoolAllocator(int growthIncrement,int allocationAlignment)74 TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
75 pageSize(growthIncrement),
76 alignment(allocationAlignment),
77 freeList(0),
78 inUseList(0),
79 numCalls(0),
80 totalBytes(0)
81 {
82 //
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;
88
89 //
90 // A large currentPageOffset indicates a new page needs to
91 // be obtained to allocate memory.
92 //
93 currentPageOffset = pageSize;
94
95 //
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;
108
109 //
110 // Align header skip
111 //
112 headerSkip = minAlign;
113 if (headerSkip < sizeof(tHeader)) {
114 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
115 }
116 }
117
~TPoolAllocator()118 TPoolAllocator::~TPoolAllocator()
119 {
120 while (inUseList) {
121 tHeader* next = inUseList->nextPage;
122 inUseList->~tHeader();
123 delete [] reinterpret_cast<char*>(inUseList);
124 inUseList = next;
125 }
126
127 // We should not check the guard blocks
128 // 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 }
136 }
137
138 // Support MSVC++ 6.0
139 const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
140 const unsigned char TAllocation::guardBlockEndVal = 0xfe;
141 const unsigned char TAllocation::userDataFill = 0xcd;
142
143 #ifdef GUARD_BLOCKS
144 const size_t TAllocation::guardBlockSize = 16;
145 #else
146 const size_t TAllocation::guardBlockSize = 0;
147 #endif
148
149 //
150 // Check a single guard block for damage
151 //
checkGuardBlock(unsigned char * blockMem,unsigned char val,const char * locText) const152 void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
153 {
154 for (size_t x = 0; x < guardBlockSize; x++) {
155 if (blockMem[x] != val) {
156 char assertMsg[80];
157
158 // We don't print the assert message. It's here just to be helpful.
159 sprintf(assertMsg, "PoolAlloc: Damage %s %u byte allocation at 0x%p\n",
160 locText, size, data());
161 assert(0 && "PoolAlloc: Damage in guard block");
162 }
163 }
164 }
165
166
push()167 void TPoolAllocator::push()
168 {
169 tAllocState state = { currentPageOffset, inUseList };
170
171 stack.push_back(state);
172
173 //
174 // Indicate there is no current page to allocate from.
175 //
176 currentPageOffset = pageSize;
177 }
178
179 //
180 // Do a mass-deallocation of all the individual allocations
181 // that have occurred since the last push(), or since the
182 // last pop(), or since the object's creation.
183 //
184 // The deallocated pages are saved for future allocations.
185 //
pop()186 void TPoolAllocator::pop()
187 {
188 if (stack.size() < 1)
189 return;
190
191 tHeader* page = stack.back().page;
192 currentPageOffset = stack.back().offset;
193
194 while (inUseList != page) {
195 // invoke destructor to free allocation list
196 inUseList->~tHeader();
197
198 tHeader* nextInUse = inUseList->nextPage;
199 if (inUseList->pageCount > 1)
200 delete [] reinterpret_cast<char*>(inUseList);
201 else {
202 inUseList->nextPage = freeList;
203 freeList = inUseList;
204 }
205 inUseList = nextInUse;
206 }
207
208 stack.pop_back();
209 }
210
211 //
212 // Do a mass-deallocation of all the individual allocations
213 // that have occurred.
214 //
popAll()215 void TPoolAllocator::popAll()
216 {
217 while (stack.size() > 0)
218 pop();
219 }
220
allocate(size_t numBytes)221 void* TPoolAllocator::allocate(size_t numBytes)
222 {
223 // If we are using guard blocks, all allocations are bracketed by
224 // them: [guardblock][allocation][guardblock]. numBytes is how
225 // much memory the caller asked for. allocationSize is the total
226 // size including guard blocks. In release build,
227 // guardBlockSize=0 and this all gets optimized away.
228 size_t allocationSize = TAllocation::allocationSize(numBytes);
229
230 //
231 // Just keep some interesting statistics.
232 //
233 ++numCalls;
234 totalBytes += numBytes;
235
236 //
237 // Do the allocation, most likely case first, for efficiency.
238 // This step could be moved to be inline sometime.
239 //
240 if (currentPageOffset + allocationSize <= pageSize) {
241 //
242 // Safe to allocate from currentPageOffset.
243 //
244 unsigned char* memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
245 currentPageOffset += allocationSize;
246 currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
247
248 return initializeAllocation(inUseList, memory, numBytes);
249 }
250
251 if (allocationSize + headerSkip > pageSize) {
252 //
253 // Do a multi-page allocation. Don't mix these with the others.
254 // The OS is efficient and allocating and free-ing multiple pages.
255 //
256 size_t numBytesToAlloc = allocationSize + headerSkip;
257 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
258 if (memory == 0)
259 return 0;
260
261 // Use placement-new to initialize header
262 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
263 inUseList = memory;
264
265 currentPageOffset = pageSize; // make next allocation come from a new page
266
267 // No guard blocks for multi-page allocations (yet)
268 return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
269 }
270
271 //
272 // Need a simple page to allocate from.
273 //
274 tHeader* memory;
275 if (freeList) {
276 memory = freeList;
277 freeList = freeList->nextPage;
278 } else {
279 memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
280 if (memory == 0)
281 return 0;
282 }
283
284 // Use placement-new to initialize header
285 new(memory) tHeader(inUseList, 1);
286 inUseList = memory;
287
288 unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
289 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
290
291 return initializeAllocation(inUseList, ret, numBytes);
292 }
293
294
295 //
296 // Check all allocations in a list for damage by calling check on each.
297 //
checkAllocList() const298 void TAllocation::checkAllocList() const
299 {
300 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
301 alloc->check();
302 }