mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-03-23 10:50:58 +00:00
Implement uninitialized variable checking.
It currently gets a lot of false positives (globals and parameters?), but the initial tests for just local vars worked.
This commit is contained in:
parent
db92a7b9f9
commit
547ea78ad7
2 changed files with 139 additions and 0 deletions
|
@ -76,6 +76,12 @@ typedef struct flownode_s {
|
|||
struct set_s *in;
|
||||
struct set_s *out;
|
||||
} live_vars;
|
||||
struct {
|
||||
struct set_s *use;
|
||||
struct set_s *def;
|
||||
struct set_s *in;
|
||||
struct set_s *out;
|
||||
} init_vars;
|
||||
struct sblock_s *sblock; ///< original statement block
|
||||
struct dag_s *dag; ///< dag for this node
|
||||
} flownode_t;
|
||||
|
|
|
@ -160,6 +160,29 @@ delete_graph (flowgraph_t *graph)
|
|||
free_graphs = graph;
|
||||
}
|
||||
|
||||
static def_t *
|
||||
flowvar_get_def (flowvar_t *var)
|
||||
{
|
||||
operand_t *op = var->op;
|
||||
|
||||
while (op->op_type == op_alias)
|
||||
op = op->o.alias;
|
||||
switch (op->op_type) {
|
||||
case op_symbol:
|
||||
return op->o.symbol->s.def;
|
||||
case op_value:
|
||||
case op_label:
|
||||
return 0;
|
||||
case op_temp:
|
||||
return op->o.tempop.def;
|
||||
case op_pointer:
|
||||
return op->o.value->v.pointer.def;
|
||||
case op_alias:
|
||||
internal_error (0, "oops, blue pill");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
flowvar_is_global (flowvar_t *var)
|
||||
{
|
||||
|
@ -177,6 +200,25 @@ flowvar_is_global (flowvar_t *var)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
flowvar_is_param (flowvar_t *var)
|
||||
{
|
||||
symbol_t *sym;
|
||||
def_t *def;
|
||||
|
||||
if (var->op->op_type != op_symbol)
|
||||
return 0;
|
||||
sym = var->op->o.symbol;
|
||||
if (sym->sy_type != sy_var)
|
||||
return 0;
|
||||
def = sym->s.def;
|
||||
if (!def->local)
|
||||
return 0;
|
||||
if (!def->param)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
flowvar_t *
|
||||
flow_get_var (operand_t *op)
|
||||
{
|
||||
|
@ -396,6 +438,96 @@ flow_live_vars (flowgraph_t *graph)
|
|||
set_delete (tmp);
|
||||
}
|
||||
|
||||
static void
|
||||
flow_uninitialized (flowgraph_t *graph)
|
||||
{
|
||||
set_t *stuse;
|
||||
set_t *stdef;
|
||||
set_t *tmp;
|
||||
set_t *uninit;
|
||||
set_t *use;
|
||||
set_t *def;
|
||||
set_iter_t *pred_iter;
|
||||
set_iter_t *var_iter;
|
||||
flownode_t *node, *pred;
|
||||
function_t *func = graph->func;
|
||||
statement_t *st;
|
||||
int i;
|
||||
|
||||
if (!graph->num_nodes)
|
||||
return;
|
||||
|
||||
stuse = set_new ();
|
||||
stdef = set_new ();
|
||||
tmp = set_new ();
|
||||
uninit = set_new ();
|
||||
|
||||
// def for the inital node contains parameters and global vars
|
||||
def = set_new ();
|
||||
for (i = 0; i < func->num_vars; i++) {
|
||||
flowvar_t *var = func->vars[i];
|
||||
if (flowvar_is_global (var) || flowvar_is_param (var))
|
||||
set_add (def, i);
|
||||
}
|
||||
// first, calculate use and def for each block, and initialize the in and
|
||||
// out sets.
|
||||
for (i = 0; i < graph->num_nodes; i++) {
|
||||
node = graph->nodes[i];
|
||||
use = set_new ();
|
||||
for (st = node->sblock->statements; st; st = st->next) {
|
||||
flow_analyze_statement (st, stuse, stdef, 0, 0);
|
||||
live_set_use (stuse, use, def); // init use uses same rules as live
|
||||
set_union (def, stdef); // for init, always def a set var
|
||||
}
|
||||
node->init_vars.use = use;
|
||||
node->init_vars.def = def;
|
||||
node->init_vars.in = set_new ();
|
||||
node->init_vars.out = set_new ();
|
||||
def = set_new ();
|
||||
}
|
||||
// flow DOWN the graph in dept-first order
|
||||
for (i = 0; i < graph->num_nodes; i++) {
|
||||
node = graph->nodes[graph->dfo[i]];
|
||||
pred_iter = set_first (node->predecessors);
|
||||
if (pred_iter) {
|
||||
do {
|
||||
// not interested in back edges
|
||||
pred = graph->nodes[pred_iter->member];
|
||||
pred_iter = set_next (pred_iter);
|
||||
} while (pred->dfn >= node->dfn && pred_iter);
|
||||
if (pred_iter)
|
||||
set_assign (tmp, pred->init_vars.out);
|
||||
}
|
||||
for ( ; pred_iter; pred_iter = set_next (pred_iter)) {
|
||||
if (graph->nodes[pred_iter->member]->dfn >= node->dfn)
|
||||
continue; // not interested in back edges
|
||||
set_intersection (tmp,
|
||||
graph->nodes[pred_iter->member]->init_vars.out);
|
||||
}
|
||||
set_assign (node->init_vars.in, tmp);
|
||||
set_assign (node->init_vars.out, tmp);
|
||||
set_union (node->init_vars.out, node->init_vars.def);
|
||||
set_assign (tmp, node->init_vars.use);
|
||||
set_difference (tmp, node->init_vars.in);
|
||||
set_union (uninit, tmp);
|
||||
}
|
||||
for (var_iter = set_first (uninit); var_iter;
|
||||
var_iter = set_next (var_iter)) {
|
||||
expr_t dummy;
|
||||
flowvar_t *var = func->vars[var_iter->member];
|
||||
def_t *def;
|
||||
|
||||
def = flowvar_get_def (var);
|
||||
dummy.line = def->line;
|
||||
dummy.file = def->file;
|
||||
warning (&dummy, "%s may be used uninitialized", def->name);
|
||||
}
|
||||
set_delete (stuse);
|
||||
set_delete (stdef);
|
||||
set_delete (tmp);
|
||||
set_delete (uninit);
|
||||
}
|
||||
|
||||
static void
|
||||
flow_build_dags (flowgraph_t *graph)
|
||||
{
|
||||
|
@ -436,6 +568,7 @@ flow_data_flow (flowgraph_t *graph)
|
|||
}
|
||||
}
|
||||
flow_live_vars (graph);
|
||||
flow_uninitialized (graph);
|
||||
flow_build_dags (graph);
|
||||
if (options.block_dot.flow)
|
||||
dump_dot ("flow", graph, dump_dot_flow);
|
||||
|
|
Loading…
Reference in a new issue