Hi,
this patch fixes the bug in extr_type_from_vtbl_ptr_store that made it to
consider store of construction virtual table or virtual table of virtual base
as store of type's virtual table.

In the testcase we have after early inlining:
virtual C::~C() (struct C * const this)
{
  unsigned int i;
  struct MultiTermDocs * _4;
  struct A * _7;
  unsigned int _10;

  <bb 2>:
  this_2(D)->D.2980._vptr.MultiTermDocs = &MEM[(void *)&_ZTV1C + 24B];
  _4 = &this_2(D)->D.2980;
  MEM[(struct MultiTermDocs *)this_2(D)]._vptr.MultiTermDocs = &MEM[(void 
*)&_ZTC1C0_13MultiTermDocs + 24B];
  MultiTermDocs::wrap (_4);

&_ZTC1C0_13MultiTermDocs is the construction vtable, while its context is
structure C and we thus assume that it is initialized to &_ZTV1C + 16B in the
rest of code.

This leads to wrong jump function:
  Jump functions of caller  virtual C::~C()/29:
    callsite  virtual C::~C()/29 -> MultiTermDocs::~MultiTermDocs()/10 :
       param 0: KNOWN TYPE: base  struct C, offset 0, component struct 
MultiTermDocs
       param 1: CONST: &MEM[(void *)&_ZTT1C + 8B]

This is wrong, since type of _4 at call of WRAP is really construction of C.
With the patch we get:

  Jump functions of caller  virtual C::~C()/29:
    callsite  virtual C::~C()/29 -> void MultiTermDocs::wrap()/8 :
       param 0: ANCESTOR: 0, offset 0, struct MultiTermDocs

It looks bit confusing, but the ANCESTOR has no type_preserved flag,
so it is basically just PASS_THROUGH in a funny representation.

The patch adds code to look into bases of the type obtained from DECL_CONTEXT
of the vtable store and try to lookup BINFO that links precisely the given
vtable at given offset.

With current patch we just give up on both cases mentioned above now. With
virtual tables since we do not have binfo to return and for virtual base
because extr_type_from_vtbl_ptr_store should really work on
polynmorphic_call_context that is something I plan for next stage1.

The bug exists in gcc-4.8 and the patch is bacportable with code from
ipa-devirt.c relocated to ipa-prop. I will do this with a week of delay or so.

Bootstrapped/regtested x86_64-linux, will commit it shortly.

        * ipa-devirt.c (subbinfo_with_vtable_at_offset,
        vtable_pointer_value_to_binfo): New functions.
        * ipa-utils.h (vtable_pointer_value_to_binfo): Declare.
        * ipa-prop.c (extr_type_from_vtbl_ptr_store): Use it.
        * g++.dg/ipa/devirt-23.C: New testcase.
        * g++.dg/ipa/devirt-20.C: Fix template.
Index: ipa-devirt.c
===================================================================
*** ipa-devirt.c        (revision 207393)
--- ipa-devirt.c        (working copy)
*************** contains_type_p (tree outer_type, HOST_W
*** 972,977 ****
--- 972,1041 ----
    return get_class_context (&context, otr_type);
  }
  
+ /* Lookup base of BINFO that has virtual table VTABLE with OFFSET.  */
+ 
+ static tree
+ subbinfo_with_vtable_at_offset (tree binfo, tree offset, tree vtable)
+ {
+   tree v = BINFO_VTABLE (binfo);
+   int i;
+   tree base_binfo;
+ 
+   gcc_assert (!v || TREE_CODE (v) == POINTER_PLUS_EXPR);
+   
+   if (v && tree_int_cst_equal (TREE_OPERAND (v, 1), offset)
+       && TREE_OPERAND (TREE_OPERAND (v, 0), 0) == vtable)
+     return binfo;
+   for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+     if (polymorphic_type_binfo_p (base_binfo))
+       {
+       base_binfo = subbinfo_with_vtable_at_offset (base_binfo, offset, 
vtable);
+       if (base_binfo)
+         return base_binfo;
+       }
+   return NULL;
+ }
+ 
+ /* T is known constant value of virtual table pointer.  Return BINFO of the
+    instance type.  */
+ 
+ tree
+ vtable_pointer_value_to_binfo (tree t)
+ {
+   /* We expect &MEM[(void *)&virtual_table + 16B].
+      We obtain object's BINFO from the context of the virtual table. 
+      This one contains pointer to virtual table represented via
+      POINTER_PLUS_EXPR.  Verify that this pointer match to what
+      we propagated through.
+ 
+      In the case of virtual inheritance, the virtual tables may
+      be nested, i.e. the offset may be different from 16 and we may
+      need to dive into the type representation.  */
+   if (t && TREE_CODE (t) == ADDR_EXPR
+       && TREE_CODE (TREE_OPERAND (t, 0)) == MEM_REF
+       && TREE_CODE (TREE_OPERAND (TREE_OPERAND (t, 0), 0)) == ADDR_EXPR
+       && TREE_CODE (TREE_OPERAND (TREE_OPERAND (t, 0), 1)) == INTEGER_CST
+       && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 0), 0), 0))
+         == VAR_DECL)
+       && DECL_VIRTUAL_P (TREE_OPERAND (TREE_OPERAND
+                                        (TREE_OPERAND (t, 0), 0), 0)))
+     {
+       tree vtable = TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 0), 0), 0);
+       tree offset = TREE_OPERAND (TREE_OPERAND (t, 0), 1);
+       tree binfo = TYPE_BINFO (DECL_CONTEXT (vtable));
+ 
+       binfo = subbinfo_with_vtable_at_offset (binfo, offset, vtable);
+ 
+       /* FIXME: for stores of construction vtables we return NULL,
+        because we do not have BINFO for those. Eventually we should fix
+        our representation to allow this case to be handled, too.
+        In the case we see store of BINFO we however may assume
+        that standard folding will be ale to cope with it.  */
+       return binfo;
+     }
+   return NULL;
+ }
+ 
  /* Given REF call in FNDECL, determine class of the polymorphic
     call (OTR_TYPE), its token (OTR_TOKEN) and CONTEXT.
     Return pointer to object described by the context  */
