REGISTERS

    /* Registers are allocated top down; the order here is odd in order to make
     * sure that non-volatile registers get allocated from r31 (or f31) down.
     *
     * Attributes may have at most one of: int, float, long, double. These
     * indicate that the register is used to store a value of that type. If
     * your register can store more than one type, create an alias. Registers
     * with none of these cannot be copied by the code generator (and so cannot
     * be moved from register to register or spilt).
     */

    r12                                           int volatile; 
    r11                                           int volatile; 
    r10                                           int volatile; 
    r9                                            int volatile;
    r8                                            int volatile;
    r7                                            int volatile;
    r6                                            int volatile;
    r5                                            int volatile;
    r4                                            int volatile;
    r3                                            int volatile ret;

    r31                                           int; 
    r30                                           int; 
    r29                                           int; 
    r28                                           int; 
    r27                                           int; 
    r26                                           int; 
    r25                                           int; 
    r24                                           int; 
    r23                                           int; 
    r22                                           int; 
    r21                                           int; 
    r20                                           int; 
    r19                                           int; 
    r18                                           int; 
    r17                                           int; 
    r16                                           int;
    r15                                           int;
    r14                                           int;
    r13                                           int;

    r11r12  named("r11", "r12") aliases(r11, r12) long volatile;
    r9r10   named("r9",  "r10") aliases(r9,  r10) long volatile;
    r7r8    named("r7",  "r8")  aliases(r7,  r8)  long volatile;
    r5r6    named("r5",  "r6")  aliases(r6,  r6)  long volatile;
    r3r4    named("r3",  "r4")  aliases(r3,  r4)  long volatile lret;

    r29r30  named("r29", "r30") aliases(r29, r30) long;
    r27r28  named("r27", "r28") aliases(r27, r28) long;
    r25r26  named("r25", "r26") aliases(r25, r26) long;
    r23r24  named("r23", "r24") aliases(r23, r24) long;
    r21r22  named("r21", "r22") aliases(r21, r22) long;
    r19r20  named("r19", "r20") aliases(r19, r20) long;
    r17r18  named("r17", "r18") aliases(r17, r18) long;
    r15r16  named("r15", "r16") aliases(r15, r16) long;
    r13r14  named("r13", "r14") aliases(r13, r14) long;

    f14                                           float volatile;
    f13                                           float volatile;
    f12                                           float volatile;
    f11                                           float volatile;
    f10                                           float volatile;
    f9                                            float volatile;
    f8                                            float volatile;
    f7                                            float volatile;
    f6                                            float volatile;
    f5                                            float volatile;
    f4                                            float volatile;
    f3                                            float volatile fret;
    f2                                            float volatile;
    f1                                            float volatile;
    f0                                            float volatile;

    f31                                           float;
    f30                                           float;
    f29                                           float;
    f28                                           float;
    f27                                           float;
    f26                                           float;
    f25                                           float;
    f24                                           float;
    f23                                           float;
    f22                                           float;
    f21                                           float;
    f20                                           float;
    f19                                           float;
    f18                                           float;
    f17                                           float;
    f16                                           float;
    f15                                           float;

    d14     named("f14")       aliases(f14)       double volatile;
    d13     named("f13")       aliases(f13)       double volatile;
    d12     named("f12")       aliases(f12)       double volatile;
    d11     named("f11")       aliases(f11)       double volatile;
    d10     named("f10")       aliases(f10)       double volatile;
    d9      named("f9")        aliases(f9)        double volatile;
    d8      named("f8")        aliases(f8)        double volatile;
    d7      named("f7")        aliases(f7)        double volatile;
    d6      named("f6")        aliases(f6)        double volatile;
    d5      named("f5")        aliases(f5)        double volatile;
    d4      named("f4")        aliases(f4)        double volatile;
    d3      named("f3")        aliases(f3)        double volatile dret;
    d2      named("f2")        aliases(f2)        double volatile;
    d1      named("f1")        aliases(f1)        double volatile;
    d0      named("f0")        aliases(f0)        double volatile;

    d31     named("f31")       aliases(f31)       double;
    d30     named("f30")       aliases(f30)       double;
    d29     named("f29")       aliases(f29)       double;
    d28     named("f28")       aliases(f28)       double;
    d27     named("f27")       aliases(f27)       double;
    d26     named("f26")       aliases(f26)       double;
    d25     named("f25")       aliases(f25)       double;
    d24     named("f24")       aliases(f24)       double;
    d23     named("f23")       aliases(f23)       double;
    d22     named("f22")       aliases(f22)       double;
    d21     named("f21")       aliases(f21)       double;
    d20     named("f20")       aliases(f20)       double;
    d19     named("f19")       aliases(f19)       double;
    d18     named("f18")       aliases(f18)       double;
    d17     named("f17")       aliases(f17)       double;
    d16     named("f16")       aliases(f16)       double;
    d15     named("f15")       aliases(f15)       double;

	cr0                                           cr;



