printf
Again, here is the code for the example:
#include <stdio.h>
#include <lightning.h>
static jit_state_t *_jit;
typedef void (*pvfi)(int); /* Pointer to Void Function of Int */
int main(int argc, char *argv[])
{
pvfi myFunction; /* ptr to generated code */
jit_node_t *start, *end; /* a couple of labels */
jit_node_t *in; /* to get the argument */
init_jit(argv[0]);
_jit = jit_new_state();
start = jit_note(__FILE__, __LINE__);
jit_prolog();
in = jit_arg();
jit_getarg(JIT_R1, in);
jit_prepare();
jit_pushargi((jit_word_t)"generated %d bytes\n");
jit_ellipsis();
jit_pushargr(JIT_R1);
jit_finishi(printf);
jit_ret();
jit_epilog();
end = jit_note(__FILE__, __LINE__);
myFunction = jit_emit();
/* call the generated code,
passing its size as argument */
myFunction((char*)jit_address(end) - (char*)jit_address(start));
jit_clear_state();
jit_disassemble();
jit_destroy_state();
finish_jit();
return 0;
}
The function shows how many bytes were generated. Most of the code is not very interesting, as it resembles very closely the program presented in A function which increments a number by one.
For this reason, we’re going to concentrate on just a few statements.
start = jit_note(__FILE__, __LINE__);
…
end = jit_note(__FILE__, __LINE__);
These two instruction call the jit_note
macro, which creates
a note in the jit code; arguments to jit_note
usually are a
filename string and line number integer, but using NULL for the
string argument is perfectly valid if only need to create a simple
marker in the code.
jit_ellipsis();
ellipsis
usually is only required if calling varargs functions
with double arguments, but it is a good practice to properly describe
the … in the call sequence.
jit_pushargi((jit_word_t)"generated %d bytes\n");
Note the use of the (jit_word_t)
cast, that is used only
to avoid a compiler warning, due to using a pointer where a
wordsize integer type was expected.
jit_prepare();
…
jit_finishi(printf);
Once the arguments to printf
have been pushed, what means
moving them to stack or register arguments, the printf
function is called and the stack cleaned. Note how GNU lightning
abstracts the differences between different architectures and
ABI’s – the client program does not know how parameter passing
works on the host architecture.
jit_epilog();
Usually it is not required to call epilog
, but because it
is implicitly called when noticing the end of a function, if the
end
variable was set with a note
call after the
ret
, it would not consider the function epilog.
myFunction((char*)jit_address(end) - (char*)jit_address(start));
This calls the generate jit function passing as argument the offset
difference from the start
and end
notes. The address
call must be done after the emit
call or either a fatal error
will happen (if GNU lightning is built with assertions enable) or an
undefined value will be returned.
jit_clear_state();
Note that jit_clear_state
was called after executing jit in
this example. It was done because it must be called after any call
to jit_address
or jit_print
.
jit_disassemble();
disassemble
will dump the generated code to standard output,
unless GNU lightning was built with the disassembler disabled, in which
case no output will be shown.