ipa-cp.c (ipcp_process_devirtualization_opportunities): Take into account anc_offset...
authorMartin Jambor <mjambor@suse.cz>
Tue, 19 Apr 2011 16:15:08 +0000 (18:15 +0200)
committerMartin Jambor <jamborm@gcc.gnu.org>
Tue, 19 Apr 2011 16:15:08 +0000 (18:15 +0200)
2011-04-19  Martin Jambor  <mjambor@suse.cz>

* ipa-cp.c (ipcp_process_devirtualization_opportunities): Take into
account anc_offset and otr_type from the indirect edge info.
* ipa-prop.c (get_ancestor_addr_info): New function.
(compute_complex_ancestor_jump_func): Assignment analysis moved to
get_ancestor_addr_info, call it.
(ipa_note_param_call): Do not initialize information about polymorphic
calls, return the indirect call graph edge.  Remove the last
parameter, adjust all callers.
(ipa_analyze_virtual_call_uses): Process also calls to ancestors of
parameters.  Initialize polymorphic information in the indirect edge.

* testsuite/g++.dg/ipa/devirt-7.C: New test.

From-SVN: r172716

gcc/ChangeLog
gcc/ipa-cp.c
gcc/ipa-prop.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ipa/devirt-7.C [new file with mode: 0644]

index 29dd3c6d5f27fc65aa125c40f9e64e96b3c9fd3d..7ada452d6ef24f0ac7869519acd4b25a7ee706c1 100644 (file)
@@ -1,3 +1,16 @@
+2011-04-19  Martin Jambor  <mjambor@suse.cz>
+
+       * ipa-cp.c (ipcp_process_devirtualization_opportunities): Take into
+       account anc_offset and otr_type from the indirect edge info.
+       * ipa-prop.c (get_ancestor_addr_info): New function.
+       (compute_complex_ancestor_jump_func): Assignment analysis moved to
+       get_ancestor_addr_info, call it.
+       (ipa_note_param_call): Do not initialize information about polymorphic
+       calls, return the indirect call graph edge.  Remove the last
+       parameter, adjust all callers.
+       (ipa_analyze_virtual_call_uses): Process also calls to ancestors of
+       parameters.  Initialize polymorphic information in the indirect edge.
+
 2011-04-19  Eric Botcazou  <ebotcazou@adacore.com>
 
        PR lto/48148
