knight/shared/KeLumpAllocator.cpp
author David Anderson <dvander@alliedmods.net>
Fri Aug 15 05:22:26 2008 +0000 (2008-08-15)
changeset 2226 d10b38524be3
child 2541 0352326a1f3f
permissions -rw-r--r--
Landed sourcepawn-1.2. The big changes:

1) JIT compilation/optimization now occurs per-function, and only when functions are first used. We're now officially a whole-method JIT rather than an AOT compiler (albiet, still a simple JIT). This has two implications: Functions are now much better abstracted internally, and loading a plugin is now much less expensive. If a function contains calls to other functions, THOSE functions are only compiled when they're invoked as well.

2) I've removed debug mode. We always show full backtraces now, as there was a very cheap way to implement this which really cleaned up everything. This is great for a number of reasons -- there's less code, the JIT is better designed, we don't need to relocate debug tables, and best of all we no longer have to tell users to enable debug mode at their own expense.
dvander@2226
     1
#include "KePlatform.h"
dvander@2226
     2
#include <KeLumpAllocator.h>
dvander@2226
     3
#include <KeVector.h>
dvander@2226
     4
dvander@2226
     5
using namespace Knight;
dvander@2226
     6
dvander@2226
     7
/**
dvander@2226
     8
 * :TODO: don't make this part of the page, because touching it means 
dvander@2226
     9
 * dirtying a page.  Instead, we should have a separate linked list.
dvander@2226
    10
 * Maybe that linked list itself could be marshalled from one page.
dvander@2226
    11
 */
dvander@2226
    12
struct KeLumpRegion
dvander@2226
    13
{
dvander@2226
    14
	char *base;
dvander@2226
    15
	char *cur;
dvander@2226
    16
	size_t size;
dvander@2226
    17
	size_t avail;
dvander@2226
    18
	KeLumpRegion *next;
dvander@2226
    19
	KeLumpRegion *prev;
dvander@2226
    20
};
dvander@2226
    21
dvander@2226
    22
