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