1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "PoolAlloc.h"
16
17 #ifndef _MSC_VER
18 #include <stdint.h>
19 #endif
20 #include <stdio.h>
21
22 #include "InitializeGlobals.h"
23 #include "osinclude.h"
24
25 OS_TLSIndex PoolIndex = OS_INVALID_TLS_INDEX;
26
InitializePoolIndex()27 bool InitializePoolIndex()
28 {
29 assert(PoolIndex == OS_INVALID_TLS_INDEX);
30
31 PoolIndex = OS_AllocTLSIndex();
32 return PoolIndex != OS_INVALID_TLS_INDEX;
33 }
34
FreePoolIndex()35 void FreePoolIndex()
36 {
37 assert(PoolIndex != OS_INVALID_TLS_INDEX);
38
39 OS_FreeTLSIndex(PoolIndex);
40 PoolIndex = OS_INVALID_TLS_INDEX;
41 }
42
GetGlobalPoolAllocator()43 TPoolAllocator* GetGlobalPoolAllocator()
44 {
45 assert(PoolIndex != OS_INVALID_TLS_INDEX);
46 return static_cast<TPoolAllocator*>(OS_GetTLSValue(PoolIndex));
47 }
48
SetGlobalPoolAllocator(TPoolAllocator * poolAllocator)49 void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator)
50 {
51 assert(PoolIndex != OS_INVALID_TLS_INDEX);
52 OS_SetTLSValue(PoolIndex, poolAllocator);
53 }
54
55 //
56 // Implement the functionality of the TPoolAllocator class, which
57 // is documented in PoolAlloc.h.
58 //
TPoolAllocator(int growthIncrement,int allocationAlignment)59 TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
60 pageSize(growthIncrement),
61 alignment(allocationAlignment),
62 freeList(0),
63 inUseList(0),
64 numCalls(0),
65 totalBytes(0)
66 {
67 //
68 // Don't allow page sizes we know are smaller than all common
69 // OS page sizes.
70 //
71 if (pageSize < 4*1024)
72 pageSize = 4*1024;
73
74 //
75 // A large currentPageOffset indicates a new page needs to
76 // be obtained to allocate memory.
77 //
78 currentPageOffset = pageSize;
79
80 //
81 // Adjust alignment to be at least pointer aligned and
82 // power of 2.
83 //
84 size_t minAlign = sizeof(void*);
85 alignment &= ~(minAlign - 1);
86 if (alignment < minAlign)
87 alignment = minAlign;
88 size_t a = 1;
89 while (a < alignment)
90 a <<= 1;
91 alignment = a;
92 alignmentMask = a - 1;
93
94 //
95 // Align header skip
96 //
97 headerSkip = minAlign;
98 if (headerSkip < sizeof(tHeader)) {
99 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
100 }
101 }
102
~TPoolAllocator()103 TPoolAllocator::~TPoolAllocator()
104 {
105 while (inUseList) {
106 tHeader* next = inUseList->nextPage;
107 inUseList->~tHeader();
108 delete [] reinterpret_cast<char*>(inUseList);
109 inUseList = next;
110 }
111
112 // We should not check the guard blocks
113 // here, because we did it already when the block was
114 // placed into the free list.
115 //
116 while (freeList) {
117 tHeader* next = freeList->nextPage;
118 delete [] reinterpret_cast<char*>(freeList);
119 freeList = next;
120 }
121 }
122
123 // Support MSVC++ 6.0
124 const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
125 const unsigned char TAllocation::guardBlockEndVal = 0xfe;
126 const unsigned char TAllocation::userDataFill = 0xcd;
127
128 #ifdef GUARD_BLOCKS
129 const size_t TAllocation::guardBlockSize = 16;
130 #else
131 const size_t TAllocation::guardBlockSize = 0;
132 #endif
133
134 //
135 // Check a single guard block for damage
136 //
checkGuardBlock(unsigned char * blockMem,unsigned char val,const char * locText) const137 void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
138 {
139 #ifdef GUARD_BLOCKS
140 for (size_t x = 0; x < guardBlockSize; x++) {
141 if (blockMem[x] != val) {
142 char assertMsg[80];
143
144 // We don't print the assert message. It's here just to be helpful.
145 #if defined(_MSC_VER)
146 _snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %Iu byte allocation at 0x%p\n",
147 locText, size, data());
148 #else
149 snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n",
150 locText, size, data());
151 #endif
152 assert(0 && "PoolAlloc: Damage in guard block");
153 }
154 }
155 #endif
156 }
157
158
push()159 void TPoolAllocator::push()
160 {
161 tAllocState state = { currentPageOffset, inUseList };
162
163 stack.push_back(state);
164
165 //
166 // Indicate there is no current page to allocate from.
167 //
168 currentPageOffset = pageSize;
169 }
170
171 //
172 // Do a mass-deallocation of all the individual allocations
173 // that have occurred since the last push(), or since the
174 // last pop(), or since the object's creation.
175 //
176 // The deallocated pages are saved for future allocations.
177 //
pop()178 void TPoolAllocator::pop()
179 {
180 if (stack.size() < 1)
181 return;
182
183 tHeader* page = stack.back().page;
184 currentPageOffset = stack.back().offset;
185
186 while (inUseList != page) {
187 // invoke destructor to free allocation list
188 inUseList->~tHeader();
189
190 tHeader* nextInUse = inUseList->nextPage;
191 if (inUseList->pageCount > 1)
192 delete [] reinterpret_cast<char*>(inUseList);
193 else {
194 inUseList->nextPage = freeList;
195 freeList = inUseList;
196 }
197 inUseList = nextInUse;
198 }
199
200 stack.pop_back();
201 }
202
203 //
204 // Do a mass-deallocation of all the individual allocations
205 // that have occurred.
206 //
popAll()207 void TPoolAllocator::popAll()
208 {
209 while (stack.size() > 0)
210 pop();
211 }
212
allocate(size_t numBytes)213 void* TPoolAllocator::allocate(size_t numBytes)
214 {
215 //
216 // Just keep some interesting statistics.
217 //
218 ++numCalls;
219 totalBytes += numBytes;
220
221 // If we are using guard blocks, all allocations are bracketed by
222 // them: [guardblock][allocation][guardblock]. numBytes is how
223 // much memory the caller asked for. allocationSize is the total
224 // size including guard blocks. In release build,
225 // guardBlockSize=0 and this all gets optimized away.
226 size_t allocationSize = TAllocation::allocationSize(numBytes);
227 // Detect integer overflow.
228 if (allocationSize < numBytes)
229 return 0;
230
231 //
232 // Do the allocation, most likely case first, for efficiency.
233 // This step could be moved to be inline sometime.
234 //
235 if (allocationSize <= pageSize - currentPageOffset) {
236 //
237 // Safe to allocate from currentPageOffset.
238 //
239 unsigned char* memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
240 currentPageOffset += allocationSize;
241 currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
242
243 return initializeAllocation(inUseList, memory, numBytes);
244 }
245
246 if (allocationSize > pageSize - headerSkip) {
247 //
248 // Do a multi-page allocation. Don't mix these with the others.
249 // The OS is efficient and allocating and free-ing multiple pages.
250 //
251 size_t numBytesToAlloc = allocationSize + headerSkip;
252 // Detect integer overflow.
253 if (numBytesToAlloc < allocationSize)
254 return 0;
255
256 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
257 if (memory == 0)
258 return 0;
259
260 // Use placement-new to initialize header
261 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
262 inUseList = memory;
263
264 currentPageOffset = pageSize; // make next allocation come from a new page
265
266 // No guard blocks for multi-page allocations (yet)
267 return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
268 }
269
270 //
271 // Need a simple page to allocate from.
272 //
273 tHeader* memory;
274 if (freeList) {
275 memory = freeList;
276 freeList = freeList->nextPage;
277 } else {
278 memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
279 if (memory == 0)
280 return 0;
281 }
282
283 // Use placement-new to initialize header
284 new(memory) tHeader(inUseList, 1);
285 inUseList = memory;
286
287 unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
288 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
289
290 return initializeAllocation(inUseList, ret, numBytes);
291 }
292
293
294 //
295 // Check all allocations in a list for damage by calling check on each.
296 //
checkAllocList() const297 void TAllocation::checkAllocList() const
298 {
299 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
300 alloc->check();
301 }
302