The libjit library assists with the process of building Just-In-Time compilers for languages, virtual machines, and emulators.

Copyright © 2004 Southern Storm Software, Pty Ltd

Introduction and rationale for libjit
*************************************

Just-In-Time compilers are becoming increasingly popular for executing dynamic languages like Perl and Python and for semi-dynamic languages like Java and C#.  Studies have shown that JIT techniques can get close to, and sometimes exceed, the performance of statically-compiled native code.

However, there is a problem with current JIT approaches.  In almost every case, the JIT is specific to the object model, runtime support library, garbage collector, or bytecode peculiarities of a particular system. This inevitably leads to duplication of effort, where all of the good JIT work that has gone into one virtual machine cannot be reused in another.

JIT's are not only useful for implementing languages.  They can also be used in other programming fields.  Graphical applications can achieve greater performance if they can compile a special-purpose rendering routine on the fly, customized to the rendering task at hand, rather than using static routines.  Needless to say, such applications have no need for object models, garbage collectors, or huge runtime class libraries.

Most of the work on a JIT is concerned with arithmetic, numeric type conversion, memory loads/stores, looping, performing data flow analysis, assigning registers, and generating the executable machine code. Only a very small proportion of the work is concerned with language specifics.

The goal of the libjit project is to provide an extensive set of routines that takes care of the bulk of the JIT process, without tying the programmer down with language specifics.  Where we provide support for common object models, we do so strictly in add-on libraries, not as part of the core code.

Unlike other systems such as the JVM, .NET, and Parrot, libjit is not a virtual machine in its own right.  It is the foundation upon which a number of different virtual machines, dynamic scripting languages, or customized rendering routines can be built.

