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