Next: Autoconf support, Previous: GNU lightning macros, Up: Using GNU lightning
By default, gnu lightning is able to compile different functions at the same time as long as it happens in different object files, and on the other hand constrains code generation tasks to reside in a single object file.
The reason for this is not apparent, but is easily explained:
the lightning.h header file defines its state as a
static
variable, so calls to jit_set_ip
and
jit_get_ip
residing in different files access different
instruction pointers. This was not done without reason: it makes
the usage of gnu lightning much simpler, as it limits the initialization
tasks to the bare minimum and removes the need to link the program
with a separate library.
On the other hand, multi-threaded or otherwise concurrent programs
require reentrancy in the code generator, so this approach cannot be
the only one. In fact, it is possible to define your own copy of
gnu lightning's instruction state by defining a variable of type
jit_state
and #define
-ing _jit
to it:
struct jit_state lightning; #define _jit lightning
You are free to define the jit_state
variable as you like:
extern
, static
to a function, auto
, or global.
This feature takes advantage of an aspect of macros (cascaded macros), which is documented thus in CPP's reference manual:
A cascade of macros is when one macro's body contains a reference to another macro. This is very common practice. For example,#define BUFSIZE 1020 #define TABLESIZE BUFSIZEThis is not at all the same as defining
TABLESIZE
to be `1020'. The#define
forTABLESIZE
uses exactly the body you specify—in this case,BUFSIZE
—and does not check to see whether it too is the name of a macro; it's only when you useTABLESIZE
that the result of its expansion is checked for more macro names.This makes a difference if you change the definition of
BUFSIZE
at some point in the source file.TABLESIZE
, defined as shown, will always expand using the definition ofBUFSIZE
that is currently in effect: #define BUFSIZE 1020 #define TABLESIZE BUFSIZE #undef BUFSIZE #define BUFSIZE 37Now
TABLESIZE
expands (in two stages) to `37'. (The#undef
is to prevent any warning about the nontrivial redefinition ofBUFSIZE
.)
In the same way, jit_get_label
will adopt whatever definition of
_jit
is in effect:
#define jit_get_label() (_jit.pc)
Special care must be taken when functions residing in separate files
must access the same state. This could be the case, for example, if a
special library contained function for strength reduction of
multiplications to adds & shifts, or maybe of divisions to
multiplications and shifts. The function would be compiled using a
single definition of _jit
and that definition would be used
whenever the function would be called.
Since gnu lightning uses a feature of the preprocessor to obtain re-entrancy, it makes sense to rely on the preprocessor in this case too.
The idea is to pass the current struct jit_state
to the
function:
static void _opt_muli_i(jit, dest, source, n) register struct jit_state *jit; register int dest, source, n; { #define _jit jit ... #undef _jit }
doing this unbeknownst to the client, using a macro in the header file:
extern void _opt_muli_i(struct jit_state *, int, int, int); #define opt_muli_i(rd, rs, n) _opt_muli_i(&_jit, (rd), (rs), (n))
As mentioned earlier in this chapter, all gnu lightning back-ends are guaranteed to have at least six integer registers and six floating-point registers, but many back-ends will have more.
To access the entire register files, you can use the
JIT_R
, JIT_V
and JIT_FPR
macros. They
accept a parameter that identifies the register number, which
must be strictly less than JIT_R_NUM
, JIT_V_NUM
and JIT_FPR_NUM
respectively; the number need not be
constant. Of course, expressions like JIT_R0
and
JIT_R(0)
denote the same register, and likewise for
integer callee-saved, or floating-point, registers.