This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PATCH 2/4] Handle calls to ancestor objects in IPA-CP devirtualization
Hi,
On Fri, Apr 15, 2011 at 05:26:44PM +0200, Richard Guenther wrote:
> On Fri, 15 Apr 2011, Martin Jambor wrote:
>
> > Hi,
> >
> > early inlining can create virtual calls based on the part of an object
> > that represents an ancestor. This patch makes ipa-prop analysis able
> > to recognize such calls and store the required offset along with such
> > calls (the field is already there for similar purposes of indirect
> > inlining). The constant propagation is then made aware of the offset
> > field and takes it into account when looking up the proper BINFO.
> >
> > Bootstrapped and tested on x86_64-linux. OK for trunk?
> >
> > Thanks,
> >
> > Martin
> >
> >
> >
> > 2011-04-13 Martin Jambor <mjambor@suse.cz>
> >
> > * ipa-cp.c (ipcp_process_devirtualization_opportunities): Take into
> > account anc_offset and otr_type from the indirect edge info.
> > * ipa-prop.c (get_ancestor_addr_info): New function.
> > (compute_complex_ancestor_jump_func): Assignment analysis moved to
> > get_ancestor_addr_info, call it.
> > (ipa_note_param_call): Do not initialize information about polymorphic
> > calls, return the indirect call graph edge. Remove the last
> > parameter, adjust all callers.
> > (ipa_analyze_virtual_call_uses): Process also calls to ancestors of
> > parameters. Initialize polymorphic information in the indirect edge.
> >
> > * testsuite/g++.dg/ipa/devirt-7.C: New test.
> >
> >
...
> > Index: src/gcc/ipa-prop.c
> > ===================================================================
> > --- src.orig/gcc/ipa-prop.c
> > +++ src/gcc/ipa-prop.c
> > @@ -576,6 +576,49 @@ compute_complex_assign_jump_func (struct
> > }
> > }
> >
> > +/* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
> > + it looks like:
> > +
> > + iftmp.1_3 = &obj_2(D)->D.1762;
> > +
> > + The base of the MEM_REF must be a default definition SSA NAME of a
> > + parameter. Return NULL_TREE if it looks otherwise. If case of success, the
> > + whole MEM_REF expression is returned and the offset calculated from any
> > + handled components and the MEM_REF itself is stored into *OFFSET. The whole
> > + RHS stripped off the ADDR_EXPR is stored into *OBJ_P. */
> > +
> > +static tree
> > +get_ancestor_addr_info (gimple assign, tree *obj_p, HOST_WIDE_INT *offset)
> > +{
> > + HOST_WIDE_INT size, max_size;
> > + tree expr, parm, obj;
> > +
> > + if (!gimple_assign_single_p (assign))
> > + return NULL_TREE;
> > + expr = gimple_assign_rhs1 (assign);
> > +
> > + if (TREE_CODE (expr) != ADDR_EXPR)
> > + return NULL_TREE;
> > + expr = TREE_OPERAND (expr, 0);
> > + obj = expr;
> > + expr = get_ref_base_and_extent (expr, offset, &size, &max_size);
> > +
> > + if (TREE_CODE (expr) != MEM_REF
> > + /* If this is a varying address, punt. */
> > + || max_size == -1
> > + || max_size != size)
> > + return NULL_TREE;
> > + parm = TREE_OPERAND (expr, 0);
> > + if (TREE_CODE (parm) != SSA_NAME
> > + || !SSA_NAME_IS_DEFAULT_DEF (parm)
>
> Might be an uninitialized variable, so also check
> TREE_CODE (SSA_NAME_VAR (parm)) == PARM_DECL?
>
This was already handled by checking the return value of
ipa_get_param_decl_index but I guess that the code will be more
readable with that check explicit and an assert that
ipa_get_param_decl_index can indeed find an index for the decl so I
changed both places accordingly.
> > + || *offset < 0)
>
> Check this above where you check max_size/size.
OK
...
> > - var = SSA_NAME_VAR (obj);
> > - index = ipa_get_param_decl_index (info, var);
> > + if (SSA_NAME_IS_DEFAULT_DEF (obj))
>
> Check for PARM_DECL.
>
Like above.
> Otherwise ok.
>
Thanks, this is what I'm currently testing and what I intend to commit
if all goes well.
Martin
2011-04-18 Martin Jambor <mjambor@suse.cz>
* ipa-cp.c (ipcp_process_devirtualization_opportunities): Take into
account anc_offset and otr_type from the indirect edge info.
* ipa-prop.c (get_ancestor_addr_info): New function.
(compute_complex_ancestor_jump_func): Assignment analysis moved to
get_ancestor_addr_info, call it.
(ipa_note_param_call): Do not initialize information about polymorphic
calls, return the indirect call graph edge. Remove the last
parameter, adjust all callers.
(ipa_analyze_virtual_call_uses): Process also calls to ancestors of
parameters. Initialize polymorphic information in the indirect edge.
* testsuite/g++.dg/ipa/devirt-7.C: New test.
Index: src/gcc/ipa-cp.c
===================================================================
*** src.orig/gcc/ipa-cp.c
--- src/gcc/ipa-cp.c
*************** ipcp_process_devirtualization_opportunit
*** 1247,1254 ****
for (ie = node->indirect_calls; ie; ie = next_ie)
{
int param_index, types_count, j;
! HOST_WIDE_INT token;
! tree target, delta;
next_ie = ie->next_callee;
if (!ie->indirect_info->polymorphic)
--- 1247,1254 ----
for (ie = node->indirect_calls; ie; ie = next_ie)
{
int param_index, types_count, j;
! HOST_WIDE_INT token, anc_offset;
! tree target, delta, otr_type;
next_ie = ie->next_callee;
if (!ie->indirect_info->polymorphic)
*************** ipcp_process_devirtualization_opportunit
*** 1260,1273 ****
continue;
token = ie->indirect_info->otr_token;
target = NULL_TREE;
types_count = VEC_length (tree, info->params[param_index].types);
for (j = 0; j < types_count; j++)
{
tree binfo = VEC_index (tree, info->params[param_index].types, j);
! tree d;
! tree t = gimple_get_virt_mehtod_for_binfo (token, binfo, &d, true);
if (!t)
{
target = NULL_TREE;
--- 1260,1282 ----
continue;
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++)
{
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_mehtod_for_binfo (token, binfo, &d, true);
if (!t)
{
target = NULL_TREE;
Index: src/gcc/ipa-prop.c
===================================================================
*** src.orig/gcc/ipa-prop.c
--- src/gcc/ipa-prop.c
*************** compute_complex_assign_jump_func (struct
*** 576,581 ****
--- 576,625 ----
}
}
+ /* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
+ it looks like:
+
+ iftmp.1_3 = &obj_2(D)->D.1762;
+
+ The base of the MEM_REF must be a default definition SSA NAME of a
+ parameter. Return NULL_TREE if it looks otherwise. If case of success, the
+ whole MEM_REF expression is returned and the offset calculated from any
+ handled components and the MEM_REF itself is stored into *OFFSET. The whole
+ RHS stripped off the ADDR_EXPR is stored into *OBJ_P. */
+
+ static tree
+ get_ancestor_addr_info (gimple assign, tree *obj_p, HOST_WIDE_INT *offset)
+ {
+ HOST_WIDE_INT size, max_size;
+ tree expr, parm, obj;
+
+ if (!gimple_assign_single_p (assign))
+ return NULL_TREE;
+ expr = gimple_assign_rhs1 (assign);
+
+ if (TREE_CODE (expr) != ADDR_EXPR)
+ return NULL_TREE;
+ expr = TREE_OPERAND (expr, 0);
+ obj = expr;
+ expr = get_ref_base_and_extent (expr, offset, &size, &max_size);
+
+ if (TREE_CODE (expr) != MEM_REF
+ /* If this is a varying address, punt. */
+ || max_size == -1
+ || max_size != size
+ || *offset < 0)
+ return NULL_TREE;
+ parm = TREE_OPERAND (expr, 0);
+ if (TREE_CODE (parm) != SSA_NAME
+ || !SSA_NAME_IS_DEFAULT_DEF (parm)
+ || TREE_CODE (SSA_NAME_VAR (parm)) != PARM_DECL)
+ return NULL_TREE;
+
+ *offset += mem_ref_offset (expr).low * BITS_PER_UNIT;
+ *obj_p = obj;
+ return expr;
+ }
+
/* Given that an actual argument is an SSA_NAME that is a result of a phi
statement PHI, try to find out whether NAME is in fact a
*************** compute_complex_ancestor_jump_func (stru
*** 603,609 ****
struct ipa_jump_func *jfunc,
gimple call, gimple phi)
{
! HOST_WIDE_INT offset, size, max_size;
gimple assign, cond;
basic_block phi_bb, assign_bb, cond_bb;
tree tmp, parm, expr, obj;
--- 647,653 ----
struct ipa_jump_func *jfunc,
gimple call, gimple phi)
{
! HOST_WIDE_INT offset;
gimple assign, cond;
basic_block phi_bb, assign_bb, cond_bb;
tree tmp, parm, expr, obj;
*************** compute_complex_ancestor_jump_func (stru
*** 626,657 ****
assign = SSA_NAME_DEF_STMT (tmp);
assign_bb = gimple_bb (assign);
! if (!single_pred_p (assign_bb)
! || !gimple_assign_single_p (assign))
return;
! expr = gimple_assign_rhs1 (assign);
!
! if (TREE_CODE (expr) != ADDR_EXPR)
! return;
! expr = TREE_OPERAND (expr, 0);
! obj = expr;
! expr = get_ref_base_and_extent (expr, &offset, &size, &max_size);
!
! if (TREE_CODE (expr) != MEM_REF
! /* If this is a varying address, punt. */
! || max_size == -1
! || max_size != size)
return;
- offset += mem_ref_offset (expr).low * BITS_PER_UNIT;
parm = TREE_OPERAND (expr, 0);
- if (TREE_CODE (parm) != SSA_NAME
- || !SSA_NAME_IS_DEFAULT_DEF (parm)
- || offset < 0)
- return;
-
index = ipa_get_param_decl_index (info, SSA_NAME_VAR (parm));
! if (index < 0)
! return;
cond_bb = single_pred (assign_bb);
cond = last_stmt (cond_bb);
--- 670,683 ----
assign = SSA_NAME_DEF_STMT (tmp);
assign_bb = gimple_bb (assign);
! if (!single_pred_p (assign_bb))
return;
! expr = get_ancestor_addr_info (assign, &obj, &offset);
! if (!expr)
return;
parm = TREE_OPERAND (expr, 0);
index = ipa_get_param_decl_index (info, SSA_NAME_VAR (parm));
! gcc_assert (index >= 0);
cond_bb = single_pred (assign_bb);
cond = last_stmt (cond_bb);
*************** compute_complex_ancestor_jump_func (stru
*** 675,681 ****
jfunc->type = IPA_JF_ANCESTOR;
jfunc->value.ancestor.formal_id = index;
jfunc->value.ancestor.offset = offset;
! jfunc->value.ancestor.type = TREE_TYPE (obj);;
}
}
--- 701,707 ----
jfunc->type = IPA_JF_ANCESTOR;
jfunc->value.ancestor.formal_id = index;
jfunc->value.ancestor.offset = offset;
! jfunc->value.ancestor.type = TREE_TYPE (obj);
}
}
*************** ipa_is_ssa_with_stmt_def (tree t)
*** 1162,1190 ****
return false;
}
! /* Find the indirect call graph edge corresponding to STMT and add to it all
! information necessary to describe a call to a parameter number PARAM_INDEX.
! NODE is the caller. POLYMORPHIC should be set to true iff the call is a
! virtual one. */
! static void
! ipa_note_param_call (struct cgraph_node *node, int param_index, gimple stmt,
! bool polymorphic)
{
struct cgraph_edge *cs;
cs = cgraph_edge (node, stmt);
cs->indirect_info->param_index = param_index;
cs->indirect_info->anc_offset = 0;
! cs->indirect_info->polymorphic = polymorphic;
! if (polymorphic)
! {
! tree otr = gimple_call_fn (stmt);
! tree type, token = OBJ_TYPE_REF_TOKEN (otr);
! cs->indirect_info->otr_token = tree_low_cst (token, 1);
! type = TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (otr)));
! cs->indirect_info->otr_type = type;
! }
}
/* Analyze the CALL and examine uses of formal parameters of the caller NODE
--- 1188,1207 ----
return false;
}
! /* Find the indirect call graph edge corresponding to STMT and mark it as a
! call to a parameter number PARAM_INDEX. NODE is the caller. Return the
! indirect call graph edge. */
! static struct cgraph_edge *
! ipa_note_param_call (struct cgraph_node *node, int param_index, gimple stmt)
{
struct cgraph_edge *cs;
cs = cgraph_edge (node, stmt);
cs->indirect_info->param_index = param_index;
cs->indirect_info->anc_offset = 0;
! cs->indirect_info->polymorphic = 0;
! return cs;
}
/* Analyze the CALL and examine uses of formal parameters of the caller NODE
*************** ipa_analyze_indirect_call_uses (struct c
*** 1263,1269 ****
tree var = SSA_NAME_VAR (target);
index = ipa_get_param_decl_index (info, var);
if (index >= 0)
! ipa_note_param_call (node, index, call, false);
return;
}
--- 1280,1286 ----
tree var = SSA_NAME_VAR (target);
index = ipa_get_param_decl_index (info, var);
if (index >= 0)
! ipa_note_param_call (node, index, call);
return;
}
*************** ipa_analyze_indirect_call_uses (struct c
*** 1361,1367 ****
index = ipa_get_param_decl_index (info, rec);
if (index >= 0 && !is_parm_modified_before_call (&parms_info[index],
call, rec))
! ipa_note_param_call (node, index, call, false);
return;
}
--- 1378,1384 ----
index = ipa_get_param_decl_index (info, rec);
if (index >= 0 && !is_parm_modified_before_call (&parms_info[index],
call, rec))
! ipa_note_param_call (node, index, call);
return;
}
*************** ipa_analyze_virtual_call_uses (struct cg
*** 1375,1398 ****
struct ipa_node_params *info, gimple call,
tree target)
{
struct ipa_jump_func jfunc;
tree obj = OBJ_TYPE_REF_OBJECT (target);
- tree var;
int index;
if (!flag_devirtualize)
return;
! if (TREE_CODE (obj) != SSA_NAME
! || !SSA_NAME_IS_DEFAULT_DEF (obj))
return;
! var = SSA_NAME_VAR (obj);
! index = ipa_get_param_decl_index (info, var);
! if (index >= 0
! && !detect_type_change_ssa (obj, call, &jfunc))
! ipa_note_param_call (node, index, call, true);
}
/* Analyze a call statement CALL whether and how it utilizes formal parameters
--- 1392,1442 ----
struct ipa_node_params *info, gimple call,
tree target)
{
+ struct cgraph_edge *cs;
+ struct cgraph_indirect_call_info *ii;
struct ipa_jump_func jfunc;
tree obj = OBJ_TYPE_REF_OBJECT (target);
int index;
+ HOST_WIDE_INT anc_offset;
if (!flag_devirtualize)
return;
! if (TREE_CODE (obj) != SSA_NAME)
return;
! if (SSA_NAME_IS_DEFAULT_DEF (obj))
! {
! if (TREE_CODE (SSA_NAME_VAR (obj)) != PARM_DECL)
! return;
!
! anc_offset = 0;
! index = ipa_get_param_decl_index (info, SSA_NAME_VAR (obj));
! gcc_assert (index >= 0);
! if (detect_type_change_ssa (obj, call, &jfunc))
! return;
! }
! else
! {
! gimple stmt = SSA_NAME_DEF_STMT (obj);
! tree expr;
!
! expr = get_ancestor_addr_info (stmt, &obj, &anc_offset);
! if (!expr)
! return;
! index = ipa_get_param_decl_index (info,
! SSA_NAME_VAR (TREE_OPERAND (expr, 0)));
! gcc_assert (index >= 0);
! if (detect_type_change (obj, expr, call, &jfunc, anc_offset))
! return;
! }
! cs = ipa_note_param_call (node, index, call);
! ii = cs->indirect_info;
! ii->anc_offset = anc_offset;
! ii->otr_token = tree_low_cst (OBJ_TYPE_REF_TOKEN (target), 1);
! ii->otr_type = TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (target)));
! ii->polymorphic = 1;
}
/* Analyze a call statement CALL whether and how it utilizes formal parameters
Index: src/gcc/testsuite/g++.dg/ipa/devirt-7.C
===================================================================
*** /dev/null
--- src/gcc/testsuite/g++.dg/ipa/devirt-7.C
***************
*** 0 ****
--- 1,87 ----
+ /* Verify that IPA-CP can do devirtualization even if the virtual call
+ comes from a method that has been early-inlined into a descendant. */
+ /* { dg-do run } */
+ /* { dg-options "-O3 -fdump-ipa-cp" } */
+
+ 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);
+ int middleman_1 (int i);
+ };
+
+
+ class B : public Distraction, public A
+ {
+ public:
+ virtual int foo (int i);
+ int middleman_2 (int i);
+ __attribute__ ((noinline)) B();
+ };
+
+ 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;
+ }
+
+ int __attribute__ ((always_inline))
+ A::middleman_1 (int i)
+ {
+ return this->foo (i);
+ }
+
+ int __attribute__ ((noinline))
+ B::middleman_2 (int i)
+ {
+ return this->middleman_1 (i);
+ }
+
+ B::B ()
+ {
+ }
+
+ int main (int argc, char *argv[])
+ {
+ class B b;
+ int i;
+
+ for (i = 0; i < get_input(); i++)
+ if (b.middleman_2 (get_input ()) != 3)
+ abort ();
+ return 0;
+ }
+
+ /* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo" "cp" } } */
+ /* { dg-final { cleanup-ipa-dump "cp" } } */