The LLVM project (http://www.llvm.org/) has some similar characteristics to libjit in that its intermediate format is generic across front-end languages.  It is written in C++ and provides a large set of compiler development and optimization components; much larger than libjit itself provides.  According to its author, Chris Lattner, a subset of its capabilities can be used to build JIT's.

Libjit should free developers to think about the design of their front ends, and not get bogged down in the details of code execution. Meanwhile, experts in the design and implementation of JIT's can concentrate on solving code execution problems, instead of front end support issues.

This document describes how to use the library in application programs. We start with a list of features and some simple tutorials.  Finally, we provide a complete reference guide for all of the API functions in libjit, broken down by function category.

Obtaining libjit
================

Currently it is recommended to get libjit source code from its Savannah git repository (http://savannah.gnu.org/git/?group=libjit):

git clone git://git.savannah.gnu.org/libjit.git

The latest released version of libjit is severely out of date and its use is discuraged. Still it can be downloaded from here:

     http://ftp.gnu.org/old-gnu/dotgnu/libjit/

Further reading
===============

While it isn't strictly necessary to know about compiler internals to use libjit, you can make more effective use of the library if you do.  We recommend the "Dragon Book" as an excellent resource on compiler internals, particularly the sections on code generation and optimization:

     Alfred V. Aho, Ravi Sethi, and Jeffrey D. Ullman, "Compilers: Principles, Techniques, and Tools", Addison-Wesley, 1986.

IBM, Intel, and others have done a lot of research into JIT implementation techniques over the years.  If you are interested in working on the internals of libjit, then you may want to make yourself familiar with the relevant literature (this is by no means a complete list):

     IBM's Jikes RVM (Research Virtual Machine),

     http://www-124.ibm.com/developerworks/oss/jikesrvm/.

     Intel's ORP (Open Runtime Platform),

     http://orp.sourceforge.net/.

Features of libjit
******************

  * The primary interface is in C, for maximal reusability.  Class interfaces are available for programmers who prefer C++.

  * Designed for portability to all major 32-bit and 64-bit platforms.

  * Simple three-address API for library users, but opaque enough that other representations can be used inside the library in future without affecting existing users.

  * Up-front or on-demand compilation of any function.

  * In-built support to re-compile functions with greater optimization, automatically redirecting previous callers to the new version.

  * Fallback interpreter for running code on platforms that don't have a native code generator yet.  This reduces the need for programmers to write their own interpreters for such platforms.

  * Arithmetic, bitwise, conversion, and comparison operators for 8-bit, 16-bit, 32-bit, or 64-bit integer types; and 32-bit, 64-bit, or longer floating point types.  Includes overflow detecting arithmetic for integer types.

  * Large set of mathematical and trigonometric operations (sqrt, sin, cos, min, abs, etc) for inlining floating-point library functions.

  * Simplified type layout and exception handling mechanisms, upon which a variety of different object models can be built.

  * Support for nested functions, able to access their parent's local variables (for implementing Pascal-style languages).

Tutorials in using libjit
*************************

In this chapter, we describe how to use libjit with a number of short tutorial exercises.  Full source for these tutorials can be found in the tutorial directory of the libjit source tree.

For simplicity, we will ignore errors such as out of memory conditions, but a real program would be expected to handle such errors.

Tutorial 1 - mul_add
====================

In the first tutorial, we will build and compile the following function (the source code can be found in tutorial/t1.c):

     int mul_add(int x, int y, int z)
     {
         return x * y + z;
     }

To use the JIT, we first include the <jit/jit.h> file:

     #include <jit/jit.h>

All of the header files are placed into the jit sub-directory, to separate them out from regular system headers.  When libjit is installed, you will typically find these headers in /usr/local/include/jit or /usr/include/jit, depending upon how your system is configured.  You should also link with the -ljit option.

Every program that uses libjit needs to call jit_context_create:

     jit_context_t context;
     ...
     context = jit_context_create();

Almost everything that is done with libjit is done relative to a context.  In particular, a context holds all of the functions that you have built and compiled.

You can have multiple contexts at any one time, but normally you will only need one.  Multiple contexts may be useful if you wish to run multiple virtual machines side by side in the same process, without them interfering with each other.

Whenever we are constructing a function, we need to lock down the context to prevent multiple threads from using the builder at a time:

     jit_context_build_start(context);

The next step is to construct the function object that will represent our mul_add function:

     jit_function_t function;
     ...
     function = jit_function_create(context, signature);

The signature is a jit_type_t object that describes the function's parameters and return value.  This tells libjit how to generate the proper calling conventions for the function:

     jit_type_t params[3];
     jit_type_t signature;
     ...
     params[0] = jit_type_int;
     params[1] = jit_type_int;
     params[2] = jit_type_int;
     signature = jit_type_create_signature
         (jit_abi_cdecl, jit_type_int, params, 3, 1);

This declares a function that takes three parameters of type int and returns a result of type int.  We've requested that the function use the cdecl application binary interface (ABI), which indicates normal C calling conventions.  See Types, for more information on signature types.

Now that we have a function object, we need to construct the instructions in its body.  First, we obtain references to each of the function's parameter values:

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

Values are one of the two cornerstones of the libjit process. Values represent parameters, local variables, and intermediate temporary results.  Once we have the parameters, we compute the result of x * y + z as follows:

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

This demonstrates the other cornerstone of the libjit process: instructions.  Each of these instructions takes two values as arguments and returns a new temporary value with the result.

Students of compiler design will notice that the above statements look very suspiciously like the "three address statements" that are described in compiler textbooks.  And that is indeed what they are internally within libjit.

If you don't know what three address statements are, then don't worry. The library hides most of the details from you.  All you need to do is break your code up into simple operation steps (addition, multiplication, negation, copy, etc).  Then perform the steps one at a time, using the temporary values in subsequent steps.  See Instructions, for a complete list of all instructions that are supported by libjit.

Now that we have computed the desired result, we return it to the caller using jit_insn_return:

     jit_insn_return(function, temp2);

We have completed the process of building the function body.  Now we compile it into its executable form:

     jit_function_compile(function);
     jit_context_build_end(context);

As a side-effect, this will discard all of the memory associated with the values and instructions that we constructed while building the function.  They are no longer required, because we now have the executable form that we require.

We also unlock the context, because it is now safe for other threads to access the function building process.

Up until this point, we haven't executed the mul_add function. All we have done is build and compile it, ready for execution.  To execute it, we call jit_function_apply:

     jit_int arg1, arg2, arg3;
     void *args[3];
     jit_int result;
     ...
     arg1 = 3;
     arg2 = 5;
     arg3 = 2;
     args[0] = &arg1;
     args[1] = &arg2;
     args[2] = &arg3;
     jit_function_apply(function, args, &result);
     printf("mul_add(3, 5, 2) = %d\n", (int)result);

We pass an array of pointers to jit_function_apply, each one pointing to the corresponding argument value.  This gives us a very general purpose mechanism for calling any function that may be built and compiled using libjit.  If all went well, the program should print the following:

     mul_add(3, 5, 2) = 17

You will notice that we used jit_int as the type of the arguments, not int.  The jit_int type is guaranteed to be 32 bits in size on all platforms, whereas int varies in size from platform to platform.  Since we wanted our function to work the same everywhere, we used a type with a predictable size.

If you really wanted the system int type, you would use jit_type_sys_int instead of jit_type_int when you created the function's signature.  The jit_type_sys_int type is guaranteed to match the local system's int precision.

Finally, we clean up the context and all of the memory that was used:

     jit_context_destroy(context);

Tutorial 2 - gcd
================

In this second tutorial, we implement the subtracting Euclidean Greatest Common Divisor (GCD) algorithm over positive integers. This tutorial demonstrates how to handle conditional branching and function calls.  In C, the code for the gcd function is as follows:

     unsigned int gcd(unsigned int x, unsigned int y)
     {
         if(x == y)
         {
             return x;
         }
         else if(x < y)
         {
             return gcd(x, y - x);
         }
         else
         {
             return gcd(x - y, y);
         }
     }

The source code for this tutorial can be found in tutorial/t2.c. Many of the details are similar to the previous tutorial.  We omit those details here and concentrate on how to build the function body. See Tutorial 1, for more information.

We start by checking the condition x == y:

     jit_value_t x, y, temp1;
     ...
     x = jit_value_get_param(function, 0);
     y = jit_value_get_param(function, 1);
     temp1 = jit_insn_eq(function, x, y);

This is very similar to our previous tutorial, except that we are using the eq operator this time.  If the condition is not true, we want to skip the return statement.  We achieve this with the jit_insn_branch_if_not instruction:

     jit_label_t label1 = jit_label_undefined;
     ...
     jit_insn_branch_if_not(function, temp1, &label1);

The label must be initialized to jit_label_undefined.  It will be updated by jit_insn_branch_if_not to refer to a future position in the code that we haven't seen yet.

If the condition is true, then execution falls through to the next instruction where we return x to the caller:

     jit_insn_return(function, x);

If the condition was not true, then we branched to label1 above. We fix the location of the label using jit_insn_label:

     jit_insn_label(function, &label1);

We use similar code to check the condition x < y, and branch to label2 if it is not true:

     jit_value_t temp2;
     jit_label_t label2 = jit_label_undefined;
     ...
     temp2 = jit_insn_lt(function, x, y);
     jit_insn_branch_if_not(function, temp2, &label2);

At this point, we need to call the gcd function with the arguments x and y - x.  The code for this is fairly straight-forward.  The jit_insn_call instruction calls the function listed in its third argument.  In this case, we are calling ourselves recursively:

     jit_value_t temp_args[2];
     jit_value_t temp3;
     ...
     temp_args[0] = x;
     temp_args[1] = jit_insn_sub(function, y, x);
     temp3 = jit_insn_call
         (function, "gcd", function, 0, temp_args, 2, 0);
     jit_insn_return(function, temp3);

The string "gcd" in the second argument is for diagnostic purposes only.  It can be helpful when debugging, but the libjit library otherwise makes no use of it.  You can set it to NULL if you wish.

In general, libjit does not maintain mappings from names to jit_function_t objects.  It is assumed that the front end will take care of that, using whatever naming scheme is appropriate to its needs.

The final part of the gcd function is similar to the previous one:

     jit_value_t temp4;
     ...
     jit_insn_label(function, &label2);
     temp_args[0] = jit_insn_sub(function, x, y);
     temp_args[1] = y;
     temp4 = jit_insn_call
         (function, "gcd", function, 0, temp_args, 2, 0);
     jit_insn_return(function, temp4);

We can now compile the function and execute it in the usual manner.

Tutorial 3 - compiling on-demand
================================

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 tutorial/t3.c.

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

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

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

     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;
     }

When the on-demand compiler returns, libjit will call jit_function_compile and then jump to the newly compiled code. Upon the second and subsequent calls to the function, libjit will bypass the on-demand compiler and call the compiled code directly. Note that in case of on-demand compilation libjit automatically locks and unlocks the corresponding context with jit_context_build_start and 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:

     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);

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

     jit_context_build_start(context);
     jit_function_get_on_demand_compiler(function)(function);
     jit_function_compile(function);
     jit_context_build_end(context);

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 mul_add.  In a real program, you would probably call 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 jit_function_clear_recompilable so that libjit can manage the function more efficiently from then on.

