X-Git-Url: https://oss.titaniummirror.com/gitweb/?a=blobdiff_plain;f=gcc%2Fcgraphbuild.c;fp=gcc%2Fcgraphbuild.c;h=75db87544cea3706c6e65c3cbc541f3da8b95d9f;hb=6fed43773c9b0ce596dca5686f37ac3fc0fa11c0;hp=0000000000000000000000000000000000000000;hpb=27b11d56b743098deb193d510b337ba22dc52e5c;p=msp430-gcc.git diff --git a/gcc/cgraphbuild.c b/gcc/cgraphbuild.c new file mode 100644 index 00000000..75db8754 --- /dev/null +++ b/gcc/cgraphbuild.c @@ -0,0 +1,279 @@ +/* Callgraph construction. + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 + Free Software Foundation, Inc. + Contributed by Jan Hubicka + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "tree-flow.h" +#include "langhooks.h" +#include "pointer-set.h" +#include "cgraph.h" +#include "intl.h" +#include "gimple.h" +#include "tree-pass.h" + +/* Walk tree and record all calls and references to functions/variables. + Called via walk_tree: TP is pointer to tree to be examined. */ + +static tree +record_reference (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED) +{ + tree t = *tp; + tree decl; + + switch (TREE_CODE (t)) + { + case VAR_DECL: + if (TREE_STATIC (t) || DECL_EXTERNAL (t)) + { + varpool_mark_needed_node (varpool_node (t)); + if (lang_hooks.callgraph.analyze_expr) + return lang_hooks.callgraph.analyze_expr (tp, walk_subtrees); + } + break; + + case FDESC_EXPR: + case ADDR_EXPR: + /* Record dereferences to the functions. This makes the + functions reachable unconditionally. */ + decl = TREE_OPERAND (*tp, 0); + if (TREE_CODE (decl) == FUNCTION_DECL) + cgraph_mark_needed_node (cgraph_node (decl)); + break; + + default: + /* Save some cycles by not walking types and declaration as we + won't find anything useful there anyway. */ + if (IS_TYPE_OR_DECL_P (*tp)) + { + *walk_subtrees = 0; + break; + } + + if ((unsigned int) TREE_CODE (t) >= LAST_AND_UNUSED_TREE_CODE) + return lang_hooks.callgraph.analyze_expr (tp, walk_subtrees); + break; + } + + return NULL_TREE; +} + +/* Give initial reasons why inlining would fail on all calls from + NODE. Those get either nullified or usually overwritten by more precise + reason later. */ + +static void +initialize_inline_failed (struct cgraph_node *node) +{ + struct cgraph_edge *e; + + for (e = node->callers; e; e = e->next_caller) + { + gcc_assert (!e->callee->global.inlined_to); + gcc_assert (e->inline_failed); + if (node->local.redefined_extern_inline) + e->inline_failed = N_("redefined extern inline functions are not " + "considered for inlining"); + else if (!node->local.inlinable) + e->inline_failed = N_("function not inlinable"); + else if (gimple_call_cannot_inline_p (e->call_stmt)) + e->inline_failed = N_("mismatched arguments"); + else + e->inline_failed = N_("function not considered for inlining"); + } +} + +/* Computes the frequency of the call statement so that it can be stored in + cgraph_edge. BB is the basic block of the call statement. */ +int +compute_call_stmt_bb_frequency (basic_block bb) +{ + int entry_freq = ENTRY_BLOCK_PTR->frequency; + int freq = bb->frequency; + + if (!entry_freq) + entry_freq = 1, freq++; + + freq = freq * CGRAPH_FREQ_BASE / entry_freq; + if (freq > CGRAPH_FREQ_MAX) + freq = CGRAPH_FREQ_MAX; + + return freq; +} + +/* Create cgraph edges for function calls. + Also look for functions and variables having addresses taken. */ + +static unsigned int +build_cgraph_edges (void) +{ + basic_block bb; + struct cgraph_node *node = cgraph_node (current_function_decl); + struct pointer_set_t *visited_nodes = pointer_set_create (); + gimple_stmt_iterator gsi; + tree step; + + /* Create the callgraph edges and record the nodes referenced by the function. + body. */ + FOR_EACH_BB (bb) + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + tree decl; + + if (is_gimple_call (stmt) && (decl = gimple_call_fndecl (stmt))) + { + size_t i; + size_t n = gimple_call_num_args (stmt); + cgraph_create_edge (node, cgraph_node (decl), stmt, + bb->count, compute_call_stmt_bb_frequency (bb), + bb->loop_depth); + for (i = 0; i < n; i++) + walk_tree (gimple_call_arg_ptr (stmt, i), record_reference, + node, visited_nodes); + if (gimple_call_lhs (stmt)) + walk_tree (gimple_call_lhs_ptr (stmt), record_reference, node, + visited_nodes); + } + else + { + struct walk_stmt_info wi; + memset (&wi, 0, sizeof (wi)); + wi.info = node; + wi.pset = visited_nodes; + walk_gimple_op (stmt, record_reference, &wi); + if (gimple_code (stmt) == GIMPLE_OMP_PARALLEL + && gimple_omp_parallel_child_fn (stmt)) + { + tree fn = gimple_omp_parallel_child_fn (stmt); + cgraph_mark_needed_node (cgraph_node (fn)); + } + if (gimple_code (stmt) == GIMPLE_OMP_TASK) + { + tree fn = gimple_omp_task_child_fn (stmt); + if (fn) + cgraph_mark_needed_node (cgraph_node (fn)); + fn = gimple_omp_task_copy_fn (stmt); + if (fn) + cgraph_mark_needed_node (cgraph_node (fn)); + } + } + } + + /* Look for initializers of constant variables and private statics. */ + for (step = cfun->local_decls; + step; + step = TREE_CHAIN (step)) + { + tree decl = TREE_VALUE (step); + if (TREE_CODE (decl) == VAR_DECL + && (TREE_STATIC (decl) && !DECL_EXTERNAL (decl))) + varpool_finalize_decl (decl); + else if (TREE_CODE (decl) == VAR_DECL && DECL_INITIAL (decl)) + walk_tree (&DECL_INITIAL (decl), record_reference, node, visited_nodes); + } + + pointer_set_destroy (visited_nodes); + initialize_inline_failed (node); + return 0; +} + +struct gimple_opt_pass pass_build_cgraph_edges = +{ + { + GIMPLE_PASS, + NULL, /* name */ + NULL, /* gate */ + build_cgraph_edges, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + PROP_cfg, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0 /* todo_flags_finish */ + } +}; + +/* Record references to functions and other variables present in the + initial value of DECL, a variable. */ + +void +record_references_in_initializer (tree decl) +{ + struct pointer_set_t *visited_nodes = pointer_set_create (); + walk_tree (&DECL_INITIAL (decl), record_reference, NULL, visited_nodes); + pointer_set_destroy (visited_nodes); +} + +/* Rebuild cgraph edges for current function node. This needs to be run after + passes that don't update the cgraph. */ + +unsigned int +rebuild_cgraph_edges (void) +{ + basic_block bb; + struct cgraph_node *node = cgraph_node (current_function_decl); + gimple_stmt_iterator gsi; + + cgraph_node_remove_callees (node); + + node->count = ENTRY_BLOCK_PTR->count; + + FOR_EACH_BB (bb) + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + tree decl; + + if (is_gimple_call (stmt) && (decl = gimple_call_fndecl (stmt))) + cgraph_create_edge (node, cgraph_node (decl), stmt, + bb->count, compute_call_stmt_bb_frequency (bb), + bb->loop_depth); + + } + initialize_inline_failed (node); + gcc_assert (!node->global.inlined_to); + return 0; +} + +struct gimple_opt_pass pass_rebuild_cgraph_edges = +{ + { + GIMPLE_PASS, + NULL, /* name */ + NULL, /* gate */ + rebuild_cgraph_edges, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + PROP_cfg, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + } +};