1 #include <KePlatform.h>
5 #if defined KE_PLATFORM_WINDOWS
7 #elif defined KE_PLATFORM_POSIX
15 #include "KeCodeAllocator.h"
19 using namespace Knight;
24 * Defines a region of memory that is made of pages.
29 unsigned char *block_start;
30 unsigned char *block_pos;
31 KeFreedCode *free_list;
38 * Defines freed code. We keep the size here because
39 * when we touch the linked list we don't want to dirty pages.
44 unsigned char *block_start;
55 class Knight::KeCodeCache
59 * First region that is live for use.
61 KeCodeRegion *first_live;
64 * First region that is full but has free entries.
66 KeCodeRegion *first_partial;
69 * First region that is full.
71 KeCodeRegion *first_full;
74 * Page granularity and size.
76 unsigned int page_size;
77 unsigned int page_granularity;
80 * This isn't actually for code, this is the node cache.
82 KeCodeRegion *node_cache;
83 KeFreedCode *free_node_list;
86 KeCodeCache *Knight::KE_CreateCodeCache()
90 cache = new KeCodeCache;
92 memset(cache, 0, sizeof(KeCodeCache));
94 #if defined KE_PLATFORM_WINDOWS
98 cache->page_size = info.dwPageSize;
99 cache->page_granularity = info.dwAllocationGranularity;
101 cache->page_size = cache->page_granularity = sysconf(_SC_PAGESIZE);
107 inline size_t MinAllocSize()
111 size = sizeof(KeSecret);
113 size -= size % ALIGNMENT;
118 inline size_t ke_GetAllocSize(size_t size)
120 size += sizeof(KeSecret);
122 size -= size % ALIGNMENT;
127 void *ke_AllocInRegion(KeCodeCache *cache,
129 KeCodeRegion *region,
136 /* Squirrel some info in the alloc. */
137 secret = (KeSecret *)ptr;
138 secret->region = region;
139 secret->size = alloc_size;
140 ptr += sizeof(KeSecret);
142 region->total_free -= alloc_size;
144 /* Check if we can't use the fast-path anymore. */
145 if ((is_live && region->end_free < MinAllocSize())
146 || (!is_live && region->total_free < MinAllocSize()))
148 KeCodeRegion **start;
150 *prev = region->next;
152 /* Select the appropriate arena. */
155 if (region->total_free < MinAllocSize())
157 start = &cache->first_full;
161 start = &cache->first_partial;
166 start = &cache->first_full;
169 region->next = *start;
176 void *ke_AllocFromLive(KeCodeCache *cache, size_t size)
180 KeCodeRegion *region, **prev;
182 region = cache->first_live;
183 prev = &cache->first_live;
184 alloc_size = ke_GetAllocSize(size);
186 while (region != NULL)
188 if (region->end_free >= alloc_size)
190 /* Yay! We can do a simple alloc here. */
191 ptr = ke_AllocInRegion(cache, prev, region, region->block_pos, alloc_size, true);
193 /* Update our counters. */
194 region->block_pos += alloc_size;
195 region->end_free -= alloc_size;
199 prev = ®ion->next;
200 region = region->next;
206 void *ke_AllocFromPartial(KeCodeCache *cache, size_t size)
210 KeCodeRegion *region, **prev;
212 region = cache->first_partial;
213 prev = &cache->first_partial;
214 alloc_size = ke_GetAllocSize(size);
216 while (region != NULL)
218 if (region->total_free >= alloc_size)
220 KeFreedCode *node, **last;
222 assert(region->free_list != NULL);
224 last = ®ion->free_list;
225 node = region->free_list;
228 if (node->size >= alloc_size)
231 ptr = ke_AllocInRegion(cache, prev, region, node->block_start, alloc_size, false);
233 region->total_free -= node->size;
236 /* Make sure bookkeepping is correct. */
237 assert((region->free_list == NULL && region->total_free == 0)
238 || (region->free_list != NULL && region->total_free != 0));
240 /* Link us back into the free node list. */
241 node->next = cache->free_node_list;
242 cache->free_node_list = node->next;
250 prev = ®ion->next;
251 region = region->next;
257 KeCodeRegion *ke_AddRegionForSize(KeCodeCache *cache, size_t size)
259 KeCodeRegion *region;
261 region = new KeCodeRegion;
263 size = ke_GetAllocSize(size);
264 size += cache->page_granularity * 2;
265 size -= size % cache->page_granularity;
267 #if defined KE_PLATFORM_WINDOWS
268 region->block_start = (unsigned char *)VirtualAlloc(NULL, size, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
269 #elif defined KE_PLATFORM_POSIX
270 region->block_start = (unsigned char *)mmap(NULL, size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0);
271 region->block_start = (region->block_start == MAP_FAILED) ? NULL : region->block_start;
276 if (region->block_start == NULL)
282 region->block_pos = region->block_start;
283 region->end_free = region->total_free = region->total_size = size;
284 region->next = cache->first_live;
285 cache->first_live = region;
286 region->free_list = NULL;
291 void *Knight::KE_AllocCode(KeCodeCache *cache, size_t size)
295 /* Check live easy-adds */
296 if (cache->first_live != NULL)
298 if ((ptr = ke_AllocFromLive(cache, size)) != NULL)
304 /* Try looking in the free lists */
305 if (cache->first_partial != NULL)
307 if ((ptr = ke_AllocFromPartial(cache, size)) != NULL)
313 /* Create a new region */
314 if (ke_AddRegionForSize(cache, size) == NULL)
319 return ke_AllocFromLive(cache, size);
322 KeFreedCode *ke_GetFreeNode(KeCodeCache *cache)
326 if (cache->free_node_list != NULL)
328 ret = cache->free_node_list;
329 cache->free_node_list = ret->next;
334 /* See if the current free node region has space. */
335 if (cache->node_cache != NULL
336 && cache->node_cache->end_free >= sizeof(KeFreedCode))
338 ret = (KeFreedCode *)cache->node_cache->block_pos;
339 cache->node_cache->block_pos += sizeof(KeFreedCode);
340 cache->node_cache->total_free -= sizeof(KeFreedCode);
341 cache->node_cache->end_free -= sizeof(KeFreedCode);
346 /* Otherwise, we need to alloc a new region. */
347 KeCodeRegion *region = new KeCodeRegion;
349 region->block_start = new unsigned char[cache->page_size / sizeof(KeFreedCode)];
350 region->block_pos = region->block_start + sizeof(KeFreedCode);
351 region->total_size = cache->page_size / sizeof(KeFreedCode);
352 region->total_free = region->end_free = (region->total_size - sizeof(KeFreedCode));
353 region->free_list = NULL;
354 region->next = cache->node_cache;
355 cache->node_cache = region;
357 return (KeFreedCode *)region->block_start;
360 void Knight::KE_FreeCode(KeCodeCache *cache, void *code)
365 KeCodeRegion *region;
367 ptr = (unsigned char *)code;
368 secret = (KeSecret *)(ptr - sizeof(KeSecret));
369 region = secret->region;
370 node = ke_GetFreeNode(cache);
371 node->block_start = (unsigned char *)code;
372 node->next = region->free_list;
373 region->free_list = node;
374 node->region = region;
375 node->size = secret->size;
378 KeCodeRegion *ke_DestroyRegion(KeCodeRegion *region)
384 #if defined KE_PLATFORM_WINDOWS
385 VirtualFree(region->block_start, 0, MEM_RELEASE);
387 munmap(region->block_start, region->total_size);
395 void ke_DestroyRegionChain(KeCodeRegion *first)
397 while (first != NULL)
399 first = ke_DestroyRegion(first);
403 void Knight::KE_DestroyCodeCache(KeCodeCache *cache)
405 /* Destroy every region and call it a day. */
406 ke_DestroyRegionChain(cache->first_full);
407 ke_DestroyRegionChain(cache->first_live);
408 ke_DestroyRegionChain(cache->first_partial);
410 /* We use normal malloc for node cache regions */
411 KeCodeRegion *region, *next;
413 region = cache->node_cache;
414 while (region != NULL)
417 delete [] region->block_start;