nanojit OpCodes

April 16, 2017 · View on GitHub

In Nanojit, LIR is the source language for compilation to machine code. LIR stands for low-level intermediate representation.  It is a typed assembly language. The LIR instruction set is best learnt by reading nanojit/LIRopcode.tbl.  Code for manipulating LIR is in nanojit/LIR.h.

LIR OpCodes

Opcodes use type-indicators suffixes that are loosely based on C type names:

    • 'c': "char", ie. 8-bit integer
    • 's': "short", ie. 16-bit integer
    • 'i': "int", ie. 32-bit integer
    • 'q': "quad", ie. 64-bit integer
    • 'u': "unsigned", is used as a prefix on integer type-indicators when necessary
    • 'f': "float", ie. 32-bit floating point value
  • -'f4': "float4", ie. 128-bit SIMD value containing 4 single-precision floating point values
    • 'd': "double", ie. 64-bit floating point value
    • 'p': "pointer", ie. an int on 32-bit machines, a quad on 64-bit machines

'p' opcodes are all aliases of int and quad opcodes, they're given in LIR.h and chosen according to the platform pointer size.

Certain opcodes aren't supported on all platforms

Miscellaneous operations

OpcodeTodoReturn TypeFeaturedDescription
startOp0Vstart of a fragment
regfenceOp0VA register fence causes no code to be generated, but it affects register allocation so that no registers are live when it is reached.
unreachableOp0Vindicate that this location cannot be reached (no live regs here)
skipSkVlinks code chunks
paramiPI32-bitload an int parameter (register or stack location)
paramqPQ64-bitload a quad parameter (register or stack location)
allocpIorFPallocate stack space (result is an address)
retiOp1Vreturn an int
retqOp1V64-bitreturn a quad
retdOp1Vreturn a double
retfOp1Vreturn a float
retf4Op1Vreturn a float4
liveiOp1Vextend live range of an int
liveqOp1V64-bitextend live range of a quad
livedOp1Vextend live range of a double
livefOp1Vextend live range of a float
livef4Op1Vextend live range of a float4
fileOp1V[VTune] source filename for debug symbols
lineOp1V[VTune] source line number for debug symbols
pcOp1V[Shark] record the machine address of this instruction
commentOp1Va comment shown, on its own line, in LIR dumps
safeSafeVdeoptimization safepoint
endsafeSafeVdeoptimization safepoint

Loads and stores

OpcodeTodoReturn TypeFeaturedDescription
ldc2iLdIload char and sign-extend to an int
lds2iLdIload short and sign-extend to an int
lduc2uiLdIload unsigned char and zero-extend to an unsigned int
ldus2uiLdIload unsigned short and zero-extend to an unsigned int
ldiLdIload int
ldqLdQ64-bitload quad
lddLdDload double
ldfLdFload float
ldf2dLdDload float and extend to a double
ldf4LdF4load float4 (SIMD, 4 floats)
sti2cStVstore int truncated to char
sti2sStVstore int truncated to short
stiStVstore int
stqStV64-bitstore quad
stdStVstore double
std2fStV
stfStV
stf4StV

Calls

OpcodeTodoReturn TypeFeaturedDescription
callvCVcall subroutine that returns void
calliCIcall subroutine that returns an int
callqCQ64-bitcall subroutine that returns a quad
calldCDcall subroutine that returns a double
callfCFcall subroutine that returns a float
callf4CF4call subroutine that returns a float4

Branches and labels

'jt' and 'jf' must be adjacent so that (op ^ 1) gives the opposite one. Static assertions in LIR.h check this requirement.

OpcodeTodoReturn TypeFeaturedDescription
jOp2Vjump always
jtOp2Vjump if true
jfOp2Vjump if false
jtblJtblVjump to address in table
labelOp0Va jump target (no machine code is emitted for this)

Guards

'xt' and 'xf' must be adjacent so that (op ^ 1) gives the opposite one. Static assertions in LIR.h check this requirement.

OP_UN (align_guards)

OpcodeTodoReturn TypeFeaturedDescription
xOp2Vexit always
xtOp2Vexit if true
xfOp2Vexit if false
xbarrierOp2VA LIR_xbarrier cause no code to be generated, but it acts like a never-taken guard in that it inhibits certain optimisations, such as dead stack store elimination.

Immediates

OpcodeTodoReturn TypeFeaturedDescription
immiIorFIint immediate
immqQorDQ64-bitquad immediate
immdQorDDdouble immediate
immfIorFFfloat immediate
immf4F4F4float4 immediate

Comparisons

All comparisons return an int: 0 on failure and 1 on success. Within each type group, order must be preserved so that, except for eq*, (op ^ 1) gives the opposite one (eg. lt ^ 1 == gt). eq* must have odd numbers for this to work. They must also remain contiguous so that opcode range checking works correctly. Static assertions in LIR.h check these requirements.

