You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1068 lines
29 KiB

/*
* jit-function.c - Functions for manipulating function blocks.
*
* Copyright (C) 2004, 2006-2008 Southern Storm Software, Pty Ltd.
*
* This file is part of the libjit library.
*
* The libjit library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 2.1 of
* the License, or (at your option) any later version.
*
* The libjit library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the libjit library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "jit-internal.h"
#include "jit-apply-func.h"
#include "jit-rules.h"
#include "jit-setjmp.h"
/*@
* @deftypefun jit_function_t jit_function_create (jit_context_t @var{context}, jit_type_t @var{signature})
* Create a new function block and associate it with a JIT context.
* Returns NULL if out of memory.
*
* A function persists for the lifetime of its containing context.
* It initially starts life in the "building" state, where the user
* constructs instructions that represents the function body.
* Once the build process is complete, the user calls
* @code{jit_function_compile} to convert it into its executable form.
*
* It is recommended that you call @code{jit_context_build_start} before
* calling @code{jit_function_create}, and then call
* @code{jit_context_build_end} after you have called
* @code{jit_function_compile}. This will protect the JIT's internal
* data structures within a multi-threaded environment.
* @end deftypefun
@*/
jit_function_t
jit_function_create(jit_context_t context, jit_type_t signature)
{
jit_function_t func;
#if !defined(JIT_BACKEND_INTERP) && (defined(jit_redirector_size) || defined(jit_indirector_size))
unsigned char *trampoline;
#endif
/* Acquire the memory context */
_jit_memory_lock(context);
if(!_jit_memory_ensure(context))
{
_jit_memory_unlock(context);
return 0;
}
/* Allocate memory for the function and clear it */
func = _jit_memory_alloc_function(context);
if(!func)
{
_jit_memory_unlock(context);
return 0;
}
#if !defined(JIT_BACKEND_INTERP) && (defined(jit_redirector_size) || defined(jit_indirector_size))
trampoline = (unsigned char *) _jit_memory_alloc_trampoline(context);
if(!trampoline)
{
_jit_memory_free_function(context, func);
_jit_memory_unlock(context);
return 0;
}
# if defined(jit_redirector_size)
func->redirector = trampoline;
trampoline += jit_redirector_size;
# endif
# if defined(jit_indirector_size)
func->indirector = trampoline;
# endif
#endif /* !defined(JIT_BACKEND_INTERP) && (defined(jit_redirector_size) || defined(jit_indirector_size)) */
/* Release the memory context */
_jit_memory_unlock(context);
/* Initialize the function block */
func->context = context;
func->signature = jit_type_copy(signature);
func->optimization_level = JIT_OPTLEVEL_NORMAL;
#if !defined(JIT_BACKEND_INTERP) && defined(jit_redirector_size)
/* If we aren't using interpretation, then point the function's
initial entry point at the redirector, which in turn will
invoke the on-demand compiler */
func->entry_point = _jit_create_redirector
(func->redirector, (void *) context->on_demand_driver,
func, jit_type_get_abi(signature));
_jit_flush_exec(func->redirector, jit_redirector_size);
#endif
#if !defined(JIT_BACKEND_INTERP) && defined(jit_indirector_size)
_jit_create_indirector(func->indirector, (void**) &(func->entry_point));
_jit_flush_exec(func->indirector, jit_indirector_size);
#endif
/* Add the function to the context list */
func->next = 0;
func->prev = context->last_function;
if(context->last_function)
{
context->last_function->next = func;
}
else
{
context->functions = func;
}
context->last_function = func;
/* Return the function to the caller */
return func;
}
/*@
* @deftypefun jit_function_t jit_function_create_nested (jit_context_t @var{context}, jit_type_t @var{signature}, jit_function_t @var{parent})
* Create a new function block and associate it with a JIT context.
* In addition, this function is nested inside the specified
* @var{parent} function and is able to access its parent's
* (and grandparent's) local variables.
*
* The front end is responsible for ensuring that the nested function can
* never be called by anyone except its parent and sibling functions.
* The front end is also responsible for ensuring that the nested function
* is compiled before its parent.
* @end deftypefun
@*/
jit_function_t jit_function_create_nested
(jit_context_t context, jit_type_t signature, jit_function_t parent)
{
jit_function_t func;
func = jit_function_create(context, signature);
if(!func)
{
return 0;
}
func->nested_parent = parent;
return func;
}
int _jit_function_ensure_builder(jit_function_t func)
{
/* Handle the easy cases first */
if(!func)
{
return 0;
}
if(func->builder)
{
return 1;
}
/* Allocate memory for the builder and clear it */
func->builder = jit_cnew(struct _jit_builder);
if(!(func->builder))
{
return 0;
}
/* Cache the value of the JIT_OPTION_POSITION_INDEPENDENT option */
func->builder->position_independent
= jit_context_get_meta_numeric(
func->context, JIT_OPTION_POSITION_INDEPENDENT);
/* Initialize the function builder */
jit_memory_pool_init(&(func->builder->value_pool), struct _jit_value);
jit_memory_pool_init(&(func->builder->edge_pool), struct _jit_edge);
jit_memory_pool_init(&(func->builder->meta_pool), struct _jit_meta);
/* Create the entry block */
if(!_jit_block_init(func))
{
_jit_function_free_builder(func);
return 0;
}
/* Create instructions to initialize the incoming arguments */
func->builder->current_block = func->builder->entry_block;
if(!_jit_create_entry_insns(func))
{
_jit_function_free_builder(func);
return 0;
}
/* The current position is where initialization code will be
inserted by "jit_insn_move_blocks_to_start" */
func->builder->init_block = func->builder->current_block;
/* Start first block for function body */
if(!jit_insn_new_block(func))
{
_jit_function_free_builder(func);
return 0;
}
/* The builder is ready to go */
return 1;
}
void _jit_function_free_builder(jit_function_t func)
{
if(func->builder)
{
_jit_block_free(func);
jit_memory_pool_free(&(func->builder->edge_pool), 0);
jit_memory_pool_free(&(func->builder->value_pool), _jit_value_free);
jit_memory_pool_free(&(func->builder->meta_pool), _jit_meta_free_one);
jit_free(func->builder->param_values);
jit_free(func->builder->label_info);
jit_free(func->builder);
func->builder = 0;
func->is_optimized = 0;
}
}
void
_jit_function_destroy(jit_function_t func)
{
jit_context_t context;
if(!func)
{
return;
}
context = func->context;
if(func->next)
{
func->next->prev = func->prev;
}
else
{
context->last_function = func->prev;
}
if(func->prev)
{
func->prev->next = func->next;
}
else
{
context->functions = func->next;
}
_jit_function_free_builder(func);
_jit_varint_free_data(func->bytecode_offset);
jit_meta_destroy(&func->meta);
jit_type_free(func->signature);
_jit_memory_lock(context);
#if !defined(JIT_BACKEND_INTERP) && (defined(jit_redirector_size) || defined(jit_indirector_size))
# if defined(jit_redirector_size)
_jit_memory_free_trampoline(context, func->redirector);
# else
_jit_memory_free_trampoline(context, func->indirector);
# endif
#endif
_jit_memory_free_function(context, func);
_jit_memory_unlock(context);
}
/*@
* @deftypefun void jit_function_abandon (jit_function_t @var{func})
* Abandon this function during the build process. This should be called
* when you detect a fatal error that prevents the function from being
* properly built. The @var{func} object is completely destroyed and
* detached from its owning context. The function is left alone if
* it was already compiled.
* @end deftypefun
@*/
void jit_function_abandon(jit_function_t func)
{
if(func && func->builder)
{
if(func->is_compiled)
{
/* We already compiled this function previously, but we
have tried to recompile it with new contents. Throw
away the builder, but keep the original version */
_jit_function_free_builder(func);
}
else
{
/* This function was never compiled, so abandon entirely */
_jit_function_destroy(func);
}
}
}
/*@
* @deftypefun jit_context_t jit_function_get_context (jit_function_t @var{func})
* Get the context associated with a function.
* @end deftypefun
@*/
jit_context_t jit_function_get_context(jit_function_t func)
{
if(func)
{
return func->context;
}
else
{
return 0;
}
}
/*@
* @deftypefun jit_type_t jit_function_get_signature (jit_function_t @var{func})
* Get the signature associated with a function.
* @end deftypefun
@*/
jit_type_t jit_function_get_signature(jit_function_t func)
{
if(func)
{
return func->signature;
}
else
{
return 0;
}
}
/*@
* @deftypefun int jit_function_set_meta (jit_function_t @var{func}, int @var{type}, void *@var{data}, jit_meta_free_func @var{free_data}, int @var{build_only})
* Tag a function with some metadata. Returns zero if out of memory.
*
* Metadata may be used to store dependency graphs, branch prediction
* information, or any other information that is useful to optimizers
* or code generators. It can also be used by higher level user code
* to store information about the function that is specific to the
* virtual machine or language.
*
* If the @var{type} already has some metadata associated with it, then
* the previous value will be freed.
*
* If @var{build_only} is non-zero, then the metadata will be freed
* when the function is compiled with @code{jit_function_compile}.
* Otherwise the metadata will persist until the JIT context is destroyed,
* or @code{jit_function_free_meta} is called for the specified @var{type}.
*
* Metadata type values of 10000 or greater are reserved for internal use.
* @end deftypefun
@*/
int jit_function_set_meta(jit_function_t func, int type, void *data,
jit_meta_free_func free_data, int build_only)
{
if(build_only)
{
if(!_jit_function_ensure_builder(func))
{
return 0;
}
return jit_meta_set(&(func->builder->meta), type, data,
free_data, func);
}
else
{
return jit_meta_set(&(func->meta), type, data, free_data, 0);
}
}
/*@
* @deftypefun {void *} jit_function_get_meta (jit_function_t @var{func}, int @var{type})
* Get the metadata associated with a particular tag. Returns NULL
* if @var{type} does not have any metadata associated with it.
* @end deftypefun
@*/
void *jit_function_get_meta(jit_function_t func, int type)
{
void *data = jit_meta_get(func->meta, type);
if(!data && func->builder)
{
data = jit_meta_get(func->builder->meta, type);
}
return data;
}
/*@
* @deftypefun void jit_function_free_meta (jit_function_t @var{func}, int @var{type})
* Free metadata of a specific type on a function. Does nothing if
* the @var{type} does not have any metadata associated with it.
* @end deftypefun
@*/
void jit_function_free_meta(jit_function_t func, int type)
{
jit_meta_free(&(func->meta), type);
if(func->builder)
{
jit_meta_free(&(func->builder->meta), type);
}
}
/*@
* @deftypefun jit_function_t jit_function_next (jit_context_t @var{context}, jit_function_t @var{prev})
* Iterate over the defined functions in creation order. The @var{prev}
* argument should be NULL on the first call. Returns NULL at the end.
* @end deftypefun
@*/
jit_function_t jit_function_next(jit_context_t context, jit_function_t prev)
{
if(prev)
{
return prev->next;
}
else if(context)
{
return context->functions;
}
else
{
return 0;
}
}
/*@
* @deftypefun jit_function_t jit_function_previous (jit_context_t @var{context}, jit_function_t @var{prev})
* Iterate over the defined functions in reverse creation order.
* @end deftypefun
@*/
jit_function_t jit_function_previous(jit_context_t context, jit_function_t prev)
{
if(prev)
{
return prev->prev;
}
else if(context)
{
return context->last_function;
}
else
{
return 0;
}
}
/*@
* @deftypefun jit_block_t jit_function_get_entry (jit_function_t @var{func})
* Get the entry block for a function. This is always the first block
* created by @code{jit_function_create}.
* @end deftypefun
@*/
jit_block_t jit_function_get_entry(jit_function_t func)
{
if(func && func->builder)
{
return func->builder->entry_block;
}
else
{
return 0;
}
}
/*@
* @deftypefun jit_block_t jit_function_get_current (jit_function_t @var{func})
* Get the current block for a function. New blocks are created by
* certain @code{jit_insn_xxx} calls.
* @end deftypefun
@*/
jit_block_t jit_function_get_current(jit_function_t func)
{
if(func && func->builder)
{
return func->builder->current_block;
}
else
{
return 0;
}
}
/*@
* @deftypefun jit_function_t jit_function_get_nested_parent (jit_function_t @var{func})
* Get the nested parent for a function, or NULL if @var{func}
* does not have a nested parent.
* @end deftypefun
@*/
jit_function_t jit_function_get_nested_parent(jit_function_t func)
{
if(func)
{
return func->nested_parent;
}
else
{
return 0;
}
}
/*
* Information that is stored for an exception region in the cache.
*/
typedef struct jit_cache_eh *jit_cache_eh_t;
struct jit_cache_eh
{
jit_label_t handler_label;
unsigned char *handler;
jit_cache_eh_t previous;
};
/*@
* @deftypefun int jit_function_is_compiled (jit_function_t @var{func})
* Determine if a function has already been compiled.
* @end deftypefun
@*/
int jit_function_is_compiled(jit_function_t func)
{
if(func)
{
return func->is_compiled;
}
else
{
return 0;
}
}
/*@
* @deftypefun int jit_function_set_recompilable (jit_function_t @var{func})
* Mark this function as a candidate for recompilation. That is,
* it is possible that we may call @code{jit_function_compile}
* more than once, to re-optimize an existing function.
*
* It is very important that this be called before the first time that
* you call @code{jit_function_compile}. Functions that are recompilable
* are invoked in a slightly different way to non-recompilable functions.
* If you don't set this flag, then existing invocations of the function
* may continue to be sent to the original compiled version, not the new
* version.
* @end deftypefun
@*/
void jit_function_set_recompilable(jit_function_t func)
{
if(func)
{
func->is_recompilable = 1;
}
}
/*@
* @deftypefun void jit_function_clear_recompilable (jit_function_t @var{func})
* Clear the recompilable flag on this function. Normally you would use
* this once you have decided that the function has been optimized enough,
* and that you no longer intend to call @code{jit_function_compile} again.
*
* Future uses of the function with @code{jit_insn_call} will output a
* direct call to the function, which is more efficient than calling
* its recompilable version. Pre-existing calls to the function may still
* use redirection stubs, and will remain so until the pre-existing
* functions are themselves recompiled.
* @end deftypefun
@*/
void jit_function_clear_recompilable(jit_function_t func)
{
if(func)
{
func->is_recompilable = 0;
}
}
/*@
* @deftypefun int jit_function_is_recompilable (jit_function_t @var{func})
* Determine if this function is recompilable.
* @end deftypefun
@*/
int jit_function_is_recompilable(jit_function_t func)
{
if(func)
{
return func->is_recompilable;
}
else
{
return 0;
}
}
#ifdef JIT_BACKEND_INTERP
/*
* Closure handling function for "jit_function_to_closure".
*/
static void function_closure(jit_type_t signature, void *result,
void **args, void *user_data)
{
if(!jit_function_apply((jit_function_t)user_data, args, result))
{
/* We cannot report the exception through the closure,
so we have no choice but to rethrow it up the stack */
jit_exception_throw(jit_exception_get_last());
}
}
#endif /* JIT_BACKEND_INTERP */
/*@
* @deftypefun {void *} jit_function_to_closure (jit_function_t @var{func})
* Convert a compiled function into a closure that can called directly
* from C. Returns NULL if out of memory, or if closures are not
* supported on this platform.
*
* If the function has not been compiled yet, then this will return
* a pointer to a redirector that will arrange for the function to be
* compiled on-demand when it is called.
*
* Creating a closure for a nested function is not recommended as
* C does not have any way to call such closures directly.
* @end deftypefun
@*/
void *jit_function_to_closure(jit_function_t func)
{
if(!func)
{
return 0;
}
#ifdef JIT_BACKEND_INTERP
return jit_closure_create(func->context, func->signature,
function_closure, (void *)func);
#else
/* On native platforms, use the closure entry point */
if(func->indirector && (!func->is_compiled || func->is_recompilable))
{
return func->indirector;
}
return func->entry_point;
#endif
}
/*@
* @deftypefun jit_function_t jit_function_from_closure (jit_context_t @var{context}, void *@var{closure})
* Convert a closure back into a function. Returns NULL if the
* closure does not correspond to a function in the specified context.
* @end deftypefun
@*/
jit_function_t
jit_function_from_closure(jit_context_t context, void *closure)
{
void *func_info;
if(!context)
{
return 0;
}
func_info = _jit_memory_find_function_info(context, closure);
if(!func_info)
{
return 0;
}
return _jit_memory_get_function(context, func_info);
}
/*@
* @deftypefun jit_function_t jit_function_from_pc (jit_context_t @var{context}, void *@var{pc}, void **@var{handler})
* Get the function that contains the specified program counter location.
* Also return the address of the @code{catch} handler for the same location.
* Returns NULL if the program counter does not correspond to a function
* under the control of @var{context}.
* @end deftypefun
@*/
jit_function_t
jit_function_from_pc(jit_context_t context, void *pc, void **handler)
{
void *func_info;
jit_function_t func;
if(!context)
{
return 0;
}
/* Get the function and the exception handler cookie */
func_info = _jit_memory_find_function_info(context, pc);
if(!func_info)
{
return 0;
}
func = _jit_memory_get_function(context, func_info);
if(!func)
{
return 0;
}
/* Convert the cookie into a handler address */
if(handler)
{
#if 0
if(func->cookie)
{
*handler = ((jit_cache_eh_t) func->cookie)->handler;
}
else
{
*handler = 0;
}
#else
*handler = func->cookie;
#endif
}
return func;
}
/*@
* @deftypefun {void *} jit_function_to_vtable_pointer (jit_function_t @var{func})
* Return a pointer that is suitable for referring to this function
* from a vtable. Such pointers should only be used with the
* @code{jit_insn_call_vtable} instruction.
*
* Using @code{jit_insn_call_vtable} is generally more efficient than
* @code{jit_insn_call_indirect} for calling virtual methods.
*
* The vtable pointer might be the same as the closure, but this isn't
* guaranteed. Closures can be used with @code{jit_insn_call_indirect}.
* @end deftypefun
@*/
void *
jit_function_to_vtable_pointer(jit_function_t func)
{
#ifdef JIT_BACKEND_INTERP
/* In the interpreted version, the function pointer is used in vtables */
return func;
#else
/* On native platforms, the closure entry point is the vtable pointer */
if(!func)
{
return 0;
}
if(func->indirector && (!func->is_compiled || func->is_recompilable))
{
return func->indirector;
}
return func->entry_point;
#endif
}
/*@
* @deftypefun jit_function_t jit_function_from_vtable_pointer (jit_context_t @var{context}, void *@var{vtable_pointer})
* Convert a vtable_pointer back into a function. Returns NULL if the
* vtable_pointer does not correspond to a function in the specified context.
* @end deftypefun
@*/
jit_function_t
jit_function_from_vtable_pointer(jit_context_t context, void *vtable_pointer)
{
#ifdef JIT_BACKEND_INTERP
/* In the interpreted version, the function pointer is used in vtables */
jit_function_t func = (jit_function_t)vtable_pointer;
if(func && func->context == context)
{
return func;
}
return 0;
#else
void *func_info;
if(!context)
{
return 0;
}
func_info = _jit_memory_find_function_info(context, vtable_pointer);
if(!func_info)
{
return 0;
}
return _jit_memory_get_function(context, func_info);
#endif
}
/*@
* @deftypefun void jit_function_set_on_demand_compiler (jit_function_t @var{func}, jit_on_demand_func @var{on_demand})
* Specify the C function to be called when @var{func} needs to be
* compiled on-demand. This should be set just after the function
* is created, before any build or compile processes begin.
*
* You won't need an on-demand compiler if you always build and compile
* your functions before you call them. But if you can call a function
* before it is built, then you must supply an on-demand compiler.
*
* When on-demand compilation is requested, @code{libjit} takes the following
* actions:
*
* @enumerate
* @item
* The context is locked by calling @code{jit_context_build_start}.
*
* @item
* If the function has already been compiled, @code{libjit} unlocks
* the context and returns immediately. This can happen because of race
* conditions between threads: some other thread may have beaten us
* to the on-demand compiler.
*
* @item
* The user's on-demand compiler is called. It is responsible for building
* the instructions in the function's body. It should return one of the
* result codes @code{JIT_RESULT_OK}, @code{JIT_RESULT_COMPILE_ERROR},
* or @code{JIT_RESULT_OUT_OF_MEMORY}.
*
* @item
* If the user's on-demand function hasn't already done so, @code{libjit}
* will call @code{jit_function_compile} to compile the function.
*
* @item
* The context is unlocked by calling @code{jit_context_build_end} and
* @code{libjit} jumps to the newly-compiled entry point. If an error
* occurs, a built-in exception of type @code{JIT_RESULT_COMPILE_ERROR}
* or @code{JIT_RESULT_OUT_OF_MEMORY} will be thrown.
* @end enumerate
*
* Normally you will need some kind of context information to tell you
* which higher-level construct is being compiled. You can use the
* metadata facility to add this context information to the function
* just after you create it with @code{jit_function_create}.
* @end deftypefun
@*/
void
jit_function_set_on_demand_compiler(jit_function_t func, jit_on_demand_func on_demand)
{
if(func)
{
func->on_demand = on_demand;
}
}
/*@
* @deftypefun jit_on_demand_func jit_function_get_on_demand_compiler (jit_function_t @var{func})
* Returns function's on-demand compiler.
* @end deftypefun
@*/
jit_on_demand_func
jit_function_get_on_demand_compiler(jit_function_t func)
{
if(func)
{
return func->on_demand;
}
return 0;
}
/*@
* @deftypefun int jit_function_apply (jit_function_t @var{func}, void **@var{args}, void *@var{return_area})
* Call the function @var{func} with the supplied arguments. Each element
* in @var{args} is a pointer to one of the arguments, and @var{return_area}
* points to a buffer to receive the return value. Returns zero if an
* exception occurred.
*
* This is the primary means for executing a function from ordinary
* C code without creating a closure first with @code{jit_function_to_closure}.
* Closures may not be supported on all platforms, but function application
* is guaranteed to be supported everywhere.
*
* Function applications acts as an exception blocker. If any exceptions
* occur during the execution of @var{func}, they won't travel up the
* stack any further than this point. This prevents ordinary C code
* from being accidentally presented with a situation that it cannot handle.
* This blocking protection is not present when a function is invoked
* via its closure.
* @end deftypefun
*
* @deftypefun int jit_function_apply_vararg (jit_function_t @var{func}, jit_type_t @var{signature}, void **@var{args}, void *@var{return_area})
* Call the function @var{func} with the supplied arguments. There may
* be more arguments than are specified in the function's original signature,
* in which case the additional values are passed as variable arguments.
* This function is otherwise identical to @code{jit_function_apply}.
* @end deftypefun
@*/
#if !defined(JIT_BACKEND_INTERP)
/* The interpreter version is in "jit-interp.cpp" */
int jit_function_apply(jit_function_t func, void **args, void *return_area)
{
if(func)
{
return jit_function_apply_vararg
(func, func->signature, args, return_area);
}
else
{
return jit_function_apply_vararg(func, 0, args, return_area);
}
}
int jit_function_apply_vararg
(jit_function_t func, jit_type_t signature, void **args, void *return_area)
{
struct jit_backtrace call_trace;
void *entry;
jit_jmp_buf jbuf;
/* Establish a "setjmp" point here so that we can unwind the
stack to this point when an exception occurs and then prevent
the exception from propagating further up the stack */
_jit_unwind_push_setjmp(&jbuf);
if(setjmp(jbuf.buf))
{
_jit_unwind_pop_setjmp();
return 0;
}
/* Create a backtrace entry that blocks exceptions from
flowing further than this up the stack */
_jit_backtrace_push(&call_trace, 0);
/* Get the function's entry point */
if(!func)
{
jit_exception_builtin(JIT_RESULT_NULL_FUNCTION);
return 0;
}
if(func->nested_parent)
{
jit_exception_builtin(JIT_RESULT_CALLED_NESTED);
return 0;
}
if(func->is_compiled)
{
entry = func->entry_point;
}
else
{
entry = (*func->context->on_demand_driver)(func);
}
/* Get the default signature if necessary */
if(!signature)
{
signature = func->signature;
}
/* Clear the exception state */
jit_exception_clear_last();
/* Apply the function. If it returns, then there is no exception */
jit_apply(signature, func->entry_point, args,
jit_type_num_params(func->signature), return_area);
/* Restore the backtrace and "setjmp" contexts and exit */
_jit_unwind_pop_setjmp();
return 1;
}
#endif /* !JIT_BACKEND_INTERP */
/*@
* @deftypefun void jit_function_set_optimization_level (jit_function_t @var{func}, unsigned int @var{level})
* Set the optimization level for @var{func}. Increasing values indicate
* that the @code{libjit} dynamic compiler should expend more effort to
* generate better code for this function. Usually you would increase
* this value just before forcing @var{func} to recompile.
*
* When the optimization level reaches the value returned by
* @code{jit_function_get_max_optimization_level()}, there is usually
* little point in continuing to recompile the function because
* @code{libjit} may not be able to do any better.
*
* The front end is usually responsible for choosing candidates for
* function inlining. If it has identified more such candidates, then
* it may still want to recompile @var{func} again even once it has
* reached the maximum optimization level.
* @end deftypefun
@*/
void
jit_function_set_optimization_level(jit_function_t func, unsigned int level)
{
unsigned int max_level = jit_function_get_max_optimization_level();
if(level > max_level)
{
level = max_level;
}
if(func)
{
func->optimization_level = level;
}
}
/*@
* @deftypefun {unsigned int} jit_function_get_optimization_level (jit_function_t @var{func})
* Get the current optimization level for @var{func}.
* @end deftypefun
@*/
unsigned int
jit_function_get_optimization_level(jit_function_t func)
{
if(func)
{
return func->optimization_level;
}
else
{
return JIT_OPTLEVEL_NONE;
}
}
/*@
* @deftypefun {unsigned int} jit_function_get_max_optimization_level (void)
* Get the maximum optimization level that is supported by @code{libjit}.
* @end deftypefun
@*/
unsigned int
jit_function_get_max_optimization_level(void)
{
return JIT_OPTLEVEL_NORMAL;
}
/*@
* @deftypefun {jit_label_t} jit_function_reserve_label (jit_function_t @var{func})
* Allocate a new label for later use within the function @var{func}. Most
* instructions that require a label could perform label allocation themselves.
* A separate label allocation could be useful to fill a jump table with
* identical entries.
* @end deftypefun
@*/
jit_label_t
jit_function_reserve_label(jit_function_t func)
{
/* Ensure that we have a function builder */
if(!_jit_function_ensure_builder(func))
{
return jit_label_undefined;
}
return (func->builder->next_label)++;
}
/*@
* @deftypefun {int} jit_function_labels_equal (jit_function_t @var{func}, jit_label_t @var{label}, jit_label_t @var{label2})
* Check if labels @var{label} and @var{label2} defined within the function
* @var{func} are equal that is belong to the same basic block. Labels that
* are not associated with any block are never considered equal.
* @end deftypefun
@*/
int
jit_function_labels_equal(jit_function_t func, jit_label_t label, jit_label_t label2)
{
jit_block_t block, block2;
if(func && func->builder
&& label != jit_label_undefined
&& label2 != jit_label_undefined
&& label < func->builder->max_label_info
&& label2 < func->builder->max_label_info)
{
block = func->builder->label_info[label].block;
if(block)
{
block2 = func->builder->label_info[label2].block;
return block == block2;
}
}
return 0;
}