ipa-cp.c (ipcp_process_devirtualization_opportunities): Devirtualize also according...
authorMartin Jambor <mjambor@suse.cz>
Tue, 19 Apr 2011 16:35:33 +0000 (18:35 +0200)
committerMartin Jambor <jamborm@gcc.gnu.org>
Tue, 19 Apr 2011 16:35:33 +0000 (18:35 +0200)
2011-04-19  Martin Jambor  <mjambor@suse.cz>

* ipa-cp.c (ipcp_process_devirtualization_opportunities): Devirtualize
also according to actual contants.
* gimple-fold.c (gimple_extract_devirt_binfo_from_cst): New function.
(gimple_fold_call): Use it.
* gimple.h (gimple_extract_devirt_binfo_from_cst): Declare.

* testsuite/g++.dg/opt/devirt1.C: Bump to -O2, remove XFAIL.
* testsuite/g++.dg/opt/devirt2.C: New test.
* testsuite/g++.dg/ipa/devirt-g-1.C: Likewise.

From-SVN: r172719

gcc/ChangeLog
gcc/gimple-fold.c
gcc/gimple.h
gcc/ipa-cp.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ipa/devirt-g-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/opt/devirt1.C
gcc/testsuite/g++.dg/opt/devirt2.C [new file with mode: 0644]

index 785980d91d1040b442a90abcb9c23ae3cb70d86a..5c44ebda4f4c72adeccc8112f41c8ef1589366cf 100644 (file)
@@ -1,3 +1,11 @@
+2011-04-19  Martin Jambor  <mjambor@suse.cz>
+
+       * ipa-cp.c (ipcp_process_devirtualization_opportunities): Devirtualize
+       also according to actual contants.
+       * gimple-fold.c (gimple_extract_devirt_binfo_from_cst): New function.
+       (gimple_fold_call): Use it.
+       * gimple.h (gimple_extract_devirt_binfo_from_cst): Declare.
+
 2011-04-19  Martin Jambor  <mjambor@suse.cz>
 
        * ipa-prop.c (stmt_may_be_vtbl_ptr_store): Return false for scalar
index a6e326bdd1d1c9574067495e483ba6f2e355f743..8d3ab6eb1ff087ab9e04925b06d951bb41d90668 100644 (file)
@@ -1445,6 +1445,74 @@ gimple_adjust_this_by_delta (gimple_stmt_iterator *gsi, tree delta)
   gimple_call_set_arg (call_stmt, 0, tmp);
 }
 
+/* Return a binfo to be used for devirtualization of calls based on an object
+   represented by a declaration (i.e. a global or automatically allocated one)
+   or NULL if it cannot be found or is not safe.  CST is expected to be an
+   ADDR_EXPR of such object or the function will return NULL.  Currently it is
+   safe to use such binfo only if it has no base binfo (i.e. no ancestors).  */
+
+tree
+gimple_extract_devirt_binfo_from_cst (tree cst)
+{
+  HOST_WIDE_INT offset, size, max_size;
+  tree base, type, expected_type, binfo;
+  bool last_artificial = false;
+
+  if (!flag_devirtualize
+      || TREE_CODE (cst) != ADDR_EXPR
+      || TREE_CODE (TREE_TYPE (TREE_TYPE (cst))) != RECORD_TYPE)
+    return NULL_TREE;
+
+  cst = TREE_OPERAND (cst, 0);
+  expected_type = TREE_TYPE (cst);
+  base = get_ref_base_and_extent (cst, &offset, &size, &max_size);
+  type = TREE_TYPE (base);
+  if (!DECL_P (base)
+      || max_size == -1
+      || max_size != size
+      || TREE_CODE (type) != RECORD_TYPE)
+    return NULL_TREE;
+
+  /* Find the sub-object the constant actually refers to and mark whether it is
+     an artificial one (as opposed to a user-defined one).  */
+  while (true)
+    {
+      HOST_WIDE_INT pos, size;
+      tree fld;
+
+      if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (expected_type))
+       break;
+      if (offset < 0)
+       return NULL_TREE;
+
+      for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld))
+       {
+         if (TREE_CODE (fld) != FIELD_DECL)
+           continue;
+
+         pos = int_bit_position (fld);
+         size = tree_low_cst (DECL_SIZE (fld), 1);
+         if (pos <= offset && (pos + size) > offset)
+           break;
+       }
+      if (!fld || TREE_CODE (TREE_TYPE (fld)) != RECORD_TYPE)
+       return NULL_TREE;
+
+      last_artificial = DECL_ARTIFICIAL (fld);
+      type = TREE_TYPE (fld);
+      offset -= pos;
+    }
+  /* Artifical sub-objects are ancestors, we do not want to use them for
+     devirtualization, at least not here.  */
+  if (last_artificial)
+    return NULL_TREE;
+  binfo = TYPE_BINFO (type);
+  if (!binfo || BINFO_N_BASE_BINFOS (binfo) > 0)
+    return NULL_TREE;
+  else
+    return binfo;
+}
+
 /* Attempt to fold a call statement referenced by the statement iterator GSI.
    The statement may be replaced by another statement, e.g., if the call
    simplifies to a constant value. Return true if any changes were made.
@@ -1473,10 +1541,27 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
 
   /* Check for virtual calls that became direct calls.  */
   callee = gimple_call_fn (stmt);
