From 554b2e4710be0eefa746286b6aeac3b27078f486 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Fri, 30 Nov 2012 17:04:18 +0900 Subject: [PATCH] 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. --- tools/qfcc/include/flow.h | 5 +++ tools/qfcc/include/statements.h | 1 + tools/qfcc/source/dot_flow.c | 19 +++++++++++ tools/qfcc/source/flow.c | 59 +++++++++++++++++++++++++++++++++ tools/qfcc/source/statements.c | 12 +++++++ 5 files changed, 96 insertions(+) diff --git a/tools/qfcc/include/flow.h b/tools/qfcc/include/flow.h index 17cc0fc1b..162d49020 100644 --- a/tools/qfcc/include/flow.h +++ b/tools/qfcc/include/flow.h @@ -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); //@} diff --git a/tools/qfcc/include/statements.h b/tools/qfcc/include/statements.h index 338e32880..195a63d76 100644 --- a/tools/qfcc/include/statements.h +++ b/tools/qfcc/include/statements.h @@ -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); diff --git a/tools/qfcc/source/dot_flow.c b/tools/qfcc/source/dot_flow.c index 141db49ac..8382691a7 100644 --- a/tools/qfcc/source/dot_flow.c +++ b/tools/qfcc/source/dot_flow.c @@ -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); +} diff --git a/tools/qfcc/source/flow.c b/tools/qfcc/source/flow.c index ed362a641..b2e42b6e4 100644 --- a/tools/qfcc/source/flow.c +++ b/tools/qfcc/source/flow.c @@ -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); diff --git a/tools/qfcc/source/statements.c b/tools/qfcc/source/statements.c index bafb0e83c..bed9e11fd 100644 --- a/tools/qfcc/source/statements.c +++ b/tools/qfcc/source/statements.c @@ -392,6 +392,18 @@ statement_is_jumpb (statement_t *s) return !strcmp (s->opcode, ""); } +int +statement_is_call (statement_t *s) +{ + if (!s) + return 0; + if (!strncmp (s->opcode, "opcode, "