#include "diagnostic-core.h"
#include "rtl.h"
#include "ipa-utils.h"
+#include "lto-streamer.h"
+
+const char * const ld_plugin_symbol_resolution_names[]=
+{
+ "",
+ "undef",
+ "prevailing_def",
+ "prevailing_def_ironly",
+ "preempted_reg",
+ "preempted_ir",
+ "resolved_ir",
+ "resolved_exec",
+ "resolved_dyn"
+};
static void cgraph_node_remove_callers (struct cgraph_node *node);
static inline void cgraph_edge_remove_caller (struct cgraph_edge *e);
node->previous = NULL;
node->global.estimated_growth = INT_MIN;
node->frequency = NODE_FREQUENCY_NORMAL;
+ node->count_materialization_scale = REG_BR_PROB_BASE;
ipa_empty_ref_list (&node->ref_list);
cgraph_nodes = node;
cgraph_n_nodes++;
return alias_node;
}
-/* Attempt to mark ALIAS as an alias to DECL. Return TRUE if successful.
+/* Attempt to mark ALIAS as an alias to DECL. Return alias node if successful
+ and NULL otherwise.
Same body aliases are output whenever the body of DECL is output,
and cgraph_node (ALIAS) transparently returns cgraph_node (DECL). */
-bool
+struct cgraph_node *
cgraph_same_body_alias (tree alias, tree decl)
{
#ifndef ASM_OUTPUT_DEF
/* If aliases aren't supported by the assembler, fail. */
- return false;
+ return NULL;
#endif
/*gcc_assert (!assembler_name_hash);*/
- return cgraph_same_body_alias_1 (alias, decl) != NULL;
+ return cgraph_same_body_alias_1 (alias, decl);
}
-void
+/* Add thunk alias into callgraph. The alias declaration is ALIAS and it
+ alises DECL with an adjustments made into the first parameter.
+ See comments in thunk_adjust for detail on the parameters. */
+
+struct cgraph_node *
cgraph_add_thunk (tree alias, tree decl, bool this_adjusting,
HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value,
tree virtual_offset,
node = cgraph_same_body_alias_1 (alias, decl);
gcc_assert (node);
-#ifdef ENABLE_CHECKING
- gcc_assert (!virtual_offset
- || tree_int_cst_equal (virtual_offset, size_int (virtual_value)));
-#endif
+ gcc_checking_assert (!virtual_offset
+ || tree_int_cst_equal (virtual_offset,
+ size_int (virtual_value)));
node->thunk.fixed_offset = fixed_offset;
node->thunk.this_adjusting = this_adjusting;
node->thunk.virtual_value = virtual_value;
node->thunk.virtual_offset_p = virtual_offset != NULL;
node->thunk.alias = real_alias;
node->thunk.thunk_p = true;
+ return node;
}
/* Returns the cgraph node assigned to DECL or NULL if no cgraph node
is assigned. */
struct cgraph_node *
-cgraph_get_node_or_alias (tree decl)
+cgraph_get_node_or_alias (const_tree decl)
{
struct cgraph_node key, *node = NULL, **slot;
if (!cgraph_hash)
return NULL;
- key.decl = decl;
+ key.decl = CONST_CAST2 (tree, const_tree, decl);
slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key,
NO_INSERT);
is assigned. */
struct cgraph_node *
-cgraph_get_node (tree decl)
+cgraph_get_node (const_tree decl)
{
struct cgraph_node key, *node = NULL, **slot;
if (!cgraph_hash)
return NULL;
- key.decl = decl;
+ key.decl = CONST_CAST2 (tree, const_tree, decl);
slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key,
NO_INSERT);
indirect call into a direct one. */
struct cgraph_node *new_callee = cgraph_node (decl);
- cgraph_make_edge_direct (e, new_callee);
+ cgraph_make_edge_direct (e, new_callee, 0);
}
push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
have not been loaded yet. */
if (call_stmt)
{
-#ifdef ENABLE_CHECKING
- /* This is rather pricely check possibly trigerring construction of
- call stmt hashtable. */
- gcc_assert (!cgraph_edge (caller, call_stmt));
-#endif
+ /* This is a rather expensive check possibly trigerring
+ construction of call stmt hashtable. */
+ gcc_checking_assert (!cgraph_edge (caller, call_stmt));
gcc_assert (is_gimple_call (call_stmt));
}
return edge;
}
+/* Allocate cgraph_indirect_call_info and set its fields to default values. */
+
+struct cgraph_indirect_call_info *
+cgraph_allocate_init_indirect_info (void)
+{
+ struct cgraph_indirect_call_info *ii;
+
+ ii = ggc_alloc_cleared_cgraph_indirect_call_info ();
+ ii->param_index = -1;
+ return ii;
+}
/* Create an indirect edge with a yet-undetermined callee where the call
statement destination is a formal parameter of the caller with index
edge->indirect_unknown_callee = 1;
initialize_inline_failed (edge);
- edge->indirect_info = ggc_alloc_cleared_cgraph_indirect_call_info ();
- edge->indirect_info->param_index = -1;
+ edge->indirect_info = cgraph_allocate_init_indirect_info ();
edge->indirect_info->ecf_flags = ecf_flags;
edge->next_callee = caller->indirect_calls;
}
/* Make an indirect EDGE with an unknown callee an ordinary edge leading to
- CALLEE. */
+ CALLEE. DELTA is an integer constant that is to be added to the this
+ pointer (first parameter) to compensate for skipping a thunk adjustment. */
void
-cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee)
+cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee,
+ HOST_WIDE_INT delta)
{
edge->indirect_unknown_callee = 0;
+ edge->indirect_info->thunk_delta = delta;
/* Get the edge out of the indirect edge list. */
if (edge->prev_callee)
{
/* See if the edge is already there and has the correct callee. It
might be so because of indirect inlining has already updated
- it. */
- if (new_call && e->callee && e->callee->decl == new_call)
- return;
+ it. We also might've cloned and redirected the edge. */
+ if (new_call && e->callee)
+ {
+ struct cgraph_node *callee = e->callee;
+ while (callee)
+ {
+ if (callee->decl == new_call
+ || callee->former_clone_of == new_call)
+ return;
+ callee = callee->clone_of;
+ }
+ }
/* Otherwise remove edge and create new one; we can't simply redirect
since function has changed, so inline plan and other information
void
cgraph_mark_address_taken_node (struct cgraph_node *node)
{
+ gcc_assert (!node->global.inlined_to);
cgraph_mark_reachable_node (node);
node->address_taken = 1;
}
fprintf (f, " (inline copy in %s/%i)",
cgraph_node_name (node->global.inlined_to),
node->global.inlined_to->uid);
+ if (node->same_comdat_group)
+ fprintf (f, " (same comdat group as %s/%i)",
+ cgraph_node_name (node->same_comdat_group),
+ node->same_comdat_group->uid);
if (node->clone_of)
fprintf (f, " (clone of %s/%i)",
cgraph_node_name (node->clone_of),
fprintf (f, " local");
if (node->local.externally_visible)
fprintf (f, " externally_visible");
- if (node->local.used_from_object_file)
- fprintf (f, " used_from_object_file");
+ if (node->resolution != LDPR_UNKNOWN)
+ fprintf (f, " %s",
+ ld_plugin_symbol_resolution_names[(int)node->resolution]);
if (node->local.finalized)
fprintf (f, " finalized");
if (node->local.disregard_inline_limits)
fprintf (f, " redefined_extern_inline");
if (TREE_ASM_WRITTEN (node->decl))
fprintf (f, " asm_written");
+ if (node->only_called_at_startup)
+ fprintf (f, " only_called_at_startup");
+ if (node->only_called_at_exit)
+ fprintf (f, " only_called_at_exit");
fprintf (f, "\n called by: ");
for (edge = node->callers; edge; edge = edge->next_caller)
void
change_decl_assembler_name (tree decl, tree name)
{
- gcc_assert (!assembler_name_hash);
+ struct cgraph_node *node;
+ void **slot;
if (!DECL_ASSEMBLER_NAME_SET_P (decl))
+ SET_DECL_ASSEMBLER_NAME (decl, name);
+ else
{
- SET_DECL_ASSEMBLER_NAME (decl, name);
- return;
- }
- if (name == DECL_ASSEMBLER_NAME (decl))
- return;
+ if (name == DECL_ASSEMBLER_NAME (decl))
+ return;
- if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
- && DECL_RTL_SET_P (decl))
- warning (0, "%D renamed after being referenced in assembly", decl);
+ if (assembler_name_hash
+ && TREE_CODE (decl) == FUNCTION_DECL
+ && (node = cgraph_get_node_or_alias (decl)) != NULL)
+ {
+ tree old_name = DECL_ASSEMBLER_NAME (decl);
+ slot = htab_find_slot_with_hash (assembler_name_hash, old_name,
+ decl_assembler_name_hash (old_name),
+ NO_INSERT);
+ /* Inline clones are not hashed. */
+ if (slot && *slot == node)
+ htab_clear_slot (assembler_name_hash, slot);
+ }
+ if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
+ && DECL_RTL_SET_P (decl))
+ warning (0, "%D renamed after being referenced in assembly", decl);
- SET_DECL_ASSEMBLER_NAME (decl, name);
+ SET_DECL_ASSEMBLER_NAME (decl, name);
+ }
+ if (assembler_name_hash
+ && TREE_CODE (decl) == FUNCTION_DECL
+ && (node = cgraph_get_node_or_alias (decl)) != NULL)
+ {
+ slot = htab_find_slot_with_hash (assembler_name_hash, name,
+ decl_assembler_name_hash (name),
+ INSERT);
+ gcc_assert (!*slot);
+ *slot = node;
+ }
}
/* Add a top-level asm statement to the list. */
}
}
else
- new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq,
- e->loop_nest + loop_nest);
+ {
+ new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq,
+ e->loop_nest + loop_nest);
+ if (e->indirect_info)
+ {
+ new_edge->indirect_info
+ = ggc_alloc_cleared_cgraph_indirect_call_info ();
+ *new_edge->indirect_info = *e->indirect_info;
+ }
+ }
new_edge->inline_failed = e->inline_failed;
new_edge->indirect_inlining_edge = e->indirect_inlining_edge;
new_edge->lto_stmt_uid = stmt_uid;
+ /* Clone flags that depend on call_stmt availability manually. */
+ new_edge->can_throw_external = e->can_throw_external;
+ new_edge->call_stmt_cannot_inline_p = e->call_stmt_cannot_inline_p;
if (update_original)
{
e->count -= new_edge->count;
new_node->analyzed = n->analyzed;
new_node->local = n->local;
new_node->local.externally_visible = false;
- new_node->local.used_from_object_file = false;
new_node->local.local = true;
new_node->local.vtable_method = false;
new_node->global = n->global;
n->count = 0;
}
- for (i = 0; VEC_iterate (cgraph_edge_p, redirect_callers, i, e); i++)
+ FOR_EACH_VEC_ELT (cgraph_edge_p, redirect_callers, i, e)
{
/* Redirect calls to the old version node to point to its new
version. */
size_t i;
struct ipa_replace_map *map;
-#ifdef ENABLE_CHECKING
if (!flag_wpa)
- gcc_assert (tree_versionable_function_p (old_decl));
-#endif
+ gcc_checking_assert (tree_versionable_function_p (old_decl));
/* Make a new FUNCTION_DECL tree node */
if (!args_to_skip)
DECL_WEAK (new_node->decl) = 0;
new_node->clone.tree_map = tree_map;
new_node->clone.args_to_skip = args_to_skip;
- for (i = 0; VEC_iterate (ipa_replace_map_p, tree_map, i, map); i++)
+ FOR_EACH_VEC_ELT (ipa_replace_map_p, tree_map, i, map)
{
tree var = map->new_tree;
struct cgraph_node *orig_node;
for (orig_node = old_node; orig_node->clone_of; orig_node = orig_node->clone_of)
;
- for (arg = DECL_ARGUMENTS (orig_node->decl); arg; arg = TREE_CHAIN (arg), oldi++)
+ for (arg = DECL_ARGUMENTS (orig_node->decl); arg; arg = DECL_CHAIN (arg), oldi++)
{
if (bitmap_bit_p (old_node->clone.combined_args_to_skip, oldi))
{
else
new_node->clone.combined_args_to_skip = args_to_skip;
new_node->local.externally_visible = 0;
- new_node->local.used_from_object_file = 0;
new_node->local.local = 1;
new_node->lowered = true;
new_node->reachable = true;
AVAIL_AVAILABLE here? That would be good reason to preserve this
bit. */
- else if (DECL_REPLACEABLE_P (node->decl) && !DECL_EXTERNAL (node->decl))
+ else if (decl_replaceable_p (node->decl) && !DECL_EXTERNAL (node->decl))
avail = AVAIL_OVERWRITABLE;
else avail = AVAIL_AVAILABLE;
if (TREE_CODE (decl) == VAR_DECL)
DECL_COMMON (decl) = 0;
- else if (TREE_CODE (decl) == FUNCTION_DECL)
+ else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
+
+ if (DECL_COMDAT (decl))
{
+ /* It is possible that we are linking against library defining same COMDAT
+ function. To avoid conflict we need to rename our local name of the
+ function just in the case WHOPR partitioning decide to make it hidden
+ to avoid cross partition references. */
+ if (flag_wpa)
+ {
+ const char *old_name;
+
+ old_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ struct cgraph_node *node = cgraph_get_node_or_alias (decl);
+ change_decl_assembler_name (decl,
+ clone_function_name (decl, "local"));
+ if (node->local.lto_file_data)
+ lto_record_renamed_decl (node->local.lto_file_data,
+ old_name,
+ IDENTIFIER_POINTER
+ (DECL_ASSEMBLER_NAME (decl)));
+ }
+ else if (TREE_CODE (decl) == VAR_DECL)
+ {
+ struct varpool_node *vnode = varpool_get_node (decl);
+ /* change_decl_assembler_name will warn here on vtables because
+ C++ frontend still sets TREE_SYMBOL_REFERENCED on them. */
+ SET_DECL_ASSEMBLER_NAME (decl,
+ clone_function_name (decl, "local"));
+ if (vnode->lto_file_data)
+ lto_record_renamed_decl (vnode->lto_file_data,
+ old_name,
+ IDENTIFIER_POINTER
+ (DECL_ASSEMBLER_NAME (decl)));
+ }
+ }
+ DECL_SECTION_NAME (decl) = 0;
DECL_COMDAT (decl) = 0;
- DECL_COMDAT_GROUP (decl) = 0;
- DECL_WEAK (decl) = 0;
- DECL_EXTERNAL (decl) = 0;
}
- else
- gcc_unreachable ();
+ DECL_COMDAT_GROUP (decl) = 0;
+ DECL_WEAK (decl) = 0;
+ DECL_EXTERNAL (decl) = 0;
TREE_PUBLIC (decl) = 0;
if (!DECL_RTL_SET_P (decl))
return;
node->local.externally_visible = false;
node->local.local = true;
+ node->resolution = LDPR_PREVAILING_DEF_IRONLY;
gcc_assert (cgraph_function_body_availability (node) == AVAIL_LOCAL);
}
}
if any to READONLY. */
void
-cgraph_set_readonly_flag (struct cgraph_node *node, bool readonly)
+cgraph_set_const_flag (struct cgraph_node *node, bool readonly, bool looping)
{
struct cgraph_node *alias;
+ /* Static constructors and destructors without a side effect can be
+ optimized out. */
+ if (!looping && readonly)
+ {
+ if (DECL_STATIC_CONSTRUCTOR (node->decl))
+ DECL_STATIC_CONSTRUCTOR (node->decl) = 0;
+ if (DECL_STATIC_DESTRUCTOR (node->decl))
+ DECL_STATIC_DESTRUCTOR (node->decl) = 0;
+ }
TREE_READONLY (node->decl) = readonly;
+ DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping;
for (alias = node->same_body; alias; alias = alias->next)
- TREE_READONLY (alias->decl) = readonly;
+ {
+ TREE_READONLY (alias->decl) = readonly;
+ DECL_LOOPING_CONST_OR_PURE_P (alias->decl) = looping;
+ }
}
/* Set DECL_PURE_P on NODE's decl and on same_body aliases of NODE
if any to PURE. */
void
-cgraph_set_pure_flag (struct cgraph_node *node, bool pure)
+cgraph_set_pure_flag (struct cgraph_node *node, bool pure, bool looping)
{
struct cgraph_node *alias;
+ /* Static constructors and destructors without a side effect can be
+ optimized out. */
+ if (!looping && pure)
+ {
+ if (DECL_STATIC_CONSTRUCTOR (node->decl))
+ DECL_STATIC_CONSTRUCTOR (node->decl) = 0;
+ if (DECL_STATIC_DESTRUCTOR (node->decl))
+ DECL_STATIC_DESTRUCTOR (node->decl) = 0;
+ }
DECL_PURE_P (node->decl) = pure;
+ DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping;
for (alias = node->same_body; alias; alias = alias->next)
- DECL_PURE_P (alias->decl) = pure;
-}
-
-/* Set DECL_LOOPING_CONST_OR_PURE_P on NODE's decl and on
- same_body aliases of NODE if any to LOOPING_CONST_OR_PURE. */
-
-void
-cgraph_set_looping_const_or_pure_flag (struct cgraph_node *node,
- bool looping_const_or_pure)
-{
- struct cgraph_node *alias;
- DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping_const_or_pure;
- for (alias = node->same_body; alias; alias = alias->next)
- DECL_LOOPING_CONST_OR_PURE_P (alias->decl) = looping_const_or_pure;
+ {
+ DECL_PURE_P (alias->decl) = pure;
+ DECL_LOOPING_CONST_OR_PURE_P (alias->decl) = looping;
+ }
}
/* See if the frequency of NODE can be updated based on frequencies of its
cgraph_propagate_frequency (struct cgraph_node *node)
{
bool maybe_unlikely_executed = true, maybe_executed_once = true;
+ bool only_called_at_startup = true;
+ bool only_called_at_exit = true;
+ bool changed = false;
struct cgraph_edge *edge;
+
if (!node->local.local)
return false;
gcc_assert (node->analyzed);
- if (node->frequency == NODE_FREQUENCY_HOT)
- return false;
- if (node->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED)
- return false;
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "Processing frequency %s\n", cgraph_node_name (node));
+
for (edge = node->callers;
- edge && (maybe_unlikely_executed || maybe_executed_once);
+ edge && (maybe_unlikely_executed || maybe_executed_once
+ || only_called_at_startup || only_called_at_exit);
edge = edge->next_caller)
{
+ if (edge->caller != node)
+ {
+ only_called_at_startup &= edge->caller->only_called_at_startup;
+ /* It makes snese to put main() together with the static constructors.
+ It will be executed for sure, but rest of functions called from
+ main are definitly not at startup only. */
+ if (MAIN_NAME_P (DECL_NAME (edge->caller->decl)))
+ only_called_at_startup = 0;
+ only_called_at_exit &= edge->caller->only_called_at_exit;
+ }
if (!edge->frequency)
continue;
switch (edge->caller->frequency)
break;
case NODE_FREQUENCY_EXECUTED_ONCE:
if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " Called by %s that is executed once\n", cgraph_node_name (node));
+ fprintf (dump_file, " Called by %s that is executed once\n",
+ cgraph_node_name (node));
maybe_unlikely_executed = false;
if (edge->loop_nest)
{
case NODE_FREQUENCY_HOT:
case NODE_FREQUENCY_NORMAL:
if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " Called by %s that is normal or hot\n", cgraph_node_name (node));
+ fprintf (dump_file, " Called by %s that is normal or hot\n",
+ cgraph_node_name (node));
maybe_unlikely_executed = false;
maybe_executed_once = false;
break;
}
}
- if (maybe_unlikely_executed)
- {
- node->frequency = NODE_FREQUENCY_UNLIKELY_EXECUTED;
+ if ((only_called_at_startup && !only_called_at_exit)
+ && !node->only_called_at_startup)
+ {
+ node->only_called_at_startup = true;
if (dump_file)
- fprintf (dump_file, "Node %s promoted to unlikely executed.\n", cgraph_node_name (node));
- return true;
- }
- if (maybe_executed_once && node->frequency != NODE_FREQUENCY_EXECUTED_ONCE)
- {
- node->frequency = NODE_FREQUENCY_EXECUTED_ONCE;
+ fprintf (dump_file, "Node %s promoted to only called at startup.\n",
+ cgraph_node_name (node));
+ changed = true;
+ }
+ if ((only_called_at_exit && !only_called_at_startup)
+ && !node->only_called_at_exit)
+ {
+ node->only_called_at_exit = true;
if (dump_file)
- fprintf (dump_file, "Node %s promoted to executed once.\n", cgraph_node_name (node));
- return true;
- }
- return false;
+ fprintf (dump_file, "Node %s promoted to only called at exit.\n",
+ cgraph_node_name (node));
+ changed = true;
+ }
+ /* These come either from profile or user hints; never update them. */
+ if (node->frequency == NODE_FREQUENCY_HOT
+ || node->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED)
+ return changed;
+ if (maybe_unlikely_executed)
+ {
+ node->frequency = NODE_FREQUENCY_UNLIKELY_EXECUTED;
+ if (dump_file)
+ fprintf (dump_file, "Node %s promoted to unlikely executed.\n",
+ cgraph_node_name (node));
+ changed = true;
+ }
+ else if (maybe_executed_once && node->frequency != NODE_FREQUENCY_EXECUTED_ONCE)
+ {
+ node->frequency = NODE_FREQUENCY_EXECUTED_ONCE;
+ if (dump_file)
+ fprintf (dump_file, "Node %s promoted to executed once.\n",
+ cgraph_node_name (node));
+ changed = true;
+ }
+ return changed;
}
/* Return true when NODE can not return or throw and thus
return cgraph_node_cannot_return (e->callee);
}
+/* Return true when function NODE can be removed from callgraph
+ if all direct calls are eliminated. */
+
+bool
+cgraph_can_remove_if_no_direct_calls_and_refs_p (struct cgraph_node *node)
+{
+ gcc_assert (!node->global.inlined_to);
+ /* Extern inlines can always go, we will use the external definition. */
+ if (DECL_EXTERNAL (node->decl))
+ return true;
+ /* When function is needed, we can not remove it. */
+ if (node->needed || node->reachable_from_other_partition)
+ return false;
+ if (DECL_STATIC_CONSTRUCTOR (node->decl)
+ || DECL_STATIC_DESTRUCTOR (node->decl))
+ return false;
+ /* Only COMDAT functions can be removed if externally visible. */
+ if (node->local.externally_visible
+ && (!DECL_COMDAT (node->decl)
+ || cgraph_used_from_object_file_p (node)))
+ return false;
+ return true;
+}
+
+/* Return true when function NODE can be excpected to be removed
+ from program when direct calls in this compilation unit are removed.
+
+ As a special case COMDAT functions are
+ cgraph_can_remove_if_no_direct_calls_p while the are not
+ cgraph_only_called_directly_p (it is possible they are called from other
+ unit)
+
+ This function behaves as cgraph_only_called_directly_p because eliminating
+ all uses of COMDAT function does not make it neccesarily disappear from
+ the program unless we are compiling whole program or we do LTO. In this
+ case we know we win since dynamic linking will not really discard the
+ linkonce section. */
+
+bool
+cgraph_will_be_removed_from_program_if_no_direct_calls (struct cgraph_node *node)
+{
+ gcc_assert (!node->global.inlined_to);
+ if (cgraph_used_from_object_file_p (node))
+ return false;
+ if (!in_lto_p && !flag_whole_program)
+ return cgraph_only_called_directly_p (node);
+ else
+ {
+ if (DECL_EXTERNAL (node->decl))
+ return true;
+ return cgraph_can_remove_if_no_direct_calls_p (node);
+ }
+}
+
+/* Return true when RESOLUTION indicate that linker will use
+ the symbol from non-LTo object files. */
+
+bool
+resolution_used_from_other_file_p (enum ld_plugin_symbol_resolution resolution)
+{
+ return (resolution == LDPR_PREVAILING_DEF
+ || resolution == LDPR_PREEMPTED_REG
+ || resolution == LDPR_RESOLVED_EXEC
+ || resolution == LDPR_RESOLVED_DYN);
+}
+
+/* Return true when NODE is known to be used from other (non-LTO) object file.
+ Known only when doing LTO via linker plugin. */
+
+bool
+cgraph_used_from_object_file_p (struct cgraph_node *node)
+{
+ struct cgraph_node *alias;
+
+ gcc_assert (!node->global.inlined_to);
+ if (!TREE_PUBLIC (node->decl) || DECL_EXTERNAL (node->decl))
+ return false;
+ if (resolution_used_from_other_file_p (node->resolution))
+ return true;
+ for (alias = node->same_body; alias; alias = alias->next)
+ if (TREE_PUBLIC (alias->decl)
+ && resolution_used_from_other_file_p (alias->resolution))
+ return true;
+ return false;
+}
+
#include "gt-cgraph.h"