+2015-03-03 Jan Hubicka <hubicka@ucw.cz>
+
+ * cgraph.c (cgraph_node::can_remove_if_no_direct_calls_p): Rewrite
+ for correct comdat handling.
+ (cgraph_node::will_be_removed_from_program_if_no_direct_calls_p):
+ Likewise.
+ * cgraph.h (call_for_symbol_and_aliases): Fix formating.
+ (used_from_object_file_p_worker): Remove.
+ (cgraph_node::only_called_directly_or_alised): Add
+ used_from_object_file_p.
+ * ipa-inline-analysis.c (growth_likely_positive): Optimie.
+ * ipa-inline-transform.c (can_remove_node_now_p_1): Use
+ can_remove_if_no_direct_calls_and_refs_p.
+
2015-03-04 Nick Clifton <nickc@redhat.com>
* config/rl78/rl78.h (enum reg_class): Remove real registers from
return !node->can_remove_if_no_direct_calls_and_refs_p ();
}
-/* Return true when function cgraph_node and its aliases can be removed from
- callgraph if all direct calls are eliminated. */
+/* Return true if whole comdat group can be removed if there are no direct
+ calls to THIS. */
bool
cgraph_node::can_remove_if_no_direct_calls_p (void)
{
- /* Extern inlines can always go, we will use the external definition. */
- if (DECL_EXTERNAL (decl))
- return true;
- if (address_taken)
+ struct ipa_ref *ref;
+
+ /* For local symbols or non-comdat group it is the same as
+ can_remove_if_no_direct_calls_p. */
+ if (!externally_visible || !same_comdat_group)
+ {
+ if (DECL_EXTERNAL (decl))
+ return true;
+ if (address_taken)
+ return false;
+ return !call_for_symbol_and_aliases (nonremovable_p, NULL, true);
+ }
+
+ /* Otheriwse check if we can remove the symbol itself and then verify
+ that only uses of the comdat groups are direct call to THIS
+ or its aliases. */
+ if (!can_remove_if_no_direct_calls_and_refs_p ())
return false;
- return !call_for_symbol_and_aliases (nonremovable_p, NULL, true);
+
+ /* Check that all refs come from within the comdat group. */
+ for (int i = 0; iterate_referring (i, ref); i++)
+ if (ref->referring->get_comdat_group () != get_comdat_group ())
+ return false;
+
+ struct cgraph_node *target = ultimate_alias_target ();
+ for (cgraph_node *next = dyn_cast<cgraph_node *> (same_comdat_group);
+ next != this; next = dyn_cast<cgraph_node *> (next->same_comdat_group))
+ {
+ if (!externally_visible)
+ continue;
+ if (!next->alias
+ && !next->can_remove_if_no_direct_calls_and_refs_p ())
+ return false;
+
+ /* If we see different symbol than THIS, be sure to check calls. */
+ if (next->ultimate_alias_target () != target)
+ for (cgraph_edge *e = next->callers; e; e = e->next_caller)
+ if (e->caller->get_comdat_group () != get_comdat_group ())
+ return false;
+
+ for (int i = 0; next->iterate_referring (i, ref); i++)
+ if (ref->referring->get_comdat_group () != get_comdat_group ())
+ return false;
+ }
+ return true;
}
/* Return true when function cgraph_node can be expected to be removed
bool
cgraph_node::will_be_removed_from_program_if_no_direct_calls_p (void)
{
+ struct ipa_ref *ref;
gcc_assert (!global.inlined_to);
+ if (DECL_EXTERNAL (decl))
+ return true;
- if (call_for_symbol_and_aliases (used_from_object_file_p_worker,
- NULL, true))
- return false;
if (!in_lto_p && !flag_whole_program)
- return only_called_directly_p ();
- else
{
- if (DECL_EXTERNAL (decl))
- return true;
- return can_remove_if_no_direct_calls_p ();
+ /* If the symbol is in comdat group, we need to verify that whole comdat
+ group becomes unreachable. Technically we could skip references from
+ within the group, too. */
+ if (!only_called_directly_p ())
+ return false;
+ if (same_comdat_group && externally_visible)
+ {
+ struct cgraph_node *target = ultimate_alias_target ();
+ for (cgraph_node *next = dyn_cast<cgraph_node *> (same_comdat_group);
+ next != this;
+ next = dyn_cast<cgraph_node *> (next->same_comdat_group))
+ {
+ if (!externally_visible)
+ continue;
+ if (!next->alias
+ && !next->only_called_directly_p ())
+ return false;
+
+ /* If we see different symbol than THIS,
+ be sure to check calls. */
+ if (next->ultimate_alias_target () != target)
+ for (cgraph_edge *e = next->callers; e; e = e->next_caller)
+ if (e->caller->get_comdat_group () != get_comdat_group ())
+ return false;
+
+ for (int i = 0; next->iterate_referring (i, ref); i++)
+ if (ref->referring->get_comdat_group () != get_comdat_group ())
+ return false;
+ }
+ }
+ return true;
}
+ else
+ return can_remove_if_no_direct_calls_p ();
}
When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are
skipped. */
bool call_for_symbol_and_aliases (bool (*callback) (symtab_node *, void *),
- void *data,
- bool include_overwrite);
+ void *data,
+ bool include_overwrite);
/* If node can not be interposable by static or dynamic linker to point to
different definition, return this symbol. Otherwise look for alias with
returns cgraph_node::get (DECL). */
static cgraph_node * create_same_body_alias (tree alias, tree decl);
- /* Worker for cgraph_can_remove_if_no_direct_calls_p. */
- static bool used_from_object_file_p_worker (cgraph_node *node, void *)
- {
- return node->used_from_object_file_p ();
- }
-
/* Verify whole cgraph structure. */
static void DEBUG_FUNCTION verify_cgraph_nodes (void);
&& !DECL_VIRTUAL_P (decl)
&& !DECL_STATIC_CONSTRUCTOR (decl)
&& !DECL_STATIC_DESTRUCTOR (decl)
+ && !used_from_object_file_p ()
&& !externally_visible);
}
struct cgraph_edge *e;
gcc_checking_assert (edge_growth > 0);
+ if (DECL_EXTERNAL (node->decl))
+ return true;
/* Unlike for functions called once, we play unsafe with
COMDATs. We can allow that since we know functions
in consideration are small (and thus risk is small) and
functions may or may not disappear when eliminated from
current unit. With good probability making aggressive
choice in all units is going to make overall program
- smaller.
-
- Consequently we ask cgraph_can_remove_if_no_direct_calls_p
- instead of
- cgraph_will_be_removed_from_program_if_no_direct_calls */
- if (DECL_EXTERNAL (node->decl)
- || !node->can_remove_if_no_direct_calls_p ())
- return true;
-
- if (!node->will_be_removed_from_program_if_no_direct_calls_p ()
- && (!DECL_COMDAT (node->decl)
- || !node->can_remove_if_no_direct_calls_p ()))
+ smaller. */
+ if (DECL_COMDAT (node->decl))
+ {
+ if (!node->can_remove_if_no_direct_calls_p ())
+ return true;
+ }
+ else if (!node->will_be_removed_from_program_if_no_direct_calls_p ())
return true;
max_callers = inline_summaries->get (node)->size * 4 / edge_growth + 2;
}
/* FIXME: When address is taken of DECL_EXTERNAL function we still
can remove its offline copy, but we would need to keep unanalyzed node in
- the callgraph so references can point to it. */
+ the callgraph so references can point to it.
+
+ Also for comdat group we can ignore references inside a group as we
+ want to prove the group as a whole to be dead. */
return (!node->address_taken
- && node->can_remove_if_no_direct_calls_p ()
+ && node->can_remove_if_no_direct_calls_and_refs_p ()
/* Inlining might enable more devirtualizing, so we want to remove
those only after all devirtualizable virtual calls are processed.
Lacking may edges in callgraph we just preserve them post