summaryrefslogtreecommitdiff
path: root/util/romcc/romcc.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/romcc/romcc.c')
-rw-r--r--util/romcc/romcc.c308
1 files changed, 241 insertions, 67 deletions
diff --git a/util/romcc/romcc.c b/util/romcc/romcc.c
index 48632a8fc6..42e06d3498 100644
--- a/util/romcc/romcc.c
+++ b/util/romcc/romcc.c
@@ -23,7 +23,6 @@
#warning "FIXME boundary cases with small types in larger registers"
#warning "FIXME give clear error messages about unused variables"
#warning "FIXME properly handle multi dimensional arrays"
-#warning "FIXME fix scc_transform"
/* Control flow graph of a loop without goto.
*
@@ -1015,6 +1014,9 @@ static void __internal_warning(struct compile_state *state, struct triple *ptr,
va_list args;
va_start(args, fmt);
loc(stderr, state, ptr);
+ if (ptr) {
+ fprintf(stderr, "%p %s ", ptr, tops(ptr->op));
+ }
fprintf(stderr, "Internal compiler warning: ");
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
@@ -1184,40 +1186,6 @@ static void unuse_triple(struct triple *used, struct triple *unuser)
}
}
-static void push_triple(struct triple *used, struct triple *user)
-{
- struct triple_set *new;
- if (!used)
- return;
- if (!user)
- return;
- /* Append new to the head of the list,
- * it's the only sensible behavoir for a stack.
- */
- new = xcmalloc(sizeof(*new), "triple_set");
- new->member = user;
- new->next = used->use;
- used->use = new;
-}
-
-static void pop_triple(struct triple *used, struct triple *unuser)
-{
- struct triple_set *use, **ptr;
- ptr = &used->use;
- while(*ptr) {
- use = *ptr;
- if (use->member == unuser) {
- *ptr = use->next;
- xfree(use);
- /* Only free one occurance from the stack */
- return;
- }
- else {
- ptr = &use->next;
- }
- }
-}
-
static void put_occurance(struct occurance *occurance)
{
occurance->count -= 1;
@@ -1827,6 +1795,15 @@ static void release_triple(struct compile_state *state, struct triple *ptr)
{
struct triple_set *set, *next;
struct triple **expr;
+ struct block *block;
+ /* Make certain the we are not the first or last element of a block */
+ block = block_of_triple(state, ptr);
+ if (block && (block->last == ptr)) {
+ block->last = ptr->prev;
+ }
+ if (block && (block->first == ptr)) {
+ block->first = ptr->next;
+ }
/* Remove ptr from use chains where it is the user */
expr = triple_rhs(state, ptr, 0);
for(; expr; expr = triple_rhs(state, ptr, expr)) {
@@ -4571,7 +4548,7 @@ static struct triple *array_to_pointer(struct compile_state *state, struct tripl
type = new_type(
TYPE_POINTER | (def->type->type & QUAL_MASK),
def->type->left, 0);
- if ((def->op == OP_SDECL) || is_const(def)) {
+ if ((def->op == OP_SDECL) || IS_CONST_OP(def->op)) {
struct triple *addrconst;
if ((def->op != OP_SDECL) && (def->op != OP_BLOBCONST)) {
internal_error(state, def, "bad array constant");
@@ -9586,7 +9563,7 @@ static struct block *basic_block(struct compile_state *state,
struct triple *first)
{
struct block *block;
- struct triple *ptr;
+ struct triple *ptr, *final;
int op;
if (first->op != OP_LABEL) {
internal_error(state, 0, "block does not start with a label");
@@ -9595,6 +9572,11 @@ static struct block *basic_block(struct compile_state *state,
if (first->u.block != 0) {
return first->u.block;
}
+ /* Lookup the final instruction.
+ * It is important that the final instruction has it's own
+ * basic block.
+ */
+ final = RHS(state->main_function, 0)->prev;
/* Allocate another basic block structure */
state->last_vertex += 1;
block = xcmalloc(sizeof(*block), "block");
@@ -9602,7 +9584,8 @@ static struct block *basic_block(struct compile_state *state,
block->vertex = state->last_vertex;
ptr = first;
do {
- if ((ptr != first) && (ptr->op == OP_LABEL) && ptr->use) {
+ if ((ptr != first) && (ptr->op == OP_LABEL) &&
+ ((ptr->use) || ptr == final)) {
break;
}
block->last = ptr;
@@ -10486,10 +10469,10 @@ static void insert_phi_operations(struct compile_state *state)
/* Count how many edges flow into this block */
in_edges = front->users;
/* Insert a phi function for this variable */
- get_occurance(front->first->occurance);
+ get_occurance(var->occurance);
phi = alloc_triple(
state, OP_PHI, var->type, -1, in_edges,
- front->first->occurance);
+ var->occurance);
phi->u.block = front;
MISC(phi, 0) = var;
use_triple(var, phi);
@@ -10515,12 +10498,71 @@ static void insert_phi_operations(struct compile_state *state)
xfree(work);
}
+
+static int count_and_number_adecls(struct compile_state *state)
+{
+ struct triple *first, *ins;
+ int adecls = 0;
+ first = RHS(state->main_function, 0);
+ ins = first;
+ do {
+ if (ins->op == OP_ADECL) {
+ adecls += 1;
+ ins->id = adecls;
+ }
+ ins = ins->next;
+ } while(ins != first);
+ return adecls;
+}
+
+static struct triple *peek_triple(struct triple_set **stacks, struct triple *var)
+{
+ struct triple_set *head;
+ struct triple *top_val;
+ top_val = 0;
+ head = stacks[var->id];
+ if (head) {
+ top_val = head->member;
+ }
+ return top_val;
+}
+
+static void push_triple(struct triple_set **stacks, struct triple *var, struct triple *val)
+{
+ struct triple_set *new;
+ /* Append new to the head of the list,
+ * it's the only sensible behavoir for a stack.
+ */
+ new = xcmalloc(sizeof(*new), "triple_set");
+ new->member = val;
+ new->next = stacks[var->id];
+ stacks[var->id] = new;
+}
+
+static void pop_triple(struct triple_set **stacks, struct triple *var, struct triple *oldval)
+{
+ struct triple_set *set, **ptr;
+ ptr = &stacks[var->id];
+ while(*ptr) {
+ set = *ptr;
+ if (set->member == oldval) {
+ *ptr = set->next;
+ xfree(set);
+ /* Only free one occurance from the stack */
+ return;
+ }
+ else {
+ ptr = &set->next;
+ }
+ }
+}
+
/*
* C(V)
* S(V)
*/
static void fixup_block_phi_variables(
- struct compile_state *state, struct block *parent, struct block *block)
+ struct compile_state *state, struct triple_set **stacks, struct block *parent, struct block *block)
{
struct block_set *set;
struct triple *ptr;
@@ -10545,8 +10587,8 @@ static void fixup_block_phi_variables(
internal_error(state, ptr, "no var???");
}
/* Find the current value of the variable */
- val = var->use->member;
- if ((val->op == OP_WRITE) || (val->op == OP_READ)) {
+ val = peek_triple(stacks, var);
+ if (val && ((val->op == OP_WRITE) || (val->op == OP_READ))) {
internal_error(state, val, "bad value in phi");
}
if (edge >= TRIPLE_RHS(ptr->sizes)) {
@@ -10567,7 +10609,7 @@ static void fixup_block_phi_variables(
static void rename_block_variables(
- struct compile_state *state, struct block *block)
+ struct compile_state *state, struct triple_set **stacks, struct block *block)
{
struct block_set *user;
struct triple *ptr, *next, *last;
@@ -10586,11 +10628,11 @@ static void rename_block_variables(
struct triple *var, *val;
var = RHS(ptr, 0);
unuse_triple(var, ptr);
- if (!var->use) {
+ /* Find the current value of the variable */
+ val = peek_triple(stacks, var);
+ if (!val) {
error(state, ptr, "variable used without being set");
}
- /* Find the current value of the variable */
- val = var->use->member;
if ((val->op == OP_WRITE) || (val->op == OP_READ)) {
internal_error(state, val, "bad value in read");
}
@@ -10623,24 +10665,24 @@ static void rename_block_variables(
propogate_use(state, ptr, tval);
unuse_triple(var, ptr);
/* Push OP_WRITE ptr->right onto a stack of variable uses */
- push_triple(var, tval);
+ push_triple(stacks, var, tval);
}
if (ptr->op == OP_PHI) {
struct triple *var;
var = MISC(ptr, 0);
/* Push OP_PHI onto a stack of variable uses */
- push_triple(var, ptr);
+ push_triple(stacks, var, ptr);
}
last = ptr;
}
block->last = last;
/* Fixup PHI functions in the cf successors */
- fixup_block_phi_variables(state, block, block->left);
- fixup_block_phi_variables(state, block, block->right);
+ fixup_block_phi_variables(state, stacks, block, block->left);
+ fixup_block_phi_variables(state, stacks, block, block->right);
/* rename variables in the dominated nodes */
for(user = block->idominates; user; user = user->next) {
- rename_block_variables(state, user->member);
+ rename_block_variables(state, stacks, user->member);
}
/* pop the renamed variable stack */
last = block->first;
@@ -10654,7 +10696,7 @@ static void rename_block_variables(
struct triple *var;
var = RHS(ptr, 0);
/* Pop OP_WRITE ptr->right from the stack of variable uses */
- pop_triple(var, RHS(ptr, 1));
+ pop_triple(stacks, var, RHS(ptr, 1));
release_triple(state, ptr);
continue;
}
@@ -10662,7 +10704,7 @@ static void rename_block_variables(
struct triple *var;
var = MISC(ptr, 0);
/* Pop OP_WRITE ptr->right from the stack of variable uses */
- pop_triple(var, ptr);
+ pop_triple(stacks, var, ptr);
}
last = ptr;
}
@@ -10708,15 +10750,117 @@ static void prune_block_variables(struct compile_state *state,
}
}
+struct phi_triple {
+ struct triple *phi;
+ unsigned orig_id;
+ int alive;
+};
+
+static void keep_phi(struct compile_state *state, struct phi_triple *live, struct triple *phi)
+{
+ struct triple **slot;
+ int zrhs, i;
+ if (live[phi->id].alive) {
+ return;
+ }
+ live[phi->id].alive = 1;
+ zrhs = TRIPLE_RHS(phi->sizes);
+ slot = &RHS(phi, 0);
+ for(i = 0; i < zrhs; i++) {
+ struct triple *used;
+ used = slot[i];
+ if (used && (used->op == OP_PHI)) {
+ keep_phi(state, live, used);
+ }
+ }
+}
+
+static void prune_unused_phis(struct compile_state *state)
+{
+ struct triple *first, *phi;
+ struct phi_triple *live;
+ int phis, i;
+
+
+ /* Find the first instruction */
+ first = RHS(state->main_function, 0);
+
+ /* Count how many phi functions I need to process */
+ phis = 0;
+ for(phi = first->next; phi != first; phi = phi->next) {
+ if (phi->op == OP_PHI) {
+ phis += 1;
+ }
+ }
+
+ /* Mark them all dead */
+ live = xcmalloc(sizeof(*live) * (phis + 1), "phi_triple");
+ phis = 0;
+ for(phi = first->next; phi != first; phi = phi->next) {
+ if (phi->op != OP_PHI) {
+ continue;
+ }
+ live[phis].alive = 0;
+ live[phis].orig_id = phi->id;
+ live[phis].phi = phi;
+ phi->id = phis;
+ phis += 1;
+ }
+
+ /* Mark phis alive that are used by non phis */
+ for(i = 0; i < phis; i++) {
+ struct triple_set *set;
+ for(set = live[i].phi->use; !live[i].alive && set; set = set->next) {
+ if (set->member->op != OP_PHI) {
+ keep_phi(state, live, live[i].phi);
+ break;
+ }
+ }
+ }
+
+ /* Delete the extraneous phis */
+ for(i = 0; i < phis; i++) {
+ struct triple **slot;
+ int zrhs, j;
+ if (!live[i].alive) {
+ release_triple(state, live[i].phi);
+ continue;
+ }
+ phi = live[i].phi;
+ slot = &RHS(phi, 0);
+ zrhs = TRIPLE_RHS(phi->sizes);
+ for(j = 0; j < zrhs; j++) {
+ if(!slot[j]) {
+ error(state, phi, "variable not set on all paths to use");
+ }
+ }
+ }
+ xfree(live);
+}
+
+
static void transform_to_ssa_form(struct compile_state *state)
{
+ struct triple_set **stacks;
+ int adecls;
insert_phi_operations(state);
#if 0
printf("@%s:%d\n", __FILE__, __LINE__);
print_blocks(state, stdout);
#endif
- rename_block_variables(state, state->first_block);
+
+ /* Allocate stacks for the Variables */
+ adecls = count_and_number_adecls(state);
+ stacks = xcmalloc(sizeof(stacks[0])*(adecls + 1), "adecl stacks");
+ rename_block_variables(state, stacks, state->first_block);
+ xfree(stacks);
+
prune_block_variables(state, state->first_block);
+
+#if 1
+ prune_unused_phis(state);
+#endif
+
}
@@ -11198,6 +11342,15 @@ static void insert_copies_to_phi(struct compile_state *state)
unuse_triple(val, phi);
use_triple(move, phi);
+ /* Walk up the dominator tree until I have found the appropriate block */
+ while(eblock && !tdominates(state, val, eblock->last)) {
+ eblock = eblock->idom;
+ }
+ if (!eblock) {
+ internal_error(state, phi, "Cannot find block dominated by %p",
+ val);
+ }
+
/* Walk through the block backwards to find
* an appropriate location for the OP_COPY.
*/
@@ -11589,6 +11742,8 @@ static int count_triples(struct compile_state *state)
} while (ins != first);
return triples;
}
+
+
struct dead_triple {
struct triple *triple;
struct dead_triple *work_next;
@@ -14761,21 +14916,40 @@ static void verify_domination(struct compile_state *state)
ins = first;
do {
for(set = ins->use; set; set = set->next) {
- struct triple **expr;
- if (set->member->op == OP_PHI) {
- continue;
- }
- /* See if the use is on the righ hand side */
- expr = triple_rhs(state, set->member, 0);
- for(; expr ; expr = triple_rhs(state, set->member, expr)) {
- if (*expr == ins) {
+ struct triple **slot;
+ struct triple *use_point;
+ int i, zrhs;
+ use_point = 0;
+ zrhs = TRIPLE_RHS(ins->sizes);
+ slot = &RHS(set->member, 0);
+ /* See if the use is on the right hand side */
+ for(i = 0; i < zrhs; i++) {
+ if (slot[i] == ins) {
break;
}
}
- if (expr &&
- !tdominates(state, ins, set->member)) {
- internal_error(state, set->member,
- "non dominated rhs use?");
+ if (i < zrhs) {
+ use_point = set->member;
+ if (set->member->op == OP_PHI) {
+ struct block_set *bset;
+ int edge;
+ bset = set->member->u.block->use;
+ for(edge = 0; bset && (edge < i); edge++) {
+ bset = bset->next;
+ }
+ if (!bset) {
+ internal_error(state, set->member,
+ "no edge for phi rhs %d\n", i);
+ }
+ use_point = bset->member->last;
+ }
+ }
+ if (use_point &&
+ !tdominates(state, ins, use_point)) {
+ internal_warning(state, ins,
+ "ins does not dominate rhs use");
+ internal_error(state, use_point,
+ "non dominated rhs use point?");
}
}
ins = ins->next;