The exact conditions under which a function should be recompiled are not specified by 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.

Tutorial 4 - mul_add, C++ version
=================================

While 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 libjitplus library.

To use the libjitplus library, we first include the <jit/jit-plus.h> file:

     #include <jit/jit-plus.h>

This file incorporates all of the definitions from <jit/jit.h>, so you have full access to the underlying C API if you need it.

This time, instead of building the mul_add function with jit_function_create and friends, we define a class to represent it:

     class mul_add_function : public jit_function
     {
     public:
         mul_add_function(jit_context& context) : jit_function(context)
         {
             create();
             set_recompilable();
         }

         virtual void build();

     protected:
         virtual jit_type_t create_signature();
     };

Where we used jit_function_t and jit_context_t before, we now use the C++ jit_function and jit_context classes.

In our constructor, we attach ourselves to the context and then call the create() method.  This is in turn will call our overridden virtual method create_signature() to obtain the signature:

     jit_type_t mul_add_function::create_signature()
     {
         // Return type, followed by three parameters,
         // terminated with "end_params".
         return signature_helper
             (jit_type_int, jit_type_int, jit_type_int,
              jit_type_int, end_params);
     }

The signature_helper() method is provided for your convenience, to help with building function signatures.  You can create your own signature manually using jit_type_create_signature if you wish.