index 0c05711361472cf9027aee790979ebcc704263fc..f7413f22a51ec6214c59877283a17cfbe431c127 100644 (file)
@@ -1247,8 +1247,8 @@ ipcp_process_devirtualization_opportunities (struct cgraph_node *node)
   for (ie = node->indirect_calls; ie; ie = next_ie)
     {
       int param_index, types_count, j;
-      HOST_WIDE_INT token;
-      tree target, delta;
+      HOST_WIDE_INT token, anc_offset;
+      tree target, delta, otr_type;
 
       next_ie = ie->next_callee;
       if (!ie->indirect_info->polymorphic)
@@ -1260,14 +1260,23 @@ ipcp_process_devirtualization_opportunities (struct cgraph_node *node)
        continue;
 
       token = ie->indirect_info->otr_token;
+      anc_offset = ie->indirect_info->anc_offset;
+      otr_type = ie->indirect_info->otr_type;
       target = NULL_TREE;
       types_count = VEC_length (tree, info->params[param_index].types);
       for (j = 0; j < types_count; j++)
        {
          tree binfo = VEC_index (tree, info->params[param_index].types, j);
-         tree d;
-         tree t = gimple_get_virt_method_for_binfo (token, binfo, &d, true);
+         tree d, t;
 
+         binfo = get_binfo_at_offset (binfo, anc_offset, otr_type);
+         if (!binfo)
+           {
+             target = NULL_TREE;
+             break;
+           }
+
+         t = gimple_get_virt_method_for_binfo (token, binfo, &d, true);
          if (!t)
            {
              target = NULL_TREE;
index 811884f1342d6e324a65f1c8039c20c6eeeb4dbe..c482165a6f38f38cc23103358c67bef6f5ff524a 100644 (file)
@@ -576,6 +576,50 @@ compute_complex_assign_jump_func (struct ipa_node_params *info,
     }
 }
 
+/* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
+   it looks like:
+
+   iftmp.1_3 = &obj_2(D)->D.1762;
+
+   The base of the MEM_REF must be a default definition SSA NAME of a
+   parameter.  Return NULL_TREE if it looks otherwise.  If case of success, the
+   whole MEM_REF expression is returned and the offset calculated from any
+   handled components and the MEM_REF itself is stored into *OFFSET.  The whole
+   RHS stripped off the ADDR_EXPR is stored into *OBJ_P.  */
+
+static tree
+get_ancestor_addr_info (gimple assign, tree *obj_p, HOST_WIDE_INT *offset)
+{
+  HOST_WIDE_INT size, max_size;
+  tree expr, parm, obj;
+
+  if (!gimple_assign_single_p (assign))
+    return NULL_TREE;
+  expr = gimple_assign_rhs1 (assign);
+
+  if (TREE_CODE (expr) != ADDR_EXPR)
+    return NULL_TREE;
+  expr = TREE_OPERAND (expr, 0);
+  obj = expr;
+  expr = get_ref_base_and_extent (expr, offset, &size, &max_size);
+
+  if (TREE_CODE (expr) != MEM_REF
+      /* If this is a varying address, punt.  */
+      || max_size == -1
+      || max_size != size
+      || *offset < 0)
+    return NULL_TREE;
+  parm = TREE_OPERAND (expr, 0);
+  if (TREE_CODE (parm) != SSA_NAME
+      || !SSA_NAME_IS_DEFAULT_DEF (parm)
+      || TREE_CODE (SSA_NAME_VAR (parm)) != PARM_DECL)
+    return NULL_TREE;
+
+  *offset += mem_ref_offset (expr).low * BITS_PER_UNIT;
+  *obj_p = obj;
+  return expr;
+}
+
 
 /* Given that an actual argument is an SSA_NAME that is a result of a phi
    statement PHI, try to find out whether NAME is in fact a
@@ -603,7 +647,7 @@ compute_complex_ancestor_jump_func (struct ipa_node_params *info,
                                    struct ipa_jump_func *jfunc,
                                    gimple call, gimple phi)
 {
-  HOST_WIDE_INT offset, size, max_size;
+  HOST_WIDE_INT offset;
   gimple assign, cond;
   basic_block phi_bb, assign_bb, cond_bb;
   tree tmp, parm, expr, obj;
@@ -626,32 +670,14 @@ compute_complex_ancestor_jump_func (struct ipa_node_params *info,
 
   assign = SSA_NAME_DEF_STMT (tmp);
   assign_bb = gimple_bb (assign);
-  if (!single_pred_p (assign_bb)
-      || !gimple_assign_single_p (assign))
+  if (!single_pred_p (assign_bb))
     return;
-  expr = gimple_assign_rhs1 (assign);
-
-  if (TREE_CODE (expr) != ADDR_EXPR)
+  expr = get_ancestor_addr_info (assign, &obj, &offset);
+  if (!expr)
     return;
-  expr = TREE_OPERAND (expr, 0);
-  obj = expr;
-  expr = get_ref_base_and_extent (expr, &offset, &size, &max_size);
-
-  if (TREE_CODE (expr) != MEM_REF
-      /* If this is a varying address, punt.  */
-      || max_size == -1
-      || max_size != size)
-    return;
-  offset += mem_ref_offset (expr).low * BITS_PER_UNIT;
   parm = TREE_OPERAND (expr, 0);
-  if (TREE_CODE (parm) != SSA_NAME
-      || !SSA_NAME_IS_DEFAULT_DEF (parm)
-      || offset < 0)
-    return;
-
   index = ipa_get_param_decl_index (info, SSA_NAME_VAR (parm));
-  if (index < 0)
-    return;
+  gcc_assert (index >= 0);
 
   cond_bb = single_pred (assign_bb);
   cond = last_stmt (cond_bb);
@@ -675,7 +701,7 @@ compute_complex_ancestor_jump_func (struct ipa_node_params *info,
       jfunc->type = IPA_JF_ANCESTOR;
       jfunc->value.ancestor.formal_id = index;
       jfunc->value.ancestor.offset = offset;
-      jfunc->value.ancestor.type = TREE_TYPE (obj);;
+      jfunc->value.ancestor.type = TREE_TYPE (obj);
     }
 }
 
