cgraphunit.c: Update toplevel comment.

* 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.

From-SVN: r186832
This commit is contained in:
Jan Hubicka 2012-04-25 18:31:42 +02:00 committed by Jan Hubicka
parent 95e5b9a43a
commit 9c8305f8fc
12 changed files with 613 additions and 652 deletions

View File

@ -1,3 +1,38 @@
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

View File

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

View File

@ -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 */

View File

@ -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);

View File

@ -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).
- cgraph_mark_needed_node
- varpool_mark_needed_node
Compile time:
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.
1) Inter-procedural optimization.
(ipa_passes)
- analyze_expr callback
This part is further split into:
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.
a) early optimizations. These are local passes executed in
the topological order on the callgraph.
??? On the tree-ssa genericizing should take place here and we will avoid
need for these hooks (replacing them by genericizing hook)
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.
Analyzing of all functions is deferred
to cgraph_finalize_compilation_unit and expansion into cgraph_optimize.
b) early small interprocedural passes.
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.
Those are interprocedural passes executed only at compilation
time. These include, for exmaple, transational memory lowering,
unreachable code removal and other simple transformations.
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.
c) IP analysis stage. All interprocedural passes do their
analysis.
Finally the call-graph is topologically sorted and all reachable functions
that has not been completely inlined or are not external are output.
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.
??? 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.
d) LTO sreaming. When doing LTO, everything important gets
streamed into the object file.
??? 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. */
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)
Each of the object files is streamed back and compiled
separately. Now the function bodies becomes available
again.
2) Virtual clone materialization
(cgraph_materialize_clone)
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
All IP passes transform function bodies based on earlier
decision of the IP propagation.
4) late small IP passes
Simple IP passes working within single program partition.
5) Expansion
(cgraph_expand_all_functions)
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.
Note that with -fno-toplevel-reorder passes 5 and 6
are combined together in cgraph_output_in_order.
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.
- 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)

View File

@ -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)

View File

@ -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, \

View File

@ -471,13 +471,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)
{

View File

@ -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;

View File

@ -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. */

View File

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

View File

@ -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)