DECLARATIONS

	cr;
    ubyteX;   /* bottom 8 bits valid, the rest undefined */
    ubyte0;   /* bottom 8 bits valid, the rest 0 */
    ushortX;  /* bottom 16 bits valid, the rest undefined */
    ushort0;  /* bottom 16 bits valid, the rest 0 */

	address fragment;



PATTERNS

/* Special */

	PAIR(BLOCK.I, BLOCK.I);



/* Miscellaneous special things */

	PUSH.I(in:(int)reg)
		emit "stwu %in, -4(sp)"
		cost 4;

    PUSH.L(in:(long)reg)
        emit "stwu %in.0, -4(sp)"
        emit "stwu %in.1, -4(sp)"
        cost 8;

    PUSH.D(in:(double)reg)
        emit "stfdu %in, -8(sp)"
        cost 4;

	out:(int)reg = POP.I
		emit "lwz %out, 0(sp)"
        emit "addi sp, sp, 4"
		cost 8;

    out:(long)reg = POP.L
        emit "lwz %out.0, 4(sp)"
        emit "lwz %out.1, 0(sp)"
        emit "addi sp, sp, 8"
        cost 12;

	out:(float)reg = POP.F
		emit "lfs %out, 0(sp)"
		emit "addi sp, sp, 4"
		cost 8;
		
	out:(double)reg = POP.D
		emit "lfd %out, 0(sp)"
		emit "addi sp, sp, 8"
		cost 8;
		
	SETRET.I(in:(ret)reg)
		emit "! setret4"
		cost 1;

    SETRET.L(in:(lret)reg)
        emit "! setret8"
        cost 1;

	STACKADJUST.I(delta:CONST.I)
        when signed_constant(%delta, 16)
		emit "addi sp, sp, $delta"
		cost 4;

	STACKADJUST.I(in:(int)reg)
		emit "add sp, sp, %in"
		cost 4;

	STACKADJUST.I(NEG.I(in:(int)reg))
		emit "subf sp, %in, sp"
		cost 4;

    out:(int)reg = GETFP.I
        emit "mr %out, fp"
        cost 4;

    SETFP.I(in:(int)reg)
        emit "mr fp, %in"
        cost 4;

    out:(int)reg = CHAINFP.I(in:(int)reg)
        emit "lwz %out, 0(%in)"
        cost 4;

    out:(int)reg = FPTOAB.I(GETFP.I)
        emit "addi %out, fp, 8"
        cost 4;

    out:(int)reg = FPTOAB.I(in:(int)reg)
        emit "addi %out, %in, 8"
        cost 4;

    out:(int)reg = FPTOLB.I(in:(int)reg)
        with %out == %in
        cost 1;

    out:(int)reg = GETSP.I
        emit "mr %out, sp"
        cost 4;

    SETSP.I(in:(int)reg)
        emit "mr sp, %in"
        cost 4;
    
    out:(int)reg = ANY.I
        cost 1;

    out:(int)reg = COPYF.I(in:(float)reg)
        emit "stfsu %in, -4(sp)"
        emit "lwz %out, 0(sp)"
        emit "addi sp, sp, 4"
        cost 12;

    out:(double)reg = COPYL.D(in:(long)reg)
        emit "stwu %in.0, -4(sp)"
        emit "stwu %in.1, -4(sp)"
        emit "lfd %out, 0(sp)"
        emit "addi sp, sp, 8"
        cost 16;

    out:(long)reg = COPYD.L(in:(double)reg)
        emit "stfdu %in, -8(sp)"
        emit "lwz %out.0, 4(sp)"
        emit "lwz %out.1, 0(sp)"
        emit "addi sp, sp, 8"
        cost 16;



