From 0127c169d155ecdad955d1b27cbc9e34ea981d42 Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Sun, 5 Oct 2014 07:02:19 +0200 Subject: [PATCH] ipa-polymorphic-call.c (walk_ssa_copies): Recognize NULL pointer checks. * ipa-polymorphic-call.c (walk_ssa_copies): Recognize NULL pointer checks. (ipa_polymorphic_call_context::get_dynamic_type): Return true if type doesn't change. * cgraph.h (cgraph_indirect_call_info): New flag. * cgraph.c (cgraph_node::create_indirect_edge): Initialize it. (cgraph_node::dump): Dump it. * ipa-prop.c (ipa_analyze_call_uses): Ignore return valud of context.get_dynamic_type. (ipa_make_edge_direct_to_target): Do not speculate edge that is already speuclative. (try_make_edge_direct_virtual_call): Use VPTR_CHANGED; Do not speculate to __builtin_unreachable (ipa_write_indirect_edge_info, ipa_read_indirect_edge_info): Stream vptr_changed. * ipa-cp.c (ipa_get_indirect_edge_target_1): Use vptr_changed. * g++.dg/ipa/devirt-47.C: New testcase. From-SVN: r215898 --- gcc/ChangeLog | 19 +++++++ gcc/cgraph.c | 3 + gcc/cgraph.h | 3 + gcc/ipa-cp.c | 6 +- gcc/ipa-polymorphic-call.c | 38 +++++++++++-- gcc/ipa-prop.c | 82 ++++++++++++++++++++++------ gcc/testsuite/ChangeLog | 4 ++ gcc/testsuite/g++.dg/ipa/devirt-47.C | 31 +++++++++++ 8 files changed, 161 insertions(+), 25 deletions(-) create mode 100644 gcc/testsuite/g++.dg/ipa/devirt-47.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d33827012e5..3bf6de1581c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,22 @@ +2014-10-04 Jan Hubicka + + * ipa-polymorphic-call.c (walk_ssa_copies): Recognize + NULL pointer checks. + (ipa_polymorphic_call_context::get_dynamic_type): Return true + if type doesn't change. + * cgraph.h (cgraph_indirect_call_info): New flag. + * cgraph.c (cgraph_node::create_indirect_edge): Initialize it. + (cgraph_node::dump): Dump it. + * ipa-prop.c (ipa_analyze_call_uses): Ignore return valud + of context.get_dynamic_type. + (ipa_make_edge_direct_to_target): Do not speculate + edge that is already speuclative. + (try_make_edge_direct_virtual_call): Use VPTR_CHANGED; Do not + speculate to __builtin_unreachable + (ipa_write_indirect_edge_info, ipa_read_indirect_edge_info): Stream + vptr_changed. + * ipa-cp.c (ipa_get_indirect_edge_target_1): Use vptr_changed. + 2014-10-04 Jan Hubicka * ipa-prop.c (ipa_compute_jump_functions_for_edge): Call diff --git a/gcc/cgraph.c b/gcc/cgraph.c index a46e1883fd0..38dc7e663fa 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -883,6 +883,7 @@ cgraph_node::create_indirect_edge (gimple call_stmt, int ecf_flags, edge->indirect_info = cgraph_allocate_init_indirect_info (); edge->indirect_info->ecf_flags = ecf_flags; + edge->indirect_info->vptr_changed = true; /* Record polymorphic call info. */ if (compute_indirect_info @@ -1988,6 +1989,8 @@ cgraph_node::dump (FILE *f) edge->indirect_info->member_ptr ? "member ptr" : "aggregate", edge->indirect_info->by_ref ? "passed by reference":"", (int)edge->indirect_info->offset); + if (edge->indirect_info->vptr_changed) + fprintf (f, " (vptr maybe changed)"); } fprintf (f, "\n"); if (edge->indirect_info->polymorphic) diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 5ed078acfa9..20b5c4e4805 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -1393,6 +1393,9 @@ struct GTY(()) cgraph_indirect_call_info /* When the previous bit is set, this one determines whether the destination is loaded from a parameter passed by reference. */ unsigned by_ref : 1; + /* For polymorphic calls this specify whether the virtual table pointer + may have changed in between function entry and the call. */ + unsigned vptr_changed : 1; }; struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgraph_edge { diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index 657536c9c8e..a3be16f8bf4 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -1560,7 +1560,8 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, t = NULL; /* Try to work out value of virtual table pointer value in replacemnets. */ - if (!t && agg_reps && !ie->indirect_info->by_ref) + if (!t && agg_reps && !ie->indirect_info->by_ref + && !ie->indirect_info->vptr_changed) { while (agg_reps) { @@ -1578,7 +1579,8 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, /* Try to work out value of virtual table pointer value in known aggregate values. */ if (!t && known_aggs.length () > (unsigned int) param_index - && !ie->indirect_info->by_ref) + && !ie->indirect_info->by_ref + && !ie->indirect_info->vptr_changed) { struct ipa_agg_jump_function *agg; agg = known_aggs[param_index]; diff --git a/gcc/ipa-polymorphic-call.c b/gcc/ipa-polymorphic-call.c index ecbd78ce33d..74226f21a87 100644 --- a/gcc/ipa-polymorphic-call.c +++ b/gcc/ipa-polymorphic-call.c @@ -760,11 +760,37 @@ walk_ssa_copies (tree op) while (TREE_CODE (op) == SSA_NAME && !SSA_NAME_IS_DEFAULT_DEF (op) && SSA_NAME_DEF_STMT (op) - && gimple_assign_single_p (SSA_NAME_DEF_STMT (op))) + && (gimple_assign_single_p (SSA_NAME_DEF_STMT (op)) + || gimple_code (SSA_NAME_DEF_STMT (op)) == GIMPLE_PHI)) { - if (gimple_assign_load_p (SSA_NAME_DEF_STMT (op))) - return op; - op = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op)); + /* Special case + if (ptr == 0) + ptr = 0; + else + ptr = ptr.foo; + This pattern is implicitly produced for casts to non-primary + bases. When doing context analysis, we do not really care + about the case pointer is NULL, becuase the call will be + undefined anyway. */ + if (gimple_code (SSA_NAME_DEF_STMT (op)) == GIMPLE_PHI) + { + gimple phi = SSA_NAME_DEF_STMT (op); + + if (gimple_phi_num_args (phi) != 2) + return op; + if (integer_zerop (gimple_phi_arg_def (phi, 0))) + op = gimple_phi_arg_def (phi, 1); + else if (integer_zerop (gimple_phi_arg_def (phi, 1))) + op = gimple_phi_arg_def (phi, 0); + else + return op; + } + else + { + if (gimple_assign_load_p (SSA_NAME_DEF_STMT (op))) + return op; + op = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op)); + } STRIP_NOPS (op); } return op; @@ -1371,6 +1397,8 @@ check_stmt_for_type_change (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef, void *data) is set), try to walk memory writes and find the actual construction of the instance. + Return true if memory is unchanged from function entry. + We do not include this analysis in the context analysis itself, because it needs memory SSA to be fully built and the walk may be expensive. So it is not suitable for use withing fold_stmt and similar uses. */ @@ -1615,7 +1643,7 @@ ipa_polymorphic_call_context::get_dynamic_type (tree instance, function_entry_reached ? " (multiple types encountered)" : ""); } - return true; + return false; } /* See if speculation given by SPEC_OUTER_TYPE, SPEC_OFFSET and SPEC_MAYBE_DERIVED_TYPE diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index d5ecea41303..80acdcc21bb 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -2371,10 +2371,10 @@ ipa_analyze_call_uses (struct func_body_info *fbi, gimple call) gcc_checking_assert (cs->indirect_info->otr_token == tree_to_shwi (OBJ_TYPE_REF_TOKEN (target))); - if (context.get_dynamic_type (instance, - OBJ_TYPE_REF_OBJECT (target), - obj_type_ref_class (target), call)) - cs->indirect_info->context = context; + context.get_dynamic_type (instance, + OBJ_TYPE_REF_OBJECT (target), + obj_type_ref_class (target), call); + cs->indirect_info->context = context; } if (TREE_CODE (target) == SSA_NAME) @@ -2878,6 +2878,38 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target, callee = cgraph_node::get_create (target); } + /* If the edge is already speculated. */ + if (speculative && ie->speculative) + { + struct cgraph_edge *e2; + struct ipa_ref *ref; + ie->speculative_call_info (e2, ie, ref); + if (e2->callee->ultimate_alias_target () + != callee->ultimate_alias_target ()) + { + if (dump_file) + fprintf (dump_file, "ipa-prop: Discovered call to a speculative target " + "(%s/%i -> %s/%i) but the call is already speculated to %s/%i. Giving up.\n", + xstrdup (ie->caller->name ()), + ie->caller->order, + xstrdup (callee->name ()), + callee->order, + xstrdup (e2->callee->name ()), + e2->callee->order); + } + else + { + if (dump_file) + fprintf (dump_file, "ipa-prop: Discovered call to a speculative target " + "(%s/%i -> %s/%i) this agree with previous speculation.\n", + xstrdup (ie->caller->name ()), + ie->caller->order, + xstrdup (callee->name ()), + callee->order); + } + return NULL; + } + if (!dbg_cnt (devirt)) return NULL; @@ -3127,17 +3159,17 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie, ctx.offset_by (ie->indirect_info->offset); - /* TODO: We want to record if type change happens. - Old code did not do that that seems like a bug. */ - ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor, - ie->indirect_info->otr_type); + if (ie->indirect_info->vptr_changed) + ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor, + ie->indirect_info->otr_type); updated = ie->indirect_info->context.combine_with (ctx, ie->indirect_info->otr_type); } /* Try to do lookup via known virtual table pointer value. */ - if (!ie->indirect_info->by_ref) + if (!ie->indirect_info->by_ref + && (!ie->indirect_info->vptr_changed || flag_devirtualize_speculatively)) { tree vtable; unsigned HOST_WIDE_INT offset; @@ -3146,16 +3178,24 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie, true); if (t && vtable_pointer_value_to_vtable (t, &vtable, &offset)) { - target = gimple_get_virt_method_for_vtable (ie->indirect_info->otr_token, + t = gimple_get_virt_method_for_vtable (ie->indirect_info->otr_token, vtable, offset); - if (target) + if (t) { - if ((TREE_CODE (TREE_TYPE (target)) == FUNCTION_TYPE - && DECL_FUNCTION_CODE (target) == BUILT_IN_UNREACHABLE) + if ((TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE + && DECL_FUNCTION_CODE (t) == BUILT_IN_UNREACHABLE) || !possible_polymorphic_call_target_p - (ie, cgraph_node::get (target))) - target = ipa_impossible_devirt_target (ie, target); - return ipa_make_edge_direct_to_target (ie, target); + (ie, cgraph_node::get (t))) + { + /* Do not speculate builtin_unreachable, it is stpid! */ + if (!ie->indirect_info->vptr_changed) + target = ipa_impossible_devirt_target (ie, target); + } + else + { + target = t; + speculative = ie->indirect_info->vptr_changed; + } } } } @@ -3188,7 +3228,7 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie, else target = ipa_impossible_devirt_target (ie, NULL_TREE); } - else if (flag_devirtualize_speculatively + else if (!target && flag_devirtualize_speculatively && !ie->speculative && ie->maybe_hot_p ()) { cgraph_node *n = try_speculative_devirtualization (ie->indirect_info->otr_type, @@ -3222,7 +3262,11 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie, if (target) { if (!possible_polymorphic_call_target_p (ie, cgraph_node::get_create (target))) - target = ipa_impossible_devirt_target (ie, target); + { + if (!speculative) + return NULL; + target = ipa_impossible_devirt_target (ie, target); + } return ipa_make_edge_direct_to_target (ie, target, speculative); } else @@ -4801,6 +4845,7 @@ ipa_write_indirect_edge_info (struct output_block *ob, bp_pack_value (&bp, ii->agg_contents, 1); bp_pack_value (&bp, ii->member_ptr, 1); bp_pack_value (&bp, ii->by_ref, 1); + bp_pack_value (&bp, ii->vptr_changed, 1); streamer_write_bitpack (&bp); if (ii->agg_contents || ii->polymorphic) streamer_write_hwi (ob, ii->offset); @@ -4832,6 +4877,7 @@ ipa_read_indirect_edge_info (struct lto_input_block *ib, ii->agg_contents = bp_unpack_value (&bp, 1); ii->member_ptr = bp_unpack_value (&bp, 1); ii->by_ref = bp_unpack_value (&bp, 1); + ii->vptr_changed = bp_unpack_value (&bp, 1); if (ii->agg_contents || ii->polymorphic) ii->offset = (HOST_WIDE_INT) streamer_read_hwi (ib); else diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 39af7210829..ce04eacf5c0 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2014-10-04 Jan Hubicka + + * g++.dg/ipa/devirt-47.C: New testcase. + 2014-10-04 Jan Hubicka PR ipa/61144 diff --git a/gcc/testsuite/g++.dg/ipa/devirt-47.C b/gcc/testsuite/g++.dg/ipa/devirt-47.C new file mode 100644 index 00000000000..85f7b634342 --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/devirt-47.C @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fno-ipa-cp -fdump-ipa-inline-details -fno-early-inlining -fdump-tree-optimized" } */ +struct A { + virtual int foo(){return 1;} +}; +struct B { + virtual int bar(){return 4;} +}; +struct C:B,A { + virtual int foo(){return 2;} +}; +static void +test (struct A *a) +{ + if (a->foo() != 2) + __builtin_abort (); +} +int +m() +{ + struct A *a = new C; + test (a); + return 0; +} + +/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a speculative target\[^\\n\]*C::_ZTh" 1 "inline" } } */ +/* { dg-final { scan-ipa-dump-not "OBJ_TYPE_REF" "optimized" } } */ +/* FIXME: We ought to inline thunk. */ +/* { dg-final { scan-ipa-dump "C::_ZThn" "optimized" } } */ +/* { dg-final { cleanup-ipa-dump "inline" } } */ +/* { dg-final { cleanup-ipa-dump "optimized" } } */ -- 2.30.2