class KeLumpAllocator
dvander@2226
    23
{
dvander@2226
    24
public:
dvander@2226
    25
	KeLumpAllocator() : m_pUsableRegions(NULL), m_pUnusableRegions(NULL)
dvander@2226
    26
	{
dvander@2226
    27
		m_DefLumpSize = 65536;
dvander@2226
    28
dvander@2226
    29
#if defined KE_PLATFORM_WINDOWS
dvander@2226
    30
		SYSTEM_INFO info;
dvander@2226
    31
dvander@2226
    32
		GetSystemInfo(&info);
dvander@2226
    33
		if (info.dwAllocationGranularity > m_DefLumpSize)
dvander@2226
    34
		{
dvander@2226
    35
			m_DefLumpSize = info.dwAllocationGranularity;
dvander@2226
    36
		}
dvander@2226
    37
#endif
dvander@2226
    38
	}
dvander@2226
    39
dvander@2226
    40
	~KeLumpAllocator()
dvander@2226
    41
	{
dvander@2226
    42
		FreeRegionChain(m_pUsableRegions);
dvander@2226
    43
		FreeRegionChain(m_pUnusableRegions);
dvander@2226
    44
	}
dvander@2226
    45
dvander@2226
    46
	void Reset()
dvander@2226
    47
	{
dvander@2226
    48
		KeLumpRegion *region;
dvander@2226
    49
dvander@2226
    50
		/* Find the tail of the usable regions. */
dvander@2226
    51
		region = m_pUsableRegions;
dvander@2226
    52
		while (region != NULL)
dvander@2226
    53
		{
dvander@2226
    54
			if (region->next == NULL)
dvander@2226
    55
			{
dvander@2226
    56
				break;
dvander@2226
    57
			}
dvander@2226
    58
		}
dvander@2226
    59
dvander@2226
    60
		/* Link the unusable chain into the usable chain. */
dvander@2226
    61
		if (region == NULL)
dvander@2226
    62
		{
dvander@2226
    63
			m_pUsableRegions = m_pUnusableRegions;
dvander@2226
    64
		}
dvander@2226
    65
		else
dvander@2226
    66
		{
dvander@2226
    67
			region->next = m_pUnusableRegions;
dvander@2226
    68
			m_pUnusableRegions->prev = region;
dvander@2226
    69
		}
dvander@2226
    70
		m_pUnusableRegions = NULL;
dvander@2226
    71
dvander@2226
    72
		region = m_pUsableRegions;
dvander@2226
    73
		while (region != NULL)
dvander@2226
    74
		{
dvander@2226
    75
			region->avail = region->size;
dvander@2226
    76
			region->cur = region->base;
dvander@2226
    77
			region = region->next;
dvander@2226
    78
		}
dvander@2226
    79
	}
dvander@2226
    80
dvander@2226
    81
	void FreeRegionChain(KeLumpRegion *region)
dvander@2226
    82
	{
dvander@2226
    83
		KeLumpRegion *next;
dvander@2226
    84
dvander@2226
    85
		while (region != NULL)
dvander@2226
    86
		{
dvander@2226
    87
			next = region->next;
dvander@2226
    88
			
dvander@2226
    89
#if defined KE_PLATFORM_WINDOWS
dvander@2226
    90
			VirtualFree(region, 0, MEM_RELEASE);
dvander@2226
    91
#endif
dvander@2226
    92
			region = next;
dvander@2226
    93
		}
dvander@2226
    94
	}
dvander@2226
    95
dvander@2226
    96
	void *Alloc(size_t size)
dvander@2226
    97
	{
dvander@2226
    98
		char *blob;
dvander@2226
    99
		KeLumpRegion *region;
dvander@2226
   100
dvander@2226
   101
		if (size % 8 != 0)
dvander@2226
   102
		{
dvander@2226
   103
			size += 8;
dvander@2226
   104
			size -= size % 8;
dvander@2226
   105
		}
dvander@2226
   106
dvander@2226
   107
		region = FindRegionForSize(size);
dvander@2226
   108
dvander@2226
   109
		blob = region->cur;
dvander@2226
   110
		region->avail -= size;
dvander@2226
   111
		region->cur += size;
dvander@2226
   112
dvander@2226
   113
		/** 
dvander@2226
   114
		 * Technically we could make one last small allocation, but 
dvander@2226
   115
		 * this edge case is not worth the extra work.
dvander@2226
   116
		 */
dvander@2226
   117
		if (region->avail < 8)
dvander@2226
   118
		{
dvander@2226
   119
			/* Unlink us from the usable list */
dvander@2226
   120
			if (region == m_pUsableRegions)
dvander@2226
   121
			{
dvander@2226
   122
				m_pUsableRegions = m_pUsableRegions->next;
dvander@2226
   123
				m_pUsableRegions->prev = NULL;
dvander@2226
   124
			}
dvander@2226
   125
			else
dvander@2226
   126
			{
dvander@2226
   127
				region->prev->next = region->next;
dvander@2226
   128
				if (region->next != NULL)
dvander@2226
   129
				{
dvander@2226
   130
					region->next->prev = region->prev;
dvander@2226
   131
				}
dvander@2226
   132
			}
dvander@2226
   133
dvander@2226
   134
			/* Link us into the unusable list */
dvander@2226
   135
			region->prev = NULL;
dvander@2226
   136
			region->next = m_pUnusableRegions;
dvander@2226
   137
dvander@2226
   138
			if (m_pUnusableRegions != NULL)
dvander@2226
   139
			{
dvander@2226
   140
				m_pUnusableRegions->prev = region;
dvander@2226
   141
			}
dvander@2226
   142
dvander@2226
   143
			m_pUnusableRegions = region;
dvander@2226
   144
		}
dvander@2226
   145
dvander@2226
   146
		return blob;
dvander@2226
   147
	}
dvander@2226
   148
dvander@2226
   149
private:
dvander@2226
   150
	KeLumpRegion *FindRegionForSize(size_t size)
dvander@2226
   151
	{
dvander@2226
   152
		char *base;
dvander@2226
   153
		KeLumpRegion *region;
dvander@2226
   154
		size_t size_of_region;
dvander@2226
   155
dvander@2226
   156
		/**
dvander@2226
   157
		 * :TODO: replace this with a priority queue or something 
dvander@2226
   158
		 * that's actually fast.  Even worse is we dirty pages by 
dvander@2226
   159
		 * doing this.  Ouch!
dvander@2226
   160
		 */
dvander@2226
   161
		region = m_pUsableRegions;
dvander@2226
   162
		while (region != NULL)
dvander@2226
   163
		{
dvander@2226
   164
			if (region->avail >= size)
dvander@2226
   165
			{
dvander@2226
   166
				return region;
dvander@2226
   167
			}
dvander@2226
   168
			region = region->next;
dvander@2226
   169
		}
dvander@2226
   170
dvander@2226
   171
		/* Make sure regions end at 8-byte alignment. */
dvander@2226
   172
		size_of_region = sizeof(KeLumpRegion);
dvander@2226
   173
		if (size_of_region % 8 != 0)
dvander@2226
   174
		{
dvander@2226
   175
			size_of_region += 8;
dvander@2226
   176
			size_of_region -= size_of_region % 8;
dvander@2226
   177
		}
dvander@2226
   178
dvander@2226
   179
		/* If the size is too big, fix that. */
dvander@2226
   180
		if (size > m_DefLumpSize - size_of_region)
dvander@2226
   181
		{
dvander@2226
   182
			size += m_DefLumpSize;
dvander@2226
   183
			size -= size % m_DefLumpSize;
dvander@2226
   184
		}
dvander@2226
   185
		else
dvander@2226
   186
		{
dvander@2226
   187
			size = m_DefLumpSize;
dvander@2226
   188
		}
dvander@2226
   189
dvander@2226
   190
#if defined KE_PLATFORM_WINDOWS
dvander@2226
   191
		base = (char *)VirtualAlloc(
dvander@2226
   192
			NULL,
dvander@2226
   193
			m_DefLumpSize,
dvander@2226
   194
			MEM_COMMIT|MEM_RESERVE,
dvander@2226
   195
			PAGE_READWRITE);
dvander@2226
   196
#endif
dvander@2226
   197
dvander@2226
   198
		/* Initialize the region */
dvander@2226
   199
		region = (KeLumpRegion *)base;
dvander@2226
   200
		region->base = &base[size_of_region];
dvander@2226
   201
		region->size = size - size_of_region;
dvander@2226
   202
		region->cur = region->base;
dvander@2226
   203
		region->avail = region->size;
dvander@2226
   204
		region->prev = NULL;
dvander@2226
   205
		region->next = m_pUsableRegions;
dvander@2226
   206
dvander@2226
   207
		if (m_pUsableRegions != NULL)
dvander@2226
   208
		{
dvander@2226
   209
			m_pUsableRegions->prev = region;
dvander@2226
   210
		}
dvander@2226
   211
dvander@2226
   212
		m_pUsableRegions = region;
dvander@2226
   213
dvander@2226
   214
		return region;
dvander@2226
   215
	}
dvander@2226
   216
dvander@2226
   217
private:
dvander@2226
   218
	KeLumpRegion *m_pUsableRegions;
dvander@2226
   219
	KeLumpRegion *m_pUnusableRegions;
dvander@2226
   220
	size_t m_DefLumpSize;
dvander@2226
   221
};
dvander@2226
   222