/* Memory operations */

    /* Stores */

	STORE.D(addr:address, value:(double)reg)
        emit "stfd %value, %addr"
		cost 4;

	STORE.L(addr:address, value:(long)reg)
		emit "stw %value.0, 4+%addr"
		emit "stw %value.1, 0+%addr"
		cost 8;

	STORE.I(addr:address, value:(int)reg)
		emit "stw %value, %addr"
		cost 4;

	STOREH.I(addr:address, value:(int)ushortX)
		emit "sth %value, %addr"
		cost 4;

    STOREH.I(ADD.I(left:(int)reg, right:(int)reg), value:(int)ushortX)
        emit "sthx %value, %left, %right"
        cost 4;

	STOREB.I(addr:address, value:(int)ushortX)
		emit "sth %value, %addr"
		cost 4;

    STOREB.I(addr:address, value:(int)ubyteX)
		emit "stb %value, %addr"
		cost 4;

    STOREB.I(ADD.I(left:(int)reg, right:(int)reg), value:(int)ubyteX)
        emit "stbx %value, %left, %right"
        cost 4;

    /* Loads */

	out:(int)reg = LOAD.I(addr:address)
		emit "lwz %out, %addr"
		cost 4;

    out:(long)reg = LOAD.L(addr:address)
        emit "lwz %out.0, 4+%addr"
        emit "lwz %out.1, 0+%addr"
        cost 8;

	out:(int)ushort0 = LOADH.I(addr:address)
		emit "lhz %out, %addr"
		cost 4;

	out:(int)ubyte0 = LOADB.I(addr:address)
		emit "lbz %out, %addr"
		cost 4;

    /* ubyte intrinsics */

    out:(int)ubyteX = in:(int)ubyte0
        with %out == %in
        emit "! ubyte0 -> ubyteX"
        cost 1;

    out:(int)ubyte0 = in:(int)ubyteX
        emit "andi %out, %in, 0xff ! ubyteX -> ubyte0"
        cost 4;

    out:(int)reg = in:(int)ubyte0
        with %out == %in
        emit "! ubyte0 -> reg"
        cost 4;

    out:(int)ubyteX = in:(int)reg
        with %out == %in
        emit "! reg -> ubyteX"
        cost 1;

    /* ushort intrinsics */

    out:(int)ushortX = in:(int)ushort0
        with %out == %in
        emit "! ushort0 -> ushortX"
        cost 1;

    out:(int)ushort0 = in:(int)ushortX
        emit "andi %out, %in, 0xff ! ushortX -> ushort0"
        cost 4;

    out:(int)reg = in:(int)ushort0
        with %out == %in
        emit "! ushort0 -> reg"
        cost 4;

    out:(int)ushortX = in:(int)reg
        with %out == %in
        emit "! reg -> ushortX"
        cost 1;


