Hi,
this patch add basic testcaes for new devirtualization features (more to come) 
and
also fixes bug I introduced while refactoring the speculative context merging
code - that most likely explains why I observed number of speculative devirt
to drop considerably after finishing the patch.

Jason, can you, please, double check the second FIXME in testcases?

Will commit it once bootstrap/regtest of x86_64-linux finishes.

        * testsuite/g++.dg/ipa/devirt-42.C: New testcase.
        * testsuite/g++.dg/ipa/devirt-43.C: New testcase.
        * testsuite/g++.dg/ipa/devirt-44.C: New testcase.
        * testsuite/g++.dg/ipa/devirt-45.C: New testcase.
        * ipa-polymorphic-call.c
        (ipa_polymorphic_call_context::ipa_polymorphic_call_context): Fix
        code determining speculative type.
        (ipa_polymorphic_call_context::combine_with): Fix speculation merge.

Index: testsuite/g++.dg/ipa/devirt-42.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-42.C    (revision 0)
+++ testsuite/g++.dg/ipa/devirt-42.C    (revision 0)
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-ipa-cp -fdump-ipa-inline-details -fno-early-inlining 
-fdump-tree-optimized" } */
+struct A {
+  virtual int foo () {return 1;}
+  int bar () {return foo();}
+  int barbar ();
+};
+namespace {
+  struct B:A {virtual int foo () {return 2;}
+             int barbar () {return bar();}};
+}
+
+int
+A::barbar()
+{
+  return static_cast<B*>(this)->barbar();
+}
+
+main()
+{
+  struct B b;
+  struct A *a = &b;
+  return a->barbar ();
+}
+
+/* Inlining everything into main makes type clear from type of variable b.
+   However devirtualization is also possible for offline copy of A::barbar. 
Invoking
+   B's barbar makes it clear the type is at least B and B is an anonymous
+   namespace type and therefore we know it has no derivations.
+   FIXME: Currently we devirtualize speculatively only because we do not track
+   dynamic type changes well.  */
+/* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline"  
} } */
+/* { dg-final { scan-ipa-dump-times "Outer types match, merging flags" 2 
"inline"  } } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known 
target" 1 "inline"  } } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a 
speculative target" 1 "inline"  } } */
+
+/* Verify that speculation is optimized by late optimizers.  */
+/* { dg-final { scan-ipa-dump-times "return 2" 2 "optimized"  } } */
+/* { dg-final { scan-ipa-dump-not "OBJ_TYPE_REF" "optimized"  } } */
+
+/* { dg-final { cleanup-ipa-dump "inline" } } */
+/* { dg-final { cleanup-ipa-dump "optimized" } } */
Index: testsuite/g++.dg/ipa/devirt-44.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-44.C    (revision 0)
+++ testsuite/g++.dg/ipa/devirt-44.C    (revision 0)
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-ipa-cp -fdump-ipa-inline-details -fno-early-inlining 
-fdump-tree-optimized" } */
+struct A {
+  virtual int foo () {return 1;}
+  int wrapfoo () {foo();}
+  A() {wrapfoo();}
+};
+struct B:A {virtual int foo () {return 2;}};
+
+void dostuff(struct A *);
+
+static void
+test (struct A *a)
+{
+  dostuff (a);
+  if (a->foo ()!= 2)
+    __builtin_abort ();
+}
+
+main()
+{
+  struct B a;
+  dostuff (&a);
+  test (&a);
+}
+/* Here one invocation of foo is while type is in construction, while other is 
not.
+   Check that we handle that.  */
+
+/* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline"  
} } */
+/* { dg-final { scan-ipa-dump "(maybe in construction)" "inline"  } } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known 
target\[^\\n\]*A::foo" 1 "inline"  } } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known 
target\[^\\n\]*B::foo" 1 "inline"  } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */
Index: testsuite/g++.dg/ipa/devirt-41.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-41.C    (revision 0)
+++ testsuite/g++.dg/ipa/devirt-41.C    (revision 0)
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-inline-details -fno-early-inlining 
-fno-ipa-cp" } */
+struct A {virtual int foo () {return 1;}};
+struct B:A {virtual int foo () {return 2;}};
+
+void dostuff(struct A *);
+
+static void
+test (struct A *a)
+{
+  dostuff (a);
+  if (a->foo ()!= 2)
+    __builtin_abort ();
+}
+
+main()
+{
+  struct B a;
+  dostuff (&a);
+  test (&a);
+}
+/* Inlining of dostuff into main should combine polymorphic context
+   specifying Outer type:struct B offset 0
+   with Outer type (dynamic):struct A (or a derived type) offset 0
+   and enable devirtualization.
+
+   Because the type is in static storage, we know it won't change type in 
dostuff
+   and from callstack we can tell that is is not in construction/destruction.  
*/
+/* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline"  
} } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known 
target" 1 "inline"  } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */
Index: testsuite/g++.dg/ipa/devirt-43.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-43.C    (revision 0)
+++ testsuite/g++.dg/ipa/devirt-43.C    (revision 0)
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-inline-details -fno-ipa-cp 
-fno-early-inlining" } */
+struct A {virtual int foo () {return 1;}};
+struct B {int i; struct A a;};
+struct C:A {virtual int foo () {return 2;}};
+
+void dostuff(struct A *);
+
+static void
+test (struct A *a)
+{
+  dostuff (a);
+  if (a->foo ()!= 2)
+    __builtin_abort ();
+}
+
+void
+t(struct B *b)
+{
+  test(&b->a);
+}
+/* Here b comes externally, but we take speculative hint from type of the 
pointer that it is
+   of type B.  This makes A fully specified and we know C::foo is unlikely.
+   FIXME: We could most probably can devirtualize unconditonally because 
dereference of b in
+   &b->a makes the type known.  GIMPLE does not represent this.  */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a 
speculative target" 1 "inline"  } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */
Index: testsuite/g++.dg/ipa/devirt-45.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-45.C    (revision 0)
+++ testsuite/g++.dg/ipa/devirt-45.C    (revision 0)
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-ipa-cp -fdump-ipa-inline-details -fno-early-inlining 
-fdump-tree-optimized" } */
+struct A {
+  virtual int foo () {return 1;}
+  int wrapfoo () {foo();}
+  A() {wrapfoo();}
+};
+inline void* operator new(__SIZE_TYPE__ s, void* buf) throw() {
+   return buf;
+}
+struct B:A {virtual int foo () {return 2;}};
+
+void dostuff(struct A *);
+
+static void
+test2 (struct A *a)
+{
+  dostuff (a);
+  if (a->foo ()!= 2)
+    __builtin_abort ();
+}
+
+static void
+test (struct A *a)
+{
+  dostuff (a);
+  static_cast<B*>(a)->~B();
+  new(a) B();
+  test2(a);
+}
+
+main()
+{
+  struct B a;
+  dostuff (&a);
+  test (&a);
+}
+
+/* One invocation is A::foo () other is B::foo () even though the type is 
destroyed and rebuilt in test() */
+/* { dg-final { scan-ipa-dump "(maybe in construction)" "inline"  } } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known 
target\[^\\n\]*A::foo" 1 "inline"  } } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known 
target\[^\\n\]*B::foo" 1 "inline"  } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */
Index: ipa-polymorphic-call.c
===================================================================
--- ipa-polymorphic-call.c      (revision 215876)
+++ ipa-polymorphic-call.c      (working copy)
@@ -820,8 +820,7 @@ ipa_polymorphic_call_context::ipa_polymo
                                               &offset2, &size, &max_size);
 
          if (max_size != -1 && max_size == size)
-           combine_speculation_with (TYPE_MAIN_VARIANT
-                                       (TREE_TYPE (TREE_TYPE (base_pointer))),
+           combine_speculation_with (TYPE_MAIN_VARIANT (TREE_TYPE (base)),
                                      offset + offset2,
                                      true,
                                      NULL /* Do not change outer type.  */);
@@ -1970,7 +1969,7 @@ ipa_polymorphic_call_context::combine_wi
 
   updated |= combine_speculation_with (ctx.speculative_outer_type,
                                       ctx.speculative_offset,
-                                      ctx.maybe_in_construction,
+                                      ctx.speculative_maybe_derived_type,
                                       otr_type);
 
   if (updated && dump_file && (dump_flags & TDF_DETAILS))

Reply via email to