• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }