Next: , Up: Standard macros


3.4.1 Implementing forward references

Implementation of forward references takes place in:

Roughly speaking, the branch macros, as seen in Generating code at run-time, return a value that later calls to jit_patch or jit_patch_at use to complete the assembly of the forward reference. This value is usually the contents of the program counter after the branch instruction is compiled (which is accessible in the _jit.pc variable). Let's see an example from the x86 back-end:

     #define jit_bmsr_i(label, s1, s2)                            \
        (TESTLrr((s1), (s2)), JNZm(label,0,0,0), _jit.pc)

The bms (branch if mask set) instruction is assembled as the combination of a TEST instruction (bit-wise and between the two operands) and a JNZ instruction (jump if non-zero). The macro then returns the final value of the program counter.

jit_patch_at is one of the few macros that need to possess a knowledge of the machine's instruction formats. Its purpose is to patch a branch instruction (identified by the value returned at the moment the branch was compiled) to jump to the current position (that is, to the address identified by _jit.pc).

On the x86, the displacement between the jump and the landing point is expressed as a 32-bit signed integer lying in the last four bytes of the jump instruction. The definition of _jit_patch_at is:

     #define jit_patch(jump_pc, pv)    (*_PSL((jump_pc) - 4) = \
     				   (pv) - (jump_pc))

The _PSL macro is nothing more than a cast to long *, and is used here to shorten the definition and avoid cluttering it with excessive parentheses. These type-cast macros are:

On other platforms, notably RISC ones, the displacement is embedded into the instruction itself. In this case, jit_patch_at must first zero out the field, and then or in the correct displacement. The SPARC, for example, encodes the displacement in the bottom 22 bits; in addition the right-most two bits are suppressed, which are always zero because instruction have to be word-aligned.

     #define jit_patch_at(delay_pc, pv)   jit_patch_ (((delay_pc) - 1), (pv))
     
     /* branch instructions return the address of the delay
      * instruction---this is just a helper macro that makes the code more
      * readable.
      */
     #define jit_patch_(jump_pc, pv)   (*jump_pc =		    \
     	 (*jump_pc & ~_MASK(22)) |			    \
              ((_UL(pv) - _UL(jump_pc)) >> 2) & _MASK(22))

This introduces more predefined shortcut macros:

Dual to branches and jit_patch_at are jit_movi_p and jit_patch_movi, since they can also be used to implement forward references. jit_movi_p should be carefully implemented to use an encoding that is as long as possible, and it should return an address which is then passed to jit_patch_movi. The implementation of jit_patch_movi is similar to jit_patch_at.