OP_UN (align_eqi)

OpcodeTodoReturn TypeFeaturedDescription
eqiOp2Iint equality
ltiOp2Isigned int less-than
gtiOp2Isigned int greater-than
leiOp2Isigned int less-than-or-equal
geiOp2Isigned int greater-than-or-equal
ltuiOp2Iunsigned int less-than
gtuiOp2Iunsigned int greater-than
leuiOp2Iunsigned int less-than-or-equal
geuiOp2Iunsigned int greater-than-or-equal

OP_UN_64(align_eqq)

OpcodeTodoReturn TypeFeaturedDescription
eqqOp2I64-bitquad equality
ltqOp2I64-bitsigned quad less-than
gtqOp2I64-bitsigned quad greater-than
leqOp2I64-bitsigned quad less-than-or-equal
geqOp2I64-bitsigned quad greater-than-or-equal
ltuqOp2I64-bitunsigned quad less-than
gtuqOp2I64-bitunsigned quad greater-than
leuqOp2I64-bitunsigned quad less-than-or-equal
geuqOp2I64-bitunsigned quad greater-than-or-equal

OP_UN_64(align_eqd)

OpcodeTodoReturn TypeFeaturedDescription
eqdOp2Idouble equality
ltdOp2Idouble less-than
gtdOp2Idouble greater-than
ledOp2Idouble less-than-or-equal
gedOp2Idouble greater-than-or-equal

OP_UN (align_eqf)

OpcodeTodoReturn TypeFeaturedDescription
eqfOp2Ifloat equality
ltfOp2Ifloat less-than
gtfOp2Ifloat greater-than
lefOp2Ifloat less-than-or-equal
gefOp2Ifloat greater-than-or-equal
eqf4Op2Ifloat4 equality

Note: we don't do lt/gt/le/ge comparisons on float4 values

Arithmetic

OpcodeTodoReturn TypeFeaturedDescription
negiOp1Inegate int
addiOp2Iadd int
subiOp2Isubtract int
muliOp2Imultiply int
diviOp2I32-bit X86divide int
modiOp1I32-bit X86modulo int. LIR_modi is a hack. It's only used on i386/X64. The operand is the result of a LIR_divi because on i386/X64 div and mod results are computed by the same instruction.
notiOp1Ibitwise-NOT int
andiOp2Ibitwise-AND int
oriOp2Ibitwise-OR int
xoriOp2Ibitwise-XOR int
lshiOp2Ileft shift int. For all three integer shift operations, only the bottom five bits of the second operand are used, and they are treated as unsigned. This matches x86 semantics.
rshiOp2Iright shift int (>>)
rshuiOp2Iright shift unsigned int (>>>)
addqOp2Q64-bitadd quad
subqOp2Q64-bitsubtract quad
mulqOp2Q64-bit X86multiply quad
divqOp2Q64-bit X86divide quad
modqOp1Q64-bit X86modulo quad. LIR_modq is a hack. It's only used on i386/X64. The operand is the result of a LIR_divq because on i386/X64 div and mod results are computed by the same instruction.
andqOp2Q64-bitbitwise-AND quad
orqOp2Q64-bitbitwise-OR quad
xorqOp2Q64-bitbitwise-XOR quad
lshqOp2Q64-bitleft shift quad; 2nd operand is an int. For all three quad shift operations, only the bottom six bits of the second operand are used, and they are treated as unsigned. This matches x86-64 semantics.
rshqOp2Q64-bitright shift quad; 2nd operand is an int
rshuqOp2Q64-bitright shift unsigned quad; 2nd operand is an int
negdOp1Dnegate double
absdOp1Dabsolute value of double
sqrtdOp1Dsqrt double
adddOp2Dadd double
subdOp2Dsubtract double
muldOp2Dmultiply double
divdOp2Ddivide double
moddOp2Dmodulo double. LIR_modd is just a place-holder opcode, ie. the back-ends cannot generate code for it. It's used in TraceMonkey briefly but is always demoted to a LIR_modl or converted to a function call before Nanojit has to do anything serious with it.
negfOp1Fnegate float
absfOp1Fabsolute value of float
sqrtfOp1Fsqrt float
addfOp2Fadd float
subfOp2Fsubtract float
mulfOp2Fmultiply float
divfOp2Fdivide float
negf4Op1F4negate float4
absf4Op1F4absolute value of float4
sqrtf4Op1F4sqrt float4
addf4Op2F4add float4
subf4Op2F4subtract float4
mulf4Op2F4multiply float4
divf4Op2F4divide float4
recipfOp1Ffloat reciprocal
rsqrtfOp1Ffloat reciprocal square root
minfOp2Ffloat min
maxfOp2Ffloat max
cmpgtf4Op2F4float4.isGreater
cmpltf4Op2F4float4.isLess
cmpgef4Op2F4float4.isGreaterOrEqual
cmplef4Op2F4float4.isLessOrEqual
cmpeqf4Op2F4float4.isEqual
cmpnef4Op2F4float4.isNotEqual
recipf4Op1F4float4 reciprocal
rsqrtf4Op1F4float4 reciprocal square root
minf4Op2F4float4 min
maxf4Op2F4float4 max
dotf4Op2F4-component dot product
dotf3Op2F3-component dot product
dotf2Op2F2-component dot product
cmoviOp3Iconditional move int
cmovqOp3Q64-bitconditional move quad
cmovdOp3Dconditional move double
cmovfOp3Fconditional move float
cmovf4Op3F4conditional move float4