-  if (TREE_CODE (callee) == OBJ_TYPE_REF
-      && gimple_call_addr_fndecl (OBJ_TYPE_REF_EXPR (callee)) != NULL_TREE)
+  if (TREE_CODE (callee) == OBJ_TYPE_REF)
     {
-      gimple_call_set_fn (stmt, OBJ_TYPE_REF_EXPR (callee));
+      tree binfo, fndecl, delta, obj;
+      HOST_WIDE_INT token;
+
+      if (gimple_call_addr_fndecl (OBJ_TYPE_REF_EXPR (callee)) != NULL_TREE)
+       {
+         gimple_call_set_fn (stmt, OBJ_TYPE_REF_EXPR (callee));
+         return true;
+       }
+
+      obj = OBJ_TYPE_REF_OBJECT (callee);
+      binfo = gimple_extract_devirt_binfo_from_cst (obj);
+      if (!binfo)
+       return false;
+      token = TREE_INT_CST_LOW (OBJ_TYPE_REF_TOKEN (callee));
+      fndecl = gimple_get_virt_method_for_binfo (token, binfo, &delta, false);
+      if (!fndecl)
+       return false;
+      gcc_assert (integer_zerop (delta));
+      gimple_call_set_fndecl (stmt, fndecl);
       return true;
     }
 
index 9ae29c4697df0311318dc0f3053401e8a3479e9c..322ce99eb5cdd134154c173f38b7c30777de2a41 100644 (file)
@@ -898,6 +898,7 @@ const char *gimple_decl_printable_name (tree, int);
 bool gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace);
 tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree, tree *, bool);
 void gimple_adjust_this_by_delta (gimple_stmt_iterator *, tree);
+tree gimple_extract_devirt_binfo_from_cst (tree);
 /* Returns true iff T is a valid GIMPLE statement.  */
 extern bool is_gimple_stmt (tree);
 
index f7413f22a51ec6214c59877283a17cfbe431c127..270e58a95d5a916f0eeec5bef3fb6668a2f67e84 100644 (file)
@@ -1246,51 +1246,71 @@ ipcp_process_devirtualization_opportunities (struct cgraph_node *node)
 
   for (ie = node->indirect_calls; ie; ie = next_ie)
     {
-      int param_index, types_count, j;
+      int param_index;
       HOST_WIDE_INT token, anc_offset;
       tree target, delta, otr_type;
+      struct ipcp_lattice *lat;
 
       next_ie = ie->next_callee;
       if (!ie->indirect_info->polymorphic)
        continue;
       param_index = ie->indirect_info->param_index;
-      if (param_index == -1
-         || ipa_param_cannot_devirtualize_p (info, param_index)
-         || ipa_param_types_vec_empty (info, param_index))
+      if (param_index == -1)
        continue;
 
+      lat = ipcp_get_lattice (info, param_index);
       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++)
