Store the EM code up front and build the basic block graph *before*
authorDavid Given <dg@cowlark.com>
Mon, 19 Sep 2016 21:06:59 +0000 (23:06 +0200)
committerDavid Given <dg@cowlark.com>
Mon, 19 Sep 2016 21:06:59 +0000 (23:06 +0200)
generating the IR code. Lots more IR code.

mach/proto/mcg/build.lua
mach/proto/mcg/ir.c
mach/proto/mcg/ir.dat
mach/proto/mcg/ir.h
mach/proto/mcg/mcg.h
mach/proto/mcg/parse_em.c
mach/proto/mcg/treebuilder.c

index 5db304b..c94d44e 100644 (file)
@@ -29,7 +29,10 @@ cprogram {
                "./*.h",
        },
        vars = {
-               ["+cflags"] = {"-Werror-implicit-function-declaration"}
+               ["+cflags"] = {
+                       "-Werror-implicit-function-declaration",
+                       "-Wint-conversion"
+               }
        }
 }
 
index 8973bfc..8141715 100644 (file)
@@ -8,42 +8,23 @@ struct ir* new_ir0(int opcode, int size)
        ir->id = next_id++;
        ir->opcode = opcode;
        ir->size = size;
-
-       switch (ir->opcode)
-       {
-               case IR_JUMP:
-               case IR_CJUMP:
-                       ir->terminates = true;
-                       break;
-       }
-
        return ir;
 }
 
 struct ir* new_ir1(int opcode, int size,
-       struct ir* c1)
+       struct ir* left)
 {
        struct ir* ir = new_ir0(opcode, size);
-       ir->children[0] = c1;
+       ir->left = left;
        return ir;
 }
 
 struct ir* new_ir2(int opcode, int size,
-       struct ir* c1, struct ir* c2)
+       struct ir* left, struct ir* right)
 {
        struct ir* ir = new_ir0(opcode, size);
-       ir->children[0] = c1;
-       ir->children[1] = c2;
-       return ir;
-}
-
-struct ir* new_ir3(int opcode, int size,
-       struct ir* c1, struct ir* c2, struct ir* c3)
-{
-       struct ir* ir = new_ir0(opcode, size);
-       ir->children[0] = c1;
-       ir->children[1] = c2;
-       ir->children[2] = c3;
+       ir->left = left;
+       ir->right = right;
        return ir;
 }
 
@@ -87,15 +68,13 @@ struct ir* new_phiir(int size)
 
 void ir_print(const struct ir* ir)
 {
-       int i;
-       for (i=0; i<sizeof(ir->children)/sizeof(*ir->children); i++)
-       {
-               if (ir->children[i])
-                       ir_print(ir->children[i]);
-       }
+    if (ir->left)
+        ir_print(ir->left);
+    if (ir->right)
+        ir_print(ir->right);
 
        printf("\t; %c ",
-               ir->sequence ? 'S' : ' ');
+               ir->is_sequence ? 'S' : ' ');
        printf("$%d = ", ir->id);
        printf("%s%d(",
                ir_names[ir->opcode],
@@ -120,15 +99,10 @@ void ir_print(const struct ir* ir)
                        break;
 
                default:
-                       for (i=0; i<sizeof(ir->children)/sizeof(*ir->children); i++)
-                       {
-                               if (ir->children[i])
-                               {
-                                       if (i > 0)
-                                               printf(", ");
-                                       printf("$%d", ir->children[i]->id);
-                               }
-                       }
+            if (ir->left)
+                printf("$%d", ir->left->id);
+            if (ir->right)
+                printf(", $%d", ir->right->id);
        }
 
        printf(")\n");
index 044f9c0..ea47cb0 100644 (file)
@@ -3,15 +3,26 @@ ICONST
 REG
 LABEL
 BLOCK
+PAIR
 ANY
 PHI
 
+# Magic stack operations
+PUSH
+POP
+
 # Memory operations
 LOAD
 STORE
 
 # Arithemetic operations
 ADD
+SUB
+MUL
+DIV
+MOD
+NEG
+NOT
 
 # Conversions
 FROMI1
@@ -19,15 +30,27 @@ FROMI2
 FROMI4
 FROMI8
 
-# Comparisons
+FROMU1
+FROMU2
+FROMU4
+FROMU8
+
+# Tristate comparisons
 COMPARES
 COMPAREU
 