Index: ipa-utils.h
===================================================================
*** ipa-utils.h (revision 207393)
--- ipa-utils.h (working copy)
*************** tree method_class_type (tree);
*** 87,92 ****
--- 87,93 ----
  tree get_polymorphic_call_info (tree, tree, tree *,
                                HOST_WIDE_INT *,
                                ipa_polymorphic_call_context *);
+ tree vtable_pointer_value_to_binfo (tree t);
  
  /* Return vector containing possible targets of polymorphic call E.
     If FINALP is non-NULL, store true if the list is complette. 
Index: testsuite/g++.dg/ipa/devirt-23.C
===================================================================
*** testsuite/g++.dg/ipa/devirt-23.C    (revision 0)
--- testsuite/g++.dg/ipa/devirt-23.C    (revision 0)
***************
*** 0 ****
--- 1,49 ----
+ /* { dg-do run } */
+ /* { dg-options "-O3 -fno-partial-inlining -fdump-ipa-cp 
-fno-devirtualize-speculatively"  } */
+ /* Main purpose is to verify that we do not produce wrong devirtualization to
+    C::m_fn1.  We currently devirtualize to B::m_fn1, so check that. */
+ #include <stdlib.h>
+ class A {
+ public:
+   unsigned length;
+ };
+ class B {};
+ class MultiTermDocs : public virtual B {
+ protected:
+   A readerTermDocs;
+   A subReaders;
+   virtual B *m_fn1(int *) {}
+   virtual inline  ~MultiTermDocs();
+   inline void wrap(void)
+   {
+   m_fn1(NULL);
+   m_fn1(NULL);
+   m_fn1(NULL);
+   m_fn1(NULL);
+   m_fn1(NULL);
+   m_fn1(NULL);
+   m_fn1(NULL);
+   m_fn1(NULL);
+   m_fn1(NULL);
+   }
+ };
+ class C : MultiTermDocs {
+   B *m_fn1(int *);
+ };
+ MultiTermDocs::~MultiTermDocs() {
+   wrap ();
+   if (&readerTermDocs) {
+     B *a;
+     for (unsigned i = 0; i < subReaders.length; i++)
+       (a != 0);
+   }
+ }
+ 
+ B *C::m_fn1(int *) { abort (); }
+ 
+ main()
+ {
+   class C c;
+ }
+ /* { dg-final { scan-ipa-dump "Discovered a virtual call to" "cp" } } */
+ /* { dg-final { cleanup-ipa-dump "cp" } } */
Index: testsuite/g++.dg/ipa/devirt-20.C
===================================================================
*** testsuite/g++.dg/ipa/devirt-20.C    (revision 207393)
--- testsuite/g++.dg/ipa/devirt-20.C    (working copy)
*************** main(void)
*** 28,31 ****
    return 0;
  }
  /* { dg-final { scan-tree-dump-not "abort" "release_ssa"  } } */