/* Extensions and conversions */

    out:(int)reg = EXTENDB.I(in:(int)reg)
        emit "extsb %out, %in"
        cost 4;

    out:(int)reg = EXTENDH.I(in:(int)reg)
        emit "extsh %out, %in"
        cost 4;

    out:(int)reg = FROMSI.I(in:(int)reg)
        with %out == %in
        emit "! FROMSI.I(int) -> int"
        cost 1;

    out:(int)reg = FROMUI.I(in:(int)reg)
        with %out == %in
        emit "! FROMUI.I(int) -> int"
        cost 1;

    out:(long)reg = FROMSI.L(in:(int)reg)
        emit "mr %out.0, %in"
        emit "srawi %out.1, %out.0, 31"
        cost 8;

    out:(long)reg = FROMUI.L(in:(int)reg)
        emit "mr %out.0, %in"
        emit "li32 %out.1, 0"
        cost 8;

    out:(ret)reg = FROMF.I(in:(dret)reg)
        with corrupted(volatile)
        emit "bl .fromf2i"
        cost 4;

    out:(ret)reg = FROMD.I(in:(dret)reg)
        with corrupted(volatile)
        emit "bl .fromd2i"
        cost 4;

    out:(lret)reg = FROMF.L(in:(fret)reg)
        with corrupted(volatile)
        emit "bl .fromf2l"
        cost 4;

    out:(dret)reg = FROMSI.D(in:(ret)reg)
        with corrupted(volatile)
        emit "bl .fromsi2d"
        cost 4;

    out:(fret)reg = FROMUI.F(in:(ret)reg)
        with corrupted(volatile)
        emit "bl .fromui2f"
        cost 4;

    out:(dret)reg = FROMUI.D(in:(ret)reg)
        with corrupted(volatile)
        emit "bl .fromui2d"
        cost 4;

    out:(lret)reg = FROMIPAIR.L(in1:(int)reg, in2:(int)reg)
        emit "mr %out.0, %in1"
        emit "mr %out.1, %in2"
        cost 8;
    
    out:(int)reg = FROML0.I(in:(long)reg)
        emit "mr %out, %in.0"
        cost 4;

    out:(int)reg = FROML1.I(in:(long)reg)
        emit "mr %out, %in.1"
        cost 4;



/* Locals */

	out:(int)reg = in:LOCAL.I
		emit "addi %out, fp, $in"
		cost 4;

	address = in:LOCAL.I
		emit "$in(fp)";




/* Memory addressing modes */

	address = ADD.I(addr:(int)reg, offset:CONST.I)
        when signed_constant(%offset, 16)
		emit "$offset(%addr)";

	address = addr:(int)reg
		emit "0(%addr)";



/* Branches */

	JUMP(addr:BLOCK.I)
		emit "b $addr"
		cost 4;

    JUMP(dest:(int)reg)
        emit "mtspr ctr, %dest"
        emit "bcctrl 20, 0, 0"
        cost 8;

	CJUMPEQ(value:(cr)cr, PAIR(true:BLOCK.I, false:BLOCK.I))
        emit "bc 12, 2, $true" /* IFTRUE EQ */
		emit "b $false"
		cost 8;

	CJUMPLE(value:(cr)cr, PAIR(true:BLOCK.I, false:BLOCK.I))
        emit "bc 4, 1, $true" /* IFFALSE GT */
        emit "b $false"
		cost 8;

	CJUMPLT(value:(cr)cr, PAIR(true:BLOCK.I, false:BLOCK.I))
        emit "bc 12, 0, $true" /* IFTRUE LT */
        emit "b $false"
		cost 8;

    #define CALLLABEL(insn) \
        insn (dest:LABEL.I) \
            with corrupted(volatile) \
            emit "bl $dest" \
            cost 4;

    CALLLABEL(CALL)
    out:(int)reg = CALLLABEL(CALL.I)
    out:(long)reg = CALLLABEL(CALL.L)

    #define CALLINDIRECT(insn) \
        insn (dest:(int)reg) \
            with corrupted(volatile) \
            emit "mtspr ctr, %dest" \
            emit "bcctrl 20, 0, 0" \
            cost 8;

    CALLINDIRECT(CALL)
    out:(int)reg = CALLINDIRECT(CALL.I)
    out:(long)reg = CALLINDIRECT(CALL.L)

    JUMP(dest:LABEL.I)
        emit "b $dest"
        cost 4;



