From 44210a9672aed3212c0e289dddc5bd38c314290b Mon Sep 17 00:00:00 2001 From: Martin Jambor Date: Fri, 14 Nov 2014 21:03:19 +0100 Subject: [PATCH] ipa-prop.h (ipa_get_jf_pass_through_type_preserved): use agg_preserved flag instead. 2014-11-14 Martin Jambor * ipa-prop.h (ipa_get_jf_pass_through_type_preserved): use agg_preserved flag instead. (ipa_get_jf_ancestor_type_preserved): Likewise. (ipa_node_params): Rename known_vals to known_csts, update all users. New field known_contexts. (ipa_get_indirect_edge_target): Update prototype. (ipcp_poly_ctx_values_pool): Declare. (ipa_context_from_jfunc): Likewise. * ipa-inline.h (estimate_ipcp_clone_size_and_time): Updated prototype. * cgraph.h (ipa_polymorphic_call_context): New method equal_to. New parameter newline of method dump. * ipa-cp.c (ctxlat): New field. (ipcp_values_pool): Renamed to ipcp_cst_values_pool, updated all users. (ipcp_poly_ctx_values_pool):New variable. (ipa_get_poly_ctx_lat): New function. (print_ipcp_constant_value): New overloaded function for contexts. (print_all_lattices): Also print contexts. (ipa_topo_info): New field contexts; (set_all_contains_variable): Also set the flag in the context lattice. (initialize_node_lattices): Likewise for flag bottom. (ipa_get_jf_ancestor_result): Removed BINFO handling. (ipa_value_from_jfunc): Likewise. (ipa_context_from_jfunc): New function. (values_equal_for_ipcp_p): New overloaded function for contexts. (allocate_and_init_ipcp_value): Construct the value. (allocate_and_init_ipcp_value): New overloaded function for contexts. (propagate_scalar_accross_jump_function): Removed handling of KNOWN_TYPE jump functions. (propagate_context_accross_jump_function): New function. (propagate_constants_accross_call): Also propagate contexts. (ipa_get_indirect_edge_target_1): Work on contexts rather than BINFOs. (ipa_get_indirect_edge_target): Likewise. (devirtualization_time_bonus): Likewise. (gather_context_independent_values): Create and populate known_contexts vector rather than known_binfos. (perform_estimation_of_a_value): Work on contexts rather than BINFOs. (estimate_local_effects): Likewise. (add_all_node_vals_to_toposort): Also add contexts to teir topological sort. (ipcp_propagate_stage): Also propagate effects of contexts. (ipcp_discover_new_direct_edges): Receive and pass known_contexts to ipa_get_indirect_edge_target_1. (cgraph_edge_brings_value_p): New overloaded function for contexts. (create_specialized_node): Work on contexts rather than BINFOs. (find_more_contexts_for_caller_subset): New function. (known_contexts_useful_p): New function. (copy_useful_known_contexts): Likewise. (modify_known_vectors_with_val): Likewise. (ipcp_val_in_agg_replacements_p): Renamed to ipcp_val_agg_replacement_ok_p, return true for all offset indicating non-aggregate. (ipcp_val_agg_replacement_ok_p): New overloaded function for contexts. (decide_about_value): Work on contexts rather than BINFOs. (decide_whether_version_node): Likewise. (ipcp_driver): Initialize the new alloc pool. * ipa-prop.c (ipa_print_node_jump_functions_for_edge): Prettify printing of edge contexts. (ipa_set_ancestor_jf): Replace assert with conditional setting of type_preserved to false. (update_jump_functions_after_inlining): Use access function instead of reading agg_preserved directly. Store combined context in the ancestor case. (try_make_edge_direct_virtual_call): Work on contexts rather than BINFOs. (update_indirect_edges_after_inlining): Get context from ipa_context_from_jfunc. (ipa_free_node_params_substructures): Free also known_contexts. (ipa_free_all_structures_after_ipa_cp): Free the new alloc pool. (ipa_free_all_structures_after_iinln): Likewise. * ipa-inline-analysis.c (evaluate_properties_for_edge): Work on contexts rather than BINFOs. (estimate_edge_devirt_benefit): Likewise. (estimate_edge_size_and_time): Likewise. (estimate_calls_size_and_time): Likewise. (estimate_node_size_and_time): Likewise. (estimate_ipcp_clone_size_and_time): Likewise. (do_estimate_edge_time): Likewise. (do_estimate_edge_size): Likewise. (do_estimate_edge_hints): Likewise. * ipa-polymorphic-call.c (ipa_polymorphic_call_context::dump): New parameter newline, ouput newline only when it is set. (ipa_polymorphic_call_context::equal_to): New method. testsuite/ * g++.dg/ipa/devirt-11.C: Dont't run ipa-cp, remove times constraint from the dump scan. * g++.dg/ipa/devirt-21.C: Xfail. * g++.dg/ipa/devirt-24.C: Likewise. * g++.dg/ipa/devirt-10.C: Removed times constraint from the dump scan. * g++.dg/ipa/devirt-41.C: Updated the dump scan. * g++.dg/ipa/devirt-44.C: Likewise. * g++.dg/ipa/devirt-43.C: Xfail. From-SVN: r217587 --- gcc/ChangeLog | 85 +++ gcc/cgraph.h | 7 +- gcc/ipa-cp.c | 821 ++++++++++++++++++++------- gcc/ipa-inline-analysis.c | 89 +-- gcc/ipa-inline.h | 3 +- gcc/ipa-polymorphic-call.c | 52 +- gcc/ipa-prop.c | 163 +++--- gcc/ipa-prop.h | 27 +- gcc/testsuite/ChangeLog | 11 + gcc/testsuite/g++.dg/ipa/devirt-10.C | 4 +- gcc/testsuite/g++.dg/ipa/devirt-11.C | 6 +- gcc/testsuite/g++.dg/ipa/devirt-21.C | 2 +- gcc/testsuite/g++.dg/ipa/devirt-24.C | 2 +- gcc/testsuite/g++.dg/ipa/devirt-41.C | 2 +- gcc/testsuite/g++.dg/ipa/devirt-43.C | 2 +- gcc/testsuite/g++.dg/ipa/devirt-44.C | 2 +- 16 files changed, 898 insertions(+), 380 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 421a8bcc33d..c42a532a222 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,88 @@ +2014-11-14 Martin Jambor + + * ipa-prop.h (ipa_get_jf_pass_through_type_preserved): use + agg_preserved flag instead. + (ipa_get_jf_ancestor_type_preserved): Likewise. + (ipa_node_params): Rename known_vals to known_csts, update all users. + New field known_contexts. + (ipa_get_indirect_edge_target): Update prototype. + (ipcp_poly_ctx_values_pool): Declare. + (ipa_context_from_jfunc): Likewise. + * ipa-inline.h (estimate_ipcp_clone_size_and_time): Updated prototype. + * cgraph.h (ipa_polymorphic_call_context): New method equal_to. New + parameter newline of method dump. + * ipa-cp.c (ctxlat): New field. + (ipcp_values_pool): Renamed to ipcp_cst_values_pool, updated all users. + (ipcp_poly_ctx_values_pool):New variable. + (ipa_get_poly_ctx_lat): New function. + (print_ipcp_constant_value): New overloaded function for contexts. + (print_all_lattices): Also print contexts. + (ipa_topo_info): New field contexts; + (set_all_contains_variable): Also set the flag in the context lattice. + (initialize_node_lattices): Likewise for flag bottom. + (ipa_get_jf_ancestor_result): Removed BINFO handling. + (ipa_value_from_jfunc): Likewise. + (ipa_context_from_jfunc): New function. + (values_equal_for_ipcp_p): New overloaded function for contexts. + (allocate_and_init_ipcp_value): Construct the value. + (allocate_and_init_ipcp_value): New overloaded function for contexts. + (propagate_scalar_accross_jump_function): Removed handling of + KNOWN_TYPE jump functions. + (propagate_context_accross_jump_function): New function. + (propagate_constants_accross_call): Also propagate contexts. + (ipa_get_indirect_edge_target_1): Work on contexts rather than BINFOs. + (ipa_get_indirect_edge_target): Likewise. + (devirtualization_time_bonus): Likewise. + (gather_context_independent_values): Create and populate known_contexts + vector rather than known_binfos. + (perform_estimation_of_a_value): Work on contexts rather than BINFOs. + (estimate_local_effects): Likewise. + (add_all_node_vals_to_toposort): Also add contexts to teir topological + sort. + (ipcp_propagate_stage): Also propagate effects of contexts. + (ipcp_discover_new_direct_edges): Receive and pass known_contexts to + ipa_get_indirect_edge_target_1. + (cgraph_edge_brings_value_p): New overloaded function for contexts. + (create_specialized_node): Work on contexts rather than BINFOs. + (find_more_contexts_for_caller_subset): New function. + (known_contexts_useful_p): New function. + (copy_useful_known_contexts): Likewise. + (modify_known_vectors_with_val): Likewise. + (ipcp_val_in_agg_replacements_p): Renamed to + ipcp_val_agg_replacement_ok_p, return true for all offset indicating + non-aggregate. + (ipcp_val_agg_replacement_ok_p): New overloaded function for contexts. + (decide_about_value): Work on contexts rather than BINFOs. + (decide_whether_version_node): Likewise. + (ipcp_driver): Initialize the new alloc pool. + * ipa-prop.c (ipa_print_node_jump_functions_for_edge): Prettify + printing of edge contexts. + (ipa_set_ancestor_jf): Replace assert with conditional setting of + type_preserved to false. + (update_jump_functions_after_inlining): Use access function instead of + reading agg_preserved directly. Store combined context in the ancestor + case. + (try_make_edge_direct_virtual_call): Work on contexts rather than + BINFOs. + (update_indirect_edges_after_inlining): Get context from + ipa_context_from_jfunc. + (ipa_free_node_params_substructures): Free also known_contexts. + (ipa_free_all_structures_after_ipa_cp): Free the new alloc pool. + (ipa_free_all_structures_after_iinln): Likewise. + * ipa-inline-analysis.c (evaluate_properties_for_edge): Work on + contexts rather than BINFOs. + (estimate_edge_devirt_benefit): Likewise. + (estimate_edge_size_and_time): Likewise. + (estimate_calls_size_and_time): Likewise. + (estimate_node_size_and_time): Likewise. + (estimate_ipcp_clone_size_and_time): Likewise. + (do_estimate_edge_time): Likewise. + (do_estimate_edge_size): Likewise. + (do_estimate_edge_hints): Likewise. + * ipa-polymorphic-call.c (ipa_polymorphic_call_context::dump): New + parameter newline, ouput newline only when it is set. + (ipa_polymorphic_call_context::equal_to): New method. + 2014-11-14 Martin Jambor * ipa-cp.c (ipcp_value_source): Converted to a template class. All diff --git a/gcc/cgraph.h b/gcc/cgraph.h index e9a14c4d492..5d949abc722 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -1387,9 +1387,12 @@ public: /* Return TRUE if context is fully useless. */ bool useless_p () const; + /* Return TRUE if this context conveys the same information as X. */ + bool equal_to (const ipa_polymorphic_call_context &x) const; - /* Dump human readable context to F. */ - void dump (FILE *f) const; + /* Dump human readable context to F. If NEWLINE is true, it will be + terminated by a newline. */ + void dump (FILE *f, bool newline = true) const; void DEBUG_FUNCTION debug () const; /* LTO streaming. */ diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index e0acfaa82b0..4f44c4ce610 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -258,6 +258,8 @@ class ipcp_param_lattices public: /* Lattice describing the value of the parameter itself. */ ipcp_lattice itself; + /* Lattice describing the the polymorphic contexts of a parameter. */ + ipcp_lattice ctxlat; /* Lattices describing aggregate parts. */ ipcp_agg_lattice *aggs; /* Number of aggregate lattices */ @@ -278,7 +280,8 @@ public: /* Allocation pools for values and their sources in ipa-cp. */ -alloc_pool ipcp_values_pool; +alloc_pool ipcp_cst_values_pool; +alloc_pool ipcp_poly_ctx_values_pool; alloc_pool ipcp_sources_pool; alloc_pool ipcp_agg_lattice_pool; @@ -310,6 +313,15 @@ ipa_get_scalar_lat (struct ipa_node_params *info, int i) return &plats->itself; } +/* Return the lattice corresponding to the scalar value of the Ith formal + parameter of the function described by INFO. */ +static inline ipcp_lattice * +ipa_get_poly_ctx_lat (struct ipa_node_params *info, int i) +{ + struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i); + return &plats->ctxlat; +} + /* Return whether LAT is a lattice with a single constant and without an undefined value. */ @@ -343,6 +355,14 @@ print_ipcp_constant_value (FILE * f, tree v) print_generic_expr (f, v, 0); } +/* Print V which is extracted from a value in a lattice to F. */ + +static void +print_ipcp_constant_value (FILE * f, ipa_polymorphic_call_context v) +{ + v.dump(f, false); +} + /* Print a lattice LAT to F. */ template @@ -427,7 +447,8 @@ print_all_lattices (FILE * f, bool dump_sources, bool dump_benefits) struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i); fprintf (f, " param [%d]: ", i); plats->itself.print (f, dump_sources, dump_benefits); - + fprintf (f, " ctxs: "); + plats->ctxlat.print (f, dump_sources, dump_benefits); if (plats->virt_call) fprintf (f, " virt_call flag set\n"); @@ -639,6 +660,7 @@ public: int nnodes, stack_top; value_topo_info constants; + value_topo_info contexts; ipa_topo_info () : order(NULL), stack(NULL), nnodes(0), stack_top(0), constants () @@ -750,9 +772,10 @@ set_agg_lats_contain_variable (struct ipcp_param_lattices *plats) static inline bool set_all_contains_variable (struct ipcp_param_lattices *plats) { - bool ret = !plats->itself.contains_variable || !plats->aggs_contain_variable; - plats->itself.contains_variable = true; - plats->aggs_contain_variable = true; + bool ret; + ret = plats->itself.set_contains_variable (); + ret |= plats->ctxlat.set_contains_variable (); + ret |= set_agg_lats_contain_variable (plats); return ret; } @@ -787,6 +810,7 @@ initialize_node_lattices (struct cgraph_node *node) if (disable) { plats->itself.set_to_bottom (); + plats->ctxlat.set_to_bottom (); set_agg_lats_to_bottom (plats); } else @@ -853,28 +877,8 @@ ipa_get_jf_pass_through_result (struct ipa_jump_func *jfunc, tree input) static tree ipa_get_jf_ancestor_result (struct ipa_jump_func *jfunc, tree input) { - if (TREE_CODE (input) == TREE_BINFO) - { - if (!ipa_get_jf_ancestor_type_preserved (jfunc)) - return NULL; - /* FIXME: At LTO we can't propagate to non-polymorphic type, because - we have no ODR equivalency on those. This should be fixed by - propagating on types rather than binfos that would make type - matching here unnecesary. */ - if (in_lto_p - && (TREE_CODE (ipa_get_jf_ancestor_type (jfunc)) != RECORD_TYPE - || !TYPE_BINFO (ipa_get_jf_ancestor_type (jfunc)) - || !BINFO_VTABLE (TYPE_BINFO (ipa_get_jf_ancestor_type (jfunc))))) - { - if (!ipa_get_jf_ancestor_offset (jfunc)) - return input; - return NULL; - } - return get_binfo_at_offset (input, - ipa_get_jf_ancestor_offset (jfunc), - ipa_get_jf_ancestor_type (jfunc)); - } - else if (TREE_CODE (input) == ADDR_EXPR) + gcc_checking_assert (TREE_CODE (input) != TREE_BINFO); + if (TREE_CODE (input) == ADDR_EXPR) { tree t = TREE_OPERAND (input, 0); t = build_ref_for_offset (EXPR_LOCATION (t), t, @@ -888,9 +892,9 @@ ipa_get_jf_ancestor_result (struct ipa_jump_func *jfunc, tree input) return NULL_TREE; } -/* Determine whether JFUNC evaluates to a known value (that is either a - constant or a binfo) and if so, return it. Otherwise return NULL. INFO - describes the caller node so that pass-through jump functions can be +/* Determine whether JFUNC evaluates to a single known constant value and if + so, return it. Otherwise return NULL. INFO describes the caller node or + the one it is inlined to, so that pass-through jump functions can be evaluated. */ tree @@ -898,8 +902,6 @@ ipa_value_from_jfunc (struct ipa_node_params *info, struct ipa_jump_func *jfunc) { if (jfunc->type == IPA_JF_CONST) return ipa_get_jf_constant (jfunc); - else if (jfunc->type == IPA_JF_KNOWN_TYPE) - return ipa_binfo_from_known_type_jfunc (jfunc); else if (jfunc->type == IPA_JF_PASS_THROUGH || jfunc->type == IPA_JF_ANCESTOR) { @@ -912,7 +914,7 @@ ipa_value_from_jfunc (struct ipa_node_params *info, struct ipa_jump_func *jfunc) idx = ipa_get_jf_ancestor_formal_id (jfunc); if (info->ipcp_orig_node) - input = info->known_vals[idx]; + input = info->known_csts[idx]; else { ipcp_lattice *lat; @@ -940,6 +942,68 @@ ipa_value_from_jfunc (struct ipa_node_params *info, struct ipa_jump_func *jfunc) return NULL_TREE; } +/* Determie whether JFUNC evaluates to single known polymorphic context, given + that INFO describes the caller node or the one it is inlined to, CS is the + call graph edge corresponding to JFUNC and CSIDX index of the described + parameter. */ + +ipa_polymorphic_call_context +ipa_context_from_jfunc (ipa_node_params *info, cgraph_edge *cs, int csidx, + ipa_jump_func *jfunc) +{ + ipa_edge_args *args = IPA_EDGE_REF (cs); + ipa_polymorphic_call_context ctx; + ipa_polymorphic_call_context *edge_ctx + = cs ? ipa_get_ith_polymorhic_call_context (args, csidx) : NULL; + + if (edge_ctx && !edge_ctx->useless_p ()) + ctx = *edge_ctx; + + if (jfunc->type == IPA_JF_PASS_THROUGH + || jfunc->type == IPA_JF_ANCESTOR) + { + ipa_polymorphic_call_context srcctx; + int srcidx; + 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)) + return ctx; + srcidx = ipa_get_jf_pass_through_formal_id (jfunc); + } + else + { + if (!ipa_get_jf_ancestor_type_preserved (jfunc)) + return ctx; + srcidx = ipa_get_jf_ancestor_formal_id (jfunc); + } + if (info->ipcp_orig_node) + { + if (info->known_contexts.exists ()) + srcctx = info->known_contexts[srcidx]; + } + else + { + if (!info->lattices) + { + gcc_checking_assert (!flag_ipa_cp); + return ctx; + } + ipcp_lattice *lat; + lat = ipa_get_poly_ctx_lat (info, srcidx); + if (!lat->is_single_const ()) + return ctx; + srcctx = lat->values->value; + } + if (srcctx.useless_p ()) + return ctx; + if (jfunc->type == IPA_JF_ANCESTOR) + srcctx.offset_by (ipa_get_jf_ancestor_offset (jfunc)); + ctx.combine_with (srcctx); + } + + return ctx; +} /* If checking is enabled, verify that no lattice is in the TOP state, i.e. not bottom, not containing a variable component and without any known value at @@ -1000,6 +1064,16 @@ values_equal_for_ipcp_p (tree x, tree y) return operand_equal_p (x, y, 0); } +/* Return true iff X and Y should be considered equal contexts by IPA-CP. */ + +static bool +values_equal_for_ipcp_p (ipa_polymorphic_call_context x, + ipa_polymorphic_call_context y) +{ + return x.equal_to (y); +} + + /* Add a new value source to the value represented by THIS, marking that a value comes from edge CS and (if the underlying jump function is a pass-through or an ancestor one) from a caller value SRC_VAL of a caller @@ -1031,7 +1105,22 @@ allocate_and_init_ipcp_value (tree source) { ipcp_value *val; - val = new (pool_alloc (ipcp_values_pool)) ipcp_value; + val = new (pool_alloc (ipcp_cst_values_pool)) ipcp_value; + memset (val, 0, sizeof (*val)); + val->value = source; + return val; +} + +/* Allocate a new ipcp_value holding a polymorphic context, initialize its + value to SOURCE and clear all other fields. */ + +static ipcp_value * +allocate_and_init_ipcp_value (ipa_polymorphic_call_context source) +{ + ipcp_value *val; + + val = new (pool_alloc (ipcp_poly_ctx_values_pool)) + ipcp_value; memset (val, 0, sizeof (*val)); val->value = source; return val; @@ -1171,19 +1260,9 @@ propagate_scalar_accross_jump_function (struct cgraph_edge *cs, if (dest_lat->bottom) return false; - if (jfunc->type == IPA_JF_CONST - || jfunc->type == IPA_JF_KNOWN_TYPE) + if (jfunc->type == IPA_JF_CONST) { - tree val; - - if (jfunc->type == IPA_JF_KNOWN_TYPE) - { - val = ipa_binfo_from_known_type_jfunc (jfunc); - if (!val) - return dest_lat->set_contains_variable (); - } - else - val = ipa_get_jf_constant (jfunc); + tree val = ipa_get_jf_constant (jfunc); return dest_lat->add_value (val, cs, NULL, 0); } else if (jfunc->type == IPA_JF_PASS_THROUGH @@ -1227,6 +1306,93 @@ propagate_scalar_accross_jump_function (struct cgraph_edge *cs, return dest_lat->set_contains_variable (); } +/* Propagate scalar values across jump function JFUNC that is associated with + edge CS and describes argument IDX and put the values into DEST_LAT. */ + +static bool +propagate_context_accross_jump_function (cgraph_edge *cs, + ipa_jump_func *jfunc, int idx, + ipcp_lattice *dest_lat) +{ + ipa_edge_args *args = IPA_EDGE_REF (cs); + if (dest_lat->bottom) + return false; + bool ret = false; + bool added_sth = false; + + 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 (); + } + + if (jfunc->type == IPA_JF_PASS_THROUGH + || jfunc->type == IPA_JF_ANCESTOR) + { + struct ipa_node_params *caller_info = IPA_NODE_REF (cs->caller); + int src_idx; + ipcp_lattice *src_lat; + + /* TODO: Once we figure out how to propagate speculations, it will + probably be a good idea to switch to speculation if type_preserved is + 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)) + goto prop_fail; + src_idx = ipa_get_jf_pass_through_formal_id (jfunc); + } + else + { + if (!ipa_get_jf_ancestor_type_preserved (jfunc)) + goto prop_fail; + src_idx = ipa_get_jf_ancestor_formal_id (jfunc); + } + + src_lat = ipa_get_poly_ctx_lat (caller_info, src_idx); + /* If we would need to clone the caller and cannot, do not propagate. */ + if (!ipcp_versionable_function_p (cs->caller) + && (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 (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); + if (!cur.useless_p ()) + { + ret |= dest_lat->add_value (cur, cs, src_val, src_idx); + added_sth = true; + } + } + + } + + prop_fail: + if (!added_sth) + { + if (!edge_ctx.useless_p ()) + ret |= dest_lat->add_value (edge_ctx, cs); + else + ret |= dest_lat->set_contains_variable (); + } + + return ret; +} + /* If DEST_PLATS already has aggregate items, check that aggs_by_ref matches NEW_AGGS_BY_REF and if not, mark all aggs as bottoms and return true (in all other cases, return false). If there are no aggregate items, set @@ -1561,6 +1727,8 @@ propagate_constants_accross_call (struct cgraph_edge *cs) { ret |= propagate_scalar_accross_jump_function (cs, jump_func, &dest_plats->itself); + ret |= propagate_context_accross_jump_function (cs, jump_func, i, + &dest_plats->ctxlat); ret |= propagate_aggs_accross_jump_function (cs, jump_func, dest_plats); } @@ -1572,25 +1740,24 @@ propagate_constants_accross_call (struct cgraph_edge *cs) } /* If an indirect edge IE can be turned into a direct one based on KNOWN_VALS - (which can contain both constants and binfos), KNOWN_BINFOS, KNOWN_AGGS or + (which can contain both constants and binfos), KNOWN_CONTEXTS, KNOWN_AGGS or AGG_REPS return the destination. The latter three can be NULL. If AGG_REPS is not NULL, KNOWN_AGGS is ignored. */ static tree ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, - vec known_vals, - vec known_binfos, + vec known_csts, + vec known_contexts, vec known_aggs, struct ipa_agg_replacement_value *agg_reps) { int param_index = ie->indirect_info->param_index; - HOST_WIDE_INT token, anc_offset; - tree otr_type; + HOST_WIDE_INT anc_offset; tree t; tree target = NULL; if (param_index == -1 - || known_vals.length () <= (unsigned int) param_index) + || known_csts.length () <= (unsigned int) param_index) return NULL_TREE; if (!ie->indirect_info->polymorphic) @@ -1625,7 +1792,7 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, t = NULL; } else - t = known_vals[param_index]; + t = known_csts[param_index]; if (t && TREE_CODE (t) == ADDR_EXPR @@ -1639,9 +1806,7 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, return NULL_TREE; gcc_assert (!ie->indirect_info->agg_contents); - token = ie->indirect_info->otr_token; anc_offset = ie->indirect_info->offset; - otr_type = ie->indirect_info->otr_type; t = NULL; @@ -1695,42 +1860,43 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, } } - /* Did we work out BINFO via type propagation? */ - if (!t && known_binfos.length () > (unsigned int) param_index) - t = known_binfos[param_index]; - /* Or do we know the constant value of pointer? */ + /* Do we know the constant value of pointer? */ if (!t) - t = known_vals[param_index]; - if (!t) - return NULL_TREE; + t = known_csts[param_index]; - if (TREE_CODE (t) != TREE_BINFO) + gcc_checking_assert (!t || TREE_CODE (t) != TREE_BINFO); + + ipa_polymorphic_call_context context; + if (known_contexts.length () > (unsigned int) param_index) { - ipa_polymorphic_call_context context (t, ie->indirect_info->otr_type, - anc_offset); - vec targets; - bool final; - - targets = possible_polymorphic_call_targets - (ie->indirect_info->otr_type, - ie->indirect_info->otr_token, - context, &final); - if (!final || targets.length () > 1) - return NULL_TREE; - if (targets.length () == 1) - target = targets[0]->decl; - else - target = ipa_impossible_devirt_target (ie, NULL_TREE); + context = known_contexts[param_index]; + if (t) + { + ipa_polymorphic_call_context ctx2 = ipa_polymorphic_call_context + (t, ie->indirect_info->otr_type, anc_offset); + if (!ctx2.useless_p ()) + context.combine_with (ctx2, ie->indirect_info->otr_type); + } } + else if (t) + context = ipa_polymorphic_call_context (t, ie->indirect_info->otr_type, + anc_offset); else - { - tree binfo; + return NULL_TREE; - binfo = get_binfo_at_offset (t, anc_offset, otr_type); - if (!binfo) - return NULL_TREE; - target = gimple_get_virt_method_for_binfo (token, binfo); - } + vec targets; + bool final; + + targets = possible_polymorphic_call_targets + (ie->indirect_info->otr_type, + ie->indirect_info->otr_token, + context, &final); + if (!final || targets.length () > 1) + return NULL_TREE; + if (targets.length () == 1) + target = targets[0]->decl; + else + target = ipa_impossible_devirt_target (ie, NULL_TREE); if (target && !possible_polymorphic_call_target_p (ie, cgraph_node::get (target))) @@ -1740,27 +1906,27 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, } -/* If an indirect edge IE can be turned into a direct one based on KNOWN_VALS - (which can contain both constants and binfos), KNOWN_BINFOS (which can be - NULL) or KNOWN_AGGS (which also can be NULL) return the destination. */ +/* If an indirect edge IE can be turned into a direct one based on KNOWN_CSTS, + KNOWN_CONTEXTS (which can be vNULL) or KNOWN_AGGS (which also can be vNULL) + return the destination. */ tree ipa_get_indirect_edge_target (struct cgraph_edge *ie, - vec known_vals, - vec known_binfos, + vec known_csts, + vec known_contexts, vec known_aggs) { - return ipa_get_indirect_edge_target_1 (ie, known_vals, known_binfos, + return ipa_get_indirect_edge_target_1 (ie, known_csts, known_contexts, known_aggs, NULL); } /* Calculate devirtualization time bonus for NODE, assuming we know KNOWN_CSTS - and KNOWN_BINFOS. */ + and KNOWN_CONTEXTS. */ static int devirtualization_time_bonus (struct cgraph_node *node, vec known_csts, - vec known_binfos, + vec known_contexts, vec known_aggs) { struct cgraph_edge *ie; @@ -1773,7 +1939,7 @@ devirtualization_time_bonus (struct cgraph_node *node, enum availability avail; tree target; - target = ipa_get_indirect_edge_target (ie, known_csts, known_binfos, + target = ipa_get_indirect_edge_target (ie, known_csts, known_contexts, known_aggs); if (!target) continue; @@ -1890,25 +2056,27 @@ context_independent_aggregate_values (struct ipcp_param_lattices *plats) return res; } -/* Allocate KNOWN_CSTS, KNOWN_BINFOS and, if non-NULL, KNOWN_AGGS and populate - them with values of parameters that are known independent of the context. - INFO describes the function. If REMOVABLE_PARAMS_COST is non-NULL, the - movement cost of all removable parameters will be stored in it. */ +/* Allocate KNOWN_CSTS, KNOWN_CONTEXTS and, if non-NULL, KNOWN_AGGS and + populate them with values of parameters that are known independent of the + context. INFO describes the function. If REMOVABLE_PARAMS_COST is + non-NULL, the movement cost of all removable parameters will be stored in + it. */ static bool gather_context_independent_values (struct ipa_node_params *info, - vec *known_csts, - vec *known_binfos, - vec *known_aggs, - int *removable_params_cost) + vec *known_csts, + vec + *known_contexts, + vec *known_aggs, + int *removable_params_cost) { int i, count = ipa_get_param_count (info); bool ret = false; known_csts->create (0); - known_binfos->create (0); + known_contexts->create (0); known_csts->safe_grow_cleared (count); - known_binfos->safe_grow_cleared (count); + known_contexts->safe_grow_cleared (count); if (known_aggs) { known_aggs->create (0); @@ -1926,28 +2094,25 @@ gather_context_independent_values (struct ipa_node_params *info, if (lat->is_single_const ()) { ipcp_value *val = lat->values; - if (TREE_CODE (val->value) != TREE_BINFO) - { - (*known_csts)[i] = val->value; - if (removable_params_cost) - *removable_params_cost - += estimate_move_cost (TREE_TYPE (val->value), false); - ret = true; - } - else if (plats->virt_call) - { - (*known_binfos)[i] = val->value; - ret = true; - } - else if (removable_params_cost - && !ipa_is_param_used (info, i)) - *removable_params_cost += ipa_get_param_move_cost (info, i); + gcc_checking_assert (TREE_CODE (val->value) != TREE_BINFO); + (*known_csts)[i] = val->value; + if (removable_params_cost) + *removable_params_cost + += estimate_move_cost (TREE_TYPE (val->value), false); + ret = true; } else if (removable_params_cost && !ipa_is_param_used (info, i)) *removable_params_cost += ipa_get_param_move_cost (info, i); + ipcp_lattice *ctxlat = &plats->ctxlat; + if (ctxlat->is_single_const ()) + { + (*known_contexts)[i] = ctxlat->values->value; + ret = true; + } + if (known_aggs) { vec *agg_items; @@ -1985,14 +2150,14 @@ agg_jmp_p_vec_for_t_vec (vec known_aggs) } /* Perform time and size measurement of NODE with the context given in - KNOWN_CSTS, KNOWN_BINFOS and KNOWN_AGGS, calculate the benefit and cost + KNOWN_CSTS, KNOWN_CONTEXTS and KNOWN_AGGS, calculate the benefit and cost given BASE_TIME of the node without specialization, REMOVABLE_PARAMS_COST of all context-independent removable parameters and EST_MOVE_COST of estimated movement of the considered parameter and store it into VAL. */ static void perform_estimation_of_a_value (cgraph_node *node, vec known_csts, - vec known_binfos, + vec known_contexts, vec known_aggs_ptrs, int base_time, int removable_params_cost, int est_move_cost, ipcp_value_base *val) @@ -2000,11 +2165,11 @@ perform_estimation_of_a_value (cgraph_node *node, vec known_csts, int time, size, time_benefit; inline_hints hints; - estimate_ipcp_clone_size_and_time (node, known_csts, known_binfos, + estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts, known_aggs_ptrs, &size, &time, &hints); time_benefit = base_time - time - + devirtualization_time_bonus (node, known_csts, known_binfos, + + devirtualization_time_bonus (node, known_csts, known_contexts, known_aggs_ptrs) + hint_time_bonus (hints) + removable_params_cost + est_move_cost; @@ -2029,7 +2194,8 @@ estimate_local_effects (struct cgraph_node *node) { struct ipa_node_params *info = IPA_NODE_REF (node); int i, count = ipa_get_param_count (info); - vec known_csts, known_binfos; + vec known_csts; + vec known_contexts; vec known_aggs; vec known_aggs_ptrs; bool always_const; @@ -2044,7 +2210,7 @@ estimate_local_effects (struct cgraph_node *node) node->name (), node->order, base_time); always_const = gather_context_independent_values (info, &known_csts, - &known_binfos, &known_aggs, + &known_contexts, &known_aggs, &removable_params_cost); known_aggs_ptrs = agg_jmp_p_vec_for_t_vec (known_aggs); if (always_const) @@ -2056,9 +2222,9 @@ estimate_local_effects (struct cgraph_node *node) init_caller_stats (&stats); node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats, false); - estimate_ipcp_clone_size_and_time (node, known_csts, known_binfos, + estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts, known_aggs_ptrs, &size, &time, &hints); - time -= devirtualization_time_bonus (node, known_csts, known_binfos, + time -= devirtualization_time_bonus (node, known_csts, known_contexts, known_aggs_ptrs); time -= hint_time_bonus (hints); time -= removable_params_cost; @@ -2104,32 +2270,19 @@ estimate_local_effects (struct cgraph_node *node) struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i); ipcp_lattice *lat = &plats->itself; ipcp_value *val; - int emc; if (lat->bottom || !lat->values - || known_csts[i] - || known_binfos[i]) + || known_csts[i]) continue; for (val = lat->values; val; val = val->next) { - if (TREE_CODE (val->value) != TREE_BINFO) - { - known_csts[i] = val->value; - known_binfos[i] = NULL_TREE; - emc = estimate_move_cost (TREE_TYPE (val->value), true); - } - else if (plats->virt_call) - { - known_csts[i] = NULL_TREE; - known_binfos[i] = val->value; - emc = 0; - } - else - continue; + gcc_checking_assert (TREE_CODE (val->value) != TREE_BINFO); + known_csts[i] = val->value; - perform_estimation_of_a_value (node, known_csts, known_binfos, + int emc = estimate_move_cost (TREE_TYPE (val->value), true); + perform_estimation_of_a_value (node, known_csts, known_contexts, known_aggs_ptrs, base_time, removable_params_cost, emc, val); @@ -2143,10 +2296,44 @@ estimate_local_effects (struct cgraph_node *node) val->local_time_benefit, val->local_size_cost); } } - known_binfos[i] = NULL_TREE; known_csts[i] = NULL_TREE; } + for (i = 0; i < count; i++) + { + struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i); + + if (!plats->virt_call) + continue; + + ipcp_lattice *ctxlat = &plats->ctxlat; + ipcp_value *val; + + if (ctxlat->bottom + || !ctxlat->values + || !known_contexts[i].useless_p ()) + continue; + + for (val = ctxlat->values; val; val = val->next) + { + known_contexts[i] = val->value; + perform_estimation_of_a_value (node, known_csts, known_contexts, + known_aggs_ptrs, base_time, + removable_params_cost, 0, val); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " - estimates for polymorphic context "); + print_ipcp_constant_value (dump_file, val->value); + fprintf (dump_file, " for "); + ipa_dump_param (dump_file, info, i); + fprintf (dump_file, ": time_benefit: %i, size: %i\n", + val->local_time_benefit, val->local_size_cost); + } + } + known_contexts[i] = ipa_polymorphic_call_context (); + } + for (i = 0; i < count ; i++) { struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i); @@ -2174,7 +2361,7 @@ estimate_local_effects (struct cgraph_node *node) item.value = val->value; vec_safe_push (ajf->items, item); - perform_estimation_of_a_value (node, known_csts, known_binfos, + perform_estimation_of_a_value (node, known_csts, known_contexts, known_aggs_ptrs, base_time, removable_params_cost, 0, val); @@ -2200,7 +2387,7 @@ estimate_local_effects (struct cgraph_node *node) vec_free (known_aggs[i].items); known_csts.release (); - known_binfos.release (); + known_contexts.release (); known_aggs.release (); known_aggs_ptrs.release (); } @@ -2274,17 +2461,30 @@ add_all_node_vals_to_toposort (cgraph_node *node, ipa_topo_info *topo) struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i); ipcp_lattice *lat = &plats->itself; struct ipcp_agg_lattice *aglat; - ipcp_value *val; if (!lat->bottom) - for (val = lat->values; val; val = val->next) - topo->constants.add_val (val); + { + ipcp_value *val; + for (val = lat->values; val; val = val->next) + topo->constants.add_val (val); + } if (!plats->aggs_bottom) for (aglat = plats->aggs; aglat; aglat = aglat->next) if (!aglat->bottom) - for (val = aglat->values; val; val = val->next) - topo->constants.add_val (val); + { + ipcp_value *val; + for (val = aglat->values; val; val = val->next) + topo->constants.add_val (val); + } + + ipcp_lattice *ctxlat = &plats->ctxlat; + if (!ctxlat->bottom) + { + ipcp_value *ctxval; + for (ctxval = ctxlat->values; ctxval; ctxval = ctxval->next) + topo->contexts.add_val (ctxval); + } } } @@ -2389,8 +2589,8 @@ value_topo_info::propagate_effects () } -/* Propagate constants, binfos and their effects from the summaries - interprocedurally. */ +/* Propagate constants, polymorphic contexts and their effects from the + summaries interprocedurally. */ static void ipcp_propagate_stage (struct ipa_topo_info *topo) @@ -2435,6 +2635,7 @@ ipcp_propagate_stage (struct ipa_topo_info *topo) ipcp_verify_propagated_values (); #endif topo->constants.propagate_effects (); + topo->contexts.propagate_effects (); if (dump_file) { @@ -2444,11 +2645,13 @@ ipcp_propagate_stage (struct ipa_topo_info *topo) } /* Discover newly direct outgoing edges from NODE which is a new clone with - known KNOWN_VALS and make them direct. */ + known KNOWN_CSTS and make them direct. */ static void ipcp_discover_new_direct_edges (struct cgraph_node *node, - vec known_vals, + vec known_csts, + vec + known_contexts, struct ipa_agg_replacement_value *aggvals) { struct cgraph_edge *ie, *next_ie; @@ -2459,8 +2662,8 @@ ipcp_discover_new_direct_edges (struct cgraph_node *node, tree target; next_ie = ie->next_callee; - target = ipa_get_indirect_edge_target_1 (ie, known_vals, vNULL, vNULL, - aggvals); + target = ipa_get_indirect_edge_target_1 (ie, known_csts, known_contexts, + vNULL, aggvals); if (target) { bool agg_contents = ie->indirect_info->agg_contents; @@ -2589,7 +2792,7 @@ cgraph_edge_brings_value_p (struct cgraph_edge *cs, { tree t; if (src->offset == -1) - t = caller_info->known_vals[src->index]; + t = caller_info->known_csts[src->index]; else t = get_clone_agg_value (cs->caller, src->offset, src->index); return (t != NULL_TREE @@ -2618,6 +2821,35 @@ cgraph_edge_brings_value_p (struct cgraph_edge *cs, } } +/* Return true if edge CS does bring about the value described by SRC. */ + +static bool +cgraph_edge_brings_value_p (struct cgraph_edge *cs, + ipcp_value_source + *src) +{ + struct ipa_node_params *caller_info = IPA_NODE_REF (cs->caller); + cgraph_node *real_dest = cs->callee->function_symbol (); + struct ipa_node_params *dst_info = IPA_NODE_REF (real_dest); + + if ((dst_info->ipcp_orig_node && !dst_info->is_all_contexts_clone) + || caller_info->node_dead) + return false; + if (!src->val) + return true; + + if (caller_info->ipcp_orig_node) + return (caller_info->known_contexts.length () > (unsigned) src->index) + && values_equal_for_ipcp_p (src->val->value, + caller_info->known_contexts[src->index]); + + struct ipcp_param_lattices *plats = ipa_get_parm_lattices (caller_info, + src->index); + return plats->ctxlat.is_single_const () + && values_equal_for_ipcp_p (src->val->value, + plats->ctxlat.values->value); +} + /* Get the next clone in the linked list of clones of an edge. */ static inline struct cgraph_edge * @@ -2847,12 +3079,14 @@ update_specialized_profile (struct cgraph_node *new_node, dump_profile_updates (orig_node, new_node); } -/* Create a specialized version of NODE with known constants and types of - parameters in KNOWN_VALS and redirect all edges in CALLERS to it. */ +/* Create a specialized version of NODE with known constants in KNOWN_CSTS, + known contexts in KNOWN_CONTEXTS and known aggregate values in AGGVALS and + redirect all edges in CALLERS to it. */ static struct cgraph_node * create_specialized_node (struct cgraph_node *node, - vec known_vals, + vec known_csts, + vec known_contexts, struct ipa_agg_replacement_value *aggvals, vec callers) { @@ -2870,10 +3104,9 @@ create_specialized_node (struct cgraph_node *node, args_to_skip = BITMAP_GGC_ALLOC (); for (i = 0; i < count; i++) { - tree t = known_vals[i]; + tree t = known_csts[i]; - if ((t && TREE_CODE (t) != TREE_BINFO) - || !ipa_is_param_used (info, i)) + if (t || !ipa_is_param_used (info, i)) bitmap_set_bit (args_to_skip, i); } } @@ -2886,11 +3119,12 @@ create_specialized_node (struct cgraph_node *node, for (i = 0; i < count ; i++) { - tree t = known_vals[i]; - if (t && TREE_CODE (t) != TREE_BINFO) + tree t = known_csts[i]; + if (t) { struct ipa_replace_map *replace_map; + gcc_checking_assert (TREE_CODE (t) != TREE_BINFO); replace_map = get_replacement_map (info, t, i); if (replace_map) vec_safe_push (replace_trees, replace_map); @@ -2907,6 +3141,15 @@ create_specialized_node (struct cgraph_node *node, { fprintf (dump_file, " the new node is %s/%i.\n", new_node->name (), new_node->order); + if (known_contexts.exists ()) + { + for (i = 0; i < count ; i++) + if (!known_contexts[i].useless_p ()) + { + fprintf (dump_file, " known ctx %i is ", i); + known_contexts[i].dump (dump_file); + } + } if (aggvals) ipa_dump_agg_replacement_values (dump_file, aggvals); } @@ -2914,21 +3157,21 @@ create_specialized_node (struct cgraph_node *node, update_profiling_info (node, new_node); new_info = IPA_NODE_REF (new_node); new_info->ipcp_orig_node = node; - new_info->known_vals = known_vals; + new_info->known_csts = known_csts; + new_info->known_contexts = known_contexts; - ipcp_discover_new_direct_edges (new_node, known_vals, aggvals); + ipcp_discover_new_direct_edges (new_node, known_csts, known_contexts, aggvals); callers.release (); return new_node; } /* Given a NODE, and a subset of its CALLERS, try to populate blanks slots in - KNOWN_VALS with constants and types that are also known for all of the - CALLERS. */ + KNOWN_CSTS with constants that are also known for all of the CALLERS. */ static void find_more_scalar_values_for_callers_subset (struct cgraph_node *node, - vec known_vals, + vec known_csts, vec callers) { struct ipa_node_params *info = IPA_NODE_REF (node); @@ -2940,7 +3183,7 @@ find_more_scalar_values_for_callers_subset (struct cgraph_node *node, tree newval = NULL_TREE; int j; - if (ipa_get_scalar_lat (info, i)->bottom || known_vals[i]) + if (ipa_get_scalar_lat (info, i)->bottom || known_csts[i]) continue; FOR_EACH_VEC_ELT (callers, j, cs) @@ -2977,11 +3220,80 @@ find_more_scalar_values_for_callers_subset (struct cgraph_node *node, fprintf (dump_file, "\n"); } - known_vals[i] = newval; + known_csts[i] = newval; } } } +/* Given a NODE and a subset of its CALLERS, try to populate plank slots in + KNOWN_CONTEXTS with polymorphic contexts that are also known for all of the + CALLERS. */ + +static void +find_more_contexts_for_caller_subset (cgraph_node *node, + vec + *known_contexts, + vec callers) +{ + ipa_node_params *info = IPA_NODE_REF (node); + int i, count = ipa_get_param_count (info); + + for (i = 0; i < count ; i++) + { + cgraph_edge *cs; + + if (ipa_get_poly_ctx_lat (info, i)->bottom + || (known_contexts->exists () + && !(*known_contexts)[i].useless_p ())) + continue; + + ipa_polymorphic_call_context newval; + bool found = false; + int j; + + FOR_EACH_VEC_ELT (callers, j, cs) + { + if (i >= ipa_get_cs_argument_count (IPA_EDGE_REF (cs))) + return; + ipa_jump_func *jfunc = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), + i); + 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) + { + found = true; + newval = ctx; + } + } + + if (found) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " adding an extra known polymorphic " + "context "); + print_ipcp_constant_value (dump_file, newval); + fprintf (dump_file, " for "); + ipa_dump_param (dump_file, info, i); + fprintf (dump_file, "\n"); + } + + if (!known_contexts->exists ()) + known_contexts->safe_grow_cleared (ipa_get_param_count (info)); + (*known_contexts)[i] = newval; + } + + } +} + /* Go through PLATS and create a vector of values consisting of values and offsets (minus OFFSET) of lattices that contain only a single value. */ @@ -3355,7 +3667,7 @@ cgraph_edge_brings_all_scalars_for_node (struct cgraph_edge *cs, struct ipa_jump_func *jump_func; tree val, t; - val = dest_info->known_vals[i]; + val = dest_info->known_csts[i]; if (!val) continue; @@ -3489,28 +3801,70 @@ perhaps_add_new_callers (cgraph_node *node, ipcp_value *val) update_specialized_profile (val->spec_node, node, redirected_sum); } +/* Return true if KNOWN_CONTEXTS contain at least one useful context. */ -/* Copy KNOWN_BINFOS to KNOWN_VALS. */ +static bool +known_contexts_useful_p (vec known_contexts) +{ + ipa_polymorphic_call_context *ctx; + int i; + + FOR_EACH_VEC_ELT (known_contexts, i, ctx) + if (!ctx->useless_p ()) + return true; + return false; +} + +/* Return a copy of KNOWN_CSTS if it is not empty, otherwise return vNULL. */ + +static vec +copy_useful_known_contexts (vec known_contexts) +{ + if (known_contexts_useful_p (known_contexts)) + return known_contexts.copy (); + else + return vNULL; +} + +/* Copy KNOWN_CSTS and modify the copy according to VAL and INDEX. If + non-empty, replace KNOWN_CONTEXTS with its copy too. */ static void -move_binfos_to_values (vec known_vals, - vec known_binfos) +modify_known_vectors_with_val (vec *known_csts, + vec *known_contexts, + ipcp_value *val, + int index) { - tree t; - int i; + *known_csts = known_csts->copy (); + *known_contexts = copy_useful_known_contexts (*known_contexts); + (*known_csts)[index] = val->value; +} - for (i = 0; known_binfos.iterate (i, &t); i++) - if (t) - known_vals[i] = t; +/* Replace KNOWN_CSTS with its copy. Also copy KNOWN_CONTEXTS and modify the + copy according to VAL and INDEX. */ + +static void +modify_known_vectors_with_val (vec *known_csts, + vec *known_contexts, + ipcp_value *val, + int index) +{ + *known_csts = known_csts->copy (); + *known_contexts = known_contexts->copy (); + (*known_contexts)[index] = val->value; } -/* Return true if there is a replacement equivalent to VALUE, INDEX and OFFSET - among those in the AGGVALS list. */ +/* Return true if OFFSET indicates this was not an aggregate value or there is + a replacement equivalent to VALUE, INDEX and OFFSET among those in the + AGGVALS list. */ DEBUG_FUNCTION bool -ipcp_val_in_agg_replacements_p (struct ipa_agg_replacement_value *aggvals, - int index, HOST_WIDE_INT offset, tree value) +ipcp_val_agg_replacement_ok_p (ipa_agg_replacement_value *aggvals, + int index, HOST_WIDE_INT offset, tree value) { + if (offset == -1) + return true; + while (aggvals) { if (aggvals->index == index @@ -3522,22 +3876,32 @@ ipcp_val_in_agg_replacements_p (struct ipa_agg_replacement_value *aggvals, return false; } +/* Return true if offset is minus one because source of a polymorphic contect + cannot be an aggregate value. */ + +DEBUG_FUNCTION bool +ipcp_val_agg_replacement_ok_p (ipa_agg_replacement_value *, + int , HOST_WIDE_INT offset, + ipa_polymorphic_call_context) +{ + return offset == -1; +} + /* Decide wheter to create a special version of NODE for value VAL of parameter at the given INDEX. If OFFSET is -1, the value is for the parameter itself, otherwise it is stored at the given OFFSET of the parameter. KNOWN_CSTS, - KNOWN_BINFOS and KNOWN_AGGS describe the other already known values. */ + KNOWN_CONTEXTS and KNOWN_AGGS describe the other already known values. */ template static bool decide_about_value (struct cgraph_node *node, int index, HOST_WIDE_INT offset, ipcp_value *val, vec known_csts, - vec known_binfos) + vec known_contexts) { struct ipa_agg_replacement_value *aggvals; int freq_sum, caller_count; gcov_type count_sum; vec callers; - vec kv; if (val->spec_node) { @@ -3583,16 +3947,20 @@ decide_about_value (struct cgraph_node *node, int index, HOST_WIDE_INT offset, node->name (), node->order); callers = gather_edges_for_value (val, caller_count); - kv = known_csts.copy (); - move_binfos_to_values (kv, known_binfos); if (offset == -1) - kv[index] = val->value; - find_more_scalar_values_for_callers_subset (node, kv, callers); + modify_known_vectors_with_val (&known_csts, &known_contexts, val, index); + else + { + known_csts = known_csts.copy (); + known_contexts = copy_useful_known_contexts (known_contexts); + } + find_more_scalar_values_for_callers_subset (node, known_csts, callers); + find_more_contexts_for_caller_subset (node, &known_contexts, callers); aggvals = find_aggregate_values_for_callers_subset (node, callers); - gcc_checking_assert (offset == -1 - || ipcp_val_in_agg_replacements_p (aggvals, index, - offset, val->value)); - val->spec_node = create_specialized_node (node, kv, aggvals, callers); + gcc_checking_assert (ipcp_val_agg_replacement_ok_p (aggvals, index, + offset, val->value)); + val->spec_node = create_specialized_node (node, known_csts, known_contexts, + aggvals, callers); overall_size += val->local_size_cost; /* TODO: If for some lattice there is only one other known value @@ -3608,7 +3976,8 @@ decide_whether_version_node (struct cgraph_node *node) { struct ipa_node_params *info = IPA_NODE_REF (node); int i, count = ipa_get_param_count (info); - vec known_csts, known_binfos; + vec known_csts; + vec known_contexts; vec known_aggs = vNULL; bool ret = false; @@ -3619,7 +3988,7 @@ decide_whether_version_node (struct cgraph_node *node) fprintf (dump_file, "\nEvaluating opportunities for %s/%i.\n", node->name (), node->order); - gather_context_independent_values (info, &known_csts, &known_binfos, + gather_context_independent_values (info, &known_csts, &known_contexts, info->do_clone_for_all_contexts ? &known_aggs : NULL, NULL); @@ -3627,14 +3996,16 @@ decide_whether_version_node (struct cgraph_node *node) { struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i); ipcp_lattice *lat = &plats->itself; - ipcp_value *val; + ipcp_lattice *ctxlat = &plats->ctxlat; if (!lat->bottom - && !known_csts[i] - && !known_binfos[i]) - for (val = lat->values; val; val = val->next) - ret |= decide_about_value (node, i, -1, val, known_csts, - known_binfos); + && !known_csts[i]) + { + ipcp_value *val; + for (val = lat->values; val; val = val->next) + ret |= decide_about_value (node, i, -1, val, known_csts, + known_contexts); + } if (!plats->aggs_bottom) { @@ -3648,8 +4019,18 @@ decide_whether_version_node (struct cgraph_node *node) || !aglat->is_single_const ())) for (val = aglat->values; val; val = val->next) ret |= decide_about_value (node, i, aglat->offset, val, - known_csts, known_binfos); + known_csts, known_contexts); } + + if (!ctxlat->bottom + && known_contexts[i].useless_p ()) + { + ipcp_value *val; + for (val = ctxlat->values; val; val = val->next) + ret |= decide_about_value (node, i, -1, val, known_csts, + known_contexts); + } + info = IPA_NODE_REF (node); } @@ -3664,8 +4045,13 @@ decide_whether_version_node (struct cgraph_node *node) node->order); callers = node->collect_callers (); - move_binfos_to_values (known_csts, known_binfos); - clone = create_specialized_node (node, known_csts, + + if (!known_contexts_useful_p (known_contexts)) + { + known_contexts.release (); + known_contexts = vNULL; + } + clone = create_specialized_node (node, known_csts, known_contexts, known_aggs_to_agg_replacement_list (known_aggs), callers); info = IPA_NODE_REF (node); @@ -3677,9 +4063,11 @@ decide_whether_version_node (struct cgraph_node *node) ret = true; } else - known_csts.release (); + { + known_csts.release (); + known_contexts.release (); + } - known_binfos.release (); return ret; } @@ -3803,8 +4191,11 @@ ipcp_driver (void) edge_removal_hook_holder = symtab->add_edge_removal_hook (&ipcp_edge_removal_hook, NULL); - ipcp_values_pool = create_alloc_pool ("IPA-CP values", - sizeof (ipcp_value), 32); + ipcp_cst_values_pool = create_alloc_pool ("IPA-CP constant values", + sizeof (ipcp_value), 32); + ipcp_poly_ctx_values_pool = create_alloc_pool + ("IPA-CP polymorphic contexts", + sizeof (ipcp_value), 32); ipcp_sources_pool = create_alloc_pool ("IPA-CP value sources", sizeof (ipcp_value_source), 64); ipcp_agg_lattice_pool = create_alloc_pool ("IPA_CP aggregate lattices", diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c index 0373b108699..69e06123fee 100644 --- a/gcc/ipa-inline-analysis.c +++ b/gcc/ipa-inline-analysis.c @@ -895,7 +895,8 @@ static void evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p, clause_t *clause_ptr, vec *known_vals_ptr, - vec *known_binfos_ptr, + vec + *known_contexts_ptr, vec *known_aggs_ptr) { struct cgraph_node *callee = e->callee->ultimate_alias_target (); @@ -907,12 +908,12 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p, *clause_ptr = inline_p ? 0 : 1 << predicate_not_inlined_condition; if (known_vals_ptr) known_vals_ptr->create (0); - if (known_binfos_ptr) - known_binfos_ptr->create (0); + if (known_contexts_ptr) + known_contexts_ptr->create (0); if (ipa_node_params_vector.exists () && !e->call_stmt_cannot_inline_p - && ((clause_ptr && info->conds) || known_vals_ptr || known_binfos_ptr)) + && ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr)) { struct ipa_node_params *parms_info; struct ipa_edge_args *args = IPA_EDGE_REF (e); @@ -928,8 +929,8 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p, known_vals.safe_grow_cleared (count); if (count && (info->conds || known_aggs_ptr)) known_aggs.safe_grow_cleared (count); - if (count && known_binfos_ptr) - known_binfos_ptr->safe_grow_cleared (count); + if (count && known_contexts_ptr) + known_contexts_ptr->safe_grow_cleared (count); for (i = 0; i < count; i++) { @@ -937,14 +938,16 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p, tree cst = ipa_value_from_jfunc (parms_info, jf); if (cst) { - if (known_vals.exists () && TREE_CODE (cst) != TREE_BINFO) + gcc_checking_assert (TREE_CODE (cst) != TREE_BINFO); + if (known_vals.exists ()) known_vals[i] = cst; - else if (known_binfos_ptr != NULL - && TREE_CODE (cst) == TREE_BINFO) - (*known_binfos_ptr)[i] = cst; } else if (inline_p && !es->param[i].change_prob) known_vals[i] = error_mark_node; + + if (known_contexts_ptr) + (*known_contexts_ptr)[i] = ipa_context_from_jfunc (parms_info, e, + i, jf); /* TODO: When IPA-CP starts propagating and merging aggregate jump functions, use its knowledge of the caller too, just like the scalar case above. */ @@ -2969,14 +2972,14 @@ make_pass_inline_parameters (gcc::context *ctxt) } -/* Estimate benefit devirtualizing indirect edge IE, provided KNOWN_VALS and - KNOWN_BINFOS. */ +/* Estimate benefit devirtualizing indirect edge IE, provided KNOWN_VALS, + KNOWN_CONTEXTS and KNOWN_AGGS. */ static bool estimate_edge_devirt_benefit (struct cgraph_edge *ie, int *size, int *time, vec known_vals, - vec known_binfos, + vec known_contexts, vec known_aggs) { tree target; @@ -2984,12 +2987,12 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie, struct inline_summary *isummary; enum availability avail; - if (!known_vals.exists () && !known_binfos.exists ()) + if (!known_vals.exists () && !known_contexts.exists ()) return false; if (!flag_indirect_inlining) return false; - target = ipa_get_indirect_edge_target (ie, known_vals, known_binfos, + target = ipa_get_indirect_edge_target (ie, known_vals, known_contexts, known_aggs); if (!target) return false; @@ -3013,7 +3016,7 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie, /* Increase SIZE, MIN_SIZE (if non-NULL) and TIME for size and time needed to handle edge E with probability PROB. Set HINTS if edge may be devirtualized. - KNOWN_VALS, KNOWN_AGGS and KNOWN_BINFOS describe context of the call + KNOWN_VALS, KNOWN_AGGS and KNOWN_CONTEXTS describe context of the call site. */ static inline void @@ -3021,7 +3024,7 @@ estimate_edge_size_and_time (struct cgraph_edge *e, int *size, int *min_size, int *time, int prob, vec known_vals, - vec known_binfos, + vec known_contexts, vec known_aggs, inline_hints *hints) { @@ -3031,7 +3034,7 @@ estimate_edge_size_and_time (struct cgraph_edge *e, int *size, int *min_size, int cur_size; if (!e->callee && estimate_edge_devirt_benefit (e, &call_size, &call_time, - known_vals, known_binfos, known_aggs) + known_vals, known_contexts, known_aggs) && hints && e->maybe_hot_p ()) *hints |= INLINE_HINT_indirect_call; cur_size = call_size * INLINE_SIZE_SCALE; @@ -3047,9 +3050,8 @@ estimate_edge_size_and_time (struct cgraph_edge *e, int *size, int *min_size, /* Increase SIZE, MIN_SIZE and TIME for size and time needed to handle all - calls in NODE. - POSSIBLE_TRUTHS, KNOWN_VALS, KNOWN_AGGS and KNOWN_BINFOS describe context of - the call site. */ + calls in NODE. POSSIBLE_TRUTHS, KNOWN_VALS, KNOWN_AGGS and KNOWN_CONTEXTS + describe context of the call site. */ static void estimate_calls_size_and_time (struct cgraph_node *node, int *size, @@ -3057,7 +3059,7 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size, inline_hints *hints, clause_t possible_truths, vec known_vals, - vec known_binfos, + vec known_contexts, vec known_aggs) { struct cgraph_edge *e; @@ -3074,14 +3076,14 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size, estimate_edge_size_and_time (e, size, es->predicate ? NULL : min_size, time, REG_BR_PROB_BASE, - known_vals, known_binfos, + known_vals, known_contexts, known_aggs, hints); } else estimate_calls_size_and_time (e->callee, size, min_size, time, hints, possible_truths, - known_vals, known_binfos, + known_vals, known_contexts, known_aggs); } } @@ -3093,14 +3095,14 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size, estimate_edge_size_and_time (e, size, es->predicate ? NULL : min_size, time, REG_BR_PROB_BASE, - known_vals, known_binfos, known_aggs, + known_vals, known_contexts, known_aggs, hints); } } /* Estimate size and time needed to execute NODE assuming - POSSIBLE_TRUTHS clause, and KNOWN_VALS, KNOWN_AGGS and KNOWN_BINFOS + POSSIBLE_TRUTHS clause, and KNOWN_VALS, KNOWN_AGGS and KNOWN_CONTEXTS information about NODE's arguments. If non-NULL use also probability information present in INLINE_PARAM_SUMMARY vector. Additionally detemine hints determined by the context. Finally compute @@ -3112,7 +3114,7 @@ static void estimate_node_size_and_time (struct cgraph_node *node, clause_t possible_truths, vec known_vals, - vec known_binfos, + vec known_contexts, vec known_aggs, int *ret_size, int *ret_min_size, int *ret_time, inline_hints *ret_hints, @@ -3189,7 +3191,7 @@ estimate_node_size_and_time (struct cgraph_node *node, hints |= INLINE_HINT_declared_inline; estimate_calls_size_and_time (node, &size, &min_size, &time, &hints, possible_truths, - known_vals, known_binfos, known_aggs); + known_vals, known_contexts, known_aggs); gcc_checking_assert (size >= 0); gcc_checking_assert (time >= 0); time = RDIV (time, INLINE_TIME_SCALE); @@ -3212,13 +3214,14 @@ estimate_node_size_and_time (struct cgraph_node *node, /* Estimate size and time needed to execute callee of EDGE assuming that parameters known to be constant at caller of EDGE are propagated. - KNOWN_VALS and KNOWN_BINFOS are vectors of assumed known constant values + KNOWN_VALS and KNOWN_CONTEXTS are vectors of assumed known constant values and types for parameters. */ void estimate_ipcp_clone_size_and_time (struct cgraph_node *node, vec known_vals, - vec known_binfos, + vec + known_contexts, vec known_aggs, int *ret_size, int *ret_time, inline_hints *hints) @@ -3227,7 +3230,7 @@ estimate_ipcp_clone_size_and_time (struct cgraph_node *node, clause = evaluate_conditions_for_known_args (node, false, known_vals, known_aggs); - estimate_node_size_and_time (node, clause, known_vals, known_binfos, + estimate_node_size_and_time (node, clause, known_vals, known_contexts, known_aggs, ret_size, NULL, ret_time, hints, vNULL); } @@ -3672,7 +3675,7 @@ do_estimate_edge_time (struct cgraph_edge *edge) struct cgraph_node *callee; clause_t clause; vec known_vals; - vec known_binfos; + vec known_contexts; vec known_aggs; struct inline_edge_summary *es = inline_edge_summary (edge); int min_size; @@ -3681,9 +3684,9 @@ do_estimate_edge_time (struct cgraph_edge *edge) gcc_checking_assert (edge->inline_failed); evaluate_properties_for_edge (edge, true, - &clause, &known_vals, &known_binfos, + &clause, &known_vals, &known_contexts, &known_aggs); - estimate_node_size_and_time (callee, clause, known_vals, known_binfos, + estimate_node_size_and_time (callee, clause, known_vals, known_contexts, known_aggs, &size, &min_size, &time, &hints, es->param); /* When we have profile feedback, we can quite safely identify hot @@ -3697,7 +3700,7 @@ do_estimate_edge_time (struct cgraph_edge *edge) hints |= INLINE_HINT_known_hot; known_vals.release (); - known_binfos.release (); + known_contexts.release (); known_aggs.release (); gcc_checking_assert (size >= 0); gcc_checking_assert (time >= 0); @@ -3728,7 +3731,7 @@ do_estimate_edge_size (struct cgraph_edge *edge) struct cgraph_node *callee; clause_t clause; vec known_vals; - vec known_binfos; + vec known_contexts; vec known_aggs; /* When we do caching, use do_estimate_edge_time to populate the entry. */ @@ -3746,12 +3749,12 @@ do_estimate_edge_size (struct cgraph_edge *edge) /* Early inliner runs without caching, go ahead and do the dirty work. */ gcc_checking_assert (edge->inline_failed); evaluate_properties_for_edge (edge, true, - &clause, &known_vals, &known_binfos, + &clause, &known_vals, &known_contexts, &known_aggs); - estimate_node_size_and_time (callee, clause, known_vals, known_binfos, + estimate_node_size_and_time (callee, clause, known_vals, known_contexts, known_aggs, &size, NULL, NULL, NULL, vNULL); known_vals.release (); - known_binfos.release (); + known_contexts.release (); known_aggs.release (); return size; } @@ -3767,7 +3770,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge) struct cgraph_node *callee; clause_t clause; vec known_vals; - vec known_binfos; + vec known_contexts; vec known_aggs; /* When we do caching, use do_estimate_edge_time to populate the entry. */ @@ -3785,12 +3788,12 @@ do_estimate_edge_hints (struct cgraph_edge *edge) /* Early inliner runs without caching, go ahead and do the dirty work. */ gcc_checking_assert (edge->inline_failed); evaluate_properties_for_edge (edge, true, - &clause, &known_vals, &known_binfos, + &clause, &known_vals, &known_contexts, &known_aggs); - estimate_node_size_and_time (callee, clause, known_vals, known_binfos, + estimate_node_size_and_time (callee, clause, known_vals, known_contexts, known_aggs, NULL, NULL, NULL, &hints, vNULL); known_vals.release (); - known_binfos.release (); + known_contexts.release (); known_aggs.release (); hints |= simple_edge_hints (edge); return hints; diff --git a/gcc/ipa-inline.h b/gcc/ipa-inline.h index 2ac6e4e93cb..4ba626441a2 100644 --- a/gcc/ipa-inline.h +++ b/gcc/ipa-inline.h @@ -223,7 +223,8 @@ void initialize_inline_failed (struct cgraph_edge *); int estimate_time_after_inlining (struct cgraph_node *, struct cgraph_edge *); int estimate_size_after_inlining (struct cgraph_node *, struct cgraph_edge *); void estimate_ipcp_clone_size_and_time (struct cgraph_node *, - vec, vec, + vec, + vec, vec, int *, int *, inline_hints *); int do_estimate_growth (struct cgraph_node *); diff --git a/gcc/ipa-polymorphic-call.c b/gcc/ipa-polymorphic-call.c index 30b5db242af..f1905f1609a 100644 --- a/gcc/ipa-polymorphic-call.c +++ b/gcc/ipa-polymorphic-call.c @@ -599,10 +599,11 @@ decl_maybe_in_construction_p (tree base, tree outer_type, return false; } -/* Dump human readable context to F. */ +/* Dump human readable context to F. If NEWLINE is true, it will be terminated + by a newline. */ void -ipa_polymorphic_call_context::dump (FILE *f) const +ipa_polymorphic_call_context::dump (FILE *f, bool newline) const { fprintf (f, " "); if (invalid) @@ -634,7 +635,8 @@ ipa_polymorphic_call_context::dump (FILE *f) const speculative_offset); } } - fprintf(f, "\n"); + if (newline) + fprintf(f, "\n"); } /* Print context to stderr. */ @@ -2130,3 +2132,47 @@ ipa_polymorphic_call_context::possible_dynamic_type_change (bool in_poly_cdtor, else if (in_poly_cdtor) maybe_in_construction = true; } + +/* Return TRUE if this context conveys the same information as OTHER. */ + +bool +ipa_polymorphic_call_context::equal_to + (const ipa_polymorphic_call_context &x) const +{ + if (useless_p ()) + return x.useless_p (); + if (invalid) + return x.invalid; + if (x.useless_p () || x.invalid) + return false; + + if (outer_type) + { + if (!x.outer_type + || !types_odr_comparable (outer_type, x.outer_type) + || !types_same_for_odr (outer_type, x.outer_type) + || offset != x.offset + || maybe_in_construction != x.maybe_in_construction + || maybe_derived_type != x.maybe_derived_type + || dynamic != x.dynamic) + return false; + } + else if (x.outer_type) + return false; + + if (speculative_outer_type) + { + if (!x.speculative_outer_type + || !types_odr_comparable (speculative_outer_type, + x.speculative_outer_type) + || !types_same_for_odr (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) + return false; + + return true; +} diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index db85c7d1e7f..d9331f36219 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -380,8 +380,14 @@ ipa_print_node_jump_functions_for_edge (FILE *f, struct cgraph_edge *cs) fprintf (f, "\n"); } } - if (IPA_EDGE_REF (cs)->polymorphic_call_contexts) - ipa_get_ith_polymorhic_call_context (IPA_EDGE_REF (cs), i)->dump (f); + + struct ipa_polymorphic_call_context *ctx + = ipa_get_ith_polymorhic_call_context (IPA_EDGE_REF (cs), i); + if (ctx && !ctx->useless_p ()) + { + fprintf (f, " Context: "); + ctx->dump (dump_file); + } } } @@ -559,7 +565,8 @@ ipa_set_ancestor_jf (struct ipa_jump_func *jfunc, HOST_WIDE_INT offset, type = NULL_TREE; if (type) type = TYPE_MAIN_VARIANT (type); - gcc_assert (!type_preserved || contains_polymorphic_type_p (type)); + if (!type || !contains_polymorphic_type_p (type)) + type_preserved = false; jfunc->type = IPA_JF_ANCESTOR; jfunc->value.ancestor.formal_id = formal_id; jfunc->value.ancestor.offset = offset; @@ -2622,9 +2629,12 @@ combine_known_type_and_ancestor_jfs (struct ipa_jump_func *src, + ipa_get_jf_ancestor_offset (dst); combined_type = ipa_get_jf_ancestor_type (dst); - ipa_set_jf_known_type (dst, combined_offset, - ipa_get_jf_known_type_base_type (src), - combined_type); + if (combined_type) + ipa_set_jf_known_type (dst, combined_offset, + ipa_get_jf_known_type_base_type (src), + combined_type); + else + dst->type = IPA_JF_UNKNOWN; } /* Update the jump functions associated with call graph edge E when the call @@ -2669,7 +2679,7 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs, struct ipa_polymorphic_call_context ctx = *src_ctx; /* TODO: Make type preserved safe WRT contexts. */ - if (!dst->value.ancestor.agg_preserved) + if (!ipa_get_jf_ancestor_type_preserved (dst)) ctx.possible_dynamic_type_change (e->in_polymorphic_cdtor); ctx.offset_by (dst->value.ancestor.offset); if (!ctx.useless_p ()) @@ -2678,6 +2688,7 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs, count); dst_ctx = ipa_get_ith_polymorhic_call_context (args, i); } + dst_ctx->combine_with (ctx); } if (src->agg.items @@ -2739,7 +2750,7 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs, struct ipa_polymorphic_call_context ctx = *src_ctx; /* TODO: Make type preserved safe WRT contexts. */ - if (!dst->value.ancestor.agg_preserved) + if (!ipa_get_jf_pass_through_type_preserved (dst)) ctx.possible_dynamic_type_change (e->in_polymorphic_cdtor); if (!ctx.useless_p ()) { @@ -3152,41 +3163,24 @@ ipa_impossible_devirt_target (struct cgraph_edge *ie, tree target) /* Try to find a destination for indirect edge IE that corresponds to a virtual call based on a formal parameter which is described by jump function JFUNC and if it can be determined, make it direct and return the direct edge. - Otherwise, return NULL. NEW_ROOT_INFO is the node info that JFUNC lattices - are relative to. */ + Otherwise, return NULL. CTX describes the polymorphic context that the + parameter the call is based on brings along with it. */ static struct cgraph_edge * try_make_edge_direct_virtual_call (struct cgraph_edge *ie, struct ipa_jump_func *jfunc, - struct ipa_node_params *new_root_info, - struct ipa_polymorphic_call_context *ctx_ptr) + struct ipa_polymorphic_call_context ctx) { - tree binfo, target = NULL; + tree target = NULL; bool speculative = false; - bool updated = false; if (!flag_devirtualize) return NULL; - /* If this is call of a function parameter, restrict its type - based on knowlede of the context. */ - if (ctx_ptr && !ie->indirect_info->by_ref) - { - struct ipa_polymorphic_call_context ctx = *ctx_ptr; - - ctx.offset_by (ie->indirect_info->offset); - - 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); - } + gcc_assert (!ie->indirect_info->by_ref); /* Try to do lookup via known virtual table pointer value. */ - if (!ie->indirect_info->by_ref - && (!ie->indirect_info->vptr_changed || flag_devirtualize_speculatively)) + if (!ie->indirect_info->vptr_changed || flag_devirtualize_speculatively) { tree vtable; unsigned HOST_WIDE_INT offset; @@ -3217,67 +3211,44 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie, } } - binfo = ipa_value_from_jfunc (new_root_info, jfunc); - - if (binfo && TREE_CODE (binfo) != TREE_BINFO) - { - struct ipa_polymorphic_call_context ctx (binfo, - ie->indirect_info->otr_type, - ie->indirect_info->offset); - updated |= ie->indirect_info->context.combine_with - (ctx, ie->indirect_info->otr_type); + ipa_polymorphic_call_context ie_context (ie); + vec targets; + bool final; + + ctx.offset_by (ie->indirect_info->offset); + if (ie->indirect_info->vptr_changed) + ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor, + ie->indirect_info->otr_type); + ctx.combine_with (ie_context, ie->indirect_info->otr_type); + targets = possible_polymorphic_call_targets + (ie->indirect_info->otr_type, + ie->indirect_info->otr_token, + ctx, &final); + if (final && targets.length () <= 1) + { + if (targets.length () == 1) + target = targets[0]->decl; + else + target = ipa_impossible_devirt_target (ie, NULL_TREE); } - - if (updated) - { - ipa_polymorphic_call_context context (ie); - vec targets; - bool final; - - targets = possible_polymorphic_call_targets - (ie->indirect_info->otr_type, - ie->indirect_info->otr_token, - context, &final); - if (final && targets.length () <= 1) - { - if (targets.length () == 1) - target = targets[0]->decl; - else - target = ipa_impossible_devirt_target (ie, NULL_TREE); - } - else if (!target && flag_devirtualize_speculatively - && !ie->speculative && ie->maybe_hot_p ()) - { - cgraph_node *n = try_speculative_devirtualization (ie->indirect_info->otr_type, - ie->indirect_info->otr_token, - ie->indirect_info->context); - if (n) - { - target = n->decl; - speculative = true; - } - } - } - - if (binfo && TREE_CODE (binfo) == TREE_BINFO) - { - binfo = get_binfo_at_offset (binfo, ie->indirect_info->offset, - ie->indirect_info->otr_type); - if (binfo) + else if (!target && flag_devirtualize_speculatively + && !ie->speculative && ie->maybe_hot_p ()) + { + cgraph_node *n; + n = try_speculative_devirtualization (ie->indirect_info->otr_type, + ie->indirect_info->otr_token, + ie->indirect_info->context); + if (n) { - tree t = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token, - binfo); - if (t) - { - target = t; - speculative = false; - } + target = n->decl; + speculative = true; } } if (target) { - if (!possible_polymorphic_call_target_p (ie, cgraph_node::get_create (target))) + if (!possible_polymorphic_call_target_p + (ie, cgraph_node::get_create (target))) { if (speculative) return NULL; @@ -3336,11 +3307,9 @@ update_indirect_edges_after_inlining (struct cgraph_edge *cs, new_direct_edge = NULL; else if (ici->polymorphic) { - ipa_polymorphic_call_context *ctx; - ctx = ipa_get_ith_polymorhic_call_context (top, param_index); - new_direct_edge = try_make_edge_direct_virtual_call (ie, jfunc, - new_root_info, - ctx); + ipa_polymorphic_call_context ctx; + ctx = ipa_context_from_jfunc (new_root_info, cs, param_index, jfunc); + new_direct_edge = try_make_edge_direct_virtual_call (ie, jfunc, ctx); } else new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc, @@ -3474,7 +3443,7 @@ propagate_controlled_uses (struct cgraph_edge *cs) { struct cgraph_node *n; struct ipa_ref *ref; - tree t = new_root_info->known_vals[src_idx]; + tree t = new_root_info->known_csts[src_idx]; if (t && TREE_CODE (t) == ADDR_EXPR && TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL @@ -3617,7 +3586,8 @@ ipa_free_node_params_substructures (struct ipa_node_params *info) free (info->lattices); /* Lattice values and their sources are deallocated with their alocation pool. */ - info->known_vals.release (); + info->known_csts.release (); + info->known_contexts.release (); memset (info, 0, sizeof (*info)); } @@ -3892,7 +3862,8 @@ ipa_free_all_structures_after_ipa_cp (void) ipa_free_all_edge_args (); ipa_free_all_node_params (); free_alloc_pool (ipcp_sources_pool); - free_alloc_pool (ipcp_values_pool); + free_alloc_pool (ipcp_cst_values_pool); + free_alloc_pool (ipcp_poly_ctx_values_pool); free_alloc_pool (ipcp_agg_lattice_pool); ipa_unregister_cgraph_hooks (); if (ipa_refdesc_pool) @@ -3911,8 +3882,10 @@ ipa_free_all_structures_after_iinln (void) ipa_unregister_cgraph_hooks (); if (ipcp_sources_pool) free_alloc_pool (ipcp_sources_pool); - if (ipcp_values_pool) - free_alloc_pool (ipcp_values_pool); + if (ipcp_cst_values_pool) + free_alloc_pool (ipcp_cst_values_pool); + if (ipcp_poly_ctx_values_pool) + free_alloc_pool (ipcp_poly_ctx_values_pool); if (ipcp_agg_lattice_pool) free_alloc_pool (ipcp_agg_lattice_pool); if (ipa_refdesc_pool) diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h index fdf48117734..2227c0d4114 100644 --- a/gcc/ipa-prop.h +++ b/gcc/ipa-prop.h @@ -278,13 +278,14 @@ ipa_get_jf_pass_through_agg_preserved (struct ipa_jump_func *jfunc) return jfunc->value.pass_through.agg_preserved; } -/* Return the type_preserved flag of a pass through jump function JFUNC. */ +/* Return true if pass through jump function JFUNC preserves type + information. */ static inline bool ipa_get_jf_pass_through_type_preserved (struct ipa_jump_func *jfunc) { gcc_checking_assert (jfunc->type == IPA_JF_PASS_THROUGH); - return jfunc->value.pass_through.type_preserved; + return jfunc->value.pass_through.agg_preserved; } /* Return the offset of an ancestor jump function JFUNC. */ @@ -324,13 +325,13 @@ ipa_get_jf_ancestor_agg_preserved (struct ipa_jump_func *jfunc) return jfunc->value.ancestor.agg_preserved; } -/* Return the type_preserved flag of an ancestor jump function JFUNC. */ +/* Return true if ancestor jump function JFUNC presrves type information. */ static inline bool ipa_get_jf_ancestor_type_preserved (struct ipa_jump_func *jfunc) { gcc_checking_assert (jfunc->type == IPA_JF_ANCESTOR); - return jfunc->value.ancestor.type_preserved; + return jfunc->value.ancestor.agg_preserved; } /* Summary describing a single formal parameter. */ @@ -363,9 +364,12 @@ struct ipa_node_params /* Only for versioned nodes this field would not be NULL, it points to the node that IPA cp cloned from. */ struct cgraph_node *ipcp_orig_node; - /* If this node is an ipa-cp clone, these are the known values that describe - what it has been specialized for. */ - vec known_vals; + /* If this node is an ipa-cp clone, these are the known constants that + describe what it has been specialized for. */ + vec known_csts; + /* If this node is an ipa-cp clone, these are the known polymorphic contexts + that describe what it has been specialized for. */ + vec known_contexts; /* Whether the param uses analysis and jump function computation has already been performed. */ unsigned analysis_done : 1; @@ -592,7 +596,7 @@ bool ipa_propagate_indirect_call_infos (struct cgraph_edge *cs, /* Indirect edge and binfo processing. */ tree ipa_get_indirect_edge_target (struct cgraph_edge *ie, vec , - vec , + vec, vec ); struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree, bool speculative = false); @@ -615,7 +619,8 @@ void ipa_print_node_jump_functions (FILE *f, struct cgraph_node *node); void ipa_print_all_jump_functions (FILE * f); void ipcp_verify_propagated_values (void); -extern alloc_pool ipcp_values_pool; +extern alloc_pool ipcp_cst_values_pool; +extern alloc_pool ipcp_poly_ctx_values_pool; extern alloc_pool ipcp_sources_pool; extern alloc_pool ipcp_agg_lattice_pool; @@ -716,6 +721,10 @@ int ipa_get_param_decl_index (struct ipa_node_params *, tree); tree ipa_value_from_jfunc (struct ipa_node_params *info, struct ipa_jump_func *jfunc); unsigned int ipcp_transform_function (struct cgraph_node *node); +ipa_polymorphic_call_context ipa_context_from_jfunc (ipa_node_params *, + cgraph_edge *, + int, + ipa_jump_func *); void ipa_dump_param (FILE *, struct ipa_node_params *info, int i); bool ipa_modify_expr (tree *, bool, ipa_parm_adjustment_vec); ipa_parm_adjustment *ipa_get_adjustment_candidate (tree **, bool *, diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index c7d7a904dc0..46493f225e3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2014-11-14 Martin Jambor + + * g++.dg/ipa/devirt-11.C: Dont't run ipa-cp, remove times constraint + from the dump scan. + * g++.dg/ipa/devirt-21.C: Xfail. + * g++.dg/ipa/devirt-24.C: Likewise. + * g++.dg/ipa/devirt-10.C: Removed times constraint from the dump scan. + * g++.dg/ipa/devirt-41.C: Updated the dump scan. + * g++.dg/ipa/devirt-44.C: Likewise. + * g++.dg/ipa/devirt-43.C: Xfail. + 2014-11-14 Jonathan Wakely * g++.dg/abi/abi-tag11.C: New. diff --git a/gcc/testsuite/g++.dg/ipa/devirt-10.C b/gcc/testsuite/g++.dg/ipa/devirt-10.C index 658442e3451..e29d33fa955 100644 --- a/gcc/testsuite/g++.dg/ipa/devirt-10.C +++ b/gcc/testsuite/g++.dg/ipa/devirt-10.C @@ -27,8 +27,6 @@ struct wxBufferedPaintDC : public wxBufferedDC { void OnPaint(wxPaintEvent & event) { wxBufferedPaintDC dc; } -/* IPA-CP should really discover both cases, but for time being the second is handled by inliner. */ -/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline" } } */ -/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "cp" } } */ +/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 2 "cp" } } */ /* { dg-final { cleanup-ipa-dump "inline" } } */ /* { dg-final { cleanup-ipa-dump "cp" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/devirt-11.C b/gcc/testsuite/g++.dg/ipa/devirt-11.C index 3246e214f14..7a698f16967 100644 --- a/gcc/testsuite/g++.dg/ipa/devirt-11.C +++ b/gcc/testsuite/g++.dg/ipa/devirt-11.C @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-ipa-inline -fno-devirtualize-speculatively" } */ +/* { dg-options "-O2 -fno-ipa-cp -fdump-ipa-inline -fno-devirtualize-speculatively" } */ int baz (); struct A { @@ -42,7 +42,5 @@ bar () baz (); c + d; } -/* While inlining function called once we should devirtualize a new call to fn3. - Because fn2 is already removed, we should not devirtualize. */ -/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline" } } */ +/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target" "inline" } } */ /* { dg-final { cleanup-ipa-dump "inline" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/devirt-21.C b/gcc/testsuite/g++.dg/ipa/devirt-21.C index 675bd08d782..99f60afe33c 100644 --- a/gcc/testsuite/g++.dg/ipa/devirt-21.C +++ b/gcc/testsuite/g++.dg/ipa/devirt-21.C @@ -37,5 +37,5 @@ main() { class C c; } -/* { dg-final { scan-ipa-dump "Discovered a virtual call to" "cp" { xfail *-*-* } } } */ +/* { dg-final { scan-ipa-dump "Discovered a virtual call to" "cp" } } */ /* { dg-final { cleanup-ipa-dump "cp" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/devirt-24.C b/gcc/testsuite/g++.dg/ipa/devirt-24.C index 01bfdc2ec50..6ac4b606d88 100644 --- a/gcc/testsuite/g++.dg/ipa/devirt-24.C +++ b/gcc/testsuite/g++.dg/ipa/devirt-24.C @@ -36,7 +36,7 @@ C *b = new (C); sort(f, *b); } } -/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline" } } */ +/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline" { xfail *-*-* } } } */ /* { dg-final { cleanup-ipa-dump "inline" } } */ /* { dg-final { scan-ipa-dump-times "Aggregate passed by reference" 1 "cp" } } */ /* { dg-final { cleanup-ipa-dump "cp" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/devirt-41.C b/gcc/testsuite/g++.dg/ipa/devirt-41.C index 5ba1158f8f7..926c3c49813 100644 --- a/gcc/testsuite/g++.dg/ipa/devirt-41.C +++ b/gcc/testsuite/g++.dg/ipa/devirt-41.C @@ -26,6 +26,6 @@ main() Because the type is in static storage, we know it won't change type in dostuff and from callstack we can tell that is is not in construction/destruction. */ -/* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline" } } */ +/* { dg-final { scan-ipa-dump "Second type is base of first" "inline" } } */ /* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline" } } */ /* { dg-final { cleanup-ipa-dump "inline" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/devirt-43.C b/gcc/testsuite/g++.dg/ipa/devirt-43.C index 9be49e764ea..be1aaa86754 100644 --- a/gcc/testsuite/g++.dg/ipa/devirt-43.C +++ b/gcc/testsuite/g++.dg/ipa/devirt-43.C @@ -23,5 +23,5 @@ t(struct B *b) of type B. This makes A fully specified and we know C::foo is unlikely. FIXME: We could most probably can devirtualize unconditonally because dereference of b in &b->a makes the type known. GIMPLE does not represent this. */ -/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a speculative target" 1 "inline" } } */ +/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a speculative target" 1 "inline" { xfail *-*-* } } } */ /* { dg-final { cleanup-ipa-dump "inline" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/devirt-44.C b/gcc/testsuite/g++.dg/ipa/devirt-44.C index cfc6e554459..214cf95d6e9 100644 --- a/gcc/testsuite/g++.dg/ipa/devirt-44.C +++ b/gcc/testsuite/g++.dg/ipa/devirt-44.C @@ -26,7 +26,7 @@ main() /* Here one invocation of foo is while type is in construction, while other is not. Check that we handle that. */ -/* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline" } } */ +/* { dg-final { scan-ipa-dump "Second type is base of first" "inline" } } */ /* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*A::foo" 1 "inline" } } */ /* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*B::foo" 1 "inline" } } */ /* { dg-final { cleanup-ipa-dump "inline" } } */ -- 2.30.2