gimple.c (gimple_fold_obj_type_ref): Removed (a replacement moved to gimple-fold.c).

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.

	* testsuite/g++.dg/otr-fold-1.C: New test.
	* testsuite/g++.dg/otr-fold-2.C: New test.

From-SVN: r159362
This commit is contained in:
Martin Jambor 2010-05-13 14:19:02 +02:00 committed by Martin Jambor
parent 2b45bf2152
commit 1ae6fe9be5
7 changed files with 317 additions and 52 deletions

View File

@ -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

View File

@ -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);

View File

@ -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"

View File

@ -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);

View File

@ -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

View File

@ -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" } } */

View File

@ -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" } } */