diff mbox

[PR,45934,3/5] Identify the new dynamic type after change

Message ID 20110115004105.GB14818@virgil.arch.suse.de
State New
Headers show

Commit Message

Martin Jambor Jan. 15, 2011, 12:41 a.m. UTC
On Wed, Dec 15, 2010 at 05:49:05PM +0100, Martin Jambor wrote:
> This patch adds code attempting to identify of the new dynamic type
> after it has been changed, which has been missing in the previous
> patch.  All the details are in the previous email.

While trying to modify this patch to accommodate changes in the
previous one I realized that currently offset parameter of
dynamic_type_change gets as a parameter is not necessarily the same
offset that get_binfo_at offset expects (if the binfo belongs to a
sub-object within base).

This afternoon I could not think of anything better than punting if
offset is not zero - if it is I used the same binfo even before,
except for when looking through a non-artificial field but that was
most probably a mistake.  I will think about that more on Monday
(especially if I can make a testcase with a non-artificial field on
offset zero) but the final version is very likely to be the following
(there are only changes in detect_type_change).

Thanks,

Martin


2011-01-14  Martin Jambor  <mjambor@suse.cz>

	* ipa-prop.c (type_change_info): New fields object, known_current_type
	and multiple_types_encountered.
	(extr_type_from_vtbl_ptr_store): New function.
	(check_stmt_for_type_change): Use it, set multiple_types_encountered if
        the result is different from the prvious one.
	(detect_type_change): Set up new fields in tci, build knonw type
	jump functions if the new type chan be identified.

	* testsuite/g++.dg/ipa/devirt-c-1.C: Add dump scans.
	* testsuite/g++.dg/ipa/devirt-c-2.C: Likewise.
	* testsuite/g++.dg/ipa/devirt-c-7.C: New test.
diff mbox

Patch