+      if (lat->type == IPA_CONST_VALUE)
        {
-         tree binfo = VEC_index (tree, info->params[param_index].types, j);
-         tree d, t;
-
+         tree binfo = gimple_extract_devirt_binfo_from_cst (lat->constant);
+         if (!binfo)
+           continue;
          binfo = get_binfo_at_offset (binfo, anc_offset, otr_type);
          if (!binfo)
-           {
-             target = NULL_TREE;
-             break;
-           }
+           continue;
+         target = gimple_get_virt_method_for_binfo (token, binfo, &delta,
+                                                    false);
+       }
+      else
+       {
+         int  types_count, j;
 
-         t = gimple_get_virt_method_for_binfo (token, binfo, &d, true);
-         if (!t)
-           {
-             target = NULL_TREE;
-             break;
-           }
-         else if (!target)
-           {
-             target = t;
-             delta = d;
-           }
-         else if (target != t || !tree_int_cst_equal (delta, d))
+         if (ipa_param_cannot_devirtualize_p (info, param_index)
+             || ipa_param_types_vec_empty (info, param_index))
+           continue;
+
+         types_count = VEC_length (tree, info->params[param_index].types);
+         for (j = 0; j < types_count; j++)
            {
-             target = NULL_TREE;
-             break;
+             tree binfo = VEC_index (tree, info->params[param_index].types, j);
+             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;
+                 break;
+               }
+             else if (!target)
+               {
+                 target = t;
+                 delta = d;
+               }
+             else if (target != t || !tree_int_cst_equal (delta, d))
+               {
+                 target = NULL_TREE;
+                 break;
+               }
            }
        }
 
index bc66cbb3d4d623f319719623a0eb755089eb3285..71c4556ea0e0c2c87bd81c0fd5119ea425ab80e7 100644 (file)
@@ -1,3 +1,9 @@
+2011-04-19  Martin Jambor  <mjambor@suse.cz>
+
+       * g++.dg/opt/devirt1.C: Bump to -O2, remove XFAIL.
+       * g++.dg/opt/devirt2.C: New test.
+       * g++.dg/ipa/devirt-g-1.C: Likewise.
+
 2011-04-19  Tobias Burnus  <burnus@net-b.de>
 
        PR fortran/48588
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-g-1.C b/gcc/testsuite/g++.dg/ipa/devirt-g-1.C
new file mode 100644 (file)
index 0000000..175f24e
--- /dev/null
@@ -0,0 +1,24 @@
+// { dg-do compile }
+// { dg-options "-O2 -fdump-ipa-cp -fdump-tree-optimized" }
+
+struct S { S(); virtual void xyzzy(); void otherstuff(); };
+struct R { int a; S s; R(); };
+S s;
+R r;
+
+void S::xyzzy ()
+{
+  otherstuff ();
+  otherstuff ();
+}
+
+static void __attribute__ ((noinline)) foo(S *p) { p->xyzzy(); }
+void bar() {foo(&s); }
+
+static void __attribute__ ((noinline)) foh(S *p) { p->xyzzy(); }
+void bah() {foh(&r.s); }
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*S::xyzzy" "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 0a825c2a5906a5d277e45ad9fb73092ba6801436..f9b4dc2fc31b87b0286f4461f4eba3bd8d930ae1 100644 (file)
@@ -1,6 +1,6 @@
 // { dg-do compile }
-// { dg-options "-O" }
-// { dg-final { scan-assembler "xyzzy" { xfail *-*-* } } }
+// { dg-options "-O2" }
+// { dg-final { scan-assembler "xyzzy" } }
 
 struct S { S(); virtual void xyzzy(); };
 inline void foo(S *s) { s->xyzzy(); }
diff --git a/gcc/testsuite/g++.dg/opt/devirt2.C b/gcc/testsuite/g++.dg/opt/devirt2.C
new file mode 100644 (file)
index 0000000..087dd17
--- /dev/null
@@ -0,0 +1,11 @@
+// { dg-do compile }
+// { dg-options "-O2" }
+// { dg-final { scan-assembler-times "xyzzy" 2 } }
+
+struct S { S(); virtual void xyzzy(); };
+struct R { int a; S s; R(); };
+S s;
+R r;
+inline void foo(S *p) { p->xyzzy(); }
+void bar() {foo(&s);}
+void bah() {foo(&r.s);}