@@ -1162,29 +1188,20 @@ ipa_is_ssa_with_stmt_def (tree t)
     return false;
 }
 
-/* Find the indirect call graph edge corresponding to STMT and add to it all
-   information necessary to describe a call to a parameter number PARAM_INDEX.
-   NODE is the caller.  POLYMORPHIC should be set to true iff the call is a
-   virtual one.  */
+/* Find the indirect call graph edge corresponding to STMT and mark it as a
+   call to a parameter number PARAM_INDEX.  NODE is the caller.  Return the
+   indirect call graph edge.  */
 
-static void
-ipa_note_param_call (struct cgraph_node *node, int param_index, gimple stmt,
-                    bool polymorphic)
+static struct cgraph_edge *
+ipa_note_param_call (struct cgraph_node *node, int param_index, gimple stmt)
 {
   struct cgraph_edge *cs;
 
   cs = cgraph_edge (node, stmt);
   cs->indirect_info->param_index = param_index;
   cs->indirect_info->anc_offset = 0;
-  cs->indirect_info->polymorphic = polymorphic;
-  if (polymorphic)
-    {
-      tree otr = gimple_call_fn (stmt);
-      tree type, token = OBJ_TYPE_REF_TOKEN (otr);
-      cs->indirect_info->otr_token = tree_low_cst (token, 1);
-      type = TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (otr)));
-      cs->indirect_info->otr_type = type;
-    }
+  cs->indirect_info->polymorphic = 0;
+  return cs;
 }
 
 /* Analyze the CALL and examine uses of formal parameters of the caller NODE
@@ -1263,7 +1280,7 @@ ipa_analyze_indirect_call_uses (struct cgraph_node *node,
       tree var = SSA_NAME_VAR (target);
       index = ipa_get_param_decl_index (info, var);
       if (index >= 0)
-       ipa_note_param_call (node, index, call, false);
+       ipa_note_param_call (node, index, call);
       return;
     }
 
@@ -1361,7 +1378,7 @@ ipa_analyze_indirect_call_uses (struct cgraph_node *node,
   index = ipa_get_param_decl_index (info, rec);
   if (index >= 0 && !is_parm_modified_before_call (&parms_info[index],
                                                   call, rec))
-    ipa_note_param_call (node, index, call, false);
+    ipa_note_param_call (node, index, call);
 
   return;
 }
@@ -1375,24 +1392,51 @@ ipa_analyze_virtual_call_uses (struct cgraph_node *node,
                               struct ipa_node_params *info, gimple call,
                               tree target)
 {
+  struct cgraph_edge *cs;
+  struct cgraph_indirect_call_info *ii;
   struct ipa_jump_func jfunc;
   tree obj = OBJ_TYPE_REF_OBJECT (target);
-  tree var;
   int index;
+  HOST_WIDE_INT anc_offset;
 
   if (!flag_devirtualize)
     return;
 
-  if (TREE_CODE (obj) != SSA_NAME
-      || !SSA_NAME_IS_DEFAULT_DEF (obj))
+  if (TREE_CODE (obj) != SSA_NAME)
     return;
 
-  var = SSA_NAME_VAR (obj);
-  index = ipa_get_param_decl_index (info, var);
+  if (SSA_NAME_IS_DEFAULT_DEF (obj))
+    {
+      if (TREE_CODE (SSA_NAME_VAR (obj)) != PARM_DECL)
+       return;
 
-  if (index >= 0
-      && !detect_type_change_ssa (obj, call, &jfunc))
-    ipa_note_param_call (node, index, call, true);
+      anc_offset = 0;
+      index = ipa_get_param_decl_index (info, SSA_NAME_VAR (obj));
+      gcc_assert (index >= 0);
+      if (detect_type_change_ssa (obj, call, &jfunc))
+       return;
+    }
+  else
+    {
+      gimple stmt = SSA_NAME_DEF_STMT (obj);
+      tree expr;
+
+      expr = get_ancestor_addr_info (stmt, &obj, &anc_offset);
+      if (!expr)
+       return;
+      index = ipa_get_param_decl_index (info,
+                                       SSA_NAME_VAR (TREE_OPERAND (expr, 0)));
+      gcc_assert (index >= 0);
+      if (detect_type_change (obj, expr, call, &jfunc, anc_offset))
+       return;
+    }
+
+  cs = ipa_note_param_call (node, index, call);
+  ii = cs->indirect_info;
+  ii->anc_offset = anc_offset;
+  ii->otr_token = tree_low_cst (OBJ_TYPE_REF_TOKEN (target), 1);
+  ii->otr_type = TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (target)));
+  ii->polymorphic = 1;
 }
 
 /* Analyze a call statement CALL whether and how it utilizes formal parameters
index 69737234c9aa6459ab7f6ca4dbf075a558de3191..49bd36795ccaa501a6bf763cbf88734ce5c2e0a4 100644 (file)
@@ -1,3 +1,7 @@
+2011-04-19  Martin Jambor  <mjambor@suse.cz>
+
+       * g++.dg/ipa/devirt-7.C: New test.
+
 2011-04-19  Richard Guenther  <rguenther@suse.de>
 
        PR lto/48207
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-7.C b/gcc/testsuite/g++.dg/ipa/devirt-7.C
new file mode 100644 (file)
index 0000000..ac147b5
--- /dev/null
@@ -0,0 +1,87 @@
+/* Verify that IPA-CP can do devirtualization even if the virtual call
+   comes from a method that has been early-inlined into a descendant.  */
+/* { dg-do run } */
+/* { dg-options "-O3 -fdump-ipa-cp"  } */
+
+extern "C" void abort (void);
+
+class Distraction
+{
+public:
+  float f;
+  double d;
+  Distraction ()
+  {
+    f = 8.3;
+    d = 10.2;
+  }
+  virtual float bar (float z);
+};
+
+class A
+{
+public:
+  int data;
+  virtual int foo (int i);
+  int middleman_1 (int i);
+};
+
+
+class B : public Distraction, public A
+{
+public:
+  virtual int foo (int i);
+  int middleman_2 (int i);
+  __attribute__ ((noinline)) B();
+};
+
+float Distraction::bar (float z)
+{
+  f += z;
+  return f/2;
+}
+
+int A::foo (int i)
+{
+  return i + 1;
+}
+
+int B::foo (int i)
+{
+  return i + 2;
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+int __attribute__ ((always_inline))
+A::middleman_1 (int i)
+{
+  return this->foo (i);
+}
+
+int __attribute__ ((noinline))
+B::middleman_2 (int i)
+{
+  return this->middleman_1 (i);
+}
+
+B::B ()
+{
+}
+
+int main (int argc, char *argv[])
+{
+  class B b;
+  int i;
+
+  for (i = 0; i < get_input(); i++)
+    if (b.middleman_2 (get_input ()) != 3)
+      abort ();
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo"  "cp"  } } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */