Sophie

Sophie

distrib > Mandriva > 2007.0 > i586 > media > contrib-release > by-pkgid > 4c9f17ec5da473f7fb52041bb9197c5a > files > 91

kaffe-devel-1.1.8-0.20060723.1mdv2007.0.i586.rpm


High level documentation for the just-in-time compiler, version 3.

Introduction
------------

The jit3 just-in-time compiler is used to translate Java byte codes
into native machine code, resulting in a significant speedup over the
interpreter.  However, unlike the interpreter, the compiler must be
ported by hand to new architectures and operating systems.  This
document attempts to give a high level overview of the layout and
control flow of the jit3 compiler, more detailed information can be
found at:

  http://www.cs.pdx.edu/~sanseri/kaffe/kaffe.html
  http://egp.free.fr/port-kaffe/port-kaffe-0.2.html
  http://www2.biglobe.ne.jp/~inaba/trampolines.html


Configure
---------

If the jit3 engine is available for your architecture it will
automatically be enabled.  This detection is done by testing for the
existence of the "config/${host_cpu}/jit3-${host_cpu}.def" and
"config/${host_cpu}/${host_os}/jit3-md.h" files.  If you would like to
disable the jit3 engine you can revert to the original jit or
interpreter by passing the "--with-engine" argument to configure.

Enabling the jitter will also make other options available, such as,
cross language debugging and profiling.  These options can assist a
great deal when analyzing the generated code.  For more information,
consult the FAQ.profiler, FAQ.xdebugging, and FAQ.xprofiler files
found in this directory.


Usage
-----

Nothing special needs to be done to make use of the jit3 engine,
enabling the system in configure is enough.


Implementation
--------------

The root of the compiler is the "translate" function found in the
kaffe/kaffevm/jit3/machine.c file.  The core of the compiler consists
of three stages:  byte code analysis, generating instructions, and
linking the generated code into the VM.

The first stage, byte code analysis, is performed by the verifyMethod
function in kaffe/kaffevm/code-analyse.c.  This function generates a
"codeinfo" structure that contains information about the stack
requirements of the method, usage information for any locals, and
attributes associated with individual byte codes.

Once the byte code has been analyzed the process of translating
individual instructions can begin.  The translation is done separately
for each basic block and occurs in two passes, first, the byte codes
are mapped onto an intermediate function set by the
kaffe/kaffevm/kaffe.def file (which is included by
kaffe/kaffevm/jit3/funcs.c).  These intermediate functions and macros
then generate a list of "sequence" objects which contain function
pointers to the architecture dependent back end.  Lastly, when the
translator reaches the end of a basic block the set of generated
sequence structures are iterated over causing the back end to emit the
appropriate instruction sequences.

Finally, when all of the basic blocks have been processed, the
resulting native code is copied to its final resting place and linking
is initiated.  Linking the code involves rewriting the generated
instructions using information that is only available at the end of
code generation.  For example, addresses referred to in the code may
have changed when it was copied to its new location or stack frame
offsets and sizes may have changed during the generation of successive
basic blocks.

Psuedo code for the translate() function:

  translate(Method *xmeth, errorInfo *einfo)
  {
    codeInfo = verifyMethod(xmeth); // Analyze the byte code.

    /* Clear the memory used to store in progress native code. */
    nativecode.clear();
    /* Iterate over each byte code. */
    foreach bytecode in xmeth.bcode
    {
      switch( bytecode )
      {
        /* 
	 * Interpret each byte using a series of calls to intermediate
	 * functions and macros that will append to the "sequences"
	 * list.
	 */
        #include "kaffe.def"
      }
      if( codeinfo.startofbasicblock(pc) )
      {
        /*
	 * We're at the end of a basic block, emit the current set of
	 * translated instructions.
	 */
	sequences.walk(nativecode);
	sequences.reset(); // Empty the list
      }
    }
    /* Emit any pending instructions from the last basic block. */
    sequences.walk();
    /* Allocate a final resting place for the code. */
    dest = gc_malloc(nativecode.size);
    /* Copy from our scratch space. */
    memcpy(dest, nativecode);
    /* Link the labels using the new location of the code. */
    labels.link(dest);
  }

As an alternative to translating byte codes, the intermediate layer
can be used directly to generate methods required by the VM.  For
example, the JNI and KNI wrapper functions as generated by the
kaffe/kaffevm/jni.c file use the "pusharg" and similar functions
defined by the intermediate layer to push the arguments on the stack
and call the function.

Extra Detail:

Besides the straight forward translation of instructions, the jitter
can also generate a "constant pool" for each method and a series of
"fake" calls for triggering exceptions.  The constant pool is used to
hold constants referenced by the method, but are to large to inline in
the instruction stream.  For example, a 32 bit number, like
"0xdeadbeef", will not fit into the load immediate instruction
provided by the PowerPC.  Instead, the number is stored in memory
prepended to the method so that it can be loaded based on a PC value
minus some offset.  The "fake" calls are used to move calls to
exception functions out of the main line code while still maintaining
a proper return address.  For example, in the x86 back end, the
conditional branch used to detect a bad array index is unable to
directly call "soft_badarrayindex()".  Therefore, it jumps to the fake
call which pushes the PC of the offending instruction and calls the
exception function.  If the proper PC wasn't pushed, then any
generated stack traces would not show the correct source of the
exception.  Unfortunately, generating fake calls for each test can
result in a lot of redundant code.  Alternatively, architectures that
support branch and link instructions can use them to reach the fake
call where it will branch directly to the soft function.

