ack/mach/powerpc/ncg/table
George Koehler f64b7d8ea0 Rewrite how PowerPC ncg does conditional branches and tests.
The rewritten code rules bring 3 new features:

  1.  The new rules compare a small constant with a register by
      reversing the comparison and using `cmpwi` or `cmplwi`.  The old
      rules put the constant in a register.

  2.  The new rules emit shorter code to yield the test results,
      without referencing the tables in mach/powerpc/ncg/tge.s.

  3.  The new rules use the extended `beq` and relatives, not the
      basic `bc`, in the assembly output.

I delete the old tristate tokens and the old moves, because they
confused me.  Some of the old moves weren't really moves.  For
example, `move R3, C0` and then `move C0, R0` did not move r3 to r0.

I rename C0 to CR0.
2017-01-25 19:08:55 -05:00

2555 lines
59 KiB
Plaintext

EM_WSIZE = 4
EM_PSIZE = 4
EM_BSIZE = 8 /* two words saved in call frame */
INT8 = 1 /* Size of values */
INT16 = 2
INT32 = 4
INT64 = 8
FP_OFFSET = 0 /* Offset of saved FP relative to our FP */
PC_OFFSET = 4 /* Offset of saved PC relative to our FP */
#define COMMENT(n) /* comment {LABEL, n} */
#define nicesize(x) ((x)==INT8 || (x)==INT16 || (x)==INT32 || (x)==INT64)
#define smalls(n) sfit(n, 16)
#define smallu(n) ufit(n, 16)
#define lo(n) ((n) & 0xFFFF)
#define hi(n) (((n)>>16) & 0xFFFF)
/* Use these for instructions that treat the low half as signed --- his()
* includes a modifier to produce the correct value when the low half gets
* sign extended. Er, do make sure you load the low half second. */
#define los(n) (lo(n) | (((0-(lo(n)>>15)) & ~0xFFFF)))
#define his(n) ((hi(n) + (lo(n)>>15)) & 0xFFFF)
PROPERTIES
GPR /* any GPR */
REG /* any allocatable GPR */
REG_PAIR(8) /* speed hack for sti 8 */
FPR(8) /* any FPR */
FREG(8) /* any allocatable FPR */
FSREG /* any allocatable single-precision FPR */
SPR /* any SPR */
CR /* any CR */
GPR0 GPRSP GPRFP GPR3 GPR4 GPR5 GPR6 GPR7
GPR8 GPR9 GPR10 GPR11 GPR12 GPR13 GPR14 GPR15
GPR16 GPR17 GPR18 GPR19 GPR20 GPR21 GPR22 GPR23
GPR24 GPR25 GPR26 GPR27 GPR28 GPR29 GPR30 GPR31
FPR0(8) FPR1(8) FPR2(8) FPR3(8) FPR4(8) FPR5(8) FPR6(8) FPR7(8)
FPR8(8) FPR9(8) FPR10(8) FPR11(8) FPR12(8) FPR13(8) FPR14(8) FPR15(8)
FPR16(8) FPR17(8) FPR18(8) FPR19(8) FPR20(8) FPR21(8) FPR22(8) FPR23(8)
FPR24(8) FPR25(8) FPR26(8) FPR27(8) FPR28(8) FPR29(8) FPR30(8) FPR31(8)
REGISTERS
/* Reverse order to encourage ncg to allocate them from r31 down */
R31("r31") : GPR, REG, GPR31 regvar.
R30("r30") : GPR, REG, GPR30 regvar.
R29("r29") : GPR, REG, GPR29 regvar.
R28("r28") : GPR, REG, GPR28 regvar.
R27("r27") : GPR, REG, GPR27 regvar.
R26("r26") : GPR, REG, GPR26 regvar.
R25("r25") : GPR, REG, GPR25 regvar.
R24("r24") : GPR, REG, GPR24 regvar.
R23("r23") : GPR, REG, GPR23 regvar.
R22("r22") : GPR, REG, GPR22 regvar.
R21("r21") : GPR, REG, GPR21 regvar.
R20("r20") : GPR, REG, GPR20 regvar.
R19("r19") : GPR, REG, GPR19 regvar.
R18("r18") : GPR, REG, GPR18 regvar.
R17("r17") : GPR, REG, GPR17 regvar.
R16("r16") : GPR, REG, GPR16 regvar.
R15("r15") : GPR, REG, GPR15 regvar.
R14("r14") : GPR, REG, GPR14 regvar.
R13("r13") : GPR, REG, GPR13 regvar.
R12("r12") : GPR, REG, GPR12.
R11("r11") : GPR, GPR11.
R10("r10") : GPR, REG, GPR10.
R9("r9") : GPR, REG, GPR9.
R8("r8") : GPR, REG, GPR8.
R7("r7") : GPR, REG, GPR7.
R6("r6") : GPR, REG, GPR6.
R5("r5") : GPR, REG, GPR5.
R4("r4") : GPR, REG, GPR4.
R3("r3") : GPR, REG, GPR3.
FP("fp") : GPR, GPRFP.
SP("sp") : GPR, GPRSP.
R0("r0") : GPR, GPR0.
/* speed hack for sti 8 */
PAIR_R9_R10=R9+R10 : REG_PAIR.
PAIR_R7_R8=R7+R8 : REG_PAIR.
PAIR_R5_R6=R5+R6 : REG_PAIR.
PAIR_R3_R4=R3+R4 : REG_PAIR.
/*
* F14 to F31 are reserved for regvar, if we ever implement
* it. Don't add them to FREG; the register allocator would
* be too slow.
*/
F31("f31") : FPR, FPR31.
F30("f30") : FPR, FPR30.
F29("f29") : FPR, FPR29.
F28("f28") : FPR, FPR28.
F27("f27") : FPR, FPR27.
F26("f26") : FPR, FPR26.
F25("f25") : FPR, FPR25.
F24("f24") : FPR, FPR24.
F23("f23") : FPR, FPR23.
F22("f22") : FPR, FPR22.
F21("f21") : FPR, FPR21.
F20("f20") : FPR, FPR20.
F19("f19") : FPR, FPR19.
F18("f18") : FPR, FPR18.
F17("f17") : FPR, FPR17.
F16("f16") : FPR, FPR16.
F15("f15") : FPR, FPR15.
F14("f14") : FPR, FPR14.
F13("f13") : FPR, FREG, FPR13.
F12("f12") : FPR, FREG, FPR12.
F11("f11") : FPR, FREG, FPR11.
F10("f10") : FPR, FREG, FPR10.
F9("f9") : FPR, FREG, FPR9.
F8("f8") : FPR, FREG, FPR8.
F7("f7") : FPR, FREG, FPR7.
F6("f6") : FPR, FREG, FPR6.
F5("f5") : FPR, FREG, FPR5.
F4("f4") : FPR, FREG, FPR4.
F3("f3") : FPR, FREG, FPR3.
F2("f2") : FPR, FREG, FPR2.
F1("f1") : FPR, FREG, FPR1.
F0("f0") : FPR, FPR0.
FS13("f13")=F13 : FSREG.
FS12("f12")=F12 : FSREG.
FS11("f11")=F11 : FSREG.
FS10("f10")=F10 : FSREG.
FS9("f9")=F9 : FSREG.
FS8("f8")=F8 : FSREG.
FS7("f7")=F7 : FSREG.
FS6("f6")=F6 : FSREG.
FS5("f5")=F5 : FSREG.
FS4("f4")=F4 : FSREG.
FS3("f3")=F3 : FSREG.
FS2("f2")=F2 : FSREG.
FS1("f1")=F1 : FSREG.
LR("lr") : SPR.
CTR("ctr") : SPR.
CR0("cr0") : CR.
#define RSCRATCH R11
#define FSCRATCH F0
TOKENS
/* Used only in instruction descriptions (to generate the correct syntax). */
GPRINDIRECT = { GPR reg; INT off; } 4 off "(" reg ")".
GPRINDIRECT_OFFSET_LO = { GPR reg; ADDR adr; } 4 "lo16[" adr "](" reg ")".
CONST = { INT val; } 4 val.
/* Primitives */
LABEL = { ADDR adr; } 4 adr.
LABEL_OFFSET_HI = { ADDR adr; } 4 "hi16[" adr "]".
LABEL_OFFSET_HA = { ADDR adr; } 4 "ha16[" adr "]".
LABEL_OFFSET_LO = { ADDR adr; } 4 "lo16[" adr "]".
LOCAL = { INT off; } 4.
/* Allows us to use regvar() to refer to registers */
GPRE = { GPR reg; } 4 reg.
/* Constants on the stack */
CONST_N8000 = { INT val; } 4.
CONST_N7FFF_N0001 = { INT val; } 4.
CONST_0000_7FFF = { INT val; } 4.
CONST_8000 = { INT val; } 4.
CONST_8001_FFFF = { INT val; } 4.
CONST_HZ = { INT val; } 4.
CONST_HL = { INT val; } 4.
/* Expression partial results */
SUM_RIS = { GPR reg; INT offhi; } 4.
SUM_RC = { GPR reg; INT off; } 4.
SUM_RR = { GPR reg1; GPR reg2; } 4.
SEX_B = { GPR reg; } 4.
SEX_H = { GPR reg; } 4.
IND_RC_B = { GPR reg; INT off; } 4.
IND_RR_B = { GPR reg1; GPR reg2; } 4.
IND_RC_H = { GPR reg; INT off; } 4.
IND_RR_H = { GPR reg1; GPR reg2; } 4.
IND_RC_H_S = { GPR reg; INT off; } 4.
IND_RR_H_S = { GPR reg1; GPR reg2; } 4.
IND_RC_W = { GPR reg; INT off; } 4.
IND_RR_W = { GPR reg1; GPR reg2; } 4.
IND_RC_D = { GPR reg; INT off; } 8.
IND_RR_D = { GPR reg1; GPR reg2; } 8.
NOT_R = { GPR reg; } 4.
AND_RR = { GPR reg1; GPR reg2; } 4.
OR_RR = { GPR reg1; GPR reg2; } 4.
OR_RIS = { GPR reg; INT valhi; } 4.
OR_RC = { GPR reg; INT val; } 4.
XOR_RR = { GPR reg1; GPR reg2; } 4.
XOR_RIS = { GPR reg; INT valhi; } 4.
XOR_RC = { GPR reg; INT val; } 4.
COND_RC = { GPR reg; INT val; } 4.
COND_RR = { GPR reg1; GPR reg2; } 4.
CONDL_RC = { GPR reg; INT val; } 4.
CONDL_RR = { GPR reg1; GPR reg2; } 4.
COND_FS = { FSREG reg1; FSREG reg2; } 4.
COND_FD = { FREG reg1; FREG reg2; } 4.
XEQ = { GPR reg; } 4.
XNE = { GPR reg; } 4.
XGT = { GPR reg; } 4.
XGE = { GPR reg; } 4.
XLT = { GPR reg; } 4.
XLE = { GPR reg; } 4.
SETS
/* signed 16-bit integer */
CONST2 = CONST_N8000 + CONST_N7FFF_N0001 + CONST_0000_7FFF.
/* integer that, when negated, fits signed 16-bit */
CONST2_WHEN_NEG = CONST_N7FFF_N0001 + CONST_0000_7FFF + CONST_8000.
/* unsigned 16-bit integer */
UCONST2 = CONST_0000_7FFF + CONST_8000 + CONST_8001_FFFF.
/* any constant on stack */
CONST_ALL = CONST_N8000 + CONST_N7FFF_N0001 + CONST_0000_7FFF +
CONST_8000 + CONST_8001_FFFF + CONST_HZ + CONST_HL.
SUM_ALL = SUM_RC + SUM_RR.
SEX_ALL = SEX_B + SEX_H.
LOGICAL_ALL = NOT_R + AND_RR + OR_RR + OR_RC + XOR_RR +
XOR_RC.
/* indirect 4-byte value */
IND_ALL_W = IND_RC_W + IND_RR_W.
/* indirect 8-byte value */
IND_ALL_D = IND_RC_D + IND_RR_D.
/* any indirect value that fits in a GPR */
IND_ALL_BHW = IND_RC_B + IND_RR_B + IND_RC_H + IND_RR_H +
IND_RC_H_S + IND_RR_H_S + IND_ALL_W.
/* anything killed by sti (store indirect) */
MEMORY = IND_ALL_BHW + IND_ALL_D.
OP_ALL_W = SUM_ALL + SEX_ALL + LOGICAL_ALL + IND_ALL_W.
INSTRUCTIONS
/* We give time as cycles of total latency from Freescale
* Semiconductor, MPC7450 RISC Microprocessor Family Reference
* Manual, Rev. 5, section 6.6.
*
* We have only 4-byte alignment for doubles; 8-byte alignment is
* optimal. We guess the misalignment penalty by adding 1 cycle to
* the cost of loading or storing a double:
* lfd lfdu lfdx: 4 -> 5
* stfd stfdu stfdx: 3 -> 4
*/
cost(4, 1) /* space, time */
add GPR:wo, GPR:ro, GPR:ro.
addX "add." GPR:wo, GPR:ro, GPR:ro.
addi GPR:wo, GPR:ro, CONST:ro.
addis GPR:wo, GPR:ro, CONST+LABEL_OFFSET_HI+LABEL_OFFSET_HA:ro.
and GPR:wo, GPR:ro, GPR:ro.
andc GPR:wo, GPR:ro, GPR:ro.
andiX "andi." GPR:wo:cc, GPR:ro, CONST:ro.
andisX "andis." GPR:wo:cc, GPR:ro, CONST:ro.
b LABEL:ro.
bc CONST:ro, CONST:ro, LABEL:ro.
beq LABEL:ro.
bne LABEL:ro.
bgt LABEL:ro.
bge LABEL:ro.
blt LABEL:ro.
ble LABEL:ro.
bxx LABEL:ro. /* dummy */
bcctr CONST:ro, CONST:ro, CONST:ro.
bctr.
bcctrl CONST:ro, CONST:ro, CONST:ro.
bctrl.
bclr CONST:ro, CONST:ro, CONST:ro.
bl LABEL:ro.
cmp CR:ro, CONST:ro, GPR:ro, GPR:ro kills :cc.
cmpw GPR:ro, GPR:ro kills :cc.
cmpi CR:ro, CONST:ro, GPR:ro, CONST:ro kills :cc.
cmpwi GPR:ro, CONST:ro kills :cc.
cmpl CR:ro, CONST:ro, GPR:ro, GPR:ro kills :cc.
cmplw GPR:ro, GPR:ro kills :cc.
cmpli CR:ro, CONST:ro, GPR:ro, CONST:ro kills :cc.
cmplwi GPR:ro, CONST:ro kills :cc.
divw GPR:wo, GPR:ro, GPR:ro cost(4, 23).
divwu GPR:wo, GPR:ro, GPR:ro cost(4, 23).
eqv GPR:wo, GPR:ro, GPR:ro.
extsb GPR:wo, GPR:ro.
extsh GPR:wo, GPR:ro.
fadd FREG:wo, FREG:ro, FREG:ro cost(4, 5).
fadds FSREG:wo, FSREG:ro, FSREG:ro cost(4, 5).
fcmpo CR:wo, FREG:ro, FREG:ro cost(4, 5).
fcmpo CR:wo, FSREG:ro, FSREG:ro cost(4, 5).
fdiv FREG:wo, FREG:ro, FREG:ro cost(4, 35).
fdivs FSREG:wo, FSREG:ro, FSREG:ro cost(4, 21).
fmr FPR:wo, FPR:ro cost(4, 5).
fmr FSREG:wo, FSREG:ro cost(4, 5).
fmul FREG:wo, FREG:ro, FREG:ro cost(4, 5).
fmuls FSREG:wo, FSREG:ro, FSREG:ro cost(4, 5).
fneg FREG:wo, FREG:ro cost(4, 5).
fneg FSREG:wo, FSREG:ro cost(4, 5).
frsp FSREG:wo, FREG:ro cost(4, 5).
fsub FREG:wo, FREG:ro, FREG:ro cost(4, 5).
fsubs FSREG:wo, FSREG:ro, FSREG:ro cost(4, 5).
lbz GPR:wo, GPRINDIRECT:ro cost(4, 3).
lbzx GPR:wo, GPR:ro, GPR:ro cost(4, 3).
lfd FPR:wo, GPRINDIRECT:ro cost(4, 5).
lfdu FPR:wo, GPRINDIRECT:ro cost(4, 5).
lfdx FPR:wo, GPR:ro, GPR:ro cost(4, 5).
lfs FSREG:wo, GPRINDIRECT:ro cost(4, 4).
lfsu FSREG:wo, GPRINDIRECT:rw cost(4, 4).
lfsx FSREG:wo, GPR:ro, GPR:ro cost(4, 4).
lha GPR:wo, GPRINDIRECT:ro cost(4, 3).
lhax GPR:wo, GPR:ro, GPR:ro cost(4, 3).
lhz GPR:wo, GPRINDIRECT:ro cost(4, 3).
lhzx GPR:wo, GPR:ro, GPR:ro cost(4, 3).
li32 GPR:wo, CONST:ro cost(8, 2).
lwzu GPR:wo, GPRINDIRECT:ro cost(4, 3).
lwzx GPR:wo, GPR:ro, GPR:ro cost(4, 3).
lwz GPR:wo, GPRINDIRECT+GPRINDIRECT_OFFSET_LO:ro cost(4, 3).
nand GPR:wo, GPR:ro, GPR:ro.
neg GPR:wo, GPR:ro.
nor GPR:wo, GPR:ro, GPR:ro.
mfcr GPR:wo cost(4,2).
mullw GPR:wo, GPR:ro, GPR:ro cost(4, 4).
mfspr GPR:wo, SPR:ro cost(4, 3).
mtspr SPR:wo, GPR:ro cost(4, 2).
or GPR:wo, GPR:ro, GPR:ro.
orc GPR:wo, GPR:ro, GPR:ro.
ori GPR:wo, GPR:ro, CONST+LABEL_OFFSET_LO:ro.
oris GPR:wo, GPR:ro, CONST:ro.
orX "or." GPR:wo:cc, GPR:ro, GPR:ro.
rlwinm GPR:wo, GPR:ro, CONST:ro, CONST:ro, CONST:ro.
extlwi GPR:wo, GPR:ro, CONST:ro, CONST:ro.
extrwi GPR:wo, GPR:ro, CONST:ro, CONST:ro.
slw GPR:wo, GPR:ro, GPR:ro.
subf GPR:wo, GPR:ro, GPR:ro.
sraw GPR:wo, GPR:ro, GPR:ro cost(4, 2).
srawi GPR:wo, GPR:ro, CONST:ro cost(4, 2).
srw GPR:wo, GPR:ro, GPR:ro.
stb GPR:ro, GPRINDIRECT:rw cost(4, 3).
stbx GPR:ro, GPR:ro, GPR:ro cost(4, 3).
stfd FPR:ro, GPRINDIRECT:rw cost(4, 4).
stfdu FPR:ro, GPRINDIRECT:rw cost(4, 4).
stfdx FPR:ro, GPR:ro, GPR:ro cost(4, 4).
stfs FSREG:ro, GPRINDIRECT:rw cost(4, 3).
stfsu FSREG:ro, GPRINDIRECT:rw cost(4, 3).
stfsx FSREG:ro, GPR:ro, GPR:ro cost(4, 3).
sth GPR:ro, GPRINDIRECT:rw cost(4, 3).
sthx GPR:ro, GPR:ro, GPR:ro cost(4, 3).
stw GPR:ro, GPRINDIRECT:rw cost(4, 3).
stwx GPR:ro, GPR:ro, GPR:ro cost(4, 3).
stwu GPR+GPRE:ro, GPRINDIRECT:rw cost(4, 3).
xor GPR:wo, GPR:ro, GPR:ro.
xori GPR:wo, GPR:ro, CONST:ro.
xoris GPR:wo, GPR:ro, CONST:ro.
comment "!" LABEL:ro cost(0, 0).
MOVES
from GPR to GPR
gen
COMMENT("move GPR->GPR")
or %2, %1, %1
/* GPRE exists solely to allow us to use regvar() (which can only be used in
an expression) as a register constant. */
from GPR to GPRE
gen
COMMENT("move GPR->GPRE")
or %2.reg, %1, %1
/* Constants */
from CONST_ALL + CONST smalls(%val) to GPR
gen
COMMENT("move CONST_ALL->GPR smalls")
addi %2, R0, {CONST, %1.val}
from CONST_ALL + CONST to GPR
gen
COMMENT("move CONST_ALL->GPR")
addis %2, R0, {CONST, hi(%1.val)}
ori %2, %2, {CONST, lo(%1.val)}
/* Can't use addi %2, %2, {CONST, los(%1.val)}
* because %2 might be R0. */
from LABEL to GPR
gen
COMMENT("move LABEL->GPR")
addis %2, R0, {LABEL_OFFSET_HI, %1.adr}
ori %2, %2, {LABEL_OFFSET_LO, %1.adr}
/* Sign extension */
from SEX_B to GPR
gen
COMMENT("move SEX_B->GPR")
extsb %2, %1.reg
from SEX_H to GPR
gen
COMMENT("move SEX_H->GPR")
extsh %2, %1.reg
/* Register + something */
from SUM_RIS to GPR
gen
COMMENT("move SUM_RIS->GPR")
addis %2, %1.reg, {CONST, %1.offhi}
from SUM_RC to GPR
gen
COMMENT("move SUM_RC->GPR")
addi %2, %1.reg, {CONST, %1.off}
from SUM_RR to GPR
gen
COMMENT("move SUM_RR->GPR")
add %2, %1.reg1, %1.reg2
/* Read byte */
from IND_RC_B to GPR
gen
COMMENT("move IND_RC_B->GPR")
lbz %2, {GPRINDIRECT, %1.reg, %1.off}
from IND_RR_B to GPR
gen
COMMENT("move IND_RR_B->GPR")
lbzx %2, %1.reg1, %1.reg2
/* Write byte */
from GPR to IND_RC_B
gen
COMMENT("move GPR->IND_RC_B")
stb %1, {GPRINDIRECT, %2.reg, %2.off}
from GPR to IND_RR_B
gen
COMMENT("move GPR->IND_RR_B")
stbx %1, %2.reg1, %2.reg2
/* Read halfword (short) */
from IND_RC_H to GPR
gen
COMMENT("move IND_RC_H->GPR")
lhz %2, {GPRINDIRECT, %1.reg, %1.off}
from IND_RR_H to GPR
gen
COMMENT("move IND_RR_H->GPR")
lhzx %2, %1.reg1, %1.reg2
from IND_RC_H_S to GPR
gen
COMMENT("move IND_RC_H_S->GPR")
lha %2, {GPRINDIRECT, %1.reg, %1.off}
from IND_RR_H_S to GPR
gen
COMMENT("move IND_RR_H_S->GPR")
lhax %2, %1.reg1, %1.reg2
/* Write halfword */
from GPR to IND_RC_H
gen
COMMENT("move GPR->IND_RC_H")
sth %1, {GPRINDIRECT, %2.reg, %2.off}
from GPR to IND_RR_H
gen
COMMENT("move GPR->IND_RR_H")
sthx %1, %2.reg1, %2.reg2
/* Read word */
from IND_RC_W to GPR
gen
COMMENT("move IND_RC_W->GPR")
lwz %2, {GPRINDIRECT, %1.reg, %1.off}
from IND_RR_W to GPR
gen
COMMENT("move IND_RR_W->GPR")
lwzx %2, %1.reg1, %1.reg2
from IND_RC_W to FSREG
gen
COMMENT("move IND_RC_W->FSREG")
lfs %2, {GPRINDIRECT, %1.reg, %1.off}
from IND_RR_W to FSREG
gen
COMMENT("move IND_RR_W->FSREG")
lfsx %2, %1.reg1, %1.reg2
/* Write word */
from GPR to IND_RC_W
gen
COMMENT("move GPR->IND_RC_W")
stw %1, {GPRINDIRECT, %2.reg, %2.off}
from GPR to IND_RR_W
gen
COMMENT("move GPR->IND_RR_W")
stwx %1, %2.reg1, %2.reg2
from FSREG to IND_RC_W
gen
COMMENT("move FSREG->IND_RC_W")
stfs %1, {GPRINDIRECT, %2.reg, %2.off}
from FSREG to IND_RR_W
gen
COMMENT("move FSREG->IND_RR_W")
stfsx %1, %2.reg1, %2.reg2
/* Read double */
from IND_RC_D to FPR
gen
COMMENT("move IND_RC_D->FPR")
lfd %2, {GPRINDIRECT, %1.reg, %1.off}
from IND_RR_D to FPR
gen
COMMENT("move IND_RR_D->FPR")
lfdx %2, %1.reg1, %1.reg2
/* Write double */
from FPR to IND_RC_D
gen
COMMENT("move FPR->IND_RC_D")
stfd %1, {GPRINDIRECT, %2.reg, %2.off}
from FPR to IND_RR_D
gen
COMMENT("move FPR->IND_RR_W")
stfdx %1, %2.reg1, %2.reg2
/* Logicals */
from NOT_R to GPR
gen
COMMENT("move NOT_R->GPR")
nor %2, %1.reg, %1.reg
from AND_RR to GPR
gen
COMMENT("move AND_RR->GPR")
and %2, %1.reg1, %1.reg2
from OR_RR to GPR
gen
COMMENT("move OR_RR->GPR")
or %2, %1.reg1, %1.reg2
from OR_RIS to GPR
gen
COMMENT("move OR_RIS->GPR")
oris %2, %1.reg, {CONST, %1.valhi}
from OR_RC to GPR
gen
COMMENT("move OR_RC->GPR")
ori %2, %1.reg, {CONST, %1.val}
from XOR_RR to GPR
gen
COMMENT("move XOR_RR->GPR")
xor %2, %1.reg1, %1.reg2
from XOR_RIS to GPR
gen
COMMENT("move XOR_RIS->GPR")
xoris %2, %1.reg, {CONST, %1.valhi}
from XOR_RC to GPR
gen
COMMENT("move XOR_RC->GPR")
xori %2, %1.reg, {CONST, %1.val}
/* Conditions */
/* Compare values, then copy cr0 to GPR. */
from COND_RC to GPR
gen
cmpwi %1.reg, {CONST, %1.val}
mfcr %2
from COND_RR to GPR
gen
cmpw %1.reg1, %1.reg2
mfcr %2
from CONDL_RC to GPR
gen
cmplwi %1.reg, {CONST, %1.val}
mfcr %2
from CONDL_RR to GPR
gen
cmplw %1.reg1, %1.reg2
mfcr %2
from COND_FS to GPR
gen
fcmpo CR0, %1.reg1, %1.reg2
mfcr %2
from COND_FD to GPR
gen
fcmpo CR0, %1.reg1, %1.reg2
mfcr %2
/* Given a copy of cr0 in %1.reg, extract a condition bit
* (lt, gt, eq) and perhaps flip it.
*/
from XEQ to GPR
gen
extrwi %2, %1.reg, {CONST, 1}, {CONST, 2}
from XNE to GPR
gen
extrwi %2, %1.reg, {CONST, 1}, {CONST, 2}
xori %2, %2, {CONST, 1}
from XGT to GPR
gen
extrwi %2, %1.reg, {CONST, 1}, {CONST, 1}
from XGE to GPR
gen
extrwi %2, %1.reg, {CONST, 1}, {CONST, 0}
xori %2, %2, {CONST, 1}
from XLT to GPR
gen
extrwi %2, %1.reg, {CONST, 1}, {CONST, 0}
from XLE to GPR
gen
extrwi %2, %1.reg, {CONST, 1}, {CONST, 1}
xori %2, %2, {CONST, 1}
/* Miscellaneous */
from OP_ALL_W + LABEL + CONST_ALL to GPRE
gen
move %1, %2.reg
TESTS
to test GPR
gen
orX RSCRATCH, %1, %1
STACKINGRULES
from LOCAL to STACK
gen
COMMENT("stack LOCAL")
stwu {GPRE, regvar(%1.off)}, {GPRINDIRECT, SP, 0-4}
from REG to STACK
gen
COMMENT("stack REG")
stwu %1, {GPRINDIRECT, SP, 0-4}
from REG_PAIR to STACK
gen
COMMENT("stack REG_PAIR")
stwu %1.2, {GPRINDIRECT, SP, 0-4}
stwu %1.1, {GPRINDIRECT, SP, 0-4}
from CONST_ALL + LABEL to STACK
gen
COMMENT("stack CONST_ALL + LABEL")
move %1, RSCRATCH
stwu RSCRATCH, {GPRINDIRECT, SP, 0-4}
from SEX_B to STACK
gen
COMMENT("stack SEX_B")
extsb RSCRATCH, %1.reg
stwu RSCRATCH, {GPRINDIRECT, SP, 0-4}
from SEX_H to STACK
gen
COMMENT("stack SEX_H")
extsh RSCRATCH, %1.reg
stwu RSCRATCH, {GPRINDIRECT, SP, 0-4}
from SUM_ALL + LOGICAL_ALL to STACK
gen
COMMENT("stack SUM_ALL + LOGICAL_ALL")
move %1, RSCRATCH
stwu RSCRATCH, {GPRINDIRECT, SP, 0-4}
from IND_ALL_BHW to STACK
gen
COMMENT("stack IND_ALL_BHW")
move %1, RSCRATCH
stwu RSCRATCH, {GPRINDIRECT, SP, 0-4}
from IND_ALL_D to STACK
gen
COMMENT("stack IND_ALL_D")
move %1, FSCRATCH
stfdu FSCRATCH, {GPRINDIRECT, SP, 0-8}
from FREG to STACK
gen
COMMENT("stack FPR")
stfdu %1, {GPRINDIRECT, SP, 0-8}
from FSREG to STACK
gen
COMMENT("stack FSREG")
stfsu %1, {GPRINDIRECT, SP, 0-4}
COERCIONS
from REG
uses REG
gen
COMMENT("coerce REG->REG")
move %1, %a
yields %a
from CONST_ALL
uses REG
gen
COMMENT("coerce CONST_ALL->REG")
move %1, %a
yields %a
from LABEL
uses REG
gen
COMMENT("coerce LABEL->REG")
move %1, %a
yields %a
from STACK
uses REG
gen
COMMENT("coerce STACK->REG")
lwz %a, {GPRINDIRECT, SP, 0}
addi SP, SP, {CONST, 4}
yields %a
from STACK
uses REG_PAIR
gen
COMMENT("coerce STACK->REG_PAIR")
lwz %a.1, {GPRINDIRECT, SP, 0}
lwz %a.2, {GPRINDIRECT, SP, 4}
addi SP, SP, {CONST, 8}
yields %a
from SEX_B
uses REG
gen
COMMENT("coerce SEX_B->REG")
extsb %a, %1.reg
yields %a
from SEX_H
uses REG
gen
COMMENT("coerce SEX_H->REG")
extsh %a, %1.reg
yields %a
from SUM_ALL + LOGICAL_ALL
uses REG
gen
move %1, %a
yields %a
from FSREG
uses FSREG
gen
fmr %a, %1
yields %a
from FREG
uses FREG
gen
fmr %a, %1
yields %a
from STACK
uses FREG
gen
COMMENT("coerce STACK->FREG")
lfd %a, {GPRINDIRECT, SP, 0}
addi SP, SP, {CONST, 8}
yields %a
from STACK
uses FSREG
gen
COMMENT("coerce STACK->FSREG")
lfs %a, {GPRINDIRECT, SP, 0}
addi SP, SP, {CONST, 4}
yields %a
from IND_ALL_BHW
uses REG
gen
move %1, %a
yields %a
from IND_ALL_W
uses FSREG
gen
move %1, %a
yields %a
/*
* from IND_RC_D to REG_PAIR is not possible, because
* %1.off+4 might overflow a signed 16-bit integer in
* move {IND_RC_W, %1.val, %1.off+4}, %a.2
*/
from IND_ALL_D
uses FREG
gen
move %1, %a
yields %a
PATTERNS
/* Intrinsics */
pat loc $1==(0-0x8000) /* Load constant */
yields {CONST_N8000, $1}
pat loc $1>=(0-0x7FFF) && $1<=(0-1)
yields {CONST_N7FFF_N0001, $1}
pat loc $1>=0 && $1<=0x7FFF
yields {CONST_0000_7FFF, $1}
pat loc $1==0x8000
yields {CONST_8000, $1}
pat loc $1>=0x8001 && $1<=0xFFFF
yields {CONST_8001_FFFF, $1}
pat loc lo($1)==0
yields {CONST_HZ, $1}
pat loc
yields {CONST_HL, $1}
pat dup $1==INT32 /* Duplicate word on top of stack */
with REG
yields %1 %1
with FSREG
yields %1 %1
pat dup $1==INT64 /* Duplicate double-word on top of stack */
with REG REG
yields %2 %1 %2 %1
with FREG
yields %1 %1
pat exg $1==INT32 /* Exchange top two words on stack */
with REG REG
yields %1 %2
pat stl lol $1==$2 /* Store then load local */
leaving
dup 4
stl $1
pat sdl ldl $1==$2 /* Store then load double local */
leaving
dup 8
sdl $1
pat lal sti lal loi $1==$3 && $2==$4 /* Store then load local, of a different size */
leaving
dup INT32
lal $1
sti $2
pat ste loe $1==$2 /* Store then load external */
leaving
dup 4
ste $1
/* Type conversions */
pat loc loc cii loc loc cii $1==$4 && $2==$5 /* madness, generated by the C compiler */
leaving
loc $1
loc $2
cii
pat loc loc cii loc loc cii $2==INT32 && $5==INT32 && $4<$2 /* madness, generated by the C compiler */
leaving
loc $4
loc $5
cii
pat loc loc ciu /* signed X -> unsigned X */
leaving
loc $1
loc $2
cuu
pat loc loc cuu $1==$2 /* unsigned X -> unsigned X */
/* nop */
pat loc loc cii $1==$2 /* signed X -> signed X */
/* nop */
pat loc loc cui $1==$2 /* unsigned X -> signed X */
/* nop */
pat loc loc cui $1==INT8 && $2==INT32 /* unsigned char -> signed int */
/* nop */
pat loc loc cui $1==INT16 && $2==INT32 /* unsigned short -> signed int */
/* nop */
pat loc loc cii $1==INT8 && $2==INT32 /* signed char -> signed int */
with GPR
yields {SEX_B, %1}
pat loc loc cii $1==2 && $2==4 /* signed char -> signed short */
with GPR
yields {SEX_H, %1}
/* Local variables */
pat lal smalls($1) /* Load address of local */
yields {SUM_RC, FP, $1}
pat lal /* Load address of local */
uses REG={SUM_RIS, FP, his($1)}
yields {SUM_RC, %a, los($1)}
pat lol inreg($1)>0 /* Load from local */
yields {LOCAL, $1}
pat lol /* Load from local */
leaving
lal $1
loi INT32
pat ldl /* Load double-word from local */
leaving
lal $1
loi INT32*2
pat stl inreg($1)>0 /* Store to local */
with CONST_ALL + LABEL + GPR + OP_ALL_W
kills regvar($1), LOCAL %off==$1
gen
move %1, {GPRE, regvar($1)}
pat stl /* Store to local */
leaving
lal $1
sti INT32
pat sdl /* Store double-word to local */
leaving
lal $1
sti INT32*2
pat lil inreg($1)>0 /* Load from indirected local */
uses REG
gen
lwz %a, {GPRINDIRECT, regvar($1), 0}
yields %a
pat lil /* Load from indirected local */
leaving
lol $1
loi INT32
pat sil /* Save to indirected local */
leaving
lol $1
sti INT32
pat zrl /* Zero local */
leaving
loc 0
stl $1
pat inl /* Increment local */
leaving
lol $1
loc 1
adi 4
stl $1
pat del /* Decrement local */
leaving
lol $1
loc 1
sbi 4
stl $1
/* Global variables */
pat lpi /* Load address of external function */
leaving
lae $1
pat lae /* Load address of external */
yields {LABEL, $1}
pat loe /* Load word external */
leaving
lae $1
loi INT32
pat ste /* Store word external */
leaving
lae $1
sti INT32
pat lde /* Load double-word external */
leaving
lae $1
loi INT64
pat sde /* Store double-word external */
leaving
lae $1
sti INT64
pat zre /* Zero external */
leaving
loc 0
ste $1
pat ine /* Increment external */
kills MEMORY
uses REG={LABEL, $1}, REG
gen
lwz %b, {GPRINDIRECT, %a, 0}
addi %b, %b, {CONST, 1}
stw %b, {GPRINDIRECT, %a, 0}
pat dee /* Decrement external */
kills MEMORY
uses REG={LABEL, $1}, REG
gen
lwz %b, {GPRINDIRECT, %a, 0}
addi %b, %b, {CONST, 0-1}
stw %b, {GPRINDIRECT, %a, 0}
/* Structures */
pat lof /* Load word offsetted */
leaving
adp $1
loi INT32
pat ldf /* Load double-word offsetted */
leaving
adp $1
loi INT64
pat stf /* Store word offsetted */
leaving
adp $1
sti INT32
pat sdf /* Store double-word offsetted */
leaving
adp $1
sti INT64
/* Loads and stores */
pat loi $1==INT8 /* Load byte indirect */
with GPR
yields {IND_RC_B, %1, 0}
with SUM_RR
yields {IND_RR_B, %1.reg1, %1.reg2}
with SUM_RC
yields {IND_RC_B, %1.reg, %1.off}
pat loi loc loc cii $1==INT16 && $2==INT16 && $3==INT32
/* Load half-word indirect and sign extend */
with GPR
yields {IND_RC_H_S, %1, 0}
with SUM_RR
yields {IND_RR_H_S, %1.reg1, %1.reg2}
with SUM_RC
yields {IND_RC_H_S, %1.reg, %1.off}
pat loi $1==INT16 /* Load half-word indirect */
with GPR
yields {IND_RC_H, %1, 0}
with SUM_RR
yields {IND_RR_H, %1.reg1, %1.reg2}
with SUM_RC
yields {IND_RC_H, %1.reg, %1.off}
pat loi $1==INT32 /* Load word indirect */
with LABEL
uses REG
gen
addis %a, R0, {LABEL_OFFSET_HA, %1.adr}
lwz %a, {GPRINDIRECT_OFFSET_LO, %a, %1.adr}
yields %a
with GPR
yields {IND_RC_W, %1, 0}
with SUM_RC
yields {IND_RC_W, %1.reg, %1.off}
with SUM_RR
yields {IND_RR_W, %1.reg1, %1.reg2}
pat loi $1==INT64 /* Load double-word indirect */
with GPR
yields {IND_RC_D, %1, 0}
with SUM_RC
yields {IND_RC_D, %1.reg, %1.off}
with SUM_RR
yields {IND_RR_D, %1.reg1, %1.reg2}
pat loi /* Load arbitrary size */
leaving
loc $1
los INT32
pat los $1==INT32 /* Load arbitrary size */
with GPR3 GPR4 STACK
kills ALL
gen
bl {LABEL, ".los"}
pat sti $1==INT8 /* Store byte indirect */
with GPR GPR
kills MEMORY
gen
stb %2, {GPRINDIRECT, %1, 0}
with SUM_RR GPR
kills MEMORY
gen
stbx %2, %1.reg1, %1.reg2
with SUM_RC GPR
kills MEMORY
gen
move %2, {IND_RC_B, %1.reg, %1.off}
with GPR SEX_B
kills MEMORY
gen
stb %2.reg, {GPRINDIRECT, %1, 0}
with SUM_RR SEX_B
kills MEMORY
gen
stbx %2.reg, %1.reg1, %1.reg2
with SUM_RC SEX_B
kills MEMORY
gen
move %2.reg, {IND_RC_B, %1.reg, %1.off}
pat sti $1==INT16 /* Store half-word indirect */
with GPR GPR
kills MEMORY
gen
sth %2, {GPRINDIRECT, %1, 0}
with SUM_RR GPR
kills MEMORY
gen
sthx %2, %1.reg1, %1.reg2
with SUM_RC GPR
kills MEMORY
gen
move %2, {IND_RC_H, %1.reg, %1.off}
with GPR SEX_H
kills MEMORY
gen
sth %2.reg, {GPRINDIRECT, %1, 0}
with SUM_RR SEX_H
kills MEMORY
gen
sthx %2.reg, %1.reg1, %1.reg2
with SUM_RC SEX_H
kills MEMORY
gen
move %2.reg, {IND_RC_H, %1.reg, %1.off}
pat sti $1==INT32 /* Store word indirect */
with GPR GPR+FSREG
kills MEMORY
gen
move %2, {IND_RC_W, %1, 0}
with SUM_RR GPR+FSREG
kills MEMORY
gen
move %2, {IND_RR_W, %1.reg1, %1.reg2}
with SUM_RC GPR+FSREG
kills MEMORY
gen
move %2, {IND_RC_W, %1.reg, %1.off}
pat sti $1==INT64 /* Store double-word indirect */
with REG FREG
kills MEMORY
gen
move %2, {IND_RC_D, %1, 0}
with SUM_RR FREG
kills MEMORY
gen
move %2, {IND_RR_D, %1.reg1, %1.reg2}
with SUM_RC FREG
kills MEMORY
gen
move %2, {IND_RC_D, %1.reg, %1.off}
/*
* This pattern would be too slow:
* with REG REG REG
* ncg can't handle that many registers, and would
* take about 2 seconds on each sti 8. So we use
* REG_PAIR as a speed hack for sti 8.
*/
with REG REG_PAIR
kills MEMORY
gen
move %2.1, {IND_RC_W, %1, 0}
move %2.2, {IND_RC_W, %1, 4}
/*
* Next 2 patterns exist because there is no coercion
* from IND_ALL_D to REG_PAIR.
*/
with REG IND_RC_D
kills MEMORY
uses REG={SUM_RC, %2.reg, %2.off}, REG_PAIR
gen
move {IND_RC_W, %a, 0}, %b.1
move {IND_RC_W, %a, 4}, %b.2
move %b.1, {IND_RC_W, %1, 0}
move %b.2, {IND_RC_W, %1, 4}
with REG IND_RR_D
kills MEMORY
uses REG={SUM_RR, %2.reg1, %2.reg2}, REG_PAIR
gen
move {IND_RC_W, %a, 0}, %b.1
move {IND_RC_W, %a, 4}, %b.2
move %b.1, {IND_RC_W, %1, 0}
move %b.2, {IND_RC_W, %1, 4}
pat sti /* Store arbitrary size */
leaving
loc $1
sts INT32
pat sts $1==INT32 /* Store arbitrary size */
with GPR3 GPR4 STACK
kills ALL
gen
bl {LABEL, ".sts"}
/* Arithmetic wrappers */
pat ads $1==4 /* Add var to pointer */
leaving adi $1
pat sbs $1==4 /* Subtract var from pointer */
leaving sbi $1
pat adp /* Add constant to pointer */
leaving
loc $1
adi 4
pat adu /* Add unsigned */
leaving
adi $1
pat sbu /* Subtract unsigned */
leaving
sbi $1
pat inc /* Add 1 */
leaving
loc 1
adi 4
pat dec /* Subtract 1 */
leaving
loc 1
sbi 4
pat loc mlu $2==2 /* Unsigned multiply by constant */
leaving
loc $1
mli 4
pat mlu /* Unsigned multiply by var */
leaving
mli $1
pat loc slu /* Shift left unsigned by constant amount */
leaving
loc $1
sli $2
pat slu /* Shift left unsigned by variable amount */
leaving
sli $1
/* Word arithmetic */
pat adi $1==4 /* Add word (second + top) */
with REG REG
yields {SUM_RR, %1, %2}
with CONST2 REG
yields {SUM_RC, %2, %1.val}
with REG CONST2
yields {SUM_RC, %1, %2.val}
with CONST_HZ REG
uses reusing %2, REG={SUM_RIS, %2, his(%1.val)}
yields %a
with REG CONST_HZ
uses reusing %1, REG={SUM_RIS, %1, his(%2.val)}
yields %a
with CONST_ALL-CONST2-CONST_HZ REG
uses reusing %2, REG={SUM_RIS, %2, his(%1.val)}
yields {SUM_RC, %a, los(%1.val)}
with REG CONST_ALL-CONST2-CONST_HZ
uses reusing %1, REG={SUM_RIS, %1, his(%2.val)}
yields {SUM_RC, %a, los(%2.val)}
with CONST_ALL LABEL
yields {LABEL, %2.adr+%1.val}
pat sbi $1==4 /* Subtract word (second - top) */
with REG REG
uses reusing %2, REG
gen
subf %a, %1, %2
yields %a
with CONST2_WHEN_NEG REG
yields {SUM_RC, %2, 0-%1.val}
with CONST_HZ REG
uses reusing %2, REG={SUM_RIS, %2, his(0-%1.val)}
yields %a
with CONST_ALL-CONST2_WHEN_NEG-CONST_HZ REG
uses reusing %2, REG={SUM_RIS, %2, his(0-%1.val)}
yields {SUM_RC, %a, los(0-%1.val)}
with CONST_ALL LABEL
yields {LABEL, %2.adr+(0-%1.val)}
pat ngi $1==4 /* Negate word */
with REG
uses reusing %1, REG
gen
neg %a, %1
yields %a
pat mli $1==4 /* Multiply word (second * top) */
with REG REG
uses reusing %2, REG
gen
mullw %a, %2, %1
yields %a
pat dvi $1==4 /* Divide word (second / top) */
with REG REG
uses reusing %2, REG
gen
divw %a, %2, %1
yields %a
pat dvu $1==4 /* Divide unsigned word (second / top) */
with REG REG
uses reusing %2, REG
gen
divwu %a, %2, %1
yields %a
pat rmi $1==4 /* Remainder word (second % top) */
with REG REG
uses REG
gen
divw %a, %2, %1
mullw %a, %a, %1
subf %a, %a, %2
yields %a
pat rmu $1==4 /* Remainder unsigned word (second % top) */
with REG REG
uses REG
gen
divwu %a, %2, %1
mullw %a, %a, %1
subf %a, %a, %2
yields %a
pat and $1==4 /* AND word */
with GPR NOT_R
uses reusing %1, REG
gen
andc %a, %1, %2.reg
yields %a
with NOT_R GPR
uses reusing %1, REG
gen
andc %a, %2, %1.reg
yields %a
with GPR GPR
yields {AND_RR, %1, %2}
with GPR UCONST2
uses reusing %1, REG
gen
andiX %a, %1, {CONST, %2.val}
yields %a
with UCONST2 GPR
uses reusing %2, REG
gen
andiX %a, %2, {CONST, %1.val}
yields %a
with GPR CONST_HZ
uses reusing %1, REG
gen
andisX %a, %1, {CONST, hi(%2.val)}
yields %a
with CONST_HZ GPR
uses reusing %2, REG
gen
andisX %a, %2, {CONST, hi(%1.val)}
yields %a
pat and defined($1) /* AND set */
leaving
loc $1
cal ".and"
pat and !defined($1)
leaving
cal ".and"
pat ior $1==4 /* OR word */
with REG NOT_R
uses reusing %1, REG
gen
orc %a, %1, %2.reg
yields %a
with NOT_R REG
uses reusing %2, REG
gen
orc %a, %2, %1.reg
yields %a
with REG REG
yields {OR_RR, %1, %2}
with REG UCONST2
yields {OR_RC, %1, %2.val}
with UCONST2 REG
yields {OR_RC, %2, %1.val}
with REG CONST_HZ
uses reusing %1, REG={OR_RIS, %1, hi(%2.val)}
yields %a
with CONST_HZ REG
uses reusing %2, REG={OR_RIS, %2, hi(%1.val)}
yields %a
with REG CONST_ALL-UCONST2-CONST_HZ
uses reusing %1, REG={OR_RIS, %1, hi(%2.val)}
yields {OR_RC, %1, lo(%2.val)}
with CONST_ALL-UCONST2-CONST_HZ REG
uses reusing %2, REG={OR_RIS, %2, hi(%1.val)}
yields {OR_RC, %2, lo(%1.val)}
pat ior defined($1) /* OR set */
leaving
loc $1
cal ".ior"
/* OR set (variable), used in lang/m2/libm2/LtoUset.e */
pat ior !defined($1)
leaving
cal ".ior"
pat xor $1==4 /* XOR word */
with REG REG
yields {XOR_RR, %1, %2}
with REG UCONST2
yields {XOR_RC, %1, %2.val}
with UCONST2 REG
yields {XOR_RC, %2, %1.val}
with REG CONST_HZ
uses reusing %1, REG={XOR_RIS, %1, hi(%2.val)}
yields %a
with CONST_HZ REG
uses reusing %2, REG={XOR_RIS, %2, hi(%1.val)}
yields %a
with REG CONST_ALL-UCONST2-CONST_HZ
uses reusing %1, REG={XOR_RIS, %1, hi(%2.val)}
yields {XOR_RC, %1, lo(%2.val)}
with CONST_ALL-UCONST2-CONST_HZ REG
uses reusing %2, REG={XOR_RIS, %2, hi(%1.val)}
yields {XOR_RC, %2, lo(%1.val)}
pat xor defined($1) /* XOR set */
with STACK
kills ALL
gen
move {CONST, $1}, R3
bl {LABEL, ".xor"}
pat com $1==INT32 /* NOT word */
with AND_RR
uses REG
gen
nand %a, %1.reg1, %1.reg2
yields %a
with OR_RR
uses REG
gen
nor %a, %1.reg1, %1.reg2
yields %a
with XOR_RR
uses REG
gen
eqv %a, %1.reg1, %1.reg2
yields %a
with GPR
yields {NOT_R, %1}
pat com defined($1) /* NOT set */
leaving
loc $1
cal ".com"
pat com !defined($1)
leaving
cal ".com"
pat zer $1==4 /* Push zero */
leaving
loc 0
pat zer defined($1) /* Create empty set */
leaving
loc $1
cal ".zer"
pat sli $1==4 /* Shift left (second << top) */
with CONST_ALL GPR
uses reusing %2, REG
gen
rlwinm %a, %2, {CONST, (%1.val & 0x1F)}, {CONST, 0}, {CONST, 31-(%1.val & 0x1F)}
yields %a
with GPR GPR
uses reusing %2, REG
gen
slw %a, %2, %1
yields %a
pat sri $1==4 /* Shift right signed (second >> top) */
with CONST_ALL GPR
uses reusing %2, REG
gen
srawi %a, %2, {CONST, %1.val & 0x1F}
yields %a
with GPR GPR
uses reusing %2, REG
gen
sraw %a, %2, %1
yields %a
pat sru $1==4 /* Shift right unsigned (second >> top) */
with CONST_ALL GPR
uses reusing %2, REG
gen
rlwinm %a, %2, {CONST, 32-(%1.val & 0x1F)}, {CONST, (%1.val & 0x1F)}, {CONST, 31}
yields %a
with GPR GPR
uses reusing %2, REG
gen
srw %a, %2, %1
yields %a
/* Arrays */
pat aar $1==INT32 /* Index array */
with GPR3 GPR4 GPR5
kills ALL
gen
bl {LABEL, ".aar4"}
yields R3
pat lae lar $2==INT32 && nicesize(rom($1, 3)) /* Load array */
leaving
lae $1
aar INT32
loi rom($1, 3)
pat lar $1==INT32 /* Load array */
with GPR3 GPR4 GPR5 STACK
kills ALL
gen
bl {LABEL, ".lar4"}
pat lae sar $2==INT32 && nicesize(rom($1, 3)) /* Store array */
leaving
lae $1
aar INT32
sti rom($1, 3)
pat sar $1==INT32 /* Store array */
with GPR3 GPR4 GPR5 STACK
kills ALL
gen
bl {LABEL, ".sar4"}
/* Sets */
pat set defined($1) /* Create singleton set */
leaving
loc $1
cal ".set"
/* Create set (variable), used in lang/m2/libm2/LtoUset.e */
pat set !defined($1)
leaving
cal ".set"
pat inn defined($1) /* Test for set bit */
leaving
loc $1
cal ".inn"
pat inn !defined($1)
leaving
cal ".inn"
/* Boolean resolutions */
pat teq /* top = (top == 0) */
with REG
uses reusing %1, REG
gen
test %1
mfcr %a
move {XEQ, %a}, %a
yields %a
pat tne /* top = (top != 0) */
with REG
uses reusing %1, REG
gen
test %1
mfcr %a
move {XNE, %a}, %a
yields %a
pat tlt /* top = (top < 0) */
with REG
uses reusing %1, REG
gen
test %1
mfcr %a
move {XLT, %a}, %a
yields %a
pat tle /* top = (top <= 0) */
with REG
uses reusing %1, REG
gen
test %1
mfcr %a
move {XLE, %a}, %a
yields %a
pat tgt /* top = (top > 0) */
with REG
uses reusing %1, REG
gen
test %1
mfcr %a
move {XGT, %a}, %a
yields %a
pat tge /* top = (top >= 0) */
with REG
uses reusing %1, REG
gen
test %1
mfcr %a
move {XGE, %a}, %a
yields %a
pat cmi teq $1==4 /* Signed second == top */
with REG CONST2
uses reusing %1, REG={COND_RC, %1, %2.val}
gen move {XEQ, %a}, %a
yields %a
with CONST2 REG
uses reusing %1, REG={COND_RC, %2, %1.val}
gen move {XEQ, %a}, %a
yields %a
with REG REG
uses reusing %1, REG={COND_RR, %2, %1}
gen move {XEQ, %a}, %a
yields %a
pat cmi tne $1==4 /* Signed second != top */
with REG CONST2
uses reusing %1, REG={COND_RC, %1, %2.val}
gen move {XNE, %a}, %a
yields %a
with CONST2 REG
uses reusing %1, REG={COND_RC, %2, %1.val}
gen move {XNE, %a}, %a
yields %a
with REG REG
uses reusing %1, REG={COND_RR, %2, %1}
gen move {XNE, %a}, %a
yields %a
pat cmi tgt $1==4 /* Signed second > top */
with REG CONST2
uses reusing %1, REG={COND_RC, %1, %2.val}
gen move {XLT, %a}, %a
yields %a
with CONST2 REG
uses reusing %1, REG={COND_RC, %2, %1.val}
gen move {XGT, %a}, %a
yields %a
with REG REG
uses reusing %1, REG={COND_RR, %2, %1}
gen move {XGT, %a}, %a
yields %a
pat cmi tge $1==4 /* Signed second >= top */
with REG CONST2
uses reusing %1, REG={COND_RC, %1, %2.val}
gen move {XLE, %a}, %a
yields %a
with CONST2 REG
uses reusing %1, REG={COND_RC, %2, %1.val}
gen move {XGE, %a}, %a
yields %a
with REG REG
uses reusing %1, REG={COND_RR, %2, %1}
gen move {XGE, %a}, %a
yields %a
pat cmi tlt $1==4 /* Signed second < top */
with REG CONST2
uses reusing %1, REG={COND_RC, %1, %2.val}
gen move {XGT, %a}, %a
yields %a
with CONST2 REG
uses reusing %1, REG={COND_RC, %2, %1.val}
gen move {XLT, %a}, %a
yields %a
with REG REG
uses reusing %1, REG={COND_RR, %2, %1}
gen move {XLT, %a}, %a
yields %a
pat cmi tle $1==4 /* Signed second <= top */
with REG CONST2
uses reusing %1, REG={COND_RC, %1, %2.val}
gen move {XGE, %a}, %a
yields %a
with CONST2 REG
uses reusing %1, REG={COND_RC, %2, %1.val}
gen move {XLE, %a}, %a
yields %a
with REG REG
uses reusing %1, REG={COND_RR, %2, %1}
gen move {XLE, %a}, %a
yields %a
pat cmu teq $1==4 /* Unsigned second == top */
with REG UCONST2
uses reusing %1, REG={CONDL_RC, %1, %2.val}
gen move {XEQ, %a}, %a
yields %a
with UCONST2 REG
uses reusing %1, REG={CONDL_RC, %2, %1.val}
gen move {XEQ, %a}, %a
yields %a
with REG REG
uses reusing %1, REG={CONDL_RR, %2, %1}
gen move {XEQ, %a}, %a
yields %a
pat cmu tne $1==4 /* Unsigned second != top */
with REG UCONST2
uses reusing %1, REG={CONDL_RC, %1, %2.val}
gen move {XNE, %a}, %a
yields %a
with UCONST2 REG
uses reusing %1, REG={CONDL_RC, %2, %1.val}
gen move {XNE, %a}, %a
yields %a
with REG REG
uses reusing %1, REG={CONDL_RR, %2, %1}
gen move {XNE, %a}, %a
yields %a
pat cmu tgt $1==4 /* Unsigned second > top */
with REG UCONST2
uses reusing %1, REG={CONDL_RC, %1, %2.val}
gen move {XLT, %a}, %a
yields %a
with UCONST2 REG
uses reusing %1, REG={CONDL_RC, %2, %1.val}
gen move {XGT, %a}, %a
yields %a
with REG REG
uses reusing %1, REG={CONDL_RR, %2, %1}
gen move {XGT, %a}, %a
yields %a
pat cmu tge $1==4 /* Unsigned second >= top */
with REG UCONST2
uses reusing %1, REG={CONDL_RC, %1, %2.val}
gen move {XLE, %a}, %a
yields %a
with UCONST2 REG
uses reusing %1, REG={CONDL_RC, %2, %1.val}
gen move {XGE, %a}, %a
yields %a
with REG REG
uses reusing %1, REG={CONDL_RR, %2, %1}
gen move {XGE, %a}, %a
yields %a
pat cmu tlt $1==4 /* Unsigned second < top */
with REG UCONST2
uses reusing %1, REG={CONDL_RC, %1, %2.val}
gen move {XGT, %a}, %a
yields %a
with UCONST2 REG
uses reusing %1, REG={CONDL_RC, %2, %1.val}
gen move {XLT, %a}, %a
yields %a
with REG REG
uses reusing %1, REG={CONDL_RR, %2, %1}
gen move {XLT, %a}, %a
yields %a
pat cmu tle $1==4 /* Unsigned second <= top */
with REG UCONST2
uses reusing %1, REG={CONDL_RC, %1, %2.val}
gen move {XGE, %a}, %a
yields %a
with UCONST2 REG
uses reusing %1, REG={CONDL_RC, %2, %1.val}
gen move {XLE, %a}, %a
yields %a
with REG REG
uses reusing %1, REG={CONDL_RR, %2, %1}
gen move {XLE, %a}, %a
yields %a
/* Simple branches */
proc zxx example zeq
with REG STACK
gen
test %1
bxx* {LABEL, $1}
/* Pop signed int, branch if... */
pat zeq call zxx("beq") /* top == 0 */
pat zne call zxx("bne") /* top != 0 */
pat zgt call zxx("bgt") /* top > 0 */
pat zge call zxx("bge") /* top >= 0 */
pat zlt call zxx("blt") /* top < 0 */
pat zle call zxx("ble") /* top >= 0 */
/* The peephole optimizer rewrites
* cmi 4 zeq
* as beq, and does same for bne, bgt, and so on.
*/
proc bxx example beq
with REG CONST2 STACK
gen
cmpwi %1, {CONST, %2.val}
bxx[2] {LABEL, $1}
with CONST2 REG STACK
gen
cmpwi %2, {CONST, %1.val}
bxx[1] {LABEL, $1}
with REG REG STACK
gen
cmpw %2, %1
bxx[1] {LABEL, $1}
/* Pop two signed ints, branch if... */
pat beq call bxx("beq", "beq") /* second == top */
pat bne call bxx("bne", "bne") /* second != top */
pat bgt call bxx("bgt", "blt") /* second > top */
pat bge call bxx("bge", "ble") /* second >= top */
pat blt call bxx("blt", "bgt") /* second < top */
pat ble call bxx("ble", "bge") /* second >= top */
proc cmu4zxx example cmu zeq
with REG CONST2 STACK
gen
cmplwi %1, {CONST, %2.val}
bxx[2] {LABEL, $2}
with CONST2 REG STACK
gen
cmplwi %2, {CONST, %1.val}
bxx[1] {LABEL, $2}
with REG REG STACK
gen
cmplw %2, %1
bxx[1] {LABEL, $2}
/* Pop two unsigned ints, branch if... */
pat cmu zeq $1==4 call cmu4zxx("beq", "beq")
pat cmu zne $1==4 call cmu4zxx("bne", "bne")
pat cmu zgt $1==4 call cmu4zxx("bgt", "blt")
pat cmu zge $1==4 call cmu4zxx("bge", "ble")
pat cmu zlt $1==4 call cmu4zxx("blt", "bgt")
pat cmu zle $1==4 call cmu4zxx("ble", "bge")
/* Comparisons */
/* Each comparison extracts the lt and gt bits from cr0.
* extlwi %a, %a, 2, 0
* puts lt in the sign bit, so lt yields a negative result,
* gt yields positive.
* rlwinm %a, %a, 1, 31, 0
* puts gt in the sign bit, to reverse the comparison.
*/
pat cmi $1==INT32 /* Signed tristate compare */
with REG CONST2
uses reusing %1, REG={COND_RC, %1, %2.val}
gen rlwinm %a, %a, {CONST, 1}, {CONST, 31}, {CONST, 0}
yields %a
with CONST2 REG
uses reusing %2, REG={COND_RC, %2, %1.val}
gen extlwi %a, %a, {CONST, 2}, {CONST, 0}
yields %a
with REG REG
uses reusing %1, REG={COND_RR, %2, %1}
gen extlwi %a, %a, {CONST, 2}, {CONST, 0}
yields %a
pat cmu $1==INT32 /* Unsigned tristate compare */
with REG UCONST2
uses reusing %1, REG={CONDL_RC, %1, %2.val}
gen rlwinm %a, %a, {CONST, 1}, {CONST, 31}, {CONST, 0}
yields %a
with UCONST2 REG
uses reusing %2, REG={CONDL_RC, %2, %1.val}
gen extlwi %a, %a, {CONST, 2}, {CONST, 0}
yields %a
with REG REG
uses reusing %1, REG={CONDL_RR, %2, %1}
gen extlwi %a, %a, {CONST, 2}, {CONST, 0}
yields %a
pat cmp /* Compare pointers */
leaving
cmu INT32
pat cms $1==INT32 /* Compare blocks (word sized) */
leaving
cmi INT32
pat cms defined($1)
with STACK
kills ALL
gen
move {CONST, $1}, R3
bl {LABEL, ".cms"}
yields R3
/* Other branching and labelling */
pat lab topeltsize($1)==4 && !fallthrough($1)
kills ALL
gen
labeldef $1
yields R3
pat lab topeltsize($1)==4 && fallthrough($1)
with GPR3 STACK
kills ALL
gen
labeldef $1
yields %1
pat lab topeltsize($1)!=4
with STACK
kills ALL
gen
labeldef $1
pat bra topeltsize($1)==4 /* Unconditional jump with TOS GPRister */
with GPR3 STACK
gen
b {LABEL, $1}
pat bra topeltsize($1)!=4 /* Unconditional jump without TOS GPRister */
with STACK
gen
b {LABEL, $1}
/* Miscellaneous */
pat cal /* Call procedure */
with STACK
kills ALL
gen
bl {LABEL, $1}
pat cai /* Call procedure indirect */
with GPR STACK
kills ALL
gen
mtspr CTR, %1
bctrl.
pat lfr $1==INT32 /* Load function result, word */
yields R3
pat lfr $1==INT64 /* Load function result, double-word */
yields R4 R3
pat ret $1==0 /* Return from procedure */
gen
return
b {LABEL, ".ret"}
pat ret $1==INT32 /* Return from procedure, word */
with GPR3
gen
return
b {LABEL, ".ret"}
pat ret $1==INT64 /* Return from procedure, double-word */
with GPR3 GPR4
gen
return
b {LABEL, ".ret"}
pat blm /* Block move constant length */
with GPR GPR STACK
uses REG
gen
move {CONST, $1}, %a
stwu %a, {GPRINDIRECT, SP, 0-4}
stwu %2, {GPRINDIRECT, SP, 0-4}
stwu %1, {GPRINDIRECT, SP, 0-4}
bl {LABEL, "_memmove"}
addi SP, SP, {CONST, 12}
pat bls /* Block move variable length */
with GPR GPR GPR STACK
gen
stwu %1, {GPRINDIRECT, SP, 0-4}
stwu %3, {GPRINDIRECT, SP, 0-4}
stwu %2, {GPRINDIRECT, SP, 0-4}
bl {LABEL, "_memmove"}
addi SP, SP, {CONST, 12}
pat csa /* Array-lookup switch */
with STACK
gen
b {LABEL, ".csa"}
pat csb /* Table-lookup switch */
with STACK
gen
b {LABEL, ".csb"}
/* EM specials */
pat fil /* Set current filename */
leaving
lae $1
ste "hol0+4"
pat lin /* Set current line number */
leaving
loc $1
ste "hol0"
pat lni /* Increment line number */
leaving
ine "hol0"
pat lim /* Load EM trap ignore mask */
leaving
lde ".ignmask"
pat sim /* Store EM trap ignore mask */
leaving
ste ".ignmask"
pat trp /* Raise EM trap */
with GPR3
gen
bl {LABEL, ".trap"}
pat sig /* Set trap handler */
leaving
ste ".trppc"
pat rtt /* Return from trap */
leaving
ret 0
pat lxl $1==0 /* Load FP */
leaving
lor 0
pat lxl $1==1 /* Load caller's FP */
leaving
lxl 0
dch
pat dch /* FP -> caller FP */
with GPR
uses reusing %1, REG
gen
lwz %a, {GPRINDIRECT, %1, FP_OFFSET}
yields %a
pat lpb /* Convert FP to argument address */
leaving
adp EM_BSIZE
pat lxa /* Load caller's SP */
leaving
lxl $1
lpb
pat gto /* longjmp */
uses REG
gen
move {LABEL, $1}, %a
move {IND_RC_W, %a, 8}, FP
move {IND_RC_W, %a, 4}, SP
move {IND_RC_W, %a, 0}, %a
mtspr CTR, %a
bctr.
pat lor $1==0 /* Load FP */
uses REG
gen
move FP, %a
yields %a
pat lor $1==1 /* Load SP */
uses REG
gen
move SP, %a
yields %a
pat str $1==0 /* Store FP */
with GPR
gen
move %1, FP
pat str $1==1 /* Store SP */
with GPR
gen
move %1, SP
pat loc ass $1==4 && $2==4 /* Drop 4 bytes from stack */
with exact GPR
/* nop */
with STACK
gen
addi SP, SP, {CONST, 4}
pat ass $1==4 /* Adjust stack by variable amount */
with CONST2 STACK
gen
move {SUM_RC, SP, %1.val}, SP
with CONST_HZ STACK
gen
move {SUM_RC, SP, his(%1.val)}, SP
with CONST_ALL-CONST2-CONST_HZ STACK
gen
move {SUM_RC, SP, his(%1.val)}, SP
move {SUM_RC, SP, los(%1.val)}, SP
with GPR STACK
gen
move {SUM_RR, SP, %1}, SP
pat asp /* Adjust stack by constant amount */
leaving
loc $1
ass 4
pat lae rck $2==4 /* Range check */
with REG
gen
cmpwi %1, {CONST, rom($1, 1)}
blt {LABEL, ".trap_erange"}
cmpwi %1, {CONST, rom($1, 2)}
bgt {LABEL, ".trap_erange"}
yields %1
/* Floating point support */
/* All very cheap and nasty --- this needs to be properly integrated into
* the code generator. ncg doesn't like having separate FPU registers. */
/* Single-precision */
pat zrf $1==INT32 /* Push zero */
leaving
loe ".fs_00000000"
pat adf $1==INT32 /* Add single */
with FSREG FSREG
uses reusing %1, FSREG
gen
fadds %a, %2, %1
yields %a
pat sbf $1==INT32 /* Subtract single */
with FSREG FSREG
uses reusing %1, FSREG
gen
fsubs %a, %2, %1
yields %a
pat mlf $1==INT32 /* Multiply single */
with FSREG FSREG
uses reusing %1, FSREG
gen
fmuls %a, %2, %1
yields %a
pat dvf $1==INT32 /* Divide single */
with FSREG FSREG
uses reusing %1, FSREG
gen
fdivs %a, %2, %1
yields %a
pat ngf $1==INT32 /* Negate single */
with FSREG
uses reusing %1, FSREG
gen
fneg %a, %1
yields %a
pat cmf $1==INT32 /* Compare single */
with FSREG FSREG
uses REG={COND_FS, %2, %1}
gen extlwi %a, %a, {CONST, 2}, {CONST, 0}
yields %a
pat cmf teq $1==4 /* Single second == top */
with FSREG FSREG
uses REG={COND_FS, %2, %1}
gen move {XEQ, %a}, %a
yields %a
pat cmf tne $1==4 /* Single second == top */
with FSREG FSREG
uses REG={COND_FS, %2, %1}
gen move {XNE, %a}, %a
yields %a
pat cmf tgt $1==4 /* Single second > top */
with FSREG FSREG
uses REG={COND_FS, %2, %1}
gen move {XGT, %a}, %a
yields %a
pat cmf tge $1==4 /* Single second >= top */
with FSREG FSREG
uses REG={COND_FS, %2, %1}
gen move {XGE, %a}, %a
yields %a
pat cmf tlt $1==4 /* Single second < top */
with FSREG FSREG
uses REG={COND_FS, %2, %1}
gen move {XLT, %a}, %a
yields %a
pat cmf tle $1==4 /* Single second <= top */
with FSREG FSREG
uses REG={COND_FS, %2, %1}
gen move {XLE, %a}, %a
yields %a
proc cmf4zxx example cmf zeq
with FREG FREG STACK
uses REG
gen
fcmpo CR0, %2, %1
bxx* {LABEL, $2}
/* Pop 2 singles, branch if... */
pat cmf zeq $1==4 call cmf4zxx("beq")
pat cmf zne $1==4 call cmf4zxx("bne")
pat cmf zgt $1==4 call cmf4zxx("bgt")
pat cmf zge $1==4 call cmf4zxx("bge")
pat cmf zlt $1==4 call cmf4zxx("blt")
pat cmf zle $1==4 call cmf4zxx("ble")
pat loc loc cff $1==INT32 && $2==INT64 /* Convert single to double */
with FSREG
yields %1.1
pat loc loc cfu $1==INT32 && $2==INT32 /* Convert single to unsigned int */
with STACK
gen
bl {LABEL, ".cfu4"}
pat loc loc cfi $1==INT32 && $2==INT32 /* Convert single to signed int */
with STACK
gen
bl {LABEL, ".cfi4"}
pat loc loc cif $1==INT32 && $2==INT32 /* Convert integer to single */
with STACK
gen
bl {LABEL, ".cif4"}
pat loc loc cuf $1==INT32 && $2==INT32 /* Convert unsigned int to single */
with STACK
gen
bl {LABEL, ".cuf4"}
pat fef $1==INT32 /* Split single */
with STACK
gen
bl {LABEL, ".fef4"}
/* Double-precision */
pat zrf $1==INT64 /* Push zero */
leaving
lde ".fd_00000000"
pat adf $1==INT64 /* Add double */
with FREG FREG
uses FREG
gen
fadd %a, %2, %1
yields %a
pat sbf $1==INT64 /* Subtract double */
with FREG FREG
uses FREG
gen
fsub %a, %2, %1
yields %a
pat mlf $1==INT64 /* Multiply double */
with FREG FREG
uses reusing %1, FREG
gen
fmul %a, %2, %1
yields %a
pat dvf $1==INT64 /* Divide double */
with FREG FREG
uses reusing %1, FREG
gen
fdiv %a, %2, %1
yields %a
pat ngf $1==INT64 /* Negate double */
with FREG
uses reusing %1, FREG
gen
fneg %a, %1
yields %a
pat cmf $1==INT64 /* Compare double */
with FREG FREG
uses REG={COND_FD, %2, %1}
gen extlwi %a, %a, {CONST, 2}, {CONST, 0}
yields %a
pat cmf teq $1==8 /* Double second == top */
with FREG FREG
uses REG={COND_FD, %2, %1}
gen move {XEQ, %a}, %a
yields %a
pat cmf tne $1==8 /* Single second == top */
with FREG FREG
uses REG={COND_FD, %2, %1}
gen move {XNE, %a}, %a
yields %a
pat cmf tgt $1==8 /* Double second > top */
with FREG FREG
uses REG={COND_FD, %2, %1}
gen move {XGT, %a}, %a
yields %a
pat cmf tge $1==8 /* Double second >= top */
with FREG FREG
uses REG={COND_FD, %2, %1}
gen move {XGE, %a}, %a
yields %a
pat cmf tlt $1==8 /* Double second < top */
with FREG FREG
uses REG={COND_FD, %2, %1}
gen move {XLT, %a}, %a
yields %a
pat cmf tle $1==8 /* Double second <= top */
with FREG FREG
uses REG={COND_FD, %2, %1}
gen move {XLE, %a}, %a
yields %a
proc cmf8zxx example cmf zeq
with FREG FREG STACK
uses REG
gen
fcmpo CR0, %2, %1
bxx* {LABEL, $2}
/* Pop 2 doubles, branch if... */
pat cmf zeq $1==8 call cmf8zxx("beq")
pat cmf zne $1==8 call cmf8zxx("bne")
pat cmf zgt $1==8 call cmf8zxx("bgt")
pat cmf zge $1==8 call cmf8zxx("bge")
pat cmf zlt $1==8 call cmf8zxx("blt")
pat cmf zle $1==8 call cmf8zxx("ble")
pat loc loc cff $1==INT64 && $2==INT32 /* Convert double to single */
with FREG
uses reusing %1, FSREG
gen
frsp %a, %1
yields %a
pat loc loc cfu $1==INT64 && $2==INT32 /* Convert double to unsigned int */
with STACK
gen
bl {LABEL, ".cfu8"}
pat loc loc cfi $1==INT64 && $2==INT32 /* Convert double to signed int */
with STACK
gen
bl {LABEL, ".cfi8"}
pat loc loc cif $1==INT32 && $2==INT64 /* Convert integer to double */
with STACK
kills ALL
gen
bl {LABEL, ".cif8"}
pat loc loc cuf $1==INT32 && $2==INT64 /* Convert unsigned int to double */
with STACK
gen
bl {LABEL, ".cuf8"}
pat fef $1==INT64 /* Split exponent, fraction */
with GPR3 GPR4
kills ALL
gen
bl {LABEL, ".fef8"}
yields R4 R3 R5
pat fif $1==INT64 /* Multiply then split integer, fraction */
with FPR1 FPR2
kills ALL
gen
bl {LABEL, ".fif8"}
yields F1 F2