/* Comparisons */

	cr:(cr)cr = COMPARESI.I(left:(int)reg, right:(int)reg)
        emit "cmp %cr, 0, %left, %right"
		cost 4;

	cr:(cr)cr = COMPARESI.I(left:(int)reg, right:CONST.I)
        when signed_constant(%right, 16)
        emit "cmpi %cr, 0, %left, $right"
		cost 4;

	cr:(cr)cr = COMPAREUI.I(left:(int)reg, right:(int)reg)
        emit "cmpl %cr, 0, %left, %right"
		cost 4;

	cr:(cr)cr = COMPAREUI.I(left:(int)reg, right:CONST.I)
        when signed_constant(%right, 16)
        emit "cmpli %cr, 0, %left, $right"
		cost 4;

    out:(cr)cr = COMPARESI.I(in:(cr)cr, result:CONST.I)
        when specific_constant(%result, 0)
        with %out == %in
        emit "! COMPARESI.I(cr, 0)"
        cost 4;



/* Booleans */

    out:(int)reg = IFEQ.I(in:(cr)cr)
        emit "mfcr %out" /* get cr0 */
        emit "rlwinm %out, %out, [32-2], 2, 31" /* extract just EQ */
        cost 8;

    out:(int)reg = IFEQ.I(in:(int)reg)
        emit "cntlzw %out, %in" /* returns 0..32 */
        emit "rlwinm %out, %out, [32-5], 5, 31" /* if 32, return 1, otherwise 0 */
        cost 8;

    out:(int)reg = IFLT.I(in:(cr)cr)
        emit "mfcr %out" /* get cr0 */
        emit "andi. %out, %out, 1" /* leave just LT */
        cost 8;

    out:(int)reg = IFLE.I(in:(cr)cr)
        emit "mfcr %out"                        /* get cr0 */
        emit "andi. %out, %out, 5"              /* leave just LT and EQ */
        emit "cntlzw %out, %out"                /* returns 0..32 */
        emit "rlwinm %out, %out, [32-5], 5, 31" /* if 32, return 1, otherwise 0 */
        emit "xori %out, %out, 1"               /* negate */
        cost 8;



/* Conversions */

#if 0
    out:(int)reg = CIU44(in:(int)reg)
        with %out == %in
        emit "! ciu44"
        cost 4;

    out:(int)reg = CUI44(in:(int)reg)
        with %out == %in
        emit "! cui44"
        cost 4;
#endif

