mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 14:20:59 +00:00
afd1eb775b
Rather short (no worse than ev_int, though) but more consistency is usually a good thing.
1277 lines
32 KiB
C
1277 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, ".")
|
|
&& n->label->opcode && !strcmp (n->label->opcode, ".="))
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
if (n->type == st_func) {
|
|
const char *num_params = 0;
|
|
int first_param = 0;
|
|
flowvar_t **flowvars = dag->flownode->graph->func->vars;
|
|
|
|
if (!strncmp (n->label->opcode, "<RCALL", 6)) {
|
|
num_params = n->label->opcode + 6;
|
|
first_param = 2;
|
|
} else if (!strncmp (n->label->opcode, "<CALL", 5)) {
|
|
num_params = n->label->opcode + 5;
|
|
} else if (!strcmp (n->label->opcode, "<RETURN>")) {
|
|
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);
|
|
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[0] = fix_op_type (src, type);
|
|
for ( ; var_iter; var_iter = set_next (var_iter)) {
|
|
var = dag->labels[var_iter->element];
|
|
operands[1] = fix_op_type (var->op, type);
|
|
if (!dst)
|
|
dst = operands[1];
|
|
|
|
st = build_statement ("=", 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[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 (dagnode->label->opcode, operands,
|
|
dagnode->label->expr);
|
|
sblock_add_statement (block, st);
|
|
// the source location is suitable for use in other nodes
|
|
dst = operands[0];
|
|
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);
|
|
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]]);
|
|
}
|