mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-25 05:01:24 +00:00
[qfcc] Don't split basic blocks on function calls
This allows the dags code to optimize the return values, and when I make the node killing by function calls less aggressive, should make for many more potential CSE optimizations.
This commit is contained in:
parent
e298771d50
commit
073c93eebf
5 changed files with 65 additions and 52 deletions
|
@ -62,7 +62,7 @@ typedef struct dagnode_s {
|
|||
struct dagnode_s *killed; ///< node is unavailable for cse (by node)
|
||||
st_type_t type; ///< type of node (st_none = leaf)
|
||||
daglabel_t *label; ///< ident/const if leaf node, or operator
|
||||
const struct type_s *tl;
|
||||
const struct type_s *vtype; ///< operand type
|
||||
struct operand_s *value; ///< operand holding the value of this node
|
||||
/// \name child nodes
|
||||
/// if \a children[0] is null, the rest must be null as well. Similar for
|
||||
|
@ -89,11 +89,12 @@ typedef struct dag_s {
|
|||
struct dag_s *next;
|
||||
dagnode_t **nodes; ///< array of all dagnodes in this dag
|
||||
int num_nodes;
|
||||
int killer_node; ///< last mass-killer node
|
||||
int *topo; ///< nodes in topological sort order
|
||||
int num_topo; ///< number of nodes in topo (may be <
|
||||
///< num_nodes after dead node removal)
|
||||
daglabel_t **labels; ///< array of all daglabels in this dag
|
||||
int num_labels;
|
||||
daglabel_t **labels; ///< array of all daglabels in this dag
|
||||
struct set_s *roots; ///< set of root nodes
|
||||
struct flownode_s *flownode;///< flow node this dag represents
|
||||
} dag_t;
|
||||
|
|
|
@ -333,7 +333,7 @@ leaf_node (dag_t *dag, operand_t *op, const expr_t *expr)
|
|||
if (!op)
|
||||
return 0;
|
||||
node = new_node (dag);
|
||||
node->tl = op->type;
|
||||
node->vtype = op->type;
|
||||
label = operand_label (dag, op);
|
||||
label->dagnode = node;
|
||||
label->expr = expr;
|
||||
|
@ -458,6 +458,9 @@ dag_make_child (dag_t *dag, operand_t *op, statement_t *s)
|
|||
// No valid node found (either first reference to the value,
|
||||
// or the value's node was killed).
|
||||
node = leaf_node (dag, op, s->expr);
|
||||
if (node && dag->killer_node >= 0) {
|
||||
set_add (node->edges, dag->killer_node);
|
||||
}
|
||||
}
|
||||
if (killer) {
|
||||
// When an operand refers to a killed node, it must be
|
||||
|
@ -507,6 +510,9 @@ dagnode_add_children (dag_t *dag, dagnode_t *n, operand_t *operands[4],
|
|||
set_add (child->parents, n->number);
|
||||
}
|
||||
}
|
||||
if (operands[0]) {
|
||||
n->vtype = operands[0]->type;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -891,7 +897,7 @@ dag_sort_nodes (dag_t *dag)
|
|||
}
|
||||
|
||||
static void
|
||||
dag_kill_nodes (dag_t *dag, dagnode_t *n)
|
||||
dag_kill_nodes (dag_t *dag, dagnode_t *n, bool is_call)
|
||||
{
|
||||
int i;
|
||||
dagnode_t *node;
|
||||
|
@ -908,14 +914,14 @@ dag_kill_nodes (dag_t *dag, dagnode_t *n)
|
|||
// point to itself (the required type is recursive).
|
||||
continue;
|
||||
}
|
||||
if (op_is_constant (node->label->op)) {
|
||||
if (!is_call && op_is_constant (node->label->op)) {
|
||||
// While constants in the Quake VM can be changed via a pointer,
|
||||
// doing so would cause much more fun than a simple
|
||||
// mis-optimization would, so consider them safe from pointer
|
||||
// operations.
|
||||
continue;
|
||||
}
|
||||
if (op_is_temp (node->label->op)) {
|
||||
if (!is_call && op_is_temp (node->label->op)) {
|
||||
// Assume that the pointer cannot point to a temporary variable.
|
||||
// This is reasonable as there is no programmer access to temps.
|
||||
continue;
|
||||
|
@ -923,6 +929,7 @@ dag_kill_nodes (dag_t *dag, dagnode_t *n)
|
|||
node->killed = n;
|
||||
}
|
||||
n->killed = 0;
|
||||
dag->killer_node = n->number;
|
||||
}
|
||||
|
||||
static __attribute__((pure)) int
|
||||
|
@ -1017,6 +1024,7 @@ dag_create (flownode_t *flownode)
|
|||
num_lables = num_statements * (FLOW_OPERANDS + 1 + 8) + num_aux;
|
||||
dag->labels = alloca (num_lables * sizeof (daglabel_t));
|
||||
dag->roots = set_new ();
|
||||
dag->killer_node = -1;
|
||||
|
||||
// actual dag creation
|
||||
for (s = block->statements; s; s = s->next) {
|
||||
|
@ -1067,7 +1075,10 @@ dag_create (flownode_t *flownode)
|
|||
}
|
||||
}
|
||||
if (n->type == st_ptrassign) {
|
||||
dag_kill_nodes (dag, n);
|
||||
dag_kill_nodes (dag, n, false);
|
||||
}
|
||||
if (s->type == st_func && strcmp (s->opcode, "call") == 0) {
|
||||
dag_kill_nodes (dag, n, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1289,43 +1300,6 @@ generate_memsetps (dag_t *dag, sblock_t *block, dagnode_t *dagnode)
|
|||
return dst;
|
||||
}
|
||||
|
||||
static operand_t *
|
||||
generate_call (dag_t *dag, sblock_t *block, dagnode_t *dagnode)
|
||||
{
|
||||
set_iter_t *var_iter;
|
||||
daglabel_t *var = 0;
|
||||
operand_t *operands[3] = {0, 0, 0};
|
||||
statement_t *st;
|
||||
operand_t *dst;
|
||||
|
||||
operands[0] = make_operand (dag, block, dagnode, 0);
|
||||
if (dagnode->children[1]) {
|
||||
operands[1] = make_operand (dag, block, dagnode, 1);
|
||||
}
|
||||
dst = operands[0];
|
||||
for (var_iter = set_first (dagnode->identifiers); var_iter;
|
||||
var_iter = set_next (var_iter)) {
|
||||
if (var) {
|
||||
internal_error (var->expr, "more than one return value for call");
|
||||
}
|
||||
var = dag->labels[var_iter->element];
|
||||
operands[2] = var->op;
|
||||
dst = operands[2];
|
||||
st = build_statement ("call", operands, var->expr);
|
||||
sblock_add_statement (block, st);
|
||||
}
|
||||
if (var_iter) {
|
||||
set_del_iter (var_iter);
|
||||
}
|
||||
if (!var) {
|
||||
// void call or return value ignored, still have to call
|
||||
operands[2] = make_operand (dag, block, dagnode, 2);
|
||||
st = build_statement ("call", operands, dagnode->label->expr);
|
||||
sblock_add_statement (block, st);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static operand_t *
|
||||
generate_assignments (dag_t *dag, sblock_t *block, operand_t *src,
|
||||
set_iter_t *var_iter, const type_t *type)
|
||||
|
@ -1362,6 +1336,43 @@ generate_assignments (dag_t *dag, sblock_t *block, operand_t *src,
|
|||
return dst;
|
||||
}
|
||||
|
||||
static operand_t *
|
||||
generate_call (dag_t *dag, sblock_t *block, dagnode_t *dagnode)
|
||||
{
|
||||
set_iter_t *var_iter;
|
||||
daglabel_t *var = 0;
|
||||
operand_t *operands[3] = {0, 0, 0};
|
||||
statement_t *st;
|
||||
operand_t *dst = nullptr;
|
||||
const type_t *type = dagnode->vtype;
|
||||
|
||||
operands[0] = make_operand (dag, block, dagnode, 0);
|
||||
if (dagnode->children[1]) {
|
||||
operands[1] = make_operand (dag, block, dagnode, 1);
|
||||
}
|
||||
if ((var_iter = set_first (dagnode->identifiers))) {
|
||||
var = dag->labels[var_iter->element];
|
||||
operands[2] = var->op;
|
||||
var_iter = set_next (var_iter);
|
||||
dst = operands[2];
|
||||
st = build_statement ("call", operands, var->expr);
|
||||
sblock_add_statement (block, st);
|
||||
generate_assignments (dag, block, operands[2], var_iter, type);
|
||||
}
|
||||
if (!var) {
|
||||
if (dagnode->children[2]) {
|
||||
// void call or return value ignored, still have to call
|
||||
operands[2] = make_operand (dag, block, dagnode, 2);
|
||||
} else {
|
||||
operands[2] = temp_operand (type, dagnode->label->expr);
|
||||
dst = operands[2];
|
||||
}
|
||||
st = build_statement ("call", operands, dagnode->label->expr);
|
||||
sblock_add_statement (block, st);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static void
|
||||
dag_gencode (dag_t *dag, sblock_t *block, dagnode_t *dagnode)
|
||||
{
|
||||
|
@ -1412,10 +1423,9 @@ dag_gencode (dag_t *dag, sblock_t *block, dagnode_t *dagnode)
|
|||
operands[0] = make_operand (dag, block, dagnode, 0);
|
||||
if (dagnode->children[1])
|
||||
operands[1] = make_operand (dag, block, dagnode, 1);
|
||||
type = get_type (dagnode->label->expr);
|
||||
type = dagnode->vtype;
|
||||
if (!(var_iter = set_first (dagnode->identifiers))) {
|
||||
operands[2] = temp_operand (get_type (dagnode->label->expr),
|
||||
dagnode->label->expr);
|
||||
operands[2] = temp_operand (type, dagnode->label->expr);
|
||||
} else {
|
||||
daglabel_t *var = dag->labels[var_iter->element];
|
||||
|
||||
|
|
|
@ -56,11 +56,11 @@ print_node_def (dstring_t *dstr, dag_t *dag, dagnode_t *node)
|
|||
set_iter_t *id_iter;
|
||||
daglabel_t *id;
|
||||
|
||||
dasprintf (dstr, " \"dagnode_%p\" [%slabel=\"%s%s (%d)", node,
|
||||
dasprintf (dstr, " \"dagnode_%p\" [%slabel=\"%s%s (%d:%d)", node,
|
||||
node->type != st_none ? "" : "shape=box,",
|
||||
daglabel_string (node->label),
|
||||
node->killed ? " k" : "",
|
||||
node->topo);
|
||||
node->number, node->topo);
|
||||
for (id_iter = set_first (node->identifiers); id_iter;
|
||||
id_iter = set_next (id_iter)) {
|
||||
id = dag->labels[id_iter->element];
|
||||
|
|
|
@ -1765,7 +1765,10 @@ flow_analyze_statement (statement_t *s, set_t *use, set_t *def, set_t *kill,
|
|||
if (operands) {
|
||||
operands[1] = s->opa;
|
||||
operands[2] = s->opb;
|
||||
operands[3] = s->opc;
|
||||
if (calln >= 0 || (s->opc && s->opc->op_type == op_value)) {
|
||||
operands[3] = s->opc;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case st_flow:
|
||||
|
|
|
@ -1251,8 +1251,7 @@ expr_call (sblock_t *sblock, const expr_t *call, operand_t **op)
|
|||
s->use = use;
|
||||
s->kill = kill;
|
||||
sblock_add_statement (sblock, s);
|
||||
sblock->next = new_sblock ();
|
||||
return sblock->next;
|
||||
return sblock;
|
||||
}
|
||||
|
||||
static sblock_t *
|
||||
|
|
Loading…
Reference in a new issue