The final thing we do in the constructor is call set_recompilable() to mark the mul_add function as recompilable, just as we did in Tutorial 3.

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 build() method to build the function's body on-demand:

     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);
     }

This is similar to the first version that we wrote in Tutorial 1. Instructions are created with insn_* methods that correspond to their 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 jit_value objects. This can simplify the function build process considerably when we have lots of expressions to compile.  We could have used insn_mul and insn_add instead in this example and the result would have been the same.

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

     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);

See C++ Interface, for more information on the libjitplus library.

Tutorial 5 - 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 return instruction.

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

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

In Tutorial 5, this is modified to the following:

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

There is no need for the jit_insn_return, because the call will never return to that point in the code.  Behind the scenes, 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 try or alloca statements.

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

Dynamic Pascal - A full JIT example
===================================

This 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 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 dpas name.pas.  You may also need to pass the -I option to specify the location of the system library if you have used an import clause in your program.  e.g. dpas -I$HOME/libjit/dpas/library name.pas.

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

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

There are a few differences to "Standard Pascal":

 1. Identifiers are case-insensitive, but case-preserving.

 2. Program headings are normally program Name (Input, Output);.  This can be abbreviated to program Name; as the program modifiers are ignored.

 3. Some GNU Pascal operators like xor, shl, @, etc have been added.

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

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

 6. The type Address is logically equivalent to C's void *. Any pointer or array can be implicitly cast to Address.  An explicit cast is required to cast back to a typed pointer (you cannot cast back to an array).

 7. The String type is declared as ^Char.  Single-dimensional arrays of Char can be implicitly cast to any String destination.  Strings are not bounds-checked, so be careful.  Arrays are bounds-checked.

 8. Pointers can be used as arrays.  e.g. p[n] will access the n'th item of an unbounded array located at p.  Use with care.

 9. We don't support file of types.  Data can be written to stdout using Write and WriteLn, but that is the extent of the I/O facilities.

 10. The declaration import Name1, Name2, ...; can be used at the head of a program to declare additional files to include.  e.g. import stdio will import the contents of stdio.pas.  We don't support units.

 11. The idiom ; .. can be used at the end of a formal parameter list to declare that the procedure or function takes a variable number of arguments. The builtin function va_arg(Type) is used to extract the arguments.

 12. The directive import("Library") can be used to declare that a function or procedure was imported from an external C library.  For example, the following imports the C puts and printf functions:

          function puts (str : String) : SysInt; import ("libc")
          function printf (format : String; ..) : SysInt; import ("libc")

     Functions that are imported in this manner have case-sensitive names. i.e. using Printf above will fail.

 13. The throw keyword can be used to throw an exception.  The argument must be a pointer.  The try, catch, and finally keywords are used to manage such exceptions further up the stack.  e.g.

          try
              ...
          catch Name : Type
              ...
          finally
              ...
          end

     The catch block will be invoked with the exception pointer that was supplied to throw, after casting it to Type (which must be a pointer type).  Specifying throw on its own without an argument will rethrow the current exception pointer, and can only be used inside a catch block.

     Dynamic Pascal does not actually check the type of the thrown pointer. If you have multiple kinds of exceptions, then you must store some kind of type indicator in the block that is thrown and then inspect ^Name to see what the indicator says.

 14. The exit keyword can be used to break out of a loop.

 15. Function calls can be used as procedure calls.  The return value is ignored.

 16. Hexadecimal constants can be expressed as XXH.  The first digit must be between 0 and 9, but the remaining digits can be any hex digit.

 17. Ternary conditionals can be expressed as (if e1 then e2 else e3). The brackets are required.  This is equivalent to C's e1 ? e2 : e3.

 18. Assigning to a function result will immediately return.  i.e. it is similar to return value; in C.  It isn't necessary to arrange for execution to flow through to the end of the function as in regular Pascal.

 19. The term sizeof(Type) can be used to get the size of a type.

 20. Procedure and function headings can appear in a record type to declare a field with a pointer to procedure/function type.

