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