|
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 "common/angleutils.h" |
|
15 #include "compiler/InitializeGlobals.h" |
|
16 #include "compiler/osinclude.h" |
|
17 |
|
18 OS_TLSIndex PoolIndex = OS_INVALID_TLS_INDEX; |
|
19 |
|
20 bool InitializePoolIndex() |
|
21 { |
|
22 assert(PoolIndex == OS_INVALID_TLS_INDEX); |
|
23 |
|
24 PoolIndex = OS_AllocTLSIndex(); |
|
25 return PoolIndex != OS_INVALID_TLS_INDEX; |
|
26 } |
|
27 |
|
28 void FreePoolIndex() |
|
29 { |
|
30 assert(PoolIndex != OS_INVALID_TLS_INDEX); |
|
31 |
|
32 OS_FreeTLSIndex(PoolIndex); |
|
33 PoolIndex = OS_INVALID_TLS_INDEX; |
|
34 } |
|
35 |
|
36 TPoolAllocator* GetGlobalPoolAllocator() |
|
37 { |
|
38 assert(PoolIndex != OS_INVALID_TLS_INDEX); |
|
39 return static_cast<TPoolAllocator*>(OS_GetTLSValue(PoolIndex)); |
|
40 } |
|
41 |
|
42 void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator) |
|
43 { |
|
44 assert(PoolIndex != OS_INVALID_TLS_INDEX); |
|
45 OS_SetTLSValue(PoolIndex, poolAllocator); |
|
46 } |
|
47 |
|
48 // |
|
49 // Implement the functionality of the TPoolAllocator class, which |
|
50 // is documented in PoolAlloc.h. |
|
51 // |
|
52 TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) : |
|
53 pageSize(growthIncrement), |
|
54 alignment(allocationAlignment), |
|
55 freeList(0), |
|
56 inUseList(0), |
|
57 numCalls(0), |
|
58 totalBytes(0) |
|
59 { |
|
60 // |
|
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; |
|
66 |
|
67 // |
|
68 // A large currentPageOffset indicates a new page needs to |
|
69 // be obtained to allocate memory. |
|
70 // |
|
71 currentPageOffset = pageSize; |
|
72 |
|
73 // |
|
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; |
|
86 |
|
87 // |
|
88 // Align header skip |
|
89 // |
|
90 headerSkip = minAlign; |
|
91 if (headerSkip < sizeof(tHeader)) { |
|
92 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask; |
|
93 } |
|
94 } |
|
95 |
|
96 TPoolAllocator::~TPoolAllocator() |
|
97 { |
|
98 while (inUseList) { |
|
99 tHeader* next = inUseList->nextPage; |
|
100 inUseList->~tHeader(); |
|
101 delete [] reinterpret_cast<char*>(inUseList); |
|
102 inUseList = next; |
|
103 } |
|
104 |
|
105 // We should not check the guard blocks |
|
106 // 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 } |
|
114 } |
|
115 |
|
116 // Support MSVC++ 6.0 |
|
117 const unsigned char TAllocation::guardBlockBeginVal = 0xfb; |
|
118 const unsigned char TAllocation::guardBlockEndVal = 0xfe; |
|
119 const unsigned char TAllocation::userDataFill = 0xcd; |
|
120 |
|
121 #ifdef GUARD_BLOCKS |
|
122 const size_t TAllocation::guardBlockSize = 16; |
|
123 #else |
|
124 const size_t TAllocation::guardBlockSize = 0; |
|
125 #endif |
|
126 |
|
127 // |
|
128 // Check a single guard block for damage |
|
129 // |
|
130 void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const |
|
131 { |
|
132 #ifdef GUARD_BLOCKS |
|
133 for (size_t x = 0; x < guardBlockSize; x++) { |
|
134 if (blockMem[x] != val) { |
|
135 char assertMsg[80]; |
|
136 |
|
137 // We don't print the assert message. It's here just to be helpful. |
|
138 #if defined(_MSC_VER) |
|
139 snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %Iu byte allocation at 0x%p\n", |
|
140 locText, size, data()); |
|
141 #else |
|
142 snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n", |
|
143 locText, size, data()); |
|
144 #endif |
|
145 assert(0 && "PoolAlloc: Damage in guard block"); |
|
146 } |
|
147 } |
|
148 #endif |
|
149 } |
|
150 |
|
151 |
|
152 void TPoolAllocator::push() |
|
153 { |
|
154 tAllocState state = { currentPageOffset, inUseList }; |
|
155 |
|
156 stack.push_back(state); |
|
157 |
|
158 // |
|
159 // Indicate there is no current page to allocate from. |
|
160 // |
|
161 currentPageOffset = pageSize; |
|
162 } |
|
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 // |
|
171 void TPoolAllocator::pop() |
|
172 { |
|
173 if (stack.size() < 1) |
|
174 return; |
|
175 |
|
176 tHeader* page = stack.back().page; |
|
177 currentPageOffset = stack.back().offset; |
|
178 |
|
179 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 } |
|
192 |
|
193 stack.pop_back(); |
|
194 } |
|
195 |
|
196 // |
|
197 // Do a mass-deallocation of all the individual allocations |
|
198 // that have occurred. |
|
199 // |
|
200 void TPoolAllocator::popAll() |
|
201 { |
|
202 while (stack.size() > 0) |
|
203 pop(); |
|
204 } |
|
205 |
|
206 void* TPoolAllocator::allocate(size_t numBytes) |
|
207 { |
|
208 // |
|
209 // Just keep some interesting statistics. |
|
210 // |
|
211 ++numCalls; |
|
212 totalBytes += numBytes; |
|
213 |
|
214 // 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 |
|
224 // |
|
225 // Do the allocation, most likely case first, for efficiency. |
|
226 // This step could be moved to be inline sometime. |
|
227 // |
|
228 if (allocationSize <= pageSize - currentPageOffset) { |
|
229 // |
|
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; |
|
235 |
|
236 return initializeAllocation(inUseList, memory, numBytes); |
|
237 } |
|
238 |
|
239 if (allocationSize > pageSize - headerSkip) { |
|
240 // |
|
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; |
|
245 // Detect integer overflow. |
|
246 if (numBytesToAlloc < allocationSize) |
|
247 return 0; |
|
248 |
|
249 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]); |
|
250 if (memory == 0) |
|
251 return 0; |
|
252 |
|
253 // Use placement-new to initialize header |
|
254 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize); |
|
255 inUseList = memory; |
|
256 |
|
257 currentPageOffset = pageSize; // make next allocation come from a new page |
|
258 |
|
259 // No guard blocks for multi-page allocations (yet) |
|
260 return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip); |
|
261 } |
|
262 |
|
263 // |
|
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 } |
|
275 |
|
276 // 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; |
|
282 |
|
283 return initializeAllocation(inUseList, ret, numBytes); |
|
284 } |
|
285 |
|
286 |
|
287 // |
|
288 // Check all allocations in a list for damage by calling check on each. |
|
289 // |
|
290 void TAllocation::checkAllocList() const |
|
291 { |
|
292 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc) |
|
293 alloc->check(); |
|
294 } |