blob: f3d19eb5d9e255e85125b4238d73d5491c0d3482 [file] [log] [blame]
Nicolas Capens0bac2852016-05-07 06:09:58 -04001// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
John Bauman66b8ab22014-05-06 15:57:45 -04002//
Nicolas Capens0bac2852016-05-07 06:09:58 -04003// 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
John Bauman66b8ab22014-05-06 15:57:45 -04006//
Nicolas Capens0bac2852016-05-07 06:09:58 -04007// 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.
John Bauman66b8ab22014-05-06 15:57:45 -040014
Nicolas Capenscc863da2015-01-21 15:50:55 -050015#include "PoolAlloc.h"
John Bauman66b8ab22014-05-06 15:57:45 -040016
17#ifndef _MSC_VER
18#include <stdint.h>
19#endif
20#include <stdio.h>
21
Nicolas Capenscc863da2015-01-21 15:50:55 -050022#include "InitializeGlobals.h"
23#include "osinclude.h"
John Bauman66b8ab22014-05-06 15:57:45 -040024
25OS_TLSIndex PoolIndex = OS_INVALID_TLS_INDEX;
26
John Bauman66b8ab22014-05-06 15:57:45 -040027bool InitializePoolIndex()
28{
Nicolas Capens0bac2852016-05-07 06:09:58 -040029 assert(PoolIndex == OS_INVALID_TLS_INDEX);
John Bauman66b8ab22014-05-06 15:57:45 -040030
Nicolas Capens0bac2852016-05-07 06:09:58 -040031 PoolIndex = OS_AllocTLSIndex();
32 return PoolIndex != OS_INVALID_TLS_INDEX;
John Bauman66b8ab22014-05-06 15:57:45 -040033}
34
35void FreePoolIndex()
36{
Nicolas Capens0bac2852016-05-07 06:09:58 -040037 assert(PoolIndex != OS_INVALID_TLS_INDEX);
Nicolas Capens978ddc52014-11-11 12:42:08 -050038
Nicolas Capens0bac2852016-05-07 06:09:58 -040039 OS_FreeTLSIndex(PoolIndex);
40 PoolIndex = OS_INVALID_TLS_INDEX;
John Bauman66b8ab22014-05-06 15:57:45 -040041}
42
Nicolas Capens978ddc52014-11-11 12:42:08 -050043TPoolAllocator* GetGlobalPoolAllocator()
John Bauman66b8ab22014-05-06 15:57:45 -040044{
Nicolas Capens0bac2852016-05-07 06:09:58 -040045 assert(PoolIndex != OS_INVALID_TLS_INDEX);
46 return static_cast<TPoolAllocator*>(OS_GetTLSValue(PoolIndex));
John Bauman66b8ab22014-05-06 15:57:45 -040047}
48
49void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator)
50{
Nicolas Capens0bac2852016-05-07 06:09:58 -040051 assert(PoolIndex != OS_INVALID_TLS_INDEX);
52 OS_SetTLSValue(PoolIndex, poolAllocator);
John Bauman66b8ab22014-05-06 15:57:45 -040053}
54
55//
56// Implement the functionality of the TPoolAllocator class, which
57// is documented in PoolAlloc.h.
58//
Nicolas Capens0bac2852016-05-07 06:09:58 -040059TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
60 pageSize(growthIncrement),
61 alignment(allocationAlignment),
62 freeList(0),
63 inUseList(0),
64 numCalls(0),
65 totalBytes(0)
John Bauman66b8ab22014-05-06 15:57:45 -040066{
Nicolas Capens0bac2852016-05-07 06:09:58 -040067 //
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;
John Bauman66b8ab22014-05-06 15:57:45 -040073
Nicolas Capens0bac2852016-05-07 06:09:58 -040074 //
75 // A large currentPageOffset indicates a new page needs to
76 // be obtained to allocate memory.
77 //
78 currentPageOffset = pageSize;
John Bauman66b8ab22014-05-06 15:57:45 -040079
Nicolas Capens0bac2852016-05-07 06:09:58 -040080 //
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;
John Bauman66b8ab22014-05-06 15:57:45 -040093
Nicolas Capens0bac2852016-05-07 06:09:58 -040094 //
95 // Align header skip
96 //
97 headerSkip = minAlign;
98 if (headerSkip < sizeof(tHeader)) {
99 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
100 }
John Bauman66b8ab22014-05-06 15:57:45 -0400101}
102
103TPoolAllocator::~TPoolAllocator()
104{
Nicolas Capens0bac2852016-05-07 06:09:58 -0400105 while (inUseList) {
106 tHeader* next = inUseList->nextPage;
107 inUseList->~tHeader();
108 delete [] reinterpret_cast<char*>(inUseList);
109 inUseList = next;
110 }
John Bauman66b8ab22014-05-06 15:57:45 -0400111
Nicolas Capens0bac2852016-05-07 06:09:58 -0400112 // 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 }
John Bauman66b8ab22014-05-06 15:57:45 -0400121}
122
123// Support MSVC++ 6.0
124const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
125const unsigned char TAllocation::guardBlockEndVal = 0xfe;
126const unsigned char TAllocation::userDataFill = 0xcd;
127
128#ifdef GUARD_BLOCKS
Nicolas Capens0bac2852016-05-07 06:09:58 -0400129 const size_t TAllocation::guardBlockSize = 16;
John Bauman66b8ab22014-05-06 15:57:45 -0400130#else
Nicolas Capens0bac2852016-05-07 06:09:58 -0400131 const size_t TAllocation::guardBlockSize = 0;
John Bauman66b8ab22014-05-06 15:57:45 -0400132#endif
133
134//
135// Check a single guard block for damage
136//
137void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
138{
139#ifdef GUARD_BLOCKS
Nicolas Capens0bac2852016-05-07 06:09:58 -0400140 for (size_t x = 0; x < guardBlockSize; x++) {
141 if (blockMem[x] != val) {
142 char assertMsg[80];
John Bauman66b8ab22014-05-06 15:57:45 -0400143
Nicolas Capens0bac2852016-05-07 06:09:58 -0400144 // 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 }
John Bauman66b8ab22014-05-06 15:57:45 -0400155#endif
156}
157
158
159void TPoolAllocator::push()
160{
Nicolas Capens0bac2852016-05-07 06:09:58 -0400161 tAllocState state = { currentPageOffset, inUseList };
John Bauman66b8ab22014-05-06 15:57:45 -0400162
Nicolas Capens0bac2852016-05-07 06:09:58 -0400163 stack.push_back(state);
164
165 //
166 // Indicate there is no current page to allocate from.
167 //
168 currentPageOffset = pageSize;
John Bauman66b8ab22014-05-06 15:57:45 -0400169}
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//
178void TPoolAllocator::pop()
179{
Nicolas Capens0bac2852016-05-07 06:09:58 -0400180 if (stack.size() < 1)
181 return;
John Bauman66b8ab22014-05-06 15:57:45 -0400182
Nicolas Capens0bac2852016-05-07 06:09:58 -0400183 tHeader* page = stack.back().page;
184 currentPageOffset = stack.back().offset;
John Bauman66b8ab22014-05-06 15:57:45 -0400185
Nicolas Capens0bac2852016-05-07 06:09:58 -0400186 while (inUseList != page) {
187 // invoke destructor to free allocation list
188 inUseList->~tHeader();
John Bauman66b8ab22014-05-06 15:57:45 -0400189
Nicolas Capens0bac2852016-05-07 06:09:58 -0400190 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();
John Bauman66b8ab22014-05-06 15:57:45 -0400201}
202
203//
204// Do a mass-deallocation of all the individual allocations
205// that have occurred.
206//
207void TPoolAllocator::popAll()
208{
Nicolas Capens0bac2852016-05-07 06:09:58 -0400209 while (stack.size() > 0)
210 pop();
John Bauman66b8ab22014-05-06 15:57:45 -0400211}
212
213void* TPoolAllocator::allocate(size_t numBytes)
214{
Nicolas Capens0bac2852016-05-07 06:09:58 -0400215 //
216 // Just keep some interesting statistics.
217 //
218 ++numCalls;
219 totalBytes += numBytes;
John Bauman66b8ab22014-05-06 15:57:45 -0400220
Nicolas Capens0bac2852016-05-07 06:09:58 -0400221 // 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;
Nicolas Capensff7f1002014-11-11 11:31:47 -0500230
Nicolas Capens0bac2852016-05-07 06:09:58 -0400231 //
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;
John Bauman66b8ab22014-05-06 15:57:45 -0400242
Nicolas Capens0bac2852016-05-07 06:09:58 -0400243 return initializeAllocation(inUseList, memory, numBytes);
244 }
John Bauman66b8ab22014-05-06 15:57:45 -0400245
Nicolas Capens0bac2852016-05-07 06:09:58 -0400246 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;
Nicolas Capensff7f1002014-11-11 11:31:47 -0500255
Nicolas Capens0bac2852016-05-07 06:09:58 -0400256 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
257 if (memory == 0)
258 return 0;
John Bauman66b8ab22014-05-06 15:57:45 -0400259
Nicolas Capens0bac2852016-05-07 06:09:58 -0400260 // Use placement-new to initialize header
261 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
262 inUseList = memory;
John Bauman66b8ab22014-05-06 15:57:45 -0400263
Nicolas Capens0bac2852016-05-07 06:09:58 -0400264 currentPageOffset = pageSize; // make next allocation come from a new page
John Bauman66b8ab22014-05-06 15:57:45 -0400265
Nicolas Capens0bac2852016-05-07 06:09:58 -0400266 // No guard blocks for multi-page allocations (yet)
267 return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
268 }
John Bauman66b8ab22014-05-06 15:57:45 -0400269
Nicolas Capens0bac2852016-05-07 06:09:58 -0400270 //
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 }
John Bauman66b8ab22014-05-06 15:57:45 -0400282
Nicolas Capens0bac2852016-05-07 06:09:58 -0400283 // Use placement-new to initialize header
284 new(memory) tHeader(inUseList, 1);
285 inUseList = memory;
John Bauman66b8ab22014-05-06 15:57:45 -0400286
Nicolas Capens0bac2852016-05-07 06:09:58 -0400287 unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
288 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
289
290 return initializeAllocation(inUseList, ret, numBytes);
John Bauman66b8ab22014-05-06 15:57:45 -0400291}
292
293
294//
295// Check all allocations in a list for damage by calling check on each.
296//
297void TAllocation::checkAllocList() const
298{
Nicolas Capens0bac2852016-05-07 06:09:58 -0400299 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
300 alloc->check();
John Bauman66b8ab22014-05-06 15:57:45 -0400301}