

5. Building and compiling functions with the JIT

Function: jit_function_t jit_function_create (jit_context_t context, jit_type_t 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 jit_function_compile to convert it into its executable form.

It is recommended that you call jit_context_build_start before calling jit_function_create, and then call jit_context_build_end after you have called jit_function_compile. This will protect the JIT's internal data structures within a multi-threaded environment.

Function: jit_function_t jit_function_create_nested (jit_context_t context, jit_type_t signature, jit_function_t parent)

Create a new function block and associate it with a JIT context. In addition, this function is nested inside the specified 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.

Function: void jit_function_abandon (jit_function_t 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 func object is completely destroyed and detached from its owning context. The function is left alone if it was already compiled.

Function: jit_context_t jit_function_get_context (jit_function_t func)

Get the context associated with a function.

Function: jit_type_t jit_function_get_signature (jit_function_t func)

Get the signature associated with a function.

Function: int jit_function_set_meta (jit_function_t func, int type, void *data, jit_meta_free_func free_data, int 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 build_only is non-zero, then the metadata will be freed when the function is compiled with jit_function_compile. Otherwise the metadata will persist until the JIT context is destroyed, or jit_function_free_meta is called for the specified type.


Function: void * jit_function_get_meta (jit_function_t func, int type)


Function: void jit_function_free_meta (jit_function_t func, int type)

Free metadata of a specific type on a function. Does nothing if the type does not have any metadata associated with it.

Function: jit_function_t jit_function_next (jit_context_t context, jit_function_t prev)

Iterate over the defined functions in creation order. The prev argument should be NULL on the first call. Returns NULL at the end.

Function: jit_function_t jit_function_previous (jit_context_t context, jit_function_t prev)

Iterate over the defined functions in reverse creation order.

Function: jit_block_t jit_function_get_entry (jit_function_t func)

Get the entry block for a function. This is always the first block created by jit_function_create.

Function: jit_block_t jit_function_get_current (jit_function_t func)

Get the current block for a function. New blocks are created by certain jit_insn_xxx calls.

Function: jit_function_t jit_function_get_nested_parent (jit_function_t func)

Get the nested parent for a function, or NULL if func does not have a nested parent.

Function: 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 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 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 jit_function_compile a second time.

Function: 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 jit_function_setup_entry.

Function: int jit_function_setup_entry (jit_function_t func, void *entry_point)

Make a function compiled with jit_function_compile_entry available for invocation and free the resources used for compilation. If entry_point is null then it only frees the resources.

Function: int jit_function_is_compiled (jit_function_t func)

Determine if a function has already been compiled.

Function: int jit_function_set_recompilable (jit_function_t func)

Mark this function as a candidate for recompilation. That is, it is possible that we may call 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 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.

Function: void jit_function_clear_recompilable (jit_function_t 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 jit_function_compile again.

Future uses of the function with 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.

Function: int jit_function_is_recompilable (jit_function_t func)

Determine if this function is recompilable.

Function: void * jit_function_to_closure (jit_function_t 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.

Function: jit_function_t jit_function_from_closure (jit_context_t context, void *closure)

Convert a closure back into a function. Returns NULL if the closure does not correspond to a function in the specified context.

Function: jit_function_t jit_function_from_pc (jit_context_t context, void *pc, void **handler)

Get the function that contains the specified program counter location. Also return the address of the catch handler for the same location. Returns NULL if the program counter does not correspond to a function under the control of context.

Function: void * jit_function_to_vtable_pointer (jit_function_t func)

Return a pointer that is suitable for referring to this function from a vtable. Such pointers should only be used with the jit_insn_call_vtable instruction.

Using jit_insn_call_vtable is generally more efficient than 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 jit_insn_call_indirect.

Function: jit_function_t jit_function_from_vtable_pointer (jit_context_t context, void *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.

Function: void jit_function_set_on_demand_compiler (jit_function_t func, jit_on_demand_func on_demand)

Specify the C function to be called when 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, libjit takes the following actions:

  1. The context is locked by calling jit_context_build_start.

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 jit_function_create.

Function: jit_on_demand_func jit_function_get_on_demand_compiler (jit_function_t func)

Returns function's on-demand compiler.

Function: int jit_function_apply (jit_function_t func, void **args, void *return_area)

Call the function func with the supplied arguments. Each element in args is a pointer to one of the arguments, and 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 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 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.

Function: int jit_function_apply_vararg (jit_function_t func, jit_type_t signature, void **args, void *return_area)

Call the function 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 jit_function_apply.

Function: void jit_function_set_optimization_level (jit_function_t func, unsigned int level)

Set the optimization level for func. Increasing values indicate that the libjit dynamic compiler should expend more effort to generate better code for this function. Usually you would increase this value just before forcing func to recompile.

When the optimization level reaches the value returned by jit_function_get_max_optimization_level(), there is usually little point in continuing to recompile the function because 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 func again even once it has reached the maximum optimization level.

Function: unsigned int jit_function_get_optimization_level (jit_function_t func)

Get the current optimization level for func.

Function: unsigned int jit_function_get_max_optimization_level (void)

Get the maximum optimization level that is supported by libjit.

Function: jit_label_t jit_function_reserve_label (jit_function_t func)

Allocate a new label for later use within the function 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.

