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, "