aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog13
-rw-r--r--gcc/ipa-cp.c17
-rw-r--r--gcc/ipa-prop.c144
-rw-r--r--gcc/testsuite/ChangeLog4
-rw-r--r--gcc/testsuite/g++.dg/ipa/devirt-7.C87
5 files changed, 211 insertions, 54 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 29dd3c6..7ada452 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,16 @@
+2011-04-19 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.
+
2011-04-19 Eric Botcazou <ebotcazou@adacore.com>
PR lto/48148
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 0c05711..f7413f2 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 811884f..c482165 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 6973723..49bd367 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2011-04-19 Martin Jambor <mjambor@suse.cz>
+
+ * g++.dg/ipa/devirt-7.C: New test.
+
2011-04-19 Richard Guenther <rguenther@suse.de>
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 0000000..ac147b5
--- /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" } } */