/* ALU operations */

    #define ALUR(name, instr) \
        out:(int)reg = name(left:(int)reg, right:(int)reg) \
            emit instr " %out, %left, %right"              \
            cost 4;                                        \

    #define ALUC(name, instr) \
        out:(int)reg = name(left:(int)reg, right:CONST.I)  \
            when signed_constant(%right, 16)               \
            emit instr " %out, %left, $right"              \
            cost 4;                                        \

    #define ALUC_reversed(name, instr) \
        out:(int)reg = name(left:CONST.I, right:(int)reg)  \
            when signed_constant(%left, 16)                \
            emit instr " %out, %right, $left"              \
            cost 4;                                        \

    #define ALUCC(name, instr) \
        ALUC(name, instr) \
        ALUC_reversed(name, instr)

    ALUR(ADD.I, "add")
    ALUCC(ADD.I, "addi")

	out:(int)reg = SUB.I(left:(int)reg, right:(int)reg)
		emit "subf %out, %left, %right"
		cost 4;

	out:(int)reg = SUB.I(left:(int)reg, right:CONST.I)
		emit "addi %out, %left, -[$right]"
		cost 4;

	out:(int)reg = MOD.I(left:(int)reg, right:(int)reg)
        with preserved(%left), preserved(%right)
		emit "divw %out, %left, %right"
        emit "mullw %out, %out, %right"
        emit "subf %out, %out, %left"
		cost 12;

	out:(int)reg = MODU.I(left:(int)reg, right:(int)reg)
        with preserved(%left), preserved(%right)
		emit "divwu %out, %left, %right"
        emit "mullw %out, %out, %right"
        emit "subf %out, %out, %left"
		cost 12;

    ALUR(MUL.I, "mullw")
    ALUCC(MUL.I, "mulli")

    ALUR(DIV.I, "divw")
    ALUR(DIVU.I, "divwu")

    ALUR(ASL.I, "slw")
    ALUR(ASR.I, "sraw")

    ALUR(LSL.I, "slw")
    ALUR(LSR.I, "srw")

    out:(int)reg = NEG.I(left:(int)reg)
        emit "neg %out, %left"
        cost 4;

    out:(int)reg = NOT.I(left:(int)reg)
        emit "cntlzw %out, %left"
        emit "rlwinm %out, %out, 32-5, 5, 31"
        cost 8;

    ALUR(AND.I, "and")
    ALUCC(AND.I, "andi.")

    ALUR(OR.I, "or")
    ALUCC(OR.I, "ori")

    ALUR(EOR.I, "xor")
    ALUCC(EOR.I, "xori")

	out:(int)reg = value:LABEL.I
		emit "li32 %out, $value"
		cost 4;

	out:(int)reg = value:BLOCK.I
		emit "li32 %out, $value"
		cost 4;

	out:(int)reg = value:CONST.I
		emit "li32 %out, $value"
		cost 8;


/* FPU operations */

    #define FPU4R(name, instr) \
        out:(float)reg = name(left:(float)reg, right:(float)reg) \
            emit instr " %out, %left, %right"                    \
            cost 4;                                              \

    #define FPU8R(name, instr) \
        out:(double)reg = name(left:(double)reg, right:(double)reg) \
            emit instr " %out, %left, %right"                       \
            cost 4;                                                 \

    out:(float)reg = LOAD.F(addr:address)
        emit "lfs %out, %addr"
        cost 4;
        
    out:(double)reg = LOAD.D(addr:address)
        emit "lfd %out, %addr"
        cost 4;

    out:(float)reg = in:CONST.F
        when specific_constant(%in, 0)
        emit "li32 r0, .fd_00000000"
        emit "lfs %out, 0(r0)"
        cost 12;

    FPU4R(ADDF.F, "fadds")
    FPU8R(ADDF.D, "fadd")

    FPU4R(SUBF.F, "fsubs")
    FPU8R(SUBF.D, "fsub")

    FPU4R(MULF.F, "fmuls")
    FPU8R(MULF.D, "fmul")

    FPU4R(DIVF.F, "fdivs")
    FPU8R(DIVF.D, "fdiv")

    out:(double)reg = ADDF.D(MULF.D(m1:(double)reg, m2:(double)reg), m3:(double)reg)
        emit "fmadd %out, %m1, %m2, %m3"
        cost 4;

    out:(double)reg = ADDF.D(m3:(double)reg, MULF.D(m1:(double)reg, m2:(double)reg))
        emit "fmadd %out, %m1, %m2, %m3"
        cost 4;

    out:(float)reg = NEGF.F(left:(float)reg)
        emit "fneg %out, %left"
        cost 4;

    out:(double)reg = NEGF.D(left:(double)reg)
        emit "fneg %out, %left"
        cost 4;

	cr:(cr)cr = COMPAREF.I(left:(float)reg, right:(float)reg)
        emit "fcmpu %cr, %left, %right"
		cost 4;

	cr:(cr)cr = COMPARED.I(left:(double)reg, right:(double)reg)
        emit "fcmpu %cr, %left, %right"
		cost 4;

/* vim: set sw=4 ts=4 expandtab : */