Hi, this patch disables iteration of early inliner. I added the code in early days of indirect inlining mostly to catch things that are not well handled by IPA inliner. We got better on devirtualization and indirect inlining and I think we now handle pretty much all interesting cases there.
Disabling iteration prevents problem with unbounded recursive inlining of strongly connected components Richi observed. On the other hand we still loose optimization on dead code removal: DCE past early inlining usually removes &function statement leading to the offline copy of the function to be optimized out for simple chains of more than one indirect inlining. This is rather infrequent scenario though - usually you have one simple wrapper with calback, not simple wrapper with callback and simple callback with another callback. Bootstrapped/regtested x86_64-linux, compensated the 3 testcases in tesuite testing the feature to both test the feature with parameter increased and thest that the optimization happens anyway at IPA level by default. I also benchmarke SPEC2k/mozilla/spec2k6/C++ benchmark and found no regressions. Will commit it tomorrow if there are no complains. Honza * g++.dg/tree-ssa/inline-1.C: Update max-inliner-iterations. * g++.dg/tree-ssa/inline-2.C: Update max-inliner-iterations. * g++.dg/tree-ssa/inline-3.C: Update max-inliner-iterations. * g++.dg/ipa/inline-1.C: New testcase. * g++.dg/ipa/inline-2.C: New testcase. * g++.dg/ipa/inline-3.C: New testcase. * params.def (PARAM_EARLY_INLINER_MAX_ITERATIONS): Drop to 1. Index: testsuite/g++.dg/tree-ssa/inline-2.C =================================================================== *** testsuite/g++.dg/tree-ssa/inline-2.C (revision 195370) --- testsuite/g++.dg/tree-ssa/inline-2.C (working copy) *************** *** 1,5 **** /* { dg-do compile } */ ! /* { dg-options "-O2 -fdump-tree-einline" } */ /* { dg-add-options bind_pic_locally } */ namespace std { --- 1,5 ---- /* { dg-do compile } */ ! /* { dg-options "-O2 -fdump-tree-einline --param max-early-inliner-iterations=3" } */ /* { dg-add-options bind_pic_locally } */ namespace std { Index: testsuite/g++.dg/tree-ssa/inline-3.C =================================================================== *** testsuite/g++.dg/tree-ssa/inline-3.C (revision 195370) --- testsuite/g++.dg/tree-ssa/inline-3.C (working copy) *************** *** 1,5 **** /* { dg-do compile } */ ! /* { dg-options "-O2 -fdump-tree-einline" } */ /* { dg-add-options bind_pic_locally } */ #include <algorithm> --- 1,5 ---- /* { dg-do compile } */ ! /* { dg-options "-O2 -fdump-tree-einline --param max-early-inliner-iterations=3" } */ /* { dg-add-options bind_pic_locally } */ #include <algorithm> Index: testsuite/g++.dg/tree-ssa/inline-1.C =================================================================== *** testsuite/g++.dg/tree-ssa/inline-1.C (revision 195370) --- testsuite/g++.dg/tree-ssa/inline-1.C (working copy) *************** *** 1,5 **** /* { dg-do compile } */ ! /* { dg-options "-O2 -fdump-tree-einline" } */ /* { dg-add-options bind_pic_locally } */ namespace std { --- 1,5 ---- /* { dg-do compile } */ ! /* { dg-options "-O2 -fdump-tree-einline --param max-early-inliner-iterations=3" } */ /* { dg-add-options bind_pic_locally } */ namespace std { Index: testsuite/g++.dg/torture/20070621-1.C =================================================================== *** testsuite/g++.dg/torture/20070621-1.C (revision 195370) --- testsuite/g++.dg/torture/20070621-1.C (working copy) *************** *** 1,3 **** --- 1,4 ---- + // { dg-do compile } /* Reduced from libstdc++-v3/testsuite/25_algorithms/equal/1.cc 1.2.ii: In function 'void test1()': Index: testsuite/g++.dg/ipa/inline-1.C =================================================================== *** testsuite/g++.dg/ipa/inline-1.C (revision 0) --- testsuite/g++.dg/ipa/inline-1.C (revision 0) *************** *** 0 **** --- 1,36 ---- + /* { dg-do compile } */ + /* { dg-options "-O2 -fdump-ipa-inline --param max-early-inliner-iterations=1" } */ + /* { dg-add-options bind_pic_locally } */ + + namespace std { + extern "C" void puts(const char *s); + } + + template <class T, class E> void + foreach (T b, T e, void (*ptr)(E)) + { + for (; b != e; b++) + ptr(*b); + } + + void + inline_me (char *x) + { + std::puts(x); + } + + static void + inline_me_too (char *x) + { + std::puts(x); + } + + int main(int argc, char **argv) + { + foreach (argv, argv + argc, inline_me); + foreach (argv, argv + argc, inline_me_too); + } + + /* { dg-final { scan-tree-dump-times "Considering void inline_me\\(" 1 "inline"} } */ + /* { dg-final { scan-tree-dump-times "Considering void inline_me_too\\(" 1 "inline"} } */ + /* { dg-final { cleanup-tree-dump "einline" } } */ Index: testsuite/g++.dg/ipa/inline-2.C =================================================================== *** testsuite/g++.dg/ipa/inline-2.C (revision 0) --- testsuite/g++.dg/ipa/inline-2.C (revision 0) *************** *** 0 **** --- 1,36 ---- + /* { dg-do compile } */ + /* { dg-options "-O2 -fdump-ipa-inline --param max-early-inliner-iterations=1" } */ + /* { dg-add-options bind_pic_locally } */ + + namespace std { + extern "C" void puts(const char *s); + } + + template <class T, class E> void + foreach (T b, T e, E ptr) + { + for (; b != e; b++) + ptr(*b); + } + + void + inline_me (char *x) + { + std::puts(x); + } + + static void + inline_me_too (char *x) + { + std::puts(x); + } + + int main(int argc, char **argv) + { + foreach (argv, argv + argc, inline_me); + foreach (argv, argv + argc, inline_me_too); + } + + /* { dg-final { scan-ipa-dump-times "Considering void inline_me\\(" 1 "inline"} } */ + /* { dg-final { scan-ipa-dump-times "Considering void inline_me_too\\(" 1 "inline"} } */ + /* { dg-final { cleanup-ipa-dump "inline" } } */ Index: testsuite/g++.dg/ipa/inline-3.C =================================================================== *** testsuite/g++.dg/ipa/inline-3.C (revision 0) --- testsuite/g++.dg/ipa/inline-3.C (revision 0) *************** *** 0 **** --- 1,29 ---- + /* { dg-do compile } */ + /* { dg-options "-O2 -fdump-ipa-inline --param max-early-inliner-iterations=1" } */ + /* { dg-add-options bind_pic_locally } */ + + #include <algorithm> + + void foo(const char *s); + + void + inline_me (char *x) + { + foo(x); + } + + static void + inline_me_too (char *x) + { + foo(x); + } + + int main(int argc, char **argv) + { + std::for_each (argv, argv + argc, inline_me); + std::for_each (argv, argv + argc, inline_me_too); + } + + /* { dg-final { scan-ipa-dump-times "Considering void inline_me\\(" 1 "inline"} } */ + /* { dg-final { scan-ipa-dump-times "Considering void inline_me_too\\(" 1 "inline"} } */ + /* { dg-final { cleanup-tree-dump "inline" } } */ Index: lto-streamer-in.c =================================================================== *** lto-streamer-in.c (revision 195370) --- lto-streamer-in.c (working copy) *************** lto_read_tree (struct lto_input_block *i *** 1032,1037 **** --- 1032,1039 ---- && TREE_CODE (result) != TRANSLATION_UNIT_DECL) DECL_INITIAL (result) = stream_read_tree (ib, data_in); + gcc_assert (TREE_CODE (result) != BLOCK || !flag_wpa); + /* We should never try to instantiate an MD or NORMAL builtin here. */ if (TREE_CODE (result) == FUNCTION_DECL) gcc_assert (!streamer_handle_as_builtin_p (result)); Index: params.def =================================================================== *** params.def (revision 195370) --- params.def (working copy) *************** DEFPARAM (PARAM_MIN_INLINE_RECURSIVE_PRO *** 109,115 **** DEFPARAM (PARAM_EARLY_INLINER_MAX_ITERATIONS, "max-early-inliner-iterations", "The maximum number of nested indirect inlining performed by early inliner", ! 10, 0, 0) /* Limit on probability of entry BB. */ DEFPARAM (PARAM_COMDAT_SHARING_PROBABILITY, --- 109,115 ---- DEFPARAM (PARAM_EARLY_INLINER_MAX_ITERATIONS, "max-early-inliner-iterations", "The maximum number of nested indirect inlining performed by early inliner", ! 1, 0, 0) /* Limit on probability of entry BB. */ DEFPARAM (PARAM_COMDAT_SHARING_PROBABILITY,