From 405914731a9a06617eadb4dc604d0fe5e901fc76 Mon Sep 17 00:00:00 2001 From: Martin Jambor Date: Tue, 19 Apr 2011 18:15:08 +0200 Subject: [PATCH] ipa-cp.c (ipcp_process_devirtualization_opportunities): Take into account anc_offset and otr_type from the indirect edge info. 2011-04-19 Martin Jambor * 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. From-SVN: r172716 --- gcc/ChangeLog | 13 +++ gcc/ipa-cp.c | 17 +++- gcc/ipa-prop.c | 144 ++++++++++++++++++---------- gcc/testsuite/ChangeLog | 4 + gcc/testsuite/g++.dg/ipa/devirt-7.C | 87 +++++++++++++++++ 5 files changed, 211 insertions(+), 54 deletions(-) create mode 100644 gcc/testsuite/g++.dg/ipa/devirt-7.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 29dd3c6d5f2..7ada452d6ef 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2011-04-19 Martin Jambor + + * 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. + 2011-04-19 Eric Botcazou PR lto/48148 diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index 0c057113614..f7413f22a51 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -1247,8 +1247,8 @@ ipcp_process_devirtualization_opportunities (struct cgraph_node *node) for (ie = node->indirect_calls; ie; ie = next_ie) { int param_index, types_count, j; - HOST_WIDE_INT token; - tree target, delta; + HOST_WIDE_INT token, anc_offset; + tree target, delta, otr_type; next_ie = ie->next_callee; if (!ie->indirect_info->polymorphic) @@ -1260,14 +1260,23 @@ ipcp_process_devirtualization_opportunities (struct cgraph_node *node) 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; - tree t = gimple_get_virt_method_for_binfo (token, binfo, &d, true); + tree d, t; + binfo = get_binfo_at_offset (binfo, anc_offset, otr_type); + if (!binfo) + { + target = NULL_TREE; + break; + } + + t = gimple_get_virt_method_for_binfo (token, binfo, &d, true); if (!t) { target = NULL_TREE; diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index 811884f1342..c482165a6f3 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -576,6 +576,50 @@ compute_complex_assign_jump_func (struct ipa_node_params *info, } } +/* 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 @@ -603,7 +647,7 @@ compute_complex_ancestor_jump_func (struct ipa_node_params *info, struct ipa_jump_func *jfunc, gimple call, gimple phi) { - HOST_WIDE_INT offset, size, max_size; + HOST_WIDE_INT offset; gimple assign, cond; basic_block phi_bb, assign_bb, cond_bb; tree tmp, parm, expr, obj; @@ -626,32 +670,14 @@ compute_complex_ancestor_jump_func (struct ipa_node_params *info, assign = SSA_NAME_DEF_STMT (tmp); assign_bb = gimple_bb (assign); - if (!single_pred_p (assign_bb) - || !gimple_assign_single_p (assign)) + if (!single_pred_p (assign_bb)) return; - expr = gimple_assign_rhs1 (assign); - - if (TREE_CODE (expr) != ADDR_EXPR) + expr = get_ancestor_addr_info (assign, &obj, &offset); + if (!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; + gcc_assert (index >= 0); cond_bb = single_pred (assign_bb); cond = last_stmt (cond_bb); @@ -675,7 +701,7 @@ compute_complex_ancestor_jump_func (struct ipa_node_params *info, jfunc->type = IPA_JF_ANCESTOR; jfunc->value.ancestor.formal_id = index; jfunc->value.ancestor.offset = offset; - jfunc->value.ancestor.type = TREE_TYPE (obj);; + jfunc->value.ancestor.type = TREE_TYPE (obj); } } @@ -1162,29 +1188,20 @@ ipa_is_ssa_with_stmt_def (tree t) 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. */ +/* 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 void -ipa_note_param_call (struct cgraph_node *node, int param_index, gimple stmt, - bool polymorphic) +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 = 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; - } + cs->indirect_info->polymorphic = 0; + return cs; } /* Analyze the CALL and examine uses of formal parameters of the caller NODE @@ -1263,7 +1280,7 @@ ipa_analyze_indirect_call_uses (struct cgraph_node *node, 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); + ipa_note_param_call (node, index, call); return; } @@ -1361,7 +1378,7 @@ ipa_analyze_indirect_call_uses (struct cgraph_node *node, 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); + ipa_note_param_call (node, index, call); return; } @@ -1375,24 +1392,51 @@ ipa_analyze_virtual_call_uses (struct cgraph_node *node, 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); - tree var; int index; + HOST_WIDE_INT anc_offset; if (!flag_devirtualize) return; - if (TREE_CODE (obj) != SSA_NAME - || !SSA_NAME_IS_DEFAULT_DEF (obj)) + if (TREE_CODE (obj) != SSA_NAME) return; - var = SSA_NAME_VAR (obj); - index = ipa_get_param_decl_index (info, var); + if (SSA_NAME_IS_DEFAULT_DEF (obj)) + { + if (TREE_CODE (SSA_NAME_VAR (obj)) != PARM_DECL) + return; - if (index >= 0 - && !detect_type_change_ssa (obj, call, &jfunc)) - ipa_note_param_call (node, index, call, true); + 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 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 69737234c9a..49bd36795cc 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2011-04-19 Martin Jambor + + * g++.dg/ipa/devirt-7.C: New test. + 2011-04-19 Richard Guenther PR lto/48207 diff --git a/gcc/testsuite/g++.dg/ipa/devirt-7.C b/gcc/testsuite/g++.dg/ipa/devirt-7.C new file mode 100644 index 00000000000..ac147b57599 --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/devirt-7.C @@ -0,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" } } */ -- 2.30.2