[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2.3.1 A function which increments a number by one

Let's see how to create and use the sample incr function created in GNU lightning's instruction set:

 
#include <stdio.h>
#include "lightning.h"

static jit_insn codeBuffer[1024];

typedef int (*pifi)(int);    /* Pointer to Int Function of Int */

int main()
{
  pifi  incr = (pifi) (jit_set_ip(codeBuffer).iptr);
  int   in;

  jit_leaf(1);                     /*      leaf  1             */
  in = jit_arg_i();                /* in = arg_i               */
  jit_getarg_i(JIT_R0, in);        /*      getarg_i R0         */
  jit_addi_i(JIT_RET, JIT_R0, 1);  /*      addi_i   RET, R0, 1 */
  jit_ret();                       /*      ret                 */

  jit_flush_code(codeBuffer, jit_get_ip().ptr);

  /* call the generated code, passing 5 as an argument */
  printf("%d + 1 = %d\n", 5, incr(5));
  return 0;
}

Let's examine the code line by line (well, almost...):

#include "lightning.h"
You already know about this. It defines all of GNU lightning's macros.

static jit_insn codeBuffer[1024];
You might wonder about what is jit_insn. It is just a type that is defined by GNU lightning. Its exact definition depends on the architecture; in general, defining an array of 1024 jit_insns allows one to write 100 to 400 GNU lightning instructions (depending on the architecture and exact instructions).

typedef int (*pifi)(int);
Just a handy typedef for a pointer to a function that takes an int and returns another.

pifi incr = (pifi) (jit_set_ip(codeBuffer).iptr);
This is the first GNU lightning macro we encounter that does not map to an instruction. It is jit_set_ip, which takes a pointer to an area of memory where compiled code will be put and returns the same value, cast to a union type whose members are pointers to functions returning different C types. This union is called jit_code and is defined as follows:

 
    typedef union jit_code {
      char               *ptr;
      void               (*vptr)();
      char               (*cptr)();
      unsigned char      (*ucptr)();
      short              (*sptr)();
      unsigned short     (*usptr)();
      int                (*iptr)();
      unsigned int       (*uiptr)();
      long               (*lptr)();
      unsigned long      (*ulptr)();
      void *             (*pptr)();
      float              (*fptr)();
      double             (*dptr)();
    } jit_code;

Any of the members could have been used, since the result is soon casted to type pifi but, for the sake of clarity, the program uses iptr, a pointer to a function with no prototype and returning an int.

Analogous to jit_set_ip is jit_get_ip, which does not modify the instruction pointer--it is nothing more than a cast of the current IP to jit_code.

int in;
A footnote in GNU lightning's instruction set, under the description of arg, says that macros implementing arg return a value--we'll be using this variable to store the result of arg.

jit_leaf(1);
Ok, so we start generating code for our beloved function... it will accept one argument and won't call any other function.

in = jit_arg_i();
jit_getarg_i(JIT_R0, in);
We retrieve the first (and only) argument, an integer, and store it into the general-purpose register R0.

jit_addi_i(JIT_RET, JIT_R0, 1);
We add one to the content of the register and store the result in the return value.

jit_ret();
This instruction generates a standard function epilog that returns the contents of the RET register.

jit_flush_code(codeBuffer, jit_get_ip().ptr);
This instruction is very important. It flushes the generated code area out of the processor's instruction cache, avoiding the processor executes bogus data that it happens to find there. The jit_flush_code function accepts the first and the last address to flush; we use jit_get_ip to find out the latter.

printf("%d + 1 = %d", 5, incr(5));
Calling our function is this simple--it is not distinguishable from a normal C function call, the only difference being that incr is a variable.

GNU lightning abstracts two phases of dynamic code generation: selecting instructions that map the standard representation, and emitting binary code for these instructions. The client program has the responsibility of describing the code to be generated using the standard GNU lightning instruction set.

Let's examine the code generated for incr on the SPARC and x86 architectures (on the right is the code that an assembly-language programmer would write):

SPARC
 
    save %sp, -96, %sp
    mov  %i0, %l0                   retl
    add  %l0, 1,  %i0               add %o0, 1, %o0
    ret
    restore
In this case, GNU lightning introduces overhead to create a register window (not knowing that the procedure is a leaf procedure) and to move the argument to the general purpose register R0 (which maps to %l0 on the SPARC). The former overhead could be avoided by teaching GNU lightning about leaf procedures (see section 4. The future of GNU lightning); the latter could instead be avoided by rewriting the getarg instruction as jit_getarg_i(JIT_RET, in), which was not done in this example.

x86
 
    pushl %ebp
    movl  %esp, %ebp
    pushl %ebx
    pushl %esi
    pushl %edi
    movl  8(%ebp), %eax        movl 4(%esp), %eax
    addl  $1, %eax             incl %eax
    popl  %edi
    popl  %esi
    popl  %ebx
    popl  %ebp
    ret                        ret
In this case, the main overhead is due to the function's prolog and epilog, which is nine instructions long on the x86; a hand-written routine would not save unused callee-preserved registers on the stack. It is to be said, however, that this is not a problem in more complicated uses, because more complex procedure would probably use the V0 through V2 registers (%ebx, %esi, %edi); in this case, a hand-written routine would have included the prolog too. Also, a ten byte prolog would probably be a small overhead in a more complex function.

In such a simple case, the macros that make up the back-end compile reasonably efficient code, with the notable exception of prolog/epilog code.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

This document was generated by Alistair Turnbull on April, 12 2005 using texi2html