mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-28 23:22:43 +00:00
[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:
parent
d11f7be6bf
commit
d8a78fc849
5 changed files with 188 additions and 90 deletions
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue