Next: , Previous: GNU lightning macros, Up: Using GNU lightning


2.4 Re-entrant usage of 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 BUFSIZE
     

This is not at all the same as defining TABLESIZE to be `1020'. The #define for TABLESIZE 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 use TABLESIZE 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 of BUFSIZE that is currently in effect: #define BUFSIZE 1020 #define TABLESIZE BUFSIZE #undef BUFSIZE #define BUFSIZE 37

Now TABLESIZE expands (in two stages) to `37'. (The #undef is to prevent any warning about the nontrivial redefinition of BUFSIZE.)

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))

2.4.1 Registers

2.5 Accessing the whole register file

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.