aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2012-04-25 16:31:42 +0000
committerhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2012-04-25 16:31:42 +0000
commitda5e1e7ce7a1323e3eeeeacd3687823d83cd1025 (patch)
tree59428eb8af21f903b36ff6bf0614f87d9cb27f0d /gcc
parent2e4c90f020156f2a6054853e25bd6e1a2b0d98e6 (diff)
* 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@186832 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog35
-rw-r--r--gcc/cgraph.c460
-rw-r--r--gcc/cgraph.h62
-rw-r--r--gcc/cgraphbuild.c14
-rw-r--r--gcc/cgraphunit.c640
-rw-r--r--gcc/ipa-inline-transform.c15
-rw-r--r--gcc/langhooks-def.h8
-rw-r--r--gcc/langhooks.c7
-rw-r--r--gcc/langhooks.h9
-rw-r--r--gcc/tree-inline.c3
-rw-r--r--gcc/tree.c1
-rw-r--r--gcc/varpool.c11
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)