From d8a78fc849bb65bfeed7730713ca869a27065721 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Fri, 1 Sep 2023 09:40:09 +0900 Subject: [PATCH] [qfcc] Handle aliased temps better Because the aliases were treated as live, every alias of a temp resulted in an assignment, which proved to be quite significant (4-5 assignments in some simple GA expressions). By using an alias node in the dag, the unaliased temp can be marked live while the alias is treated as an operation rather than an operand. Now my GA expressions have no superfluous assignments (generally no assignments at all). --- tools/qfcc/include/dags.h | 1 + tools/qfcc/include/statements.h | 3 + tools/qfcc/source/dags.c | 178 +++++++++++++++++++++++--------- tools/qfcc/source/flow.c | 1 + tools/qfcc/source/statements.c | 95 +++++++++-------- 5 files changed, 188 insertions(+), 90 deletions(-) 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; }