[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:
Bill Currie 2024-02-21 20:43:33 +09:00
parent e298771d50
commit 073c93eebf
5 changed files with 65 additions and 52 deletions

View file

@ -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;

View file

@ -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];

View file

@ -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];

View file

@ -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:

View file

@ -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 *