416 lines
8.6 KiB
C
416 lines
8.6 KiB
C
#include "mcg.h"
|
|
|
|
static struct symbol* currentproc;
|
|
static struct basicblock* rootbb;
|
|
static struct basicblock* currentbb;
|
|
|
|
static int stackptr;
|
|
static struct ir* stack[64];
|
|
|
|
static void resetstack(void)
|
|
{
|
|
stackptr = 0;
|
|
}
|
|
|
|
static void push(struct ir* ir)
|
|
{
|
|
if (stackptr == sizeof(stack)/sizeof(*stack))
|
|
fatal("stack overflow");
|
|
|
|
stack[stackptr++] = ir;
|
|
}
|
|
|
|
static struct ir* pop(void)
|
|
{
|
|
if (stackptr == 0)
|
|
fatal("stack underflow");
|
|
|
|
return stack[--stackptr];
|
|
}
|
|
|
|
static struct ir* appendir(struct ir* ir)
|
|
{
|
|
assert(currentbb != NULL);
|
|
ir->sequence = true;
|
|
APPEND(currentbb->irs, ir);
|
|
|
|
ir_print(ir);
|
|
return ir;
|
|
}
|
|
|
|
void tb_filestart(void)
|
|
{
|
|
}
|
|
|
|
void tb_fileend(void)
|
|
{
|
|
}
|
|
|
|
static void materialise(void)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<stackptr; i++)
|
|
appendir(stack[i]);
|
|
}
|
|
|
|
static void changeblock(struct basicblock* bb)
|
|
{
|
|
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]);
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
void tb_ilabel(const char* label)
|
|
{
|
|
materialise();
|
|
|
|
#if 0
|
|
if (currentbb->irs_count == 0)
|
|
{
|
|
/* Current BB has no instructions, so just alias it to the
|
|
* new name.
|
|
*/
|
|
bb_alias(currentbb, label);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
void tb_procstart(const char* label, size_t nlocals)
|
|
{
|
|
assert(currentproc == NULL);
|
|
|
|
currentproc = symbol_get(label);
|
|
currentproc->section = SECTION_TEXT;
|
|
|
|
rootbb = calloc(sizeof(struct basicblock), 1);
|
|
currentbb = rootbb;
|
|
|
|
resetstack();
|
|
}
|
|
|
|
void tb_procend(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)
|
|
{
|
|
/* ignored */
|
|
}
|
|
|
|
static struct ir* address_of_local(int index)
|
|
{
|
|
return
|
|
new_ir2(
|
|
IR_ADD, EM_pointersize,
|
|
new_regir((index < 0) ? IRR_LB : IRR_AB),
|
|
new_wordir(index)
|
|
);
|
|
}
|
|
|
|
static struct ir* tristate_compare(int size, 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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
return
|
|
new_ir1(
|
|
opcode, destsize,
|
|
pop()
|
|
);
|
|
}
|
|
|
|
void tb_insn_simple(int opcode, int flags)
|
|
{
|
|
switch (opcode)
|
|
{
|
|
case op_cii:
|
|
{
|
|
struct ir* destsize = pop();
|
|
struct ir* srcsize = pop();
|
|
|
|
assert(srcsize->opcode == IR_ICONST);
|
|
assert(destsize->opcode == IR_ICONST);
|
|
|
|
push(
|
|
convert(destsize->u.ivalue, srcsize->u.ivalue, IR_FROMI1)
|
|
);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
fatal("unknown insn_simple instruction '%s'",
|
|
em_mnem[opcode - sp_fmnem]);
|
|
}
|
|
}
|
|
|
|
void tb_insn_label(int opcode, int flags, const char* label, arith offset)
|
|
{
|
|
materialise();
|
|
|
|
switch (opcode)
|
|
{
|
|
case op_zne:
|
|
{
|
|
struct basicblock* truebb = bb_get(label);
|
|
struct basicblock* falsebb = bb_get(NULL);
|
|
|
|
APPENDU(currentbb->outblocks, truebb);
|
|
APPENDU(currentbb->outblocks, falsebb);
|
|
|
|
appendir(
|
|
new_ir3(
|
|
IR_CJUMP, 0,
|
|
pop(),
|
|
new_bbir(truebb),
|
|
new_bbir(falsebb)
|
|
)
|
|
);
|
|
|
|
changeblock(falsebb);
|
|
break;
|
|
}
|
|
|
|
case op_bra:
|
|
{
|
|
struct basicblock* destbb = bb_get(label);
|
|
APPENDU(currentbb->outblocks, destbb);
|
|
|
|
appendir(
|
|
new_ir1(
|
|
IR_JUMP, 0,
|
|
new_bbir(destbb)
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
fatal("unknown insn_label instruction '%s'",
|
|
em_mnem[opcode - sp_fmnem]);
|
|
}
|
|
}
|
|
|
|
void tb_insn_value(int opcode, int flags, arith value)
|
|
{
|
|
struct ir* left;
|
|
struct ir* right;
|
|
|
|
switch (opcode)
|
|
{
|
|
case op_lol:
|
|
push(
|
|
new_ir1(
|
|
IR_LOAD, EM_wordsize,
|
|
address_of_local(value)
|
|
)
|
|
);
|
|
break;
|
|
|
|
case op_stl:
|
|
appendir(
|
|
new_ir2(
|
|
IR_STORE, EM_wordsize,
|
|
address_of_local(value),
|
|
pop()
|
|
)
|
|
);
|
|
break;
|
|
|
|
case op_loc:
|
|
push(
|
|
new_wordir(value)
|
|
);
|
|
break;
|
|
|
|
case op_loi:
|
|
push(
|
|
new_ir1(
|
|
IR_LOAD, value,
|
|
pop()
|
|
)
|
|
);
|
|
break;
|
|
|
|
case op_sti:
|
|
right = pop();
|
|
left = pop();
|
|
|
|
appendir(
|
|
new_ir2(
|
|
IR_STORE, value,
|
|
right, left
|
|
)
|
|
);
|
|
break;
|
|
|
|
case op_cmi:
|
|
push(
|
|
tristate_compare(value, IR_COMPARES)
|
|
);
|
|
break;
|
|
|
|
case op_cmu:
|
|
push(
|
|
tristate_compare(value, IR_COMPAREU)
|
|
);
|
|
break;
|
|
|
|
case op_ads:
|
|
right = pop();
|
|
left = pop();
|
|
|
|
if (value != EM_pointersize)
|
|
right = convert(EM_pointersize, value, IR_FROMI1);
|
|
|
|
push(
|
|
new_ir2(
|
|
IR_ADD, EM_wordsize,
|
|
left, right
|
|
)
|
|
);
|
|
break;
|
|
|
|
case op_dup:
|
|
{
|
|
struct ir* v = pop();
|
|
appendir(v);
|
|
push(v);
|
|
push(v);
|
|
break;
|
|
}
|
|
|
|
case op_asp:
|
|
{
|
|
switch (value)
|
|
{
|
|
case 0:
|
|
break;
|
|
|
|
case -1:
|
|
case -2:
|
|
case -4:
|
|
case -8:
|
|
push(new_anyir(-value));
|
|
break;
|
|
|
|
default:
|
|
appendir(
|
|
new_ir2(
|
|
IR_SETREG, EM_pointersize,
|
|
new_regir(IRR_SP),
|
|
new_ir2(
|
|
IR_ADD, EM_pointersize,
|
|
new_regir(IRR_SP),
|
|
new_wordir(value)
|
|
)
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case op_ret:
|
|
{
|
|
if (value > 0)
|
|
{
|
|
left = pop();
|
|
assert(left->size == value);
|
|
appendir(
|
|
new_ir2(
|
|
IR_SETREG, value,
|
|
new_regir(IRR_RR),
|
|
left
|
|
)
|
|
);
|
|
}
|
|
|
|
appendir(
|
|
new_ir0(
|
|
IR_RET, 0
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
fatal("unknown insn_value instruction '%s'",
|
|
em_mnem[opcode - sp_fmnem]);
|
|
}
|
|
}
|
|
|
|
/* vim: set sw=4 ts=4 expandtab : */
|
|
|