Initializing the JIT
********************

     [file libjitext-init.texi not included]
     [file libjitext-context.texi not included]

Building and compiling functions with the JIT
*********************************************

     [file libjitext-function.texi not included]
     [file libjitext-compile.texi not included]

Manipulating system types
*************************

     [file libjitext-type.texi not included]

Working with temporary values in the JIT
****************************************

     [file libjitext-value.texi not included]

Working with instructions in the JIT
************************************

     [file libjitext-insn.texi not included]

Working with basic blocks in the JIT
************************************

     [file libjitext-block.texi not included]

Intrinsic functions available to libjit users
*********************************************

     [file libjitext-intrinsic.texi not included]

Handling exceptions
*******************

     [file libjitext-except.texi not included]

Hooking a breakpoint debugger into libjit
*****************************************

     [file libjitext-debugger.texi not included]

Manipulating ELF binaries
*************************

     [file libjitext-elf-read.texi not included]

Miscellaneous utility routines
******************************

The libjit library provides a number of utility routines that it itself uses internally, but which may also be useful to front ends.

     [file libjitext-util.texi not included]
     [file libjitext-meta.texi not included]
     [file libjitext-apply.texi not included]
     [file libjitext-walk.texi not included]
     [file libjitext-dynlib.texi not included]
     [file libjitext-cpp-mangle.texi not included]

Diagnostic routines
*******************

     [file libjitext-dump.texi not included]

Using libjit from C++
*********************

This chapter describes the classes and methods that are available in the libjitplus library.  To use this library, you must include the header <jit/jit-plus.h> and link with the -ljitplus and -ljit options.

Contexts in C++
***************

     [file libjitext-plus-context.texi not included]

Values in C++
*************

     [file libjitext-plus-value.texi not included]

Functions in C++
****************

     [file libjitext-plus-function.texi not included]

