2016-03-10 Jan Hubicka <hubicka@ucw.cz>
+ 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 <hubicka@ucw.cz>
+
PR ipa/69630
* ipa-devirt.c (possible_polymorphic_call_targets): Do not ICE
on cxa_pure_virtual.
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))
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. */
static bool
has_addr_references_p (struct cgraph_node *node,
- void *data ATTRIBUTE_UNUSED)
+ void *)
{
int i;
struct ipa_ref *ref = NULL;
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. */
(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. */
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
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)
+2016-03-10 Jan Hubicka <hubicka@ucw.cz>
+
+ PR lto/69589
+ * g++.dg/lto/pr69589_0.C: New testcase
+ * g++.dg/lto/pr69589_1.C: New testcase
+
2016-03-10 Marek Polacek <polacek@redhat.com>
PR c++/70153
--- /dev/null
+// { 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 <typename> struct B;
+template <typename T, typename = B<T> >
+using Z = int;
+template <typename> struct C;
+struct S {
+ int e;
+ virtual ~S () {}
+};
+struct D : S {
+ A a;
+ long i;
+ D() { { e ? &a[i] : nullptr; } }
+};
+template <>
+struct C<int> { Z<S> m8 () const; };
+Z<S>
+C<int>::m8 () const
+{
+ D ();
+}
+
--- /dev/null
+struct A;
+template <class T>
+struct Q { Q (T); };
+template<typename T, class D>
+struct U {
+ ~U () { m1 (nullptr); }
+ D m2 ();
+ T *u;
+ void m1 (T *) { m2 () (u); }
+};
+struct F { F (int *); };
+template <class, class T = F>
+using W = Q<T>;
+int a, b;
+void fn1 (void *);
+template <class T>
+void
+fn2 (T *x)
+{
+ if (x)
+ x->~T();
+ fn1 (x);
+}
+template <typename T>
+struct C {
+ void operator() (T *x) { fn2 (x); }
+};
+struct D;
+template <typename T, typename D = C<T> >
+using V = U<T, D>;
+struct A {
+ A (int *);
+};
+struct S;
+struct G {
+ V<S> m3 ();
+};
+struct S {
+ int e;
+ virtual ~S () {}
+};
+template<typename T>
+struct H {
+ H (int, T x, int) : h(x) {}
+ G g;
+ void m4 () { g.m3 (); }
+ T h;
+};
+struct I {
+ I(A, W<D>);
+};
+void
+test ()
+{
+ A c (&b);
+ W<D> d (&b);
+ I e (c, d);
+ H<I> f (0, e, a);
+ f.m4 ();
+}
+