mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-03-21 18:01:15 +00:00
Add flow analysis to determin the type of .return.
It doesn't quite work yet, but... It has proven necessary to know what type .return has at any point in the function. The segfault in ctf is caused by the return statement added to the end of the void function messing with the expr pointer stored in the daglabel for .return. While this is actually by design (though the statement really should have a valid expr pointer rather than), it actually highlights a bigger problem: there's no stable knowledge of the current type of .return. This is not a problem in expression statements as the dagnodes for expression statements store the desired types of all operands. However, when assigning from .return to attached variables in a leaf node, the type of .return is not stored anywhere but the expression last accessing .return.
This commit is contained in:
parent
388247791d
commit
554b2e4710
5 changed files with 96 additions and 0 deletions
|
@ -82,6 +82,10 @@ typedef struct flownode_s {
|
|||
struct set_s *in;
|
||||
struct set_s *out;
|
||||
} init_vars;
|
||||
struct {
|
||||
etype_t in;
|
||||
etype_t out; ///< if different from in, then block defines
|
||||
} return_type; ///< type of .return for this node
|
||||
struct sblock_s *sblock; ///< original statement block
|
||||
struct dag_s *dag; ///< dag for this node
|
||||
} flownode_t;
|
||||
|
@ -112,6 +116,7 @@ struct sblock_s *flow_generate (flowgraph_t *graph);
|
|||
void dump_dot_flow (void *g, const char *filename);
|
||||
void dump_dot_flow_dags (void *g, const char *filename);
|
||||
void dump_dot_flow_live (void *g, const char *filename);
|
||||
void dump_dot_flow_return (void *g, const char *filename);
|
||||
|
||||
//@}
|
||||
|
||||
|
|
|
@ -115,6 +115,7 @@ statement_t *new_statement (st_type_t type, const char *opcode,
|
|||
int statement_is_cond (statement_t *s);
|
||||
int statement_is_goto (statement_t *s);
|
||||
int statement_is_jumpb (statement_t *s);
|
||||
int statement_is_call (statement_t *s);
|
||||
int statement_is_return (statement_t *s);
|
||||
sblock_t *statement_get_target (statement_t *s);
|
||||
sblock_t **statement_get_targetlist (statement_t *s);
|
||||
|
|
|
@ -200,10 +200,23 @@ print_flow_node_live (dstring_t *dstr, flowgraph_t *graph, flownode_t *node,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_flow_node_return (dstring_t *dstr, flowgraph_t *graph, flownode_t *node,
|
||||
int level)
|
||||
{
|
||||
int indent = level * 2 + 2;
|
||||
|
||||
dasprintf (dstr, "%*s\"fn_%p\" [label=\"%d (%d)\\n%s\\n%s\"];\n",
|
||||
indent, "", node, node->id, node->dfn,
|
||||
pr_type_name[node->return_type.in],
|
||||
pr_type_name[node->return_type.out]);
|
||||
}
|
||||
|
||||
static flow_dot_t flow_dot_methods[] = {
|
||||
{"", print_flow_node, print_flow_edge},
|
||||
{"dag", print_flow_node_dag, print_flow_edge_dag},
|
||||
{"live", print_flow_node_live, print_flow_edge},
|
||||
{"return", print_flow_node_return, print_flow_edge},
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -254,3 +267,9 @@ dump_dot_flow_live (void *g, const char *filename)
|
|||
{
|
||||
print_flowgraph (&flow_dot_methods[2], (flowgraph_t *) g, filename);
|
||||
}
|
||||
|
||||
void
|
||||
dump_dot_flow_return (void *g, const char *filename)
|
||||
{
|
||||
print_flowgraph (&flow_dot_methods[3], (flowgraph_t *) g, filename);
|
||||
}
|
||||
|
|
|
@ -558,6 +558,64 @@ flow_uninitialized (flowgraph_t *graph)
|
|||
set_delete (predecessors);
|
||||
}
|
||||
|
||||
static etype_t
|
||||
get_function_type (operand_t *op)
|
||||
{
|
||||
type_t *type = &type_void;
|
||||
|
||||
//FIXME fuction type casts?
|
||||
while (op->op_type == op_alias)
|
||||
op = op->o.alias;
|
||||
if (op->op_type == op_symbol) {
|
||||
type = op->o.symbol->type;
|
||||
if (type->type != ev_func)
|
||||
internal_error (0, "not a function symbol");
|
||||
type = type->t.func.type;
|
||||
} else if (op->op_type == op_value) {
|
||||
if (op->o.value->type != ev_func)
|
||||
internal_error (0, "not a function value");
|
||||
type = op->o.value->v.func_val.type;
|
||||
}
|
||||
// fixme temps?
|
||||
return low_level_type (type);
|
||||
}
|
||||
|
||||
static void
|
||||
flow_set_return_type (flownode_t *node, etype_t type)
|
||||
{
|
||||
statement_t *st = (statement_t *) node->sblock->tail;
|
||||
node->return_type.in = type;
|
||||
node->return_type.out = node->return_type.in;
|
||||
if (node->sblock->statements
|
||||
&& statement_is_call (st))
|
||||
node->return_type.out = get_function_type (st->opa);
|
||||
}
|
||||
|
||||
static void
|
||||
flow_return_type (flowgraph_t *graph)
|
||||
{
|
||||
etype_t return_type;
|
||||
flownode_t *node;
|
||||
set_iter_t *pred_iter;
|
||||
flownode_t *pred;
|
||||
int i;
|
||||
|
||||
node = graph->nodes[0];
|
||||
flow_set_return_type (node, ev_void);
|
||||
for (i = 1; i < graph->num_nodes; i++) {
|
||||
node = graph->nodes[graph->dfo[i]];
|
||||
for (pred_iter = set_first (node->predecessors); pred_iter;
|
||||
pred_iter = set_next (pred_iter)) {
|
||||
pred = graph->nodes[pred_iter->member];
|
||||
if (return_type == ev_type_count)
|
||||
return_type = pred->return_type.out;
|
||||
if (return_type != pred->return_type.out)
|
||||
return_type = ev_void;
|
||||
}
|
||||
flow_set_return_type (node, return_type);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
flow_build_dags (flowgraph_t *graph)
|
||||
{
|
||||
|
@ -599,6 +657,7 @@ flow_data_flow (flowgraph_t *graph)
|
|||
}
|
||||
flow_live_vars (graph);
|
||||
flow_uninitialized (graph);
|
||||
flow_return_type (graph);
|
||||
flow_build_dags (graph);
|
||||
if (options.block_dot.flow)
|
||||
dump_dot ("flow", graph, dump_dot_flow);
|
||||
|
|
|
@ -392,6 +392,18 @@ statement_is_jumpb (statement_t *s)
|
|||
return !strcmp (s->opcode, "<JUMPB>");
|
||||
}
|
||||
|
||||
int
|
||||
statement_is_call (statement_t *s)
|
||||
{
|
||||
if (!s)
|
||||
return 0;
|
||||
if (!strncmp (s->opcode, "<CALL", 5))
|
||||
return 1;
|
||||
if (!strncmp (s->opcode, "<RCALL", 6))
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
statement_is_return (statement_t *s)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue