--- /dev/null
+#include "mcg.h"
+
+/* mcg stack frames are laid out as:
+ *
+ * | ...params...
+ * | --------------- <- ab
+ * | old FR
+ * | old FP
+ * | --------------- <- fp (a.k.a. lb)
+ * | locals
+ * | ---------------
+ * | spills
+ * | --------------- <- sb
+ * | saved regs
+ * | --------------- <- sp, rb
+ * V ...user area...
+ *
+ * st indexes up; lb indexes down.
+ *
+ * Note that [fp] == old_fp and ab == fp + 8.
+ */
+
+static ARRAYOF(struct hreg) saved_regs;
+
+void platform_calculate_offsets(void)
+{
+ int i;
+
+ saved_regs.count = 0;
+ for (i=0; i<current_proc->usedregs.count; i++)
+ {
+ struct hreg* hreg = current_proc->usedregs.item[i];
+
+ if (!(hreg->attrs & burm_volatile_ATTR) &&
+ ((hreg->attrs & burm_long_ATTR) || (hreg->attrs & burm_double_ATTR)))
+ {
+ hreg->offset = current_proc->saved_size;
+ current_proc->saved_size += 8;
+ array_append(&saved_regs, hreg);
+ }
+ }
+ for (i=0; i<current_proc->usedregs.count; i++)
+ {
+ struct hreg* hreg = current_proc->usedregs.item[i];
+
+ if (!(hreg->attrs & burm_volatile_ATTR) &&
+ ((hreg->attrs & burm_int_ATTR) || (hreg->attrs & burm_float_ATTR)))
+ {
+ hreg->offset = current_proc->saved_size;
+ current_proc->saved_size += 4;
+ array_append(&saved_regs, hreg);
+ }
+ }
+
+ current_proc->fp_to_ab = 8;
+ current_proc->fp_to_lb = 0;
+ current_proc->fp_to_sb = -(current_proc->locals_size + current_proc->spills_size);
+ current_proc->fp_to_rb = current_proc->fp_to_sb - current_proc->saved_size;
+}
+
+struct hop* platform_prologue(void)
+{
+ int i;
+ int spoffset = current_proc->saved_size + current_proc->spills_size +
+ current_proc->locals_size;
+ struct hop* hop = new_hop(current_proc->entry, NULL);
+
+ hop_add_insel(hop, "! locals_size = %d", current_proc->locals_size);
+ hop_add_insel(hop, "! spills_size = %d", current_proc->spills_size);
+ hop_add_insel(hop, "! saved_size = %d", current_proc->saved_size);
+ hop_add_insel(hop, "! params @ fp+%d", current_proc->fp_to_ab);
+ hop_add_insel(hop, "! lr @ fp+4");
+ hop_add_insel(hop, "! fp @ fp+0");
+ hop_add_insel(hop, "! locals @ fp-%d to fp+0",
+ current_proc->locals_size);
+ hop_add_insel(hop, "! spills @ fp-%d to fp-%d",
+ -current_proc->fp_to_sb, current_proc->locals_size);
+ for (i=saved_regs.count-1; i>=0; i--)
+ {
+ struct hreg* hreg = saved_regs.item[i];
+ hop_add_insel(hop, "! %s @ fp-%d",
+ hreg->id, -(current_proc->fp_to_rb + hreg->offset));
+ }
+
+ hop_add_insel(hop, "addiu sp, sp, %d", -(spoffset + 8));
+ hop_add_insel(hop, "sw fp, %d(sp)", spoffset + 0);
+ hop_add_insel(hop, "sw ra, %d(sp)", spoffset + 4);
+ hop_add_insel(hop, "addiu fp, sp, %d", spoffset);
+
+ /* Saved reg offsets are negative. */
+ for (i=0; i<saved_regs.count; i++)
+ {
+ struct hreg* hreg = saved_regs.item[i];
+ if (hreg->attrs & burm_int_ATTR)
+ hop_add_insel(hop, "sw %H, %d(fp)",
+ hreg, current_proc->fp_to_rb + hreg->offset);
+ else if (hreg->attrs & burm_float_ATTR)
+ hop_add_insel(hop, "swc1 %H, %d(fp)",
+ hreg, current_proc->fp_to_rb + hreg->offset);
+ }
+ return hop;
+}
+
+struct hop* platform_epilogue(void)
+{
+ struct hop* hop = new_hop(current_proc->exit, NULL);
+ int i;
+
+ for (i=0; i<saved_regs.count; i++)
+ {
+ struct hreg* hreg = saved_regs.item[i];
+ if (hreg->attrs & burm_int_ATTR)
+ hop_add_insel(hop, "lw %H, %d(fp)",
+ hreg, current_proc->fp_to_rb + hreg->offset);
+ else if (hreg->attrs & burm_float_ATTR)
+ hop_add_insel(hop, "lwc1 %H, %d(fp)",
+ hreg, current_proc->fp_to_rb + hreg->offset);
+ }
+
+ hop_add_insel(hop, "lw ra, 4(fp)");
+ hop_add_insel(hop, "lw at, 0(fp)"); /* load old fp */
+ hop_add_insel(hop, "addiu sp, fp, %d", current_proc->fp_to_ab);
+ hop_add_insel(hop, "move fp, at");
+ hop_add_insel(hop, "jr ra");
+
+ return hop;
+}
+
+struct hop* platform_move(struct basicblock* bb, struct hreg* src, struct hreg* dest)
+{
+ struct hop* hop = new_hop(bb, NULL);
+
+ if ((src->attrs & TYPE_ATTRS) != (dest->attrs & TYPE_ATTRS))
+ {
+ assert(!src->is_stacked);
+ assert(!dest->is_stacked);
+
+ switch (src->attrs & TYPE_ATTRS)
+ {
+ case burm_int_ATTR:
+ hop_add_insel(hop, "stwu %H, -4(sp)", src);
+ break;
+
+ case burm_long_ATTR:
+ hop_add_insel(hop, "stwu %0H, -4(sp)", src);
+ hop_add_insel(hop, "stwu %1H, -4(sp)", src);
+ break;
+
+ case burm_float_ATTR:
+ hop_add_insel(hop, "stfsu %H, -4(sp)", src);
+ break;
+
+ case burm_double_ATTR:
+ hop_add_insel(hop, "stfdu %H, -8(sp)", src);
+ break;
+
+ default:
+ goto nomove;
+ }
+
+ switch (dest->attrs & TYPE_ATTRS)
+ {
+ case burm_int_ATTR:
+ hop_add_insel(hop, "lwz %H, 0(sp)", dest);
+ break;
+
+ case burm_long_ATTR:
+ hop_add_insel(hop, "lwz %0H, 4(sp)", dest);
+ hop_add_insel(hop, "lwz %1H, 0(sp)", dest);
+ break;
+
+ case burm_float_ATTR:
+ hop_add_insel(hop, "lfs %H, 0(sp)", dest);
+ break;
+
+ case burm_double_ATTR:
+ hop_add_insel(hop, "lfd %H, 0(sp)", dest);
+ break;
+
+ default:
+ goto nomove;
+ }
+
+ switch (dest->attrs & TYPE_ATTRS)
+ {
+ case burm_int_ATTR:
+ case burm_float_ATTR:
+ hop_add_insel(hop, "addi sp, sp, 4");
+ break;
+
+ case burm_double_ATTR:
+ case burm_long_ATTR:
+ hop_add_insel(hop, "addi sp, sp, 8");
+ break;
+
+ default:
+ goto nomove;
+ }
+ }
+ else
+ {
+ uint32_t type = src->attrs & TYPE_ATTRS;
+
+ if (!src->is_stacked && dest->is_stacked)
+ {
+ switch (type)
+ {
+ case burm_int_ATTR:
+ hop_add_insel(hop, "stw %H, %S(fp) ! %H", src, dest, dest);
+ break;
+
+ case burm_float_ATTR:
+ hop_add_insel(hop, "stfs %H, %S(fp) ! %H", src, dest, dest);
+ break;
+
+ case burm_long_ATTR:
+ hop_add_insel(hop, "stw %0H, 4+%S(fp) ! %H", src, dest, dest);
+ hop_add_insel(hop, "stw %1H, 0+%S(fp) ! %H", src, dest, dest);
+ break;
+
+ case burm_double_ATTR:
+ hop_add_insel(hop, "stfd %H, %S(fp) ! %H", src, dest, dest);
+ break;
+
+ default:
+ goto nomove;
+ }
+ }
+ else if (src->is_stacked && !dest->is_stacked)
+ {
+ switch (type)
+ {
+ case burm_int_ATTR:
+ hop_add_insel(hop, "lwz %H, %S(fp) ! %H", dest, src, src);
+ break;
+
+ case burm_float_ATTR:
+ hop_add_insel(hop, "lfs %H, %S(fp) ! %H", dest, src, src);
+ break;
+
+ case burm_double_ATTR:
+ hop_add_insel(hop, "lfd %H, %S(fp) ! %H", dest, src, src);
+ break;
+
+ default:
+ goto nomove;
+ }
+ }
+ else if (!src->is_stacked && !dest->is_stacked)
+ {
+ switch (type)
+ {
+ case burm_int_ATTR:
+ hop_add_insel(hop, "move %H, %H", dest, src);
+ break;
+
+ case burm_long_ATTR:
+ hop_add_insel(hop, "move %0H, %0H", dest, src);
+ hop_add_insel(hop, "move %1H, %1H", dest, src);
+ break;
+
+ case burm_float_ATTR:
+ case burm_double_ATTR:
+ hop_add_insel(hop, "fmr %H, %H", dest, src);
+ break;
+
+ default:
+ goto nomove;
+ }
+ }
+ else
+ goto nomove;
+ }
+
+ return hop;
+
+nomove:
+ fatal("cannot move %s to %s", src->id, dest->id);
+}
+
+struct hop* platform_swap(struct basicblock* bb, struct hreg* src, struct hreg* dest)
+{
+ struct hop* hop = new_hop(bb, NULL);
+
+ assert(!src->is_stacked);
+ assert(!dest->is_stacked);
+ assert((src->attrs & TYPE_ATTRS) == (dest->attrs & TYPE_ATTRS));
+
+ switch (src->attrs & TYPE_ATTRS)
+ {
+ case burm_int_ATTR:
+ hop_add_insel(hop, "mr r0, %H", src);
+ hop_add_insel(hop, "mr %H, %H", src, dest);
+ hop_add_insel(hop, "mr %H, r0", dest);
+ break;
+
+ case burm_long_ATTR:
+ hop_add_insel(hop, "mr r0, %0H", src);
+ hop_add_insel(hop, "mr %0H, %0H", src, dest);
+ hop_add_insel(hop, "mr %0H, r0", dest);
+
+ hop_add_insel(hop, "mr r0, %1H", src);
+ hop_add_insel(hop, "mr %1H, %1H", src, dest);
+ hop_add_insel(hop, "mr %1H, r0", dest);
+ break;
+
+ case burm_float_ATTR:
+ case burm_double_ATTR:
+ hop_add_insel(hop, "fmr f0, %H", src);
+ hop_add_insel(hop, "fmr %H, %H", src, dest);
+ hop_add_insel(hop, "fmr %H, f0", dest);
+ break;
+ }
+
+ return hop;
+}
+
+const char* platform_label(const char* label)
+{
+ /* Labels starting with . are internal, not exported, and don't need mangling. */
+
+ if (label[0] == '.')
+ return label;
+
+ /* Otherwise, mangle. */
+
+ return aprintf("_%s", label);
+}
+
+/* vim: set sw=4 ts=4 expandtab : */
+
--- /dev/null
+REGISTERS
+
+ /* Registers are allocated top down. The odd order below is to make sure
+ * that cheap registers get allocated first.
+ *
+ * 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).
+ */
+
+ r4 named("r4") int volatile;
+ r5 named("r5") int volatile;
+ r6 named("r6") int volatile;
+ r7 named("r7") int volatile;
+ r8 named("r8") int volatile;
+ r9 named("r9") int volatile;
+ r10 named("r10") int volatile;
+ r11 named("r11") int volatile;
+ r12 named("r12") int volatile;
+ r13 named("r13") int volatile;
+ r14 named("r14") int volatile;
+ r15 named("r15") int volatile;
+ r24 named("r24") int volatile;
+ r25 named("r25") int volatile;
+ r2 named("r2") int volatile iret;
+ r3 named("r3") int volatile;
+
+ r17 named("r16") int;
+ r18 named("r18") int;
+ r19 named("r19") int;
+ r20 named("r20") int;
+ r21 named("r21") int;
+ r22 named("r22") int;
+ r23 named("r23") int;
+
+ r4r5 named("r4", "r5") aliases(r4, r5) long volatile;
+ r6r7 named("r6", "r7") aliases(r6, r7) long volatile;
+ r8r9 named("r8", "r9") aliases(r8, r9) long volatile;
+ r10r11 named("r10", "r11") aliases(r10, r11) long volatile;
+ r12r13 named("r12", "r13") aliases(r12, r13) long volatile;
+ r14r15 named("r14", "r15") aliases(r14, r15) long volatile;
+ r24r25 named("r24", "r25") aliases(r24, r25) long volatile;
+ r2r3 named("r2", "r3") aliases(r2, r3) long volatile lret;
+
+ zero named("zero") zero int volatile;
+
+ f0 float;
+ d0 double;
+
+DECLARATIONS
+
+ 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 "addiu sp, sp, -4"
+ emit "sw %in, 0(sp)"
+ cost 8;
+
+ PUSH.L(in:(long)reg)
+ emit "addiu sp, sp, -8"
+ emit "sw %in.0, 0(sp)"
+ emit "sw %in.1, 4(sp)"
+ cost 12;
+
+ out:(int)reg = POP.I
+ emit "lw %out, 0(sp)"
+ emit "addiu sp, sp, 4"
+ cost 8;
+
+ out:(long)reg = POP.L
+ emit "lw %out.0, 4(sp)"
+ emit "lw %out.1, 0(sp)"
+ emit "addiu sp, sp, 8"
+ cost 12;
+
+ SETRET.I(in:(iret)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 "addiu sp, sp, $delta"
+ cost 4;
+
+ STACKADJUST.I(in:(int)reg)
+ emit "addu sp, sp, %in"
+ cost 4;
+
+ STACKADJUST.I(NEG.I(in:(int)reg))
+ emit "subu sp, sp, %in"
+ cost 4;
+
+ out:(int)reg = GETFP.I
+ emit "move %out, fp"
+ cost 4;
+
+ SETFP.I(in:(int)reg)
+ emit "move fp, %in"
+ cost 4;
+
+ out:(int)reg = CHAINFP.I(in:(int)reg)
+ emit "lw %out, 0(%in)"
+ cost 4;
+
+ out:(int)reg = FPTOAB.I(GETFP.I)
+ emit "addiu %out, fp, 8"
+ cost 4;
+
+ out:(int)reg = FPTOAB.I(in:(int)reg)
+ emit "addiu %out, %in, 8"
+ cost 4;
+
+ out:(int)reg = FPTOLB.I(in:(int)reg)
+ with %out == %in
+ cost 1;
+
+ out:(int)reg = GETSP.I
+ emit "move %out, sp"
+ cost 4;
+
+ SETSP.I(in:(int)reg)
+ emit "move sp, %in"
+ cost 4;
+
+ out:(int)reg = ANY.I
+ cost 1;
+
+ out:(long)reg = ANY.L
+ cost 1;
+
+
+
+/* Memory operations */
+
+ /* Stores */
+
+ STORE.L(addr:address, value:(long)reg)
+ emit "sw %value.0, 4+%addr"
+ emit "sw %value.1, 0+%addr"
+ cost 8;
+
+ STORE.I(addr:address, value:(int)reg)
+ emit "sw %value, %addr"
+ cost 4;
+
+ STOREH.I(addr:address, value:(int)ushortX)
+ emit "sh %value, %addr"
+ cost 4;
+
+ STOREB.I(addr:address, value:(int)ubyteX)
+ emit "sb %value, %addr"
+ cost 4;
+
+ /* Loads */
+
+ out:(int)reg = LOAD.I(addr:address)
+ emit "lw %out, %addr"
+ cost 4;
+
+ /* We can't just load directly because %out.0 and %addr might share
+ * a register, resulting in %addr being corrupted before %out.1 is
+ * loaded. */
+ out:(long)reg = LOAD.L(addr:address)
+ emit "lw at, 4+%addr"
+ emit "lw %out.1, 0+%addr"
+ emit "move %out.0, at"
+ cost 12;
+
+ out:(int)ushort0 = LOADH.I(addr:address)
+ emit "lh %out, %addr"
+ cost 4;
+
+ out:(int)ubyte0 = LOADB.I(addr:address)
+ emit "lb %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 "andiu %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 "andiu %out, %in, 0xffff ! 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 "seb %out, %in"
+ cost 4;
+
+ out:(int)reg = EXTENDH.I(in:(int)reg)
+ emit "seh %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 "move %out.0, %in"
+ emit "sra %out.1, %in, 31"
+ cost 8;
+
+ out:(long)reg = FROMUI.L(in:(int)reg)
+ emit "mr %out.0, %in"
+ emit "li %out.1, 0"
+ cost 8;
+
+ out:(lret)reg = FROMIPAIR.L(in1:(int)reg, in2:(int)reg)
+ emit "move %out.0, %in1"
+ emit "move %out.1, %in2"
+ cost 8;
+
+ out:(int)reg = FROML0.I(in:(long)reg)
+ emit "move %out, %in.0"
+ cost 4;
+
+ out:(int)reg = FROML1.I(in:(long)reg)
+ emit "move %out, %in.1"
+ cost 4;
+
+
+
+/* Locals */
+
+ out:(int)reg = in:LOCAL.I
+ emit "addiu %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"
+ emit "nop"
+ cost 8;
+
+ FARJUMP(addr:LABEL.I)
+ with corrupted(volatile)
+ emit "b $addr"
+ emit "nop"
+ cost 8;
+
+ JUMP(dest:(int)reg)
+ emit "jr %dest"
+ emit "nop"
+ cost 8;
+
+ CJUMPEQ(COMPARESI.I(left:(int)reg, right:(int)reg), PAIR(true:BLOCK.I, false:BLOCK.I))
+ emit "beq %left, %right, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 16;
+
+ CJUMPLT(COMPARESI.I(left:(int)reg, right:(int)reg), PAIR(true:BLOCK.I, false:BLOCK.I))
+ emit "slt at, %left, %right"
+ emit "bne at, zero, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 20;
+
+ CJUMPLT(COMPAREUI.I(left:(int)reg, right:(int)reg), PAIR(true:BLOCK.I, false:BLOCK.I))
+ emit "sltu at, %left, %right"
+ emit "bne at, zero, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 20;
+
+ CJUMPLT(COMPARESI.I(left:(int)reg, right:CONST.I), PAIR(true:BLOCK.I, false:BLOCK.I))
+ when specific_constant(%right, 0)
+ emit "bltz %left, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 16;
+
+ CJUMPLE(COMPARESI.I(left:(int)reg, right:(int)reg), PAIR(true:BLOCK.I, false:BLOCK.I))
+ emit "sle at, %left, %right"
+ emit "bne at, zero, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 20;
+
+ CJUMPLE(COMPAREUI.I(left:(int)reg, right:(int)reg), PAIR(true:BLOCK.I, false:BLOCK.I))
+ emit "sleu at, %left, %right"
+ emit "bne at, zero, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 20;
+
+ CJUMPLE(COMPARESI.I(left:(int)reg, right:CONST.I), PAIR(true:BLOCK.I, false:BLOCK.I))
+ when specific_constant(%right, 0)
+ emit "blez %left, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 16;
+
+ COMPAREUI.I(left:(int)reg, right:(int)reg);
+
+ #define CALLLABEL(insn) \
+ insn (dest:LABEL.I) \
+ with corrupted(volatile) \
+ emit "bal $dest" \
+ emit "nop" \
+ cost 8;
+
+ CALLLABEL(CALL)
+ out:(iret)reg = CALLLABEL(CALL.I)
+ out:(lret)reg = CALLLABEL(CALL.L)
+
+ #define CALLINDIRECT(insn) \
+ insn (dest:(int)reg) \
+ with corrupted(volatile) \
+ emit "jalr %dest" \
+ emit "nop" \
+ cost 8;
+
+ CALLINDIRECT(CALL)
+ out:(iret)reg = CALLINDIRECT(CALL.I)
+ out:(lret)reg = CALLINDIRECT(CALL.L)
+
+ JUMP(dest:LABEL.I)
+ emit "b $dest"
+ emit "nop"
+ cost 8;
+
+
+
+/* Comparisons */
+
+ /* The COMPARE nodes return tristate integer values; -1, 0 or 1. */
+
+ out:(int)reg = COMPARESI.I(left:(int)reg, right:(int)reg)
+ emit "slt at, %left, %right"
+ emit "bne at, zero, 1f"
+ emit "li %out, -1"
+ emit "slt %out, %right, %left"
+ emit "1:"
+ cost 20;
+
+ out:(int)reg = COMPAREUI.I(left:(int)reg, right:(int)reg)
+ emit "sltu at, %left, %right"
+ emit "bne at, zero, 1f"
+ emit "li %out, -1"
+ emit "sltu %out, %right, %left"
+ emit "1:"
+ cost 20;
+
+/* Booleans */
+
+ /* If 0 then 1, else 0 */
+ out:(int)reg = IFEQ.I(in:(int)reg)
+ emit "sleu %out, %in, zero"
+ cost 4;;
+
+ /* If -1 then 1, else 0 */
+ out:(int)reg = IFLT.I(in:(int)reg)
+ emit "slt %out, %in, zero"
+ cost 4;
+
+ /* If 1 or 0 then 1, else 0 */
+ out:(int)reg = IFLE.I(in:(int)reg)
+ emit "sle %out, %in, zero"
+ cost 4;
+
+
+
+/* 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 */
+
+ /* reg + reg */
+ #define ALUR(name, instr) \
+ out:(int)reg = name(left:(int)reg, right:(int)reg) \
+ emit instr " %out, %left, %right" \
+ cost 4; \
+
+ /* reg + const */
+ #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; \
+
+ /* const + reg */
+ #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; \
+
+ /* reg + const AND const + reg */
+ #define ALUCC(name, instr) \
+ ALUC(name, instr) \
+ ALUC_reversed(name, instr)
+
+ ALUR(ADD.I, "addu")
+ ALUCC(ADD.I, "addiu")
+
+ out:(int)reg = SUB.I(left:(int)reg, right:(int)reg)
+ emit "subu %out, %right, %left"
+ cost 4;
+
+ out:(int)reg = SUB.I(left:(int)reg, right:CONST.I)
+ emit "addiu %out, %left, -[$right]"
+ cost 4;
+
+ out:(int)reg = MOD.I(left:(int)reg, right:(int)reg)
+ emit "div %left, %right"
+ emit "mfhi %out"
+ cost 8;
+
+ out:(int)reg = MODU.I(left:(int)reg, right:(int)reg)
+ emit "divu %left, %right"
+ emit "mfhi %out"
+ cost 8;
+
+ ALUR(MUL.I, "mul")
+
+ ALUR(DIV.I, "divw")
+ ALUR(DIVU.I, "divwu")
+
+ ALUR(ASL.I, "sll")
+ ALUC(ASL.I, "sllv")
+ ALUR(ASR.I, "sra")
+ ALUC(ASR.I, "srav")
+
+ ALUR(LSL.I, "sll")
+ ALUC(LSL.I, "sllv")
+ ALUR(LSR.I, "srl")
+ ALUC(LSR.I, "srlv")
+
+ out:(int)reg = NEG.I(left:(int)reg)
+ emit "neg %out, %left"
+ cost 4;
+
+ out:(int)reg = NOT.I(in:(int)reg)
+ emit "nor %out, %in, %in"
+ cost 4;
+
+ 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 "li %out, $value"
+ cost 4;
+
+ out:(zero)reg = value:CONST.I
+ when specific_constant(%value, 0)
+ cost 1;
+
+
+/* vim: set sw=4 ts=4 expandtab : */
+