Alien-LibJIT

 view release on metacpan or  search on metacpan

libjit/doc/libjit.texi  view on Meta::CPAN


@node Tutorial 3, Tutorial 4, Tutorial 2, Tutorials
@section Tutorial 3 - compiling on-demand
@cindex On-demand compilation tutorial

In the previous tutorials, we compiled everything that we needed
at startup time, and then entered the execution phase.  The real power
of a JIT becomes apparent when you use it to compile functions
only as they are called.  You can thus avoid compiling functions
that are never called in a given program run, saving memory and
startup time.

We demonstrate how to do on-demand compilation by rewriting Tutorial 1.
The source code for the modified version is in @code{tutorial/t3.c}.

When the @code{mul_add} function is created, we don't create its function
body or call @code{jit_function_compile}.  We instead provide a
C function called @code{compile_mul_add} that performs on-demand
compilation:

@example
jit_function_t function;
...
function = jit_function_create(context, signature);
jit_function_set_on_demand_compiler(function, compile_mul_add);
@end example

We can now call this function with @code{jit_function_apply}, and the
system will automatically call @code{compile_mul_add} for us if the
function hasn't been built yet.  The contents of @code{compile_mul_add}
are fairly obvious:

@example
int compile_mul_add(jit_function_t function)
@{
    jit_value_t x, y, z;
    jit_value_t temp1, temp2;

    x = jit_value_get_param(function, 0);
    y = jit_value_get_param(function, 1);
    z = jit_value_get_param(function, 2);

    temp1 = jit_insn_mul(function, x, y);
    temp2 = jit_insn_add(function, temp1, z);

    jit_insn_return(function, temp2);
    return 1;
@}
@end example

When the on-demand compiler returns, @code{libjit} will call
@code{jit_function_compile} and then jump to the newly compiled code.
Upon the second and subsequent calls to the function, @code{libjit}
will bypass the on-demand compiler and call the compiled code directly.
Note that in case of on-demand compilation @code{libjit} automatically
locks and unlocks the corresponding context with
@code{jit_context_build_start} and @code{jit_context_build_end} calls.

Sometimes you may wish to force a commonly used function to
be recompiled, so that you can apply additional optimization.
To do this, you must set the "recompilable" flag just after the
function is first created:

@example
jit_function_t function;
...
function = jit_function_create(context, signature);
jit_function_set_recompilable(function);
jit_function_set_on_demand_compiler(function, compile_mul_add);
@end example

Once the function is compiled (either on-demand or up-front) its
intermediate representation built by @code{libjit} is discarded.
To force the function to be recompiled you need to build it again
and call @code{jit_function_compile} after that.  As always when
the function is built and compiled manually it is necessary
to take care of context locking:

@example
jit_context_build_start(context);
jit_function_get_on_demand_compiler(function)(function);
jit_function_compile(function);
jit_context_build_end(context);
@end example

After this, any existing references to the function will be redirected
to the new version.  However, if some thread is currently executing the
previous version, then it will keep doing so until the previous version
exits.  Only after that will subsequent calls go to the new version.

In this tutorial, we use the same on-demand compiler when we
recompile @code{mul_add}.  In a real program, you would probably call
@code{jit_function_set_on_demand_compiler} to set a new on-demand
compiler that performs greater levels of optimization.

If you no longer intend to recompile the function, you should call
@code{jit_function_clear_recompilable} so that @code{libjit} can
manage the function more efficiently from then on.

The exact conditions under which a function should be recompiled
are not specified by @code{libjit}.  It may be because the function
has been called several times and has reached some threshold.
Or it may be because some other function that it calls has become a
candidate for inlining.  It is up to the front end to decide when
recompilation is warranted, usually based on language-specific
heuristics.

@c -----------------------------------------------------------------------

@node Tutorial 4, Tutorial 5, Tutorial 3, Tutorials
@section Tutorial 4 - mul_add, C++ version
@cindex mul_add C++ tutorial

While @code{libjit} can be easily accessed from C++ programs using
the C API's, you may instead wish to use an API that better reflects
the C++ programming paradigm.  We demonstrate how to do this by rewriting
Tutorial 3 using the @code{libjitplus} library.

@noindent
To use the @code{libjitplus} library, we first include
the @code{<jit/jit-plus.h>} file:

libjit/doc/libjit.texi  view on Meta::CPAN


The C++ library will create the function as compilable on-demand for
us, so we don't have to do that explicitly.  But we do have to override
the virtual @code{build()} method to build the function's body on-demand:

@example
void mul_add_function::build()
@{
    jit_value x = get_param(0);
    jit_value y = get_param(1);
    jit_value z = get_param(2);

    insn_return(x * y + z);
@}
@end example

