diff options
-rw-r--r-- | gcc/ChangeLog | 35 | ||||
-rw-r--r-- | gcc/cgraph.c | 460 | ||||
-rw-r--r-- | gcc/cgraph.h | 62 | ||||
-rw-r--r-- | gcc/cgraphbuild.c | 14 | ||||
-rw-r--r-- | gcc/cgraphunit.c | 640 | ||||
-rw-r--r-- | gcc/ipa-inline-transform.c | 15 | ||||
-rw-r--r-- | gcc/langhooks-def.h | 8 | ||||
-rw-r--r-- | gcc/langhooks.c | 7 | ||||
-rw-r--r-- | gcc/langhooks.h | 9 | ||||
-rw-r--r-- | gcc/tree-inline.c | 3 | ||||
-rw-r--r-- | gcc/tree.c | 1 | ||||
-rw-r--r-- | gcc/varpool.c | 11 |
12 files changed, 613 insertions, 652 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 99203ffb398..51fefab463b 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,40 @@ 2012-04-25 Jan Hubicka <jh@suse.cz> + * cgraphunit.c: Update toplevel comment. + (tree_rest_of_compilation): Merge into cgraph_expand_function. + (cgraph_analyze_function): Make static. + (cgraph_decide_is_function_needed): Make static. + (cgraph_add_new_function): Use expand_function instead of + rest_of_compilation. + (clone_of_p, verify_edge_count_and_frequency, cgraph_debug_gimple_stmt, + verify_edge_corresponds_to_fndecl, verify_cgraph_node, verify_cgraph): + Move to cgraph.c + (cgraph_inline_p): Remove. + (cgraph_preserve_function_body_p): Move to ipa-inline-transform. + (init_cgraph): Add comment. + * cgraphbuild.c (record_reference, mark_address, mark_load, + mark_store): Do not call analyze_expr hook. + * cgraph.c: Update toplevel comment. + (clone_of_p, verify_edge_count_and_frequency, cgraph_debug_gimple_stmt, + verify_edge_corresponds_to_fndecl, verify_cgraph_node, verify_cgraph): + Move fere from cgraphunit.c + (cgraph_mark_force_output_node): Move to cgraph.h + * cgraph.h: Reorder so the comments match the function placement. + (cgraph_analyze_function, cgraph_decide_is_function_needed): Remove. + (cgraph_mark_force_output_node): Move here from cgraph.c + * tree.c (free_lang_data): Do not clear analyze_expr hook. + * ipa-inline-transform.c (preserve_function_body_p): New function. + (inline_transform): Update. + * langhooks.c (lhd_callgraph_analyze_expr): Remove. + * langhooks.h (lang_hooks_for_callgraph): Remove. + (lang_hooks): Remove callgraph. + * tree-inline.c (expand_call_inline): Do not use cgraph_inline_p. + * varpool.c: Remove out of date comment. + * langhooks-def.h (lhd_callgraph_analyze_expr): Remove. + (LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR): Remove. + +2012-04-25 Jan Hubicka <jh@suse.cz> + PR middle-end/53089 * cgraphunit.c (referred_to_p): Move ahead in file to avoid forward declaration. (cgraph_finalize_function): Finalize them here. diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 298a030400c..e689d1083ca 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -21,56 +21,8 @@ along with GCC; see the file COPYING3. If not see /* This file contains basic routines manipulating call graph -The callgraph: - - The call-graph is data structure designed for intra-procedural optimization - but it is also used in non-unit-at-a-time compilation to allow easier code - sharing. - - The call-graph consist of nodes and edges represented via linked lists. - Each function (external or not) corresponds to the unique node. - - The mapping from declarations to call-graph nodes is done using hash table - based on DECL_UID. The call-graph nodes are created lazily using - cgraph_node function when called for unknown declaration. - - The callgraph at the moment does not represent all indirect calls or calls - from other compilation units. Flag NEEDED is set for each node that may be - accessed in such an invisible way and it shall be considered an entry point - to the callgraph. - - On the other hand, the callgraph currently does contain some edges for - indirect calls with unknown callees which can be accessed through - indirect_calls field of a node. It should be noted however that at the - moment only calls which are potential candidates for indirect inlining are - added there. - - Interprocedural information: - - Callgraph is place to store data needed for interprocedural optimization. - All data structures are divided into three components: local_info that - is produced while analyzing the function, global_info that is result - of global walking of the callgraph on the end of compilation and - rtl_info used by RTL backend to propagate data from already compiled - functions to their callers. - - Moreover, each node has a uid which can be used to keep information in - on-the-side arrays. UIDs are reused and therefore reasonably dense. - - Inlining plans: - - The function inlining information is decided in advance and maintained - in the callgraph as so called inline plan. - For each inlined call, the callee's node is cloned to represent the - new function copy produced by inliner. - Each inlined call gets a unique corresponding clone node of the callee - and the data structure is updated while inlining is performed, so - the clones are eliminated and their callee edges redirected to the - caller. - - Each edge has "inline_failed" field. When the field is set to NULL, - the call will be inlined. When it is non-NULL it contains a reason - why inlining wasn't performed. */ + The call-graph is a data structure designed for intra-procedural optimization. + It represents a multi-graph where nodes are functions and edges are call sites. */ #include "config.h" #include "system.h" @@ -100,6 +52,7 @@ The callgraph: #include "lto-streamer.h" #include "ipa-inline.h" #include "cfgloop.h" +#include "gimple-pretty-print.h" const char * const ld_plugin_symbol_resolution_names[]= { @@ -1472,16 +1425,6 @@ cgraph_remove_node_and_inline_clones (struct cgraph_node *node, struct cgraph_no return found; } -/* Likewise indicate that a node is needed, i.e. reachable via some - external means. */ - -void -cgraph_mark_force_output_node (struct cgraph_node *node) -{ - node->symbol.force_output = 1; - gcc_assert (!node->global.inlined_to); -} - /* Likewise indicate that a node is having address taken. */ void @@ -2672,4 +2615,401 @@ collect_callers_of_node (struct cgraph_node *node) return redirect_callers; } +/* Return TRUE if NODE2 is equivalent to NODE or its clone. */ +static bool +clone_of_p (struct cgraph_node *node, struct cgraph_node *node2) +{ + node = cgraph_function_or_thunk_node (node, NULL); + node2 = cgraph_function_or_thunk_node (node2, NULL); + while (node != node2 && node2) + node2 = node2->clone_of; + return node2 != NULL; +} + +/* Verify edge E count and frequency. */ + +static bool +verify_edge_count_and_frequency (struct cgraph_edge *e) +{ + bool error_found = false; + if (e->count < 0) + { + error ("caller edge count is negative"); + error_found = true; + } + if (e->frequency < 0) + { + error ("caller edge frequency is negative"); + error_found = true; + } + if (e->frequency > CGRAPH_FREQ_MAX) + { + error ("caller edge frequency is too large"); + error_found = true; + } + if (gimple_has_body_p (e->caller->symbol.decl) + && !e->caller->global.inlined_to + /* FIXME: Inline-analysis sets frequency to 0 when edge is optimized out. + Remove this once edges are actualy removed from the function at that time. */ + && (e->frequency + || (inline_edge_summary_vec + && ((VEC_length(inline_edge_summary_t, inline_edge_summary_vec) + <= (unsigned) e->uid) + || !inline_edge_summary (e)->predicate))) + && (e->frequency + != compute_call_stmt_bb_frequency (e->caller->symbol.decl, + gimple_bb (e->call_stmt)))) + { + error ("caller edge frequency %i does not match BB frequency %i", + e->frequency, + compute_call_stmt_bb_frequency (e->caller->symbol.decl, + gimple_bb (e->call_stmt))); + error_found = true; + } + return error_found; +} + +/* Switch to THIS_CFUN if needed and print STMT to stderr. */ +static void +cgraph_debug_gimple_stmt (struct function *this_cfun, gimple stmt) +{ + /* debug_gimple_stmt needs correct cfun */ + if (cfun != this_cfun) + set_cfun (this_cfun); + debug_gimple_stmt (stmt); +} + +/* Verify that call graph edge E corresponds to DECL from the associated + statement. Return true if the verification should fail. */ + +static bool +verify_edge_corresponds_to_fndecl (struct cgraph_edge *e, tree decl) +{ + struct cgraph_node *node; + + if (!decl || e->callee->global.inlined_to) + return false; + node = cgraph_get_node (decl); + + /* We do not know if a node from a different partition is an alias or what it + aliases and therefore cannot do the former_clone_of check reliably. */ + if (!node || node->symbol.in_other_partition) + return false; + node = cgraph_function_or_thunk_node (node, NULL); + + if ((e->callee->former_clone_of != node->symbol.decl + && (!node->same_body_alias + || e->callee->former_clone_of != node->thunk.alias)) + /* IPA-CP sometimes redirect edge to clone and then back to the former + function. This ping-pong has to go, eventually. */ + && (node != cgraph_function_or_thunk_node (e->callee, NULL)) + && !clone_of_p (node, e->callee) + /* If decl is a same body alias of some other decl, allow e->callee to be + a clone of a clone of that other decl too. */ + && (!node->same_body_alias + || !clone_of_p (cgraph_get_node (node->thunk.alias), e->callee))) + return true; + else + return false; +} + +/* Verify cgraph nodes of given cgraph node. */ +DEBUG_FUNCTION void +verify_cgraph_node (struct cgraph_node *node) +{ + struct cgraph_edge *e; + struct function *this_cfun = DECL_STRUCT_FUNCTION (node->symbol.decl); + basic_block this_block; + gimple_stmt_iterator gsi; + bool error_found = false; + + if (seen_error ()) + return; + + timevar_push (TV_CGRAPH_VERIFY); + error_found |= verify_symtab_base ((symtab_node) node); + for (e = node->callees; e; e = e->next_callee) + if (e->aux) + { + error ("aux field set for edge %s->%s", + identifier_to_locale (cgraph_node_name (e->caller)), + identifier_to_locale (cgraph_node_name (e->callee))); + error_found = true; + } + if (node->count < 0) + { + error ("execution count is negative"); + error_found = true; + } + if (node->global.inlined_to && node->symbol.externally_visible) + { + error ("externally visible inline clone"); + error_found = true; + } + if (node->global.inlined_to && node->symbol.address_taken) + { + error ("inline clone with address taken"); + error_found = true; + } + if (node->global.inlined_to && node->symbol.force_output) + { + error ("inline clone is forced to output"); + error_found = true; + } + for (e = node->indirect_calls; e; e = e->next_callee) + { + if (e->aux) + { + error ("aux field set for indirect edge from %s", + identifier_to_locale (cgraph_node_name (e->caller))); + error_found = true; + } + if (!e->indirect_unknown_callee + || !e->indirect_info) + { + error ("An indirect edge from %s is not marked as indirect or has " + "associated indirect_info, the corresponding statement is: ", + identifier_to_locale (cgraph_node_name (e->caller))); + cgraph_debug_gimple_stmt (this_cfun, e->call_stmt); + error_found = true; + } + } + for (e = node->callers; e; e = e->next_caller) + { + if (verify_edge_count_and_frequency (e)) + error_found = true; + if (!e->inline_failed) + { + if (node->global.inlined_to + != (e->caller->global.inlined_to + ? e->caller->global.inlined_to : e->caller)) + { + error ("inlined_to pointer is wrong"); + error_found = true; + } + if (node->callers->next_caller) + { + error ("multiple inline callers"); + error_found = true; + } + } + else + if (node->global.inlined_to) + { + error ("inlined_to pointer set for noninline callers"); + error_found = true; + } + } + for (e = node->indirect_calls; e; e = e->next_callee) + if (verify_edge_count_and_frequency (e)) + error_found = true; + if (!node->callers && node->global.inlined_to) + { + error ("inlined_to pointer is set but no predecessors found"); + error_found = true; + } + if (node->global.inlined_to == node) + { + error ("inlined_to pointer refers to itself"); + error_found = true; + } + + if (node->clone_of) + { + struct cgraph_node *n; + for (n = node->clone_of->clones; n; n = n->next_sibling_clone) + if (n == node) + break; + if (!n) + { + error ("node has wrong clone_of"); + error_found = true; + } + } + if (node->clones) + { + struct cgraph_node *n; + for (n = node->clones; n; n = n->next_sibling_clone) + if (n->clone_of != node) + break; + if (n) + { + error ("node has wrong clone list"); + error_found = true; + } + } + if ((node->prev_sibling_clone || node->next_sibling_clone) && !node->clone_of) + { + error ("node is in clone list but it is not clone"); + error_found = true; + } + if (!node->prev_sibling_clone && node->clone_of && node->clone_of->clones != node) + { + error ("node has wrong prev_clone pointer"); + error_found = true; + } + if (node->prev_sibling_clone && node->prev_sibling_clone->next_sibling_clone != node) + { + error ("double linked list of clones corrupted"); + error_found = true; + } + + if (node->analyzed && node->alias) + { + bool ref_found = false; + int i; + struct ipa_ref *ref; + + if (node->callees) + { + error ("Alias has call edges"); + error_found = true; + } + for (i = 0; ipa_ref_list_reference_iterate (&node->symbol.ref_list, + i, ref); i++) + if (ref->use != IPA_REF_ALIAS) + { + error ("Alias has non-alias reference"); + error_found = true; + } + else if (ref_found) + { + error ("Alias has more than one alias reference"); + error_found = true; + } + else + ref_found = true; + if (!ref_found) + { + error ("Analyzed alias has no reference"); + error_found = true; + } + } + if (node->analyzed && node->thunk.thunk_p) + { + if (!node->callees) + { + error ("No edge out of thunk node"); + error_found = true; + } + else if (node->callees->next_callee) + { + error ("More than one edge out of thunk node"); + error_found = true; + } + if (gimple_has_body_p (node->symbol.decl)) + { + error ("Thunk is not supposed to have body"); + error_found = true; + } + } + else if (node->analyzed && gimple_has_body_p (node->symbol.decl) + && !TREE_ASM_WRITTEN (node->symbol.decl) + && (!DECL_EXTERNAL (node->symbol.decl) || node->global.inlined_to) + && !flag_wpa) + { + if (this_cfun->cfg) + { + /* The nodes we're interested in are never shared, so walk + the tree ignoring duplicates. */ + struct pointer_set_t *visited_nodes = pointer_set_create (); + /* Reach the trees by walking over the CFG, and note the + enclosing basic-blocks in the call edges. */ + FOR_EACH_BB_FN (this_block, this_cfun) + for (gsi = gsi_start_bb (this_block); + !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + if (is_gimple_call (stmt)) + { + struct cgraph_edge *e = cgraph_edge (node, stmt); + tree decl = gimple_call_fndecl (stmt); + if (e) + { + if (e->aux) + { + error ("shared call_stmt:"); + cgraph_debug_gimple_stmt (this_cfun, stmt); + error_found = true; + } + if (!e->indirect_unknown_callee) + { + if (verify_edge_corresponds_to_fndecl (e, decl)) + { + error ("edge points to wrong declaration:"); + debug_tree (e->callee->symbol.decl); + fprintf (stderr," Instead of:"); + debug_tree (decl); + error_found = true; + } + } + else if (decl) + { + error ("an indirect edge with unknown callee " + "corresponding to a call_stmt with " + "a known declaration:"); + error_found = true; + cgraph_debug_gimple_stmt (this_cfun, e->call_stmt); + } + e->aux = (void *)1; + } + else if (decl) + { + error ("missing callgraph edge for call stmt:"); + cgraph_debug_gimple_stmt (this_cfun, stmt); + error_found = true; + } + } + } + pointer_set_destroy (visited_nodes); + } + else + /* No CFG available?! */ + gcc_unreachable (); + + for (e = node->callees; e; e = e->next_callee) + { + if (!e->aux) + { + error ("edge %s->%s has no corresponding call_stmt", + identifier_to_locale (cgraph_node_name (e->caller)), + identifier_to_locale (cgraph_node_name (e->callee))); + cgraph_debug_gimple_stmt (this_cfun, e->call_stmt); + error_found = true; + } + e->aux = 0; + } + for (e = node->indirect_calls; e; e = e->next_callee) + { + if (!e->aux) + { + error ("an indirect edge from %s has no corresponding call_stmt", + identifier_to_locale (cgraph_node_name (e->caller))); + cgraph_debug_gimple_stmt (this_cfun, e->call_stmt); + error_found = true; + } + e->aux = 0; + } + } + if (error_found) + { + dump_cgraph_node (stderr, node); + internal_error ("verify_cgraph_node failed"); + } + timevar_pop (TV_CGRAPH_VERIFY); +} + +/* Verify whole cgraph structure. */ +DEBUG_FUNCTION void +verify_cgraph (void) +{ + struct cgraph_node *node; + + if (seen_error ()) + return; + + FOR_EACH_FUNCTION (node) + verify_cgraph_node (node); +} #include "gt-cgraph.h" diff --git a/gcc/cgraph.h b/gcc/cgraph.h index df74d51af8f..652556f9df1 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -574,37 +574,9 @@ bool cgraph_for_node_and_aliases (struct cgraph_node *, bool (*) (struct cgraph_node *, void *), void *, bool); VEC (cgraph_edge_p, heap) * collect_callers_of_node (struct cgraph_node *node); - - -/* In cgraphunit.c */ -extern FILE *cgraph_dump_file; -void cgraph_finalize_function (tree, bool); -void cgraph_analyze_function (struct cgraph_node *); -void cgraph_finalize_compilation_unit (void); -void cgraph_optimize (void); -void cgraph_mark_force_output_node (struct cgraph_node *); -void cgraph_mark_address_taken_node (struct cgraph_node *); -bool cgraph_inline_p (struct cgraph_edge *, cgraph_inline_failed_t *reason); -bool cgraph_preserve_function_body_p (struct cgraph_node *); void verify_cgraph (void); void verify_cgraph_node (struct cgraph_node *); -void cgraph_build_static_cdtor (char which, tree body, int priority); -void cgraph_reset_static_var_maps (void); -void init_cgraph (void); -struct cgraph_node * cgraph_copy_node_for_versioning (struct cgraph_node *, - tree, VEC(cgraph_edge_p,heap)*, bitmap); -struct cgraph_node *cgraph_function_versioning (struct cgraph_node *, - VEC(cgraph_edge_p,heap)*, - VEC(ipa_replace_map_p,gc)*, - bitmap, bool, bitmap, - basic_block, const char *); -void tree_function_versioning (tree, tree, VEC (ipa_replace_map_p,gc)*, - bool, bitmap, bool, bitmap, basic_block); -void record_references_in_initializer (tree, bool); -bool cgraph_process_new_functions (void); -void cgraph_process_same_body_aliases (void); - -bool cgraph_decide_is_function_needed (struct cgraph_node *, tree); +void cgraph_mark_address_taken_node (struct cgraph_node *); typedef void (*cgraph_edge_hook)(struct cgraph_edge *, void *); typedef void (*cgraph_node_hook)(struct cgraph_node *, void *); @@ -631,10 +603,31 @@ struct cgraph_2node_hook_list *cgraph_add_node_duplication_hook (cgraph_2node_ho void cgraph_remove_node_duplication_hook (struct cgraph_2node_hook_list *); gimple cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *); bool cgraph_propagate_frequency (struct cgraph_node *node); + +/* In cgraphunit.c */ +extern FILE *cgraph_dump_file; +void cgraph_finalize_function (tree, bool); +void cgraph_finalize_compilation_unit (void); +void cgraph_optimize (void); +void init_cgraph (void); +struct cgraph_node * cgraph_copy_node_for_versioning (struct cgraph_node *, + tree, VEC(cgraph_edge_p,heap)*, bitmap); +struct cgraph_node *cgraph_function_versioning (struct cgraph_node *, + VEC(cgraph_edge_p,heap)*, + VEC(ipa_replace_map_p,gc)*, + bitmap, bool, bitmap, + basic_block, const char *); +void tree_function_versioning (tree, tree, VEC (ipa_replace_map_p,gc)*, + bool, bitmap, bool, bitmap, basic_block); +bool cgraph_process_new_functions (void); +void cgraph_process_same_body_aliases (void); + + /* In cgraphbuild.c */ unsigned int rebuild_cgraph_edges (void); void cgraph_rebuild_references (void); int compute_call_stmt_bb_frequency (tree, basic_block bb); +void record_references_in_initializer (tree, bool); /* In ipa.c */ bool cgraph_remove_unreachable_nodes (bool, FILE *); @@ -646,6 +639,7 @@ void cgraph_node_set_remove (cgraph_node_set, struct cgraph_node *); void dump_cgraph_node_set (FILE *, cgraph_node_set); void debug_cgraph_node_set (cgraph_node_set); void free_cgraph_node_set (cgraph_node_set); +void cgraph_build_static_cdtor (char which, tree body, int priority); varpool_node_set varpool_node_set_new (void); varpool_node_set_iterator varpool_node_set_find (varpool_node_set, @@ -1284,4 +1278,14 @@ decl_is_tm_clone (const_tree fndecl) return n->tm_clone; return false; } + +/* Likewise indicate that a node is needed, i.e. reachable via some + external means. */ + +static inline void +cgraph_mark_force_output_node (struct cgraph_node *node) +{ + node->symbol.force_output = 1; + gcc_checking_assert (!node->global.inlined_to); +} #endif /* GCC_CGRAPH_H */ diff --git a/gcc/cgraphbuild.c b/gcc/cgraphbuild.c index 445a392110d..a847980c663 100644 --- a/gcc/cgraphbuild.c +++ b/gcc/cgraphbuild.c @@ -85,8 +85,6 @@ record_reference (tree *tp, int *walk_subtrees, void *data) if (TREE_CODE (decl) == VAR_DECL) { struct varpool_node *vnode = varpool_node (decl); - if (lang_hooks.callgraph.analyze_expr) - lang_hooks.callgraph.analyze_expr (&decl, walk_subtrees); ipa_record_reference ((symtab_node)ctx->varpool_node, (symtab_node)vnode, IPA_REF_ADDR, NULL); @@ -102,9 +100,6 @@ record_reference (tree *tp, int *walk_subtrees, void *data) *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; } @@ -239,10 +234,7 @@ mark_address (gimple stmt, tree addr, void *data) && (TREE_STATIC (addr) || DECL_EXTERNAL (addr))) { struct varpool_node *vnode = varpool_node (addr); - int walk_subtrees; - if (lang_hooks.callgraph.analyze_expr) - lang_hooks.callgraph.analyze_expr (&addr, &walk_subtrees); ipa_record_reference ((symtab_node)data, (symtab_node)vnode, IPA_REF_ADDR, stmt); @@ -271,10 +263,7 @@ mark_load (gimple stmt, tree t, void *data) && (TREE_STATIC (t) || DECL_EXTERNAL (t))) { struct varpool_node *vnode = varpool_node (t); - int walk_subtrees; - if (lang_hooks.callgraph.analyze_expr) - lang_hooks.callgraph.analyze_expr (&t, &walk_subtrees); ipa_record_reference ((symtab_node)data, (symtab_node)vnode, IPA_REF_LOAD, stmt); @@ -292,10 +281,7 @@ mark_store (gimple stmt, tree t, void *data) && (TREE_STATIC (t) || DECL_EXTERNAL (t))) { struct varpool_node *vnode = varpool_node (t); - int walk_subtrees; - if (lang_hooks.callgraph.analyze_expr) - lang_hooks.callgraph.analyze_expr (&t, &walk_subtrees); ipa_record_reference ((symtab_node)data, (symtab_node)vnode, IPA_REF_STORE, stmt); diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index a58cd08c0d9..60ccc82591d 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -1,4 +1,4 @@ -/* Callgraph based interprocedural optimizations. +/* Driver of optimization process Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. Contributed by Jan Hubicka @@ -19,11 +19,10 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ -/* This module implements main driver of compilation process as well as - few basic interprocedural optimizers. +/* This module implements main driver of compilation process. The main scope of this file is to act as an interface in between - tree based frontends and the backend (and middle end) + tree based frontends and the backend. The front-end is supposed to use following functionality: @@ -45,66 +44,115 @@ along with GCC; see the file COPYING3. If not see This function is called once (source level) compilation unit is finalized and it will no longer change. - In the call-graph construction and local function analysis takes - place here. Bodies of unreachable functions are released to - conserve memory usage. + The symbol table is constructed starting from the trivially needed + symbols finalized by the frontend. Functions are lowered into + GIMPLE representation and callgraph/reference lists are constructed. + Those are used to discover other neccesary functions and variables. + + At the end the bodies of unreachable functions are removed. The function can be called multiple times when multiple source level - compilation units are combined (such as in C frontend) + compilation units are combined. - cgraph_optimize - In this unit-at-a-time compilation the intra procedural analysis takes - place here. In particular the static functions whose address is never - taken are marked as local. Backend can then use this information to - modify calling conventions, do better inlining or similar optimizations. + This passes control to the back-end. Optimizations are performed and + final assembler is generated. This is done in the following way. Note + that with link time optimization the process is split into three + stages (compile time, linktime analysis and parallel linktime as + indicated bellow). + + Compile time: + + 1) Inter-procedural optimization. + (ipa_passes) + + This part is further split into: + + a) early optimizations. These are local passes executed in + the topological order on the callgraph. + + The purpose of early optimiations is to optimize away simple + things that may otherwise confuse IP analysis. Very simple + propagation across the callgraph is done i.e. to discover + functions without side effects and simple inlining is performed. + + b) early small interprocedural passes. + + Those are interprocedural passes executed only at compilation + time. These include, for exmaple, transational memory lowering, + unreachable code removal and other simple transformations. + + c) IP analysis stage. All interprocedural passes do their + analysis. + + Interprocedural passes differ from small interprocedural + passes by their ability to operate across whole program + at linktime. Their analysis stage is performed early to + both reduce linking times and linktime memory usage by + not having to represent whole program in memory. + + d) LTO sreaming. When doing LTO, everything important gets + streamed into the object file. + + Compile time and or linktime analysis stage (WPA): + + At linktime units gets streamed back and symbol table is + merged. Function bodies are not streamed in and not + available. + e) IP propagation stage. All IP passes execute their + IP propagation. This is done based on the earlier analysis + without having function bodies at hand. + f) Ltrans streaming. When doing WHOPR LTO, the program + is partitioned and streamed into multple object files. + + Compile time and/or parallel linktime stage (ltrans) - - cgraph_mark_needed_node - - varpool_mark_needed_node + Each of the object files is streamed back and compiled + separately. Now the function bodies becomes available + again. - When function or variable is referenced by some hidden way the call-graph - data structure must be updated accordingly by this function. - There should be little need to call this function and all the references - should be made explicit to cgraph code. At present these functions are - used by C++ frontend to explicitly mark the keyed methods. + 2) Virtual clone materialization + (cgraph_materialize_clone) - - analyze_expr callback + IP passes can produce copies of existing functoins (such + as versioned clones or inline clones) without actually + manipulating their bodies by creating virtual clones in + the callgraph. At this time the virtual clones are + turned into real functions + 3) IP transformation - This function is responsible for lowering tree nodes not understood by - generic code into understandable ones or alternatively marking - callgraph and varpool nodes referenced by the as needed. + All IP passes transform function bodies based on earlier + decision of the IP propagation. - ??? On the tree-ssa genericizing should take place here and we will avoid - need for these hooks (replacing them by genericizing hook) + 4) late small IP passes - Analyzing of all functions is deferred - to cgraph_finalize_compilation_unit and expansion into cgraph_optimize. + Simple IP passes working within single program partition. - In cgraph_finalize_compilation_unit the reachable functions are - analyzed. During analysis the call-graph edges from reachable - functions are constructed and their destinations are marked as - reachable. References to functions and variables are discovered too - and variables found to be needed output to the assembly file. Via - mark_referenced call in assemble_variable functions referenced by - static variables are noticed too. + 5) Expansion + (cgraph_expand_all_functions) - The intra-procedural information is produced and its existence - indicated by global_info_ready. Once this flag is set it is impossible - to change function from !reachable to reachable and thus - assemble_variable no longer call mark_referenced. + At this stage functions that needs to be output into + assembler are identified and compiled in topological order + 6) Output of variables and aliases + Now it is known what variable references was not optimized + out and thus all variables are output to the file. - Finally the call-graph is topologically sorted and all reachable functions - that has not been completely inlined or are not external are output. + Note that with -fno-toplevel-reorder passes 5 and 6 + are combined together in cgraph_output_in_order. - ??? It is possible that reference to function or variable is optimized - out. We can not deal with this nicely because topological order is not - suitable for it. For tree-ssa we may consider another pass doing - optimization and re-discovering reachable functions. + Finally there are functions to manipulate the callgraph from + backend. + - cgraph_add_new_function is used to add backend produced + functions introduced after the unit is finalized. + The functions are enqueue for later processing and inserted + into callgraph with cgraph_process_new_functions. - ??? Reorganize code so variables are output very last and only if they - really has been referenced by produced code, so we catch more cases - where reference has been optimized out. */ + - cgraph_function_versioning + produces a copy of function into new one (a version) + and apply simple transformations +*/ #include "config.h" #include "system.h" @@ -124,8 +172,6 @@ along with GCC; see the file COPYING3. If not see #include "target.h" #include "cgraph.h" #include "diagnostic.h" -#include "tree-pretty-print.h" -#include "gimple-pretty-print.h" #include "timevar.h" #include "params.h" #include "fibheap.h" @@ -136,6 +182,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-iterator.h" #include "tree-pass.h" #include "tree-dump.h" +#include "gimple-pretty-print.h" #include "output.h" #include "coverage.h" #include "plugin.h" @@ -154,7 +201,7 @@ static void cgraph_expand_all_functions (void); static void cgraph_mark_functions_to_output (void); static void cgraph_expand_function (struct cgraph_node *); static void cgraph_output_pending_asms (void); -static void tree_rest_of_compilation (struct cgraph_node *); +static void cgraph_analyze_function (struct cgraph_node *); FILE *cgraph_dump_file; @@ -166,7 +213,7 @@ static GTY (()) tree vtable_entry_type; and differs from later logic removing unnecesary functions that can take into account results of analysis, whole program info etc. */ -bool +static bool cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl) { /* If the user told us it is used, then it must be so. */ @@ -470,8 +517,8 @@ cgraph_add_new_function (tree fndecl, bool lowered) if (!gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl))) execute_pass_list (pass_early_local_passes.pass.sub); bitmap_obstack_release (NULL); - tree_rest_of_compilation (node); pop_cfun (); + cgraph_expand_function (node); current_function_decl = NULL; break; @@ -486,404 +533,6 @@ cgraph_add_new_function (tree fndecl, bool lowered) DECL_FUNCTION_PERSONALITY (fndecl) = lang_hooks.eh_personality (); } -/* Return TRUE if NODE2 is equivalent to NODE or its clone. */ -static bool -clone_of_p (struct cgraph_node *node, struct cgraph_node *node2) -{ - node = cgraph_function_or_thunk_node (node, NULL); - node2 = cgraph_function_or_thunk_node (node2, NULL); - while (node != node2 && node2) - node2 = node2->clone_of; - return node2 != NULL; -} - -/* Verify edge E count and frequency. */ - -static bool -verify_edge_count_and_frequency (struct cgraph_edge *e) -{ - bool error_found = false; - if (e->count < 0) - { - error ("caller edge count is negative"); - error_found = true; - } - if (e->frequency < 0) - { - error ("caller edge frequency is negative"); - error_found = true; - } - if (e->frequency > CGRAPH_FREQ_MAX) - { - error ("caller edge frequency is too large"); - error_found = true; - } - if (gimple_has_body_p (e->caller->symbol.decl) - && !e->caller->global.inlined_to - /* FIXME: Inline-analysis sets frequency to 0 when edge is optimized out. - Remove this once edges are actualy removed from the function at that time. */ - && (e->frequency - || (inline_edge_summary_vec - && ((VEC_length(inline_edge_summary_t, inline_edge_summary_vec) - <= (unsigned) e->uid) - || !inline_edge_summary (e)->predicate))) - && (e->frequency - != compute_call_stmt_bb_frequency (e->caller->symbol.decl, - gimple_bb (e->call_stmt)))) - { - error ("caller edge frequency %i does not match BB frequency %i", - e->frequency, - compute_call_stmt_bb_frequency (e->caller->symbol.decl, - gimple_bb (e->call_stmt))); - error_found = true; - } - return error_found; -} - -/* Switch to THIS_CFUN if needed and print STMT to stderr. */ -static void -cgraph_debug_gimple_stmt (struct function *this_cfun, gimple stmt) -{ - /* debug_gimple_stmt needs correct cfun */ - if (cfun != this_cfun) - set_cfun (this_cfun); - debug_gimple_stmt (stmt); -} - -/* Verify that call graph edge E corresponds to DECL from the associated - statement. Return true if the verification should fail. */ - -static bool -verify_edge_corresponds_to_fndecl (struct cgraph_edge *e, tree decl) -{ - struct cgraph_node *node; - - if (!decl || e->callee->global.inlined_to) - return false; - node = cgraph_get_node (decl); - - /* We do not know if a node from a different partition is an alias or what it - aliases and therefore cannot do the former_clone_of check reliably. */ - if (!node || node->symbol.in_other_partition) - return false; - node = cgraph_function_or_thunk_node (node, NULL); - - if ((e->callee->former_clone_of != node->symbol.decl - && (!node->same_body_alias - || e->callee->former_clone_of != node->thunk.alias)) - /* IPA-CP sometimes redirect edge to clone and then back to the former - function. This ping-pong has to go, eventually. */ - && (node != cgraph_function_or_thunk_node (e->callee, NULL)) - && !clone_of_p (node, e->callee) - /* If decl is a same body alias of some other decl, allow e->callee to be - a clone of a clone of that other decl too. */ - && (!node->same_body_alias - || !clone_of_p (cgraph_get_node (node->thunk.alias), e->callee))) - return true; - else - return false; -} - -/* Verify cgraph nodes of given cgraph node. */ -DEBUG_FUNCTION void -verify_cgraph_node (struct cgraph_node *node) -{ - struct cgraph_edge *e; - struct function *this_cfun = DECL_STRUCT_FUNCTION (node->symbol.decl); - basic_block this_block; - gimple_stmt_iterator gsi; - bool error_found = false; - - if (seen_error ()) - return; - - timevar_push (TV_CGRAPH_VERIFY); - error_found |= verify_symtab_base ((symtab_node) node); - for (e = node->callees; e; e = e->next_callee) - if (e->aux) - { - error ("aux field set for edge %s->%s", - identifier_to_locale (cgraph_node_name (e->caller)), - identifier_to_locale (cgraph_node_name (e->callee))); - error_found = true; - } - if (node->count < 0) - { - error ("execution count is negative"); - error_found = true; - } - if (node->global.inlined_to && node->symbol.externally_visible) - { - error ("externally visible inline clone"); - error_found = true; - } - if (node->global.inlined_to && node->symbol.address_taken) - { - error ("inline clone with address taken"); - error_found = true; - } - if (node->global.inlined_to && node->symbol.force_output) - { - error ("inline clone is forced to output"); - error_found = true; - } - for (e = node->indirect_calls; e; e = e->next_callee) - { - if (e->aux) - { - error ("aux field set for indirect edge from %s", - identifier_to_locale (cgraph_node_name (e->caller))); - error_found = true; - } - if (!e->indirect_unknown_callee - || !e->indirect_info) - { - error ("An indirect edge from %s is not marked as indirect or has " - "associated indirect_info, the corresponding statement is: ", - identifier_to_locale (cgraph_node_name (e->caller))); - cgraph_debug_gimple_stmt (this_cfun, e->call_stmt); - error_found = true; - } - } - for (e = node->callers; e; e = e->next_caller) - { - if (verify_edge_count_and_frequency (e)) - error_found = true; - if (!e->inline_failed) - { - if (node->global.inlined_to - != (e->caller->global.inlined_to - ? e->caller->global.inlined_to : e->caller)) - { - error ("inlined_to pointer is wrong"); - error_found = true; - } - if (node->callers->next_caller) - { - error ("multiple inline callers"); - error_found = true; - } - } - else - if (node->global.inlined_to) - { - error ("inlined_to pointer set for noninline callers"); - error_found = true; - } - } - for (e = node->indirect_calls; e; e = e->next_callee) - if (verify_edge_count_and_frequency (e)) - error_found = true; - if (!node->callers && node->global.inlined_to) - { - error ("inlined_to pointer is set but no predecessors found"); - error_found = true; - } - if (node->global.inlined_to == node) - { - error ("inlined_to pointer refers to itself"); - error_found = true; - } - - if (node->clone_of) - { - struct cgraph_node *n; - for (n = node->clone_of->clones; n; n = n->next_sibling_clone) - if (n == node) - break; - if (!n) - { - error ("node has wrong clone_of"); - error_found = true; - } - } - if (node->clones) - { - struct cgraph_node *n; - for (n = node->clones; n; n = n->next_sibling_clone) - if (n->clone_of != node) - break; - if (n) - { - error ("node has wrong clone list"); - error_found = true; - } - } - if ((node->prev_sibling_clone || node->next_sibling_clone) && !node->clone_of) - { - error ("node is in clone list but it is not clone"); - error_found = true; - } - if (!node->prev_sibling_clone && node->clone_of && node->clone_of->clones != node) - { - error ("node has wrong prev_clone pointer"); - error_found = true; - } - if (node->prev_sibling_clone && node->prev_sibling_clone->next_sibling_clone != node) - { - error ("double linked list of clones corrupted"); - error_found = true; - } - - if (node->analyzed && node->alias) - { - bool ref_found = false; - int i; - struct ipa_ref *ref; - - if (node->callees) - { - error ("Alias has call edges"); - error_found = true; - } - for (i = 0; ipa_ref_list_reference_iterate (&node->symbol.ref_list, - i, ref); i++) - if (ref->use != IPA_REF_ALIAS) - { - error ("Alias has non-alias reference"); - error_found = true; - } - else if (ref_found) - { - error ("Alias has more than one alias reference"); - error_found = true; - } - else - ref_found = true; - if (!ref_found) - { - error ("Analyzed alias has no reference"); - error_found = true; - } - } - if (node->analyzed && node->thunk.thunk_p) - { - if (!node->callees) - { - error ("No edge out of thunk node"); - error_found = true; - } - else if (node->callees->next_callee) - { - error ("More than one edge out of thunk node"); - error_found = true; - } - if (gimple_has_body_p (node->symbol.decl)) - { - error ("Thunk is not supposed to have body"); - error_found = true; - } - } - else if (node->analyzed && gimple_has_body_p (node->symbol.decl) - && !TREE_ASM_WRITTEN (node->symbol.decl) - && (!DECL_EXTERNAL (node->symbol.decl) || node->global.inlined_to) - && !flag_wpa) - { - if (this_cfun->cfg) - { - /* The nodes we're interested in are never shared, so walk - the tree ignoring duplicates. */ - struct pointer_set_t *visited_nodes = pointer_set_create (); - /* Reach the trees by walking over the CFG, and note the - enclosing basic-blocks in the call edges. */ - FOR_EACH_BB_FN (this_block, this_cfun) - for (gsi = gsi_start_bb (this_block); - !gsi_end_p (gsi); - gsi_next (&gsi)) - { - gimple stmt = gsi_stmt (gsi); - if (is_gimple_call (stmt)) - { - struct cgraph_edge *e = cgraph_edge (node, stmt); - tree decl = gimple_call_fndecl (stmt); - if (e) - { - if (e->aux) - { - error ("shared call_stmt:"); - cgraph_debug_gimple_stmt (this_cfun, stmt); - error_found = true; - } - if (!e->indirect_unknown_callee) - { - if (verify_edge_corresponds_to_fndecl (e, decl)) - { - error ("edge points to wrong declaration:"); - debug_tree (e->callee->symbol.decl); - fprintf (stderr," Instead of:"); - debug_tree (decl); - error_found = true; - } - } - else if (decl) - { - error ("an indirect edge with unknown callee " - "corresponding to a call_stmt with " - "a known declaration:"); - error_found = true; - cgraph_debug_gimple_stmt (this_cfun, e->call_stmt); - } - e->aux = (void *)1; - } - else if (decl) - { - error ("missing callgraph edge for call stmt:"); - cgraph_debug_gimple_stmt (this_cfun, stmt); - error_found = true; - } - } - } - pointer_set_destroy (visited_nodes); - } - else - /* No CFG available?! */ - gcc_unreachable (); - - for (e = node->callees; e; e = e->next_callee) - { - if (!e->aux) - { - error ("edge %s->%s has no corresponding call_stmt", - identifier_to_locale (cgraph_node_name (e->caller)), - identifier_to_locale (cgraph_node_name (e->callee))); - cgraph_debug_gimple_stmt (this_cfun, e->call_stmt); - error_found = true; - } - e->aux = 0; - } - for (e = node->indirect_calls; e; e = e->next_callee) - { - if (!e->aux) - { - error ("an indirect edge from %s has no corresponding call_stmt", - identifier_to_locale (cgraph_node_name (e->caller))); - cgraph_debug_gimple_stmt (this_cfun, e->call_stmt); - error_found = true; - } - e->aux = 0; - } - } - if (error_found) - { - dump_cgraph_node (stderr, node); - internal_error ("verify_cgraph_node failed"); - } - timevar_pop (TV_CGRAPH_VERIFY); -} - -/* Verify whole cgraph structure. */ -DEBUG_FUNCTION void -verify_cgraph (void) -{ - struct cgraph_node *node; - - if (seen_error ()) - return; - - FOR_EACH_FUNCTION (node) - verify_cgraph_node (node); -} - /* Output all asm statements we have stored up to be output. */ static void @@ -900,11 +549,13 @@ cgraph_output_pending_asms (void) } /* Analyze the function scheduled to be output. */ -void +static void cgraph_analyze_function (struct cgraph_node *node) { tree save = current_function_decl; tree decl = node->symbol.decl; + location_t saved_loc = input_location; + input_location = DECL_SOURCE_LOCATION (decl); if (node->alias && node->thunk.alias) { @@ -917,6 +568,7 @@ cgraph_analyze_function (struct cgraph_node *node) { error ("function %q+D part of alias cycle", node->symbol.decl); node->alias = false; + input_location = saved_loc; return; } if (!VEC_length (ipa_ref_t, node->symbol.ref_list.references)) @@ -1002,6 +654,7 @@ cgraph_analyze_function (struct cgraph_node *node) node->analyzed = true; current_function_decl = save; + input_location = saved_loc; } /* C++ frontend produce same body aliases all over the place, even before PCH @@ -1888,15 +1541,23 @@ assemble_thunks_and_aliases (struct cgraph_node *node) } } -/* Perform IPA transforms and all further optimizations and compilation - for FNDECL. */ +/* Expand function specified by NODE. */ static void -tree_rest_of_compilation (struct cgraph_node *node) +cgraph_expand_function (struct cgraph_node *node) { - tree fndecl = node->symbol.decl; + tree decl = node->symbol.decl; location_t saved_loc; + /* We ought to not compile any inline clones. */ + gcc_assert (!node->global.inlined_to); + + announce_function (decl); + node->process = 0; + gcc_assert (node->lowered); + + /* Generate RTL for the body of DECL. */ + timevar_push (TV_REST_OF_COMPILATION); gcc_assert (cgraph_global_info_ready); @@ -1905,10 +1566,10 @@ tree_rest_of_compilation (struct cgraph_node *node) bitmap_obstack_initialize (NULL); /* Initialize the RTL code for the function. */ - current_function_decl = fndecl; + current_function_decl = decl; saved_loc = input_location; - input_location = DECL_SOURCE_LOCATION (fndecl); - init_function_start (fndecl); + input_location = DECL_SOURCE_LOCATION (decl); + init_function_start (decl); gimple_register_cfg_hooks (); @@ -1936,9 +1597,9 @@ tree_rest_of_compilation (struct cgraph_node *node) /* If requested, warn about function definitions where the function will return a value (usually of some struct or union type) which itself will take up a lot of stack space. */ - if (warn_larger_than && !DECL_EXTERNAL (fndecl) && TREE_TYPE (fndecl)) + if (warn_larger_than && !DECL_EXTERNAL (decl) && TREE_TYPE (decl)) { - tree ret_type = TREE_TYPE (TREE_TYPE (fndecl)); + tree ret_type = TREE_TYPE (TREE_TYPE (decl)); if (ret_type && TYPE_SIZE_UNIT (ret_type) && TREE_CODE (TYPE_SIZE_UNIT (ret_type)) == INTEGER_CST @@ -1950,53 +1611,34 @@ tree_rest_of_compilation (struct cgraph_node *node) if (compare_tree_int (TYPE_SIZE_UNIT (ret_type), size_as_int) == 0) warning (OPT_Wlarger_than_, "size of return value of %q+D is %u bytes", - fndecl, size_as_int); + decl, size_as_int); else warning (OPT_Wlarger_than_, "size of return value of %q+D is larger than %wd bytes", - fndecl, larger_than_size); + decl, larger_than_size); } } - gimple_set_body (fndecl, NULL); - if (DECL_STRUCT_FUNCTION (fndecl) == 0 - && !cgraph_get_node (fndecl)->origin) + gimple_set_body (decl, NULL); + if (DECL_STRUCT_FUNCTION (decl) == 0 + && !cgraph_get_node (decl)->origin) { /* Stop pointing to the local nodes about to be freed. But DECL_INITIAL must remain nonzero so we know this was an actual function definition. For a nested function, this is done in c_pop_function_context. If rest_of_compilation set this to 0, leave it 0. */ - if (DECL_INITIAL (fndecl) != 0) - DECL_INITIAL (fndecl) = error_mark_node; + if (DECL_INITIAL (decl) != 0) + DECL_INITIAL (decl) = error_mark_node; } input_location = saved_loc; ggc_collect (); timevar_pop (TV_REST_OF_COMPILATION); -} - -/* Expand function specified by NODE. */ - -static void -cgraph_expand_function (struct cgraph_node *node) -{ - tree decl = node->symbol.decl; - - /* We ought to not compile any inline clones. */ - gcc_assert (!node->global.inlined_to); - - announce_function (decl); - node->process = 0; - gcc_assert (node->lowered); - - /* Generate RTL for the body of DECL. */ - tree_rest_of_compilation (node); /* Make sure that BE didn't give up on compiling. */ gcc_assert (TREE_ASM_WRITTEN (decl)); current_function_decl = NULL; - gcc_assert (!cgraph_preserve_function_body_p (node)); /* It would make a lot more sense to output thunks before function body to get more forward and lest backwarding jumps. This is however would need solving problem @@ -2011,16 +1653,6 @@ cgraph_expand_function (struct cgraph_node *node) cgraph_node_remove_callees (node); } -/* Return true when CALLER_DECL should be inlined into CALLEE_DECL. */ - -bool -cgraph_inline_p (struct cgraph_edge *e, cgraph_inline_failed_t *reason) -{ - *reason = e->inline_failed; - return !e->inline_failed; -} - - /* Expand all functions that must be output. @@ -2166,20 +1798,6 @@ cgraph_output_in_order (void) free (nodes); } -/* Return true when function body of DECL still needs to be kept around - for later re-use. */ -bool -cgraph_preserve_function_body_p (struct cgraph_node *node) -{ - gcc_assert (cgraph_global_info_ready); - gcc_assert (!node->alias && !node->thunk.thunk_p); - - /* Look if there is any clone around. */ - if (node->clones) - return true; - return false; -} - static void ipa_passes (void) { @@ -2277,7 +1895,7 @@ output_weakrefs (void) : get_alias_symbol (vnode->symbol.decl)); } - +/* Initialize callgraph dump file. */ void init_cgraph (void) diff --git a/gcc/ipa-inline-transform.c b/gcc/ipa-inline-transform.c index c6cbd015c33..e07468ae84f 100644 --- a/gcc/ipa-inline-transform.c +++ b/gcc/ipa-inline-transform.c @@ -353,6 +353,19 @@ save_inline_function_body (struct cgraph_node *node) return first_clone; } +/* Return true when function body of DECL still needs to be kept around + for later re-use. */ +bool +preserve_function_body_p (struct cgraph_node *node) +{ + gcc_assert (cgraph_global_info_ready); + gcc_assert (!node->alias && !node->thunk.thunk_p); + + /* Look if there is any clone around. */ + if (node->clones) + return true; + return false; +} /* Apply inline plan to function. */ @@ -369,7 +382,7 @@ inline_transform (struct cgraph_node *node) /* We might need the body of this function so that we can expand it inline somewhere else. */ - if (cgraph_preserve_function_body_p (node)) + if (preserve_function_body_p (node)) save_inline_function_body (node); for (e = node->callees; e; e = e->next_callee) diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h index ed3230c6291..d8f479f3f5d 100644 --- a/gcc/langhooks-def.h +++ b/gcc/langhooks-def.h @@ -69,7 +69,6 @@ extern void lhd_init_options (unsigned int, extern bool lhd_complain_wrong_lang_p (const struct cl_option *); extern bool lhd_handle_option (size_t, const char *, int, int, location_t, const struct cl_option_handlers *); -extern tree lhd_callgraph_analyze_expr (tree *, int *); /* Declarations for tree gimplification hooks. */ @@ -132,12 +131,6 @@ extern void lhd_omp_firstprivatize_type_sizes (struct gimplify_omp_ctx *, LANG_HOOKS_TREE_INLINING_VAR_MOD_TYPE_P, \ } -#define LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR lhd_callgraph_analyze_expr - -#define LANG_HOOKS_CALLGRAPH_INITIALIZER { \ - LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR \ -} - /* Hooks for tree gimplification. */ #define LANG_HOOKS_GIMPLIFY_EXPR lhd_gimplify_expr @@ -292,7 +285,6 @@ extern void lhd_end_section (void); LANG_HOOKS_COMMON_ATTRIBUTE_TABLE, \ LANG_HOOKS_FORMAT_ATTRIBUTE_TABLE, \ LANG_HOOKS_TREE_INLINING_INITIALIZER, \ - LANG_HOOKS_CALLGRAPH_INITIALIZER, \ LANG_HOOKS_TREE_DUMP_INITIALIZER, \ LANG_HOOKS_DECLS, \ LANG_HOOKS_FOR_TYPES_INITIALIZER, \ diff --git a/gcc/langhooks.c b/gcc/langhooks.c index 017e024e248..9d0e25e0689 100644 --- a/gcc/langhooks.c +++ b/gcc/langhooks.c @@ -472,13 +472,6 @@ lhd_print_error_function (diagnostic_context *context, const char *file, } tree -lhd_callgraph_analyze_expr (tree *tp ATTRIBUTE_UNUSED, - int *walk_subtrees ATTRIBUTE_UNUSED) -{ - return NULL; -} - -tree lhd_make_node (enum tree_code code) { return make_node (code); diff --git a/gcc/langhooks.h b/gcc/langhooks.h index 15497225cbd..76b1fe26013 100644 --- a/gcc/langhooks.h +++ b/gcc/langhooks.h @@ -43,13 +43,6 @@ struct lang_hooks_for_tree_inlining bool (*var_mod_type_p) (tree, tree); }; -struct lang_hooks_for_callgraph -{ - /* The node passed is a language-specific tree node. If its contents - are relevant to use of other declarations, mark them. */ - tree (*analyze_expr) (tree *, int *); -}; - /* The following hooks are used by tree-dump.c. */ struct lang_hooks_for_tree_dump @@ -407,8 +400,6 @@ struct lang_hooks struct lang_hooks_for_tree_inlining tree_inlining; - struct lang_hooks_for_callgraph callgraph; - struct lang_hooks_for_tree_dump tree_dump; struct lang_hooks_for_decls decls; diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index d0710c0ddf7..2ba95f509fe 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -3807,8 +3807,9 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) fn = DECL_ABSTRACT_ORIGIN (fn); /* Don't try to inline functions that are not well-suited to inlining. */ - if (!cgraph_inline_p (cg_edge, &reason)) + if (cg_edge->inline_failed) { + reason = cg_edge->inline_failed; /* If this call was originally indirect, we do not want to emit any inlining related warnings or sorry messages because there are no guarantees regarding those. */ diff --git a/gcc/tree.c b/gcc/tree.c index 7837d45e11b..4623d3765e4 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -5244,7 +5244,6 @@ free_lang_data (void) /* Reset some langhooks. Do not reset types_compatible_p, it may still be used indirectly via the get_alias_set langhook. */ - lang_hooks.callgraph.analyze_expr = NULL; lang_hooks.dwarf_name = lhd_dwarf_name; lang_hooks.decl_printable_name = gimple_decl_printable_name; /* We do not want the default decl_assembler_name implementation, diff --git a/gcc/varpool.c b/gcc/varpool.c index 7c8d1fd8c9f..75e546d2bc0 100644 --- a/gcc/varpool.c +++ b/gcc/varpool.c @@ -37,17 +37,6 @@ along with GCC; see the file COPYING3. If not see #include "tree-flow.h" #include "flags.h" -/* This file contains basic routines manipulating variable pool. - - Varpool acts as interface in between the front-end and middle-end - and drives the decision process on what variables and when are - going to be compiled. - - The varpool nodes are allocated lazily for declarations - either by frontend or at callgraph construction time. - All variables supposed to be output into final file needs to be - explicitly marked by frontend via VARPOOL_FINALIZE_DECL function. */ - /* Return varpool node assigned to DECL. Create new one when needed. */ struct varpool_node * varpool_node (tree decl) |