quakeforge/tools/qfcc/source/dags.c
Bill Currie 616a52efb5 [qfcc] Implement flow analysis for Ruamoko calls
Thanks to the use/def/kill lists attached to statements for pseudo-ops,
it turned out to be a lot easier to implement flow analysis (and thus
dags processing) than I expected. I suspect I should go back and make
the old call code use them too, and probably several other places, as
that will greatly simplify the edge setting.
2022-01-21 17:14:10 +09:00

1289 lines
32 KiB
C

/*
dags.c
DAG representation of basic blocks
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/05/08
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include <ctype.h>
#include "qfalloca.h"
#include "QF/alloc.h"
#include "QF/dstring.h"
#include "QF/mathlib.h"
#include "QF/set.h"
#include "tools/qfcc/include/dags.h"
#include "tools/qfcc/include/diagnostic.h"
#include "tools/qfcc/include/dot.h"
#include "tools/qfcc/include/flow.h"
#include "tools/qfcc/include/function.h"
#include "tools/qfcc/include/options.h"
#include "tools/qfcc/include/qfcc.h"
#include "tools/qfcc/include/statements.h"
#include "tools/qfcc/include/strpool.h"
#include "tools/qfcc/include/symtab.h"
#include "tools/qfcc/include/type.h"
#include "tools/qfcc/include/value.h"
static daglabel_t *labels_freelist;
static dagnode_t *nodes_freelist;
static dag_t *dags_freelist;
static daglabel_t *daglabel_chain;
static void
flush_daglabels (void)
{
while (daglabel_chain) {
operand_t *op;
if ((op = daglabel_chain->op)) {
if (op->op_type == op_def)
op->def->daglabel = 0;
else if (op->op_type == op_temp)
op->tempop.daglabel = 0;
else if (op->op_type == op_value)
op->value->daglabel = 0;
else if (op->op_type == op_label)
op->label->daglabel = 0;
else
internal_error (op->expr, "unexpected operand type");
}
daglabel_chain = daglabel_chain->daglabel_chain;
}
}
static dag_t *
new_dag (void)
{
dag_t *dag;
ALLOC (256, dag_t, dags, dag);
return dag;
}
static daglabel_t *
new_label (dag_t *dag)
{
daglabel_t *label;
ALLOC (256, daglabel_t, labels, label);
label->daglabel_chain = daglabel_chain;
daglabel_chain = label;
label->number = dag->num_labels;
dag->labels[dag->num_labels++] = label;
return label;
}
static dagnode_t *
new_node (dag_t *dag)
{
dagnode_t *node;
ALLOC (256, dagnode_t, nodes, node);
node->parents = set_new ();
node->edges = set_new ();
node->identifiers = set_new ();
node->reachable = set_new ();
node->number = dag->num_nodes;
set_add (dag->roots, node->number); // nodes start out as root nodes
dag->nodes[dag->num_nodes++] = node;
return node;
}
const char *
daglabel_string (daglabel_t *label)
{
static dstring_t *str;
if ((label->opcode && label->op) || (!label->opcode && !label->op))
return "bad label";
if (label->opcode)
return label->opcode;
if (!str)
str = dstring_new ();
// operand_string might use quote_string, which returns a pointer to
// a static variable.
dstring_copystr (str, operand_string (label->op));
#if 0
if (label->op->type) {
dstring_appendstr (str, label->op->type->encoding);
}
#endif
return quote_string (str->str);
}
static daglabel_t *
opcode_label (dag_t *dag, const char *opcode, expr_t *expr)
{
daglabel_t *label;
label = new_label (dag);
label->opcode = opcode;
label->expr = expr;
return label;
}
static daglabel_t *
operand_label (dag_t *dag, operand_t *op)
{
def_t *def = 0;
ex_value_t *val = 0;
daglabel_t *label;
if (!op)
return 0;
if (op->op_type == op_temp) {
//while (op->tempop.alias)
// op = op->tempop.alias;
if (op->tempop.daglabel)
return op->tempop.daglabel;
label = new_label (dag);
label->op = op;
op->tempop.daglabel = label;
} else if (op->op_type == op_def) {
def = op->def;
if (def->daglabel)
return def->daglabel;
label = new_label (dag);
label->op = op;
def->daglabel = label;
} else if (op->op_type == op_value) {
val = op->value;
if (val->daglabel)
return val->daglabel;
label = new_label (dag);
label->op = op;
val->daglabel = label;
} else if (op->op_type == op_label) {
if (op->label->daglabel)
return op->label->daglabel;
label = new_label (dag);
label->op = op;
op->label->daglabel = label;
} else {
internal_error (op->expr, "unexpected operand type: %d", op->op_type);
}
return label;
}
static dagnode_t *
leaf_node (dag_t *dag, operand_t *op, expr_t *expr)
{
daglabel_t *label;
dagnode_t *node;
if (!op)
return 0;
node = new_node (dag);
node->tl = op->type;
label = operand_label (dag, op);
label->dagnode = node;
label->expr = expr;
node->label = label;
return node;
}
static __attribute__((pure)) dagnode_t *
dag_node (operand_t *op)
{
def_t *def;
dagnode_t *node = 0;
if (!op)
return 0;
if (op->op_type == op_def) {
def = op->def;
if (def->daglabel)
node = def->daglabel->dagnode;
} else if (op->op_type == op_temp) {
while (op->tempop.alias)
op = op->tempop.alias;
if (op->tempop.daglabel)
node = op->tempop.daglabel->dagnode;
} else if (op->op_type == op_value) {
if (op->value->daglabel)
node = op->value->daglabel->dagnode;
} else if (op->op_type == op_label) {
if (op->label->daglabel)
node = op->label->daglabel->dagnode;
}
return node;
}
static void
dag_make_leafs (dag_t *dag, statement_t *s, operand_t *operands[FLOW_OPERANDS])
{
int i;
flow_analyze_statement (s, 0, 0, 0, operands);
for (i = 1; i < FLOW_OPERANDS; i++) {
if (!dag_node (operands[i])) {
leaf_node (dag, operands[i], s->expr);
}
}
}
static void
dagnode_set_reachable (dag_t *dag, dagnode_t *node)
{
for (set_iter_t *edge_iter = set_first (node->edges); edge_iter;
edge_iter = set_next (edge_iter)) {
dagnode_t *r = dag->nodes[edge_iter->element];
// The other node is directly reachable
set_add (node->reachable, r->number);
// All nodes reachable by the other node are indirectly reachable
// from this node.
set_union (node->reachable, r->reachable);
}
}
static void
dag_make_children (dag_t *dag, statement_t *s,
operand_t *operands[FLOW_OPERANDS],
dagnode_t *children[3])
{
int i;
flow_analyze_statement (s, 0, 0, 0, operands);
for (i = 0; i < 3; i++) {
dagnode_t *node = dag_node (operands[i + 1]);
dagnode_t *killer = 0;
if (node && node->killed) {
// If the node has been killed, then a new node is needed
killer = node->killed;
node = 0;
}
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);
}
if (killer) {
// When an operand refers to a killed node, it must be
// evaluated AFTER the killing node has been evaluated.
set_add (node->edges, killer->number);
// If killer is set, then node is guaranteed to be a new node
// and thus does not have any parents, so no need to worry about
// updating the reachable sets of any parent nodes.
dagnode_set_reachable (dag, node);
}
children[i] = node;
}
}
static int
dagnode_deref_match (const dagnode_t *n, const daglabel_t *op,
dagnode_t *children[3])
{
int i;
for (i = 0; i < 2; i++) {
if (n->children[i] != children[i + 1])
return 0;
}
return 1;
}
static int
dagnode_match (const dagnode_t *n, const daglabel_t *op,
dagnode_t *children[3])
{
int i;
if (n->killed)
return 0;
if (!strcmp (op->opcode, "load")
&& n->label->opcode && !strcmp (n->label->opcode, "store"))
return dagnode_deref_match (n, op, children);
if (n->label->opcode != op->opcode)
return 0;
for (i = 0; i < 3; i++) {
if (n->children[i] != children[i])
return 0;
}
return 1;
}
static dagnode_t *
dagnode_search (dag_t *dag, daglabel_t *op, dagnode_t *children[3])
{
int i;
for (i = 0; i < dag->num_nodes; i++)
if (dagnode_match (dag->nodes[i], op, children))
return dag->nodes[i];
return 0;
}
static void
dagnode_add_children (dag_t *dag, dagnode_t *n, operand_t *operands[4],
dagnode_t *children[3])
{
int i;
for (i = 0; i < 3; i++) {
dagnode_t *child = children[i];
if ((n->children[i] = child)) {
n->types[i] = operands[i + 1]->type;
set_remove (dag->roots, child->number);
set_add (child->parents, n->number);
}
}
}
static int
dagnode_tempop_set_edges_visit (tempop_t *tempop, void *_node)
{
dagnode_t *node = (dagnode_t *) _node;
daglabel_t *label;
label = tempop->daglabel;
if (label && label->dagnode) {
set_add (node->edges, label->dagnode->number);
label->live = 1;
}
return 0;
}
static int
dagnode_def_set_edges_visit (def_t *def, void *_node)
{
dagnode_t *node = (dagnode_t *) _node;
daglabel_t *label;
label = def->daglabel;
if (label && label->dagnode) {
set_add (node->edges, label->dagnode->number);
label->live = 1;
}
return 0;
}
static int
dag_find_node (def_t *def, void *_daglabel)
{
daglabel_t **daglabel = (daglabel_t **) _daglabel;
if (def->daglabel && def->daglabel->dagnode) {
*daglabel = def->daglabel;
return def->daglabel->dagnode->number + 1; // ensure never 0
}
return 0;
}
static void
dagnode_set_edges (dag_t *dag, dagnode_t *n, statement_t *s)
{
int i;
for (i = 0; i < 3; i++) {
dagnode_t *child = n->children[i];
if (child && n != child)
set_add (n->edges, child->number);
}
if (n->type == st_flow)
return;
for (i = 0; i < 3; i++) {
dagnode_t *child = n->children[i];
if (child) {
if (child->label->op) {
dagnode_t *node = child->label->dagnode;
operand_t *op = child->label->op;
if (node != child && node != n) {
set_add (node->edges, n->number);
}
if (op->op_type == op_value
&& op->value->lltype == ev_ptr
&& op->value->v.pointer.def) {
def_visit_all (op->value->v.pointer.def, 1,
dagnode_def_set_edges_visit, n);
}
if (op->op_type == op_def
&& (op->def->alias || op->def->alias_defs)) {
def_visit_all (op->def, 1,
dagnode_def_set_edges_visit, n);
}
if (op->op_type == op_temp
&& (op->tempop.alias || op->tempop.alias_ops)) {
tempop_visit_all (&op->tempop, 1,
dagnode_tempop_set_edges_visit, n);
}
}
if (n != child)
set_add (n->edges, child->number);
}
}
for (operand_t *use = s->use; use; use = use->next) {
if (use->op_type == op_pseudo) {
continue;
}
daglabel_t *label = operand_label (dag, use);
label->live = 1;
set_add (n->edges, label->dagnode->number);
}
if (n->type == st_func) {
const char *num_params = 0;
int first_param = 0;
flowvar_t **flowvars = dag->flownode->graph->func->vars;
if (!strcmp (n->label->opcode, "call")) {
// nothing to do
} else if (!strncmp (n->label->opcode, "rcall", 5)) {
num_params = n->label->opcode + 6;
first_param = 2;
} else if (!strncmp (n->label->opcode, "call", 4)) {
num_params = n->label->opcode + 5;
} else if (!strcmp (n->label->opcode, "return") && n->children[0]) {
daglabel_t *label = n->children[0]->label;
if (!label->op) {
set_iter_t *lab_i;
for (lab_i = set_first (n->children[0]->identifiers); lab_i;
lab_i = set_next (lab_i)) {
label = dag->labels[lab_i->element];
}
}
label->live = 1;
}
if (num_params && isdigit (*num_params)) {
for (i = first_param; i < *num_params - '0'; i++) {
flowvar_t *var = flowvars[i + 1];
def_t *param_def = var->op->def;
daglabel_t *daglabel;
int param_node;
// FIXME hopefully only the one alias :P
param_node = def_visit_all (param_def, 0, dag_find_node,
&daglabel);
if (!param_node) {
bug (n->label->expr, ".param_%d not set for %s", i,
n->label->opcode);
continue;
}
daglabel->live = 1;
set_add (n->edges, param_node - 1);
}
}
}
}
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)
{
daglabel_t *l = (daglabel_t *) _l;
dagnode_t *node = l->dagnode;
daglabel_t *label;
if (tempop == &l->op->tempop)
return 0;
label = tempop->daglabel;
if (label && label->dagnode && !label->dagnode->killed) {
set_add (node->edges, label->dagnode->number);
set_remove (node->edges, node->number);
label->dagnode->killed = node;
}
return 0;
}
static int
dag_def_kill_aliases_visit (def_t *def, void *_l)
{
daglabel_t *l = (daglabel_t *) _l;
dagnode_t *node = l->dagnode;
daglabel_t *label;
if (def == l->op->def)
return 0;
label = def->daglabel;
if (label && label->dagnode && !label->dagnode->killed) {
set_add (node->edges, label->dagnode->number);
set_remove (node->edges, node->number);
label->dagnode->killed = node;
}
return 0;
}
static void
dag_kill_aliases (daglabel_t *l)
{
operand_t *op = l->op;
if (op->op_type == op_temp) {
if (op->tempop.alias || op->tempop.alias_ops) {
tempop_visit_all (&op->tempop, 1,
dag_tempop_kill_aliases_visit, l);
}
} else if (op->op_type == op_def) {
if (op->def->alias || op->def->alias_defs) {
def_visit_all (op->def, 1, dag_def_kill_aliases_visit, l);
}
} else {
internal_error (op->expr, "rvalue assignment?");
}
}
static int
dag_tempop_live_aliases (tempop_t *tempop, void *_t)
{
if (tempop != _t && tempop->daglabel)
tempop->daglabel->live = 1;
return 0;
}
static int
dag_def_live_aliases (def_t *def, void *_d)
{
if (def != _d && def->daglabel)
def->daglabel->live = 1;
return 0;
}
static void
dag_live_aliases(operand_t *op)
{
// FIXME it would be better to propogate the aliasing
if (op->op_type == op_temp
&& (op->tempop.alias || op->tempop.alias_ops)) {
tempop_visit_all (&op->tempop, 1, dag_tempop_live_aliases,
&op->tempop);
}
if (op->op_type == op_def
&& (op->def->alias || op->def->alias_defs)) {
def_visit_all (op->def, 1, dag_def_live_aliases, op->def);
}
}
static void
dag_make_var_live (set_t *live_vars, operand_t *op)
{
flowvar_t *var = 0;
if (op) {
dag_live_aliases (op);
var = flow_get_var (op);
}
if (var)
set_add (live_vars, var->number);
}
static int
dagnode_attach_label (dag_t *dag, dagnode_t *n, daglabel_t *l)
{
if (!l->op)
internal_error (0, "attempt to attach operator label to dagnode "
"identifiers");
if (!op_is_identifier (l->op))
internal_error (l->op->expr,
"attempt to attach non-identifer label to dagnode "
"identifiers");
if (l->dagnode) {
dagnode_t *node = l->dagnode;
set_remove (node->identifiers, l->number);
// If the target node (n) is reachable by the label's node or its
// parents, then attaching the label's node to the target node would
// cause the label's node to be written before it used.
set_t *reachable = set_new ();
set_assign (reachable, node->reachable);
for (set_iter_t *node_iter = set_first (node->parents); node_iter;
node_iter = set_next (node_iter)) {
dagnode_t *p = dag->nodes[node_iter->element];
set_union (reachable, p->reachable);
}
int is_reachable = set_is_member (reachable, n->number);
set_delete (reachable);
if (is_reachable) {
return 0;
}
// this assignment to the variable must come after any previous uses,
// which includes itself and its parents
set_add (n->edges, node->number);
set_union (n->edges, node->parents);
dagnode_set_reachable (dag, n);
}
l->live = 0; // remove live forcing on assignment
l->dagnode = n;
set_add (n->identifiers, l->number);
dag_kill_aliases (l);
if (n->label->op) {
dag_live_aliases (n->label->op);
}
return 1;
}
static int
dag_tempop_alias_live (tempop_t *tempop, void *_live_vars)
{
set_t *live_vars = (set_t *) _live_vars;
if (!tempop->flowvar)
return 0;
return set_is_member (live_vars, tempop->flowvar->number);
}
static int
dag_def_alias_live (def_t *def, void *_live_vars)
{
set_t *live_vars = (set_t *) _live_vars;
if (!def->flowvar)
return 0;
return set_is_member (live_vars, def->flowvar->number);
}
static void
dag_remove_dead_vars (dag_t *dag, set_t *live_vars)
{
int i;
for (i = 0; i < dag->num_labels; i++) {
daglabel_t *l = dag->labels[i];
flowvar_t *var;
if (!l->op || !l->dagnode)
continue;
if (l->live) // label forced live (probably via an alias)
continue;
var = flow_get_var (l->op);
if (!var)
continue;
if (set_is_member (dag->flownode->global_vars, var->number))
continue;
if (l->op->op_type == op_def
&& def_visit_all (l->op->def, 1, dag_def_alias_live, live_vars))
continue;
if (l->op->op_type == op_temp
&& tempop_visit_all (&l->op->tempop, 1, dag_tempop_alias_live,
live_vars))
continue;
if (!set_is_member (live_vars, var->number))
set_remove (l->dagnode->identifiers, l->number);
}
}
static void
dag_sort_visit (dag_t *dag, set_t *visited, int node_index, int *topo)
{
set_iter_t *node_iter;
dagnode_t *node;
if (set_is_member (visited, node_index))
return;
set_add (visited, node_index);
node = dag->nodes[node_index];
for (node_iter = set_first (node->edges); node_iter;
node_iter = set_next (node_iter))
dag_sort_visit (dag, visited, node_iter->element, topo);
node->topo = *topo;
dag->topo[(*topo)++] = node_index;
}
static void
dag_sort_nodes (dag_t *dag)
{
set_iter_t *root_iter;
set_t *visited = set_new ();
int topo = 0;
int *tmp_topo;
if (dag->topo)
free (dag->topo);
dag->topo = alloca (dag->num_nodes * sizeof (int));
for (root_iter = set_first (dag->roots); root_iter;
root_iter = set_next (root_iter))
dag_sort_visit (dag, visited, root_iter->element, &topo);
set_delete (visited);
tmp_topo = malloc (topo * sizeof (int));
memcpy (tmp_topo, dag->topo, topo * sizeof (int));
dag->topo = tmp_topo;
dag->num_topo = topo;
}
static void
dag_kill_nodes (dag_t *dag, dagnode_t *n)
{
int i;
dagnode_t *node;
for (i = 0; i < dag->num_nodes; i++) {
node = dag->nodes[i];
if (node->killed) {
//the node is already killed
continue;
}
if (node == n->children[1]) {
// assume the pointer does not point to itself. This should be
// reasonable because without casting, only a void pointer can
// point to itself (the required type is recursive).
continue;
}
if (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)) {
// Assume that the pointer cannot point to a temporary variable.
// This is reasonable as there is no programmer access to temps.
continue;
}
node->killed = n;
}
n->killed = 0;
}
dag_t *
dag_create (flownode_t *flownode)
{
dag_t *dag;
sblock_t *block = flownode->sblock;
statement_t *s;
dagnode_t **nodes;
daglabel_t **labels;
int num_statements = 0;
int num_nodes;
int num_lables;
set_t *live_vars = set_new ();
flush_daglabels ();
// count the number of statements so the number of nodes and labels can be
// guessed
for (s = block->statements; s; s = s->next)
num_statements++;
set_assign (live_vars, flownode->live_vars.out);
dag = new_dag ();
dag->flownode = flownode;
// at most FLOW_OPERANDS per statement
num_nodes = num_statements * FLOW_OPERANDS;
dag->nodes = alloca (num_nodes * sizeof (dagnode_t));
// at most FLOW_OPERANDS per statement, + return + params
num_lables = num_statements * (FLOW_OPERANDS + 1 + 8);
dag->labels = alloca (num_lables * sizeof (daglabel_t));
dag->roots = set_new ();
// do a first pass to ensure all operands have an "x_0" leaf node
// prior do actual dag creation
for (s = block->statements; s; s = s->next) {
operand_t *operands[FLOW_OPERANDS];
dag_make_leafs (dag, s, operands);
}
// actual dag creation
for (s = block->statements; s; s = s->next) {
operand_t *operands[FLOW_OPERANDS];
dagnode_t *n = 0, *children[3] = {0, 0, 0};
daglabel_t *op, *lx;
int i;
dag_make_children (dag, s, operands, children);
if (s->type == st_flow || s->type == st_func) {
for (i = 0; i < 3; i++) {
if (children[i]) {
dag_make_var_live (live_vars, operands[i + 1]);
}
}
}
if (operands[4]) {
// a movep instruction knew what it was reading, so mark that
// as live
dag_make_var_live (live_vars, operands[4]);
}
op = opcode_label (dag, s->opcode, s->expr);
n = children[0];
if (s->type != st_assign) {
if (!(n = dagnode_search (dag, op, children))) {
n = new_node (dag);
n->type = s->type;
n->label = op;
dagnode_add_children (dag, n, operands, children);
dagnode_set_edges (dag, n, s);
dagnode_set_reachable (dag, n);
}
}
lx = operand_label (dag, operands[0]);
if (lx && lx->dagnode != n) {
lx->expr = s->expr;
if (!dagnode_attach_label (dag, n, lx)) {
// attempting to attach the label to the node would create
// a dependency cycle in the dag, so a new node needs to be
// created for the source operand
if (s->type == st_assign) {
n = leaf_node (dag, operands[1], s->expr);
dagnode_attach_label (dag, n, lx);
} else {
internal_error (s->expr, "unexpected failure to attach"
" label to node");
}
}
}
if (n->type == st_ptrassign) {
dag_kill_nodes (dag, n);
}
}
nodes = malloc (dag->num_nodes * sizeof (dagnode_t *));
memcpy (nodes, dag->nodes, dag->num_nodes * sizeof (dagnode_t *));
dag->nodes = nodes;
labels = malloc (dag->num_labels * sizeof (daglabel_t *));
memcpy (labels, dag->labels, dag->num_labels * sizeof (daglabel_t *));
dag->labels = labels;
#if 0
if (options.block_dot.dags) {
flownode->dag = dag;
dump_dot ("raw-dags", flownode->graph, dump_dot_flow_dags);
}
#endif
dag_remove_dead_vars (dag, live_vars);
dag_sort_nodes (dag);
set_delete (live_vars);
return dag;
}
static statement_t *
build_statement (const char *opcode, operand_t **operands, expr_t *expr)
{
int i;
operand_t *op;
statement_t *st = new_statement (st_none, opcode, expr);
for (i = 0; i < 3; i++) {
if ((op = operands[i])) {
while (op->op_type == op_alias)
op = op->alias;
if (op->op_type == op_temp) {
while (op->tempop.alias)
op = op->tempop.alias;
op->tempop.users++;
}
}
}
st->opa = operands[0];
st->opb = operands[1];
st->opc = operands[2];
return st;
}
#if 0
static void
dag_calc_node_costs (dagnode_t *dagnode)
{
int i;
for (i = 0; i < 3; i++)
if (dagnode->children[i])
dag_calc_node_costs (dagnode->children[i]);
// if dagnode->a is null, then this is a leaf (as b and c are guaranted to
// be null)
if (!dagnode->children[0]) {
// Because qc vm statements don't mix source and destination operands,
// leaves never need temporary variables.
dagnode->cost = 0;
} else {
int different = 0;
// a non-leaf is guaranteed to have a valid first child
dagnode->cost = dagnode->children[0]->cost;
for (i = 1; i < 3; i++) {
if (dagnode->children[i]
&& dagnode->children[i]->cost != dagnode->cost) {
dagnode->cost = max (dagnode->cost,
dagnode->children[i]->cost);
different = 1;
}
}
if (!different)
dagnode->cost += 1;
}
}
#endif
static operand_t *
fix_op_type (operand_t *op, type_t *type)
{
if (op && op->op_type != op_label && op->type != type)
op = alias_operand (type, op, op->expr);
return op;
}
static operand_t *
make_operand (dag_t *dag, sblock_t *block, const dagnode_t *dagnode, int index)
{
operand_t *op;
op = dagnode->children[index]->value;
op = fix_op_type (op, dagnode->types[index]);
return op;
}
static operand_t *
generate_moves (dag_t *dag, sblock_t *block, dagnode_t *dagnode)
{
set_iter_t *var_iter;
daglabel_t *var;
operand_t *operands[3] = {0, 0, 0};
statement_t *st;
operand_t *dst;
operands[0] = make_operand (dag, block, dagnode, 0);
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)) {
var = dag->labels[var_iter->element];
operands[2] = var->op;
dst = operands[2];
st = build_statement ("move", operands, var->expr);
sblock_add_statement (block, st);
}
return dst;
}
static operand_t *
generate_moveps (dag_t *dag, sblock_t *block, dagnode_t *dagnode)
{
set_iter_t *var_iter;
daglabel_t *var;
operand_t *operands[3] = {0, 0, 0};
statement_t *st;
operand_t *dst = 0;
type_t *type;
int offset = 0;
def_t *dstDef;
operands[0] = make_operand (dag, block, dagnode, 0);
operands[1] = make_operand (dag, block, dagnode, 1);
if (dagnode->children[2]) {
operands[2] = make_operand (dag, block, dagnode, 2);
st = build_statement ("movep", operands, dagnode->label->expr);
sblock_add_statement (block, st);
if ((var_iter = set_first (dagnode->identifiers))) {
var = dag->labels[var_iter->element];
dst = var->op;
set_del_iter (var_iter);
}
} else {
for (var_iter = set_first (dagnode->identifiers); var_iter;
var_iter = set_next (var_iter)) {
var = dag->labels[var_iter->element];
dst = var->op;
type = dst->def->type;
dstDef = dst->def;
if (dstDef->alias) {
offset = dstDef->offset;
dstDef = dstDef->alias;
}
operands[2] = value_operand (new_pointer_val (offset, type, dstDef, 0),
operands[1]->expr);
st = build_statement ("movep", operands, var->expr);
sblock_add_statement (block, st);
}
}
return dst;
}
static operand_t *
generate_memsets (dag_t *dag, sblock_t *block, dagnode_t *dagnode)
{
set_iter_t *var_iter;
daglabel_t *var;
operand_t *operands[3] = {0, 0, 0};
statement_t *st;
operand_t *dst;
operands[0] = make_operand (dag, block, dagnode, 0);
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)) {
var = dag->labels[var_iter->element];
operands[2] = var->op;
dst = operands[2];
st = build_statement ("memset", operands, var->expr);
sblock_add_statement (block, st);
}
return dst;
}
static operand_t *
generate_memsetps (dag_t *dag, sblock_t *block, dagnode_t *dagnode)
{
set_iter_t *var_iter;
daglabel_t *var;
operand_t *operands[3] = {0, 0, 0};
statement_t *st;
operand_t *dst = 0;
type_t *type;
int offset = 0;
def_t *dstDef;
operands[0] = make_operand (dag, block, dagnode, 0);
operands[1] = make_operand (dag, block, dagnode, 1);
if (dagnode->children[2]) {
operands[2] = make_operand (dag, block, dagnode, 2);
st = build_statement ("memsetp", operands, dagnode->label->expr);
sblock_add_statement (block, st);
} else {
for (var_iter = set_first (dagnode->identifiers); var_iter;
var_iter = set_next (var_iter)) {
var = dag->labels[var_iter->element];
dst = var->op;
type = dst->def->type;
dstDef = dst->def;
if (dstDef->alias) {
offset = dstDef->offset;
dstDef = dstDef->alias;
}
operands[2] = value_operand (new_pointer_val (offset, type, dstDef, 0),
operands[1]->expr);
st = build_statement ("memsetp", operands, var->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, type_t *type)
{
statement_t *st;
operand_t *dst = 0;
operand_t *operands[3] = {0, 0, 0};
daglabel_t *var;
operands[2] = fix_op_type (src, type);
for ( ; var_iter; var_iter = set_next (var_iter)) {
var = dag->labels[var_iter->element];
operands[0] = fix_op_type (var->op, type);
if (!dst)
dst = operands[0];
st = build_statement ("assign", operands, var->expr);
sblock_add_statement (block, st);
}
return dst;
}
static void
dag_gencode (dag_t *dag, sblock_t *block, dagnode_t *dagnode)
{
operand_t *operands[3] = {0, 0, 0};
operand_t *dst = 0;
statement_t *st;
set_iter_t *var_iter;
int i;
type_t *type;
switch (dagnode->type) {
case st_none:
if (!dagnode->label->op)
internal_error (0, "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_expr:
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);
if (!(var_iter = set_first (dagnode->identifiers))) {
operands[2] = temp_operand (get_type (dagnode->label->expr),
dagnode->label->expr);
} else {
daglabel_t *var = dag->labels[var_iter->element];
operands[2] = fix_op_type (var->op, type);
var_iter = set_next (var_iter);
}
dst = operands[2];
st = build_statement (dagnode->label->opcode, operands,
dagnode->label->expr);
sblock_add_statement (block, st);
generate_assignments (dag, block, operands[2], var_iter, type);
break;
case st_assign:
internal_error (0, "unexpected assignment node");
case st_ptrassign:
operands[2] = make_operand (dag, block, dagnode, 0);
operands[0] = make_operand (dag, block, dagnode, 2);
if (dagnode->children[1])
operands[1] = make_operand (dag, block, dagnode, 1);
st = build_statement (dagnode->label->opcode, operands,
dagnode->label->expr);
sblock_add_statement (block, st);
// the source location is suitable for use in other nodes
dst = operands[2];
break;
case st_move:
dst = generate_moves (dag, block, dagnode);
break;
case st_ptrmove:
dst = generate_moveps (dag, block, dagnode);
break;
case st_memset:
dst = generate_memsets (dag, block, dagnode);
break;
case st_ptrmemset:
dst = generate_memsetps (dag, block, dagnode);
break;
case st_state:
case st_func:
for (i = 0; i < 3; i++)
if (dagnode->children[i])
operands[i] = make_operand (dag, block, dagnode, i);
st = build_statement (dagnode->label->opcode, operands,
dagnode->label->expr);
sblock_add_statement (block, st);
break;
case st_flow:
operands[0] = make_operand (dag, block, dagnode, 0);
if (dagnode->children[1])
operands[1] = make_operand (dag, block, dagnode, 1);
if (dagnode->children[2])
operands[2] = make_operand (dag, block, dagnode, 2);
st = build_statement (dagnode->label->opcode, operands,
dagnode->label->expr);
sblock_add_statement (block, st);
break;
}
dagnode->value = dst;
}
void
dag_remove_dead_nodes (dag_t *dag)
{
int added_root;
set_iter_t *root_i, *child_i;
dagnode_t *node, *child;
do {
added_root = 0;
for (root_i = set_first (dag->roots); root_i;
root_i = set_next (root_i)) {
node = dag->nodes[root_i->element];
// only st_none (leaf nodes), st_expr and st_move can become
// dead nodes (no live vars attached).
if (node->type != st_none && node->type != st_expr
&& node->type != st_move)
continue;
if (!set_is_empty (node->identifiers))
continue;
// MOVEP with a variable destination pointer is never dead
if (node->type == st_move && node->children[2])
continue;
set_remove (dag->roots, node->number);
for (child_i = set_first (node->edges); child_i;
child_i = set_next (child_i)) {
child = dag->nodes[child_i->element];
if (!set_is_member (child->parents, node->number))
continue; // not really a child (dependency edge)
set_remove (child->parents, node->number);
if (set_is_empty (child->parents)) {
set_add (dag->roots, child->number);
added_root = 1;
}
}
}
} while (added_root);
// clean up any stray edges that point to removed nodes
for (int i = 0; i < dag->num_nodes; i++) {
node = dag->nodes[i];
for (child_i = set_first (node->edges); child_i;
child_i = set_next (child_i)) {
child = dag->nodes[child_i->element];
if (!set_is_member (dag->roots, child->number)
&& set_is_empty (child->parents)) {
set_remove (node->edges, child->number);
}
}
}
dag_sort_nodes (dag);
}
void
dag_generate (dag_t *dag, sblock_t *block)
{
int i;
for (i = 0; i < dag->num_topo; i++)
dag_gencode (dag, block, dag->nodes[dag->topo[i]]);
}