+# Boolean comparisons
+IFEQ
+IFLT
+IFLE
+
 # Flow control
+CALL
 JUMP
 CJUMP
 RET
 
 # Special
 SETREG
+GETREG
 
index 33b8925..6574ecb 100644 (file)
@@ -16,7 +16,8 @@ struct ir
        int id;
        int opcode;
        int size;
-       struct ir* children[3];
+       struct ir* left;
+       struct ir* right;
        union {
                arith ivalue;
                int rvalue;
@@ -26,8 +27,7 @@ struct ir
                        ARRAY(struct ir, srcs);
                } phivalue;
        } u;
-       bool sequence : 1;
-       bool terminates : 1;
+       bool is_sequence : 1;
 };
 
 extern const char* ir_names[];
@@ -37,8 +37,6 @@ extern struct ir* new_ir1(int opcode, int size,
        struct ir* c1);
 extern struct ir* new_ir2(int opcode, int size,
        struct ir* c1, struct ir* c2);
-extern struct ir* new_ir3(int opcode, int size,
-       struct ir* c1, struct ir* c2, struct ir* c3);
 
 extern struct ir* new_labelir(const char* label);
 extern struct ir* new_regir(int reg);
index 06e8160..ce332ff 100644 (file)
@@ -31,7 +31,8 @@ enum {
        SECTION_TEXT
 };
 
