#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)
{
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)
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)
}
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:
break;
case EM_DEFILB:
- tb_ilabel(ilabel_to_str(insn.em_ilb));
+ queue_ilabel(insn.em_ilb);
break;
case EM_DEFDLB:
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);
#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;
}
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)
);
}
-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(
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)
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(
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;
{
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
)
);
}
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 : */