From df0d8136f133fe11790812caee0712f37c0ae5d9 Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Sun, 16 Nov 2014 22:01:45 +0100 Subject: [PATCH] * ipa-polymorphic-call.c (ipa_polymorphic_call_context::speculation_consistent_p): Constify. (ipa_polymorphic_call_context::meet_speculation_with): New function. (ipa_polymorphic_call_context::combine_with): Handle types in construction better. (ipa_polymorphic_call_context::equal_to): Do not bother about useless speculation. (ipa_polymorphic_call_context::meet_with): New function. * cgraph.h (class ipa_polymorphic_call_context): Add meet_width, meet_speculation_with; constify speculation_consistent_p. * ipa-cp.c (ipa_context_from_jfunc): Handle speculation; combine with incomming context. (propagate_context_accross_jump_function): Likewise; be more cureful. about set_contains_variable. (ipa_get_indirect_edge_target_1): Fix handling of dynamic type changes. (find_more_scalar_values_for_callers_subset): Fix. (find_more_contexts_for_caller_subset): Perform meet operation. From-SVN: r217634 --- gcc/ChangeLog | 20 +++ gcc/cgraph.h | 4 +- gcc/ipa-cp.c | 69 ++++---- gcc/ipa-polymorphic-call.c | 321 ++++++++++++++++++++++++++++++++++++- 4 files changed, 375 insertions(+), 39 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d035fe15e20..d385e334bf7 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,23 @@ +2014-11-16 Jan Hubicka + + * ipa-polymorphic-call.c + (ipa_polymorphic_call_context::speculation_consistent_p): Constify. + (ipa_polymorphic_call_context::meet_speculation_with): New function. + (ipa_polymorphic_call_context::combine_with): Handle types in construction + better. + (ipa_polymorphic_call_context::equal_to): Do not bother about useless + speculation. + (ipa_polymorphic_call_context::meet_with): New function. + * cgraph.h (class ipa_polymorphic_call_context): Add + meet_width, meet_speculation_with; constify speculation_consistent_p. + * ipa-cp.c (ipa_context_from_jfunc): Handle speculation; combine with incomming + context. + (propagate_context_accross_jump_function): Likewise; be more cureful. + about set_contains_variable. + (ipa_get_indirect_edge_target_1): Fix handling of dynamic type changes. + (find_more_scalar_values_for_callers_subset): Fix. + (find_more_contexts_for_caller_subset): Perform meet operation. + 2014-11-16 Jan Hubicka * passes.c (execute_one_pass): Do not apply all transforms prior diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 77c2a6058e7..fc5fe82d1be 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -1389,6 +1389,7 @@ public: If actual type the context is being used in is known, OTR_TYPE should be set accordingly. This improves quality of combined result. */ bool combine_with (ipa_polymorphic_call_context, tree otr_type = NULL); + bool meet_with (ipa_polymorphic_call_context, tree otr_type = NULL); /* Return TRUE if context is fully useless. */ bool useless_p () const; @@ -1406,9 +1407,10 @@ public: private: bool combine_speculation_with (tree, HOST_WIDE_INT, bool, tree); + bool meet_speculation_with (tree, HOST_WIDE_INT, bool, tree); void set_by_decl (tree, HOST_WIDE_INT); bool set_by_invariant (tree, tree, HOST_WIDE_INT); - bool speculation_consistent_p (tree, HOST_WIDE_INT, bool, tree); + bool speculation_consistent_p (tree, HOST_WIDE_INT, bool, tree) const; void make_speculative (tree otr_type = NULL); }; diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index 0529f174615..375b49e5f91 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -946,17 +946,17 @@ ipa_context_from_jfunc (ipa_node_params *info, cgraph_edge *cs, int csidx, { ipa_polymorphic_call_context srcctx; int srcidx; + bool type_preserved = true; if (jfunc->type == IPA_JF_PASS_THROUGH) { - if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR - || !ipa_get_jf_pass_through_type_preserved (jfunc)) + if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR) return ctx; + type_preserved = ipa_get_jf_pass_through_type_preserved (jfunc); srcidx = ipa_get_jf_pass_through_formal_id (jfunc); } else { - if (!ipa_get_jf_ancestor_type_preserved (jfunc)) - return ctx; + type_preserved = ipa_get_jf_ancestor_type_preserved (jfunc); srcidx = ipa_get_jf_ancestor_formal_id (jfunc); } if (info->ipcp_orig_node) @@ -981,7 +981,10 @@ ipa_context_from_jfunc (ipa_node_params *info, cgraph_edge *cs, int csidx, return ctx; if (jfunc->type == IPA_JF_ANCESTOR) srcctx.offset_by (ipa_get_jf_ancestor_offset (jfunc)); - ctx.combine_with (srcctx); + if (!type_preserved) + srcctx.possible_dynamic_type_change (cs->in_polymorphic_cdtor); + srcctx.combine_with (ctx); + return srcctx; } return ctx; @@ -1298,15 +1301,13 @@ propagate_context_accross_jump_function (cgraph_edge *cs, return false; bool ret = false; bool added_sth = false; + bool type_preserved = true; ipa_polymorphic_call_context edge_ctx, *edge_ctx_ptr = ipa_get_ith_polymorhic_call_context (args, idx); if (edge_ctx_ptr) - { - edge_ctx = *edge_ctx_ptr; - edge_ctx.clear_speculation (); - } + edge_ctx = *edge_ctx_ptr; if (jfunc->type == IPA_JF_PASS_THROUGH || jfunc->type == IPA_JF_ANCESTOR) @@ -1320,15 +1321,14 @@ propagate_context_accross_jump_function (cgraph_edge *cs, not set instead of punting. */ if (jfunc->type == IPA_JF_PASS_THROUGH) { - if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR - || !ipa_get_jf_pass_through_type_preserved (jfunc)) + if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR) goto prop_fail; + type_preserved = ipa_get_jf_pass_through_type_preserved (jfunc); src_idx = ipa_get_jf_pass_through_formal_id (jfunc); } else { - if (!ipa_get_jf_ancestor_type_preserved (jfunc)) - goto prop_fail; + type_preserved = ipa_get_jf_ancestor_type_preserved (jfunc); src_idx = ipa_get_jf_ancestor_formal_id (jfunc); } @@ -1338,21 +1338,24 @@ propagate_context_accross_jump_function (cgraph_edge *cs, && (src_lat->contains_variable || (src_lat->values_count > 1))) goto prop_fail; - if (src_lat->contains_variable) - ret |= dest_lat->set_contains_variable (); ipcp_value *src_val; for (src_val = src_lat->values; src_val; src_val = src_val->next) { ipa_polymorphic_call_context cur = src_val->value; + + if (!type_preserved) + cur.possible_dynamic_type_change (cs->in_polymorphic_cdtor); if (jfunc->type == IPA_JF_ANCESTOR) cur.offset_by (ipa_get_jf_ancestor_offset (jfunc)); - /* TODO: Perhaps attempt to look up some used OTR type? */ - cur.clear_speculation (); - if (!edge_ctx.useless_p ()) - cur.combine_with (edge_ctx); + /* TODO: In cases we know how the context is going to be used, + we can improve the result by passing proper OTR_TYPE. */ + cur.combine_with (edge_ctx); if (!cur.useless_p ()) { + if (src_lat->contains_variable + && !edge_ctx.equal_to (cur)) + ret |= dest_lat->set_contains_variable (); ret |= dest_lat->add_value (cur, cs, src_val, src_idx); added_sth = true; } @@ -1848,6 +1851,10 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, if (known_contexts.length () > (unsigned int) param_index) { context = known_contexts[param_index]; + context.offset_by (anc_offset); + if (ie->indirect_info->vptr_changed) + context.possible_dynamic_type_change (ie->in_polymorphic_cdtor, + ie->indirect_info->otr_type); if (t) { ipa_polymorphic_call_context ctx2 = ipa_polymorphic_call_context @@ -3160,6 +3167,7 @@ find_more_scalar_values_for_callers_subset (struct cgraph_node *node, struct cgraph_edge *cs; tree newval = NULL_TREE; int j; + bool first = true; if (ipa_get_scalar_lat (info, i)->bottom || known_csts[i]) continue; @@ -3178,13 +3186,15 @@ find_more_scalar_values_for_callers_subset (struct cgraph_node *node, t = ipa_value_from_jfunc (IPA_NODE_REF (cs->caller), jump_func); if (!t || (newval - && !values_equal_for_ipcp_p (t, newval))) + && !values_equal_for_ipcp_p (t, newval)) + || (!first && !newval)) { newval = NULL_TREE; break; } else newval = t; + first = false; } if (newval) @@ -3226,7 +3236,7 @@ find_more_contexts_for_caller_subset (cgraph_node *node, continue; ipa_polymorphic_call_context newval; - bool found = false; + bool first = true; int j; FOR_EACH_VEC_ELT (callers, j, cs) @@ -3238,21 +3248,18 @@ find_more_contexts_for_caller_subset (cgraph_node *node, ipa_polymorphic_call_context ctx; ctx = ipa_context_from_jfunc (IPA_NODE_REF (cs->caller), cs, i, jfunc); - ctx.clear_speculation (); - if (ctx.useless_p () - || (found && !values_equal_for_ipcp_p (newval, ctx))) - { - found = false; - break; - } - else if (!found) + if (first) { - found = true; newval = ctx; + first = false; } + else + newval.meet_with (ctx); + if (newval.useless_p ()) + break; } - if (found) + if (!newval.useless_p ()) { if (dump_file && (dump_flags & TDF_DETAILS)) { diff --git a/gcc/ipa-polymorphic-call.c b/gcc/ipa-polymorphic-call.c index f1905f1609a..452f2d26a5a 100644 --- a/gcc/ipa-polymorphic-call.c +++ b/gcc/ipa-polymorphic-call.c @@ -1727,7 +1727,7 @@ bool ipa_polymorphic_call_context::speculation_consistent_p (tree spec_outer_type, HOST_WIDE_INT spec_offset, bool spec_maybe_derived_type, - tree otr_type) + tree otr_type) const { if (!flag_devirtualize_speculatively) return false; @@ -1873,6 +1873,102 @@ ipa_polymorphic_call_context::combine_speculation_with return false; } +/* Make speculation less specific so + NEW_OUTER_TYPE, NEW_OFFSET, NEW_MAYBE_DERIVED_TYPE is also included. + If OTR_TYPE is set, assume the context is used with OTR_TYPE. */ + +bool +ipa_polymorphic_call_context::meet_speculation_with + (tree new_outer_type, HOST_WIDE_INT new_offset, bool new_maybe_derived_type, + tree otr_type) +{ + if (!new_outer_type && speculative_outer_type) + { + clear_speculation (); + return true; + } + + /* restrict_to_inner_class may eliminate wrong speculation making our job + easeier. */ + if (otr_type) + restrict_to_inner_class (otr_type); + + if (!speculative_outer_type + || !speculation_consistent_p (speculative_outer_type, + speculative_offset, + speculative_maybe_derived_type, + otr_type)) + return false; + + if (!speculation_consistent_p (new_outer_type, new_offset, + new_maybe_derived_type, otr_type)) + { + clear_speculation (); + return true; + } + + else if (types_must_be_same_for_odr (speculative_outer_type, + new_outer_type)) + { + if (speculative_offset != new_offset) + { + clear_speculation (); + return true; + } + else + { + if (!speculative_maybe_derived_type && new_maybe_derived_type) + { + speculative_maybe_derived_type = true; + return true; + } + else + return false; + } + } + /* See if one type contains the other as a field (not base). */ + else if (contains_type_p (new_outer_type, new_offset - speculative_offset, + speculative_outer_type, false, false)) + return false; + else if (contains_type_p (speculative_outer_type, + speculative_offset - new_offset, + new_outer_type, false, false)) + { + speculative_outer_type = new_outer_type; + speculative_offset = new_offset; + speculative_maybe_derived_type = new_maybe_derived_type; + return true; + } + /* See if OUTER_TYPE is base of CTX.OUTER_TYPE. */ + else if (contains_type_p (new_outer_type, + new_offset - speculative_offset, + speculative_outer_type, false, true)) + { + if (!speculative_maybe_derived_type) + { + speculative_maybe_derived_type = true; + return true; + } + return false; + } + /* See if CTX.OUTER_TYPE is base of OUTER_TYPE. */ + else if (contains_type_p (speculative_outer_type, + speculative_offset - new_offset, new_outer_type, false, true)) + { + speculative_outer_type = new_outer_type; + speculative_offset = new_offset; + speculative_maybe_derived_type = true; + return true; + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Giving up on speculative meet\n"); + clear_speculation (); + return true; + } +} + /* Assume that both THIS and a given context is valid and strenghten THIS if possible. Return true if any strenghtening was made. If actual type the context is being used in is known, OTR_TYPE should be @@ -2065,6 +2161,16 @@ ipa_polymorphic_call_context::combine_with (ipa_polymorphic_call_context ctx, fprintf (dump_file, "First context does not permit base -> invalid\n"); goto invalidate; } + /* Pick the base type. */ + else if (maybe_in_construction) + { + outer_type = ctx.outer_type; + maybe_in_construction = ctx.maybe_in_construction; + maybe_derived_type = ctx.maybe_derived_type; + offset = ctx.offset; + dynamic = ctx.dynamic; + updated = true; + } } } /* TODO handle merging using hiearchy. */ @@ -2160,19 +2266,220 @@ ipa_polymorphic_call_context::equal_to else if (x.outer_type) return false; - if (speculative_outer_type) + + if (speculative_outer_type + && speculation_consistent_p (speculative_outer_type, speculative_offset, + speculative_maybe_derived_type, NULL_TREE)) { - if (!x.speculative_outer_type - || !types_odr_comparable (speculative_outer_type, - x.speculative_outer_type) + if (!x.speculative_outer_type) + return false; + + if (!types_odr_comparable (speculative_outer_type, + x.speculative_outer_type) || !types_same_for_odr (speculative_outer_type, - x.speculative_outer_type) + x.speculative_outer_type) || speculative_offset != x.speculative_offset || speculative_maybe_derived_type != x.speculative_maybe_derived_type) return false; } - else if (x.speculative_outer_type) + else if (x.speculative_outer_type + && x.speculation_consistent_p (x.speculative_outer_type, + x.speculative_offset, + x.speculative_maybe_derived_type, + NULL)) return false; return true; } + +/* Modify context to be strictly less restrictive than CTX. */ + +bool +ipa_polymorphic_call_context::meet_with (ipa_polymorphic_call_context ctx, + tree otr_type) +{ + bool updated = false; + + if (useless_p () || ctx.invalid) + return false; + + /* Restricting context to inner type makes merging easier, however do not + do that unless we know how the context is used (OTR_TYPE is non-NULL) */ + if (otr_type && !useless_p () && !ctx.useless_p ()) + { + restrict_to_inner_class (otr_type); + ctx.restrict_to_inner_class (otr_type); + if(invalid) + return false; + } + + if (equal_to (ctx)) + return false; + + if (ctx.useless_p () || invalid) + { + *this = ctx; + return true; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Polymorphic call context meet:"); + dump (dump_file); + fprintf (dump_file, "With context: "); + ctx.dump (dump_file); + if (otr_type) + { + fprintf (dump_file, "To be used with type: "); + print_generic_expr (dump_file, otr_type, TDF_SLIM); + fprintf (dump_file, "\n"); + } + } + + if (!dynamic && ctx.dynamic) + { + dynamic = true; + updated = true; + } + + /* If call is known to be invalid, we are done. */ + if (!outer_type) + ; + else if (!ctx.outer_type) + { + clear_outer_type (); + updated = true; + } + /* If types are known to be same, merging is quite easy. */ + else if (types_must_be_same_for_odr (outer_type, ctx.outer_type)) + { + if (offset != ctx.offset + && TYPE_SIZE (outer_type) + && TREE_CODE (TYPE_SIZE (outer_type)) == INTEGER_CST) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Outer types match, offset mismatch -> clearing\n"); + clear_outer_type (); + return true; + } + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Outer types match, merging flags\n"); + if (!maybe_in_construction && ctx.maybe_in_construction) + { + updated = true; + maybe_in_construction = true; + } + if (!maybe_derived_type && ctx.maybe_derived_type) + { + updated = true; + maybe_derived_type = true; + } + if (!dynamic && ctx.dynamic) + { + updated = true; + dynamic = true; + } + } + /* See if one type contains the other as a field (not base). */ + else if (contains_type_p (ctx.outer_type, ctx.offset - offset, + outer_type, false, false)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Second type contain the first as a field\n"); + + /* The second type is more specified, so we keep the first. + We need to set DYNAMIC flag to avoid declaring context INVALID + of OFFSET ends up being out of range. */ + if (!dynamic + && (ctx.dynamic + || (!otr_type + && (!TYPE_SIZE (ctx.outer_type) + || !TYPE_SIZE (outer_type) + || !operand_equal_p (TYPE_SIZE (ctx.outer_type), + TYPE_SIZE (outer_type), 0))))) + { + dynamic = true; + updated = true; + } + } + else if (contains_type_p (outer_type, offset - ctx.offset, + ctx.outer_type, false, false)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "First type contain the second as a field\n"); + + if (!dynamic + && (ctx.dynamic + || (!otr_type + && (!TYPE_SIZE (ctx.outer_type) + || !TYPE_SIZE (outer_type) + || !operand_equal_p (TYPE_SIZE (ctx.outer_type), + TYPE_SIZE (outer_type), 0))))) + dynamic = true; + outer_type = ctx.outer_type; + offset = ctx.offset; + dynamic = ctx.dynamic; + maybe_in_construction = ctx.maybe_in_construction; + maybe_derived_type = ctx.maybe_derived_type; + updated = true; + } + /* See if OUTER_TYPE is base of CTX.OUTER_TYPE. */ + else if (contains_type_p (ctx.outer_type, + ctx.offset - offset, outer_type, false, true)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "First type is base of second\n"); + if (!maybe_derived_type) + { + maybe_derived_type = true; + updated = true; + } + if (!maybe_in_construction && ctx.maybe_in_construction) + { + maybe_in_construction = true; + updated = true; + } + if (!dynamic && ctx.dynamic) + { + dynamic = true; + updated = true; + } + } + /* See if CTX.OUTER_TYPE is base of OUTER_TYPE. */ + else if (contains_type_p (outer_type, + offset - ctx.offset, ctx.outer_type, false, true)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Second type is base of first\n"); + outer_type = ctx.outer_type; + offset = ctx.offset; + updated = true; + if (!maybe_derived_type) + maybe_derived_type = true; + if (!maybe_in_construction && ctx.maybe_in_construction) + maybe_in_construction = true; + if (!dynamic && ctx.dynamic) + dynamic = true; + } + /* TODO handle merging using hiearchy. */ + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Giving up on meet\n"); + clear_outer_type (); + updated = true; + } + + updated |= meet_speculation_with (ctx.speculative_outer_type, + ctx.speculative_offset, + ctx.speculative_maybe_derived_type, + otr_type); + + if (updated && dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Updated as: "); + dump (dump_file); + fprintf (dump_file, "\n"); + } + return updated; +} -- 2.30.2