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