Conversions

rounding behavior of LIR_d2f is platform-specific

PlatformAsm codeBehavior
x86 w/ x87FST32uses current FP control word (default is rounding)
x86 w/ SSEcvtsd2ssaccording to MXCSR register (default is round to nearest)
x64 (SSE)cvtsd2ssaccording to MXCSR register (default is round to nearest)
othersnot implemented yet

The rounding behavior of LIR_d2i is platform specific.

PlatformAsm codeBehavior
x86 w/ x87fistuses current FP control word (default is rounding)
x86 w/ SSEcvttsd2siperforms round to zero (truncate)
x64 (SSE)cvttsd2siperforms round to zero (truncate)
PowerPCunsupported
ARMftosidround to nearest
MIPStrunc.w.dperforms round to zero (truncate)
SH4frtcperforms round to zero (truncate)
SPARCfdtoiperforms round to zero (truncate)
  • round to zero examples: 1.9 -> 1, 1.1 -> 1, -1.1 -> -1, -1.9 -> -1
  • round to nearest examples: 1.9 -> 2, 1.1 -> 1, -1.1 -> -1, -1.9 -> -2
OpcodeTodoReturn TypeFeaturedDescription
i2qOp1Q64-bitsign-extend int to quad
ui2uqOp1Q64-bitzero-extend unsigned int to unsigned quad
q2iOp1I64-bittruncate quad to int (removes the high 32 bits)
q2dOp1D64-bitconvert quad to double
i2dOp1Dconvert int to double
i2fOp1Fconvert int to float
ui2dOp1Dconvert unsigned int to double
ui2fOp1Fconvert unsigned int to float
f2dOp1Dconvert float to double
d2fOp1Fconvert double to float (no exceptions raised)
d2iOp1Iconvert double to int (no exceptions raised)
d2qOp1Q64-bit X86convert double to quad (no exceptions raised?)
f2iOp1Iconvert float to int (no exceptions raised)
f2f4Op1F4convert float to float4 (no exceptions raised) - essentially copies the float across all elements
ffff2f4Op4F4convert float to float4 (no exceptions raised) - essentially copies the float across all elements
f4xOp1Fextract first float from a float4
f4yOp1Fextract second float from a float4
f4zOp1Fextract third float from a float4
f4wOp1Fextract fourth float from a float4
swzf4Op1bF4swizzle float4 according to 8-bit selector
dasqOp1Q64-bitinterpret the bits of a double as a quad
qasdOp1D64-bitinterpret the bits of a quad as a double

Overflow arithmetic

These all exit if overflow occurred. The result is valid on either path.

OpcodeTodoReturn TypeFeaturedDescription
addxoviOp3Iadd int and exit on overflow
subxoviOp3Isubtract int and exit on overflow
mulxoviOp3Imultiply int and exit on overflow

These all branch if overflow occurred. The result is valid on either path.

OpcodeTodoReturn TypeFeaturedDescription
addjoviOp3Iadd int and branch on overflow
subjoviOp3Isubtract int and branch on overflow
muljoviOp3Imultiply int and branch on overflow
addjovqOp3Q64-bitadd quad and branch on overflow
subjovqOp3Q64-bitsubtract quad and branch on overflow

SoftFloat

OpcodeTodoReturn TypeFeaturedDescription
dlo2iOp1ISFget the low 32 bits of a double as an int
dhi2iOp1ISFget the high 32 bits of a double as an int
ii2dOp2DSFjoin two ints (1st arg is low bits, 2nd is high)
hcalliOp1ISFLIR_hcalli is a hack that's only used on 32-bit platforms that use SoftFloat. Its operand is always a LIR_calli, but one that specifies a function that returns a double. It indicates that the double result is returned via two 32-bit integer registers. The result is always used as the second operand of a LIR_ii2d.

Safepoint Polling

OpcodeTodoReturn TypeFeaturedDescription
memfenceOp0V
brsavpcOp2Vbranch and save pc
restorepcOp0V
pushstateOp0V
popstateOp0V