array_append(array, count, max, value);
}
+void array_remove(void** array, int* count, void* value)
+{
+ int i;
+
+ for (i=0; i<*count; i++)
+ {
+ if (array[i] == value)
+ {
+ while (i < (*count-1))
+ {
+ array[i] = array[i+1];
+ i++;
+ }
+ (*count)--;
+ return;
+ }
+ }
+}
+
/* vim: set sw=4 ts=4 expandtab : */
#define APPENDU(ARRAY, VALUE) \
array_appendu((void***) &ARRAY, &ARRAY##_count, &ARRAY##_max, VALUE)
+#define REMOVE(ARRAY, VALUE) \
+ array_remove((void**) ARRAY, &ARRAY##_count, VALUE)
+
extern void array_append(void*** array, int* count, int* max, void* value);
extern bool array_contains(void** array, int count, void* value);
extern void array_appendu(void*** array, int* count, int* max, void* value);
+extern void array_remove(void** array, int* count, void* value);
#endif
struct basicblock* bb = proc->blocks[i];
int j;
- tracef(k, "%c: block %s\n", k, bb->name);
+ tracef(k, "%c:\n", k);
+ tracef(k, "%c: BLOCK: %s\n", k, bb->name);
+ tracef(k, "%c: from:", k);
for (int j=0; j<bb->inblocks_count; j++)
{
struct basicblock* obb = bb->inblocks[j];
- tracef(k, "%c: %s ->\n", k, obb->name);
+ tracef(k, " %s", obb->name);
}
-
- for (int j=0; j<bb->irs_count; j++)
- ir_print(k, bb->irs[j]);
-
+ tracef(k, "\n");
+ tracef(k, "%c: to:", k);
for (int j=0; j<bb->outblocks_count; j++)
{
struct basicblock* obb = bb->outblocks[j];
- tracef(k, "%c: -> %s\n", k, obb->name);
+ tracef(k, " %s", obb->name);
}
+ tracef(k, "\n");
+
+ for (int j=0; j<bb->irs_count; j++)
+ ir_print(k, bb->irs[j]);
}
}
+static void remove_dead_blocks(struct procedure* proc)
+{
+ int i, j;
+
+again:
+ /* Starts at 1 because we don't want to remove the root block! */
+ for (i=1; i<proc->blocks_count; i++)
+ {
+ struct basicblock* bb = proc->blocks[i];
+
+ if (bb->inblocks_count == 0)
+ {
+ /* Nobody uses this block; disconnect it from its output
+ * blocks. */
+ for (j=0; j<bb->outblocks_count; j++)
+ REMOVE(bb->outblocks[j]->inblocks, bb);
+
+ REMOVE(proc->blocks, bb);
+ goto again;
+ }
+ }
+}
+
+static void splice_adjacent_blocks(struct procedure* proc)
+{
+ int i, j;
+
+again:
+ for (i=0; i<proc->blocks_count; i++)
+ {
+ struct basicblock* bb = proc->blocks[i];
+ if (bb->outblocks_count == 1)
+ {
+ struct basicblock* outbb = bb->outblocks[0];
+ if (outbb->inblocks_count == 1)
+ {
+ struct ir* lastir = bb->irs[bb->irs_count-1];
+
+ if ((lastir->opcode == IR_JUMP)
+ && (lastir->left->opcode == IR_BLOCK)
+ && (lastir->left->u.bvalue == outbb))
+ {
+ /* Remove jump instruction. */
+
+ bb->irs_count--;
+
+ REMOVE(bb->outblocks, outbb);
+ REMOVE(outbb->inblocks, bb);
+
+ for (j=0; j<outbb->irs_count; j++)
+ APPEND(bb->irs, outbb->irs[j]);
+ for (j=0; j<outbb->outblocks_count; j++)
+ {
+ APPENDU(bb->outblocks, outbb->outblocks[j]);
+ APPENDU(outbb->outblocks[j]->inblocks, bb);
+ REMOVE(outbb->outblocks[j]->inblocks, outbb);
+ }
+
+ REMOVE(proc->blocks, outbb);
+ goto again;
+ }
+ }
+ }
+ }
+}
+
void compile(struct procedure* proc)
{
int i;
print_blocks('1', proc);
+
+ remove_dead_blocks(proc);
+
+ for (i=0; i<proc->blocks_count; i++)
+ sse_convert_block_parameters(proc->blocks[i]);
+
+ splice_adjacent_blocks(proc);
+
+ print_blocks('2', proc);
}
/* vim: set sw=4 ts=4 expandtab : */
return ir;
}
-void ir_print(char k, const struct ir* ir)
+struct ir* ir_find(struct ir* ir, int opcode)
{
+ if (ir->opcode == opcode)
+ return ir;
+
if (ir->left && !ir->left->is_sequence)
- ir_print(k, ir->left);
+ {
+ struct ir* irr = ir_find(ir->left, opcode);
+ if (irr)
+ return irr;
+ }
+
if (ir->right && !ir->right->is_sequence)
- ir_print(k, ir->right);
+ {
+ struct ir* irr = ir_find(ir->right, opcode);
+ if (irr)
+ return irr;
+ }
- tracef(k, "%c: %c ", k, ir->is_sequence ? 'S' : ' ');
- tracef(k, "$%d = ", ir->id);
+ return NULL;
+}
+
+static void print_expr(char k, const struct ir* ir)
+{
tracef(k, "%s", ir_names[ir->opcode]);
if (ir->size)
tracef(k, "%d", ir->size);
tracef(k, "%s", ir->u.bvalue->name);
break;
+ case IR_PHI:
+ {
+ int i;
+
+ for (i=0; i<ir->u.phivalue.imports_count; i++)
+ {
+ if (i > 0)
+ tracef(k, ", ");
+ tracef(k, "$%d", ir->u.phivalue.imports[i]->id);
+ }
+ }
+
default:
if (ir->left)
- tracef(k, "$%d", ir->left->id);
+ {
+ if (ir->left->is_sequence)
+ tracef(k, "$%d", ir->left->id);
+ else
+ print_expr(k, ir->left);
+ }
if (ir->right)
- tracef(k, ", $%d", ir->right->id);
+ {
+ tracef(k, ", ");
+ if (ir->right->is_sequence)
+ tracef(k, "$%d", ir->right->id);
+ else
+ print_expr(k, ir->right);
+ }
}
- tracef(k, ")\n");
+ tracef(k, ")");
+}
+
+void ir_print(char k, const struct ir* ir)
+{
+ tracef(k, "%c: $%d = ", k, ir->id);
+ print_expr(k, ir);
+ tracef(k, "\n");
}
/* vim: set sw=4 ts=4 expandtab : */
PAIR
ANY
LOCAL
+PHI
# Magic stack operations
PUSH
POP
+SET
# Memory operations
LOAD
IFLT
IFLE
-# Flow control
+# Procedures
CALL
+
+# Flow control --- these never return
JUMP
CJUMP
RET
int size;
struct ir* left;
struct ir* right;
- union {
+ union
+ {
arith ivalue;
int rvalue;
const char* lvalue;
struct basicblock* bvalue;
+ struct
+ {
+ ARRAY(struct ir, imports);
+ } phivalue;
} u;
bool is_sequence : 1;
};
extern struct ir* new_anyir(int size);
extern struct ir* new_localir(int offset);
+extern struct ir* ir_find(struct ir* ir, int opcode);
+
extern void ir_print(char k, const struct ir* ir);
#include "ircodes.h"
{
switch (k)
{
- case 'E': return true;
- case '0': return true;
+ case 'E': return false;
+ case '0': return false;
case '1': return true;
case '2': return true;
default: return true;
{
const char* name;
ARRAY(struct insn, insns);
- ARRAY(struct ir, allirs);
ARRAY(struct ir, irs);
ARRAY(struct basicblock, inblocks);
ARRAY(struct basicblock, outblocks);
+ ARRAY(struct ir, inparams);
+ ARRAY(struct ir, outparams);
bool is_root : 1;
bool is_terminated : 1;
};
extern void tb_procedure(struct procedure* proc);
extern void tb_regvar(arith offset, int size, int type, int priority);
+extern void sse_convert_block_parameters(struct basicblock* bb);
+
extern void compile(struct procedure* proc);
#endif
{
case op_csa:
case op_csb:
+ case op_ret:
terminate_block();
break;
}
terminate_block();
}
+static void change_basicblock(struct basicblock* newbb)
+{
+ APPENDU(current_proc->blocks, newbb);
+
+ if (code_bb && !code_bb->is_terminated)
+ queue_insn_block(op_bra, newbb, NULL);
+
+ code_bb = newbb;
+}
+
static void queue_insn_ilabel(int opcode, int label)
{
const char* name = ilabel_to_str(insn.em_ilb);
case op_zle:
case op_zgt:
case op_zge:
- queue_insn_block(insn.em_opcode, left, bb_get(NULL));
+ {
+ struct basicblock* bb = bb_get(NULL);
+ queue_insn_block(insn.em_opcode, left, bb);
+ change_basicblock(bb);
break;
+ }
default:
fatal("parse_em: unhandled conditional '%s'",
}
}
-static void change_basicblock(struct basicblock* newbb)
-{
- APPENDU(current_proc->blocks, newbb);
-
- if (code_bb && !code_bb->is_terminated)
- queue_insn_block(op_bra, newbb, NULL);
-
- code_bb = newbb;
-}
-
static void queue_ilabel(arith label)
{
change_basicblock(bb_get(ilabel_to_str(label)));
--- /dev/null
+#include "mcg.h"
+
+static struct ir* get_last_push(struct basicblock* bb)
+{
+ int i;
+
+ for (i=bb->irs_count-1; i>=0; i--)
+ {
+ struct ir* ir = bb->irs[i];
+
+ if (ir->opcode == IR_PUSH)
+ return ir;
+ if (ir_find(ir, IR_POP))
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static struct ir* get_first_pop(struct basicblock* bb)
+{
+ int i;
+
+ for (i=0; i<bb->irs_count; i++)
+ {
+ struct ir* irr;
+ struct ir* ir = bb->irs[i];
+
+ if (ir->opcode == IR_PUSH)
+ return NULL;
+
+ irr = ir_find(ir, IR_POP);
+ if (irr)
+ return irr;
+ }
+
+ return NULL;
+}
+
+void sse_convert_block_parameters(struct basicblock* bb)
+{
+ int i, j;
+ struct ir* ir;
+
+ for (;;)
+ {
+ struct ir* lastpush = get_last_push(bb);
+ if (!lastpush)
+ return;
+
+ /* Abort unless *every* success block of this one starts with a pop
+ * of the same size... */
+
+ for (i=0; i<bb->outblocks_count; i++)
+ {
+ struct basicblock* outbb = bb->outblocks[i];
+
+ ir = get_first_pop(outbb);
+ if (!ir || (ir->size != lastpush->size))
+ return;
+
+ /* Also abort unless *every* predecessor block of the one we've
+ * just found *also* ends in a push of the same size. */
+
+ for (j=0; j<outbb->inblocks_count; j++)
+ {
+ struct basicblock* inbb = outbb->inblocks[i];
+
+ ir = get_last_push(inbb);
+ if (!ir || (ir->size != lastpush->size))
+ return;
+ }
+ }
+
+ /* Okay, now we can wire them all up. */
+
+ for (i=0; i<bb->outblocks_count; i++)
+ {
+ struct basicblock* outbb = bb->outblocks[i];
+ struct ir* phi = get_first_pop(outbb);
+ phi->opcode = IR_PHI;
+
+ /* Also abort unless *every* predecessor block of the one we've
+ * just found *also* ends in a push of the same size. */
+
+ for (j=0; j<outbb->inblocks_count; j++)
+ {
+ struct basicblock* inbb = outbb->inblocks[j];
+
+ ir = get_last_push(inbb);
+ *ir = *ir->left;
+ ir->is_sequence = true;
+ APPEND(phi->u.phivalue.imports, ir);
+ }
+ }
+ }
+}
+
+/* vim: set sw=4 ts=4 expandtab : */
tracef('E', " (top)\n");
}
-static void appendallirs(struct ir* ir)
-{
- if (CONTAINS(current_bb->allirs, ir))
- fatal("ir reachable from more than one place");
-
- APPEND(current_bb->allirs, ir);
- if (ir->left && !ir->left->is_sequence)
- appendallirs(ir->left);
- if (ir->right && !ir->right->is_sequence)
- appendallirs(ir->right);
-}
-
static struct ir* appendir(struct ir* ir)
{
int i;
assert(current_bb != NULL);
ir->is_sequence = true;
APPEND(current_bb->irs, ir);
- appendallirs(ir);
ir_print('0', ir);
return ir;