-struct symbol {
+struct symbol
+{
        const char* name;
        int section;
        bool is_defined : 1;
@@ -39,20 +40,50 @@ struct symbol {
        bool is_proc : 1;
 };
 
-enum {
+enum
+{
     PARAM_NONE,
-    PARAM_VALUE,
-    PARAM_LABEL
+    PARAM_IVALUE,
+    PARAM_LVALUE,
+    PARAM_BVALUE,
+};
+
+struct insn
+{
+    int opcode;
+    int paramtype;
+    union {
+        arith ivalue;
+        struct {
+            const char* label;
+            arith offset;
+        } lvalue;
+        struct {
+            struct basicblock* left;
+            struct basicblock* right;
+        } bvalue;
+    } u;
+};
+    
+struct procedure
+{
+    const char* name;
+    struct basicblock* root_bb;
+    size_t nlocals;
+    ARRAY(struct basicblock, blocks);
 };
 
-struct basicblock {
+struct basicblock
+{
     const char* name;
+    ARRAY(struct insn, insns);
     ARRAY(struct ir, irs);
     ARRAY(struct basicblock, inblocks);
     ARRAY(struct basicblock, outblocks);
     ARRAY(struct ir, outs);
     ARRAY(struct ir, ins);
     bool is_wired : 1;
+    bool is_terminated : 1;
 };
 
 extern void fatal(const char* s, ...);
@@ -78,15 +109,9 @@ extern void bb_wire_outs_to_ins(struct basicblock* outblock, struct basicblock*
 
 extern void tb_filestart(void);
 extern void tb_fileend(void);
-extern void tb_ilabel(const char* label);
-extern void tb_procstart(const char* label, size_t nlocals);
-extern void tb_procend(void);
+extern void tb_procedure(struct procedure* proc);
 extern void tb_regvar(arith offset, int size, int type, int priority);
 
-extern void tb_insn_simple(int opcode, int flags);
-extern void tb_insn_label(int opcode, int flags, const char* label, arith offset);
-extern void tb_insn_value(int opcode, int flags, arith value);
-
 #endif
 
 /* vim: set sw=4 ts=4 expandtab : */
index 22ac92e..9310dda 100644 (file)
@@ -1,7 +1,8 @@
 #include "mcg.h"
 
 static struct e_instr insn;
-static const char* current_proc;
+static struct procedure* current_proc;
+static struct basicblock* current_bb;
 
 static const char* type_to_str(int type)
 {
@@ -48,7 +49,7 @@ static void unknown_type(const char* s)
 static const char* ilabel_to_str(label l)
 {
     assert(current_proc != NULL);
-    return aprintf("__%s_I%d", current_proc, l);
+    return aprintf("__%s_I%d", current_proc->name, l);
 }
 
 static const char* dlabel_to_str(label l)
@@ -56,6 +57,103 @@ static const char* dlabel_to_str(label l)
     return aprintf("__D%d", l);
 }
 
+static struct insn* new_insn(int opcode)
+{
+    struct insn* insn = calloc(sizeof(struct insn), 1);
+    insn->opcode = opcode;
+    return insn;
+}
+
+static void queue_insn_simple(int opcode)
+{
+    struct insn* insn = new_insn(opcode);
+    insn->paramtype = PARAM_NONE;
+    APPEND(current_bb->insns, insn);
+
+    switch (opcode)
+    {
+        case op_ret:
+            current_bb->is_terminated = true;
+            current_bb = NULL;
+            break;
+    }
+}
+
+static void queue_insn_value(int opcode, arith value)
+{
+    struct insn* insn = new_insn(opcode);
+    insn->paramtype = PARAM_IVALUE;
+    insn->u.ivalue = value;
+    APPEND(current_bb->insns, insn);
+}
+
+static void queue_insn_label(int opcode, const char* label, arith offset)
+{
+    struct insn* insn = new_insn(opcode);
+    insn->paramtype = PARAM_LVALUE;
+    insn->u.lvalue.label = label;
+    insn->u.lvalue.offset = offset;
+    APPEND(current_bb->insns, insn);
+}
+
+static void queue_insn_block(int opcode, struct basicblock* left, struct basicblock* right)
+{
+    struct insn* insn = new_insn(opcode);
+    insn->paramtype = PARAM_BVALUE;
+    insn->u.bvalue.left = left;
+    insn->u.bvalue.right = right;
+    APPEND(current_bb->insns, insn);
+    
+    APPENDU(current_bb->outblocks, left);
+    if (right)
+        APPENDU(current_bb->outblocks, right);
+    APPENDU(current_bb->inblocks, current_bb);
+
+    current_bb->is_terminated = true;
+    current_bb = NULL;
+}
+
+static void queue_insn_ilabel(int opcode, int label)
+{
+    const char* name = ilabel_to_str(insn.em_ilb);
+    struct basicblock* left = bb_get(name);
+
+    switch (opcode)
+    {
+        case op_bra:
+            queue_insn_block(insn.em_opcode, left, NULL);
+            break;
+
+        case op_zeq:
+        case op_zne:
+        case op_zlt:
+        case op_zle:
+        case op_zgt:
+        case op_zge:
+            queue_insn_block(insn.em_opcode, left, bb_get(NULL));
+            break;
+
+        default:
+            fatal("parse_em: unhandled conditional '%s'", 
+                em_mnem[opcode - sp_fmnem]);
+    }
+}
+
+static void change_basicblock(struct basicblock* newbb)
+{
+    APPENDU(current_proc->blocks, newbb);
+
+    if (current_bb && !current_bb->is_terminated)
+        queue_insn_block(op_bra, newbb, NULL);
+
+    current_bb = newbb;
+}
+
+static void queue_ilabel(arith label)
+{
+    change_basicblock(bb_get(ilabel_to_str(label)));
+}
+
 static void parse_pseu(void)
 {
        switch (insn.em_opcode)
@@ -142,16 +240,19 @@ static void parse_pseu(void)
         }
 
                case ps_pro: /* procedure start */
-            if (insn.em_nlocals == -1)
-                fatal("procedures with unspecified number of locals are not supported yet");
-
-            current_proc = strdup(insn.em_pnam);
-            tb_procstart(current_proc, insn.em_nlocals);
+            current_proc = calloc(sizeof(struct procedure), 1);
+            current_proc->name = strdup(insn.em_pnam);
+            current_proc->root_bb = bb_get(current_proc->name);
+            current_proc->nlocals = insn.em_nlocals;
+            current_bb = current_proc->root_bb;
+            APPEND(current_proc->blocks, current_bb);
             break;
 
                case ps_end: /* procedure end */
-            tb_procend();
+            tb_procedure(current_proc);
+
             current_proc = NULL;
+            current_bb = NULL;
                        break;
 
                default:
@@ -207,7 +308,7 @@ void parse_em(void)
                 break;
 
             case EM_DEFILB:
-                tb_ilabel(ilabel_to_str(insn.em_ilb));
+                queue_ilabel(insn.em_ilb);
                 break;
 
             case EM_DEFDLB:
@@ -223,51 +324,48 @@ void parse_em(void)
                 break;
 
             case EM_MNEM:
-            {
-                int flags = em_flag[insn.em_opcode - sp_fmnem];
-
-                if (flags & EM_PAR)
+                if (current_bb)
                 {
-                    switch (insn.em_argtype)
+                    int flags = em_flag[insn.em_opcode - sp_fmnem];
+
+                    if (flags & EM_PAR)
                     {
-                        case ilb_ptyp:
-                            tb_insn_label(insn.em_opcode, flags,
-                                ilabel_to_str(insn.em_ilb), 0);
-                            break;
-
-                        case nof_ptyp:
-                            tb_insn_label(insn.em_opcode, flags,
-                                dlabel_to_str(insn.em_dlb), insn.em_off);
-                            break;
-
-                        case sof_ptyp:
-                            tb_insn_label(insn.em_opcode, flags,
-                                strdup(insn.em_dnam), insn.em_off);
-                            break;
-
-                        case pro_ptyp:
-                            tb_insn_label(insn.em_opcode, flags,
-                                strdup(insn.em_pnam), 0);
-                            break;
-
-                        case cst_ptyp:
-                            if ((flags & EM_PAR) == PAR_B)
-                                tb_insn_label(insn.em_opcode, flags,
-                                    ilabel_to_str(insn.em_ilb), 0);
-                            else
-                                tb_insn_value(insn.em_opcode, flags,
-                                    insn.em_cst);
-                            break;
-
-                        default:
-                            unknown_type("instruction");
+                        switch (insn.em_argtype)
+                        {
+                            case ilb_ptyp:
+                                queue_insn_ilabel(insn.em_opcode, insn.em_ilb);
+                                break;
+
+                            case nof_ptyp:
+                                queue_insn_label(insn.em_opcode,
+                                    dlabel_to_str(insn.em_dlb), insn.em_off);
+                                break;
+
+                            case sof_ptyp:
+                                queue_insn_label(insn.em_opcode,
+                                    strdup(insn.em_dnam), insn.em_off);
+                                break;
+
+                            case pro_ptyp:
+                                queue_insn_label(insn.em_opcode,
+                                    strdup(insn.em_pnam), 0);
+                                break;
+
+                            case cst_ptyp:
+                                if ((flags & EM_PAR) == PAR_B)
+                                    queue_insn_ilabel(insn.em_opcode, insn.em_ilb);
+                                else
+                                    queue_insn_value(insn.em_opcode, insn.em_cst);
+                                break;
+
+                            default:
+                                unknown_type("instruction");
+                        }
                     }
+                    else
+                        queue_insn_simple(insn.em_opcode);
                 }
-                else
-                    tb_insn_simple(insn.em_opcode, flags);
-
                 break;
-            }
 
             default:
                 fatal("unrecognised instruction type '%d'", insn.em_type);
index c7abd5c..acd8f30 100644 (file)
@@ -1,13 +1,15 @@
 #include "mcg.h"
 
 static struct symbol* currentproc;
-static struct basicblock* rootbb;
-static struct basicblock* currentbb;
+static struct basicblock* current_bb;
 
 static int stackptr;
 static struct ir* stack[64];
 
-static void resetstack(void)
+static struct ir* convert(struct ir* src, int destsize, int opcode);
+static struct ir* appendir(struct ir* ir);
+
+static void reset_stack(void)
 {
     stackptr = 0;
 }
@@ -17,132 +19,92 @@ static void push(struct ir* ir)
     if (stackptr == sizeof(stack)/sizeof(*stack))
         fatal("stack overflow");
 
-    stack[stackptr++] = ir;
-}
+    /* If we try to push something which is too small, convert it to a word
+     * first. */
 
-static struct ir* pop(void)
-{
-    if (stackptr == 0)
-        fatal("stack underflow");
+    if (ir->size < EM_wordsize)
+        ir = convert(ir, EM_wordsize, IR_FROMU1);
 
-    return stack[--stackptr];
+    stack[stackptr++] = ir;
 }
 
-static struct ir* appendir(struct ir* ir)
+static struct ir* pop(int size)
 {
-    assert(currentbb != NULL);
-    ir->sequence = true;
-    APPEND(currentbb->irs, ir);
+    if (stackptr == 0)
+    {
+        /* Nothing in our fake stack, so we have to read from the real stack. */
 
-    ir_print(ir);
-    return ir;
-}
+        if (size < EM_wordsize)
+            size = EM_wordsize;
+        return
+            new_ir0(
+                IR_POP, size
+            );
+    }
+    else
+    {
+        struct ir* ir = stack[--stackptr];
 
-void tb_filestart(void)
-{
-}
+        /* If we try to pop something which is smaller than a word, convert it first. */
+        
+        if (size < EM_wordsize)
+            ir = convert(ir, size, IR_FROMU1);
 
-void tb_fileend(void)
-{
+        if (ir->size != size)
+            fatal("expected an item on stack of size %d, but got %d\n", size, ir->size);
+        return ir;
+    }
 }
 
-static void materialise(void)
+static void print_stack(void)
 {
     int i;
 
+    printf("\t; stack:");
     for (i=0; i<stackptr; i++)
-        appendir(stack[i]);
+    {
+        struct ir* ir = stack[i];
+        printf(" $%d.%d", ir->id, ir->size);
+    }
+    printf("  (top)\n");
 }
 
-static void changeblock(struct basicblock* bb)
+static struct ir* appendir(struct ir* ir)
 {
     int i;
 
-    if (stackptr > 0)
-    {
-        printf("\t; block exiting with %d on stack:\n", stackptr);
-        for (i=0; i<stackptr; i++)
-        {
-            struct ir* ir = stack[i];
-            printf("\t;   $%d size %d\n", ir->id, ir->size);
-            APPENDU(currentbb->outs, ir);
-        }
-    }
-
-    for (i=0; i<currentbb->outblocks_count; i++)
-        bb_wire_outs_to_ins(currentbb, currentbb->outblocks[i]);
+    assert(current_bb != NULL);
+    ir->is_sequence = true;
+    APPEND(current_bb->irs, ir);
 
-    currentbb = bb;
-    printf("; new block: %s\n", currentbb->name);
-
-    resetstack();
-
-    if (currentbb->ins_count > 0)
-    {
-        printf("\t; block entering with %d on stack:\n", currentbb->ins_count);
-        for (i=0; i<currentbb->ins_count; i++)
-        {
-            struct ir* ir = currentbb->ins[i];
-            printf("\t;   $%d size %d\n", ir->id, ir->size);
-            push(ir);
-        }
-    }
+    ir_print(ir);
+    return ir;
 }
 
-void tb_ilabel(const char* label)
+static void materialise_stack(void)
 {
-    materialise();
+    int i;
 
-    #if 0
-    if (currentbb->irs_count == 0)
+    for (i=stackptr-1; i>=0; i--)
     {
-        /* Current BB has no instructions, so just alias it to the
-         * new name.
-         */
-        bb_alias(currentbb, label);
+        struct ir* ir = stack[i];
+        appendir(
+            new_ir1(
+                IR_PUSH, ir->size,
+                ir
+            )
+        );
     }
-    else
-    #endif
-    {
-        struct basicblock* newbb = bb_get(label);
-
-        if ((currentbb->irs_count == 0) ||
-            !currentbb->irs[currentbb->irs_count-1]->terminates)
-        {
-            APPEND(currentbb->outblocks, newbb);
-            appendir(
-                new_ir1(
-                    IR_JUMP, 0,
-                    new_labelir(label)
-                )
-            );
-        }
 
-        changeblock(newbb);
-    }
+    reset_stack();
 }
 
-void tb_procstart(const char* label, size_t nlocals)
+void tb_filestart(void)
 {
-    assert(currentproc == NULL);
-
-    currentproc = symbol_get(label);
-    currentproc->section = SECTION_TEXT;
-
-    rootbb = calloc(sizeof(struct basicblock), 1);
-    currentbb = rootbb;
-
-    resetstack();
 }
 
-void tb_procend(void)
+void tb_fileend(void)
 {
-    assert(currentproc != NULL);
-
-    printf("\n.text\n");
-    printf("%s:\n", currentproc->name);
-
-    currentproc = NULL;
 }
 
 void tb_regvar(arith offset, int size, int type, int priority)
@@ -160,115 +122,171 @@ static struct ir* address_of_local(int index)
         );
 }
 
-static struct ir* tristate_compare(int size, int opcode)
+static struct ir* convert(struct ir* src, int destsize, int opcode)
 {
-    struct ir* right = pop();
-    struct ir* left = pop();
-
-    return
-        new_ir2(
-            opcode, size,
-            left, right
-        );
-}
-
-static struct ir* convert(int destsize, int srcsize, int opcode)
-{
-    switch (srcsize)
+    switch (src->size)
     {
         case 1: opcode += 0; break;
         case 2: opcode += 1; break;
         case 4: opcode += 2; break;
         case 8: opcode += 3; break;
         default:
-            fatal("can't convert from things of size %d", srcsize);
+            fatal("can't convert from things of size %d", src->size);
     }
 
     return
         new_ir1(
             opcode, destsize,
-            pop()
+            src
+        );
+}
+
+static struct ir* tristate_compare(int size, int opcode)
+{
+    struct ir* right = pop(size);
+    struct ir* left = pop(size);
+
+    return
+        new_ir2(
+            opcode, size,
+            left, right
         );
 }
 
-void tb_insn_simple(int opcode, int flags)
+static void insn_simple(int opcode)
 {
     switch (opcode)
     {
         case op_cii:
         {
-            struct ir* destsize = pop();
-            struct ir* srcsize = pop();
+            struct ir* destsize = pop(EM_wordsize);
+            struct ir* srcsize = pop(EM_wordsize);
+            struct ir* value;
 
             assert(srcsize->opcode == IR_ICONST);
             assert(destsize->opcode == IR_ICONST);
 
+            value = pop(srcsize->u.ivalue);
             push(
-                convert(destsize->u.ivalue, srcsize->u.ivalue, IR_FROMI1)
+                convert(value, destsize->u.ivalue, IR_FROMI1)
             );
             break;
         }
 
+        case op_cmp:
+            push(
+                tristate_compare(EM_pointersize, IR_COMPAREU)
+            );
+            break;
+
         default:
-            fatal("unknown insn_simple instruction '%s'",
+            fatal("treebuilder: unknown simple instruction '%s'",
                 em_mnem[opcode - sp_fmnem]);
     }
 }
 
-void tb_insn_label(int opcode, int flags, const char* label, arith offset)
+static void simple_branch2(int opcode, int size,
+    struct basicblock* truebb, struct basicblock* falsebb,
+    int irop)
 {
-    materialise();
+    struct ir* right = pop(size);
+    struct ir* left = pop(size);
 
-    switch (opcode)
-    {
-        case op_zne:
-        {
-            struct basicblock* truebb = bb_get(label);
-            struct basicblock* falsebb = bb_get(NULL);
+    materialise_stack();
+    appendir(
+        new_ir2(
+            IR_CJUMP, 0,
+            new_ir2(
+                irop, size,
+                left, right
+            ),
+            new_ir2(
+                IR_PAIR, 0,
+                new_bbir(truebb),
+                new_bbir(falsebb)
+            )
+        )
+    );
+}
 
-            APPENDU(currentbb->outblocks, truebb);
-            APPENDU(currentbb->outblocks, falsebb);
+static void compare0_branch2(int opcode,
+    struct basicblock* truebb, struct basicblock* falsebb,
+    int irop)
+{
+    push(
+        new_wordir(0)
+    );
 
-            appendir(
-                new_ir3(
-                    IR_CJUMP, 0,
-                    pop(),
-                    new_bbir(truebb),
-                    new_bbir(falsebb)
-                )
-            );
+    simple_branch2(opcode, EM_wordsize, truebb, falsebb, irop);
+}
 
-            changeblock(falsebb);
-            break;
-        }
+static void insn_bvalue(int opcode, struct basicblock* leftbb, struct basicblock* rightbb)
+{
+    switch (opcode)
+    {
+        case op_zeq: compare0_branch2(opcode, leftbb, rightbb, IR_IFEQ); break;
+        case op_zlt: compare0_branch2(opcode, leftbb, rightbb, IR_IFLT); break;
+        case op_zle: compare0_branch2(opcode, leftbb, rightbb, IR_IFLE); break;
+
+        case op_zne: compare0_branch2(opcode, rightbb, leftbb, IR_IFEQ); break;
+        case op_zge: compare0_branch2(opcode, rightbb, leftbb, IR_IFLT); break;
+        case op_zgt: compare0_branch2(opcode, rightbb, leftbb, IR_IFLE); break;
 
         case op_bra:
         {
-            struct basicblock* destbb = bb_get(label);
-            APPENDU(currentbb->outblocks, destbb);
+            materialise_stack();
 
             appendir(
                 new_ir1(
                     IR_JUMP, 0,
-                    new_bbir(destbb)
+                    new_bbir(leftbb)
                 )
             );
             break;
         }
 
         default:
-            fatal("unknown insn_label instruction '%s'",
+            fatal("treebuilder: unknown bvalue instruction '%s'",
                 em_mnem[opcode - sp_fmnem]);
     }
 }
 
-void tb_insn_value(int opcode, int flags, arith value)
+static void simple_alu1(int opcode, int size, int irop)
+{
+    struct ir* val = pop(size);
+
+    push(
+        new_ir1(
+            irop, size,
+            val
+        )
+    );
+}
+
+static void simple_alu2(int opcode, int size, int irop)
 {
-    struct ir* left;
-    struct ir* right;
+    struct ir* right = pop(size);
+    struct ir* left = pop(size);
+
+    push(
+        new_ir2(
+            irop, size,
+            left, right
+        )
+    );
+}
 
+static void insn_ivalue(int opcode, arith value)
+{
     switch (opcode)
     {
+        case op_adi: simple_alu2(opcode, value, IR_ADD); break;
+        case op_sbi: simple_alu2(opcode, value, IR_SUB); break;
+        case op_mli: simple_alu2(opcode, value, IR_MUL); break;
+        case op_dvi: simple_alu2(opcode, value, IR_DIV); break;
+        case op_rmi: simple_alu2(opcode, value, IR_MOD); break;
+        case op_ngi: simple_alu1(opcode, value, IR_NEG); break;
+
         case op_lol:
             push(
                 new_ir1(
@@ -283,11 +301,17 @@ void tb_insn_value(int opcode, int flags, arith value)
                 new_ir2(
                     IR_STORE, EM_wordsize,
                     address_of_local(value),
-                    pop()
+                    pop(EM_wordsize)
                 )
             );
             break;
 
+        case op_lal:
+            push(
+                address_of_local(value)
+            );
+            break;
+
         case op_loc:
             push(
                 new_wordir(value)
@@ -298,22 +322,24 @@ void tb_insn_value(int opcode, int flags, arith value)
             push(
                 new_ir1(
                     IR_LOAD, value,
-                    pop()
+                    pop(EM_pointersize)
                 )
             );
             break;
 
         case op_sti:
-            right = pop();
-            left = pop();
+        {
+            struct ir* ptr = pop(EM_pointersize);
+            struct ir* val = pop(value);
 
             appendir(
                 new_ir2(
                     IR_STORE, value,
-                    right, left
+                    ptr, val
                 )
             );
             break;
+        }
 
         case op_cmi:
             push(
@@ -328,24 +354,59 @@ void tb_insn_value(int opcode, int flags, arith value)
             break;
 
         case op_ads:
-            right = pop();
-            left = pop();
+        {
+            struct ir* off = pop(value);
+            struct ir* ptr = pop(EM_pointersize);
 
             if (value != EM_pointersize)
-                right = convert(EM_pointersize, value, IR_FROMI1);
+                off = convert(off, EM_pointersize, IR_FROMI1);
 
             push(
                 new_ir2(
-                    IR_ADD, EM_wordsize,
-                    left, right
+                    IR_ADD, EM_pointersize,
+                    ptr, off
                 )
             );
             break;
+        }
+
+        case op_adp:
+        {
+            struct ir* ptr = pop(EM_pointersize);
 
+            push(
+                new_ir2(
+                    IR_ADD, EM_pointersize,
+                    ptr,
+                    new_wordir(value)
+                )
+            );
+            break;
+        }
+
+        case op_sbs:
+        {
+            struct ir* right = pop(EM_pointersize);
+            struct ir* left = pop(EM_pointersize);
+
+            struct ir* delta = 
+                new_ir2(
+                    IR_SUB, EM_pointersize,
+                    left, right
+                );
+
+            if (value != EM_pointersize)
+                delta = convert(delta, value, IR_FROMI1);
+
+            push(delta);
+            break;
+        }
+            
         case op_dup:
         {
-            struct ir* v = pop();
-            appendir(v);
+            struct ir* v = pop(value);
+            if (!v->is_sequence)
+                appendir(v);
             push(v);
             push(v);
             break;
@@ -386,13 +447,12 @@ void tb_insn_value(int opcode, int flags, arith value)
         {
             if (value > 0)
             {
-                left = pop();
-                assert(left->size == value);
+                struct ir* retval = pop(value);
                 appendir(
                     new_ir2(
                         IR_SETREG, value,
                         new_regir(IRR_RR),
-                        left
+                        retval
                     )
                 );
             }
@@ -405,11 +465,138 @@ void tb_insn_value(int opcode, int flags, arith value)
             break;
         }
                     
+        case op_lfr:
+        {
+            push(
+                appendir(
+                    new_ir1(
+                        IR_GETREG, value,
+                        new_regir(IRR_RR)
+                    )
+                )
+            );
+            break;
+        }
+
         default:
-            fatal("unknown insn_value instruction '%s'",
+            fatal("treebuilder: unknown ivalue instruction '%s'",
                 em_mnem[opcode - sp_fmnem]);
     }
 }
 
+static void insn_lvalue(int opcode, const char* label, arith offset)
+{
+    switch (opcode)
+    {
+        case op_lae:
+            push(
+                new_ir2(
+                    IR_ADD, EM_pointersize,
+                    new_labelir(label),
+                    new_wordir(offset)
+                )
+            );
+            break;
+
+        case op_loe:
+            push(
+                new_ir1(
+                    IR_LOAD, EM_wordsize,
+                    new_ir2(
+                        IR_ADD, EM_pointersize,
+                        new_labelir(label),
+                        new_wordir(offset)
+                    )
+                )
+            );
+            break;
+
+        case op_ste:
+            appendir(
+                new_ir2(
+                    IR_STORE, EM_wordsize,
+                    new_ir2(
+                        IR_ADD, EM_pointersize,
+                        new_labelir(label),
+                        new_wordir(offset)
+                    ),
+                    pop(EM_wordsize)
+                )
+            );
+            break;
+
+        case op_cal:
+            assert(offset == 0);
+            materialise_stack();
+            appendir(
+                new_ir1(
+                    IR_CALL, 0,
+                    new_labelir(label)
+                )
+            );
+            break;
+                    
+        default:
+            fatal("treebuilder: unknown lvalue instruction '%s'",
+                em_mnem[opcode - sp_fmnem]);
+    }
+}
+
+static void generate_tree(struct basicblock* bb)
+{
+    int i;
+
+    printf("; BLOCK %s\n", bb->name);
+    current_bb = bb;
+    reset_stack();
+
+    for (i=0; i<bb->insns_count; i++)
+    {
+        struct insn* insn = bb->insns[i];
+        printf("\t; EM: %s ", em_mnem[insn->opcode - sp_fmnem]);
+        switch (insn->paramtype)
+        {
+            case PARAM_NONE:
+                printf("\n");
+                insn_simple(insn->opcode);
+                break;
+
+            case PARAM_IVALUE:
+                printf("value=%d\n", insn->u.ivalue);
+                insn_ivalue(insn->opcode, insn->u.ivalue);
+                break;
+
+            case PARAM_LVALUE:
+                printf("label=%s offset=%d\n", 
+                    insn->u.lvalue.label, insn->u.lvalue.offset);
+                insn_lvalue(insn->opcode, insn->u.lvalue.label, insn->u.lvalue.offset);
+                break;
+
+            case PARAM_BVALUE:
+                printf("true=%s", insn->u.bvalue.left->name);
+                if (insn->u.bvalue.right)
+                    printf(" false=%s", insn->u.bvalue.right->name);
+                printf("\n");
+                insn_bvalue(insn->opcode, insn->u.bvalue.left, insn->u.bvalue.right);
+                break;
+
+            default:
+                assert(0);
+        }
+
+        print_stack();
+    }
+
+    assert(stackptr == 0);
+}
+
+void tb_procedure(struct procedure* current_proc)
+{
+    int i;
+
+    for (i=0; i<current_proc->blocks_count; i++)
+        generate_tree(current_proc->blocks[i]);
+}
+
 /* vim: set sw=4 ts=4 expandtab : */