aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Jambor <mjambor@suse.cz>2011-04-19 18:35:33 +0200
committerMartin Jambor <jamborm@gcc.gnu.org>2011-04-19 18:35:33 +0200
commit49c471e3d8e5a505b20672f7029d07bb828cbe1b (patch)
treef6f04a6735e023b75b60f7849bf8007b8c56b69d
parent9714ca724859e90773df206f552937ddc4da004c (diff)
downloadgcc-49c471e3d8e5a505b20672f7029d07bb828cbe1b.zip
gcc-49c471e3d8e5a505b20672f7029d07bb828cbe1b.tar.gz
gcc-49c471e3d8e5a505b20672f7029d07bb828cbe1b.tar.bz2
ipa-cp.c (ipcp_process_devirtualization_opportunities): Devirtualize also according to actual contants.
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
-rw-r--r--gcc/ChangeLog8
-rw-r--r--gcc/gimple-fold.c91
-rw-r--r--gcc/gimple.h1
-rw-r--r--gcc/ipa-cp.c74
-rw-r--r--gcc/testsuite/ChangeLog6
-rw-r--r--gcc/testsuite/g++.dg/ipa/devirt-g-1.C24
-rw-r--r--gcc/testsuite/g++.dg/opt/devirt1.C4
-rw-r--r--gcc/testsuite/g++.dg/opt/devirt2.C11
8 files changed, 187 insertions, 32 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 785980d..5c44ebd 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,13 @@
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
non-pointer assignments.
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index a6e326b..8d3ab6e 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -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;
}
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 9ae29c4..322ce99 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -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);
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index f7413f2..270e58a 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -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;
+ }
}
}
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index bc66cbb..71c4556 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
index 0000000..175f24e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/devirt-g-1.C
@@ -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" } } */
diff --git a/gcc/testsuite/g++.dg/opt/devirt1.C b/gcc/testsuite/g++.dg/opt/devirt1.C
index 0a825c2..f9b4dc2 100644
--- a/gcc/testsuite/g++.dg/opt/devirt1.C
+++ b/gcc/testsuite/g++.dg/opt/devirt1.C
@@ -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
index 0000000..087dd17
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/devirt2.C
@@ -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);}