Retry graphcolouring merge, better this time.
authorDavid Given <dg@cowlark.com>
Sat, 29 Sep 2018 09:28:35 +0000 (11:28 +0200)
committerDavid Given <dg@cowlark.com>
Sat, 29 Sep 2018 09:28:35 +0000 (11:28 +0200)
16 files changed:
1  2 
mach/mips/mcg/platform.c
mach/mips/mcg/table
mach/powerpc/mcg/platform.c
mach/proto/mcg/build.lua
mach/proto/mcg/data.c
mach/proto/mcg/main.c
mach/proto/mcg/mcg.h
mach/proto/mcg/parse_em.c
mach/proto/mcg/procedure.c
mach/proto/mcg/reg.h
mach/proto/mcg/treebuilder.c
modules/src/data/hashtable.c
modules/src/data/hashtable.h
util/mcgg/gram.y
util/mcgg/iburg.c
util/mcgg/scan.l

index 0000000,a29a69e..ddbeac9
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,309 +1,310 @@@
 - * |  --------------- <- 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 : */
index 0000000,2a580e2..cf80ef1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1018 +1,1024 @@@
 -     * 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 : */
Simple merge
Simple merge
@@@ -80,22 -73,10 +73,10 @@@ void data_float(const char* data, size_
        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 != '"');
  }
@@@ -7,6 -7,6 +7,7 @@@ static const char* tracechars = NULL
  FILE* outputfile = NULL;
  FILE* dominance_dot_file = NULL;
  FILE* cfg_dot_file = NULL;
++FILE* regalloc_dot_file = NULL;
  
  bool tracing(char k)
  {
@@@ -37,38 -37,38 +38,62 @@@ static bool find_procedures_cb(struct s
      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;
  }
  
@@@ -8,8 -8,7 +8,8 @@@
  #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"
@@@ -121,9 -112,9 +121,10 @@@ extern void pass_group_irs(void)
  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);
@@@ -237,16 -281,16 +281,20 @@@ static void parse_pseu(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);
@@@ -209,41 -196,27 +209,45 @@@ void procedure_compile(struct procedure
      /* 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);
  
@@@ -15,17 -24,18 +15,18 @@@ struct hre
  
  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);
  
@@@ -5,6 -5,12 +5,12 @@@ static struct basicblock* current_bb
  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);
@@@ -1332,25 -1328,34 +1328,36 @@@ static void insn_ivalue(int opcode, ari
          }
  
          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;
          }
  
@@@ -1757,4 -1760,133 +1762,133 @@@ void tb_procedure(void
          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(&current_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 : */
index 1610347,0000000..8e6a5e3
mode 100644,000000..100644
--- /dev/null
@@@ -1,289 -1,0 +1,313 @@@
 +#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);
 +}
index 1c5598f,0000000..32c046f
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,65 @@@
 +#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
@@@ -37,8 -36,10 +37,9 @@@ extern int yylex(void)
  %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;
  
@@@ -39,7 -40,8 +40,8 @@@ static int braces = 0
  "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;