From e5446328c2cb2951602484398af7b1e843397f66 Mon Sep 17 00:00:00 2001 From: Aleksey Demakov Date: Sun, 4 Feb 2007 14:31:32 +0000 Subject: [PATCH] provide for user defined on-demand compilation drivers --- ChangeLog | 7 ++ include/jit/jit-common.h | 7 ++ include/jit/jit-context.h | 3 + include/jit/jit-function.h | 5 +- jit/jit-context.c | 49 ++++++++ jit/jit-function.c | 222 ++++++++++++++++++++++++++++--------- jit/jit-internal.h | 3 + 7 files changed, 240 insertions(+), 56 deletions(-) diff --git a/ChangeLog b/ChangeLog index fef5ee0..51f67af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2007-02-04 Aleksey Demakov + + * include/jit/jit-common.h, include/jit/jit-context.h, + * include/jit/jit-function.h, jit/jit-internal.h, jit/jit-context.c, + * jit/jit-function.c: provide for user defined on-demand compilation + driver and delayed function entry point setup. + 2007-01-28 Aleksey Demakov * jit/jit-reg-alloc.c (_jit_regs_gen, set_regdesc_flags): fix bugs diff --git a/include/jit/jit-common.h b/include/jit/jit-common.h index bb03f7b..edd6633 100644 --- a/include/jit/jit-common.h +++ b/include/jit/jit-common.h @@ -88,6 +88,13 @@ typedef void (*jit_meta_free_func)(void *data); */ typedef int (*jit_on_demand_func)(jit_function_t func); +/* + * Function that is used to control on demand compilation. + * Typically, it should take care of the context locking and unlocking, + * calling function's on demand compiler, and final compilation. + */ +typedef void *(*jit_on_demand_driver_func)(jit_function_t func); + #ifdef __cplusplus }; #endif diff --git a/include/jit/jit-context.h b/include/jit/jit-context.h index 03159b6..59452a9 100644 --- a/include/jit/jit-context.h +++ b/include/jit/jit-context.h @@ -32,6 +32,9 @@ void jit_context_destroy(jit_context_t context) JIT_NOTHROW; int jit_context_supports_threads(jit_context_t context) JIT_NOTHROW; void jit_context_build_start(jit_context_t context) JIT_NOTHROW; void jit_context_build_end(jit_context_t context) JIT_NOTHROW; +void jit_context_set_on_demand_driver( + jit_context_t context, + jit_on_demand_driver_func driver) JIT_NOTHROW; int jit_context_set_meta (jit_context_t context, int type, void *data, jit_meta_free_func free_data) JIT_NOTHROW; diff --git a/include/jit/jit-function.h b/include/jit/jit-function.h index 7e5e7a9..b9d6db7 100644 --- a/include/jit/jit-function.h +++ b/include/jit/jit-function.h @@ -53,6 +53,8 @@ int jit_function_is_compiled(jit_function_t func) JIT_NOTHROW; void jit_function_set_recompilable(jit_function_t func) JIT_NOTHROW; void jit_function_clear_recompilable(jit_function_t func) JIT_NOTHROW; int jit_function_is_recompilable(jit_function_t func) JIT_NOTHROW; +int jit_function_compile_entry(jit_function_t func, void **entry_point) JIT_NOTHROW; +void jit_function_setup_entry(jit_function_t func, void *entry_point) JIT_NOTHROW; void *jit_function_to_closure(jit_function_t func) JIT_NOTHROW; jit_function_t jit_function_from_closure (jit_context_t context, void *closure) JIT_NOTHROW; @@ -62,7 +64,8 @@ void *jit_function_to_vtable_pointer(jit_function_t func) JIT_NOTHROW; jit_function_t jit_function_from_vtable_pointer (jit_context_t context, void *vtable_pointer) JIT_NOTHROW; void jit_function_set_on_demand_compiler - (jit_function_t func, jit_on_demand_func on_demand) JIT_NOTHROW; + (jit_function_t func, jit_on_demand_func on_demand) JIT_NOTHROW; +jit_on_demand_func jit_function_get_on_demand_compiler(jit_function_t func) JIT_NOTHROW; int jit_function_apply (jit_function_t func, void **args, void *return_area); int jit_function_apply_vararg diff --git a/jit/jit-context.c b/jit/jit-context.c index d80ed06..8e0f0f2 100644 --- a/jit/jit-context.c +++ b/jit/jit-context.c @@ -88,6 +88,7 @@ jit_context_t jit_context_create(void) jit_mutex_create(&(context->cache_lock)); context->functions = 0; context->last_function = 0; + context->on_demand_driver = 0; return context; } @@ -158,6 +159,54 @@ void jit_context_build_end(jit_context_t context) jit_mutex_unlock(&(context->builder_lock)); } +/*@ + * @deftypefun void jit_context_set_on_demand_driver (jit_context_t context, jit_on_demand_driver_func driver) + * Specify the C function to be called to drive on-demand compilation. + * + * When on-demand compilation is requested the default driver provided by + * @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. + * + * @item + * The entry point of the compiled function is returned from the + * driver. + * @end enumerate + * + * You may need to provide your own driver if some additional actions + * are required. + * + * @end deftypefun +@*/ +void jit_context_set_on_demand_driver(jit_context_t context, jit_on_demand_driver_func driver) +{ + context->on_demand_driver = driver; +} + /*@ * @deftypefun int jit_context_set_meta (jit_context_t context, int type, {void *} data, jit_meta_free_func free_data) * Tag a context with some metadata. Returns zero if out of memory. diff --git a/jit/jit-function.c b/jit/jit-function.c index ffb84bd..e166398 100644 --- a/jit/jit-function.c +++ b/jit/jit-function.c @@ -49,6 +49,9 @@ jit_function_t jit_function_create(jit_context_t context, jit_type_t signature) { jit_function_t func; +#if defined(jit_redirector_size) + jit_on_demand_driver_func on_demand_driver; +#endif #if defined(jit_redirector_size) || defined(jit_indirector_size) jit_cache_t cache; #endif @@ -104,20 +107,24 @@ jit_function_t jit_function_create(jit_context_t context, jit_type_t signature) func->context = context; func->signature = jit_type_copy(signature); -#if defined(jit_redirector_size) && !defined(JIT_BACKEND_INTERP) +#if 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 */ + on_demand_driver = context->on_demand_driver; + if(!on_demand_driver) + { + on_demand_driver = _jit_function_compile_on_demand; + } func->entry_point = _jit_create_redirector - (func->redirector, (void *)_jit_function_compile_on_demand, + (func->redirector, (void *) on_demand_driver, func, jit_type_get_abi(signature)); jit_flush_exec(func->redirector, jit_redirector_size); - +#endif # if defined(jit_indirector_size) _jit_create_indirector(func->indirector, (void**) &(func->entry_point)); jit_flush_exec(func->indirector, jit_indirector_size); # endif -#endif /* Add the function to the context list */ func->next = 0; @@ -618,34 +625,10 @@ static void compile_block(jit_gencode_t gen, jit_function_t func, } /* - * Information that is stored for an exception region in the cache. + * Compile a function and return its entry point. */ -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_compile (jit_function_t func) - * Compile a function to its executable form. If the function was - * already compiled, then do nothing. Returns zero on error. - * - * If an error occurs, you can use @code{jit_function_abandon} to - * completely destroy the function. Once the function has been compiled - * successfully, it can no longer be abandoned. - * - * Sometimes you may wish to recompile a function, to apply greater - * levels of optimization the second time around. You must call - * @code{jit_function_set_recompilable} before you compile the function - * the first time. On the second time around, build the function's - * instructions again, and call @code{jit_function_compile} - * a second time. - * @end deftypefun -@*/ -int jit_function_compile(jit_function_t func) +static int +compile(jit_function_t func, void **entry_point) { struct jit_gencode gen; jit_cache_t cache; @@ -657,22 +640,6 @@ int jit_function_compile(jit_function_t func) int have_prolog; #endif - /* Bail out if we have nothing to do */ - if(!func) - { - return 0; - } - if(func->is_compiled && !(func->builder)) - { - /* The function is already compiled, and we don't need to recompile */ - return 1; - } - if(!(func->builder)) - { - /* We don't have anything to compile at all */ - return 0; - } - /* We need the cache lock while we are compiling the function */ jit_mutex_lock(&(func->context->cache_lock)); @@ -809,7 +776,6 @@ int jit_function_compile(jit_function_t func) block->fixup_absolute_list = 0; } } - } while(result == JIT_CACHE_END_RESTART); @@ -841,18 +807,146 @@ int jit_function_compile(jit_function_t func) func->no_return = 1; } - /* Record the entry point */ - func->entry_point = start; - func->is_compiled = 1; - /* Free the builder structure, which we no longer require */ _jit_function_free_builder(func); /* The function has been compiled successfully */ jit_mutex_unlock(&(func->context->cache_lock)); + + /* Record the entry point */ + if(entry_point) + { + *entry_point = start; + } + return 1; } +/* + * 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_compile (jit_function_t func) + * Compile a function to its executable form. If the function was + * already compiled, then do nothing. Returns zero on error. + * + * If an error occurs, you can use @code{jit_function_abandon} to + * completely destroy the function. Once the function has been compiled + * successfully, it can no longer be abandoned. + * + * Sometimes you may wish to recompile a function, to apply greater + * levels of optimization the second time around. You must call + * @code{jit_function_set_recompilable} before you compile the function + * the first time. On the second time around, build the function's + * instructions again, and call @code{jit_function_compile} + * a second time. + * @end deftypefun +@*/ +int jit_function_compile(jit_function_t func) +{ + int result; + void *entry_point; + + /* Bail out if we have nothing to do */ + if(!func) + { + return 0; + } + if(func->is_compiled && !(func->builder)) + { + /* The function is already compiled, and we don't need to recompile */ + return 1; + } + if(!(func->builder)) + { + /* We don't have anything to compile at all */ + return 0; + } + + /* Compile and record the entry point. */ + result = compile(func, &entry_point); + if(result) + { + func->entry_point = entry_point; + func->is_compiled = 1; + } + + return result; +} + +/*@ + * @deftypefun int jit_function_compile_entry (jit_function_t func, void **entry_point) + * Compile a function to its executable form but do not make it + * available for invocation yet. It may be made available later + * with @code{jit_function_setup_entry}. + * @end deftypefun +@*/ +int +jit_function_compile_entry(jit_function_t func, void **entry_point) +{ + /* Init entry_point */ + if(entry_point) + { + *entry_point = 0; + } + else + { + return 0; + } + + /* Bail out if we have nothing to do */ + if(!func) + { + return 0; + } + if(func->is_compiled && !(func->builder)) + { + /* The function is already compiled, and we don't need to recompile */ + return 1; + } + if(!(func->builder)) + { + /* We don't have anything to compile at all */ + return 0; + } + + /* Compile and return the entry point. */ + return compile(func, entry_point); +} + +/*@ + * @deftypefun int jit_function_setup_entry (jit_function_t func, void *entry_point) + * Make a function compiled with @code{jit_function_compile_entry} + * available for invocation and free the resources used for + * compilation. If @code{entry_point} is null then it only + * frees the resources. + * @end deftypefun +@*/ +void +jit_function_setup_entry(jit_function_t func, void *entry_point) +{ + /* Bail out if we have nothing to do */ + if(!func) + { + return; + } + /* Record the entry point */ + if(entry_point) + { + func->entry_point = entry_point; + func->is_compiled = 1; + } + _jit_function_free_builder(func); +} + /*@ * @deftypefun int jit_function_recompile (jit_function_t func) * Force @code{func} to be recompiled, by calling its on-demand @@ -1202,10 +1296,28 @@ jit_function_t jit_function_from_vtable_pointer(jit_context_t context, void *vta * 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) +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 func) + * Returns function's on-demand compiler. + * @end deftypefun +@*/ +jit_on_demand_func +jit_function_get_on_demand_compiler(jit_function_t func) { - func->on_demand = on_demand; + if(func) + { + return func->on_demand; + } + return 0; } void *_jit_function_compile_on_demand(jit_function_t func) diff --git a/jit/jit-internal.h b/jit/jit-internal.h index 409b151..91d1828 100644 --- a/jit/jit-internal.h +++ b/jit/jit-internal.h @@ -478,6 +478,9 @@ struct _jit_context /* Debugger support */ jit_debugger_hook_func debug_hook; jit_debugger_t debugger; + + /* On-demand compilation driver */ + jit_on_demand_driver_func on_demand_driver; }; /* -- 2.47.3