#include "jit-internal.h"
#include "jit-apply-func.h"
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef JIT_CACHE_PAGE_SIZE
#define JIT_CACHE_PAGE_SIZE (64 * 1024)
#endif
#ifndef JIT_CACHE_MAX_PAGE_FACTOR
#define JIT_CACHE_MAX_PAGE_FACTOR 1024
#endif
typedef struct jit_cache_node *jit_cache_node_t;
struct jit_cache_node
{
jit_cache_node_t left;
jit_cache_node_t right;
unsigned char *start;
unsigned char *end;
jit_function_t func;
};
struct jit_cache_page
{
void *page;
long factor;
};
typedef struct jit_cache *jit_cache_t;
struct jit_cache
{
struct jit_cache_page *pages;
unsigned long numPages;
unsigned long maxNumPages;
unsigned long pageSize;
unsigned int maxPageFactor;
long pagesLeft;
unsigned char *free_start;
unsigned char *free_end;
unsigned char *prev_start;
unsigned char *prev_end;
jit_cache_node_t node;
struct jit_cache_node head;
struct jit_cache_node nil;
};
#define GetLeft(node) \
((jit_cache_node_t)(((jit_nuint)(node)->left) & ~((jit_nuint)1)))
#define GetRight(node) \
((node)->right)
#define SetLeft(node,value) \
((node)->left = (jit_cache_node_t)(((jit_nuint)value) | \
(((jit_nuint)(node)->left) & ((jit_nuint)1))))
#define SetRight(node,value) \
((node)->right = (value))
#define GetRed(node) \
((((jit_nuint)((node)->left)) & ((jit_nuint)1)) != 0)
#define SetRed(node) \
((node)->left = (jit_cache_node_t)(((jit_nuint)(node)->left) | ((jit_nuint)1)))
#define SetBlack(node) \
((node)->left = (jit_cache_node_t)(((jit_nuint)(node)->left) & ~((jit_nuint)1)))
void _jit_cache_destroy(jit_cache_t cache);
void * _jit_cache_alloc_data(jit_cache_t cache, unsigned long size, unsigned long align);
static void
AllocCachePage(jit_cache_t cache, int factor)
{
long num;
unsigned char *ptr;
struct jit_cache_page *list;
if(factor <= 0)
{
factor = 1;
}
if(((unsigned int) factor) > cache->maxPageFactor)
{
goto failAlloc;
}
if(cache->pagesLeft >= 0 && cache->pagesLeft < factor)
{
goto failAlloc;
}
ptr = (unsigned char *) _jit_malloc_exec((unsigned int) cache->pageSize * factor);
if(!ptr)
{
goto failAlloc;
}
if(cache->numPages == cache->maxNumPages)
{
if(cache->numPages == 0)
{
num = 16;
}
else
{
num = cache->numPages * 2;
}
if(cache->pagesLeft > 0 && num > (cache->numPages + cache->pagesLeft - factor + 1))
{
num = cache->numPages + cache->pagesLeft - factor + 1;
}
list = (struct jit_cache_page *) jit_realloc(cache->pages,
sizeof(struct jit_cache_page) * num);
if(!list)
{
_jit_free_exec(ptr, cache->pageSize * factor);
failAlloc:
cache->free_start = 0;
cache->free_end = 0;
return;
}
cache->maxNumPages = num;
cache->pages = list;
}
cache->pages[cache->numPages].page = ptr;
cache->pages[cache->numPages].factor = factor;
++(cache->numPages);
if(cache->pagesLeft > 0)
{
cache->pagesLeft -= factor;
}
cache->free_start = ptr;
cache->free_end = ptr + (int) cache->pageSize * factor;
}
static int
CacheCompare(jit_cache_t cache, unsigned char *key, jit_cache_node_t node)
{
if(node == &cache->nil || node == &cache->head)
{
return 1;
}
else
{
if(key < node->start)
{
return -1;
}
else if(key > node->start)
{
return 1;
}
else
{
return 0;
}
}
}
static jit_cache_node_t
CacheRotate(jit_cache_t cache, unsigned char *key, jit_cache_node_t around)
{
jit_cache_node_t child, grandChild;
int setOnLeft;
if(CacheCompare(cache, key, around) < 0)
{
child = GetLeft(around);
setOnLeft = 1;
}
else
{
child = GetRight(around);
setOnLeft = 0;
}
if(CacheCompare(cache, key, child) < 0)
{
grandChild = GetLeft(child);
SetLeft(child, GetRight(grandChild));
SetRight(grandChild, child);
}
else
{
grandChild = GetRight(child);
SetRight(child, GetLeft(grandChild));
SetLeft(grandChild, child);
}
if(setOnLeft)
{
SetLeft(around, grandChild);
}
else
{
SetRight(around, grandChild);
}
return grandChild;
}
#define Split() \
do { \
SetRed(temp); \
SetBlack(GetLeft(temp)); \
SetBlack(GetRight(temp)); \
if(GetRed(parent)) \
{ \
SetRed(grandParent); \
if((CacheCompare(cache, key, grandParent) < 0) != \
(CacheCompare(cache, key, parent) < 0)) \
{ \
parent = CacheRotate(cache, key, grandParent); \
} \
temp = CacheRotate(cache, key, greatGrandParent); \
SetBlack(temp); \
} \
} while (0)
static void
AddToLookupTree(jit_cache_t cache, jit_cache_node_t method)
{
unsigned char *key = method->start;
jit_cache_node_t temp;
jit_cache_node_t greatGrandParent;
jit_cache_node_t grandParent;
jit_cache_node_t parent;
jit_cache_node_t nil = &(cache->nil);
int cmp;
temp = &(cache->head);
greatGrandParent = temp;
grandParent = temp;
parent = temp;
while(temp != nil)
{
greatGrandParent = grandParent;
grandParent = parent;
parent = temp;
cmp = CacheCompare(cache, key, temp);
if(cmp == 0)
{
return;
}
else if(cmp < 0)
{
temp = GetLeft(temp);
}
else
{
temp = GetRight(temp);
}
if(GetRed(GetLeft(temp)) && GetRed(GetRight(temp)))
{
Split();
}
}
method->left = (jit_cache_node_t)(((jit_nuint)nil) | ((jit_nuint)1));
method->right = nil;
if(CacheCompare(cache, key, parent) < 0)
{
SetLeft(parent, method);
}
else
{
SetRight(parent, method);
}
Split();
SetBlack(cache->head.right);
}
jit_cache_t
_jit_cache_create(jit_context_t context)
{
jit_cache_t cache;
long limit, cache_page_size;
int max_page_factor;
unsigned long exec_page_size;
limit = (long)
jit_context_get_meta_numeric(context, JIT_OPTION_CACHE_LIMIT);
cache_page_size = (long)
jit_context_get_meta_numeric(context, JIT_OPTION_CACHE_PAGE_SIZE);
max_page_factor = (int)
jit_context_get_meta_numeric(context, JIT_OPTION_CACHE_MAX_PAGE_FACTOR);
if((cache = (jit_cache_t )jit_malloc(sizeof(struct jit_cache))) == 0)
{
return 0;
}
exec_page_size = jit_vmem_page_size();
if(cache_page_size <= 0)
{
cache_page_size = JIT_CACHE_PAGE_SIZE;
}
if(cache_page_size < exec_page_size)
{
cache_page_size = exec_page_size;
}
else
{
cache_page_size = (cache_page_size / exec_page_size) * exec_page_size;
}
if(max_page_factor <= 0)
{
max_page_factor = JIT_CACHE_MAX_PAGE_FACTOR;
}
cache->pages = 0;
cache->numPages = 0;
cache->maxNumPages = 0;
cache->pageSize = cache_page_size;
cache->maxPageFactor = max_page_factor;
cache->free_start = 0;
cache->free_end = 0;
if(limit > 0)
{
cache->pagesLeft = limit / cache_page_size;
if(cache->pagesLeft < 1)
{
cache->pagesLeft = 1;
}
}
else
{
cache->pagesLeft = -1;
}
cache->node = 0;
cache->nil.left = &(cache->nil);
cache->nil.right = &(cache->nil);
cache->nil.func = 0;
cache->head.left = 0;
cache->head.right = &(cache->nil);
cache->head.func = 0;
AllocCachePage(cache, 0);
if(!cache->free_start)
{
_jit_cache_destroy(cache);
return 0;
}
return cache;
}
void
_jit_cache_destroy(jit_cache_t cache)
{
unsigned long page;
for(page = 0; page < cache->numPages; ++page)
{
_jit_free_exec(cache->pages[page].page,
cache->pageSize * cache->pages[page].factor);
}
if(cache->pages)
{
jit_free(cache->pages);
}
jit_free(cache);
}
int
_jit_cache_extend(jit_cache_t cache, int count)
{
int factor = 1 << count;
if(cache->node)
{
return JIT_MEMORY_ERROR;
}
struct jit_cache_page *p = &cache->pages[cache->numPages - 1];
if((cache->free_start == ((unsigned char *)p->page))
&& (cache->free_end == (cache->free_start + cache->pageSize * p->factor)))
{
_jit_free_exec(p->page, cache->pageSize * p->factor);
--(cache->numPages);
if(cache->pagesLeft >= 0)
{
cache->pagesLeft += p->factor;
}
cache->free_start = 0;
cache->free_end = 0;
if(factor <= p->factor)
{
factor = p->factor << 1;
}
}
AllocCachePage(cache, factor);
if(!cache->free_start)
{
return JIT_MEMORY_TOO_BIG;
}
return JIT_MEMORY_OK;
}
jit_function_t
_jit_cache_alloc_function(jit_cache_t cache)
{
return jit_cnew(struct _jit_function);
}
void
_jit_cache_free_function(jit_cache_t cache, jit_function_t func)
{
jit_free(func);
}
int
_jit_cache_start_function(jit_cache_t cache, jit_function_t func)
{
if(cache->node)
{
return JIT_MEMORY_ERROR;
}
if(!cache->free_start)
{
return JIT_MEMORY_TOO_BIG;
}
cache->prev_start = cache->free_start;
cache->prev_end = cache->free_end;
cache->node = _jit_cache_alloc_data(
cache, sizeof(struct jit_cache_node), sizeof(void *));
if(!cache->node)
{
return JIT_MEMORY_RESTART;
}
cache->node->func = func;
cache->node->start = cache->free_start;
cache->node->end = 0;
cache->node->left = 0;
cache->node->right = 0;
return JIT_MEMORY_OK;
}
int
_jit_cache_end_function(jit_cache_t cache, int result)
{
if(!cache->node)
{
return JIT_MEMORY_ERROR;
}
if(result != JIT_MEMORY_OK)
{
cache->free_start = cache->prev_start;
cache->free_end = cache->prev_end;
cache->node = 0;
return JIT_MEMORY_RESTART;
}
cache->node->end = cache->free_start;
AddToLookupTree(cache, cache->node);
cache->node = 0;
return JIT_MEMORY_OK;
}
void *
_jit_cache_get_code_break(jit_cache_t cache)
{
if(!cache->node)
{
return 0;
}
return cache->free_start;
}
void
_jit_cache_set_code_break(jit_cache_t cache, void *ptr)
{
if(!cache->node)
{
return;
}
if((unsigned char *) ptr < cache->free_start)
{
return;
}
if((unsigned char *) ptr > cache->free_end)
{
return;
}
cache->free_start = ptr;
}
void *
_jit_cache_get_code_limit(jit_cache_t cache)
{
if(!cache->node)
{
return 0;
}
return cache->free_end;
}
void *
_jit_cache_alloc_data(jit_cache_t cache, unsigned long size, unsigned long align)
{
unsigned char *ptr;
ptr = cache->free_end - size;
ptr = (unsigned char *) (((jit_nuint) ptr) & ~(align - 1));
if(ptr < cache->free_start)
{
return 0;
}
cache->free_end = ptr;
return ptr;
}
static void *
alloc_code(jit_cache_t cache, unsigned int size, unsigned int align)
{
unsigned char *ptr;
if(cache->node)
{
return 0;
}
if(!cache->free_start)
{
return 0;
}
ptr = cache->free_start;
if(align > 1)
{
jit_nuint p = ((jit_nuint) ptr + align - 1) & ~(align - 1);
ptr = (unsigned char *) p;
}
if((ptr + size) > cache->free_end)
{
AllocCachePage(cache, 0);
if(!cache->free_start)
{
return 0;
}
ptr = cache->free_start;
if(align > 1)
{
jit_nuint p = ((jit_nuint) ptr + align - 1) & ~(align - 1);
ptr = (unsigned char *) p;
}
}
cache->free_start = ptr + size;
return (void *) ptr;
}
void *
_jit_cache_alloc_trampoline(jit_cache_t cache)
{
return alloc_code(cache,
jit_get_trampoline_size(),
jit_get_trampoline_alignment());
}
void
_jit_cache_free_trampoline(jit_cache_t cache, void *trampoline)
{
}
void *
_jit_cache_alloc_closure(jit_cache_t cache)
{
return alloc_code(cache,
jit_get_closure_size(),
jit_get_closure_alignment());
}
void
_jit_cache_free_closure(jit_cache_t cache, void *closure)
{
}
#if 0#endif
void *
_jit_cache_find_function_info(jit_cache_t cache, void *pc)
{
jit_cache_node_t node = cache->head.right;
while(node != &(cache->nil))
{
if(((unsigned char *)pc) < node->start)
{
node = GetLeft(node);
}
else if(((unsigned char *)pc) >= node->end)
{
node = GetRight(node);
}
else
{
return node;
}
}
return 0;
}
jit_function_t
_jit_cache_get_function(jit_cache_t cache, void *func_info)
{
if (func_info)
{
jit_cache_node_t node = (jit_cache_node_t) func_info;
return node->func;
}
return 0;
}
void *
_jit_cache_get_function_start(jit_memory_context_t memctx, void *func_info)
{
if (func_info)
{
jit_cache_node_t node = (jit_cache_node_t) func_info;
return node->start;
}
return 0;
}
void *
_jit_cache_get_function_end(jit_memory_context_t memctx, void *func_info)
{
if (func_info)
{
jit_cache_node_t node = (jit_cache_node_t) func_info;
return node->end;
}
return 0;
}
jit_memory_manager_t
jit_default_memory_manager(void)
{
static const struct jit_memory_manager mm = {
(jit_memory_context_t (*)(jit_context_t))
&_jit_cache_create,
(void (*)(jit_memory_context_t))
&_jit_cache_destroy,
(jit_function_info_t (*)(jit_memory_context_t, void *))
&_jit_cache_find_function_info,
(jit_function_t (*)(jit_memory_context_t, jit_function_info_t))
&_jit_cache_get_function,
(void * (*)(jit_memory_context_t, jit_function_info_t))
&_jit_cache_get_function_start,
(void * (*)(jit_memory_context_t, jit_function_info_t))
&_jit_cache_get_function_end,
(jit_function_t (*)(jit_memory_context_t))
&_jit_cache_alloc_function,
(void (*)(jit_memory_context_t, jit_function_t))
&_jit_cache_free_function,
(int (*)(jit_memory_context_t, jit_function_t))
&_jit_cache_start_function,
(int (*)(jit_memory_context_t, int))
&_jit_cache_end_function,
(int (*)(jit_memory_context_t, int))
&_jit_cache_extend,
(void * (*)(jit_memory_context_t))
&_jit_cache_get_code_limit,
(void * (*)(jit_memory_context_t))
&_jit_cache_get_code_break,
(void (*)(jit_memory_context_t, void *))
&_jit_cache_set_code_break,
(void * (*)(jit_memory_context_t))
&_jit_cache_alloc_trampoline,
(void (*)(jit_memory_context_t, void *))
&_jit_cache_free_trampoline,
(void * (*)(jit_memory_context_t))
&_jit_cache_alloc_closure,
(void (*)(jit_memory_context_t, void *))
&_jit_cache_free_closure,
(void * (*)(jit_memory_context_t, jit_size_t, jit_size_t))
&_jit_cache_alloc_data
};
return &mm;
}
#ifdef __cplusplus
};
#endif