Index: icln/gcc/ipa-prop.c
===================================================================
--- icln.orig/gcc/ipa-prop.c
+++ icln/gcc/ipa-prop.c
@@ -355,8 +355,17 @@  ipa_print_all_jump_functions (FILE *f)
 
 struct type_change_info
 {
+  /* The declaration or SSA_NAME pointer of the base that we are checking for
+     type change.  */
+  tree object;
+  /* If we actually can tell the type that the object has changed to, it is
+     stored in this field.  Otherwise it remains NULL_TREE.  */
+  tree known_current_type;
   /* Set to true if dynamic type change has been detected.  */
   bool type_maybe_changed;
+  /* Set to true if multiple types have been encountered.  known_current_type
+     must be disregarded in that case.  */
+  bool multiple_types_encountered;
 };
 
 /* Return true if STMT can modify a virtual method table pointer.
@@ -416,6 +425,49 @@  stmt_may_be_vtbl_ptr_store (gimple stmt)
   return true;
 }
 
+/* If STMT can be proved to be an assignment to the virtual method table
+   pointer of ANALYZED_OBJ and the type associated witht the new table
+   identified, return the type.  Otherwise return NULL_TREE.  */
+
+static tree
+extr_type_from_vtbl_ptr_store (gimple stmt, tree analyzed_obj)
+{
+  tree lhs, t, obj;
+
+  if (!is_gimple_assign (stmt))
+    return NULL_TREE;
+
+  lhs = gimple_assign_lhs (stmt);
+
+  if (TREE_CODE (lhs) != COMPONENT_REF)
+    return NULL_TREE;
+  obj = lhs;
+
+  if (!DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)))
+    return NULL_TREE;
+
+  do
+    {
+      obj = TREE_OPERAND (obj, 0);
+    }
+  while (TREE_CODE (obj) == COMPONENT_REF);
+  if (TREE_CODE (obj) == MEM_REF)
+    obj = TREE_OPERAND (obj, 0);
+  if (TREE_CODE (obj) == ADDR_EXPR)
+    obj = TREE_OPERAND (obj, 0);
+  if (obj != analyzed_obj)
+    return NULL_TREE;
+
+  t = gimple_assign_rhs1 (stmt);
+  if (TREE_CODE (t) != ADDR_EXPR)
+    return NULL_TREE;
+  t = get_base_address (TREE_OPERAND (t, 0));
+  if (!t || TREE_CODE (t) != VAR_DECL || !DECL_VIRTUAL_P (t))
+    return NULL_TREE;
+
+  return DECL_CONTEXT (t);
+}
+
 /* Callbeck of walk_aliased_vdefs and a helper function for
    detect_type_change to check whether a particular statement may modify
    the virtual table pointer, and if possible also determine the new type of
@@ -430,6 +482,12 @@  check_stmt_for_type_change (ao_ref *ao A
 
   if (stmt_may_be_vtbl_ptr_store (stmt))
     {
+      tree type;
+      type = extr_type_from_vtbl_ptr_store (stmt, tci->object);
+      if (tci->type_maybe_changed
+	  && type != tci->known_current_type)
+	tci->multiple_types_encountered = true;
+      tci->known_current_type = type;
       tci->type_maybe_changed = true;
       return true;
     }
@@ -449,6 +507,7 @@  detect_type_change (tree arg, tree base,
 		    struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
 {
   struct type_change_info tci;
+  tree type;
   ao_ref ao;
 
   gcc_checking_assert (DECL_P (arg)
@@ -459,8 +518,6 @@  detect_type_change (tree arg, tree base,
   if (!flag_devirtualize || !gimple_vuse (call))
     return false;
 
-  tci.type_maybe_changed = false;
-
   ao.ref = arg;
   ao.base = base;
   ao.offset = offset;
@@ -469,12 +526,33 @@  detect_type_change (tree arg, tree base,
   ao.ref_alias_set = -1;
   ao.base_alias_set = -1;
 
+  type = TREE_TYPE (arg);
+  while (handled_component_p (arg))
+    arg = TREE_OPERAND (arg, 0);
+  if (TREE_CODE (arg) == MEM_REF)
+    arg = TREE_OPERAND (arg, 0);
+  if (TREE_CODE (arg) == ADDR_EXPR)
+    arg = TREE_OPERAND (arg, 0);
+  tci.object = arg;
+  tci.known_current_type = NULL_TREE;
+  tci.type_maybe_changed = false;
+  tci.multiple_types_encountered = false;
+
   walk_aliased_vdefs (&ao, gimple_vuse (call), check_stmt_for_type_change,
 		      &tci, NULL);
   if (!tci.type_maybe_changed)
     return false;
 
-  jfunc->type = IPA_JF_UNKNOWN;
+  if (!tci.known_current_type
+      || tci.multiple_types_encountered
+      || offset != 0)
+    jfunc->type = IPA_JF_UNKNOWN;
+  else
+    {
+      jfunc->type = IPA_JF_KNOWN_TYPE;
+      jfunc->value.base_binfo = TYPE_BINFO (tci.known_current_type);
+    }
+
   return true;
 }
 
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
@@ -1,7 +1,7 @@ 
 /* Verify that ipa-cp correctly detects the dynamic type of an object
    under construction when doing devirtualization.  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fno-early-inlining -fno-inline"  } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
 
 extern "C" void abort (void);
 
@@ -69,3 +69,8 @@  int main (int argc, char *argv[])
     bah ();
   return 0;
 }
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
@@ -1,7 +1,7 @@ 
 /* Verify that ipa-cp correctly detects the dynamic type of an object
    under construction when doing devirtualization.  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fno-early-inlining -fno-inline"  } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
 
 extern "C" void abort (void);
 
@@ -77,3 +77,8 @@  int main (int argc, char *argv[])
     bah ();
   return 0;
 }
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/tree.h
===================================================================
--- icln.orig/gcc/tree.h
+++ icln/gcc/tree.h
@@ -2565,7 +2565,9 @@  struct function;
     nodes, this points to either the FUNCTION_DECL for the containing
     function, the RECORD_TYPE or UNION_TYPE for the containing type, or
     NULL_TREE or a TRANSLATION_UNIT_DECL if the given decl has "file
-    scope".  */
+    scope".  In particular, for VAR_DECLs which are virtual table pointers
+    (they have DECL_VIRTUAL set), we use DECL_CONTEXT to determine the type
+    they belong to.  */
 #define DECL_CONTEXT(NODE) (DECL_MINIMAL_CHECK (NODE)->decl_minimal.context)
 #define DECL_FIELD_CONTEXT(NODE) \
   (FIELD_DECL_CHECK (NODE)->decl_minimal.context)
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-7.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-7.C
@@ -0,0 +1,87 @@ 
+/* Verify that ipa-cp will not get confused by placement new constructing an
+   object within another one when looking for dynamic type change .  */
+/* { dg-do run } */
+/* { dg-options "-O3"  } */
+
+extern "C" void abort (void);
+namespace std {
+  typedef __SIZE_TYPE__ size_t;
+}
+inline void* __attribute__ ((always_inline))
+operator new(std::size_t, void* __p) throw()
+{
+  return __p;
+}
+
+class A
+{
+public:
+  char data[256];
+  A();
+  virtual int foo (int i);
+};
+
+class B : public A
+{
+public:
+  virtual int foo (int i);
+};
+
+class C
+{
+public:
+  C();
+  virtual double foo (double i);
+};
+
+int A::foo (int i)
+{
+  return i + 1;
+}
+
+int B::foo (int i)
+{
+  return i + 2;
+}
+
+double C::foo (double i)
+{
+  return i + 3.5;
+}
+
+static int __attribute__ ((noinline)) middleman (class A *obj, int i)
+{
+  return obj->foo (i);
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+__attribute__ ((always_inline)) C::C ()
+{
+}
+
+A::A ()
+{
+}
+
+static  __attribute__ ((noinline)) void bah ()
+{
+  class B b;
+
+  C *c = new ((void *) &b.data) C;
+
+  if (middleman (&b, get_input ()) != 3)
+    abort ();
+}
+
+int main (int argc, char *argv[])
+{
+  int i;
+
+  for (i = 0; i < 10; i++)
+    bah ();
+  return 0;
+}