jit_mutex_create(&(context->cache_lock));
context->functions = 0;
context->last_function = 0;
+ context->on_demand_driver = 0;
return 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.
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
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;
}
/*
- * 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;
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));
block->fixup_absolute_list = 0;
}
}
-
}
while(result == JIT_CACHE_END_RESTART);
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
* 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)