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_state_t *_jit;
typedef int (*pifi)(int); /* Pointer to Int Function of Int */
int main(int argc, char *argv[])
{
jit_node_t *in;
pifi incr;
init_jit(argv[0]);
_jit = jit_new_state();
jit_prolog(); /* prolog */
in = jit_arg(); /* in = arg */
jit_getarg(JIT_R0, in); /* getarg R0 */
jit_addi(JIT_R0, JIT_R0, 1); /* addi R0, R0, 1 */
jit_retr(JIT_R0); /* retr R0 */
incr = jit_emit();
jit_clear_state();
/* call the generated code, passing 5 as an argument */
printf("%d + 1 = %d\n", 5, incr(5));
jit_destroy_state();
finish_jit();
return 0;
}
Let's examine the code line by line (well, almost...):
jit_state_t. It is a structure
that stores jit code generation information. The name _jit is
special, because since multiple jit generators can run at the same
time, you must either #define _jit my_jit_state or name it
_jit.
int and returns another.
arg
and be used as argument to getarg.
int and returns an int.
jit_state_t
object. This function does global state initialization, and may need
to detect CPU or Operating System features. It receives a string
argument that is later used to read symbols from a shared object using
GNU binutils if disassembly was enabled at configure time. If no
disassembly will be performed a NULL pointer can be used as argument.
R0.
R0 register.
jit_print or
jit_address, as this call destroy the gnu lightning
intermediate representation.
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_64
architecture (on the right is the code that an assembly-language
programmer would write):
save %sp, -112, %sp
mov %i0, %g2 ret
inc %g2 inc %i0
mov %g2, %i0
restore
retl
nop
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 %g2 on the SPARC).
sub $0x30,%rsp
mov %rbp,(%rsp)
mov %rsp,%rbp
sub $0x18,%rsp
mov %rdi,%rax mov %rdi, %rax
add $0x1,%rax inc %rax
mov %rbp,%rsp
mov (%rsp),%rbp
add $0x30,%rsp
retq retq
In this case, the main overhead is due to the function's prolog and epilog, and stack alignment after reserving stack space for word to/from float conversions or moving data from/to x87 to/from SSE. Note that besides allocating space to save callee saved registers, no registers are saved/restored because gnu lightning notices those registers are not modified. There is currently no logic to detect if it needs to allocate stack space for type conversions neither proper leaf function detection, but these are subject to change (FIXME).