diff --git a/gcc/ChangeLog b/gcc/ChangeLog index f65902b9c0f..ba9f5b5795e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,14 @@ +2016-03-10 Jan Hubicka + + PR lto/69589 + * cgraph.c (cgraph_node::dump): Dump split_part and indirect_call_target. + * cgraph.h (cgraph_node): Add indirect_call_target flag. + * ipa.c (has_addr_references_p): Cleanup. + (is_indirect_call_target_p): New. + (walk_polymorphic_call_targets): Do not mark virtuals that may be + called indirectly as local. + (symbol_table::remove_unreachable_nodes): Compute indirect_call_target. + 2016-03-10 Jan Hubicka PR ipa/69630 diff --git a/gcc/cgraph.c b/gcc/cgraph.c index ee6a2090838..871ed62fd61 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -2061,6 +2061,10 @@ cgraph_node::dump (FILE *f) fprintf (f, " icf_merged"); if (merged_comdat) fprintf (f, " merged_comdat"); + if (split_part) + fprintf (f, " split_part"); + if (indirect_call_target) + fprintf (f, " indirect_call_target"); if (nonfreeing_fn) fprintf (f, " nonfreeing_fn"); if (DECL_STATIC_CONSTRUCTOR (decl)) diff --git a/gcc/cgraph.h b/gcc/cgraph.h index fc7bb223484..d0345c698c5 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -1366,6 +1366,8 @@ public: unsigned parallelized_function : 1; /* True if function is part split out by ipa-split. */ unsigned split_part : 1; + /* True if the function appears as possible target of indirect call. */ + unsigned indirect_call_target : 1; private: /* Worker for call_for_symbol_and_aliases. */ diff --git a/gcc/ipa.c b/gcc/ipa.c index 3c8fc00e6b5..6722d3b806e 100644 --- a/gcc/ipa.c +++ b/gcc/ipa.c @@ -41,7 +41,7 @@ along with GCC; see the file COPYING3. If not see static bool has_addr_references_p (struct cgraph_node *node, - void *data ATTRIBUTE_UNUSED) + void *) { int i; struct ipa_ref *ref = NULL; @@ -52,6 +52,14 @@ has_addr_references_p (struct cgraph_node *node, return false; } +/* Return true when NODE can be target of an indirect call. */ + +static bool +is_indirect_call_target_p (struct cgraph_node *node, void *) +{ + return node->indirect_call_target; +} + /* Look for all functions inlined to NODE and update their inlined_to pointers to INLINED_TO. */ @@ -172,23 +180,24 @@ walk_polymorphic_call_targets (hash_set *reachable_call_targets, (TYPE_METHOD_BASETYPE (TREE_TYPE (n->decl)))) continue; - symtab_node *body = n->function_symbol (); + n->indirect_call_target = true; + symtab_node *body = n->function_symbol (); /* Prior inlining, keep alive bodies of possible targets for devirtualization. */ - if (n->definition - && (before_inlining_p - && opt_for_fn (body->decl, optimize) - && opt_for_fn (body->decl, flag_devirtualize))) - { - /* Be sure that we will not optimize out alias target - body. */ - if (DECL_EXTERNAL (n->decl) - && n->alias - && before_inlining_p) - reachable->add (body); - reachable->add (n); - } + if (n->definition + && (before_inlining_p + && opt_for_fn (body->decl, optimize) + && opt_for_fn (body->decl, flag_devirtualize))) + { + /* Be sure that we will not optimize out alias target + body. */ + if (DECL_EXTERNAL (n->decl) + && n->alias + && before_inlining_p) + reachable->add (body); + reachable->add (n); + } /* Even after inlining we want to keep the possible targets in the boundary, so late passes can still produce direct call even if the chance for inlining is lost. */ @@ -323,6 +332,7 @@ symbol_table::remove_unreachable_nodes (FILE *file) FOR_EACH_FUNCTION (node) { node->used_as_abstract_origin = false; + node->indirect_call_target = false; if (node->definition && !node->global.inlined_to && !node->in_other_partition @@ -659,7 +669,14 @@ symbol_table::remove_unreachable_nodes (FILE *file) fprintf (file, " %s", node->name ()); node->address_taken = false; changed = true; - if (node->local_p ()) + if (node->local_p () + /* Virtual functions may be kept in cgraph just because + of possible later devirtualization. Do not mark them as + local too early so we won't optimize them out before + we are done with polymorphic call analysis. */ + && (!before_inlining_p + || !node->call_for_symbol_and_aliases + (is_indirect_call_target_p, NULL, true))) { node->local.local = true; if (file) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 093ef571965..c6ffcf38853 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2016-03-10 Jan Hubicka + + PR lto/69589 + * g++.dg/lto/pr69589_0.C: New testcase + * g++.dg/lto/pr69589_1.C: New testcase + 2016-03-10 Marek Polacek PR c++/70153 diff --git a/gcc/testsuite/g++.dg/lto/pr69589_0.C b/gcc/testsuite/g++.dg/lto/pr69589_0.C new file mode 100644 index 00000000000..bbdcb73dfc7 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr69589_0.C @@ -0,0 +1,26 @@ +// { dg-lto-do link } +// { dg-lto-options "-O2 -rdynamic" } +// { dg-extra-ld-options "-r -nostdlib" } +#pragma GCC visibility push(hidden) +struct A { int &operator[] (long); }; +template struct B; +template > +using Z = int; +template struct C; +struct S { + int e; + virtual ~S () {} +}; +struct D : S { + A a; + long i; + D() { { e ? &a[i] : nullptr; } } +}; +template <> +struct C { Z m8 () const; }; +Z +C::m8 () const +{ + D (); +} + diff --git a/gcc/testsuite/g++.dg/lto/pr69589_1.C b/gcc/testsuite/g++.dg/lto/pr69589_1.C new file mode 100644 index 00000000000..01c16208fb5 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr69589_1.C @@ -0,0 +1,61 @@ +struct A; +template +struct Q { Q (T); }; +template +struct U { + ~U () { m1 (nullptr); } + D m2 (); + T *u; + void m1 (T *) { m2 () (u); } +}; +struct F { F (int *); }; +template +using W = Q; +int a, b; +void fn1 (void *); +template +void +fn2 (T *x) +{ + if (x) + x->~T(); + fn1 (x); +} +template +struct C { + void operator() (T *x) { fn2 (x); } +}; +struct D; +template > +using V = U; +struct A { + A (int *); +}; +struct S; +struct G { + V m3 (); +}; +struct S { + int e; + virtual ~S () {} +}; +template +struct H { + H (int, T x, int) : h(x) {} + G g; + void m4 () { g.m3 (); } + T h; +}; +struct I { + I(A, W); +}; +void +test () +{ + A c (&b); + W d (&b); + I e (c, d); + H f (0, e, a); + f.m4 (); +} +