#ifndef BASICBLOCK_H
#define BASICBLOCK_H
+struct phi
+{
+ struct basicblock* prev; /* Predecessor that this phi is referring to */
+ struct ir* ir; /* IR of variable definition */
+};
+
struct basicblock
{
const char* name;
ARRAYOF(struct basicblock) nexts;
int order; /* used by dominance graph code */
- ARRAYOF(struct vreg) liveins;
+ PMAPOF(struct vreg, struct phi) phis;
+
+ /* Used by liveness calculation. */
+ ARRAYOF(struct vreg) liveins;
+ ARRAYOF(struct vreg) liveouts;
bool is_fake : 1;
bool is_root : 1;
{
array_appendu(&caller->nexts, ir->u.bvalue);
array_appendu(&ir->u.bvalue->prevs, caller);
+ pmap_add(&cfg.graph, caller, ir->u.bvalue);
}
return false;
}
void update_graph_data(struct procedure* proc)
{
cfg.entry = proc->blocks.item[0];
+ cfg.graph.count = 0;
update_block_pointers_from_ir(proc);
walk_cfg_graph();
struct graph_data
{
struct basicblock* entry;
+ PMAPOF(struct basicblock, struct basicblock) graph;
ARRAYOF(struct basicblock) preorder;
ARRAYOF(struct basicblock) postorder;
};
static const struct burm_emitter_data emitter_data;
-struct hop* new_hop(int insn_no, struct ir* ir)
+struct hop* new_hop(struct basicblock* bb, struct ir* ir)
{
struct hop* hop = calloc(1, sizeof *hop);
hop->id = hop_count++;
- hop->insn_no = insn_no;
+ hop->bb = bb;
hop->ir = ir;
return hop;
}
tracef(k, "%c: %d from $%d:", k, hop->id, hop->ir->id);
for (j=0; j<hop->ins.count; j++)
- tracef(k, " <%%%d", hop->ins.item[j]->id);
+ tracef(k, " r%%%d", hop->ins.item[j]->id);
+ for (j=0; j<hop->throughs.count; j++)
+ tracef(k, " =%%%d", hop->throughs.item[j]->id);
for (j=0; j<hop->outs.count; j++)
- tracef(k, " >%%%d", hop->outs.item[j]->id);
+ tracef(k, " w%%%d", hop->outs.item[j]->id);
tracef(k, " ");
soi = false;
struct hop
{
int id;
- int insn_no;
+ struct basicblock* bb;
struct ir* ir;
ARRAYOF(struct insel) insels;
struct vreg* output;
ARRAYOF(struct vreg) ins;
ARRAYOF(struct vreg) outs;
+ ARRAYOF(struct vreg) throughs;
PMAPOF(struct vreg, struct hreg) registers;
};
-extern struct hop* new_hop(int insn_no, struct ir* ir);
+extern struct hop* new_hop(struct basicblock* bb, struct ir* ir);
extern void hop_add_string_insel(struct hop* hop, const char* string);
extern void hop_add_vreg_insel(struct hop* hop, struct vreg* vreg);
{
if (i > 0)
tracef(k, ", ");
- tracef(k, "$%d", ir->u.phivalue.item[i]->id);
+ tracef(k, "%s=>$%d",
+ ir->u.phivalue.item[i].left->name,
+ ir->u.phivalue.item[i].right->id);
}
break;
}
int rvalue;
const char* lvalue;
struct basicblock* bvalue;
- ARRAYOF(struct ir) phivalue;
+ PMAPOF(struct basicblock, struct ir) phivalue;
} u;
struct vreg* result; /* vreg containing IR result */
#include "mcg.h"
+#include <errno.h>
#include <unistd.h>
static const char* tracechars = NULL;
+FILE* dominance_dot_file = NULL;
+FILE* cfg_dot_file = NULL;
+
bool tracing(char k)
{
if (!tracechars)
opterr = 1;
for (;;)
{
- int c = getopt(argc, argv, "-d:");
+ int c = getopt(argc, argv, "-d:D:C:");
if (c == -1)
break;
switch (c)
{
+ case 'C':
+ cfg_dot_file = fopen(optarg, "w");
+ if (!cfg_dot_file)
+ fatal("couldn't open output file '%s': %s",
+ optarg, strerror(errno));
+ fprintf(cfg_dot_file, "digraph {\n");
+ break;
+
+ case 'D':
+ dominance_dot_file = fopen(optarg, "w");
+ if (!dominance_dot_file)
+ fatal("couldn't open output file '%s': %s",
+ optarg, strerror(errno));
+ fprintf(dominance_dot_file, "digraph {\n");
+ break;
+
case 'd':
tracechars = optarg;
break;
symbol_walk(find_procedures_cb, NULL);
EM_close();
+ if (cfg_dot_file)
+ {
+ fprintf(cfg_dot_file, "}\n");
+ fclose(cfg_dot_file);
+ }
+ if (dominance_dot_file)
+ {
+ fprintf(dominance_dot_file, "}\n");
+ fclose(dominance_dot_file);
+ }
return 0;
}
extern void pass_convert_stack_ops(struct procedure* proc);
extern void pass_eliminate_trivial_blocks(struct procedure* proc);
extern void pass_group_irs(struct procedure* proc);
-extern void pass_instruction_selector(struct procedure* proc);
+extern void pass_instruction_selector(void);
+extern void pass_live_vreg_analysis(void);
extern void pass_promote_float_ops(struct procedure* proc);
-extern void pass_register_allocator(struct procedure* proc);
+extern void pass_register_allocator(void);
extern void pass_remove_dead_blocks(struct procedure* proc);
extern void pass_split_critical_edges(struct procedure* proc);
+extern FILE* dominance_dot_file;
+extern FILE* cfg_dot_file;
+
#endif
/* vim: set sw=4 ts=4 expandtab : */
#include "mcg.h"
static ARRAYOF(struct ir) pops;
-static ARRAYOF(struct ir) pushes;
+static PMAPOF(struct basicblock, struct ir) pushes;
static struct ir* get_last_push(struct basicblock* bb)
{
ir = get_last_push(inbb);
if (!ir || (ir->size != lastpush->size))
return;
- array_appendu(&pushes, ir);
+ pmap_add(&pushes, inbb, ir);
}
}
for (i=0; i<pushes.count; i++)
{
- struct ir* ir = pushes.item[i];
+ struct ir* ir = pushes.item[i].right;
*ir = *ir->left;
}
struct ir* phi = new_ir0(IR_PHI, ir->size);
for (j=0; j<pushes.count; j++)
- array_appendu(&phi->u.phivalue, pushes.item[j]);
+ pmap_add(&phi->u.phivalue,
+ pushes.item[j].left,
+ pushes.item[j].right);
phi->root = phi;
*ir = *phi;
struct vreg* vreg = find_vreg_of_child(child);
if (vreg)
+ {
hop_add_vreg_insel(current_hop, vreg);
+ array_appendu(&vreg->used, current_hop);
+ }
}
static void emit_string(const char* data)
vreg = new_vreg();
}
- insn->hop = current_hop = new_hop(0, insn->ir);
+ insn->hop = current_hop = new_hop(current_bb, insn->ir);
insn->hop->output = vreg;
if (vreg)
+ {
array_appendu(¤t_hop->outs, vreg);
+ vreg->defined = current_hop;
+ }
emit(insn);
hop_print('I', current_hop);
+ array_append(¤t_bb->hops, current_hop);
if (goal != 1)
insn->ir->result = insn->hop->output;
int j;
current_ir->result = new_vreg();
- array_append(¤t_bb->liveins, current_ir->result);
- tracef('I', "I: %d is phi:", current_ir->result->id);
+ tracef('I', "I: $%d is phi:", current_ir->result->id);
for (j=0; j<current_ir->u.phivalue.count; j++)
- tracef('I', " $%d", current_ir->u.phivalue.item[j]->id);
+ {
+ struct basicblock* parentbb = current_ir->u.phivalue.item[j].left;
+ struct ir* parentir = current_ir->u.phivalue.item[j].right;
+ struct phi* phi = calloc(1, sizeof(*phi));
+ tracef('I', " %s=>$%d", parentbb->name, parentir->id);
+
+ phi->prev = parentbb;
+ phi->ir = parentir;
+ pmap_add(¤t_bb->phis, current_ir->result, phi);
+ }
tracef('I', "\n");
}
else
}
}
-void pass_instruction_selector(struct procedure* proc)
+void pass_instruction_selector(void)
{
int i;
- for (i=0; i<proc->blocks.count; i++)
+ for (i=0; i<cfg.preorder.count; i++)
{
- current_bb = proc->blocks.item[i];
+ current_bb = cfg.preorder.item[i];
select_instructions();
}
}
--- /dev/null
+#include "mcg.h"
+
+static bool finished;
+
+static void preload_blocks(void)
+{
+ /* Any variable referenced in a phi *must* be a liveout of one of our
+ * predecessors. */
+
+ int i, j;
+ for (i=0; i<cfg.preorder.count; i++)
+ {
+ struct basicblock* bb = cfg.preorder.item[i];
+
+ for (j=0; j<bb->phis.count; j++)
+ {
+ struct vreg* vreg = bb->phis.item[j].left;
+ struct phi* phi = bb->phis.item[j].right;
+
+ assert(array_contains(&bb->prevs, phi->prev));
+ array_appendu(&phi->prev->liveouts, phi->ir->result);
+ }
+ }
+}
+
+static void propagate_liveness(struct basicblock* bb)
+{
+ static ARRAYOF(struct vreg) current;
+ int i;
+
+ current.count = 0;
+ array_appendall(¤t, &bb->liveouts);
+
+ for (i=bb->hops.count-1; i>=0; i--)
+ {
+ struct hop* hop = bb->hops.item[i];
+
+ array_removeall(¤t, &hop->outs);
+ finished &= array_appendallu(&hop->throughs, ¤t);
+ array_appendallu(¤t, &hop->ins);
+ }
+
+ for (i=0; i<bb->phis.count; i++)
+ array_remove(¤t, bb->phis.item[i].left);
+
+ finished &= array_appendallu(&bb->liveins, ¤t);
+
+ for (i=0; i<bb->prevs.count; i++)
+ {
+ struct basicblock* prev = bb->prevs.item[i];
+ finished &= array_appendallu(&prev->liveouts, ¤t);
+ }
+}
+
+void pass_live_vreg_analysis(void)
+{
+ int i;
+
+ preload_blocks();
+
+ do
+ {
+ finished = true;
+
+ tracef('L', "L: beginning liveness pass\n");
+ for (i=0; i<dominance.postorder.count; i++)
+ propagate_liveness(dominance.postorder.item[i]);
+ }
+ while (!finished);
+}
+
+/* vim: set sw=4 ts=4 expandtab : */
+
#include "mcg.h"
-static ARRAYOF(struct basicblock) blocks;
-
-static void recursively_walk_dominance_graph(struct basicblock* bb)
-{
- int i;
-
- array_append(&blocks, bb);
- tracef('R', "R: considering block %s\n", bb->name);
-
- /* Skip the entry block (which is its own dominator). */
-
- for (i=1; i<dominance.graph.count; i++)
- {
- struct basicblock* left = dominance.graph.item[i].left;
- struct basicblock* right = dominance.graph.item[i].right;
- if (right == bb)
- recursively_walk_dominance_graph(left);
- }
-}
-
-void pass_register_allocator(struct procedure* proc)
+void pass_register_allocator(void)
{
- blocks.count = 0;
- recursively_walk_dominance_graph(cfg.entry);
- assert(blocks.count == proc->blocks.count);
}
/* vim: set sw=4 ts=4 expandtab : */
(ir->opcode == IR_PHI) &&
array_contains(&needsphis, nextbb))
{
- array_appendu(&ir->u.phivalue, definitions.item[definitions.count-1]);
+ pmap_add(&ir->u.phivalue,
+ bb, definitions.item[definitions.count-1]);
}
recursively_rewrite_tree(nextbb);
}
}
+static void print_hops(char k, struct procedure* proc)
+{
+ int i;
+
+ tracef(k, "%c: procedure %s\n", k, proc->name);
+ for (int i=0; i<dominance.preorder.count; i++)
+ {
+ struct basicblock* bb = dominance.preorder.item[i];
+ int j;
+
+ tracef(k, "%c:\n", k);
+ tracef(k, "%c: %sBLOCK: %s\n", k,
+ bb->is_fake ? "FAKE " : "",
+ bb->name);
+
+ if (bb->prevs.count > 0)
+ {
+ tracef(k, "%c: FROM:", k);
+ for (j=0; j<bb->prevs.count; j++)
+ tracef(k, " %s", bb->prevs.item[j]->name);
+ tracef(k, "\n");
+ }
+
+ if (bb->nexts.count > 0)
+ {
+ tracef(k, "%c: TO:", k);
+ for (j=0; j<bb->nexts.count; j++)
+ tracef(k, " %s", bb->nexts.item[j]->name);
+ tracef(k, "\n");
+ }
+
+ if (bb->liveins.count > 0)
+ {
+ tracef(k, "%c: INS:", k);
+ for (j=0; j<bb->liveins.count; j++)
+ tracef(k, " %%%d", bb->liveins.item[j]->id);
+ tracef(k, "\n");
+ }
+
+ if (bb->liveouts.count > 0)
+ {
+ tracef(k, "%c: OUTS:", k);
+ for (j=0; j<bb->liveouts.count; j++)
+ tracef(k, " %%%d", bb->liveouts.item[j]->id);
+ tracef(k, "\n");
+ }
+
+ if (bb->phis.count > 0)
+ {
+ tracef(k, "%c: PHIS:", k);
+ for (j=0; j<bb->phis.count; j++)
+ {
+ struct vreg* vreg = bb->phis.item[j].left;
+ struct phi* phi = bb->phis.item[j].right;
+
+ tracef(k, " %%%d(via %s)=>%%%d",
+ phi->ir->result->id,
+ phi->prev->name,
+ vreg->id);
+ }
+ tracef(k, "\n");
+ }
+
+ for (j=0; j<bb->hops.count; j++)
+ hop_print(k, bb->hops.item[j]);
+ }
+}
+
+static void write_cfg_graph(const char* name)
+{
+ int i;
+
+ fprintf(cfg_dot_file, "subgraph %s {\n", name);
+ fprintf(cfg_dot_file, "\t%s [color=red];\n", cfg.entry->name);
+
+ for (i=0; i<cfg.graph.count; i++)
+ {
+ fprintf(cfg_dot_file, "\t%s -> %s;\n",
+ cfg.graph.item[i].left->name,
+ cfg.graph.item[i].right->name);
+ }
+
+ fprintf(cfg_dot_file, "}\n");
+}
+
+static void write_dominance_graph(const char* name)
+{
+ int i;
+
+ fprintf(dominance_dot_file, "subgraph %s {\n", name);
+ fprintf(dominance_dot_file, "\t%s [color=green];\n", cfg.entry->name);
+
+ for (i=0; i<dominance.graph.count; i++)
+ {
+ fprintf(dominance_dot_file, "\t%s -> %s;\n",
+ dominance.graph.item[i].right->name,
+ dominance.graph.item[i].left->name);
+ }
+
+ fprintf(dominance_dot_file, "}\n");
+}
+
void procedure_compile(struct procedure* proc)
{
int i;
pass_eliminate_trivial_blocks(proc);
pass_remove_dead_blocks(proc);
+ print_blocks('2', proc);
+ update_graph_data(proc);
+ pass_split_critical_edges(proc);
update_graph_data(proc);
/* Passes from here on can't alter the BB graph without also updating prevs
* and nexts (and then calling update_graph_data()). */
- print_blocks('2', proc);
- pass_convert_stack_ops(proc);
print_blocks('3', proc);
- pass_convert_locals_to_ssa(proc);
+ pass_convert_stack_ops(proc);
print_blocks('4', proc);
- pass_promote_float_ops(proc);
+ pass_convert_locals_to_ssa(proc);
print_blocks('5', proc);
- pass_split_critical_edges(proc);
+ pass_promote_float_ops(proc);
print_blocks('6', proc);
- update_graph_data(proc);
+ pass_instruction_selector();
+ pass_live_vreg_analysis();
+ print_hops('7', proc);
+ //pass_register_allocator();
- pass_instruction_selector(proc);
- pass_register_allocator(proc);
+ if (cfg_dot_file)
+ write_cfg_graph(proc->name);
+ if (dominance_dot_file)
+ write_dominance_graph(proc->name);
}
/* vim: set sw=4 ts=4 expandtab : */
struct vreg
{
int id;
+ struct hop* defined;
+ ARRAYOF(struct hop) used;
};
extern struct vreg* new_vreg(void);