Store the EM code up front and build the basic block graph *before*
generating the IR code. Lots more IR code.
This commit is contained in:
parent
176cd7365c
commit
6ce2495aeb
|
@ -29,7 +29,10 @@ cprogram {
|
|||
"./*.h",
|
||||
},
|
||||
vars = {
|
||||
["+cflags"] = {"-Werror-implicit-function-declaration"}
|
||||
["+cflags"] = {
|
||||
"-Werror-implicit-function-declaration",
|
||||
"-Wint-conversion"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 basicblock {
|
||||
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
|
||||
{
|
||||
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 : */
|
||||
|
|
|
@ -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;
|
||||
switch (insn.em_argtype)
|
||||
{
|
||||
case ilb_ptyp:
|
||||
queue_insn_ilabel(insn.em_opcode, insn.em_ilb);
|
||||
break;
|
||||
|
||||
case nof_ptyp:
|
||||
tb_insn_label(insn.em_opcode, flags,
|
||||
dlabel_to_str(insn.em_dlb), insn.em_off);
|
||||
break;
|
||||
case nof_ptyp:
|
||||
queue_insn_label(insn.em_opcode,
|
||||
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 sof_ptyp:
|
||||
queue_insn_label(insn.em_opcode,
|
||||
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 pro_ptyp:
|
||||
queue_insn_label(insn.em_opcode,
|
||||
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;
|
||||
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");
|
||||
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);
|
||||
|
|
|
@ -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,27 +19,86 @@ static void push(struct ir* ir)
|
|||
if (stackptr == sizeof(stack)/sizeof(*stack))
|
||||
fatal("stack overflow");
|
||||
|
||||
/* If we try to push something which is too small, convert it to a word
|
||||
* first. */
|
||||
|
||||
if (ir->size < EM_wordsize)
|
||||
ir = convert(ir, EM_wordsize, IR_FROMU1);
|
||||
|
||||
stack[stackptr++] = ir;
|
||||
}
|
||||
|
||||
static struct ir* pop(void)
|
||||
static struct ir* pop(int size)
|
||||
{
|
||||
if (stackptr == 0)
|
||||
fatal("stack underflow");
|
||||
{
|
||||
/* Nothing in our fake stack, so we have to read from the real stack. */
|
||||
|
||||
return stack[--stackptr];
|
||||
if (size < EM_wordsize)
|
||||
size = EM_wordsize;
|
||||
return
|
||||
new_ir0(
|
||||
IR_POP, size
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct ir* ir = stack[--stackptr];
|
||||
|
||||
/* 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);
|
||||
|
||||
if (ir->size != size)
|
||||
fatal("expected an item on stack of size %d, but got %d\n", size, ir->size);
|
||||
return ir;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_stack(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("\t; stack:");
|
||||
for (i=0; i<stackptr; i++)
|
||||
{
|
||||
struct ir* ir = stack[i];
|
||||
printf(" $%d.%d", ir->id, ir->size);
|
||||
}
|
||||
printf(" (top)\n");
|
||||
}
|
||||
|
||||
static struct ir* appendir(struct ir* ir)
|
||||
{
|
||||
assert(currentbb != NULL);
|
||||
ir->sequence = true;
|
||||
APPEND(currentbb->irs, ir);
|
||||
int i;
|
||||
|
||||
assert(current_bb != NULL);
|
||||
ir->is_sequence = true;
|
||||
APPEND(current_bb->irs, ir);
|
||||
|
||||
ir_print(ir);
|
||||
return ir;
|
||||
}
|
||||
|
||||
static void materialise_stack(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=stackptr-1; i>=0; i--)
|
||||
{
|
||||
struct ir* ir = stack[i];
|
||||
appendir(
|
||||
new_ir1(
|
||||
IR_PUSH, ir->size,
|
||||
ir
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
reset_stack();
|
||||
}
|
||||
|
||||
void tb_filestart(void)
|
||||
{
|
||||
}
|
||||
|
@ -46,105 +107,6 @@ 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 */
|
||||
|
@ -160,10 +122,29 @@ static struct ir* address_of_local(int index)
|
|||
);
|
||||
}
|
||||
|
||||
static struct ir* convert(struct ir* src, int destsize, int opcode)
|
||||
{
|
||||
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", src->size);
|
||||
}
|
||||
|
||||
return
|
||||
new_ir1(
|
||||
opcode, destsize,
|
||||
src
|
||||
);
|
||||
}
|
||||
|
||||
static struct ir* tristate_compare(int size, int opcode)
|
||||
{
|
||||
struct ir* right = pop();
|
||||
struct ir* left = pop();
|
||||
struct ir* right = pop(size);
|
||||
struct ir* left = pop(size);
|
||||
|
||||
return
|
||||
new_ir2(
|
||||
|
@ -172,103 +153,140 @@ static struct ir* tristate_compare(int size, int opcode)
|
|||
);
|
||||
}
|
||||
|
||||
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)
|
||||
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);
|
||||
|
||||
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)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static void compare0_branch2(int opcode,
|
||||
struct basicblock* truebb, struct basicblock* falsebb,
|
||||
int irop)
|
||||
{
|
||||
push(
|
||||
new_wordir(0)
|
||||
);
|
||||
|
||||
simple_branch2(opcode, EM_wordsize, truebb, falsebb, irop);
|
||||
}
|
||||
|
||||
static void insn_bvalue(int opcode, struct basicblock* leftbb, struct basicblock* rightbb)
|
||||
{
|
||||
switch (opcode)
|
||||
{
|
||||
case op_zne:
|
||||
{
|
||||
struct basicblock* truebb = bb_get(label);
|
||||
struct basicblock* falsebb = bb_get(NULL);
|
||||
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;
|
||||
|
||||
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_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* left;
|
||||
struct ir* right;
|
||||
struct ir* val = pop(size);
|
||||
|
||||
push(
|
||||
new_ir1(
|
||||
irop, size,
|
||||
val
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static void simple_alu2(int opcode, int size, int irop)
|
||||
{
|
||||
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 : */
|
||||
|
||||
|
|
Loading…
Reference in a new issue