Forcing build to see if upload is fixed.
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 *)valloc(size);
271 if (mprotect(region->block_start, size, PROT_READ|PROT_WRITE|PROT_EXEC) == -1)
273 free(region->block_start);
281 if (region->block_start == NULL)
287 region->block_pos = region->block_start;
288 region->end_free = region->total_free = region->total_size = size;
289 region->next = cache->first_live;
290 cache->first_live = region;
291 region->free_list = NULL;
296 void *Knight::KE_AllocCode(KeCodeCache *cache, size_t size)
300 /* Check live easy-adds */
301 if (cache->first_live != NULL)
303 if ((ptr = ke_AllocFromLive(cache, size)) != NULL)
309 /* Try looking in the free lists */
310 if (cache->first_partial != NULL)
312 if ((ptr = ke_AllocFromPartial(cache, size)) != NULL)
318 /* Create a new region */
319 if (ke_AddRegionForSize(cache, size) == NULL)
324 return ke_AllocFromLive(cache, size);
327 KeFreedCode *ke_GetFreeNode(KeCodeCache *cache)
331 if (cache->free_node_list != NULL)
333 ret = cache->free_node_list;
334 cache->free_node_list = ret->next;
339 /* See if the current free node region has space. */
340 if (cache->node_cache != NULL
341 && cache->node_cache->end_free >= sizeof(KeFreedCode))
343 ret = (KeFreedCode *)cache->node_cache->block_pos;
344 cache->node_cache->block_pos += sizeof(KeFreedCode);
345 cache->node_cache->total_free -= sizeof(KeFreedCode);
346 cache->node_cache->end_free -= sizeof(KeFreedCode);
351 /* Otherwise, we need to alloc a new region. */
352 KeCodeRegion *region = new KeCodeRegion;
354 region->block_start = new unsigned char[cache->page_size / sizeof(KeFreedCode)];
355 region->block_pos = region->block_start + sizeof(KeFreedCode);
356 region->total_size = cache->page_size / sizeof(KeFreedCode);
357 region->total_free = region->end_free = (region->total_size - sizeof(KeFreedCode));
358 region->free_list = NULL;
359 region->next = cache->node_cache;
360 cache->node_cache = region;
362 return (KeFreedCode *)region->block_start;
365 void Knight::KE_FreeCode(KeCodeCache *cache, void *code)
370 KeCodeRegion *region;
372 ptr = (unsigned char *)code;
373 secret = (KeSecret *)(ptr - sizeof(KeSecret));
374 region = secret->region;
375 node = ke_GetFreeNode(cache);
376 node->block_start = (unsigned char *)code;
377 node->next = region->free_list;
378 region->free_list = node;
379 node->region = region;
380 node->size = secret->size;
383 KeCodeRegion *ke_DestroyRegion(KeCodeRegion *region)
389 #if defined KE_PLATFORM_WINDOWS
390 VirtualFree(region->block_start, 0, MEM_RELEASE);
392 free(region->block_start);
400 void ke_DestroyRegionChain(KeCodeRegion *first)
402 while (first != NULL)
404 first = ke_DestroyRegion(first);
408 void Knight::KE_DestroyCodeCache(KeCodeCache *cache)
410 /* Destroy every region and call it a day. */
411 ke_DestroyRegionChain(cache->first_full);
412 ke_DestroyRegionChain(cache->first_live);
413 ke_DestroyRegionChain(cache->first_partial);
415 /* We use normal malloc for node cache regions */
416 KeCodeRegion *region, *next;
418 region = cache->node_cache;
419 while (region != NULL)
422 delete [] region->block_start;