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