summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog13
-rw-r--r--gcc/gimple-fold.c146
-rw-r--r--gcc/gimple.c39
-rw-r--r--gcc/gimple.h2
-rw-r--r--gcc/testsuite/ChangeLog5
-rw-r--r--gcc/testsuite/g++.dg/otr-fold-1.C76
-rw-r--r--gcc/testsuite/g++.dg/otr-fold-2.C88
7 files changed, 317 insertions, 52 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 4cc9d9d2edb..116ef8e9868 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,16 @@
+2010-05-13 Martin Jambor <mjambor@suse.cz>
+
+ * gimple.c (gimple_fold_obj_type_ref): Removed (a replacement moved to
+ gimple-fold.c).
+ * gimple-fold.c (get_base_binfo_for_type): New function.
+ (gimple_get_relevant_ref_binfo): Likewise.
+ (gimple_fold_obj_type_ref_known_binfo): Likewise.
+ (gimple_fold_obj_type_ref): Likewise.
+ (fold_gimple_call): Simplify condition for folding virtual calls
+ and call gimple_fold_obj_type_ref.
+ * gimple.h (gimple_get_relevant_ref_binfo): Declare.
+ (gimple_fold_obj_type_ref_known_binfo): Likewise.
+
2010-05-13 Andreas Schwab <schwab@linux-m68k.org>
* config/rs6000/rs6000-protos.h
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index ab076348be0..4fb1b3f0ba7 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -1401,6 +1401,137 @@ gimple_fold_builtin (gimple stmt)
return result;
}
+/* Search for a base binfo of BINFO that corresponds to TYPE and return it if
+ it is found or NULL_TREE if it is not. */
+
+static tree
+get_base_binfo_for_type (tree binfo, tree type)
+{
+ int i;
+ tree base_binfo;
+ tree res = NULL_TREE;
+
+ for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+ if (TREE_TYPE (base_binfo) == type)
+ {
+ gcc_assert (!res);
+ res = base_binfo;
+ }
+
+ return res;
+}
+
+/* Return a binfo describing the part of object referenced by expression REF.
+ Return NULL_TREE if it cannot be determined. REF can consist of a series of
+ COMPONENT_REFs of a declaration or of an INDIRECT_REF or it can also be just
+ a simple declaration, indirect reference or an SSA_NAME. If the function
+ discovers an INDIRECT_REF or an SSA_NAME, it will assume that the
+ encapsulating type is described by KNOWN_BINFO, if it is not NULL_TREE.
+ Otherwise the first non-artificial field declaration or the base declaration
+ will be examined to get the encapsulating type. */
+
+tree
+gimple_get_relevant_ref_binfo (tree ref, tree known_binfo)
+{
+ while (true)
+ {
+ if (TREE_CODE (ref) == COMPONENT_REF)
+ {
+ tree par_type;
+ tree binfo, base_binfo;
+ tree field = TREE_OPERAND (ref, 1);
+
+ if (!DECL_ARTIFICIAL (field))
+ {
+ tree type = TREE_TYPE (field);
+ if (TREE_CODE (type) == RECORD_TYPE)
+ return TYPE_BINFO (type);
+ else
+ return NULL_TREE;
+ }
+
+ par_type = TREE_TYPE (TREE_OPERAND (ref, 0));
+ binfo = TYPE_BINFO (par_type);
+ if (!binfo
+ || BINFO_N_BASE_BINFOS (binfo) == 0)
+ return NULL_TREE;
+
+ base_binfo = BINFO_BASE_BINFO (binfo, 0);
+ if (BINFO_TYPE (base_binfo) != TREE_TYPE (field))
+ {
+ tree d_binfo;
+
+ d_binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (ref, 0),
+ known_binfo);
+ /* Get descendant binfo. */
+ if (!d_binfo)
+ return NULL_TREE;
+ return get_base_binfo_for_type (d_binfo, TREE_TYPE (field));
+ }
+
+ ref = TREE_OPERAND (ref, 0);
+ }
+ else if (DECL_P (ref) && TREE_CODE (TREE_TYPE (ref)) == RECORD_TYPE)
+ return TYPE_BINFO (TREE_TYPE (ref));
+ else if (known_binfo
+ && (TREE_CODE (ref) == SSA_NAME
+ || TREE_CODE (ref) == INDIRECT_REF))
+ return known_binfo;
+ else
+ return NULL_TREE;
+ }
+}
+
+/* Fold a OBJ_TYPE_REF expression to the address of a function. TOKEN is
+ integer form of OBJ_TYPE_REF_TOKEN of the reference expression. KNOWN_BINFO
+ carries the binfo describing the true type of OBJ_TYPE_REF_OBJECT(REF). */
+
+tree
+gimple_fold_obj_type_ref_known_binfo (HOST_WIDE_INT token, tree known_binfo)
+{
+ HOST_WIDE_INT i;
+ tree v, fndecl;
+
+ v = BINFO_VIRTUALS (known_binfo);
+ i = 0;
+ while (i != token)
+ {
+ i += (TARGET_VTABLE_USES_DESCRIPTORS
+ ? TARGET_VTABLE_USES_DESCRIPTORS : 1);
+ v = TREE_CHAIN (v);
+ }
+
+ fndecl = TREE_VALUE (v);
+ return build_fold_addr_expr (fndecl);
+}
+
+
+/* Fold a OBJ_TYPE_REF expression to the address of a function. If KNOWN_TYPE
+ is not NULL_TREE, it is the true type of the outmost encapsulating object if
+ that comes from a pointer SSA_NAME. If the true outmost encapsulating type
+ can be determined from a declaration OBJ_TYPE_REF_OBJECT(REF), it is used
+ regardless of KNOWN_TYPE (which thus can be NULL_TREE). */
+
+tree
+gimple_fold_obj_type_ref (tree ref, tree known_type)
+{
+ tree obj = OBJ_TYPE_REF_OBJECT (ref);
+ tree known_binfo = known_type ? TYPE_BINFO (known_type) : NULL_TREE;
+ tree binfo;
+
+ if (TREE_CODE (obj) == ADDR_EXPR)
+ obj = TREE_OPERAND (obj, 0);
+
+ binfo = gimple_get_relevant_ref_binfo (obj, known_binfo);
+ if (binfo)
+ {
+ HOST_WIDE_INT token = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
+ return gimple_fold_obj_type_ref_known_binfo (token, binfo);
+ }
+ else
+ return NULL_TREE;
+}
+
/* 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.
@@ -1428,9 +1559,6 @@ fold_gimple_call (gimple_stmt_iterator *gsi)
}
else
{
- /* Check for resolvable OBJ_TYPE_REF. The only sorts we can resolve
- here are when we've propagated the address of a decl into the
- object slot. */
/* ??? Should perhaps do this in fold proper. However, doing it
there requires that we create a new CALL_EXPR, and that requires
copying EH region info to the new node. Easier to just do it
@@ -1438,19 +1566,11 @@ fold_gimple_call (gimple_stmt_iterator *gsi)
/* ??? Is there a good reason not to do this in fold_stmt_inplace? */
callee = gimple_call_fn (stmt);
if (TREE_CODE (callee) == OBJ_TYPE_REF
- && lang_hooks.fold_obj_type_ref
- && TREE_CODE (OBJ_TYPE_REF_OBJECT (callee)) == ADDR_EXPR
- && DECL_P (TREE_OPERAND
- (OBJ_TYPE_REF_OBJECT (callee), 0)))
+ && TREE_CODE (OBJ_TYPE_REF_OBJECT (callee)) == ADDR_EXPR)
{
tree t;
- /* ??? Caution: Broken ADDR_EXPR semantics means that
- looking at the type of the operand of the addr_expr
- can yield an array type. See silly exception in
- check_pointer_types_r. */
- t = TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (callee)));
- t = lang_hooks.fold_obj_type_ref (callee, t);
+ t = gimple_fold_obj_type_ref (callee, NULL_TREE);
if (t)
{
gimple_call_set_fn (stmt, t);
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 6f3ba6dfb3a..6f61ca7935a 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -4685,43 +4685,4 @@ gimple_decl_printable_name (tree decl, int verbosity)
return IDENTIFIER_POINTER (DECL_NAME (decl));
}
-
-/* Fold a OBJ_TYPE_REF expression to the address of a function.
- KNOWN_TYPE carries the true type of OBJ_TYPE_REF_OBJECT(REF). Adapted
- from cp_fold_obj_type_ref, but it tolerates types with no binfo
- data. */
-
-tree
-gimple_fold_obj_type_ref (tree ref, tree known_type)
-{
- HOST_WIDE_INT index;
- HOST_WIDE_INT i;
- tree v;
- tree fndecl;
-
- if (TYPE_BINFO (known_type) == NULL_TREE)
- return NULL_TREE;
-
- v = BINFO_VIRTUALS (TYPE_BINFO (known_type));
- index = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
- i = 0;
- while (i != index)
- {
- i += (TARGET_VTABLE_USES_DESCRIPTORS
- ? TARGET_VTABLE_USES_DESCRIPTORS : 1);
- v = TREE_CHAIN (v);
- }
-
- fndecl = TREE_VALUE (v);
-
-#ifdef ENABLE_CHECKING
- gcc_assert (tree_int_cst_equal (OBJ_TYPE_REF_TOKEN (ref),
- DECL_VINDEX (fndecl)));
-#endif
-
- cgraph_node (fndecl)->local.vtable_method = true;
-
- return build_fold_addr_expr (fndecl);
-}
-
#include "gt-gimple.h"
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 4f1c4d40355..d1018b70c0a 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -888,6 +888,8 @@ unsigned get_gimple_rhs_num_ops (enum tree_code);
gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL);
const char *gimple_decl_printable_name (tree, int);
tree gimple_fold_obj_type_ref (tree, tree);
+tree gimple_get_relevant_ref_binfo (tree ref, tree known_binfo);
+tree gimple_fold_obj_type_ref_known_binfo (HOST_WIDE_INT, tree);
/* Returns true iff T is a valid GIMPLE statement. */
extern bool is_gimple_stmt (tree);
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 32f980101f6..d12a4ab5a85 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2010-05-13 Martin Jambor <mjambor@suse.cz>
+
+ * g++.dg/otr-fold-1.C: New test.
+ * g++.dg/otr-fold-2.C: New test.
+
2010-05-13 Jakub Jelinek <jakub@redhat.com>
PR fortran/44036
diff --git a/gcc/testsuite/g++.dg/otr-fold-1.C b/gcc/testsuite/g++.dg/otr-fold-1.C
new file mode 100644
index 00000000000..cff5d072a9c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/otr-fold-1.C
@@ -0,0 +1,76 @@
+/* Verify that virtual calls are folded even when a typecast to an
+ ancestor is involved along the way. */
+/* { dg-do run } */
+/* { dg-options "-O -fdump-tree-optimized-slim" } */
+
+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);
+};
+
+
+class B : public Distraction, public A
+{
+public:
+ virtual int foo (int i);
+};
+
+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;
+}
+
+static inline int middleman_1 (class A *obj, int i)
+{
+ return obj->foo (i);
+}
+
+static inline int middleman_2 (class B *obj, int i)
+{
+ return middleman_1 (obj, i);
+}
+
+int main (int argc, char *argv[])
+{
+ class B b;
+
+ if (middleman_2 (&b, get_input ()) != 3)
+ abort ();
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump "= B::foo" "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
diff --git a/gcc/testsuite/g++.dg/otr-fold-2.C b/gcc/testsuite/g++.dg/otr-fold-2.C
new file mode 100644
index 00000000000..04fbf410268
--- /dev/null
+++ b/gcc/testsuite/g++.dg/otr-fold-2.C
@@ -0,0 +1,88 @@
+/* Verify that virtual calls are folded even when a typecast to an
+ ancestor is involved along the way. */
+/* { dg-do run } */
+/* { dg-options "-O -fdump-tree-optimized-slim" } */
+
+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);
+};
+
+class A_2 : public A
+{
+public:
+ int data_2;
+ virtual int baz (int i);
+};
+
+
+class B : public Distraction, public A_2
+{
+public:
+ virtual int foo (int i);
+};
+
+float Distraction::bar (float z)
+{
+ f += z;
+ return f/2;
+}
+
+int A::foo (int i)
+{
+ return i + 1;
+}
+
+int A_2::baz (int i)
+{
+ return i * 15;
+}
+
+int B::foo (int i)
+{
+ return i + 2;
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+ return 1;
+}
+
+static inline int middleman_1 (class A *obj, int i)
+{
+ return obj->foo (i);
+}
+
+static inline int middleman_2 (class A *obj, int i)
+{
+ return middleman_1 (obj, i);
+}
+
+int main (int argc, char *argv[])
+{
+ class B b;
+
+ if (middleman_2 (&b, get_input ()) != 3)
+ abort ();
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump "= B::foo" "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */