Hi, when I tested aggregate IPA-CP on SCCs, I did not notice that IPA-CP confused itself on the testcase and did not work as intended. That will be fixed by the next patch, this one fixes function cgraph_edge_brings_all_agg_vals_for_node which did not work on edges IPA-CP clones and ICEd when it saw them, which is a bit of a problem given that it is there primarily for such edges.
The patch below re-uses a big part of function find_aggregate_values_for_callers_subset rather than re-implementing the propagation once more. It means that aggregate replacement values are traversed a few times in the process but the list should be usually tiny and even in pathologic cases their size is limited by PARAM_IPA_MAX_AGG_ITEMS * number of parameters, so I do not think it's worth a bitmap. Bootstrapped and tested on x86_64-linux. OK for trunk? Thanks, Martin 2012-11-19 Martin Jambor <mjam...@suse.cz> PR tree-optimization/55260 * ipa-cp.c (intersect_aggregates_with_edge): New function. (find_aggregate_values_for_callers_subset): Part moved to the function above. Call it. (cgraph_edge_brings_all_agg_vals_for_node): Reimplemented using intersect_aggregates_with_edge. * testsuite/g++.dg/torture/pr55260-2.C: New test. Index: src/gcc/testsuite/g++.dg/torture/pr55260-2.C =================================================================== *** /dev/null --- src/gcc/testsuite/g++.dg/torture/pr55260-2.C *************** *** 0 **** --- 1,16 ---- + /* { dg-do compile } */ + /* { dg-add-options bind_pic_locally } */ + + struct B + { + virtual void test_suite_finish (); + }; + void + fn1 (B & p2) + { + fn1 (p2); + B & a = p2; + a.test_suite_finish (); + B b; + fn1 (b); + } Index: src/gcc/ipa-cp.c =================================================================== *** src.orig/gcc/ipa-cp.c --- src/gcc/ipa-cp.c *************** intersect_with_agg_replacements (struct *** 2852,2857 **** --- 2852,2978 ---- } } + /* Intersect values in INTER with aggregate values that come along edge CS to + parameter number INDEX and return it. If INTER does not actually exist yet, + copy all incoming values to it. If we determine we ended up with no values + whatsoever, return a released vector. */ + + static vec<ipa_agg_jf_item_t> + intersect_aggregates_with_edge (struct cgraph_edge *cs, int index, + vec<ipa_agg_jf_item_t> inter) + { + struct ipa_jump_func *jfunc; + jfunc = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), index); + if (jfunc->type == IPA_JF_PASS_THROUGH + && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR) + { + struct ipa_node_params *caller_info = IPA_NODE_REF (cs->caller); + int src_idx = ipa_get_jf_pass_through_formal_id (jfunc); + + if (caller_info->ipcp_orig_node) + { + struct cgraph_node *orig_node = caller_info->ipcp_orig_node; + struct ipcp_param_lattices *orig_plats; + orig_plats = ipa_get_parm_lattices (IPA_NODE_REF (orig_node), + src_idx); + if (agg_pass_through_permissible_p (orig_plats, jfunc)) + { + if (!inter.exists ()) + inter = agg_replacements_to_vector (cs->caller, 0); + else + intersect_with_agg_replacements (cs->caller, src_idx, + &inter, 0); + } + } + else + { + struct ipcp_param_lattices *src_plats; + src_plats = ipa_get_parm_lattices (caller_info, src_idx); + if (agg_pass_through_permissible_p (src_plats, jfunc)) + { + /* Currently we do not produce clobber aggregate jump + functions, adjust when we do. */ + gcc_checking_assert (!jfunc->agg.items); + if (!inter.exists ()) + inter = copy_plats_to_inter (src_plats, 0); + else + intersect_with_plats (src_plats, &inter, 0); + } + } + } + else if (jfunc->type == IPA_JF_ANCESTOR + && ipa_get_jf_ancestor_agg_preserved (jfunc)) + { + struct ipa_node_params *caller_info = IPA_NODE_REF (cs->caller); + int src_idx = ipa_get_jf_ancestor_formal_id (jfunc); + struct ipcp_param_lattices *src_plats; + HOST_WIDE_INT delta = ipa_get_jf_ancestor_offset (jfunc); + + if (caller_info->ipcp_orig_node) + { + if (!inter.exists ()) + inter = agg_replacements_to_vector (cs->caller, delta); + else + intersect_with_agg_replacements (cs->caller, index, &inter, + delta); + } + else + { + src_plats = ipa_get_parm_lattices (caller_info, src_idx);; + /* Currently we do not produce clobber aggregate jump + functions, adjust when we do. */ + gcc_checking_assert (!src_plats->aggs || !jfunc->agg.items); + if (!inter.exists ()) + inter = copy_plats_to_inter (src_plats, delta); + else + intersect_with_plats (src_plats, &inter, delta); + } + } + else if (jfunc->agg.items) + { + struct ipa_agg_jf_item *item; + int k; + + if (!inter.exists ()) + for (unsigned i = 0; i < jfunc->agg.items->length (); i++) + inter.safe_push ((*jfunc->agg.items)[i]); + else + FOR_EACH_VEC_ELT (inter, k, item) + { + int l = 0; + bool found = false;; + + if (!item->value) + continue; + + while ((unsigned) l < jfunc->agg.items->length ()) + { + struct ipa_agg_jf_item *ti; + ti = &(*jfunc->agg.items)[l]; + if (ti->offset > item->offset) + break; + if (ti->offset == item->offset) + { + gcc_checking_assert (ti->value); + if (values_equal_for_ipcp_p (item->value, + ti->value)) + found = true; + break; + } + l++; + } + if (!found) + item->value = NULL; + } + } + else + { + inter.release(); + return vec<ipa_agg_jf_item_t>(); + } + return inter; + } + /* Look at edges in CALLERS and collect all known aggregate values that arrive from all of them. */ *************** find_aggregate_values_for_callers_subset *** 2885,2995 **** FOR_EACH_VEC_ELT (callers, j, cs) { ! struct ipa_jump_func *jfunc; ! jfunc = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i); ! if (jfunc->type == IPA_JF_PASS_THROUGH ! && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR) ! { ! struct ipa_node_params *caller_info = IPA_NODE_REF (cs->caller); ! int src_idx = ipa_get_jf_pass_through_formal_id (jfunc); ! ! if (caller_info->ipcp_orig_node) ! { ! struct cgraph_node *orig_node = caller_info->ipcp_orig_node; ! struct ipcp_param_lattices *orig_plats; ! orig_plats = ipa_get_parm_lattices (IPA_NODE_REF (orig_node), ! src_idx); ! if (agg_pass_through_permissible_p (orig_plats, jfunc)) ! { ! if (!inter.exists ()) ! inter = agg_replacements_to_vector (cs->caller, 0); ! else ! intersect_with_agg_replacements (cs->caller, src_idx, ! &inter, 0); ! } ! } ! else ! { ! struct ipcp_param_lattices *src_plats; ! src_plats = ipa_get_parm_lattices (caller_info, src_idx); ! if (agg_pass_through_permissible_p (src_plats, jfunc)) ! { ! /* Currently we do not produce clobber aggregate jump ! functions, adjust when we do. */ ! gcc_checking_assert (!jfunc->agg.items); ! if (!inter.exists ()) ! inter = copy_plats_to_inter (src_plats, 0); ! else ! intersect_with_plats (src_plats, &inter, 0); ! } ! } ! } ! else if (jfunc->type == IPA_JF_ANCESTOR ! && ipa_get_jf_ancestor_agg_preserved (jfunc)) ! { ! struct ipa_node_params *caller_info = IPA_NODE_REF (cs->caller); ! int src_idx = ipa_get_jf_ancestor_formal_id (jfunc); ! struct ipcp_param_lattices *src_plats; ! HOST_WIDE_INT delta = ipa_get_jf_ancestor_offset (jfunc); ! ! if (caller_info->ipcp_orig_node) ! { ! if (!inter.exists ()) ! inter = agg_replacements_to_vector (cs->caller, delta); ! else ! intersect_with_agg_replacements (cs->caller, i, &inter, ! delta); ! } ! else ! { ! src_plats = ipa_get_parm_lattices (caller_info, src_idx);; ! /* Currently we do not produce clobber aggregate jump ! functions, adjust when we do. */ ! gcc_checking_assert (!src_plats->aggs || !jfunc->agg.items); ! if (!inter.exists ()) ! inter = copy_plats_to_inter (src_plats, delta); ! else ! intersect_with_plats (src_plats, &inter, delta); ! } ! } ! else if (jfunc->agg.items) ! { ! int k; ! ! if (!inter.exists ()) ! for (unsigned i = 0; i < jfunc->agg.items->length (); i++) ! inter.safe_push ((*jfunc->agg.items)[i]); ! else ! FOR_EACH_VEC_ELT (inter, k, item) ! { ! int l = 0; ! bool found = false;; ! ! if (!item->value) ! continue; ! ! while ((unsigned) l < jfunc->agg.items->length ()) ! { ! struct ipa_agg_jf_item *ti; ! ti = &(*jfunc->agg.items)[l]; ! if (ti->offset > item->offset) ! break; ! if (ti->offset == item->offset) ! { ! gcc_checking_assert (ti->value); ! if (values_equal_for_ipcp_p (item->value, ! ti->value)) ! found = true; ! break; ! } ! l++; ! } ! if (!found) ! item->value = NULL; ! } ! } ! else ! goto next_param; if (!inter.exists ()) goto next_param; --- 3006,3012 ---- FOR_EACH_VEC_ELT (callers, j, cs) { ! inter = intersect_aggregates_with_edge (cs, i, inter); if (!inter.exists ()) goto next_param; *************** static bool *** 3081,3117 **** cgraph_edge_brings_all_agg_vals_for_node (struct cgraph_edge *cs, struct cgraph_node *node) { ! struct ipa_node_params *caller_info = IPA_NODE_REF (cs->caller); struct ipa_agg_replacement_value *aggval; aggval = ipa_get_agg_replacements_for_node (node); ! while (aggval) { ! bool found = false; struct ipcp_param_lattices *plats; ! plats = ipa_get_parm_lattices (caller_info, aggval->index); ! if (plats->aggs_bottom || plats->aggs_contain_variable) return false; - for (struct ipcp_agg_lattice *aglat = plats->aggs; - aglat; - aglat = aglat->next) - if (aglat->offset == aggval->offset) - { - if (ipa_lat_is_single_const (aglat) - && values_equal_for_ipcp_p (aggval->value, - aglat->values->value)) - { - found = true; - break; - } - else - return false; - } ! if (!found) return false; ! aggval = aggval->next; } return true; } --- 3098,3160 ---- cgraph_edge_brings_all_agg_vals_for_node (struct cgraph_edge *cs, struct cgraph_node *node) { ! struct ipa_node_params *orig_caller_info = IPA_NODE_REF (cs->caller); struct ipa_agg_replacement_value *aggval; + int i, ec, count; aggval = ipa_get_agg_replacements_for_node (node); ! if (!aggval) ! return true; ! ! count = ipa_get_param_count (IPA_NODE_REF (node)); ! ec = ipa_get_cs_argument_count (IPA_EDGE_REF (cs)); ! if (ec < count) ! for (struct ipa_agg_replacement_value *av = aggval; av; av = av->next) ! if (aggval->index >= ec) ! return false; ! ! if (orig_caller_info->ipcp_orig_node) ! orig_caller_info = IPA_NODE_REF (orig_caller_info->ipcp_orig_node); ! ! for (i = 0; i < count; i++) { ! static vec<ipa_agg_jf_item_t> values = vec<ipa_agg_jf_item_t>(); struct ipcp_param_lattices *plats; ! bool interesting = false; ! for (struct ipa_agg_replacement_value *av = aggval; av; av = av->next) ! if (aggval->index == i) ! { ! interesting = true; ! break; ! } ! if (!interesting) ! continue; ! ! plats = ipa_get_parm_lattices (orig_caller_info, aggval->index); ! if (plats->aggs_bottom) return false; ! values = intersect_aggregates_with_edge (cs, i, values); ! if (!values.exists()) return false; ! for (struct ipa_agg_replacement_value *av = aggval; av; av = av->next) ! if (aggval->index == i) ! { ! struct ipa_agg_jf_item *item; ! int j; ! bool found = false; ! FOR_EACH_VEC_ELT (values, j, item) ! if (item->value ! && item->offset == av->offset ! && values_equal_for_ipcp_p (item->value, av->value)) ! found = true; ! if (!found) ! { ! values.release(); ! return false; ! } ! } } return true; }