Porting libjit to new architectures
***********************************

This chapter describes what needs to be done to port libjit to a new CPU architecture.  It is assumed that the reader is familiar with compiler implementation techniques and the particulars of their target CPU's instruction set.

We will use ARCH to represent the name of the architecture in the sections that follow.  It is usually the name of the CPU in lower case (e.g. x86, arm, ppc, etc).  By convention, all back end functions should be prefixed with _jit, because they are not part of the public API.

Porting the function apply facility
===================================

The first step in porting libjit to a new architecture is to port the jit_apply facility.  This provides support for calling arbitrary C functions from your application or from JIT'ed code. If you are familiar with libffi or ffcall, then jit_apply provides a similar facility.

Even if you don't intend to write a native code generator, you will probably still need to port jit_apply to each new architecture.

The libjit library makes use of gcc's __builtin_apply facility to do most of the hard work of function application. This gcc facility takes three arguments: a pointer to the function to invoke, a structure containing register arguments, and a size value that indicates the number of bytes to push onto the stack for the call.

Unfortunately, the register argument structure is very system dependent. There is no standard format for it, but it usually looks something like this:

stack_args
     Pointer to an array of argument values to push onto the stack.

struct_ptr
     Pointer to the buffer to receive a struct return value. The struct_ptr field is only present if the architecture passes struct pointers in a special register.

word_reg[0..N]
     Values for the word registers.  Platforms that pass values in registers will populate these fields.  Not present if the architecture does not use word registers for function calls.

float_reg[0..N]
     Values for the floating-point registers.  Not present if the architecture does not use floating-point registers for function calls.

It is possible to automatically detect the particulars of this structure by making test function calls and inspecting where the arguments end up in the structure.  The gen-apply program in libjit/tools takes care of this.  It outputs the jit-apply-rules.h file, which tells jit_apply how to operate.

The gen-apply program will normally "just work", but it is possible that some architectures will be stranger than usual.  You will need to modify gen-apply to detect this additional strangeness, and perhaps also modify libjit/jit/jit-apply.c.

If you aren't using gcc to compile libjit, then things may not be quite this easy.  You may have to write some inline assembly code to emulate __builtin_apply.  See the file jit-apply-x86.h for an example of how to do this. Be sure to add an #include line to jit-apply-func.h once you do this.

The other half of jit_apply is closure and redirector support. Closures are used to wrap up interpreted functions so that they can be called as regular C functions.  Redirectors are used to help compile a JIT'ed function on-demand, and then redirect control to it.

Unfortunately, you will have to write some assembly code to support closures and redirectors.  The builtin gcc facilities are not complete enough to handle the task.  See jit-apply-x86.c and jit-apply-arm.c for some examples from existing architectures. You may be able to get some ideas from the libffi and ffcall libraries as to what you need to do on your architecture.

Creating the instruction generation macros
==========================================

You will need a large number of macros and support functions to generate the raw instructions for your chosen CPU.  These macros are fairly generic and are not necessarily specific to libjit. There may already be a suitable set of macros for your CPU in some other Free Software project.

Typically, the macros are placed into a file called jit-gen-ARCH.h in the libjit/jit directory.  If some of the macros are complicated, you can place helper functions into the file jit-gen-ARCH.c. Remember to add both jit-gen-ARCH.h and jit-gen-ARCH.c to Makefile.am in libjit/jit.

Existing examples that you can look at for ideas are jit-gen-x86.h and jit-gen-arm.h.  The macros in these existing files assume that instructions can be output to a buffer in a linear fashion, and that each instruction is relatively independent of the next.

This independence principle may not be true of all CPU's.  For example, the ia64 packs up to three instructions into a single "bundle" for parallel execution.  We recommend that the macros should appear to use linear output, but call helper functions to pack bundles after the fact. This will make it easier to write the architecture definition rules. A similar approach could be used for performing instruction scheduling on platforms that require it.

Writing the architecture definition rules
=========================================

     [file libjitext-rules-interp.texi not included]

Allocating registers in the back end
====================================

     [file libjitext-reg-alloc.texi not included]