This is similar to the first version that we wrote in Tutorial 1.
Instructions are created with @code{insn_*} methods that correspond
to their @code{jit_insn_*} counterparts in the C library.

One of the nice things about the C++ API compared to the C API is that we
can use overloaded operators to manipulate @code{jit_value} objects.
This can simplify the function build process considerably when we
have lots of expressions to compile.  We could have used @code{insn_mul}
and @code{insn_add} instead in this example and the result would have
been the same.

Now that we have our @code{mul_add_function} class, we can create
an instance of the function and apply it as follows:

@example
jit_context context;
mul_add_function mul_add(context);

jit_int arg1 = 3;
jit_int arg2 = 5;
jit_int arg3 = 2;
jit_int args[3];
args[0] = &arg1;
args[1] = &arg2;
args[2] = &arg3;

mul_add.apply(args, &result);
@end example

@noindent
@xref{C++ Interface}, for more information on the @code{libjitplus}
library.

@c -----------------------------------------------------------------------

@node Tutorial 5, Dynamic Pascal, Tutorial 4, Tutorials
@section Tutorial 5 - gcd, with tail calls
@cindex gcd with tail calls

Astute readers would have noticed that Tutorial 2 included two instances
of "tail calls".  That is, calls to the same function that are immediately
followed by a @code{return} instruction.

Libjit can optimize tail calls if you provide the @code{JIT_CALL_TAIL}
flag to @code{jit_insn_call}.  Previously, we used the following code
to call @code{gcd} recursively:

@example
temp3 = jit_insn_call
    (function, "gcd", function, 0, temp_args, 2, 0);
jit_insn_return(function, temp3);
@end example

@noindent
In Tutorial 5, this is modified to the following:

@example
jit_insn_call(function, "gcd", function, 0, temp_args, 2, JIT_CALL_TAIL);
@end example

There is no need for the @code{jit_insn_return}, because the call
will never return to that point in the code.  Behind the scenes,
@code{libjit} will convert the call into a jump back to the head
of the function.

Tail calls can only be used in certain circumstances.  The source
and destination of the call must have the same function signatures.
None of the parameters should point to local variables in the current
stack frame.  And tail calls cannot be used from any source function
that uses @code{try} or @code{alloca} statements.

Because it can be difficult for @code{libjit} to determine when these
conditions have been met, it relies upon the caller to supply the
@code{JIT_CALL_TAIL} flag when it is appropriate to use a tail call.

@c -----------------------------------------------------------------------

@node Dynamic Pascal, Initialization, Tutorial 5, Tutorials
@section Dynamic Pascal - A full JIT example
@cindex Dynamic Pascal

This @code{libjit/dpas} directory contains an implementation of
"Dynamic Pascal", or "dpas" as we like to call it.  It is provided
as an example of using @code{libjit} in a real working environment.
We also use it to write test programs that exercise the JIT's capabilities.

Other Pascal implementations compile the source to executable form,
which is then run separately.  Dynamic Pascal loads the source code
at runtime, dynamically JIT'ing the program as it goes.  It thus has
a lot in common with scripting languages like Perl and Python.

If you are writing a bytecode-based virtual machine, you would use
a similar approach to Dynamic Pascal.  The key difference is that
you would build the JIT data structures after loading the bytecode
rather than after parsing the source code.

To run a Dynamic Pascal program, use @code{dpas name.pas}.  You may also
need to pass the @code{-I} option to specify the location of the system
library if you have used an @code{import} clause in your program.  e.g.
@code{dpas -I$HOME/libjit/dpas/library name.pas}.

@noindent
This Pascal grammar is based on the EBNF description at the following URL:

@uref{http://www.cs.qub.ac.uk/~S.Fitzpatrick/Teaching/Pascal/EBNF.html}

@noindent
There are a few differences to "Standard Pascal":

@enumerate
@item
Identifiers are case-insensitive, but case-preserving.

@item
Program headings are normally @code{program Name (Input, Output);}.  This can
be abbreviated to @code{program Name;} as the program modifiers are ignored.

@item
Some GNU Pascal operators like @code{xor}, @code{shl}, @code{@@}, etc
have been added.

@item
The integer type names (@code{Integer}, @code{Cardinal}, @code{LongInt}, etc)
follow those used in GNU Pascal also.  The @code{Integer} type is always
32-bits in size, while @code{LongInt} is always 64-bits in size.

@item
The types @code{SysInt}, @code{SysCard}, @code{SysLong}, @code{SysLongCard},
@code{SysLongestInt}, and @code{SysLongestCard} are guaranteed to be the
same size as the underlying C system's @code{int}, @code{unsigned int},
@code{long}, @code{unsigned long}, @code{long long}, and
@code{unsigned long long} types.

@item



( run in 0.802 second using v1.01-cache-2.11-cpan-796a6f069b2 )