4.1 Using libjit in a multi-threaded environment

The library does not handle the creation, management, and destruction
of threads itself. It is up to the front-end environment to take
care of that. But the library is thread-aware, as long as you take
some very simple steps.

In a multi-threaded environment, you must ensure that only one
thread can build functions at any one time. Otherwise the
JIT's context may become corrupted. To protect the system,
you should call jit_context_build_start before
creating the function. And then call jit_context_build_end
once the function has been fully compiled.

You can compile multiple functions during the one build process
if you wish, which is the normal case when compiling a class.

It is usually a good idea to suspend the finalization of
garbage-collected objects while function building is in progress.
Otherwise you may get a deadlock when the finalizer thread tries
to call the builder to compile a finalization routine. Suspension
of finalization is the responsibility of the caller.

4.2 Context functions

The following functions are available to create, manage, and
ultimately destroy JIT contexts:

Function: jit_context_t jit_context_create (void)

Create a new context block for the JIT. Returns NULL
if out of memory.

Function: void jit_context_destroy (jit_context_t context)

Destroy a JIT context block and everything that is associated with it.
It is very important that no threads within the program are currently
running compiled code when this function is called.

Function: int jit_context_supports_threads (jit_context_t context)

Determine if the JIT supports threads.

Function: void jit_context_build_start (jit_context_t context)

This routine should be called before you start building a function
to be JIT'ed. It acquires a lock on the context to prevent other
threads from accessing the build process, since only one thread
can be performing build operations at any one time.

Function: void jit_context_build_end (jit_context_t context)

This routine should be called once you have finished building
and compiling a function and are ready to resume normal execution.
This routine will release the build lock, allowing other threads
that are waiting on the builder to proceed.

When on-demand compilation is requested the default driver provided by
libjit takes the following actions:

The context is locked by calling jit_context_build_start.

If the function has already been compiled, 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.

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 JIT_RESULT_OK, JIT_RESULT_COMPILE_ERROR,
or JIT_RESULT_OUT_OF_MEMORY.

The context is unlocked by calling jit_context_build_end and
libjit jumps to the newly-compiled entry point. If an error
occurs, a built-in exception of type JIT_RESULT_COMPILE_ERROR
or JIT_RESULT_OUT_OF_MEMORY will be thrown.

The entry point of the compiled function is returned from the
driver.

You may need to provide your own driver if some additional actions
are required.

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 context that is specific to the
virtual machine or language.

If the type already has some metadata associated with it, then
the previous value will be freed.

Tag a context with numeric metadata. Returns zero if out of memory.
This function is more convenient for accessing the context's
special option values:

JIT_OPTION_CACHE_LIMIT

A numeric option that indicates the maximum size in bytes of the function
cache. If set to zero (the default), the function cache is unlimited
in size.

JIT_OPTION_CACHE_PAGE_SIZE

A numeric option that indicates the size in bytes of a single page in the
function cache. Memory is allocated for the cache in chunks of
this size. If set to zero, the cache page size is set to an
internally-determined default (usually 128k). The cache page size
also determines the maximum size of a single compiled function.

JIT_OPTION_PRE_COMPILE

A numeric option that indicates that this context is being used
for pre-compilation if it is set to a non-zero value. Code within
pre-compiled contexts cannot be executed directly. Instead, they
can be written out to disk in ELF format to be reloaded at
some future time.

JIT_OPTION_DONT_FOLD

A numeric option that disables constant folding when it is set to a
non-zero value. This is useful for debugging, as it forces libjit to
always execute constant expressions at run time, instead of at compile time.

JIT_OPTION_POSITION_INDEPENDENT

A numeric option that forces generation of position-independent code (PIC)
if it is set to a non-zero value. This may be mainly useful for pre-compiled
contexts.

Metadata type values of 10000 or greater are reserved for internal use.