Hi, compiling Chromium, ipa-icf introduces thousdands of aliases of one function from iostream traits (because it is duplicated so many times due to ipa-sra privatizing it to every compile unit).
This triggers non-linear behaviour in will_be_removed_from_program_if_no_direct_calls_p. This patch makes inliner to not call it that often. I also noticed that the function is not quite corect about inlining and fixed it. Bootstrapped/regtested x86_64-linux, will commit it later today. Honza * ipa-inline-analysis.c (check_callers): Check node->can_remove_if_no_direct_calls_and_refs_p. (growth_likely_positive): Reorganize to call can_remove_if_no_direct_calls_p later. * cgraph.h (will_be_removed_from_program_if_no_direct_calls_p, will_be_removed_from_program_if_no_direct_calls_p): Add will_inline parameter. * cgraph.c (cgraph_node::can_remove_if_no_direct_calls_p, cgraph_node::will_be_removed_from_program_if_no_direct_calls_p): Handle inliner case correctly. Index: ipa-inline-analysis.c =================================================================== --- ipa-inline-analysis.c (revision 221267) +++ ipa-inline-analysis.c (working copy) @@ -3978,6 +3978,9 @@ check_callers (cgraph_node *node, int *m { ipa_ref *ref; + if (!node->can_remove_if_no_direct_calls_and_refs_p ()) + return true; + for (cgraph_edge *e = node->callers; e; e = e->next_caller) { (*max_callers)--; @@ -4007,23 +4010,13 @@ growth_likely_positive (struct cgraph_no struct cgraph_edge *e; gcc_checking_assert (edge_growth > 0); + /* First quickly check if NODE is removable at all. */ 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 - moreover grow estimates already accounts that COMDAT - 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. */ - 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 ()) + if (!node->can_remove_if_no_direct_calls_and_refs_p () + || node->address_taken) return true; + max_callers = inline_summaries->get (node)->size * 4 / edge_growth + 2; for (e = node->callers; e; e = e->next_caller) @@ -4039,6 +4032,22 @@ growth_likely_positive (struct cgraph_no if (check_callers (dyn_cast <cgraph_node *> (ref->referring), &max_callers)) 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 + moreover grow estimates already accounts that COMDAT + 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. */ + 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; + return estimate_growth (node) > 0; } Index: cgraph.h =================================================================== --- cgraph.h (revision 221268) +++ cgraph.h (working copy) @@ -1110,16 +1110,23 @@ public: all uses of COMDAT function does not make it necessarily 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 will_be_removed_from_program_if_no_direct_calls_p (void); + linkonce section. + + If WILL_INLINE is true, assume that function will be inlined into all the + direct calls. */ + bool will_be_removed_from_program_if_no_direct_calls_p + (bool will_inline = false); /* Return true when function can be removed from callgraph - if all direct calls are eliminated. */ + if all direct calls and references are eliminated. The function does + not take into account comdat groups. */ bool can_remove_if_no_direct_calls_and_refs_p (void); /* Return true when function cgraph_node and its aliases can be removed from - callgraph if all direct calls are eliminated. */ - bool can_remove_if_no_direct_calls_p (void); + callgraph if all direct calls are eliminated. + If WILL_INLINE is true, assume that function will be inlined into all the + direct calls. */ + bool can_remove_if_no_direct_calls_p (bool will_inline = false); /* Return true when callgraph node is a function with Gimple body defined in current unit. Functions can also be define externally or they Index: cgraph.c =================================================================== --- cgraph.c (revision 221267) +++ cgraph.c (working copy) @@ -2415,7 +2415,7 @@ nonremovable_p (cgraph_node *node, void calls to THIS. */ bool -cgraph_node::can_remove_if_no_direct_calls_p (void) +cgraph_node::can_remove_if_no_direct_calls_p (bool will_inline) { struct ipa_ref *ref; @@ -2430,6 +2430,9 @@ cgraph_node::can_remove_if_no_direct_cal return !call_for_symbol_and_aliases (nonremovable_p, NULL, true); } + if (will_inline && address_taken) + return false; + /* 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. */ @@ -2454,12 +2457,16 @@ cgraph_node::can_remove_if_no_direct_cal /* 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 ()) + if (e->caller->get_comdat_group () != get_comdat_group () + || will_inline) return false; - for (int i = 0; next->iterate_referring (i, ref); i++) - if (ref->referring->get_comdat_group () != get_comdat_group ()) - return false; + /* If function is not being inlined, we care only about + references outside of the comdat group. */ + if (!will_inline) + for (int i = 0; next->iterate_referring (i, ref); i++) + if (ref->referring->get_comdat_group () != get_comdat_group ()) + return false; } return true; } @@ -2479,9 +2486,9 @@ cgraph_node::can_remove_if_no_direct_cal linkonce section. */ bool -cgraph_node::will_be_removed_from_program_if_no_direct_calls_p (void) +cgraph_node::will_be_removed_from_program_if_no_direct_calls_p + (bool will_inline) { - struct ipa_ref *ref; gcc_assert (!global.inlined_to); if (DECL_EXTERNAL (decl)) return true; @@ -2496,6 +2503,9 @@ cgraph_node::will_be_removed_from_progra if (same_comdat_group && externally_visible) { struct cgraph_node *target = ultimate_alias_target (); + + if (will_inline && address_taken) + return true; for (cgraph_node *next = dyn_cast<cgraph_node *> (same_comdat_group); next != this; next = dyn_cast<cgraph_node *> (next->same_comdat_group)) @@ -2510,18 +2520,15 @@ cgraph_node::will_be_removed_from_progra 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 ()) + if (e->caller->get_comdat_group () != get_comdat_group () + || will_inline) 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 (); + return can_remove_if_no_direct_calls_p (will_inline); }