[qfcc] Remove special treatment for flow/func statements

The fix in bdafdad0d5 for
`while (count--)` never did appeal to me. I think I understood the core
problem at the time, but I hadn't figured out how to use a var's
use/define sets to detect the write-before-read. Using them allows the
special handling for flow control to be removed, making things more
robust. The function call handling has been superfluous since the
Ruamoko instruction set required the auxiliary operands on the call
statements.
This commit is contained in:
Bill Currie 2024-02-19 21:40:41 +09:00
parent 1fb4a556d7
commit b842913ca9
3 changed files with 60 additions and 21 deletions

View file

@ -93,6 +93,8 @@ typedef struct flownode_s {
struct set_s *in;
struct set_s *out;
} live_vars;
int first_statement;///< first statement in function's list
int num_statements; ///< number of statements in this block
struct sblock_s *sblock; ///< original statement block
struct dag_s *dag; ///< dag for this node
} flownode_t;

View file

@ -747,19 +747,6 @@ dag_live_aliases(operand_t *op)
}
}
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)
{
@ -953,6 +940,47 @@ dag_make_op_leafs (operand_t *op, dag_t *dag, const expr_t *expr)
}
}
static void
dag_free_set (set_t **set)
{
set_delete (*set);
}
static bool
dag_check_overlap (statement_t *s, dagnode_t *node, daglabel_t *label,
flownode_t *flownode)
{
int start = s->number + 1;
int end = flownode->first_statement + flownode->num_statements;
auto nvar = flow_get_var (node->label->op);
__attribute__((cleanup(dag_free_set))) set_t *def = set_new ();
set_add_range (def, start, end - start);
set_intersection (def, nvar->define);
auto d = set_first (def);
if (!d) {
// not set in this flow node
return false;
}
auto lvar = flow_get_var (label->op);
__attribute__((cleanup(dag_free_set))) set_t *use = set_new ();
set_add_range (use, start, end - start);
set_intersection (use, lvar->use);
bool overlap = false;
for (auto u = set_first (use); u; u = set_next (u)) {
if (u->element > d->element) {
overlap = true;
set_del_iter (u);
break;
}
}
set_del_iter (d);
return overlap;
}
dag_t *
dag_create (flownode_t *flownode)
{
@ -1006,13 +1034,6 @@ dag_create (flownode_t *flownode)
daglabel_t *op, *lx;
dag_make_children (dag, s, operands, children);
if (s->type == st_flow || s->type == st_func) {
for (int i = 0; i < 3; i++) {
if (children[i]) {
dag_make_var_live (live_vars, operands[i + 1]);
}
}
}
op = opcode_label (dag, s->opcode, s->expr);
n = children[0];
if (s->type != st_assign) {
@ -1044,6 +1065,15 @@ dag_create (flownode_t *flownode)
" label to node");
}
}
if (n->type == st_none && op_is_identifier (n->label->op)) {
// if the attached variable has a use after the variable in
// the leaf node has a define in the block, then force the
// attached variable to be live. Takes care of code similar
// to `while (count--) {...}`.
if (dag_check_overlap (s, n, lx, flownode)) {
lx->live = 1;
}
}
}
if (n->type == st_ptrassign) {
dag_kill_nodes (dag, n);

View file

@ -2137,8 +2137,15 @@ flow_build_graph (function_t *func)
graph->num_nodes++;
// + 2 for the uninitialized dummy head block and the live dummy end block
graph->nodes = malloc ((graph->num_nodes + 2) * sizeof (flownode_t *));
for (i = 0, sb = sblock; sb; i++, sb = sb->next)
int num_statements = 0;
for (i = 0, sb = sblock; sb; i++, sb = sb->next) {
graph->nodes[i] = flow_make_node (sb, i, func);
graph->nodes[i]->first_statement = num_statements;
for (auto s = sb->statements; s; s = s->next) {
graph->nodes[i]->num_statements++;
}
num_statements += graph->nodes[i]->num_statements;
}
// Create the dummy node for detecting uninitialized variables
node = flow_make_node (0, graph->num_nodes, func);
graph->nodes[graph->num_nodes] = node;