diff --git a/tools/qfcc/include/dags.h b/tools/qfcc/include/dags.h index 66fbece40..d077da085 100644 --- a/tools/qfcc/include/dags.h +++ b/tools/qfcc/include/dags.h @@ -77,6 +77,7 @@ typedef struct dagnode_s { struct dagnode_s *children[3]; struct type_s *types[3]; ///< desired type of each operand (to alias) struct set_s *edges; ///< includes nodes pointed to by \a children + int offset; ///< for alias nodes //@} struct set_s *identifiers; ///< set of identifiers attached to this node struct set_s *reachable; ///< set of nodes reachable via edges (not diff --git a/tools/qfcc/include/statements.h b/tools/qfcc/include/statements.h index 929eb8e5b..9e25a694e 100644 --- a/tools/qfcc/include/statements.h +++ b/tools/qfcc/include/statements.h @@ -92,6 +92,7 @@ typedef struct operand_s { */ typedef enum { st_none, ///< not a (valid) statement. Used in dags. + st_alias, ///< not a (valid) statement. Used in dags. st_expr, ///< c = a op b; or c = op a; st_assign, ///< b = a st_ptrassign, ///< *b = a; or *(b + c) = a; @@ -153,6 +154,8 @@ int tempop_overlap (tempop_t *t1, tempop_t *t2) __attribute__((pure)); operand_t *temp_operand (struct type_s *type, struct expr_s *expr); int tempop_visit_all (tempop_t *tempop, int overlap, int (*visit) (tempop_t *, void *), void *data); +operand_t *offset_alias_operand (struct type_s *type, int offset, + operand_t *aop, struct expr_s *expr); operand_t *alias_operand (struct type_s *type, operand_t *op, struct expr_s *expr); operand_t *label_operand (struct expr_s *label); diff --git a/tools/qfcc/source/dags.c b/tools/qfcc/source/dags.c index 71b564144..097a3dd84 100644 --- a/tools/qfcc/source/dags.c +++ b/tools/qfcc/source/dags.c @@ -66,8 +66,90 @@ ALLOC_STATE (dag_t, dags); static daglabel_t *daglabel_chain; +static void dagnode_set_edges (dag_t *dag, dagnode_t *n, statement_t *s); static void dag_live_aliases(operand_t *op); +static int +op_is_identifier (operand_t *op) +{ + if (!op) + return 0; + if (op->op_type == op_label) + return 0; + if (op->op_type == op_value) + return 0; + if (op->op_type == op_temp) + return 1; + if (op->op_type != op_def) + return 0; + return 1; +} + +static int +op_is_constant (operand_t *op) +{ + if (!op) + return 0; + if (op->op_type == op_label) + return 1; + if (op->op_type == op_value) + return 1; + if (op->op_type == op_label) + return op->def->constant; + return 0; +} + +static int +op_is_temp (operand_t *op) +{ + if (!op) + return 0; + return op->op_type == op_temp; +} + +static int +op_is_alias (operand_t *op) +{ + if (!op) { + return 0; + } + if (op->op_type == op_temp) { + return !!op->tempop.alias; + } + if (op->op_type == op_def) { + return !!op->def->alias; + } + return 0; +} + +static int __attribute__((pure)) +op_alias_offset (operand_t *op) +{ + if (!op_is_alias (op)) { + internal_error (op->expr, "not an alias op"); + } + if (op->op_type == op_temp) { + return op->tempop.offset; + } + internal_error (op->expr, "eh? how?"); +} + +static operand_t * __attribute__((pure)) +unalias_op (operand_t *op) +{ + if (!op_is_alias (op)) { + internal_error (op->expr, "not an alias op"); + } + if (op->op_type == op_temp) { + return op->tempop.alias; + } + if (op->op_type == op_def) { + // XXX FIXME needed? + return op; + } + internal_error (op->expr, "eh? how?"); +} + static void flush_daglabels (void) { @@ -279,7 +361,8 @@ dag_make_children (dag_t *dag, statement_t *s, flow_analyze_statement (s, 0, 0, 0, operands); for (i = 0; i < 3; i++) { - dagnode_t *node = dag_node (operands[i + 1]); + operand_t *op = operands[i + 1]; + dagnode_t *node = dag_node (op); dagnode_t *killer = 0; if (node && (node->killed || s->type == st_address)) { @@ -291,10 +374,32 @@ dag_make_children (dag_t *dag, statement_t *s, node = 0; } + if (!node && op_is_alias (op)) { + operand_t *uop = unalias_op (op); + if (uop != op) { + if (!(node = dag_node (uop))) { + node = leaf_node (dag, uop, s->expr); + } + node->label->live = 1; + dagnode_t *n = new_node (dag); + n->type = st_alias; + n->label = opcode_label (dag, "alias", s->expr); + n->children[0] = node; + n->types[0] = op->type; + n->offset = op_alias_offset (op); + n->value = op; + set_remove (dag->roots, node->number); + set_add (node->parents, n->number); + dagnode_set_edges (dag, n, s); + dagnode_set_reachable (dag, n); + node = n; + } + } + if (!node) { // No valid node found (either first reference to the value, // or the value's node was killed). - node = leaf_node (dag, operands[i + 1], s->expr); + node = leaf_node (dag, op, s->expr); } if (killer) { // When an operand refers to a killed node, it must be @@ -527,44 +632,6 @@ dagnode_set_edges (dag_t *dag, dagnode_t *n, statement_t *s) } } -static int -op_is_identifier (operand_t *op) -{ - if (!op) - return 0; - if (op->op_type == op_label) - return 0; - if (op->op_type == op_value) - return 0; - if (op->op_type == op_temp) - return 1; - if (op->op_type != op_def) - return 0; - return 1; -} - -static int -op_is_constant (operand_t *op) -{ - if (!op) - return 0; - if (op->op_type == op_label) - return 1; - if (op->op_type == op_value) - return 1; - if (op->op_type == op_label) - return op->def->constant; - return 0; -} - -static int -op_is_temp (operand_t *op) -{ - if (!op) - return 0; - return op->op_type == op_temp; -} - static int dag_tempop_kill_aliases_visit (tempop_t *tempop, void *_l) { @@ -843,6 +910,7 @@ dag_create (flownode_t *flownode) daglabel_t **labels; int num_statements = 0; int num_use = 0; + int num_alias = 0; int num_nodes; int num_lables; set_t *live_vars = set_new (); @@ -859,17 +927,20 @@ dag_create (flownode_t *flownode) } num_use++; } + num_alias += op_is_alias (s->opa); + num_alias += op_is_alias (s->opb); + num_alias += op_is_alias (s->opc); } set_assign (live_vars, flownode->live_vars.out); dag = new_dag (); dag->flownode = flownode; - // at most FLOW_OPERANDS per statement + use - num_nodes = num_statements * FLOW_OPERANDS + num_use; + // at most FLOW_OPERANDS per statement + use + alias + num_nodes = num_statements * FLOW_OPERANDS + num_use + num_alias; dag->nodes = alloca (num_nodes * sizeof (dagnode_t)); - // at most FLOW_OPERANDS per statement, + return + params + use - num_lables = num_statements * (FLOW_OPERANDS + 1 + 8) + num_use; + // at most FLOW_OPERANDS per statement, + return + params + use + alias + num_lables = num_statements * (FLOW_OPERANDS + 1 + 8) + num_use + num_alias; dag->labels = alloca (num_lables * sizeof (daglabel_t)); dag->roots = set_new (); @@ -1233,13 +1304,28 @@ dag_gencode (dag_t *dag, sblock_t *block, dagnode_t *dagnode) switch (dagnode->type) { case st_none: if (!dagnode->label->op) - internal_error (0, "non-leaf label in leaf node"); + internal_error (dagnode->label->expr, + "non-leaf label in leaf node"); dst = dagnode->label->op; if ((var_iter = set_first (dagnode->identifiers))) { type = dst->type; dst = generate_assignments (dag, block, dst, var_iter, type); } break; + case st_alias: + if (!dagnode->children[0] || !dagnode->children[0]->value + || !dagnode->types[0] + || dagnode->children[1] || dagnode->children[2]) { + internal_error (dagnode->label->expr, "invalid alias node"); + } + dst = offset_alias_operand (dagnode->types[0], dagnode->offset, + dagnode->children[0]->value, + dagnode->label->expr); + if ((var_iter = set_first (dagnode->identifiers))) { + type = dst->type; + dst = generate_assignments (dag, block, dst, var_iter, type); + } + break; case st_address: case st_expr: operands[0] = make_operand (dag, block, dagnode, 0); @@ -1262,7 +1348,7 @@ dag_gencode (dag_t *dag, sblock_t *block, dagnode_t *dagnode) generate_assignments (dag, block, operands[2], var_iter, type); break; case st_assign: - internal_error (0, "unexpected assignment node"); + internal_error (dagnode->label->expr, "unexpected assignment node"); case st_ptrassign: operands[2] = make_operand (dag, block, dagnode, 0); operands[0] = make_operand (dag, block, dagnode, 2); diff --git a/tools/qfcc/source/flow.c b/tools/qfcc/source/flow.c index e0d33346c..6d387794d 100644 --- a/tools/qfcc/source/flow.c +++ b/tools/qfcc/source/flow.c @@ -1525,6 +1525,7 @@ flow_analyze_statement (statement_t *s, set_t *use, set_t *def, set_t *kill, switch (s->type) { case st_none: + case st_alias: internal_error (s->expr, "not a statement"); case st_address: if (s->opb) { diff --git a/tools/qfcc/source/statements.c b/tools/qfcc/source/statements.c index f063cbdb5..642ad531e 100644 --- a/tools/qfcc/source/statements.c +++ b/tools/qfcc/source/statements.c @@ -77,6 +77,7 @@ const char * const op_type_names[] = { const char * const st_type_names[] = { "st_none", + "st_alias", "st_expr", "st_assign", "st_ptrassign", @@ -473,6 +474,55 @@ tempop_visit_all (tempop_t *tempop, int overlap, return 0; } +operand_t * +offset_alias_operand (type_t *type, int offset, operand_t *aop, expr_t *expr) +{ + operand_t *top; + def_t *def; + if (type_compatible (aop->type, type)) { + if (offset) { + //For types to be compatible, they must be the same size, thus this + //seemingly mismatched error + internal_error (expr, "offset alias of same size type"); + } + return aop; + } + if (aop->op_type == op_temp) { + while (aop->tempop.alias) { + aop = aop->tempop.alias; + if (aop->op_type != op_temp) + internal_error (expr, "temp alias of non-temp var"); + if (aop->tempop.alias) + bug (expr, "aliased temp alias"); + } + for (top = aop->tempop.alias_ops; top; top = top->next) { + if (top->type == type && top->tempop.offset == offset) { + break; + } + } + if (!top) { + top = temp_operand (type, expr); + top->tempop.alias = aop; + top->tempop.offset = offset; + top->next = aop->tempop.alias_ops; + aop->tempop.alias_ops = top; + } + return top; + } else if (aop->op_type == op_def) { + def = aop->def; + while (def->alias) + def = def->alias; + return def_operand (alias_def (def, type, offset), 0, expr); + } else if (aop->op_type == op_value) { + top = value_operand (aop->value, expr); + top->type = type; + return top; + } else { + internal_error (expr, "invalid alias target: %s: %s", + optype_str (aop->op_type), operand_string (aop)); + } +} + operand_t * alias_operand (type_t *type, operand_t *op, expr_t *expr) { @@ -1584,9 +1634,7 @@ static sblock_t * expr_alias (sblock_t *sblock, expr_t *e, operand_t **op) { operand_t *aop = 0; - operand_t *top; type_t *type; - def_t *def; int offset = 0; if (e->e.alias.offset) { @@ -1594,48 +1642,7 @@ expr_alias (sblock_t *sblock, expr_t *e, operand_t **op) } type = e->e.alias.type; sblock = statement_subexpr (sblock, e->e.alias.expr, &aop); - if (type_compatible (aop->type, type)) { - if (offset) { - //For types to be compatible, they must be the same size, thus this - //seemingly mismatched error - internal_error (e, "offset alias of same size type"); - } - *op = aop; - return sblock; - } - if (aop->op_type == op_temp) { - while (aop->tempop.alias) { - aop = aop->tempop.alias; - if (aop->op_type != op_temp) - internal_error (e, "temp alias of non-temp var"); - if (aop->tempop.alias) - bug (e, "aliased temp alias"); - } - for (top = aop->tempop.alias_ops; top; top = top->next) { - if (top->type == type && top->tempop.offset == offset) { - break; - } - } - if (!top) { - top = temp_operand (type, e); - top->tempop.alias = aop; - top->tempop.offset = offset; - top->next = aop->tempop.alias_ops; - aop->tempop.alias_ops = top; - } - *op = top; - } else if (aop->op_type == op_def) { - def = aop->def; - while (def->alias) - def = def->alias; - *op = def_operand (alias_def (def, type, offset), 0, e); - } else if (aop->op_type == op_value) { - *op = value_operand (aop->value, e); - (*op)->type = type; - } else { - internal_error (e, "invalid alias target: %s: %s", - optype_str (aop->op_type), operand_string (aop)); - } + *op = offset_alias_operand (type, offset, aop, e); return sblock; }