[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).
This commit is contained in:
Bill Currie 2023-09-01 09:40:09 +09:00
parent d11f7be6bf
commit d8a78fc849
5 changed files with 188 additions and 90 deletions

View file

@ -77,6 +77,7 @@ typedef struct dagnode_s {
struct dagnode_s *children[3]; struct dagnode_s *children[3];
struct type_s *types[3]; ///< desired type of each operand (to alias) struct type_s *types[3]; ///< desired type of each operand (to alias)
struct set_s *edges; ///< includes nodes pointed to by \a children 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 *identifiers; ///< set of identifiers attached to this node
struct set_s *reachable; ///< set of nodes reachable via edges (not struct set_s *reachable; ///< set of nodes reachable via edges (not

View file

@ -92,6 +92,7 @@ typedef struct operand_s {
*/ */
typedef enum { typedef enum {
st_none, ///< not a (valid) statement. Used in dags. 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_expr, ///< c = a op b; or c = op a;
st_assign, ///< b = a st_assign, ///< b = a
st_ptrassign, ///< *b = a; or *(b + c) = 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); operand_t *temp_operand (struct type_s *type, struct expr_s *expr);
int tempop_visit_all (tempop_t *tempop, int overlap, int tempop_visit_all (tempop_t *tempop, int overlap,
int (*visit) (tempop_t *, void *), void *data); 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, operand_t *alias_operand (struct type_s *type, operand_t *op,
struct expr_s *expr); struct expr_s *expr);
operand_t *label_operand (struct expr_s *label); operand_t *label_operand (struct expr_s *label);

View file

@ -66,8 +66,90 @@ ALLOC_STATE (dag_t, dags);
static daglabel_t *daglabel_chain; 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 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 static void
flush_daglabels (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); flow_analyze_statement (s, 0, 0, 0, operands);
for (i = 0; i < 3; i++) { 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; dagnode_t *killer = 0;
if (node && (node->killed || s->type == st_address)) { if (node && (node->killed || s->type == st_address)) {
@ -291,10 +374,32 @@ dag_make_children (dag_t *dag, statement_t *s,
node = 0; 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) { if (!node) {
// No valid node found (either first reference to the value, // No valid node found (either first reference to the value,
// or the value's node was killed). // 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) { if (killer) {
// When an operand refers to a killed node, it must be // 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 static int
dag_tempop_kill_aliases_visit (tempop_t *tempop, void *_l) dag_tempop_kill_aliases_visit (tempop_t *tempop, void *_l)
{ {
@ -843,6 +910,7 @@ dag_create (flownode_t *flownode)
daglabel_t **labels; daglabel_t **labels;
int num_statements = 0; int num_statements = 0;
int num_use = 0; int num_use = 0;
int num_alias = 0;
int num_nodes; int num_nodes;
int num_lables; int num_lables;
set_t *live_vars = set_new (); set_t *live_vars = set_new ();
@ -859,17 +927,20 @@ dag_create (flownode_t *flownode)
} }
num_use++; 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); set_assign (live_vars, flownode->live_vars.out);
dag = new_dag (); dag = new_dag ();
dag->flownode = flownode; dag->flownode = flownode;
// at most FLOW_OPERANDS per statement + use // at most FLOW_OPERANDS per statement + use + alias
num_nodes = num_statements * FLOW_OPERANDS + num_use; num_nodes = num_statements * FLOW_OPERANDS + num_use + num_alias;
dag->nodes = alloca (num_nodes * sizeof (dagnode_t)); dag->nodes = alloca (num_nodes * sizeof (dagnode_t));
// at most FLOW_OPERANDS per statement, + return + params + use // at most FLOW_OPERANDS per statement, + return + params + use + alias
num_lables = num_statements * (FLOW_OPERANDS + 1 + 8) + num_use; num_lables = num_statements * (FLOW_OPERANDS + 1 + 8) + num_use + num_alias;
dag->labels = alloca (num_lables * sizeof (daglabel_t)); dag->labels = alloca (num_lables * sizeof (daglabel_t));
dag->roots = set_new (); dag->roots = set_new ();
@ -1233,13 +1304,28 @@ dag_gencode (dag_t *dag, sblock_t *block, dagnode_t *dagnode)
switch (dagnode->type) { switch (dagnode->type) {
case st_none: case st_none:
if (!dagnode->label->op) 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; dst = dagnode->label->op;
if ((var_iter = set_first (dagnode->identifiers))) { if ((var_iter = set_first (dagnode->identifiers))) {
type = dst->type; type = dst->type;
dst = generate_assignments (dag, block, dst, var_iter, type); dst = generate_assignments (dag, block, dst, var_iter, type);
} }
break; 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_address:
case st_expr: case st_expr:
operands[0] = make_operand (dag, block, dagnode, 0); 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); generate_assignments (dag, block, operands[2], var_iter, type);
break; break;
case st_assign: case st_assign:
internal_error (0, "unexpected assignment node"); internal_error (dagnode->label->expr, "unexpected assignment node");
case st_ptrassign: case st_ptrassign:
operands[2] = make_operand (dag, block, dagnode, 0); operands[2] = make_operand (dag, block, dagnode, 0);
operands[0] = make_operand (dag, block, dagnode, 2); operands[0] = make_operand (dag, block, dagnode, 2);

View file

@ -1525,6 +1525,7 @@ flow_analyze_statement (statement_t *s, set_t *use, set_t *def, set_t *kill,
switch (s->type) { switch (s->type) {
case st_none: case st_none:
case st_alias:
internal_error (s->expr, "not a statement"); internal_error (s->expr, "not a statement");
case st_address: case st_address:
if (s->opb) { if (s->opb) {

View file

@ -77,6 +77,7 @@ const char * const op_type_names[] = {
const char * const st_type_names[] = { const char * const st_type_names[] = {
"st_none", "st_none",
"st_alias",
"st_expr", "st_expr",
"st_assign", "st_assign",
"st_ptrassign", "st_ptrassign",
@ -473,6 +474,55 @@ tempop_visit_all (tempop_t *tempop, int overlap,
return 0; 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 * operand_t *
alias_operand (type_t *type, operand_t *op, expr_t *expr) 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) expr_alias (sblock_t *sblock, expr_t *e, operand_t **op)
{ {
operand_t *aop = 0; operand_t *aop = 0;
operand_t *top;
type_t *type; type_t *type;
def_t *def;
int offset = 0; int offset = 0;
if (e->e.alias.offset) { 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; type = e->e.alias.type;
sblock = statement_subexpr (sblock, e->e.alias.expr, &aop); sblock = statement_subexpr (sblock, e->e.alias.expr, &aop);
if (type_compatible (aop->type, type)) { *op = offset_alias_operand (type, offset, aop, e);
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));
}
return sblock; return sblock;
} }