Next: Common features, Up: Standard macros
Implementation of forward references takes place in:
jit_patch_at
macros
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:
_PUC(X)
to cast to a unsigned char *
.
_PUS(X)
to cast to a unsigned short *
.
_PUI(X)
to cast to a unsigned int *
.
_PSL(X)
to cast to a long *
.
_PUL(X)
to cast to a unsigned long *
.
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:
_UC(X)
to cast to a unsigned char
.
_US(X)
to cast to a unsigned short
.
_UI(X)
to cast to a unsigned int
.
_SL(X)
to cast to a long
.
_UL(X)
to cast to a unsigned long
.
_MASK(N)
gives a binary number made of N ones.
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
.