File Layout
-----------

  kaffe/kaffevm/jit3 - The home directory of the jitter.
  XXX The "organization" in here generally sucks.

  kaffe/kaffevm/jit3/basecode.[ch] - Code generation primitives,
  including:  start/end function, start/end blocks, monitor
  enter/exit, functions for generating sequence structures.

  kaffe/kaffevm/jit3/checks.h - Empty macros for satisfying the checks
  done in the kaffe.def file.

  kaffe/kaffevm/jit3/codeproto.h - Prototypes for the intermediate
  layer functions.

  kaffe/kaffevm/jit3/constpool.[ch] - Functions for manipulating the
  method's constant pool.  Constants, such as addresses, integers, and
  doubles can be added to the pool which are then copied to an area
  preceding the method code.

  kaffe/kaffevm/jit3/funcs.c - Wrapper for
  config/${host_cpu}/jit3-${host_cpu}.def file.  Defines the following
  variables and macros:

    define_insn(n, i) - Define the implementation of an architecture
    specific intermediate instruction.  This just resolves down to a
    function prototype of the form "void i (sequence *s)" and the "n"
    value is just for documentation purposes.

    codeblock - The memory area used to store native code as it is
    generated.  It is resized automatically if the native code grows
    beyond the current size.

    CODEPC - The current index into codeblock.

    OUT, BOUT, WOUT, LOUT, QOUT - Macros that store values given by
    arguments into the current position and automatically increment
    the CODEPC value.  For example, using "LOUT(0x45)" in the
    architecture defs file will put the 32 bit value "0x45" into the
    current position in the code block and increment CODEPC by four.

  kaffe/kaffevm/jit3/icode.c - The implementation of the intermediate
  functions.  These functions generate "sequence" structures that
  refer to the architecture specific functions.

  kaffe/kaffevm/jit3/labels.[ch] - Functions for manipulating label
  objects.  A label is used to rewrite code at the end of translation,
  for example, to set the destination of a forward branch.

  kaffe/kaffevm/jit3/machine.c - Contains the translate() function and
  its supporting functions, the functions for walking the list of
  sequences, functions for creating fake calls, and anything else that
  people were too lazy to separate into another file.

  kaffe/kaffevm/jit3/registers.[ch] - The register allocator.

  kaffe/kaffevm/jit3/seq.[ch] - Functions for creating sequence
  objects.

  kaffe/kaffevm/jit3/slots.[ch] - Functions for manipulating slot
  objects.  A slot is used to track a function locals, arguments, and
  temporaries.

  config/${host_cpu}/jit.h - Primary jitter header file.

    ${host_cpu}_do_fixup_trampoline() - A prototype for a function,
    usually written in asm, that is called by the trampoline to save
    the arguments to an untranslated method and call
    soft_fixup_trampoline with the method pointer and the trampoline
    address to fix.

    methodTrampoline - A structure that can wholly contain the
    trampoline code, method pointer, and the pointer pointing to
    this trampoline.

    FILL_IN_TRAMPOLINE(t, m, w) - A macro used to fill out the
    trampoline object, t, with the method, m, and the pointer to the
    trampoline pointer, w.

    FIXUP_TRAMPOLINE_DECL - A macro that defines the parameters to the
    soft_fixup_trampoline function.

    FIXUP_TRAMPOLINE_INIT - A macro that sets the "meth" and "where"
    locals in soft_fixup_trampoline to the "m" and "w" values given in
    FILL_IN_TRAMPOLINE.

    L${label_type} - A series of macros that define architecture
    dependent label types.
    XXX Needs to be a define and not an enum or anything like that
    since the file is included in places where "labels.h" isn't.

    EXTRA_LABELS(P, V, L) - A macro used to translate architecture
    dependent labels, where, "P" is a pointer to the code to modify,
    "V" is the computed value of the label, and "L" is the label
    object.

    SLOT2LOCALOFFSET(N) - A macro that translates a slot index, N, to
    a stack frame offset.

    SLOT2ARGOFFSET(N) - A macro that translates a method argument
    index, N, to a stack frame offset.

    KAFFEJIT_TO_NATIVE(M) - XXX Unknown.

    FLUSH_DCACHE(code_start, code_end) - A macro that can be used to
    flush the data cache between "code_start" and "code_end" of any
    instructions generated by the jitter.
    XXX Needs to be a define since it gets #ifdef'd.

    PUSHARG_FORWARDS - A macro that, when defined, causes icode
    functions to push arguments starting at index zero.  Otherwise,
    the arguments are pushed starting at the end of the list.

  config/${host_cpu}/${host_os}/jit3-md.h - An operating system
  specific header file.  Usually just includes ${host_cpu}/jit.h.

  config/${host_cpu}/jit3-icode.h - A header file that describes the
  capabilities of the architecture's jit3 back end.  The rangecheck()
  macros are used to decide which immediates can be inlined directly
  in the instruction stream or added to the constant pool.  And, the
  HAVE macros tell the core library which intermediate instructions it
  can handle directly and which ones need to be emulated.

  config/${host_cpu}/jit3-${host_cpu}.def - The jit3 back end,
  typically, it contains a number of functions defined using the
  "define_insn()" macro.  Basically, these functions implement the
  intermediate instructions by emitting instructions specific to the
  native instruction set.