! /* { dg-final { cleanup-ipa-dump "release_ssa" } } */
--- 28,31 ----
    return 0;
  }
  /* { dg-final { scan-tree-dump-not "abort" "release_ssa"  } } */
! /* { dg-final { cleanup-tree-dump "release_ssa" } } */
Index: ipa-prop.c
===================================================================
*** ipa-prop.c  (revision 207405)
--- ipa-prop.c  (working copy)
*************** static tree
*** 591,597 ****
  extr_type_from_vtbl_ptr_store (gimple stmt, struct type_change_info *tci)
  {
    HOST_WIDE_INT offset, size, max_size;
!   tree lhs, rhs, base;
  
    if (!gimple_assign_single_p (stmt))
      return NULL_TREE;
--- 591,597 ----
  extr_type_from_vtbl_ptr_store (gimple stmt, struct type_change_info *tci)
  {
    HOST_WIDE_INT offset, size, max_size;
!   tree lhs, rhs, base, binfo;
  
    if (!gimple_assign_single_p (stmt))
      return NULL_TREE;
*************** extr_type_from_vtbl_ptr_store (gimple st
*** 599,611 ****
    lhs = gimple_assign_lhs (stmt);
    rhs = gimple_assign_rhs1 (stmt);
    if (TREE_CODE (lhs) != COMPONENT_REF
!       || !DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1))
!       || TREE_CODE (rhs) != ADDR_EXPR)
!     return NULL_TREE;
!   rhs = get_base_address (TREE_OPERAND (rhs, 0));
!   if (!rhs
!       || TREE_CODE (rhs) != VAR_DECL
!       || !DECL_VIRTUAL_P (rhs))
      return NULL_TREE;
  
    base = get_ref_base_and_extent (lhs, &offset, &size, &max_size);
--- 599,605 ----
    lhs = gimple_assign_lhs (stmt);
    rhs = gimple_assign_rhs1 (stmt);
    if (TREE_CODE (lhs) != COMPONENT_REF
!       || !DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)))
      return NULL_TREE;
  
    base = get_ref_base_and_extent (lhs, &offset, &size, &max_size);
*************** extr_type_from_vtbl_ptr_store (gimple st
*** 624,630 ****
    else if (tci->object != base)
      return NULL_TREE;
  
!   return DECL_CONTEXT (rhs);
  }
  
  /* Callback of walk_aliased_vdefs and a helper function for
--- 618,633 ----
    else if (tci->object != base)
      return NULL_TREE;
  
!   binfo = vtable_pointer_value_to_binfo (rhs);
! 
!   /* FIXME: vtable_pointer_value_to_binfo may return BINFO of a
!      base of outer type.  In this case we would need to either
!      work on binfos or translate it back to outer type and offset.
!      KNOWN_TYPE jump functions are not ready for that, yet.  */
!   if (!binfo || TYPE_BINFO (BINFO_TYPE (binfo)) != binfo)
!    return NULL;
! 
!   return BINFO_TYPE (binfo);
  }
  
  /* Callback of walk_aliased_vdefs and a helper function for

Reply via email to