dvander@2226
   223
inline KeLumpAllocator *ke_LumpFromAllocator(ke_allocator_t *arena)
dvander@2226
   224
{
dvander@2226
   225
	return (KeLumpAllocator *)arena->user;
dvander@2226
   226
}
dvander@2226
   227
dvander@2226
   228
void *ke_LumpAlloc(ke_allocator_t *arena, size_t size)
dvander@2226
   229
{
dvander@2226
   230
	return ke_LumpFromAllocator(arena)->Alloc(size);
dvander@2226
   231
}
dvander@2226
   232
dvander@2226
   233
void ke_LumpFree(ke_allocator_t *arena, void *ptr)
dvander@2226
   234
{
dvander@2226
   235
}
dvander@2226
   236
dvander@2226
   237
ke_allocator_t * KE_LINK Knight::KE_CreateLumpAllocator()
dvander@2226
   238
{
dvander@2226
   239
	ke_allocator_t *alloc;
dvander@2226
   240
dvander@2226
   241
	alloc = new ke_allocator_t;
dvander@2226
   242
	alloc->alloc = ke_LumpAlloc;
dvander@2226
   243
	alloc->dealloc = ke_LumpFree;
dvander@2226
   244
	alloc->user = new KeLumpAllocator();
dvander@2226
   245
dvander@2226
   246
	return alloc;
dvander@2226
   247
}
dvander@2226
   248
dvander@2226
   249
void KE_LINK Knight::KE_DestroyLumpAllocator(ke_allocator_t *alloc)
dvander@2226
   250
{
dvander@2226
   251
	delete ke_LumpFromAllocator(alloc);
dvander@2226
   252
	delete alloc;
dvander@2226
   253
}
dvander@2226
   254
dvander@2226
   255
void KE_LINK Knight::KE_ResetLumpAllocator(ke_allocator_t *alloc)
dvander@2226
   256
{
dvander@2226
   257
	ke_LumpFromAllocator(alloc)->Reset();
dvander@2226
   258
}