--- /dev/null
- * | --------------- <- fp (a.k.a. lb)
+ #include "mcg.h"
+
+ /* mcg stack frames are laid out as:
+ *
+ * | ...params...
+ * | --------------- <- ab
+ * | old FR
+ * | old FP
- 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);
- }
- }
++ * | --------------- <- 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;
- hop_add_insel(hop, "! lr @ fp+4");
- hop_add_insel(hop, "! fp @ fp+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_long_ATTR)
+ {
+ hop_add_insel(hop, "sw %0H, %d(fp)",
+ hreg, current_proc->fp_to_rb + hreg->offset + 0);
+ hop_add_insel(hop, "sw %1H, %d(fp)",
+ hreg, current_proc->fp_to_rb + hreg->offset + 4);
+ }
+ else if (hreg->attrs & burm_float_ATTR)
+ hop_add_insel(hop, "swc1 %H, %d(fp)",
+ hreg, current_proc->fp_to_rb + hreg->offset);
+ else if (hreg->attrs & burm_double_ATTR)
+ hop_add_insel(hop, "sdc1 %H, %d(fp)",
+ hreg, current_proc->fp_to_rb + hreg->offset);
+ else
+ fatal("unsavable non-volatile register %s", hreg->id);
+ }
+ 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_long_ATTR)
+ {
+ hop_add_insel(hop, "lw %0H, %d(fp)",
+ hreg, current_proc->fp_to_rb + hreg->offset + 0);
+ hop_add_insel(hop, "lw %1H, %d(fp)",
+ hreg, current_proc->fp_to_rb + hreg->offset + 4);
+ }
+ else if (hreg->attrs & burm_float_ATTR)
+ hop_add_insel(hop, "lwc1 %H, %d(fp)",
+ hreg, current_proc->fp_to_rb + hreg->offset);
+ else if (hreg->attrs & burm_double_ATTR)
+ hop_add_insel(hop, "ldc1 %H, %d(fp)",
+ hreg, current_proc->fp_to_rb + hreg->offset);
+ else
+ fatal("unloadable non-volatile register %s", hreg->id);
+ }
+
+ 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, "jr ra");
+ hop_add_insel(hop, "mov fp, at"); /* delay slot */
+
+ return hop;
+ }
+
+ struct hop* platform_move(struct basicblock* bb, struct vreg* vreg, struct hreg* src, struct hreg* dest)
+ {
+ struct hop* hop = new_hop(bb, NULL);
+
+ if ((src->attrs & TYPE_ATTRS) != (dest->attrs & TYPE_ATTRS))
+ fatal("hreg move of %%%d from %s to %s with mismatched types", vreg->id, src->id, dest->id);
+ else
+ {
+ uint32_t type = src->attrs & TYPE_ATTRS;
+ tracef('R', "R: non-converting move from %s to %s of type 0x%x\n", src->id, dest->id, type);
+
+ if (!src->is_stacked && dest->is_stacked)
+ {
+ switch (type)
+ {
+ case burm_int_ATTR:
+ hop_add_insel(hop, "sw %H, %S(fp) ! %H", src, dest, dest);
+ break;
+
+ case burm_long_ATTR:
+ hop_add_insel(hop, "sw %0H, 0+%S(fp) ! %H", src, dest, dest);
+ hop_add_insel(hop, "sw %1H, 4+%S(fp) ! %H", src, dest, dest);
+ break;
+
+ case burm_float_ATTR:
+ hop_add_insel(hop, "swc1 %H, %S(fp) ! %H", src, dest, dest);
+ break;
+
+ case burm_double_ATTR:
+ hop_add_insel(hop, "sdc1 %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, "lw %H, %S(fp) ! %H", dest, src, src);
+ break;
+
+ case burm_long_ATTR:
+ /* Can't load straight into dest because it might overlap with src. */
+ hop_add_insel(hop, "lw at, 0+%S(fp) ! %H", dest, src, src);
+ hop_add_insel(hop, "lw %1H, 4+%S(fp) ! %H", dest, src, src);
+ hop_add_insel(hop, "mov %0H, at", dest);
+ break;
+
+ case burm_float_ATTR:
+ hop_add_insel(hop, "lwc1 %H, %S(fp) ! %H", dest, src, src);
+ break;
+
+ case burm_double_ATTR:
+ hop_add_insel(hop, "ldc1 %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, "mov %H, %H", dest, src);
+ break;
+
+ case burm_long_ATTR:
+ hop_add_insel(hop, "mov %0H, %0H", dest, src);
+ hop_add_insel(hop, "mov %1H, %1H", dest, src);
+ break;
+
+ case burm_float_ATTR:
+ hop_add_insel(hop, "mov.s %H, %H", dest, src);
+ break;
+
+ case burm_double_ATTR:
+ hop_add_insel(hop, "mov.d %H, %H", dest, src);
+ break;
+
+ default:
+ goto nomove;
+ }
+ }
+ else if (src->is_stacked && dest->is_stacked)
+ fatal("tried to move stacked object %%%d of type 0x%x from %s to %s", vreg->id, type, src->id, dest->id);
+ else
+ goto nomove;
+ }
+
+ return hop;
+
+ nomove:
+ fatal("cannot move %s to %s", src->id, dest->id);
++ return NULL;
+ }
+
+ struct hop* platform_swap(struct basicblock* bb, struct hreg* src, struct hreg* dest)
+ {
+ struct hop* hop = new_hop(bb, NULL);
+
+ tracef('R', "R: swap of %s to %s\n", src->id, dest->id);
+ 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, "mov at, %H", src);
+ hop_add_insel(hop, "mov %H, %H", src, dest);
+ hop_add_insel(hop, "mov %H, at", dest);
+ break;
+
+ case burm_long_ATTR:
+ hop_add_insel(hop, "mov at, %0H", src);
+ hop_add_insel(hop, "mov %0H, %0H", src, dest);
+ hop_add_insel(hop, "mov %0H, at", dest);
+
+ hop_add_insel(hop, "mov at, %1H", src);
+ hop_add_insel(hop, "mov %1H, %1H", src, dest);
+ hop_add_insel(hop, "mov %1H, at", dest);
+ break;
++
+ case burm_float_ATTR:
+ hop_add_insel(hop, "mov.s f30, %H", src);
+ hop_add_insel(hop, "mov.s %H, %H", src, dest);
+ hop_add_insel(hop, "mov.s %H, f30", dest);
+ break;
+
+ case burm_double_ATTR:
+ hop_add_insel(hop, "mov.d f30, %H", src);
+ hop_add_insel(hop, "mov.d %H, %H", src, dest);
+ hop_add_insel(hop, "mov.d %H, f30", 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
- * 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).
+ OPTIONS
+
+ LOWER_PUSHES_TO_LOADS_AND_STORES;
+
+ REGISTERS
+
+ /* Registers are allocated top down. The odd order below is to make sure
+ * that cheap registers get allocated first.
+ *
- r17 named("r16") int;
++ * Each register must be declared as having at least one type (listed above).
++ * This allows the register to contain values of that type. They may also be
++ * declared as having any number of additional attributes, which can be used
++ * to constrain register selection.
++ *
++ * Register aliases may be defined with uses(reg1, reg2, ...). These work like
++ * any other register, but they share hardware resources with some real registers.
+ */
+
+ r4 int volatile;
+ r5 int volatile;
+ r6 int volatile;
+ r7 int volatile;
+ r8 int volatile;
+ r9 int volatile;
+ r10 int volatile;
+ r11 int volatile;
+ r12 int volatile;
+ r13 int volatile;
+ r14 int volatile;
+ r15 int volatile;
+ r24 int volatile;
+ r25 int volatile;
+ r2 int volatile iret;
+ r3 int volatile;
+
- r4r5 named("r4", "r5") aliases(r4, r5) long volatile lret1;
- 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;
++ r17 named("r17") 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;
+
- r17r18 named("r17", "r18") aliases(r17, r18) long;
- r19r20 named("r19", "r20") aliases(r19, r20) long;
- r21r22 named("r21", "r22") aliases(r21, r22) long;
++ r4r5 named("r4", "r5") uses(r4, r5) long volatile lret1;
++ r6r7 named("r6", "r7") uses(r6, r7) long volatile;
++ r8r9 named("r8", "r9") uses(r8, r9) long volatile;
++ r10r11 named("r10", "r11") uses(r10, r11) long volatile;
++ r12r13 named("r12", "r13") uses(r12, r13) long volatile;
++ r14r15 named("r14", "r15") uses(r14, r15) long volatile;
++ r24r25 named("r24", "r25") uses(r24, r25) long volatile;
++ r2r3 named("r2", "r3") uses(r2, r3) long volatile lret;
+
- d0 named("f0") aliases(f0, f1) double volatile dret;
- d2 named("f2") aliases(f2, f3) double volatile;
- d4 named("f4") aliases(f4, f5) double volatile;
- d6 named("f6") aliases(f6, f7) double volatile;
- d8 named("f8") aliases(f8, f9) double volatile;
- d10 named("f10") aliases(f10, f11) double volatile;
- d12 named("f12") aliases(f12, f13) double volatile;
- d14 named("f14") aliases(f14, f15) double volatile;
- d16 named("f16") aliases(f16, f17) double volatile;
- d18 named("f18") aliases(f18, f19) double volatile;
++ r17r18 named("r17", "r18") uses(r17, r18) long;
++ r19r20 named("r19", "r20") uses(r19, r20) long;
++ r21r22 named("r21", "r22") uses(r21, r22) long;
+
+ f0 float volatile fret;
+ f1 float volatile;
+ f2 float volatile;
+ f3 float volatile;
+ f4 float volatile;
+ f5 float volatile;
+ f6 float volatile;
+ f7 float volatile;
+ f8 float volatile;
+ f9 float volatile;
+ f10 float volatile;
+ f11 float volatile;
+ f12 float volatile;
+ f13 float volatile;
+ f14 float volatile;
+ f15 float volatile;
+ f16 float volatile;
+ f17 float volatile;
+ f18 float volatile;
+ f19 float volatile;
+
+ f20 float;
+ f21 float;
+ f22 float;
+ f23 float;
+ f24 float;
+ f25 float;
+ f26 float;
+ f27 float;
+ f28 float;
+ f29 float;
+ /* f30 and f31 is used by the compiler as a temporary. */
+
- d20 named("f20") aliases(f20, f21) double;
- d22 named("f22") aliases(f22, f23) double;
- d24 named("f24") aliases(f24, f25) double;
- d26 named("f26") aliases(f26, f27) double;
- d28 named("f28") aliases(f28, f29) double;
++ d0 named("f0") uses(f0, f1) double volatile dret;
++ d2 named("f2") uses(f2, f3) double volatile;
++ d4 named("f4") uses(f4, f5) double volatile;
++ d6 named("f6") uses(f6, f7) double volatile;
++ d8 named("f8") uses(f8, f9) double volatile;
++ d10 named("f10") uses(f10, f11) double volatile;
++ d12 named("f12") uses(f12, f13) double volatile;
++ d14 named("f14") uses(f14, f15) double volatile;
++ d16 named("f16") uses(f16, f17) double volatile;
++ d18 named("f18") uses(f18, f19) double volatile;
+
- with preserved(%in1), preserved(%in2)
++ d20 named("f20") uses(f20, f21) double;
++ d22 named("f22") uses(f22, f23) double;
++ d24 named("f24") uses(f24, f25) double;
++ d26 named("f26") uses(f26, f27) double;
++ d28 named("f28") uses(f28, f29) 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;
+ intregorzero fragment;
+ byteregorzero fragment;
+ shortregorzero fragment;
+
+
+
+ PATTERNS
+
+ /* Special */
+
+ PAIR(BLOCK.I, BLOCK.I);
+
+
+
+ /* Miscellaneous special things */
+
+ 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;
+
+ out:(float)reg = POP.F
+ emit "lwc1 %out, 0(sp)"
+ emit "addiu sp, sp, 4"
+ cost 8;
+
+ out:(double)reg = POP.D
+ emit "ldc1 %out, 0(sp)"
+ emit "addiu sp, sp, 8"
+ cost 8;
+
+ SETRET.I(in:(iret)reg)
+ emit "! setret.i"
+ cost 1;
+
+ SETRET.L(in:(lret)reg)
+ emit "! setret.l"
+ cost 1;
+
+ SETRET.F(in:(fret)reg)
+ emit "! setret.f"
+ cost 1;
+
+ SETRET.D(in:(dret)reg)
+ emit "! setret.d"
+ cost 1;
+
+ STACKADJUST.I(delta:CONST.I)
+ when signed_constant(%delta, 16)
+ emit "addiu sp, sp, $delta"
+ cost 4;
+
+ STACKADJUST.I(in:intregorzero)
+ emit "addu sp, sp, %in"
+ cost 4;
+
+ STACKADJUST.I(NEG.I(in:intregorzero))
+ emit "subu sp, sp, %in"
+ cost 4;
+
+ out:(int)reg = GETFP.I
+ emit "mov %out, fp"
+ cost 4;
+
+ SETFP.I(in:(int)reg)
+ emit "mov 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 "mov %out, sp"
+ cost 4;
+
+ SETSP.I(in:(int)reg)
+ emit "mov 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, 0+%addr"
+ emit "sw %value.1, 4+%addr"
+ cost 8;
+
+ STORE.I(addr:address, value:intregorzero)
+ emit "sw %value, %addr"
+ cost 4;
+
+ STORE.I(label:LABEL.I, value:intregorzero)
+ emit "lui at, ha16[$label]"
+ emit "sw %value, lo16[$label] (at)"
+ cost 8;
+
+ STOREH.I(addr:address, value:shortregorzero)
+ emit "sh %value, %addr"
+ cost 4;
+
+ STOREH.I(label:LABEL.I, value:shortregorzero)
+ emit "lui at, ha16[$label]"
+ emit "sh %value, lo16[$label] (at)"
+ cost 8;
+
+ STOREB.I(addr:address, value:byteregorzero)
+ emit "sb %value, %addr"
+ cost 4;
+
+ STOREB.I(label:LABEL.I, value:byteregorzero)
+ emit "lui at, ha16[$label]"
+ emit "sb %value, lo16[$label] (at)"
+ cost 8;
+
+ STORE.F(addr:address, value:(float)reg)
+ emit "swc1 %value, %addr"
+ cost 4;
+
+ STORE.F(label:LABEL.I, value:(float)reg)
+ emit "lui at, ha16[$label]"
+ emit "swc1 %value, lo16[$label] (at)"
+ cost 8;
+
+ STORE.D(addr:address, value:(double)reg)
+ emit "sdc1 %value, %addr"
+ cost 4;
+
+ STORE.D(label:LABEL.I, value:(double)reg)
+ emit "lui at, ha16[$label]"
+ emit "sdc1 %value, lo16[$label] (at)"
+ cost 8;
+
+ /* Loads */
+
+ out:(int)reg = LOAD.I(addr:address)
+ emit "lw %out, %addr"
+ cost 4;
+
+ out:(int)reg = LOAD.I(label:LABEL.I)
+ emit "lui at, ha16[$label]"
+ emit "lw %out, lo16[$label] (at)"
+ cost 8;
+
+ /* 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, 0+%addr"
+ emit "lw %out.1, 4+%addr"
+ emit "mov %out.0, at"
+ cost 12;
+
+ out:(int)ushort0 = LOADH.I(addr:address)
+ emit "lhu %out, %addr"
+ cost 4;
+
+ out:(int)reg = EXTENDH.I(LOADH.I(addr:address))
+ emit "lh %out, %addr"
+ cost 4;
+
+ out:(int)ushort0 = LOADH.I(label:LABEL.I)
+ emit "lui at, ha16[$label]"
+ emit "lhu %out, lo16[$label] (at)"
+ cost 8;
+
+ out:(int)reg = EXTENDH.I(LOADH.I(label:LABEL.I))
+ emit "lui at, ha16[$label]"
+ emit "lh %out, lo16[$label] (at)"
+ cost 8;
+
+ out:(int)ubyte0 = LOADB.I(addr:address)
+ emit "lbu %out, %addr"
+ cost 4;
+
+ out:(int)reg = EXTENDB.I(LOADB.I(addr:address))
+ emit "lb %out, %addr"
+ cost 4;
+
+ out:(int)ubyte0 = LOADB.I(label:LABEL.I)
+ emit "lui at, ha16[$label]"
+ emit "lbu %out, lo16[$label] (at)"
+ cost 8;
+
+ out:(int)reg = EXTENDB.I(LOADB.I(label:LABEL.I))
+ emit "lui at, ha16[$label]"
+ emit "lb %out, lo16[$label] (at)"
+ cost 8;
+
+ out:(float)reg = LOAD.F(addr:address)
+ emit "lwc1 %out, %addr"
+ cost 4;
+
+ out:(float)reg = LOAD.F(label:LABEL.I)
+ emit "lui at, ha16[$label]"
+ emit "lwc1 %out, lo16[$label] (at)"
+ cost 8;
+
+ out:(double)reg = LOAD.D(addr:address)
+ emit "ldc1 %out, %addr"
+ cost 4;
+
+ out:(double)reg = LOAD.D(label:LABEL.I)
+ emit "lui at, ha16[$label]"
+ emit "ldc1 %out, lo16[$label] (at)"
+ cost 8;
+
+ /* 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:intregorzero)
+ emit "seb %out, %in"
+ cost 4;
+
+ out:(int)reg = EXTENDH.I(in:intregorzero)
+ 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 "mov %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)
-
++ with corrupted(%in1), corrupted(%in2)
+ emit "mov %out.0, %in1"
+ emit "mov %out.1, %in2"
+ cost 8;
- with preserved(%left), preserved(%right)
++
+ out:(int)reg = FROML0.I(in:(long)reg)
+ emit "mov %out, %in.0"
+ cost 4;
+
+ out:(int)reg = FROML1.I(in:(long)reg)
+ emit "mov %out, %in.1"
+ cost 4;
+
+ intregorzero = zero:CONST.I
+ when specific_constant(%zero, 0)
+ emit "zero";
+
+ intregorzero = value:(int)reg
+ emit "%value";
+
+ intregorzero = value:(int)ubyte0
+ emit "%value";
+
+ intregorzero = value:(int)ushort0
+ emit "%value";
+
+ shortregorzero = zero:CONST.I
+ when specific_constant(%zero, 0)
+ emit "zero";
+
+ shortregorzero = value:(int)ushort0
+ emit "%value";
+
+ shortregorzero = value:(int)ushortX
+ emit "%value";
+
+ shortregorzero = value:(int)ubyte0
+ emit "%value";
+
+ byteregorzero = zero:CONST.I
+ when specific_constant(%zero, 0)
+ emit "zero";
+
+ byteregorzero = value:(int)ubyte0
+ emit "%value";
+
+ byteregorzero = value:(int)ubyteX
+ emit "%value";
+
+
+
+ /* Locals and stack-relatives */
+
+ out:(int)reg = in:LOCAL.I
+ emit "addiu %out, fp, $in"
+ cost 4;
+
+ address = in:LOCAL.I
+ emit "$in(fp)";
+
+ address = ADD.I(GETSP.I, offset:CONST.I)
+ when signed_constant(%offset, 16)
+ emit "$offset(sp)";
+
+
+
+ /* 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 "j $addr"
+ emit "nop"
+ cost 8;
+
+ JUMP(dest:(int)reg)
+ emit "jr %dest"
+ emit "nop"
+ cost 8;
+
+ CJUMPEQ(left:(int)reg, PAIR(true:BLOCK.I, false:BLOCK.I))
+ emit "beq %left, zero, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 16;
+
+ CJUMPLT(left:(int)reg, PAIR(true:BLOCK.I, false:BLOCK.I))
+ emit "bltz %left, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 20;
+
+ CJUMPLE(left:(int)reg, PAIR(true:BLOCK.I, false:BLOCK.I))
+ emit "blez %left, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 20;
+
+ #define CALLLABEL(insn) \
+ insn (dest:LABEL.I) \
+ with corrupted(volatile) \
+ emit "jal $dest" \
+ emit "nop" \
+ cost 8;
+
+ CALLLABEL(CALL)
+ out:(iret)reg = CALLLABEL(CALL.I)
++ out:(fret)reg = CALLLABEL(CALL.F)
+ out:(lret)reg = CALLLABEL(CALL.L)
++ out:(dret)reg = CALLLABEL(CALL.D)
+
+ #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:(fret)reg = CALLINDIRECT(CALL.F)
+ out:(lret)reg = CALLINDIRECT(CALL.L)
++ out:(dret)reg = CALLINDIRECT(CALL.D)
+
+ JUMP(dest:LABEL.I)
+ emit "b $dest"
+ emit "nop"
+ cost 8;
+
+
+
+ /* Conditional branches */
+
+ /* Normally COMPARE returns a condition code (in a flags register) which the CJUMP
+ * instructions can then operate on. But MIPS doesn't have a flags register, and
+ * requires you to know what condition you're testing for when you do the comparison.
+ * mcg doesn't like this much and we have to list every combination individually.
+ */
+
+ /* Signed integer comparisons against zero */
+
+ CJUMPEQ(COMPARESI.I(left:(int)reg, zero:CONST.I), PAIR(true:BLOCK.I, false:BLOCK.I))
+ when specific_constant(%zero, 0)
+ emit "beq %left, zero, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 16;
+
+ CJUMPLT(COMPARESI.I(left:(int)reg, zero:CONST.I), PAIR(true:BLOCK.I, false:BLOCK.I))
+ when specific_constant(%zero, 0)
+ emit "bltz %left, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 16;
+
+ CJUMPLE(COMPARESI.I(left:(int)reg, zero:CONST.I), PAIR(true:BLOCK.I, false:BLOCK.I))
+ when specific_constant(%zero, 0)
+ emit "blez %left, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 16;
+
+ /* Signed integer comparisons against a constant */
+
+ CJUMPEQ(COMPARESI.I(left:(int)reg, value:CONST.I), PAIR(true:BLOCK.I, false:BLOCK.I))
+ emit "li at, $value"
+ emit "beq %left, at, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 20;
+
+ CJUMPLT(COMPARESI.I(left:(int)reg, value:CONST.I), PAIR(true:BLOCK.I, false:BLOCK.I))
+ when signed_constant(%value, 16)
+ emit "slti at, %left, $value"
+ emit "bne at, zero, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 20;
+
+ CJUMPLE(COMPARESI.I(left:(int)reg, value:CONST.I), PAIR(true:BLOCK.I, false:BLOCK.I))
+ when constant_within_inclusive_range(%value, -0x8000, 0x7ffe)
+ emit "slti at, %left, 1+$value"
+ emit "bne at, zero, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 20;
+
+ /* Signed integer comparisons against a register */
+
+ 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 20;
+
+ 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;
+
+ CJUMPLE(COMPARESI.I(left:(int)reg, right:(int)reg), PAIR(true:BLOCK.I, false:BLOCK.I))
+ emit "slt at, %right, %left"
+ emit "beq at, zero, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 20;
+
+ /* Unsigned integer comparisons against a constant */
+
+ CJUMPLT(COMPARESI.I(COMPAREUI.I(left:(int)reg, right:CONST.I), alwayszero:CONST.I), PAIR(true:BLOCK.I, false:BLOCK.I))
+ when signed_constant(%right, 16)
+ when specific_constant(%alwayszero, 0)
+ emit "sltiu at, %left, $right"
+ emit "bne at, zero, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 16;
+
+ CJUMPLE(COMPARESI.I(COMPAREUI.I(left:(int)reg, right:CONST.I), alwayszero:CONST.I), PAIR(true:BLOCK.I, false:BLOCK.I))
+ when constant_within_inclusive_range(%right, -0x8000, 0x7ffe)
+ when specific_constant(%alwayszero, 0)
+ emit "sltiu at, %left, 1+$right"
+ emit "bne at, zero, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 20;
+
+ /* Unsigned integer comparisons against registers */
+
+ CJUMPEQ(COMPARESI.I(COMPAREUI.I(left:(int)reg, right:(int)reg), alwayszero:CONST.I), PAIR(true:BLOCK.I, false:BLOCK.I))
+ when specific_constant(%alwayszero, 0)
+ emit "beq %left, %right, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 16;
+
+ CJUMPLT(COMPARESI.I(COMPAREUI.I(left:(int)reg, right:(int)reg), alwayszero:CONST.I), PAIR(true:BLOCK.I, false:BLOCK.I))
+ when specific_constant(%alwayszero, 0)
+ emit "sltu at, %left, %right"
+ emit "bne at, zero, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 16;
+
+ CJUMPLE(COMPARESI.I(COMPAREUI.I(left:(int)reg, right:(int)reg), alwayszero:CONST.I), PAIR(true:BLOCK.I, false:BLOCK.I))
+ when specific_constant(%alwayszero, 0)
+ emit "sltu at, %right, %left"
+ emit "beq at, zero, $true"
+ emit "nop"
+ emit "b $false"
+ emit "nop"
+ cost 16;
+
+
+
+
+ /* Comparisons */
+
+ /* The COMPARE nodes return tristate integer values; -1, 0 or 1. */
+
+ out:(int)reg = COMPARESI.I(left:(int)reg, right:(int)reg)
- with preserved(%left), preserved(%right)
++ with corrupted(%left), corrupted(%right)
+ emit "slt at, %left, %right"
+ emit "bne at, zero, 1f"
+ emit "li %out, -1" /* delay slot */
+ emit "slt %out, %right, %left"
+ emit "1:"
+ cost 20;
+
+ out:(int)reg = COMPAREUI.I(left:(int)reg, right:(int)reg)
- with preserved(%left), preserved(%right)
++ with corrupted(%left), corrupted(%right)
+ emit "sltu at, %left, %right"
+ emit "bne at, zero, 1f"
+ emit "li %out, -1" /* delay slot */
+ emit "sltu %out, %right, %left"
+ emit "1:"
+ cost 20;
+
+ out:(iret)reg = COMPAREUL.I(left:(lret)reg, right:(lret1)reg)
+ emit "jal .compareul"
+ emit "nop"
+ cost 30;
+
+ out:(int)reg = COMPAREF.I(left:(float)reg, right:(float)reg)
- with preserved(%left), preserved(%right)
++ with corrupted(%left), corrupted(%right)
+ emit "c.lt.s 0, %left, %right"
+ emit "li %out, -1"
+ emit "bc1t 0, 1f"
+ emit "nop"
+ emit "c.lt.s 0, %right, %left"
+ emit "li %out, 1"
+ emit "movf %out, zero, 0"
+ emit "1:"
+ cost 28;
+
+ out:(int)reg = COMPARED.I(left:(double)reg, right:(double)reg)
++ with corrupted(%left), corrupted(%right)
+ emit "c.lt.d 0, %left, %right"
+ emit "li %out, -1"
+ emit "bc1t 0, 1f"
+ emit "nop"
+ emit "c.lt.d 0, %right, %left"
+ emit "li %out, 1"
+ emit "movf %out, zero, 0"
+ emit "1:"
+ cost 28;
+
+ /* Booleans */
+
+ /* If 0 then 1, else 0 */
+ out:(int)reg = IFEQ.I(in:intregorzero)
+ emit "sltiu %out, %in, 1"
+ cost 4;
+
+ /* If -1 then 1, else 0 */
+ out:(int)reg = IFLT.I(in:intregorzero)
+ emit "srl %out, %in, 31"
+ cost 4;
+
+ /* If 1 or 0 then 1, else 0 */
+ out:(int)reg = IFLE.I(in:intregorzero)
+ emit "slti %out, %in, 1"
+ 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:intregorzero, right:intregorzero) \
+ emit instr " %out, %left, %right" \
+ cost 4; \
+
+ /* reg + const */
+ #define ALUC(name, instr) \
+ out:(int)reg = name(left:intregorzero, 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:intregorzero) \
+ 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:intregorzero, right:intregorzero)
+ emit "subu %out, %left, %right"
+ cost 4;
+
+ out:(int)reg = SUB.I(left:intregorzero, right:CONST.I)
+ emit "addiu %out, %left, -[$right]"
+ cost 4;
+
+ out:(int)reg = DIV.I(left:intregorzero, right:intregorzero)
+ emit "div %left, %right"
+ emit "mflo %out"
+ cost 8;
+
+ out:(int)reg = DIVU.I(left:intregorzero, right:intregorzero)
+ emit "divu %left, %right"
+ emit "mflo %out"
+ cost 8;
+
+ out:(int)reg = MOD.I(left:intregorzero, right:intregorzero)
+ emit "div %left, %right"
+ emit "mfhi %out"
+ cost 8;
+
+ out:(int)reg = MODU.I(left:intregorzero, right:intregorzero)
+ emit "divu %left, %right"
+ emit "mfhi %out"
+ cost 8;
+
+ ALUR(MUL.I, "mul")
+
+ ALUR(ASL.I, "sllv")
+ ALUC(ASL.I, "sll")
+ ALUR(ASR.I, "srav")
+ ALUC(ASR.I, "sra")
+
+ ALUR(LSL.I, "sllv")
+ ALUC(LSL.I, "sll")
+ ALUR(LSR.I, "srlv")
+ ALUC(LSR.I, "srl")
+
+ out:(int)reg = NEG.I(left:intregorzero)
+ emit "subu %out, zero, %left"
+ cost 4;
+
+ out:(int)reg = NOT.I(in:intregorzero)
+ 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 "lui %out, hi16[$value]"
+ emit "ori %out, %out, lo16[$value]"
+ cost 4;
+
+ out:(int)reg = value:BLOCK.I
+ emit "lui %out, hi16[$value]"
+ emit "ori %out, %out, lo16[$value]"
+ cost 4;
+
+ out:(int)reg = value:CONST.I
+ emit "li %out, $value"
+ cost 4;
+
+
+
+ /* FPU operations */
+
+ /* Doubles */
+
+ out:(double)reg = ADDF.D(left:(double)reg, right:(double)reg)
+ emit "add.d %out, %left, %right"
+ cost 4;
+
+ out:(double)reg = SUBF.D(left:(double)reg, right:(double)reg)
+ emit "sub.d %out, %left, %right"
+ cost 4;
+
+ out:(double)reg = MULF.D(left:(double)reg, right:(double)reg)
+ emit "mul.d %out, %left, %right"
+ cost 4;
+
+ out:(double)reg = DIVF.D(left:(double)reg, right:(double)reg)
+ emit "div.d %out, %left, %right"
+ cost 4;
+
+ out:(double)reg = NEGF.D(left:(double)reg)
+ emit "neg.d %out, %left"
+ cost 4;
+
+ out:(double)reg = FROMSI.D(in:(int)reg)
+ emit "mtc1 %in, %out" /* mtc1 has reversed parameters */
+ emit "cvt.d.w %out, %out"
+ cost 4;
+
+ out:(dret)reg = FROMUI.D(in:(iret)reg)
+ emit "jal .c_ui_d"
+ emit "nop"
+ cost 30;
+
+ out:(int)reg = FROMSD.I(in:(double)reg)
+ emit "trunc.w.d f30, %in"
+ emit "mfc1 %out, f30"
+ cost 8;
+
+ out:(lret)reg = FROMSD.L(in:(dret)reg)
+ emit "jal .c_sd_l"
+ emit "nop"
+ cost 30;
+
+ out:(iret)reg = FROMUD.I(in:(dret)reg)
+ with corrupted(dret)
+ emit "jal .c_ud_i"
+ emit "nop"
+ cost 30;
+
+ out:(double)reg = COPYL.D(in:(long)reg)
+ emit "mtc1 %in.0, %out" /* mtc1 has reversed parameters */
+ emit "mthc1 %in.1, %out" /* mtc1 has reversed parameters */
+ cost 8;
+
+ out:(long)reg = COPYD.L(in:(double)reg)
+ emit "mfc1 %out.0, %in"
+ emit "mfhc1 %out.1, %in"
+ cost 8;
+
+ /* Floats */
+
+ out:(float)reg = ADDF.F(left:(float)reg, right:(float)reg)
+ emit "add.d %out, %left, %right"
+ cost 4;
+
+ out:(float)reg = SUBF.F(left:(float)reg, right:(float)reg)
+ emit "sub.d %out, %left, %right"
+ cost 4;
+
+ out:(float)reg = MULF.F(left:(float)reg, right:(float)reg)
+ emit "mul.d %out, %left, %right"
+ cost 4;
+
+ out:(float)reg = DIVF.F(left:(float)reg, right:(float)reg)
+ emit "div.d %out, %left, %right"
+ cost 4;
+
+ out:(float)reg = NEGF.F(left:(float)reg)
+ emit "neg.s %out, %left"
+ cost 4;
+
+ out:(float)reg = FROMSI.F(in:intregorzero)
+ emit "mtc1 %in, %out" /* mtc1 has reversed parameters */
+ emit "cvt.s.w %out, %out"
+ cost 4;
+
+ out:(int)reg = FROMSF.I(in:(float)reg)
+ emit "trunc.w.s f30, %in"
+ emit "mfc1 %out, f30"
+ cost 8;
+
+ out:(lret)reg = FROMSF.L(in:(fret)reg)
+ emit "jal .c_sf_l"
+ emit "nop"
+ cost 30;
+
+ out:(fret)reg = FROMUI.F(in:(iret)reg)
+ emit "jal .c_ui_f"
+ emit "nop"
+ cost 30;
+
+ out:(iret)reg = FROMUF.I(in:(fret)reg)
+ with corrupted(fret)
+ emit "jal .c_uf_i"
+ emit "nop"
+ cost 30;
+
+ out:(float)reg = COPYI.F(in:intregorzero)
+ emit "mtc1 %in, %out" /* mtc1 has reversed parameters */
+ cost 4;
+
+ out:(int)reg = COPYF.I(in:(float)reg)
+ emit "mfc1 %out, %in"
+ cost 4;
+
+ out:(float)reg = value:CONST.F
+ when specific_constant(%value, 0)
+ emit "mtc1 zero, %out" /* mtc1 has reversed parameters */
+ cost 4;
+
+ /* vim: set sw=4 ts=4 expandtab : */
+
emit_header(is_ro ? SECTION_ROM : SECTION_DATA);
assert((size == 4) || (size == 8));
- i = float_cst((char*) data, size, (char*) buffer);
- if ((i != 0) && (i != 2)) /* 2 == overflow */
- fatal("cannot parse floating point constant %s sz %d", data, size);
-
- fprintf(outputfile, "\t!float %s sz %zd\n", data, size);
- fprintf(outputfile, "\t.data1 ");
- writehex(buffer[0], 1);
- for (i=1; i<size; i++)
- {
- fprintf(outputfile, ", ");
- writehex(buffer[i], 1);
- }
- fprintf(outputfile, "\n");
+ fprintf(outputfile, "\t.dataf%ld %s\n", size, data);
}
-static bool istext(c)
+static bool istext(char c)
{
return isprint(c) && (c != '"');
}
FILE* outputfile = NULL;
FILE* dominance_dot_file = NULL;
FILE* cfg_dot_file = NULL;
++FILE* regalloc_dot_file = NULL;
bool tracing(char k)
{
return false;
}
++static FILE* open_dot_file(const char* filename)
++{
++ FILE* fp = fopen(filename, "w");
++ if (!fp)
++ fatal("couldn't open output file '%s': %s",
++ filename, strerror(errno));
++ fprintf(fp, "digraph {\n");
++ return fp;
++}
++
++static void close_dot_files(void)
++{
++ if (cfg_dot_file)
++ {
++ fprintf(cfg_dot_file, "}\n");
++ fclose(cfg_dot_file);
++ }
++ if (dominance_dot_file)
++ {
++ fprintf(dominance_dot_file, "}\n");
++ fclose(dominance_dot_file);
++ }
++ if (regalloc_dot_file)
++ {
++ fprintf(regalloc_dot_file, "}\n");
++ fclose(regalloc_dot_file);
++ }
++}
++
int main(int argc, char* const argv[])
{
const char* inputfilename = NULL;
const char* outputfilename = NULL;
FILE* output;
-- int i;
program_name = argv[0];
opterr = 1;
for (;;)
{
-- int c = getopt(argc, argv, "d:D:C:o:");
++ int c = getopt(argc, argv, "-d:D:C:R:o:");
if (c == -1)
break;
switch (c)
{
case 'C':
-- cfg_dot_file = fopen(optarg, "w");
-- if (!cfg_dot_file)
-- fatal("couldn't open output file '%s': %s",
-- optarg, strerror(errno));
-- fprintf(cfg_dot_file, "digraph {\n");
++ cfg_dot_file = open_dot_file(optarg);
break;
case 'D':
-- dominance_dot_file = fopen(optarg, "w");
-- if (!dominance_dot_file)
-- fatal("couldn't open output file '%s': %s",
-- optarg, strerror(errno));
-- fprintf(dominance_dot_file, "digraph {\n");
++ dominance_dot_file = open_dot_file(optarg);
++ break;
++
++ case 'R':
++ regalloc_dot_file = open_dot_file(optarg);
break;
case 'd':
fatal("already specified an output file");
outputfilename = optarg;
break;
-- }
-- }
-- for (i = optind; i < argc; i++)
-- {
-- if (inputfilename)
-- fatal("unexpected argument '%s'", argv[i]);
-- inputfilename = argv[i];
++ case 1:
++ if (inputfilename)
++ fatal("unexpected argument '%s'", optarg);
++ inputfilename = optarg;
++ }
}
++ atexit(close_dot_files);
symbol_init();
-- if (!EM_open((char*) inputfilename))
-- fatal("couldn't open input '%s': %s",
++ if (!EM_open((char*) inputfilename))
++ fatal("couldn't open input '%s': %s",
inputfilename ? inputfilename : "<stdin>", EM_error);
if (outputfilename)
fclose(outputfile);
EM_close();
-- if (cfg_dot_file)
-- {
-- fprintf(cfg_dot_file, "}\n");
-- fclose(cfg_dot_file);
-- }
-- if (dominance_dot_file)
-- {
-- fprintf(dominance_dot_file, "}\n");
-- fclose(dominance_dot_file);
-- }
--
return 0;
}
#include <stdint.h>
#include <string.h>
#include <assert.h>
- #include "flt_arith.h"
+ #include <errno.h>
+#include "hashtable.h"
#include "em_arith.h"
#include "em_label.h"
#include "em.h"
extern void pass_infer_types(void);
extern void pass_insert_moves(void);
extern void pass_instruction_selector(void);
-extern void pass_live_vreg_analysis(void);
+extern void pass_live_value_analysis(void);
+ extern void pass_lower_pushes(void);
extern void pass_add_prologue_epilogue(void);
+extern void pass_prune_stray_moves(void);
extern void pass_register_allocator(void);
extern void pass_remove_dead_blocks(void);
extern void pass_remove_dead_phis(void);
}
case str_ptyp:
- data_block((const uint8_t*) strdup(em.em_string), em.em_size, ro);
+ {
+ uint8_t* copy = malloc(em.em_size);
+ memcpy(copy, em.em_string, em.em_size);
+ data_block(copy, em.em_size, ro);
break;
+ }
case cst_ptyp:
- data_int(em.em_cst, EM_wordsize, ro);
+ {
+ arith value = em.em_cst;
+ data_int(value, EM_wordsize, ro);
+ data_block_int(value);
break;
+ }
case nof_ptyp:
data_offset(dlabel_to_str(em.em_dlb), em.em_off, ro);
/* Passes from here on can't alter the BB graph without also updating prevs
* and nexts (and then calling update_graph_data()). */
- print_blocks('3');
+ print_blocks('e');
pass_wire_up_return_values();
- print_blocks('4');
+ pass_convert_stack_ops();
+ #if defined MCGG_OPTION_LOWER_PUSHES_TO_LOADS_AND_STORES
+ pass_lower_pushes();
+ #endif
+ print_blocks('f');
+ pass_convert_stack_ops();
+ print_blocks('g');
pass_convert_locals_to_ssa();
- print_blocks('5');
+ print_blocks('h');
+ pass_convert_inputs_to_phis();
+ print_blocks('i');
+ pass_convert_nonlocal_phis();
+ print_blocks('j');
pass_remove_dead_phis();
+ print_blocks('k');
pass_infer_types();
- print_blocks('6');
-
+ print_blocks('l');
pass_instruction_selector();
- print_hops('7');
- pass_find_phi_congruence_groups();
- pass_live_vreg_analysis();
- print_hops('8');
+ print_hops('m');
+ pass_live_value_analysis();
+ print_hops('n');
+ pass_assign_vregs();
+ print_hops('o');
+ pass_convert_copies_to_moves();
+ print_hops('p');
+ pass_collapse_adjacent_moves();
+ print_hops('q');
+ pass_prune_stray_moves();
+ print_hops('r');
+ pass_calculate_vreg_spillibility();
+ print_hops('s');
pass_register_allocator();
+ print_hops('t');
+#if 0
pass_add_prologue_epilogue();
print_hops('9');
+#endif
emit_procedure(proc);
struct vreg
{
- int id;
- uint32_t type;
- struct phicongruence* congruence;
- struct hop* defined;
- ARRAYOF(struct hop) used;
+ int id;
+ struct value* value;
+ struct vreg* coalesced_with;
+ int hreg;
+ int neighbours;
+ bool needs_register;
+ bool is_spilt;
+ const struct burm_regclass_data* regclass;
+ burm_register_bitmap_t registers;
+ struct hreg* evicted; /* stack slot to evict to */
};
-typedef PMAPOF(struct hreg, struct vreg) register_assignment_t;
-
-extern struct vreg* new_vreg(void);
-
extern struct hreg* new_hreg(const struct burm_register_data* brd);
extern struct hreg* new_stacked_hreg(uint32_t type);
static int stackptr;
static struct ir* stack[64];
- IMAPOF(struct basicblock) targets;
+ struct jumptable
+ {
++ struct hashtable targets;
+ struct basicblock* defaulttarget;
+ };
+
static struct ir* convert(struct ir* src, int srcsize, int destsize, int opcode);
static struct ir* appendir(struct ir* ir);
static void insn_ivalue(int opcode, arith value);
}
case op_csa:
- struct jumptable jumptable = {};
+ {
+ struct ir* descriptor = pop(EM_pointersize);
+ struct ir* targetvalue = appendir(pop(EM_pointersize));
++ struct jumptable jumptable = { HASHTABLE_OF_INTS };
+ int i;
+
+ if (descriptor->opcode != IR_LABEL)
+ fatal("csa is only supported if it refers "
+ "directly to a descriptor block");
+
+ parse_csa(bb_get(descriptor->u.lvalue), &jumptable);
+ emit_jumptable(targetvalue, &jumptable);
++ hashtable_reset(&jumptable.targets);
+ break;
+ }
+
case op_csb:
{
- const char* helper = aprintf(".%s",
- (opcode == op_csa) ? "csa" : "csb");
struct ir* descriptor = pop(EM_pointersize);
- struct jumptable jumptable = {};
+ struct ir* targetvalue = appendir(pop(EM_pointersize));
++ struct jumptable jumptable = { HASHTABLE_OF_INTS };
+ int i;
if (descriptor->opcode != IR_LABEL)
- fatal("csa/csb are only supported if they refer "
+ fatal("csb is only supported if it refers "
"directly to a descriptor block");
- push(descriptor);
- materialise_stack();
- appendir(
- new_ir2(
- IR_FARJUMP, 0,
- new_labelir(helper),
- extract_block_refs(bb_get(descriptor->u.lvalue))
- )
- );
+ parse_csb(bb_get(descriptor->u.lvalue), &jumptable);
+ emit_jumptable(targetvalue, &jumptable);
++ hashtable_reset(&jumptable.targets);
break;
}
generate_tree(current_proc->blocks.item[i]);
}
- imap_put(&table->targets, lowerbound+i, target);
+ static void parse_csa(struct basicblock* data_bb, struct jumptable* table)
+ {
+ struct em* em;
+ int lowerbound;
+ int count;
+ int i;
+
+ assert(data_bb->ems.count >= 3);
+
+ /* Default target */
+
+ em = data_bb->ems.item[0];
+ assert(em->opcode == op_bra);
+ assert(em->paramtype == PARAM_BVALUE);
+ table->defaulttarget = em->u.bvalue.left;
+
+ /* Lower bound */
+
+ em = data_bb->ems.item[1];
+ assert(em->opcode == op_loc);
+ assert(em->paramtype == PARAM_IVALUE);
+ lowerbound = em->u.ivalue;
+
+ /* Count */
+
+ em = data_bb->ems.item[2];
+ assert(em->opcode == op_loc);
+ assert(em->paramtype == PARAM_IVALUE);
+ count = em->u.ivalue + 1; /* value in descriptor is inclusive */
+ assert(data_bb->ems.count >= (count + 3));
+
+ /* Now, each target in turn. */
+
+ for (i=0; i<count; i++)
+ {
+ struct basicblock* target;
+
+ em = data_bb->ems.item[3 + i];
+ assert(em->opcode == op_bra);
+ assert(em->paramtype == PARAM_BVALUE);
+ target = em->u.bvalue.left;
+
- imap_put(&table->targets, value, target);
++ hashtable_puti(&table->targets, lowerbound+i, target);
+ }
+ }
+
+ static void parse_csb(struct basicblock* data_bb, struct jumptable* table)
+ {
+ struct em* em;
+ int count;
+ int i;
+
+ assert(data_bb->ems.count >= 2);
+
+ /* Default target */
+
+ em = data_bb->ems.item[0];
+ assert(em->opcode == op_bra);
+ assert(em->paramtype == PARAM_BVALUE);
+ table->defaulttarget = em->u.bvalue.left;
+
+ /* Number of targets */
+
+ em = data_bb->ems.item[1];
+ assert(em->opcode == op_loc);
+ assert(em->paramtype == PARAM_IVALUE);
+ count = em->u.ivalue;
+ assert(data_bb->ems.count >= (count*2 + 2));
+
+ /* Now, each target in turn. */
+
+ for (i=0; i<count; i++)
+ {
+ int value;
+ struct basicblock* target;
+
+ em = data_bb->ems.item[2 + i*2];
+ assert(em->opcode == op_loc);
+ assert(em->paramtype == PARAM_IVALUE);
+ value = em->u.ivalue;
+
+ em = data_bb->ems.item[3 + i*2];
+ assert(em->opcode == op_bra);
+ assert(em->paramtype == PARAM_BVALUE);
+ target = em->u.bvalue.left;
+
- int i;
++ hashtable_puti(&table->targets, value, target);
+ }
+ }
+
+ static void emit_jumptable(struct ir* targetvalue, struct jumptable* jumptable)
+ {
- for (i=0; i<jumptable->targets.count; i++)
++ struct hashtable_iterator hit = {};
+
+ materialise_stack();
- int value = jumptable->targets.item[i].left;
- struct basicblock* target = jumptable->targets.item[i].right;
++ while (hashtable_next(&jumptable->targets, &hit))
+ {
++ int value = (int) hit.key;
++ struct basicblock* target = hit.value;
+ struct basicblock* nextblock = bb_get(NULL);
+
+ array_append(¤t_proc->blocks, nextblock);
+ appendir(
+ new_ir2(
+ IR_CJUMPEQ, 0,
+ new_ir2(
+ IR_COMPARESI, EM_wordsize,
+ targetvalue,
+ new_wordir(value)
+ ),
+ new_ir2(
+ IR_PAIR, 0,
+ new_bbir(target),
+ new_bbir(nextblock)
+ )
+ )
+ );
+
+ current_bb = nextblock;
+ }
+
+ appendir(
+ new_ir1(
+ IR_JUMP, 0,
+ new_bbir(jumptable->defaulttarget)
+ )
+ );
+ }
+
/* vim: set sw=4 ts=4 expandtab : */
--- /dev/null
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include "hashtable.h"
+
+struct hashnode
+{
+ struct hashnode* next;
+ void* key;
+ void* value;
+};
+
++uint32_t standard_int_hash_function(void* key)
++{
++ uint32_t x = (uint32_t) key;
++ x = ((x >> 16) ^ x) * 0x45d9f3b;
++ x = ((x >> 16) ^ x) * 0x45d9f3b;
++ x = (x >> 16) ^ x;
++ return x;
++}
++
++bool standard_int_comparison_function(void* key1, void* key2)
++{
++ return (key1 == key2);
++}
++
+uint32_t standard_pointer_hash_function(void* key)
+{
+ /* Basile-Starynkevitch pointer hash */
+ uintptr_t ptr = (uintptr_t) key;
+ return (uint32_t) ((13 * ptr) ^ (ptr >> 15));
+}
+
+bool standard_pointer_comparison_function(void* key1, void* key2)
+{
+ return (key1 == key2);
+}
+
+uint32_t standard_string_hash_function(void* key)
+{
+ char* s = key;
+ uint32_t hash = 0;
+
+ while (*s)
+ {
+ hash = ((hash << 5) + hash) ^ *s;
+ s++;
+ }
+
+ return hash;
+}
+
+bool standard_string_comparison_function(void* key1, void* key2)
+{
+ return strcmp(key1, key2) == 0;
+}
+
+static void lazy_init(struct hashtable* ht)
+{
+ if (!ht->num_buckets)
+ ht->num_buckets = 7;
+
+ if (!ht->hashfunction)
+ ht->hashfunction = standard_pointer_hash_function;
+
+ if (!ht->cmpfunction)
+ ht->cmpfunction = standard_pointer_comparison_function;
+
+ if (!ht->allocfunction)
+ ht->allocfunction = calloc;
+
+ if (!ht->freefunction)
+ ht->freefunction = free;
+
+ if (!ht->buckets)
+ ht->buckets = ht->allocfunction(ht->num_buckets, sizeof(struct hashnode*));
+}
+
+void hashtable_empty(struct hashtable* ht)
+{
+ while (ht->size)
+ hashtable_pop(ht);
+}
+
+void hashtable_reset(struct hashtable* ht)
+{
+ lazy_init(ht);
+ hashtable_empty(ht);
+ ht->freefunction(ht->buckets);
+ ht->buckets = NULL;
+}
+
+void hashtable_rebucket(struct hashtable* ht, unsigned int num_buckets)
+{
+ struct hashnode** old_buckets = ht->buckets;
+ int old_num_buckets = ht->num_buckets;
+ int i;
+
+ ht->num_buckets = num_buckets;
+ ht->buckets = ht->allocfunction(num_buckets, sizeof(struct hashnode*));
+
+ for (i=0; i<old_num_buckets; i++)
+ {
+ while (old_buckets[i])
+ {
+ struct hashnode* hn = old_buckets[i];
+ uint32_t hash = ht->hashfunction(hn->key) & (num_buckets-1);
+
+ old_buckets[i] = hn->next;
+ hn->next = ht->buckets[hash];
+ ht->buckets[hash] = hn;
+ }
+ }
+
+ ht->freefunction(old_buckets);
+}
+
+static struct hashnode** findnodep(struct hashtable* ht, void* key)
+{
+ uint32_t hash;
+ struct hashnode** hnp;
+
+ lazy_init(ht);
+ hash = ht->hashfunction(key) & (ht->num_buckets-1);
+ hnp = &ht->buckets[hash];
+
+ for (;;)
+ {
+ void* k;
+
+ if (!*hnp)
+ break;
+ k = (*hnp)->key;
+ if ((k == key) || ht->cmpfunction(k, key))
+ break;
+
+ hnp = &(*hnp)->next;
+ }
+
+ return hnp;
+}
+
+void* hashtable_put(struct hashtable* ht, void* key, void* value)
+{
+ void* oldvalue;
+ struct hashnode** hnp = findnodep(ht, key);
+ if (!*hnp)
+ {
+ if (ht->size == (ht->num_buckets*2))
+ {
+ hashtable_rebucket(ht, ht->num_buckets*4);
+ return hashtable_put(ht, key, value);
+ }
+
+ *hnp = ht->allocfunction(1, sizeof(struct hashnode));
+ ht->size++;
+ }
+
+ oldvalue = (*hnp)->value;
+ (*hnp)->key = key;
+ (*hnp)->value = value;
+ return oldvalue;
+}
+
++void* hashtable_puti(struct hashtable* ht, int key, void* value)
++{
++ return hashtable_put(ht, (void*)key, value);
++}
++
+void* hashtable_get(struct hashtable* ht, void* key)
+{
+ if (ht)
+ {
+ struct hashnode** hnp = findnodep(ht, key);
+ if (*hnp)
+ return (*hnp)->value;
+ }
+ return NULL;
+}
+
++void* hashtable_geti(struct hashtable* ht, int key)
++{
++ return hashtable_get(ht, (void*)key);
++}
++
+void* hashtable_remove(struct hashtable* ht, void* key)
+{
+ struct hashnode** hnp = findnodep(ht, key);
+ if (*hnp)
+ {
+ struct hashnode* hn = *hnp;
+ void* value = hn->value;
+ *hnp = hn->next;
+ ht->freefunction(hn);
+ ht->size--;
+ return value;
+ }
+
+ return NULL;
+}
+
+void* hashtable_pop(struct hashtable* ht)
+{
+ int i;
+
+ if (!ht || (ht->size == 0))
+ return NULL;
+
+ lazy_init(ht);
+ for (i=0; i<ht->num_buckets; i++)
+ {
+ struct hashnode** hnp = &ht->buckets[i];
+ if (*hnp)
+ {
+ struct hashnode* hn = *hnp;
+ void* value = hn->value;
+ *hnp = hn->next;
+ ht->freefunction(hn);
+ ht->size--;
+ return value;
+ }
+ }
+
+ return NULL;
+}
+
+void* hashtable_next(struct hashtable* ht, struct hashtable_iterator* it)
+{
+ if (!ht)
+ return NULL;
+
+ lazy_init(ht);
+
+ if (!it->running)
+ {
+ /* Find the first node and return it; the stored state in the iterator
+ * is invalid. */
+
+ while (it->bucket < ht->num_buckets)
+ {
+ it->node = ht->buckets[it->bucket];
+ if (it->node)
+ goto found;
+ it->bucket++;
+ }
+
+ /* hashtable is empty! */
+ }
+ else
+ {
+ /* The iterator is running; the stored state in the iterator points at
+ * the current node, so find the next one. */
+
+ if (!it->advanced)
+ it->node = it->node->next;
+ if (it->node)
+ goto found;
+
+ for (;;)
+ {
+ it->bucket++;
+ if (it->bucket == ht->num_buckets)
+ break;
+
+ it->node = ht->buckets[it->bucket];
+ if (it->node)
+ goto found;
+ }
+
+ /* nothing left found! */
+ }
+
+ /* EOF: reset the iterator. */
+
+ it->running = false;
+ it->advanced = false;
+ it->key = it->value = NULL;
+ it->bucket = 0;
+ it->node = NULL;
+ return NULL;
+
+found:
+ it->running = true;
+ it->advanced = false;
+ it->key = it->node->key;
+ it->value = it->node->value;
+ return it->value;
+}
+
+void hashtable_delete_current(struct hashtable* ht, struct hashtable_iterator* it)
+{
+ struct hashnode** hnp;
+
+ assert(ht);
+ assert(it->running);
+
+ hnp = findnodep(ht, it->node->key);
+ it->node = it->node->next;
+ ht->freefunction(*hnp);
+ ht->size--;
+ *hnp = it->node;
+
+ it->advanced = true;
+}
+
+void hashtable_copy_all(struct hashtable* src, struct hashtable* dest)
+{
+ struct hashtable_iterator hit = {};
+ while (hashtable_next(src, &hit))
+ hashtable_put(dest, hit.key, hit.value);
+}
--- /dev/null
+#ifndef HASHTABLE_H
+#define HASHTABLE_H
+
+/* A simple, autoresizing hash table. */
+
+typedef uint32_t hashfunction_t(void* key);
+typedef bool cmpfunction_t(void* key1, void* key2);
+typedef void* allocfunction_t(size_t nmemb, size_t size);
+typedef void freefunction_t(void* ptr);
+
++extern uint32_t standard_int_hash_function(void* key);
++extern bool standard_int_comparison_function(void* key1, void* key2);
++
+extern uint32_t standard_pointer_hash_function(void* key);
+extern bool standard_pointer_comparison_function(void* key1, void* key2);
+
+extern uint32_t standard_string_hash_function(void* key);
+extern bool standard_string_comparison_function(void* key1, void* key2);
+
+struct hashtable
+{
+ hashfunction_t* hashfunction;
+ cmpfunction_t* cmpfunction;
+ allocfunction_t* allocfunction;
+ freefunction_t* freefunction;
+ unsigned int num_buckets; /* power of 2 */
+ struct hashnode** buckets;
+ int size;
+};
+
++#define HASHTABLE_OF_INTS \
++ { standard_int_hash_function, standard_int_comparison_function }
++
+#define HASHTABLE_OF_STRINGS \
+ { standard_string_hash_function, standard_string_comparison_function }
+
+struct hashtable_iterator
+{
+ /* Public */
+ void* key;
+ void* value;
+
+ /* Private */
+ bool running;
+ bool advanced;
+ int bucket;
+ struct hashnode* node;
+};
+
+extern void hashtable_empty(struct hashtable* ht);
+extern void hashtable_reset(struct hashtable* ht);
+extern void hashtable_rebucket(struct hashtable* ht, unsigned int num_buckets);
+
+extern void* hashtable_put(struct hashtable* ht, void* key, void* data);
++extern void* hashtable_puti(struct hashtable* ht, int key, void* data);
+extern void* hashtable_get(struct hashtable* ht, void* key);
++extern void* hashtable_geti(struct hashtable* ht, int key);
+extern void* hashtable_remove(struct hashtable* ht, void* key);
+extern void* hashtable_pop(struct hashtable* ht);
+extern void* hashtable_next(struct hashtable* ht, struct hashtable_iterator* it);
+extern void hashtable_delete_current(struct hashtable* ht, struct hashtable_iterator* it);
+
+extern void hashtable_copy_all(struct hashtable* src, struct hashtable* dest);
+
+#endif
%term FRAGMENT
%term NAMED
%term NOTEQUALS
+ %term OPTIONS
%term PATTERNS
%term PREFERS
-%term PRESERVED
%term REGISTERS
%term WHEN
%term WITH
#include "iburg.h"
#include "ircodes.h"
#include "astring.h"
-#include "smap.h"
-#include "mcgg.h"
+#include "registers.h"
+#include "bitmap.h"
- static char rcsid[] = "$Id$";
+ #define REGATTR_INT 0
+ #define REGATTR_LONG 1
+ #define REGATTR_FLOAT 2
+ #define REGATTR_DOUBLE 3
int maxcost = SHRT_MAX / 2;
"DECLARATIONS" return DECLARATIONS;
"PATTERNS" return PATTERNS;
"REGISTERS" return REGISTERS;
-"aliases" return ALIASES;
+ "OPTIONS" return OPTIONS;
+"uses" return USES;
"corrupted" return CORRUPTED;
"cost" return COST;
"emit" return EMIT;