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 + + * 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 * 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 + + * g++.dg/otr-fold-1.C: New test. + * g++.dg/otr-fold-2.C: New test. + 2010-05-13 Jakub Jelinek 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" } } */