#define JIT_CACHE_MAX_PAGE_FACTOR 1024
#endif
-/*
- * Structure of a debug information header for a method.
- * This header is followed by the debug data, which is
- * stored as compressed metadata integers.
- */
-typedef struct jit_cache_debug *jit_cache_debug_t;
-struct jit_cache_debug
-{
- jit_cache_debug_t next; /* Next block for the method */
-
-};
-
/*
* Method information block, organised as a red-black tree node.
* There may be more than one such block associated with a method
typedef struct jit_cache_method *jit_cache_method_t;
struct jit_cache_method
{
- void *method; /* Method containing the region */
- void *cookie; /* Cookie value for the region */
- unsigned char *start; /* Start of the region */
- unsigned char *end; /* End of the region */
- jit_cache_debug_t debug; /* Debug information for method */
+ jit_function_t func; /* Function */
jit_cache_method_t left; /* Left sub-tree and red/black bit */
jit_cache_method_t right; /* Right sub-tree */
-
};
/*
struct jit_cache_method head; /* Head of the lookup tree */
struct jit_cache_method nil; /* Nil pointer for the lookup tree */
unsigned char *start; /* Start of the current method */
- unsigned char debugData[JIT_CACHE_DEBUG_SIZE];
- int debugLen; /* Length of temporary debug data */
- jit_cache_debug_t firstDebug; /* First debug block for method */
- jit_cache_debug_t lastDebug; /* Last debug block for method */
-
};
-/*
- * Compress a "long" value so that it takes up less bytes.
- * This is used to store offsets within functions and
- * debug line numbers, which are usually small integers.
- */
-static int CompressInt(unsigned char *buf, long data)
-{
- if(data >= 0)
- {
- if(data < (long)0x40)
- {
- buf[0] = (unsigned char)(data << 1);
- return 1;
- }
- else if(data < (long)(1 << 13))
- {
- buf[0] = (unsigned char)(((data >> 7) & 0x3F) | 0x80);
- buf[1] = (unsigned char)(data << 1);
- return 2;
- }
- else if(data < (long)(1L << 28))
- {
- buf[0] = (unsigned char)((data >> 23) | 0xC0);
- buf[1] = (unsigned char)(data >> 15);
- buf[2] = (unsigned char)(data >> 7);
- buf[3] = (unsigned char)(data << 1);
- return 4;
- }
- else
- {
- buf[0] = (unsigned char)0xE0;
- buf[1] = (unsigned char)(data >> 23);
- buf[2] = (unsigned char)(data >> 15);
- buf[3] = (unsigned char)(data >> 7);
- buf[4] = (unsigned char)(data << 1);
- return 5;
- }
- }
- else
- {
- if(data >= ((long)-0x40))
- {
- buf[0] = ((((unsigned char)(data << 1)) & 0x7E) | 0x01);
- return 1;
- }
- else if(data >= ((long)-(1 << 13)))
- {
- buf[0] = (unsigned char)(((data >> 7) & 0x3F) | 0x80);
- buf[1] = (unsigned char)((data << 1) | 0x01);
- return 2;
- }
- else if(data >= ((long)-(1L << 29)))
- {
- buf[0] = (unsigned char)(((data >> 23) & 0x1F) | 0xC0);
- buf[1] = (unsigned char)(data >> 15);
- buf[2] = (unsigned char)(data >> 7);
- buf[3] = (unsigned char)((data << 1) | 0x01);
- return 4;
- }
- else
- {
- buf[0] = (unsigned char)0xE1;
- buf[1] = (unsigned char)(data >> 23);
- buf[2] = (unsigned char)(data >> 15);
- buf[3] = (unsigned char)(data >> 7);
- buf[4] = (unsigned char)((data << 1) | 0x01);
- return 5;
- }
- }
-}
-
-/*
- * Control data structure that is used by "UncompressInt".
- */
-typedef struct
-{
- const unsigned char *data; /* Current data position */
- unsigned long len; /* Length remaining to read */
- int error; /* Set to non-zero if error encountered */
-
-} UncompressReader;
-
-/*
- * Uncompress a value that was compressed by "CompressInt".
- */
-static long UncompressInt(UncompressReader *meta)
-{
- unsigned char ch;
- unsigned char ch2;
- unsigned char ch3;
- unsigned char ch4;
- unsigned long value;
-
- if(meta->len > 0)
- {
- ch = *((meta->data)++);
- --(meta->len);
- if((ch & 0x80) == 0x00)
- {
- /* One-byte form of the item */
- if((ch & 0x01) == 0x00)
- return (long)(ch >> 1);
- else
- return (long)(signed char)((ch >> 1) | 0xC0);
- }
- else if((ch & 0xC0) == 0x80)
- {
- /* Two-byte form of the item */
- if(meta->len > 0)
- {
- --(meta->len);
- value = (((unsigned long)(ch & 0x3F)) << 8) |
- ((unsigned long)(*((meta->data)++)));
- if((value & 0x01) == 0x00)
- return (long)(value >> 1);
- else
- return (long)(jit_int)((value >> 1) | 0xFFFFE000);
- }
- else
- {
- meta->error = 1;
- return 0;
- }
- }
- else if((ch & 0xE0) == 0xC0)
- {
- /* Four-byte form of the item */
- if(meta->len >= 3)
- {
- ch2 = meta->data[0];
- ch3 = meta->data[1];
- ch4 = meta->data[2];
- meta->len -= 3;
- meta->data += 3;
- value = (((unsigned long)(ch & 0x1F)) << 24) |
- (((unsigned long)ch2) << 16) |
- (((unsigned long)ch3) << 8) |
- ((unsigned long)ch4);
- if((value & 0x01) == 0x00)
- return (long)(value >> 1);
- else
- return (long)(jit_int)((value >> 1) | 0xF0000000);
- }
- else
- {
- meta->len = 0;
- meta->error = 1;
- return 0;
- }
- }
- else
- {
- /* Five-byte form of the item */
- if(meta->len >= 4)
- {
- ch = meta->data[0];
- ch2 = meta->data[1];
- ch3 = meta->data[2];
- ch4 = meta->data[3];
- meta->len -= 4;
- meta->data += 4;
- value = (((unsigned long)ch) << 24) |
- (((unsigned long)ch2) << 16) |
- (((unsigned long)ch3) << 8) |
- ((unsigned long)ch4);
- return (long)(jit_int)value;
- }
- else
- {
- meta->len = 0;
- meta->error = 1;
- return 0;
- }
- }
- }
- else
- {
- meta->error = 1;
- return 0;
- }
-}
-
/*
* Allocate a cache page and add it to the cache.
*/
else
{
/* Compare a regular node */
- if(key < node->start)
+ if(key < node->func->start)
{
return -1;
}
- else if(key > node->start)
+ else if(key > node->func->start)
{
return 1;
}
*/
static void AddToLookupTree(jit_cache_t cache, jit_cache_method_t method)
{
- unsigned char *key = method->start;
+ unsigned char *key = method->func->start;
jit_cache_method_t temp;
jit_cache_method_t greatGrandParent;
jit_cache_method_t grandParent;
SetBlack(cache->head.right);
}
-/*
- * Flush the current debug buffer.
- */
-static void FlushCacheDebug(jit_cache_posn *posn)
-{
- jit_cache_t cache = posn->cache;
- jit_cache_debug_t debug;
-
- /* Allocate a new jit_cache_debug structure to hold the data */
- debug = _jit_cache_alloc(posn,
- (unsigned long)(sizeof(struct jit_cache_debug) + cache->debugLen));
- if(!debug)
- {
- cache->debugLen = 0;
- return;
- }
-
- /* Copy the temporary debug data into the new structure */
- jit_memcpy(debug + 1, cache->debugData, cache->debugLen);
-
- /* Link the structure into the debug list */
- debug->next = 0;
- if(cache->lastDebug)
- {
- cache->lastDebug->next = debug;
- }
- else
- {
- cache->firstDebug = debug;
- }
- cache->lastDebug = debug;
-
- /* Reset the temporary debug buffer */
- cache->debugLen = 0;
-}
-
-/*
- * Write a debug pair to the cache. The pair (-1, -1)
- * terminates the debug information for a method.
- */
-static void WriteCacheDebug(jit_cache_posn *posn, long offset, long nativeOffset)
-{
- jit_cache_t cache = posn->cache;
-
- /* Write the two values to the temporary debug buffer */
- cache->debugLen += CompressInt
- (cache->debugData + cache->debugLen, offset);
- cache->debugLen += CompressInt
- (cache->debugData + cache->debugLen, nativeOffset);
- if((cache->debugLen + 5 * 2 + 1) > (int)(sizeof(cache->debugData)))
- {
- /* Overflow occurred: write -2 to mark the end of this buffer */
- cache->debugLen += CompressInt
- (cache->debugData + cache->debugLen, -2);
-
- /* Flush the debug data that we have collected so far */
- FlushCacheDebug(posn);
- }
-}
-
jit_cache_t _jit_cache_create(long limit, long cache_page_size, int max_page_factor)
{
jit_cache_t cache;
cache->pagesLeft = -1;
}
cache->method = 0;
- cache->nil.method = 0;
- cache->nil.cookie = 0;
- cache->nil.start = 0;
- cache->nil.end = 0;
- cache->nil.debug = 0;
+ cache->nil.func = 0;
cache->nil.left = &(cache->nil);
cache->nil.right = &(cache->nil);
- cache->head.method = 0;
- cache->head.cookie = 0;
- cache->head.start = 0;
- cache->head.end = 0;
- cache->head.debug = 0;
+ cache->head.func = 0;
cache->head.left = 0;
cache->head.right = &(cache->nil);
cache->start = 0;
- cache->debugLen = 0;
- cache->firstDebug = 0;
- cache->lastDebug = 0;
/* Allocate the initial cache page */
AllocCachePage(cache, 0);
jit_cache_posn *posn,
int page_factor,
int align,
- void *method)
+ jit_function_t func)
{
unsigned char *ptr;
/* There is insufficient space in this page */
return JIT_CACHE_RESTART;
}
- cache->method->method = method;
- cache->method->cookie = 0;
- cache->method->start = posn->ptr;
- cache->method->end = posn->ptr;
- cache->method->debug = 0;
+ cache->method->func = func;
+ cache->method->func->start = posn->ptr;
+ cache->method->func->end = posn->ptr;
cache->method->left = 0;
cache->method->right = 0;
/* Store the method start address */
cache->start = posn->ptr;
- /* Clear the debug data */
- cache->debugLen = 0;
- cache->firstDebug = 0;
- cache->lastDebug = 0;
-
return JIT_CACHE_OK;
}
return JIT_CACHE_RESTART;
}
- /* Terminate the debug information and flush it */
- if(cache->firstDebug || cache->debugLen)
- {
- WriteCacheDebug(posn, -1, -1);
- if(cache->debugLen)
- {
- FlushCacheDebug(posn);
- }
- }
-
/* Flush the position information back to the cache */
cache->freeStart = posn->ptr;
cache->freeEnd = posn->limit;
method = cache->method;
if(method)
{
- method->end = posn->ptr;
+ method->func->end = posn->ptr;
do
{
- method->debug = cache->firstDebug;
next = method->right;
AddToLookupTree(cache, method);
method = next;
#endif
}
-void _jit_cache_mark_bytecode(jit_cache_posn *posn, unsigned long offset)
-{
- WriteCacheDebug(posn, (long)offset,
- (long)(posn->ptr - posn->cache->start));
-}
-
-void _jit_cache_set_cookie(jit_cache_posn *posn, void *cookie)
-{
- if(posn->cache->method)
- {
- posn->cache->method->cookie = cookie;
- }
-}
-
-void *_jit_cache_get_method(jit_cache_t cache, void *pc, void **cookie)
+jit_function_t
+_jit_cache_get_method(jit_cache_t cache, void *pc)
{
jit_cache_method_t node = cache->head.right;
while(node != &(cache->nil))
{
- if(((unsigned char *)pc) < node->start)
+ if(((unsigned char *)pc) < node->func->start)
{
node = GetLeft(node);
}
- else if(((unsigned char *)pc) >= node->end)
+ else if(((unsigned char *)pc) >= node->func->end)
{
node = GetRight(node);
}
else
{
- if(cookie)
- {
- *cookie = node->cookie;
- }
- return node->method;
+ return node->func;
}
}
return 0;
}
-/*
- * Add a parent pointer to a list. Returns null if out of memory.
- */
-static int add_parent(jit_cache_method_t *parent_buf,
- jit_cache_method_t **parents,
- int *num_parents, int *max_parents,
- jit_cache_method_t node)
-{
- jit_cache_method_t *new_list;
- if(*num_parents >= *max_parents)
- {
- if(*parents == parent_buf)
- {
- new_list = (jit_cache_method_t *)jit_malloc
- ((*max_parents * 2) * sizeof(jit_cache_method_t));
- if(new_list)
- {
- jit_memcpy(new_list, *parents,
- (*num_parents) * sizeof(jit_cache_method_t));
- }
- }
- else
- {
- new_list = (jit_cache_method_t *)jit_realloc
- (*parents, (*max_parents * 2) * sizeof(jit_cache_method_t));
- }
- if(!new_list)
- {
- return 0;
- }
- *parents = new_list;
- *max_parents *= 2;
- }
- (*parents)[*num_parents] = node;
- ++(*num_parents);
- return 1;
-}
-
-void *_jit_cache_get_start_method(jit_cache_t cache, void *pc)
-{
- /* TODO: This function is not currently aware of multiple regions. */
- jit_cache_method_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->start;
- }
- }
- return 0;
-}
-
-void *_jit_cache_get_end_method(jit_cache_t cache, void *pc)
-{
- jit_cache_method_t parent_buf[16];
- jit_cache_method_t *parents = parent_buf;
- int num_parents = 0;
- int max_parents = 16;
- jit_cache_method_t node = cache->head.right;
- jit_cache_method_t last;
- jit_cache_method_t parent;
- void *method;
- while(node != &(cache->nil))
- {
- if(((unsigned char *)pc) < node->start)
- {
- if(!add_parent(parent_buf, &parents, &num_parents,
- &max_parents, node))
- {
- break;
- }
- node = GetLeft(node);
- }
- else if(((unsigned char *)pc) >= node->end)
- {
- if(!add_parent(parent_buf, &parents, &num_parents,
- &max_parents, node))
- {
- break;
- }
- node = GetRight(node);
- }
- else
- {
- /* This is the node that contains the starting position.
- We now need to do an inorder traversal from this point
- to find the last node that mentions this method */
- method = node->method;
- last = node;
- do
- {
- if(GetRight(node) != &(cache->nil))
- {
- /* Move down the left-most sub-tree of the right */
- if(!add_parent(parent_buf, &parents, &num_parents,
- &max_parents, node))
- {
- goto failed;
- }
- node = GetRight(node);
- while(GetLeft(node) != &(cache->nil))
- {
- if(!add_parent(parent_buf, &parents, &num_parents,
- &max_parents, node))
- {
- goto failed;
- }
- node = GetLeft(node);
- }
- }
- else
- {
- /* Find a parent or other ancestor that contains this
- node within its left sub-tree */
- for(;;)
- {
- if(num_parents == 0)
- {
- node = 0;
- break;
- }
- parent = parents[--num_parents];
- if(GetLeft(parent) == node)
- {
- /* We are on our parent's left, so next is parent */
- node = parent;
- break;
- }
- node = parent;
- }
- if(!node)
- {
- /* We reached the root of the tree */
- break;
- }
- }
- if(node->method == method)
- {
- last = node;
- }
- }
- while(node->method == method);
- if(parents != parent_buf)
- {
- jit_free(parents);
- }
- return last->end;
- }
- }
-failed:
- if(parents != parent_buf)
- {
- jit_free(parents);
- }
- return 0;
-}
-
-/*
- * Temporary structure for iterating over a method's debug list.
- */
-typedef struct
-{
- jit_cache_debug_t list;
- UncompressReader reader;
-
-} jit_cache_debug_iter;
-
-/*
- * Initialize a debug information list iterator for a method.
- */
-static void InitDebugIter(jit_cache_debug_iter *iter,
- jit_cache_t cache, void *start)
-{
- jit_cache_method_t node = cache->head.right;
- while(node != &(cache->nil))
- {
- if(((unsigned char *)start) < node->start)
- {
- node = GetLeft(node);
- }
- else if(((unsigned char *)start) >= node->end)
- {
- node = GetRight(node);
- }
- else
- {
- iter->list = node->debug;
- if(iter->list)
- {
- iter->reader.data = (unsigned char *)(iter->list + 1);
- iter->reader.len = JIT_CACHE_DEBUG_SIZE;
- iter->reader.error = 0;
- }
- return;
- }
- }
- iter->list = 0;
-}
-
-/*
- * Get the next debug offset pair from a debug information list.
- * Returns non-zero if OK, or zero at the end of the list.
- */
-static int GetNextDebug(jit_cache_debug_iter *iter, unsigned long *offset,
- unsigned long *nativeOffset)
-{
- long value;
- while(iter->list)
- {
- value = UncompressInt(&(iter->reader));
- if(value == -1)
- {
- return 0;
- }
- else if(value != -2)
- {
- *offset = (unsigned long)value;
- *nativeOffset = (unsigned long)(UncompressInt(&(iter->reader)));
- return 1;
- }
- iter->list = iter->list->next;
- if(iter->list)
- {
- iter->reader.data = (unsigned char *)(iter->list + 1);
- iter->reader.len = JIT_CACHE_DEBUG_SIZE;
- iter->reader.error = 0;
- }
- }
- return 0;
-}
-
-unsigned long _jit_cache_get_native(jit_cache_t cache, void *start,
- unsigned long offset, int exact)
-{
- jit_cache_debug_iter iter;
- unsigned long ofs, nativeOfs;
- unsigned long prevNativeOfs = JIT_CACHE_NO_OFFSET;
-
- /* Search for the bytecode offset */
- InitDebugIter(&iter, cache, start);
- while(GetNextDebug(&iter, &ofs, &nativeOfs))
- {
- if(exact)
- {
- if(ofs == offset)
- {
- return nativeOfs;
- }
- }
- else if(ofs > offset)
- {
- return prevNativeOfs;
- }
- prevNativeOfs = nativeOfs;
- }
-
- return exact ? JIT_CACHE_NO_OFFSET : prevNativeOfs;
-}
-
-unsigned long _jit_cache_get_bytecode(jit_cache_t cache, void *start,
- unsigned long offset, int exact)
-{
- jit_cache_debug_iter iter;
- unsigned long ofs, nativeOfs;
- unsigned long prevOfs = JIT_CACHE_NO_OFFSET;
-
- /* Search for the native offset */
- InitDebugIter(&iter, cache, start);
- while(GetNextDebug(&iter, &ofs, &nativeOfs))
- {
- if(exact)
- {
- if(nativeOfs == offset)
- {
- return ofs;
- }
- }
- else if(nativeOfs > offset)
- {
- return prevOfs;
- }
- prevOfs = ofs;
- }
-
- return exact ? JIT_CACHE_NO_OFFSET : prevOfs;
-}
-
-unsigned long _jit_cache_get_size(jit_cache_t cache)
-{
- return (cache->numPages * cache->pageSize) -
- (cache->freeEnd - cache->freeStart);
-}
-
/*
Using the cache
--------------------
The cache consists of one or more "cache pages", which contain method
-code and auxillary data. The default size for a cache page is 64k
+code and auxiliary data. The default size for a cache page is 64k
(JIT_CACHE_PAGE_SIZE). The size is adjusted to be a multiple
of the system page size (usually 4k), and then stored in "pageSize".
Method code is written into a cache page starting at the bottom of the
-page, and growing upwards. Auxillary data is written into a cache page
+page, and growing upwards. Auxiliary data is written into a cache page
starting at the top of the page, and growing downwards. When the two
regions meet, a new cache page is allocated and the process restarts.
To allow methods bigger than a single cache page it is possible to
allocate a block of consecutive pages as a single unit. The method
-code and auxillary data is written to such a multiple-page block in
+code and auxiliary data is written to such a multiple-page block in
the same manner as into an ordinary page.
-Each method has one or more jit_cache_method auxillary data blocks associated
+Each method has one or more jit_cache_method auxiliary data blocks associated
with it. These blocks indicate the start and end of regions within the
method. Normally these regions correspond to exception "try" blocks, or
regular code between "try" blocks.
Each method can also have offset information associated with it, to map
between native code addresses and offsets within the original bytecode.
This is typically used to support debugging. Offset information is stored
-as auxillary data, attached to the jit_cache_method block.
+as auxiliary data, attached to the jit_cache_method block.
Threading issues
----------------