Feng, this now looks fine to me: what is the current schedule to get this merged?
Thanks, Philipp. > On 19.09.2019, at 16:30, Feng Xue OS <fxue at os dot amperecomputing dot com> > wrote: > > Fix a bug on unary/binary operation check. > > Feng > --- > diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c > index 33d52fe5537..f218f1093b8 100644 > --- a/gcc/ipa-cp.c > +++ b/gcc/ipa-cp.c > @@ -1244,23 +1244,23 @@ initialize_node_lattices (struct cgraph_node *node) > } > } > > -/* Return the result of a (possibly arithmetic) pass through jump function > - JFUNC on the constant value INPUT. RES_TYPE is the type of the parameter > - to which the result is passed. Return NULL_TREE if that cannot be > - determined or be considered an interprocedural invariant. */ > +/* Return the result of a (possibly arithmetic) operation on the constant > + value INPUT. OPERAND is 2nd operand for binary operation. RES_TYPE is > + the type of the parameter to which the result is passed. Return > + NULL_TREE if that cannot be determined or be considered an > + interprocedural invariant. */ > > static tree > -ipa_get_jf_pass_through_result (struct ipa_jump_func *jfunc, tree input, > - tree res_type) > +ipa_get_jf_arith_result (enum tree_code opcode, tree input, tree operand, > + tree res_type) > { > tree res; > > - if (ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR) > + if (opcode == NOP_EXPR) > return input; > if (!is_gimple_ip_invariant (input)) > return NULL_TREE; > > - tree_code opcode = ipa_get_jf_pass_through_operation (jfunc); > if (!res_type) > { > if (TREE_CODE_CLASS (opcode) == tcc_comparison) > @@ -1274,8 +1274,7 @@ ipa_get_jf_pass_through_result (struct ipa_jump_func > *jfunc, tree input, > if (TREE_CODE_CLASS (opcode) == tcc_unary) > res = fold_unary (opcode, res_type, input); > else > - res = fold_binary (opcode, res_type, input, > - ipa_get_jf_pass_through_operand (jfunc)); > + res = fold_binary (opcode, res_type, input, operand); > > if (res && !is_gimple_ip_invariant (res)) > return NULL_TREE; > @@ -1283,6 +1282,21 @@ ipa_get_jf_pass_through_result (struct ipa_jump_func > *jfunc, tree input, > return res; > } > > +/* Return the result of a (possibly arithmetic) pass through jump function > + JFUNC on the constant value INPUT. RES_TYPE is the type of the parameter > + to which the result is passed. Return NULL_TREE if that cannot be > + determined or be considered an interprocedural invariant. */ > + > +static tree > +ipa_get_jf_pass_through_result (struct ipa_jump_func *jfunc, tree input, > + tree res_type) > +{ > + return ipa_get_jf_arith_result (ipa_get_jf_pass_through_operation (jfunc), > + input, > + ipa_get_jf_pass_through_operand (jfunc), > + res_type); > +} > + > /* Return the result of an ancestor jump function JFUNC on the constant value > INPUT. Return NULL_TREE if that cannot be determined. */ > > @@ -1416,6 +1430,146 @@ ipa_context_from_jfunc (ipa_node_params *info, > cgraph_edge *cs, int csidx, > return ctx; > } > > +/* See if NODE is a clone with a known aggregate value at a given OFFSET of a > + parameter with the given INDEX. */ > + > +static tree > +get_clone_agg_value (struct cgraph_node *node, HOST_WIDE_INT offset, > + int index) > +{ > + struct ipa_agg_replacement_value *aggval; > + > + aggval = ipa_get_agg_replacements_for_node (node); > + while (aggval) > + { > + if (aggval->offset == offset > + && aggval->index == index) > + return aggval->value; > + aggval = aggval->next; > + } > + return NULL_TREE; > +} > + > +/* Determine whether ITEM, jump function for an aggregate part, evaluates to > a > + single known constant value and if so, return it. Otherwise return NULL. > + NODE and INFO describes the caller node or the one it is inlined to, and > + its related info. */ > + > +static tree > +ipa_agg_value_from_node (class ipa_node_params *info, > + struct cgraph_node *node, > + struct ipa_agg_jf_item *item) > +{ > + tree value = NULL_TREE; > + int src_idx; > + > + if (item->offset < 0 || item->jftype == IPA_JF_UNKNOWN) > + return NULL_TREE; > + > + if (item->jftype == IPA_JF_CONST) > + return item->value.constant; > + > + gcc_checking_assert (item->jftype == IPA_JF_PASS_THROUGH > + || item->jftype == IPA_JF_LOAD_AGG); > + > + src_idx = item->value.pass_through.formal_id; > + > + if (info->ipcp_orig_node) > + { > + if (item->jftype == IPA_JF_PASS_THROUGH) > + value = info->known_csts[src_idx]; > + else > + value = get_clone_agg_value (node, item->value.load_agg.offset, > + src_idx); > + } > + else if (info->lattices) > + { > + class ipcp_param_lattices *src_plats > + = ipa_get_parm_lattices (info, src_idx); > + > + if (item->jftype == IPA_JF_PASS_THROUGH) > + { > + struct ipcp_lattice<tree> *lat = &src_plats->itself; > + > + if (!lat->is_single_const ()) > + return NULL_TREE; > + > + value = lat->values->value; > + } > + else if (src_plats->aggs > + && !src_plats->aggs_bottom > + && !src_plats->aggs_contain_variable > + && src_plats->aggs_by_ref == item->value.load_agg.by_ref) > + { > + struct ipcp_agg_lattice *aglat; > + > + for (aglat = src_plats->aggs; aglat; aglat = aglat->next) > + { > + if (aglat->offset > item->value.load_agg.offset) > + break; > + > + if (aglat->offset == item->value.load_agg.offset) > + { > + if (aglat->is_single_const ()) > + value = aglat->values->value; > + break; > + } > + } > + } > + } > + > + if (!value) > + return NULL_TREE; > + > + if (item->jftype == IPA_JF_LOAD_AGG) > + { > + tree load_type = item->value.load_agg.type; > + tree value_type = TREE_TYPE (value); > + > + /* Ensure value type is compatible with load type. */ > + if (!useless_type_conversion_p (load_type, value_type)) > + return NULL_TREE; > + } > + > + return ipa_get_jf_arith_result (item->value.pass_through.operation, > + value, > + item->value.pass_through.operand, > + item->type); > +} > + > +/* Determine whether AGG_JFUNC evaluates to a set of known constant value for > + an aggregate and if so, return it. Otherwise return an empty set. NODE > + and INFO describes the caller node or the one it is inlined to, and its > + related info. */ > + > +struct ipa_agg_value_set > +ipa_agg_value_set_from_jfunc (class ipa_node_params *info, cgraph_node *node, > + struct ipa_agg_jump_function *agg_jfunc) > +{ > + struct ipa_agg_value_set agg; > + struct ipa_agg_jf_item *item; > + int i; > + > + agg.items = vNULL; > + agg.by_ref = agg_jfunc->by_ref; > + > + FOR_EACH_VEC_SAFE_ELT (agg_jfunc->items, i, item) > + { > + tree value = ipa_agg_value_from_node (info, node, item); > + > + if (value) > + { > + struct ipa_agg_value value_item; > + > + value_item.offset = item->offset; > + value_item.value = value; > + > + agg.items.safe_push (value_item); > + } > + } > + return agg; > +} > + > /* 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 > the same time. */ > @@ -1592,16 +1746,25 @@ ipcp_lattice<valtype>::add_value (valtype newval, > cgraph_edge *cs, > return true; > } > > -/* Propagate values through a pass-through jump function JFUNC associated > with > - edge CS, taking values from SRC_LAT and putting them into DEST_LAT. > SRC_IDX > - is the index of the source parameter. PARM_TYPE is the type of the > - parameter to which the result is passed. */ > +/* Propagate values through an arithmetic transformation described by a jump > + function associated with edge CS, taking values from SRC_LAT and putting > + them into DEST_LAT. OPND1_TYPE is expected type for the values in > SRC_LAT. > + OPND2 is a constant value if transformation is a binary operation. > + SRC_OFFSET specifies offset in an aggregate if SRC_LAT describes lattice > of > + a part of the aggregate. SRC_IDX is the index of the source parameter. > + RES_TYPE is the value type of result being propagated into. Return true > if > + DEST_LAT changed. */ > > static bool > -propagate_vals_across_pass_through (cgraph_edge *cs, ipa_jump_func *jfunc, > - ipcp_lattice<tree> *src_lat, > - ipcp_lattice<tree> *dest_lat, int src_idx, > - tree parm_type) > +propagate_vals_across_arith_jfunc (cgraph_edge *cs, > + enum tree_code opcode, > + tree opnd1_type, > + tree opnd2, > + ipcp_lattice<tree> *src_lat, > + ipcp_lattice<tree> *dest_lat, > + HOST_WIDE_INT src_offset, > + int src_idx, > + tree res_type) > { > ipcp_value<tree> *src_val; > bool ret = false; > @@ -1611,17 +1774,22 @@ propagate_vals_across_pass_through (cgraph_edge *cs, > ipa_jump_func *jfunc, > number of them and we would just make lattices bottom. If this condition > is ever relaxed we have to detect self-feeding recursive calls in > cgraph_edge_brings_value_p in a smarter way. */ > - if ((ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR) > - && ipa_edge_within_scc (cs)) > + if (opcode != NOP_EXPR && ipa_edge_within_scc (cs)) > ret = dest_lat->set_contains_variable (); > else > for (src_val = src_lat->values; src_val; src_val = src_val->next) > { > - tree cstval = ipa_get_jf_pass_through_result (jfunc, src_val->value, > - parm_type); > + tree opnd1 = src_val->value; > + tree cstval = NULL_TREE; > + > + /* Skip source values that is incompatible with specified type. */ > + if (!opnd1_type > + || useless_type_conversion_p (opnd1_type, TREE_TYPE (opnd1))) > + cstval = ipa_get_jf_arith_result (opcode, opnd1, opnd2, res_type); > > if (cstval) > - ret |= dest_lat->add_value (cstval, cs, src_val, src_idx); > + ret |= dest_lat->add_value (cstval, cs, src_val, src_idx, > + src_offset); > else > ret |= dest_lat->set_contains_variable (); > } > @@ -1629,6 +1797,24 @@ propagate_vals_across_pass_through (cgraph_edge *cs, > ipa_jump_func *jfunc, > return ret; > } > > +/* Propagate values through a pass-through jump function JFUNC associated > with > + edge CS, taking values from SRC_LAT and putting them into DEST_LAT. > SRC_IDX > + is the index of the source parameter. PARM_TYPE is the type of the > + parameter to which the result is passed. */ > + > +static bool > +propagate_vals_across_pass_through (cgraph_edge *cs, ipa_jump_func *jfunc, > + ipcp_lattice<tree> *src_lat, > + ipcp_lattice<tree> *dest_lat, int src_idx, > + tree parm_type) > +{ > + return propagate_vals_across_arith_jfunc (cs, > + ipa_get_jf_pass_through_operation (jfunc), > + NULL_TREE, > + ipa_get_jf_pass_through_operand (jfunc), > + src_lat, dest_lat, -1, src_idx, parm_type); > +} > + > /* Propagate values through an ancestor jump function JFUNC associated with > edge CS, taking values from SRC_LAT and putting them into DEST_LAT. > SRC_IDX > is the index of the source parameter. */ > @@ -1789,7 +1975,6 @@ propagate_context_across_jump_function (cgraph_edge *cs, > added_sth = true; > } > } > - > } > > prop_fail: > @@ -2145,6 +2330,85 @@ agg_pass_through_permissible_p (class > ipcp_param_lattices *src_plats, > || ipa_get_jf_pass_through_agg_preserved (jfunc)); > } > > +/* Propagate values through ITEM, jump function for a part of an aggregate, > + into corresponding aggregate lattice AGLAT. CS is the call graph edge > + associated with the jump function. Return true if AGLAT changed in any > + way. */ > + > +static bool > +propagate_aggregate_lattice (struct cgraph_edge *cs, > + struct ipa_agg_jf_item *item, > + struct ipcp_agg_lattice *aglat) > +{ > + class ipa_node_params *caller_info; > + class ipcp_param_lattices *src_plats; > + struct ipcp_lattice<tree> *src_lat; > + HOST_WIDE_INT src_offset; > + int src_idx; > + tree load_type; > + bool ret; > + > + if (item->jftype == IPA_JF_CONST) > + { > + tree value = item->value.constant; > + > + gcc_checking_assert (is_gimple_ip_invariant (value)); > + return aglat->add_value (value, cs, NULL, 0); > + } > + > + gcc_checking_assert (item->jftype == IPA_JF_PASS_THROUGH > + || item->jftype == IPA_JF_LOAD_AGG); > + > + caller_info = IPA_NODE_REF (cs->caller); > + src_idx = item->value.pass_through.formal_id; > + src_plats = ipa_get_parm_lattices (caller_info, src_idx); > + > + if (item->jftype == IPA_JF_PASS_THROUGH) > + { > + load_type = NULL_TREE; > + src_lat = &src_plats->itself; > + src_offset = -1; > + } > + else > + { > + HOST_WIDE_INT load_offset = item->value.load_agg.offset; > + struct ipcp_agg_lattice *src_aglat; > + > + for (src_aglat = src_plats->aggs; src_aglat; src_aglat = > src_aglat->next) > + if (src_aglat->offset >= load_offset) > + break; > + > + load_type = item->value.load_agg.type; > + if (!src_aglat > + || src_aglat->offset > load_offset > + || src_aglat->size != tree_to_shwi (TYPE_SIZE (load_type)) > + || src_plats->aggs_by_ref != item->value.load_agg.by_ref) > + return aglat->set_contains_variable (); > + > + src_lat = src_aglat; > + src_offset = load_offset; > + } > + > + if (src_lat->bottom > + || (!ipcp_versionable_function_p (cs->caller) > + && !src_lat->is_single_const ())) > + return aglat->set_contains_variable (); > + > + ret = propagate_vals_across_arith_jfunc (cs, > + item->value.pass_through.operation, > + load_type, > + item->value.pass_through.operand, > + src_lat, aglat, > + src_offset, > + src_idx, > + item->type); > + > + if (src_lat->contains_variable) > + ret |= aglat->set_contains_variable (); > + > + return ret; > +} > + > /* Propagate scalar values across jump function JFUNC that is associated with > edge CS and put the values into DEST_LAT. */ > > @@ -2212,15 +2476,14 @@ propagate_aggs_across_jump_function (struct > cgraph_edge *cs, > { > HOST_WIDE_INT val_size; > > - if (item->offset < 0) > + if (item->offset < 0 || item->jftype == IPA_JF_UNKNOWN) > continue; > - gcc_checking_assert (is_gimple_ip_invariant (item->value)); > - val_size = tree_to_uhwi (TYPE_SIZE (TREE_TYPE (item->value))); > + val_size = tree_to_shwi (TYPE_SIZE (item->type)); > > if (merge_agg_lats_step (dest_plats, item->offset, val_size, > &aglat, pre_existing, &ret)) > { > - ret |= (*aglat)->add_value (item->value, cs, NULL, 0, 0); > + ret |= propagate_aggregate_lattice (cs, item, *aglat); > aglat = &(*aglat)->next; > } > else if (dest_plats->aggs_bottom) > @@ -2326,7 +2589,7 @@ static tree > ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, > vec<tree> known_csts, > vec<ipa_polymorphic_call_context> > known_contexts, > - vec<ipa_agg_jump_function_p> known_aggs, > + vec<ipa_agg_value_set> known_aggs, > struct ipa_agg_replacement_value *agg_reps, > bool *speculative) > { > @@ -2364,9 +2627,9 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, > } > if (!t) > { > - struct ipa_agg_jump_function *agg; > + struct ipa_agg_value_set *agg; > if (known_aggs.length () > (unsigned int) param_index) > - agg = known_aggs[param_index]; > + agg = &known_aggs[param_index]; > else > agg = NULL; > bool from_global_constant; > @@ -2420,8 +2683,7 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, > if (!t && known_aggs.length () > (unsigned int) param_index > && !ie->indirect_info->by_ref) > { > - struct ipa_agg_jump_function *agg; > - agg = known_aggs[param_index]; > + struct ipa_agg_value_set *agg = &known_aggs[param_index]; > t = ipa_find_agg_cst_for_param (agg, known_csts[param_index], > ie->indirect_info->offset, true); > } > @@ -2543,7 +2805,7 @@ tree > ipa_get_indirect_edge_target (struct cgraph_edge *ie, > vec<tree> known_csts, > vec<ipa_polymorphic_call_context> known_contexts, > - vec<ipa_agg_jump_function_p> known_aggs, > + vec<ipa_agg_value_set> known_aggs, > bool *speculative) > { > return ipa_get_indirect_edge_target_1 (ie, known_csts, known_contexts, > @@ -2557,7 +2819,7 @@ static int > devirtualization_time_bonus (struct cgraph_node *node, > vec<tree> known_csts, > vec<ipa_polymorphic_call_context> known_contexts, > - vec<ipa_agg_jump_function_p> known_aggs) > + vec<ipa_agg_value_set> known_aggs) > { > struct cgraph_edge *ie; > int res = 0; > @@ -2691,25 +2953,25 @@ good_cloning_opportunity_p (struct cgraph_node *node, > int time_benefit, > /* Return all context independent values from aggregate lattices in PLATS in a > vector. Return NULL if there are none. */ > > -static vec<ipa_agg_jf_item, va_gc> * > +static vec<ipa_agg_value> > context_independent_aggregate_values (class ipcp_param_lattices *plats) > { > - vec<ipa_agg_jf_item, va_gc> *res = NULL; > + vec<ipa_agg_value> res = vNULL; > > if (plats->aggs_bottom > || plats->aggs_contain_variable > || plats->aggs_count == 0) > - return NULL; > + return vNULL; > > for (struct ipcp_agg_lattice *aglat = plats->aggs; > aglat; > aglat = aglat->next) > if (aglat->is_single_const ()) > { > - struct ipa_agg_jf_item item; > + struct ipa_agg_value item; > item.offset = aglat->offset; > item.value = aglat->values->value; > - vec_safe_push (res, item); > + res.safe_push (item); > } > return res; > } > @@ -2725,7 +2987,7 @@ gather_context_independent_values (class > ipa_node_params *info, > vec<tree> *known_csts, > vec<ipa_polymorphic_call_context> > *known_contexts, > - vec<ipa_agg_jump_function> *known_aggs, > + vec<ipa_agg_value_set> *known_aggs, > int *removable_params_cost) > { > int i, count = ipa_get_param_count (info); > @@ -2775,40 +3037,20 @@ gather_context_independent_values (class > ipa_node_params *info, > > if (known_aggs) > { > - vec<ipa_agg_jf_item, va_gc> *agg_items; > - struct ipa_agg_jump_function *ajf; > + vec<ipa_agg_value> agg_items; > + struct ipa_agg_value_set *agg; > > agg_items = context_independent_aggregate_values (plats); > - ajf = &(*known_aggs)[i]; > - ajf->items = agg_items; > - ajf->by_ref = plats->aggs_by_ref; > - ret |= agg_items != NULL; > + agg = &(*known_aggs)[i]; > + agg->items = agg_items; > + agg->by_ref = plats->aggs_by_ref; > + ret |= !agg_items.is_empty (); > } > } > > return ret; > } > > -/* The current interface in ipa-inline-analysis requires a pointer vector. > - Create it. > - > - FIXME: That interface should be re-worked, this is slightly silly. Still, > - I'd like to discuss how to change it first and this demonstrates the > - issue. */ > - > -static vec<ipa_agg_jump_function_p> > -agg_jmp_p_vec_for_t_vec (vec<ipa_agg_jump_function> known_aggs) > -{ > - vec<ipa_agg_jump_function_p> ret; > - struct ipa_agg_jump_function *ajf; > - int i; > - > - ret.create (known_aggs.length ()); > - FOR_EACH_VEC_ELT (known_aggs, i, ajf) > - ret.quick_push (ajf); > - return ret; > -} > - > /* Perform time and size measurement of NODE with the context given in > KNOWN_CSTS, KNOWN_CONTEXTS and KNOWN_AGGS, calculate the benefit and cost > given BASE_TIME of the node without specialization, REMOVABLE_PARAMS_COST > of > @@ -2818,7 +3060,7 @@ agg_jmp_p_vec_for_t_vec (vec<ipa_agg_jump_function> > known_aggs) > static void > perform_estimation_of_a_value (cgraph_node *node, vec<tree> known_csts, > vec<ipa_polymorphic_call_context> known_contexts, > - vec<ipa_agg_jump_function_p> known_aggs_ptrs, > + vec<ipa_agg_value_set> known_aggs, > int removable_params_cost, > int est_move_cost, ipcp_value_base *val) > { > @@ -2827,7 +3069,7 @@ perform_estimation_of_a_value (cgraph_node *node, > vec<tree> known_csts, > ipa_hints hints; > > estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts, > - known_aggs_ptrs, &size, &time, > + known_aggs, &size, &time, > &base_time, &hints); > base_time -= time; > if (base_time > 65535) > @@ -2841,7 +3083,7 @@ perform_estimation_of_a_value (cgraph_node *node, > vec<tree> known_csts, > else > time_benefit = base_time.to_int () > + devirtualization_time_bonus (node, known_csts, known_contexts, > - known_aggs_ptrs) > + known_aggs) > + hint_time_bonus (hints) > + removable_params_cost + est_move_cost; > > @@ -2867,8 +3109,7 @@ estimate_local_effects (struct cgraph_node *node) > int i, count = ipa_get_param_count (info); > vec<tree> known_csts; > vec<ipa_polymorphic_call_context> known_contexts; > - vec<ipa_agg_jump_function> known_aggs; > - vec<ipa_agg_jump_function_p> known_aggs_ptrs; > + vec<ipa_agg_value_set> known_aggs; > bool always_const; > int removable_params_cost; > > @@ -2881,9 +3122,8 @@ estimate_local_effects (struct cgraph_node *node) > always_const = gather_context_independent_values (info, &known_csts, > &known_contexts, > &known_aggs, > &removable_params_cost); > - known_aggs_ptrs = agg_jmp_p_vec_for_t_vec (known_aggs); > int devirt_bonus = devirtualization_time_bonus (node, known_csts, > - known_contexts, known_aggs_ptrs); > + known_contexts, known_aggs); > if (always_const || devirt_bonus > || (removable_params_cost && node->local.can_change_signature)) > { > @@ -2896,7 +3136,7 @@ estimate_local_effects (struct cgraph_node *node) > node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats, > false); > estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts, > - known_aggs_ptrs, &size, &time, > + known_aggs, &size, &time, > &base_time, &hints); > time -= devirt_bonus; > time -= hint_time_bonus (hints); > @@ -2959,7 +3199,7 @@ estimate_local_effects (struct cgraph_node *node) > > int emc = estimate_move_cost (TREE_TYPE (val->value), true); > perform_estimation_of_a_value (node, known_csts, known_contexts, > - known_aggs_ptrs, > + known_aggs, > removable_params_cost, emc, val); > > if (dump_file && (dump_flags & TDF_DETAILS)) > @@ -2994,7 +3234,7 @@ estimate_local_effects (struct cgraph_node *node) > { > known_contexts[i] = val->value; > perform_estimation_of_a_value (node, known_csts, known_contexts, > - known_aggs_ptrs, > + known_aggs, > removable_params_cost, 0, val); > > if (dump_file && (dump_flags & TDF_DETAILS)) > @@ -3013,13 +3253,13 @@ estimate_local_effects (struct cgraph_node *node) > for (i = 0; i < count; i++) > { > class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i); > - struct ipa_agg_jump_function *ajf; > + struct ipa_agg_value_set *agg; > struct ipcp_agg_lattice *aglat; > > if (plats->aggs_bottom || !plats->aggs) > continue; > > - ajf = &known_aggs[i]; > + agg = &known_aggs[i]; > for (aglat = plats->aggs; aglat; aglat = aglat->next) > { > ipcp_value<tree> *val; > @@ -3031,14 +3271,14 @@ estimate_local_effects (struct cgraph_node *node) > > for (val = aglat->values; val; val = val->next) > { > - struct ipa_agg_jf_item item; > + struct ipa_agg_value item; > > item.offset = aglat->offset; > item.value = val->value; > - vec_safe_push (ajf->items, item); > + agg->items.safe_push (item); > > perform_estimation_of_a_value (node, known_csts, known_contexts, > - known_aggs_ptrs, > + known_aggs, > removable_params_cost, 0, val); > > if (dump_file && (dump_flags & TDF_DETAILS)) > @@ -3054,18 +3294,14 @@ estimate_local_effects (struct cgraph_node *node) > val->local_time_benefit, val->local_size_cost); > } > > - ajf->items->pop (); > + agg->items.pop (); > } > } > } > > - for (i = 0; i < count; i++) > - vec_free (known_aggs[i].items); > - > known_csts.release (); > known_contexts.release (); > - known_aggs.release (); > - known_aggs_ptrs.release (); > + ipa_release_agg_values (known_aggs); > } > > > @@ -3433,26 +3669,6 @@ edge_clone_summary_t::duplicate (cgraph_edge > *src_edge, cgraph_edge *dst_edge, > src_data->next_clone = dst_edge; > } > > -/* See if NODE is a clone with a known aggregate value at a given OFFSET of a > - parameter with the given INDEX. */ > - > -static tree > -get_clone_agg_value (struct cgraph_node *node, HOST_WIDE_INT offset, > - int index) > -{ > - struct ipa_agg_replacement_value *aggval; > - > - aggval = ipa_get_agg_replacements_for_node (node); > - while (aggval) > - { > - if (aggval->offset == offset > - && aggval->index == index) > - return aggval->value; > - aggval = aggval->next; > - } > - return NULL_TREE; > -} > - > /* Return true is NODE is DEST or its clone for all contexts. */ > > static bool > @@ -4074,10 +4290,10 @@ find_more_contexts_for_caller_subset (cgraph_node > *node, > /* Go through PLATS and create a vector of values consisting of values and > offsets (minus OFFSET) of lattices that contain only a single value. */ > > -static vec<ipa_agg_jf_item> > +static vec<ipa_agg_value> > copy_plats_to_inter (class ipcp_param_lattices *plats, HOST_WIDE_INT offset) > { > - vec<ipa_agg_jf_item> res = vNULL; > + vec<ipa_agg_value> res = vNULL; > > if (!plats->aggs || plats->aggs_contain_variable || plats->aggs_bottom) > return vNULL; > @@ -4085,7 +4301,7 @@ copy_plats_to_inter (class ipcp_param_lattices *plats, > HOST_WIDE_INT offset) > for (struct ipcp_agg_lattice *aglat = plats->aggs; aglat; aglat = > aglat->next) > if (aglat->is_single_const ()) > { > - struct ipa_agg_jf_item ti; > + struct ipa_agg_value ti; > ti.offset = aglat->offset - offset; > ti.value = aglat->values->value; > res.safe_push (ti); > @@ -4098,11 +4314,11 @@ copy_plats_to_inter (class ipcp_param_lattices > *plats, HOST_WIDE_INT offset) > > static void > intersect_with_plats (class ipcp_param_lattices *plats, > - vec<ipa_agg_jf_item> *inter, > + vec<ipa_agg_value> *inter, > HOST_WIDE_INT offset) > { > struct ipcp_agg_lattice *aglat; > - struct ipa_agg_jf_item *item; > + struct ipa_agg_value *item; > int k; > > if (!plats->aggs || plats->aggs_contain_variable || plats->aggs_bottom) > @@ -4140,18 +4356,18 @@ intersect_with_plats (class ipcp_param_lattices > *plats, > /* Copy aggregate replacement values of NODE (which is an IPA-CP clone) to the > vector result while subtracting OFFSET from the individual value offsets. > */ > > -static vec<ipa_agg_jf_item> > +static vec<ipa_agg_value> > agg_replacements_to_vector (struct cgraph_node *node, int index, > HOST_WIDE_INT offset) > { > struct ipa_agg_replacement_value *av; > - vec<ipa_agg_jf_item> res = vNULL; > + vec<ipa_agg_value> res = vNULL; > > for (av = ipa_get_agg_replacements_for_node (node); av; av = av->next) > if (av->index == index > && (av->offset - offset) >= 0) > { > - struct ipa_agg_jf_item item; > + struct ipa_agg_value item; > gcc_checking_assert (av->value); > item.offset = av->offset - offset; > item.value = av->value; > @@ -4167,11 +4383,11 @@ agg_replacements_to_vector (struct cgraph_node *node, > int index, > > static void > intersect_with_agg_replacements (struct cgraph_node *node, int index, > - vec<ipa_agg_jf_item> *inter, > + vec<ipa_agg_value> *inter, > HOST_WIDE_INT offset) > { > struct ipa_agg_replacement_value *srcvals; > - struct ipa_agg_jf_item *item; > + struct ipa_agg_value *item; > int i; > > srcvals = ipa_get_agg_replacements_for_node (node); > @@ -4208,9 +4424,9 @@ intersect_with_agg_replacements (struct cgraph_node > *node, int index, > 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> > +static vec<ipa_agg_value> > intersect_aggregates_with_edge (struct cgraph_edge *cs, int index, > - vec<ipa_agg_jf_item> inter) > + vec<ipa_agg_value> inter) > { > struct ipa_jump_func *jfunc; > jfunc = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), index); > @@ -4291,12 +4507,26 @@ intersect_aggregates_with_edge (struct cgraph_edge > *cs, int index, > } > else if (jfunc->agg.items) > { > - struct ipa_agg_jf_item *item; > + class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller); > + struct ipa_agg_value *item; > int k; > > if (!inter.exists ()) > for (unsigned i = 0; i < jfunc->agg.items->length (); i++) > - inter.safe_push ((*jfunc->agg.items)[i]); > + { > + struct ipa_agg_jf_item *agg_item = &(*jfunc->agg.items)[i]; > + tree value = ipa_agg_value_from_node (caller_info, cs->caller, > + agg_item); > + if (value) > + { > + struct ipa_agg_value agg_value; > + > + agg_value.offset = agg_item->offset; > + agg_value.value = value; > + > + inter.safe_push (agg_value); > + } > + } > else > FOR_EACH_VEC_ELT (inter, k, item) > { > @@ -4314,9 +4544,10 @@ intersect_aggregates_with_edge (struct cgraph_edge > *cs, int index, > break; > if (ti->offset == item->offset) > { > - gcc_checking_assert (ti->value); > - if (values_equal_for_ipcp_p (item->value, > - ti->value)) > + tree value = ipa_agg_value_from_node (caller_info, > + cs->caller, ti); > + if (value > + && values_equal_for_ipcp_p (item->value, value)) > found = true; > break; > } > @@ -4329,7 +4560,7 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, > int index, > else > { > inter.release (); > - return vec<ipa_agg_jf_item>(); > + return vNULL; > } > return inter; > } > @@ -4357,8 +4588,8 @@ find_aggregate_values_for_callers_subset (struct > cgraph_node *node, > for (i = 0; i < count; i++) > { > struct cgraph_edge *cs; > - vec<ipa_agg_jf_item> inter = vNULL; > - struct ipa_agg_jf_item *item; > + vec<ipa_agg_value> inter = vNULL; > + struct ipa_agg_value *item; > class ipcp_param_lattices *plats = ipa_get_parm_lattices (dest_info, i); > int j; > > @@ -4465,7 +4696,7 @@ cgraph_edge_brings_all_agg_vals_for_node (struct > cgraph_edge *cs, > > for (i = 0; i < count; i++) > { > - static vec<ipa_agg_jf_item> values = vec<ipa_agg_jf_item>(); > + static vec<ipa_agg_value> values = vNULL; > class ipcp_param_lattices *plats; > bool interesting = false; > for (struct ipa_agg_replacement_value *av = aggval; av; av = av->next) > @@ -4488,7 +4719,7 @@ cgraph_edge_brings_all_agg_vals_for_node (struct > cgraph_edge *cs, > for (struct ipa_agg_replacement_value *av = aggval; av; av = av->next) > if (aggval->index == i) > { > - struct ipa_agg_jf_item *item; > + struct ipa_agg_value *item; > int j; > bool found = false; > FOR_EACH_VEC_ELT (values, j, item) > @@ -4726,7 +4957,6 @@ decide_whether_version_node (struct cgraph_node *node) > int i, count = ipa_get_param_count (info); > vec<tree> known_csts; > vec<ipa_polymorphic_call_context> known_contexts; > - vec<ipa_agg_jump_function> known_aggs = vNULL; > bool ret = false; > > if (count == 0) > @@ -4737,8 +4967,7 @@ decide_whether_version_node (struct cgraph_node *node) > node->dump_name ()); > > gather_context_independent_values (info, &known_csts, &known_contexts, > - info->do_clone_for_all_contexts ? &known_aggs > - : NULL, NULL); > + NULL, NULL); > > for (i = 0; i < count;i++) > { > @@ -4807,9 +5036,6 @@ decide_whether_version_node (struct cgraph_node *node) > info = IPA_NODE_REF (node); > info->do_clone_for_all_contexts = false; > IPA_NODE_REF (clone)->is_all_contexts_clone = true; > - for (i = 0; i < count; i++) > - vec_free (known_aggs[i].items); > - known_aggs.release (); > ret = true; > } > else > diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c > index 6de060aa3fc..f8725d8dbfe 100644 > --- a/gcc/ipa-fnsummary.c > +++ b/gcc/ipa-fnsummary.c > @@ -306,9 +306,9 @@ set_hint_predicate (predicate **p, predicate > new_predicate) > the fact that parameter is indeed a constant. > > KNOWN_VALS is partial mapping of parameters of NODE to constant values. > - KNOWN_AGGS is a vector of aggreggate jump functions for each parameter. > - Return clause of possible truths. When INLINE_P is true, assume that we > are > - inlining. > + KNOWN_AGGS is a vector of aggreggate known offset/value set for each > + parameter. Return clause of possible truths. When INLINE_P is true, > assume > + that we are inlining. > > ERROR_MARK means compile time invariant. */ > > @@ -316,8 +316,7 @@ static void > evaluate_conditions_for_known_args (struct cgraph_node *node, > bool inline_p, > vec<tree> known_vals, > - vec<ipa_agg_jump_function_p> > - known_aggs, > + vec<ipa_agg_value_set> known_aggs, > clause_t *ret_clause, > clause_t *ret_nonspec_clause) > { > @@ -347,7 +346,7 @@ evaluate_conditions_for_known_args (struct cgraph_node > *node, > > if (c->agg_contents) > { > - struct ipa_agg_jump_function *agg; > + struct ipa_agg_value_set *agg; > > if (c->code == predicate::changed > && !c->by_ref > @@ -356,7 +355,7 @@ evaluate_conditions_for_known_args (struct cgraph_node > *node, > > if (known_aggs.exists ()) > { > - agg = known_aggs[c->operand_num]; > + agg = &known_aggs[c->operand_num]; > val = ipa_find_agg_cst_for_param (agg, known_vals[c->operand_num], > c->offset, c->by_ref); > } > @@ -420,12 +419,12 @@ evaluate_properties_for_edge (struct cgraph_edge *e, > bool inline_p, > vec<tree> *known_vals_ptr, > vec<ipa_polymorphic_call_context> > *known_contexts_ptr, > - vec<ipa_agg_jump_function_p> *known_aggs_ptr) > + vec<ipa_agg_value_set> *known_aggs_ptr) > { > struct cgraph_node *callee = e->callee->ultimate_alias_target (); > class ipa_fn_summary *info = ipa_fn_summaries->get (callee); > vec<tree> known_vals = vNULL; > - vec<ipa_agg_jump_function_p> known_aggs = vNULL; > + vec<ipa_agg_value_set> known_aggs = vNULL; > > if (clause_ptr) > *clause_ptr = inline_p ? 0 : 1 << predicate::not_inlined_condition; > @@ -438,15 +437,17 @@ evaluate_properties_for_edge (struct cgraph_edge *e, > bool inline_p, > && !e->call_stmt_cannot_inline_p > && ((clause_ptr && info->conds) || known_vals_ptr || > known_contexts_ptr)) > { > + struct cgraph_node *caller; > class ipa_node_params *caller_parms_info, *callee_pi; > class ipa_edge_args *args = IPA_EDGE_REF (e); > class ipa_call_summary *es = ipa_call_summaries->get (e); > int i, count = ipa_get_cs_argument_count (args); > > if (e->caller->global.inlined_to) > - caller_parms_info = IPA_NODE_REF (e->caller->global.inlined_to); > + caller = e->caller->global.inlined_to; > else > - caller_parms_info = IPA_NODE_REF (e->caller); > + caller = e->caller; > + caller_parms_info = IPA_NODE_REF (caller); > callee_pi = IPA_NODE_REF (e->callee); > > if (count && (info->conds || known_vals_ptr)) > @@ -481,10 +482,9 @@ evaluate_properties_for_edge (struct cgraph_edge *e, > bool inline_p, > if (known_contexts_ptr) > (*known_contexts_ptr)[i] > = ipa_context_from_jfunc (caller_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. */ > - known_aggs[i] = &jf->agg; > + > + known_aggs[i] = ipa_agg_value_set_from_jfunc (caller_parms_info, > + caller, &jf->agg); > } > } > else if (e->call_stmt && !e->call_stmt_cannot_inline_p > @@ -516,7 +516,7 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool > inline_p, > if (known_aggs_ptr) > *known_aggs_ptr = known_aggs; > else > - known_aggs.release (); > + ipa_release_agg_values (known_aggs); > } > > > @@ -2662,7 +2662,7 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie, > int *size, int *time, > vec<tree> known_vals, > vec<ipa_polymorphic_call_context> known_contexts, > - vec<ipa_agg_jump_function_p> known_aggs) > + vec<ipa_agg_value_set> known_aggs) > { > tree target; > struct cgraph_node *callee; > @@ -2711,7 +2711,7 @@ estimate_edge_size_and_time (struct cgraph_edge *e, int > *size, int *min_size, > int prob, > vec<tree> known_vals, > vec<ipa_polymorphic_call_context> known_contexts, > - vec<ipa_agg_jump_function_p> known_aggs, > + vec<ipa_agg_value_set> known_aggs, > ipa_hints *hints) > { > class ipa_call_summary *es = ipa_call_summaries->get (e); > @@ -2746,7 +2746,7 @@ estimate_calls_size_and_time (struct cgraph_node *node, > int *size, > clause_t possible_truths, > vec<tree> known_vals, > vec<ipa_polymorphic_call_context> known_contexts, > - vec<ipa_agg_jump_function_p> known_aggs) > + vec<ipa_agg_value_set> known_aggs) > { > struct cgraph_edge *e; > for (e = node->callees; e; e = e->next_callee) > @@ -2809,7 +2809,7 @@ estimate_node_size_and_time (struct cgraph_node *node, > clause_t nonspec_possible_truths, > vec<tree> known_vals, > vec<ipa_polymorphic_call_context> known_contexts, > - vec<ipa_agg_jump_function_p> known_aggs, > + vec<ipa_agg_value_set> known_aggs, > int *ret_size, int *ret_min_size, > sreal *ret_time, > sreal *ret_nonspecialized_time, > @@ -2945,7 +2945,7 @@ estimate_ipcp_clone_size_and_time (struct cgraph_node > *node, > vec<tree> known_vals, > vec<ipa_polymorphic_call_context> > known_contexts, > - vec<ipa_agg_jump_function_p> known_aggs, > + vec<ipa_agg_value_set> known_aggs, > int *ret_size, sreal *ret_time, > sreal *ret_nonspec_time, > ipa_hints *hints) > diff --git a/gcc/ipa-fnsummary.h b/gcc/ipa-fnsummary.h > index 173d3f2a652..7e561dab400 100644 > --- a/gcc/ipa-fnsummary.h > +++ b/gcc/ipa-fnsummary.h > @@ -260,7 +260,7 @@ void inline_analyze_function (struct cgraph_node *node); > void estimate_ipcp_clone_size_and_time (struct cgraph_node *, > vec<tree>, > vec<ipa_polymorphic_call_context>, > - vec<ipa_agg_jump_function_p>, > + vec<ipa_agg_value_set>, > int *, sreal *, sreal *, > ipa_hints *); > void ipa_merge_fn_summary_after_inlining (struct cgraph_edge *edge); > @@ -274,13 +274,13 @@ void evaluate_properties_for_edge (struct cgraph_edge > *e, bool inline_p, > vec<tree> *known_vals_ptr, > vec<ipa_polymorphic_call_context> > *known_contexts_ptr, > - vec<ipa_agg_jump_function_p> *); > + vec<ipa_agg_value_set> *); > void estimate_node_size_and_time (struct cgraph_node *node, > clause_t possible_truths, > clause_t nonspec_possible_truths, > vec<tree> known_vals, > vec<ipa_polymorphic_call_context>, > - vec<ipa_agg_jump_function_p> known_aggs, > + vec<ipa_agg_value_set> known_aggs, > int *ret_size, int *ret_min_size, > sreal *ret_time, > sreal *ret_nonspecialized_time, > diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c > index a66af277d03..bf4e6ea3392 100644 > --- a/gcc/ipa-inline-analysis.c > +++ b/gcc/ipa-inline-analysis.c > @@ -127,7 +127,7 @@ do_estimate_edge_time (struct cgraph_edge *edge) > clause_t clause, nonspec_clause; > vec<tree> known_vals; > vec<ipa_polymorphic_call_context> known_contexts; > - vec<ipa_agg_jump_function_p> known_aggs; > + vec<ipa_agg_value_set> known_aggs; > class ipa_call_summary *es = ipa_call_summaries->get (edge); > int min_size; > > @@ -154,7 +154,7 @@ do_estimate_edge_time (struct cgraph_edge *edge) > > known_vals.release (); > known_contexts.release (); > - known_aggs.release (); > + ipa_release_agg_values (known_aggs); > gcc_checking_assert (size >= 0); > gcc_checking_assert (time >= 0); > > @@ -186,7 +186,7 @@ do_estimate_edge_size (struct cgraph_edge *edge) > clause_t clause, nonspec_clause; > vec<tree> known_vals; > vec<ipa_polymorphic_call_context> known_contexts; > - vec<ipa_agg_jump_function_p> known_aggs; > + vec<ipa_agg_value_set> known_aggs; > > /* When we do caching, use do_estimate_edge_time to populate the entry. */ > > @@ -211,7 +211,7 @@ do_estimate_edge_size (struct cgraph_edge *edge) > NULL, NULL, vNULL); > known_vals.release (); > known_contexts.release (); > - known_aggs.release (); > + ipa_release_agg_values (known_aggs); > return size; > } > > @@ -227,7 +227,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge) > clause_t clause, nonspec_clause; > vec<tree> known_vals; > vec<ipa_polymorphic_call_context> known_contexts; > - vec<ipa_agg_jump_function_p> known_aggs; > + vec<ipa_agg_value_set> known_aggs; > > /* When we do caching, use do_estimate_edge_time to populate the entry. */ > > @@ -252,7 +252,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge) > NULL, NULL, &hints, vNULL); > known_vals.release (); > known_contexts.release (); > - known_aggs.release (); > + ipa_release_agg_values (known_aggs); > hints |= simple_edge_hints (edge); > return hints; > } > diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c > index a23aa2590a0..be281293eb7 100644 > --- a/gcc/ipa-prop.c > +++ b/gcc/ipa-prop.c > @@ -359,18 +359,45 @@ ipa_print_node_jump_functions_for_edge (FILE *f, struct > cgraph_edge *cs) > > fprintf (f, " Aggregate passed by %s:\n", > jump_func->agg.by_ref ? "reference" : "value"); > - FOR_EACH_VEC_SAFE_ELT (jump_func->agg.items, j, item) > + FOR_EACH_VEC_ELT (*jump_func->agg.items, j, item) > { > fprintf (f, " offset: " HOST_WIDE_INT_PRINT_DEC ", ", > item->offset); > - if (TYPE_P (item->value)) > - fprintf (f, "clobber of " HOST_WIDE_INT_PRINT_DEC " bits", > - tree_to_uhwi (TYPE_SIZE (item->value))); > - else > + fprintf (f, "type: "); > + print_generic_expr (f, item->type); > + fprintf (f, ", "); > + if (item->jftype == IPA_JF_PASS_THROUGH) > + fprintf (f, "PASS THROUGH: %d,", > + item->value.pass_through.formal_id); > + else if (item->jftype == IPA_JF_LOAD_AGG) > + { > + fprintf (f, "LOAD AGG: %d", > + item->value.pass_through.formal_id); > + fprintf (f, " [offset: " HOST_WIDE_INT_PRINT_DEC ", by %s],", > + item->value.load_agg.offset, > + item->value.load_agg.by_ref ? "reference" > + : "value"); > + } > + > + if (item->jftype == IPA_JF_PASS_THROUGH > + || item->jftype == IPA_JF_LOAD_AGG) > + { > + fprintf (f, " op %s", > + get_tree_code_name (item->value.pass_through.operation)); > + if (item->value.pass_through.operation != NOP_EXPR) > + { > + fprintf (f, " "); > + print_generic_expr (f, item->value.pass_through.operand); > + } > + } > + else if (item->jftype == IPA_JF_CONST) > { > - fprintf (f, "cst: "); > - print_generic_expr (f, item->value); > + fprintf (f, "CONST: "); > + print_generic_expr (f, item->value.constant); > } > + else if (item->jftype == IPA_JF_UNKNOWN) > + fprintf (f, "UNKNOWN: " HOST_WIDE_INT_PRINT_DEC " bits", > + tree_to_uhwi (TYPE_SIZE (item->type))); > fprintf (f, "\n"); > } > } > @@ -1135,6 +1162,67 @@ ipa_load_from_parm_agg (struct ipa_func_body_info *fbi, > return false; > } > > +/* If STMT is an assignment that loads a value from a parameter declaration, > + or from an aggregate passed as the parameter either by value or reference, > + return the index of the parameter in ipa_node_params. Otherwise return > -1. > + > + FBI holds gathered information about the function. INFO describes > + parameters of the function, STMT is the assignment statement. If it is a > + memory load from an aggregate, *OFFSET_P is filled with offset within the > + aggregate, and *BY_REF_P specifies whether the aggregate is passed by > + reference. */ > + > +static int > +load_from_unmodified_param_or_agg (struct ipa_func_body_info *fbi, > + class ipa_node_params *info, > + gimple *stmt, > + HOST_WIDE_INT *offset_p, > + bool *by_ref_p) > +{ > + int index = load_from_unmodified_param (fbi, info->descriptors, stmt); > + poly_int64 size; > + > + /* Load value from a parameter declaration. */ > + if (index >= 0) > + { > + *offset_p = -1; > + return index; > + } > + > + if (!gimple_assign_load_p (stmt)) > + return -1; > + > + tree rhs = gimple_assign_rhs1 (stmt); > + > + /* Skip memory reference containing VIEW_CONVERT_EXPR. */ > + for (tree t = rhs; handled_component_p (t); t = TREE_OPERAND (t, 0)) > + if (TREE_CODE (t) == VIEW_CONVERT_EXPR) > + return -1; > + > + /* Skip memory reference containing bit-field. */ > + if (TREE_CODE (rhs) == BIT_FIELD_REF > + || contains_bitfld_component_ref_p (rhs)) > + return -1; > + > + if (!ipa_load_from_parm_agg (fbi, info->descriptors, stmt, rhs, &index, > + offset_p, &size, by_ref_p)) > + return -1; > + > + gcc_assert (!maybe_ne (tree_to_poly_int64 (TYPE_SIZE (TREE_TYPE (rhs))), > + size)); > + if (!*by_ref_p) > + { > + tree param_type = ipa_get_type (info, index); > + > + if (!param_type || !AGGREGATE_TYPE_P (param_type)) > + return -1; > + } > + else if (TREE_THIS_VOLATILE (rhs)) > + return -1; > + > + return index; > +} > + > /* Given that an actual argument is an SSA_NAME (given in NAME) and is a > result > of an assignment statement STMT, try to determine whether we are actually > handling any of the following cases and construct an appropriate jump > @@ -1438,11 +1526,11 @@ type_like_member_ptr_p (tree type, tree *method_ptr, > tree *delta) > } > > /* If RHS is an SSA_NAME and it is defined by a simple copy assign statement, > - return the rhs of its defining statement. Otherwise return RHS as it > - is. */ > + return the rhs of its defining statement, and this statement is stored in > + *RHS_STMT. Otherwise return RHS as it is. */ > > static inline tree > -get_ssa_def_if_simple_copy (tree rhs) > +get_ssa_def_if_simple_copy (tree rhs, gimple **rhs_stmt) > { > while (TREE_CODE (rhs) == SSA_NAME && !SSA_NAME_IS_DEFAULT_DEF (rhs)) > { > @@ -1452,25 +1540,31 @@ get_ssa_def_if_simple_copy (tree rhs) > rhs = gimple_assign_rhs1 (def_stmt); > else > break; > + *rhs_stmt = def_stmt; > } > return rhs; > } > > -/* Simple linked list, describing known contents of an aggregate before > - call. */ > +/* Simple linked list, describing contents of an aggregate before call. */ > > struct ipa_known_agg_contents_list > { > /* Offset and size of the described part of the aggregate. */ > HOST_WIDE_INT offset, size; > - /* Known constant value or NULL if the contents is known to be unknown. */ > - tree constant; > + > + /* Type of the described part of the aggregate. */ > + tree type; > + > + /* Known constant value or jump function data describing contents. */ > + struct ipa_load_agg_data value; > + > /* Pointer to the next structure in the list. */ > struct ipa_known_agg_contents_list *next; > }; > > -/* Add a known content item into a linked list of ipa_known_agg_contents_list > - structure, in which all elements are sorted ascendingly by offset. */ > +/* Add an aggregate content item into a linked list of > + ipa_known_agg_contents_list structure, in which all elements > + are sorted ascendingly by offset. */ > > static inline void > add_to_agg_contents_list (struct ipa_known_agg_contents_list **plist, > @@ -1490,7 +1584,7 @@ add_to_agg_contents_list (struct > ipa_known_agg_contents_list **plist, > *plist = item; > } > > -/* Check whether a given known content is clobbered by certain element in > +/* Check whether a given aggregate content is clobbered by certain element in > a linked list of ipa_known_agg_contents_list. */ > > static inline bool > @@ -1510,27 +1604,189 @@ clobber_by_agg_contents_list_p (struct > ipa_known_agg_contents_list *list, > } > > /* Build aggregate jump function from LIST, assuming there are exactly > - CONST_COUNT constant entries there and that offset of the passed argument > + VALUE_COUNT entries there and that offset of the passed argument > is ARG_OFFSET and store it into JFUNC. */ > > static void > build_agg_jump_func_from_list (struct ipa_known_agg_contents_list *list, > - int const_count, HOST_WIDE_INT arg_offset, > + int value_count, HOST_WIDE_INT arg_offset, > struct ipa_jump_func *jfunc) > { > - vec_alloc (jfunc->agg.items, const_count); > - while (list) > + vec_alloc (jfunc->agg.items, value_count); > + for (; list; list = list->next) > + { > + struct ipa_agg_jf_item item; > + tree operand = list->value.pass_through.operand; > + > + if (list->value.pass_through.formal_id >= 0) > + { > + /* Content value is derived from some formal paramerter. */ > + if (list->value.offset >= 0) > + item.jftype = IPA_JF_LOAD_AGG; > + else > + item.jftype = IPA_JF_PASS_THROUGH; > + > + item.value.load_agg = list->value; > + if (operand) > + item.value.pass_through.operand > + = unshare_expr_without_location (operand); > + } > + else if (operand) > + { > + /* Content value is known constant. */ > + item.jftype = IPA_JF_CONST; > + item.value.constant = unshare_expr_without_location (operand); > + } > + else > + continue; > + > + item.type = list->type; > + gcc_assert (tree_to_shwi (TYPE_SIZE (list->type)) == list->size); > + > + item.offset = list->offset - arg_offset; > + gcc_assert ((item.offset % BITS_PER_UNIT) == 0); > + > + jfunc->agg.items->quick_push (item); > + } > +} > + > +/* Given an assignment statement STMT, try to collect information into > + AGG_VALUE that will be used to construct jump function for RHS of the > + assignment, from which content value of an aggregate part comes. > + > + Besides constant and simple pass-through jump functions, also try to > + identify whether it matches the following pattern that can be described by > + a load-value-from-aggregate jump function, which is a derivative of simple > + pass-through jump function. > + > + foo (int *p) > + { > + ... > + > + *(q_5 + 4) = *(p_3(D) + 28) op 1; > + bar (q_5); > + } > + > + Since load-value-from-aggregate jump function data structure is > informative > + enough to describe constant and simple pass-through jump function, here we > + do not need a jump function type, merely use FORMAL_ID and OPERAND in > + IPA_LOAD_AGG_DATA to disginguish different jump functions. */ > + > +static void > +compute_assign_agg_jump_func (struct ipa_func_body_info *fbi, > + struct ipa_load_agg_data *agg_value, > + gimple *stmt) > +{ > + tree lhs = gimple_assign_lhs (stmt); > + tree rhs1 = gimple_assign_rhs1 (stmt); > + enum tree_code code; > + int index = -1; > + > + /* Initialize jump function data for the aggregate part. */ > + memset (agg_value, 0, sizeof (*agg_value)); > + agg_value->pass_through.operation = NOP_EXPR; > + agg_value->pass_through.formal_id = -1; > + agg_value->offset = -1; > + > + if (AGGREGATE_TYPE_P (TREE_TYPE (lhs)) /* TODO: Support aggregate type. > */ > + || TREE_THIS_VOLATILE (lhs) > + || TREE_CODE (lhs) == BIT_FIELD_REF > + || contains_bitfld_component_ref_p (lhs)) > + return; > + > + /* Skip SSA copies. */ > + while (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS) > + { > + if (TREE_CODE (rhs1) != SSA_NAME || SSA_NAME_IS_DEFAULT_DEF (rhs1)) > + break; > + > + if (!is_gimple_assign (stmt = SSA_NAME_DEF_STMT (rhs1))) > + return; > + > + rhs1 = gimple_assign_rhs1 (stmt); > + } > + > + code = gimple_assign_rhs_code (stmt); > + switch (gimple_assign_rhs_class (stmt)) > { > - if (list->constant) > + case GIMPLE_SINGLE_RHS: > + if (is_gimple_ip_invariant (rhs1)) > { > - struct ipa_agg_jf_item item; > - item.offset = list->offset - arg_offset; > - gcc_assert ((item.offset % BITS_PER_UNIT) == 0); > - item.value = unshare_expr_without_location (list->constant); > - jfunc->agg.items->quick_push (item); > + agg_value->pass_through.operand = rhs1; > + return; > } > - list = list->next; > + code = NOP_EXPR; > + break; > + > + case GIMPLE_UNARY_RHS: > + /* NOTE: A GIMPLE_UNARY_RHS operation might not be tcc_unary > + (truth_not_expr is example), GIMPLE_BINARY_RHS does not imply > + tcc_binary, this subtleness is somewhat misleading. > + > + Since tcc_unary is widely used in IPA-CP code to check an operation > + with one operand, here we only allow tc_unary operation to avoid > + possible problem. Then we can use (opclass == tc_unary) or not to > + distinguish unary and binary. */ > + if (TREE_CODE_CLASS (code) != tcc_unary || CONVERT_EXPR_CODE_P (code)) > + return; > + > + rhs1 = get_ssa_def_if_simple_copy (rhs1, &stmt); > + break; > + > + case GIMPLE_BINARY_RHS: > + { > + gimple *rhs1_stmt = stmt; > + gimple *rhs2_stmt = stmt; > + tree rhs2 = gimple_assign_rhs2 (stmt); > + > + rhs1 = get_ssa_def_if_simple_copy (rhs1, &rhs1_stmt); > + rhs2 = get_ssa_def_if_simple_copy (rhs2, &rhs2_stmt); > + > + if (is_gimple_ip_invariant (rhs2)) > + { > + agg_value->pass_through.operand = rhs2; > + stmt = rhs1_stmt; > + } > + else if (is_gimple_ip_invariant (rhs1)) > + { > + if (TREE_CODE_CLASS (code) == tcc_comparison) > + code = swap_tree_comparison (code); > + else if (!commutative_tree_code (code)) > + return; > + > + agg_value->pass_through.operand = rhs1; > + stmt = rhs2_stmt; > + rhs1 = rhs2; > + } > + else > + return; > + > + if (TREE_CODE_CLASS (code) != tcc_comparison > + && !useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs1))) > + return; > + } > + break; > + > + default: > + return; > + } > + > + if (TREE_CODE (rhs1) != SSA_NAME) > + index = load_from_unmodified_param_or_agg (fbi, fbi->info, stmt, > + &agg_value->offset, > + &agg_value->by_ref); > + else if (SSA_NAME_IS_DEFAULT_DEF (rhs1)) > + index = ipa_get_param_decl_index (fbi->info, SSA_NAME_VAR (rhs1)); > + > + if (index >= 0) > + { > + if (agg_value->offset >= 0) > + agg_value->type = TREE_TYPE (rhs1); > + agg_value->pass_through.formal_id = index; > + agg_value->pass_through.operation = code; > } > + else > + agg_value->pass_through.operand = NULL_TREE; > } > > /* If STMT is a memory store to the object whose address is BASE, extract > @@ -1540,26 +1796,19 @@ build_agg_jump_func_from_list (struct > ipa_known_agg_contents_list *list, > is expected to be in form of MEM_REF expression. */ > > static bool > -extract_mem_content (gimple *stmt, tree base, bool check_ref, > +extract_mem_content (struct ipa_func_body_info *fbi, > + gimple *stmt, tree base, bool check_ref, > struct ipa_known_agg_contents_list *content) > { > HOST_WIDE_INT lhs_offset, lhs_size; > - tree lhs, rhs, lhs_base; > bool reverse; > > - if (!gimple_assign_single_p (stmt)) > - return false; > - > - lhs = gimple_assign_lhs (stmt); > - rhs = gimple_assign_rhs1 (stmt); > - > - if (!is_gimple_reg_type (TREE_TYPE (rhs)) > - || TREE_CODE (lhs) == BIT_FIELD_REF > - || contains_bitfld_component_ref_p (lhs)) > + if (!is_gimple_assign (stmt)) > return false; > > - lhs_base = get_ref_base_and_extent_hwi (lhs, &lhs_offset, > - &lhs_size, &reverse); > + tree lhs = gimple_assign_lhs (stmt); > + tree lhs_base = get_ref_base_and_extent_hwi (lhs, &lhs_offset, &lhs_size, > + &reverse); > if (!lhs_base) > return false; > > @@ -1573,32 +1822,31 @@ extract_mem_content (gimple *stmt, tree base, bool > check_ref, > else if (lhs_base != base) > return false; > > - rhs = get_ssa_def_if_simple_copy (rhs); > - > - content->size = lhs_size; > content->offset = lhs_offset; > - content->constant = is_gimple_ip_invariant (rhs) ? rhs : NULL_TREE; > + content->size = lhs_size; > + content->type = TREE_TYPE (lhs); > content->next = NULL; > > + compute_assign_agg_jump_func (fbi, &content->value, stmt); > return true; > } > > /* Traverse statements from CALL backwards, scanning whether an aggregate > given > - in ARG is filled in with constant values. ARG can either be an aggregate > - expression or a pointer to an aggregate. ARG_TYPE is the type of the > - aggregate. JFUNC is the jump function into which the constants are > - subsequently stored. AA_WALK_BUDGET_P points to limit on number of > - statements we allow get_continuation_for_phi to examine. */ > + in ARG is filled in constant or value that is derived from caller's formal > + parameter in the way described by some kind of jump function. FBI is the > + context of the caller function for interprocedural analysis. ARG can > either > + be an aggregate expression or a pointer to an aggregate. ARG_TYPE is the > + type of the aggregate. JFUNC is the jump function for the aggregate. */ > > static void > -determine_known_aggregate_parts (gcall *call, tree arg, > +determine_known_aggregate_parts (struct ipa_func_body_info *fbi, > + gcall *call, tree arg, > tree arg_type, > - struct ipa_jump_func *jfunc, > - unsigned *aa_walk_budget_p) > + struct ipa_jump_func *jfunc) > { > struct ipa_known_agg_contents_list *list = NULL, *all_list = NULL; > bitmap visited = NULL; > - int item_count = 0, const_count = 0; > + int item_count = 0, value_count = 0; > int ipa_max_agg_items = PARAM_VALUE (PARAM_IPA_MAX_AGG_ITEMS); > HOST_WIDE_INT arg_offset, arg_size; > tree arg_base; > @@ -1677,7 +1925,7 @@ determine_known_aggregate_parts (gcall *call, tree arg, > if (gimple_code (stmt) == GIMPLE_PHI) > { > dom_vuse = get_continuation_for_phi (stmt, &r, true, > - *aa_walk_budget_p, > + fbi->aa_walk_budget, > &visited, false, NULL, NULL); > continue; > } > @@ -1687,12 +1935,13 @@ determine_known_aggregate_parts (gcall *call, tree > arg, > struct ipa_known_agg_contents_list *content > = XALLOCA (struct ipa_known_agg_contents_list); > > - if (!extract_mem_content (stmt, arg_base, check_ref, content)) > + if (!extract_mem_content (fbi, stmt, arg_base, check_ref, content)) > break; > > /* Now we get a dominating virtual operand, and need to check > whether its value is clobbered any other dominating one. */ > - if (content->constant > + if ((content->value.pass_through.formal_id >= 0 > + || content->value.pass_through.operand) > && !clobber_by_agg_contents_list_p (all_list, content)) > { > struct ipa_known_agg_contents_list *copy > @@ -1702,7 +1951,7 @@ determine_known_aggregate_parts (gcall *call, tree arg, > operands, whose definitions can finally reach the call. */ > add_to_agg_contents_list (&list, (*copy = *content, copy)); > > - if (++const_count == ipa_max_agg_items) > + if (++value_count == ipa_max_agg_items) > break; > } > > @@ -1720,12 +1969,12 @@ determine_known_aggregate_parts (gcall *call, tree > arg, > > /* Third stage just goes over the list and creates an appropriate vector of > ipa_agg_jf_item structures out of it, of course only if there are > - any known constants to begin with. */ > + any meaningful items to begin with. */ > > - if (const_count) > + if (value_count) > { > jfunc->agg.by_ref = by_ref; > - build_agg_jump_func_from_list (list, const_count, arg_offset, jfunc); > + build_agg_jump_func_from_list (list, value_count, arg_offset, jfunc); > } > } > > @@ -2017,8 +2266,7 @@ ipa_compute_jump_functions_for_edge (struct > ipa_func_body_info *fbi, > || !ipa_get_jf_ancestor_agg_preserved (jfunc)) > && (AGGREGATE_TYPE_P (TREE_TYPE (arg)) > || POINTER_TYPE_P (param_type))) > - determine_known_aggregate_parts (call, arg, param_type, jfunc, > - &fbi->aa_walk_budget); > + determine_known_aggregate_parts (fbi, call, arg, param_type, jfunc); > } > if (!useful_context) > vec_free (args->polymorphic_call_contexts); > @@ -2661,6 +2909,72 @@ update_jump_functions_after_inlining (struct > cgraph_edge *cs, > class ipa_polymorphic_call_context *dst_ctx > = ipa_get_ith_polymorhic_call_context (args, i); > > + if (dst->agg.items) > + { > + struct ipa_agg_jf_item *item; > + int j; > + > + FOR_EACH_VEC_ELT (*dst->agg.items, j, item) > + { > + int dst_fid; > + struct ipa_jump_func *src; > + > + if (item->jftype != IPA_JF_PASS_THROUGH > + && item->jftype != IPA_JF_LOAD_AGG) > + continue; > + > + dst_fid = item->value.pass_through.formal_id; > + if (dst_fid >= ipa_get_cs_argument_count (top)) > + { > + item->jftype = IPA_JF_UNKNOWN; > + continue; > + } > + > + item->value.pass_through.formal_id = -1; > + src = ipa_get_ith_jump_func (top, dst_fid); > + if (src->type == IPA_JF_CONST) > + { > + if (item->jftype == IPA_JF_PASS_THROUGH > + && item->value.pass_through.operation == NOP_EXPR) > + { > + item->jftype = IPA_JF_CONST; > + item->value.constant = src->value.constant.value; > + continue; > + } > + } > + else if (src->type == IPA_JF_PASS_THROUGH > + && src->value.pass_through.operation == NOP_EXPR) > + { > + if (item->jftype == IPA_JF_PASS_THROUGH > + || !item->value.load_agg.by_ref > + || src->value.pass_through.agg_preserved) > + item->value.pass_through.formal_id > + = src->value.pass_through.formal_id; > + } > + else if (src->type == IPA_JF_ANCESTOR) > + { > + if (item->jftype == IPA_JF_PASS_THROUGH) > + { > + if (!src->value.ancestor.offset) > + item->value.pass_through.formal_id > + = src->value.ancestor.formal_id; > + } > + else if (src->value.ancestor.agg_preserved) > + { > + gcc_checking_assert (item->value.load_agg.by_ref); > + > + item->value.pass_through.formal_id > + = src->value.ancestor.formal_id; > + item->value.load_agg.offset > + += src->value.ancestor.offset; > + } > + } > + > + if (item->value.pass_through.formal_id < 0) > + item->jftype = IPA_JF_UNKNOWN; > + } > + } > + > if (dst->type == IPA_JF_ANCESTOR) > { > struct ipa_jump_func *src; > @@ -2700,8 +3014,11 @@ update_jump_functions_after_inlining (struct > cgraph_edge *cs, > } > } > > - if (src->agg.items > - && (dst->value.ancestor.agg_preserved || !src->agg.by_ref)) > + /* Parameter and argument in ancestor jump function must be pointer > + type, which means access to aggregate must be by-reference. */ > + gcc_checking_assert (!src->agg.items || src->agg.by_ref); > + > + if (src->agg.items && dst->value.ancestor.agg_preserved) > { > struct ipa_agg_jf_item *item; > int j; > @@ -3093,18 +3410,19 @@ ipa_find_agg_cst_from_init (tree scalar, > HOST_WIDE_INT offset, bool by_ref) > return find_constructor_constant_at_offset (DECL_INITIAL (scalar), offset); > } > > -/* Retrieve value from aggregate jump function AGG or static initializer of > - SCALAR (which can be NULL) for the given OFFSET or return NULL if there is > - none. BY_REF specifies whether the value has to be passed by reference or > - by value. If FROM_GLOBAL_CONSTANT is non-NULL, then the boolean it points > - to is set to true if the value comes from an initializer of a constant. > */ > +/* Retrieve value from AGG, a set of known offset/value for an aggregate or > + static initializer of SCALAR (which can be NULL) for the given OFFSET or > + return NULL if there is none. BY_REF specifies whether the value has to > be > + passed by reference or by value. If FROM_GLOBAL_CONSTANT is non-NULL, > then > + the boolean it points to is set to true if the value comes from an > + initializer of a constant. */ > > tree > -ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar, > +ipa_find_agg_cst_for_param (struct ipa_agg_value_set *agg, tree scalar, > HOST_WIDE_INT offset, bool by_ref, > bool *from_global_constant) > { > - struct ipa_agg_jf_item *item; > + struct ipa_agg_value *item; > int i; > > if (scalar) > @@ -3122,7 +3440,7 @@ ipa_find_agg_cst_for_param (struct > ipa_agg_jump_function *agg, tree scalar, > || by_ref != agg->by_ref) > return NULL; > > - FOR_EACH_VEC_SAFE_ELT (agg->items, i, item) > + FOR_EACH_VEC_ELT (agg->items, i, item) > if (item->offset == offset) > { > /* Currently we do not have clobber values, return NULL for them once > @@ -3218,11 +3536,13 @@ try_decrement_rdesc_refcount (struct ipa_jump_func > *jfunc) > pointer formal parameter described by jump function JFUNC. TARGET_TYPE is > the type of the parameter to which the result of JFUNC is passed. If it > can > be determined, return the newly direct edge, otherwise return NULL. > - NEW_ROOT_INFO is the node info that JFUNC lattices are relative to. */ > + NEW_ROOT and NEW_ROOT_INFO is the node and its info that JFUNC lattices > are > + relative to. */ > > static struct cgraph_edge * > try_make_edge_direct_simple_call (struct cgraph_edge *ie, > struct ipa_jump_func *jfunc, tree target_type, > + struct cgraph_node *new_root, > class ipa_node_params *new_root_info) > { > struct cgraph_edge *cs; > @@ -3232,10 +3552,14 @@ try_make_edge_direct_simple_call (struct cgraph_edge > *ie, > if (agg_contents) > { > bool from_global_constant; > - target = ipa_find_agg_cst_for_param (&jfunc->agg, scalar, > + ipa_agg_value_set agg = ipa_agg_value_set_from_jfunc (new_root_info, > + new_root, > + &jfunc->agg); > + target = ipa_find_agg_cst_for_param (&agg, scalar, > ie->indirect_info->offset, > ie->indirect_info->by_ref, > &from_global_constant); > + agg.release (); > if (target > && !from_global_constant > && !ie->indirect_info->guaranteed_unmodified) > @@ -3289,12 +3613,16 @@ ipa_impossible_devirt_target (struct cgraph_edge *ie, > tree target) > 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. CTX describes the polymorphic context that the > - parameter the call is based on brings along with it. */ > + parameter the call is based on brings along with it. NEW_ROOT and > + NEW_ROOT_INFO is the node and its info that JFUNC lattices are relative > + to. */ > > static struct cgraph_edge * > try_make_edge_direct_virtual_call (struct cgraph_edge *ie, > struct ipa_jump_func *jfunc, > - class ipa_polymorphic_call_context ctx) > + class ipa_polymorphic_call_context ctx, > + struct cgraph_node *new_root, > + class ipa_node_params *new_root_info) > { > tree target = NULL; > bool speculative = false; > @@ -3312,9 +3640,13 @@ try_make_edge_direct_virtual_call (struct cgraph_edge > *ie, > unsigned HOST_WIDE_INT offset; > tree scalar = (jfunc->type == IPA_JF_CONST) ? ipa_get_jf_constant > (jfunc) > : NULL; > - tree t = ipa_find_agg_cst_for_param (&jfunc->agg, scalar, > + ipa_agg_value_set agg = ipa_agg_value_set_from_jfunc (new_root_info, > + new_root, > + &jfunc->agg); > + tree t = ipa_find_agg_cst_for_param (&agg, scalar, > ie->indirect_info->offset, > true); > + agg.release (); > if (t && vtable_pointer_value_to_vtable (t, &vtable, &offset)) > { > bool can_refer; > @@ -3405,14 +3737,15 @@ update_indirect_edges_after_inlining (struct > cgraph_edge *cs, > { > class ipa_edge_args *top; > struct cgraph_edge *ie, *next_ie, *new_direct_edge; > + struct cgraph_node *new_root; > class ipa_node_params *new_root_info, *inlined_node_info; > bool res = false; > > ipa_check_create_edge_args (); > top = IPA_EDGE_REF (cs); > - new_root_info = IPA_NODE_REF (cs->caller->global.inlined_to > - ? cs->caller->global.inlined_to > - : cs->caller); > + new_root = cs->caller->global.inlined_to > + ? cs->caller->global.inlined_to : cs->caller; > + new_root_info = IPA_NODE_REF (new_root); > inlined_node_info = IPA_NODE_REF (cs->callee->function_symbol ()); > > for (ie = node->indirect_calls; ie; ie = next_ie) > @@ -3451,13 +3784,16 @@ update_indirect_edges_after_inlining (struct > cgraph_edge *cs, > { > 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); > + new_direct_edge = try_make_edge_direct_virtual_call (ie, jfunc, ctx, > + new_root, > + new_root_info); > } > else > { > tree target_type = ipa_get_type (inlined_node_info, param_index); > new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc, > target_type, > + new_root, > new_root_info); > } > > @@ -4125,6 +4461,8 @@ ipa_write_jump_function (struct output_block *ob, > bp_pack_value (&bp, jump_func->value.ancestor.agg_preserved, 1); > streamer_write_bitpack (&bp); > break; > + default: > + fatal_error (UNKNOWN_LOCATION, "invalid jump function in LTO stream"); > } > > count = vec_safe_length (jump_func->agg.items); > @@ -4138,8 +4476,36 @@ ipa_write_jump_function (struct output_block *ob, > > FOR_EACH_VEC_SAFE_ELT (jump_func->agg.items, i, item) > { > + stream_write_tree (ob, item->type, true); > streamer_write_uhwi (ob, item->offset); > - stream_write_tree (ob, item->value, true); > + streamer_write_uhwi (ob, item->jftype); > + switch (item->jftype) > + { > + case IPA_JF_UNKNOWN: > + break; > + case IPA_JF_CONST: > + stream_write_tree (ob, item->value.constant, true); > + break; > + case IPA_JF_PASS_THROUGH: > + case IPA_JF_LOAD_AGG: > + streamer_write_uhwi (ob, item->value.pass_through.operation); > + streamer_write_uhwi (ob, item->value.pass_through.formal_id); > + if (TREE_CODE_CLASS (item->value.pass_through.operation) > + != tcc_unary) > + stream_write_tree (ob, item->value.pass_through.operand, true); > + if (item->jftype == IPA_JF_LOAD_AGG) > + { > + stream_write_tree (ob, item->value.load_agg.type, true); > + streamer_write_uhwi (ob, item->value.load_agg.offset); > + bp = bitpack_create (ob->main_stream); > + bp_pack_value (&bp, item->value.load_agg.by_ref, 1); > + streamer_write_bitpack (&bp); > + } > + break; > + default: > + fatal_error (UNKNOWN_LOCATION, > + "invalid jump function in LTO stream"); > + } > } > > bp = bitpack_create (ob->main_stream); > @@ -4236,8 +4602,39 @@ ipa_read_jump_function (class lto_input_block *ib, > for (i = 0; i < count; i++) > { > struct ipa_agg_jf_item item; > + item.type = stream_read_tree (ib, data_in); > item.offset = streamer_read_uhwi (ib); > - item.value = stream_read_tree (ib, data_in); > + item.jftype = (enum jump_func_type) streamer_read_uhwi (ib); > + > + switch (item.jftype) > + { > + case IPA_JF_UNKNOWN: > + break; > + case IPA_JF_CONST: > + item.value.constant = stream_read_tree (ib, data_in); > + break; > + case IPA_JF_PASS_THROUGH: > + case IPA_JF_LOAD_AGG: > + operation = (enum tree_code) streamer_read_uhwi (ib); > + item.value.pass_through.operation = operation; > + item.value.pass_through.formal_id = streamer_read_uhwi (ib); > + if (TREE_CODE_CLASS (operation) == tcc_unary) > + item.value.pass_through.operand = NULL_TREE; > + else > + item.value.pass_through.operand = stream_read_tree (ib, data_in); > + if (item.jftype == IPA_JF_LOAD_AGG) > + { > + struct bitpack_d bp; > + item.value.load_agg.type = stream_read_tree (ib, data_in); > + item.value.load_agg.offset = streamer_read_uhwi (ib); > + bp = streamer_read_bitpack (ib); > + item.value.load_agg.by_ref = bp_unpack_value (&bp, 1); > + } > + break; > + default: > + fatal_error (UNKNOWN_LOCATION, > + "invalid jump function in LTO stream"); > + } > if (prevails) > jump_func->agg.items->quick_push (item); > } > diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h > index 30948fb8854..bcdcc4b7f02 100644 > --- a/gcc/ipa-prop.h > +++ b/gcc/ipa-prop.h > @@ -39,6 +39,15 @@ along with GCC; see the file COPYING3. If not see > argument. > Unknown - neither of the above. > > + IPA_JF_LOAD_AGG is a compound pass-through jump function, in which primary > + operation on formal parameter is memory dereference that loads a value > from > + a part of an aggregate, which is represented or pointed to by the formal > + parameter. Moreover, an additional unary/binary operation can be applied > on > + the loaded value, and final result is passed as actual argument of callee > + (e.g. *(param_1(D) + 4) op 24 ). It is meant to describe usage of > aggregate > + parameter or by-reference parameter referenced in argument passing, > commonly > + found in C++ and Fortran. > + > IPA_JF_ANCESTOR is a special pass-through jump function, which means that > the result is an address of a part of the object pointed to by the formal > parameter to which the function refers. It is mainly intended to represent > @@ -60,6 +69,7 @@ enum jump_func_type > IPA_JF_UNKNOWN = 0, /* newly allocated and zeroed jump functions default */ > IPA_JF_CONST, /* represented by field costant */ > IPA_JF_PASS_THROUGH, /* represented by field pass_through */ > + IPA_JF_LOAD_AGG, /* represented by field load_agg */ > IPA_JF_ANCESTOR /* represented by field ancestor */ > }; > > @@ -97,6 +107,26 @@ struct GTY(()) ipa_pass_through_data > unsigned agg_preserved : 1; > }; > > +/* Structure holding data required to describe a load-value-from-aggregate > + jump function. */ > + > +struct GTY(()) ipa_load_agg_data > +{ > + /* Inherit from pass through jump function, describing unary/binary > + operation on the value loaded from aggregate that is represented or > + pointed to by the formal parameter, specified by formal_id in this > + pass_through jump function data structure. */ > + struct ipa_pass_through_data pass_through; > + /* Type of the value loaded from the aggregate. */ > + tree type; > + /* Offset at which the value is located within the aggregate. */ > + HOST_WIDE_INT offset; > + /* True if loaded by reference (the aggregate is pointed to by the formal > + parameter) or false if loaded by value (the aggregate is represented > + by the formal parameter). */ > + bool by_ref; > +}; > + > /* Structure holding data required to describe an ancestor pass-through > jump function. */ > > @@ -110,38 +140,86 @@ struct GTY(()) ipa_ancestor_jf_data > unsigned agg_preserved : 1; > }; > > -/* An element in an aggegate part of a jump function describing a known value > - at a given offset. When it is part of a pass-through jump function with > - agg_preserved set or an ancestor jump function with agg_preserved set, all > - unlisted positions are assumed to be preserved but the value can be a type > - node, which means that the particular piece (starting at offset and having > - the size of the type) is clobbered with an unknown value. When > - agg_preserved is false or the type of the containing jump function is > - different, all unlisted parts are assumed to be unknown and all values > must > - fulfill is_gimple_ip_invariant. */ > +/* A jump function for an aggregate part at a given offset, which describes > how > + it content value is generated. All unlisted positions are assumed to > have a > + value defined in an unknown way. */ > > struct GTY(()) ipa_agg_jf_item > { > - /* The offset at which the known value is located within the aggregate. */ > + /* The offset for the aggregate part. */ > HOST_WIDE_INT offset; > > - /* The known constant or type if this is a clobber. */ > - tree value; > -}; > + /* Data type of the aggregate part. */ > + tree type; > > + /* Jump function type. */ > + enum jump_func_type jftype; > > -/* Aggregate jump function - i.e. description of contents of aggregates > passed > - either by reference or value. */ > + /* Represents a value of jump function. constant represents the actual > constant > + in constant jump function content. pass_through is used only in simple > pass > + through jump function context. load_agg is for > load-value-from-aggregate > + jump function context. */ > + union jump_func_agg_value > + { > + tree GTY ((tag ("IPA_JF_CONST"))) constant; > + struct ipa_pass_through_data GTY ((tag ("IPA_JF_PASS_THROUGH"))) > pass_through; > + struct ipa_load_agg_data GTY ((tag ("IPA_JF_LOAD_AGG"))) load_agg; > + } GTY ((desc ("%1.jftype"))) value; > +}; > + > +/* Jump functions describing a set of aggregate contents. */ > > struct GTY(()) ipa_agg_jump_function > { > - /* Description of the individual items. */ > + /* Description of the individual jump function item. */ > vec<ipa_agg_jf_item, va_gc> *items; > - /* True if the data was passed by reference (as opposed to by value). */ > + /* True if the data was passed by reference (as opposed to by value). */ > + bool by_ref; > +}; > + > +/* An element in an aggregate part describing a known value at a given > offset. > + All unlisted positions are assumed to be unknown and all listed values > must > + fulfill is_gimple_ip_invariant. */ > + > +struct GTY(()) ipa_agg_value > +{ > + /* The offset at which the known value is located within the aggregate. */ > + HOST_WIDE_INT offset; > + > + /* The known constant. */ > + tree value; > +}; > + > +/* Structure describing a set of known offset/value for aggregate. */ > + > +struct GTY(()) ipa_agg_value_set > +{ > + /* Description of the individual item. */ > + vec<ipa_agg_value> items; > + /* True if the data was passed by reference (as opposed to by value). */ > bool by_ref; > + > + void release () > + { > + items.release (); > + } > }; > > -typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p; > +/* For vec<ipa_agg_value_set>, DO NOT call release(), use below function > + instead. Because ipa_agg_value_set contains a field of vector type, we > + should release this child vector in each element before reclaiming the > + whole vector. */ > + > +static inline void > +ipa_release_agg_values (vec<ipa_agg_value_set> &aggs) > +{ > + ipa_agg_value_set *agg; > + int i; > + > + FOR_EACH_VEC_ELT (aggs, i, agg) > + agg->release (); > + aggs.release (); > +} > > /* Information about zero/non-zero bits. */ > class GTY(()) ipa_bits > @@ -172,8 +250,8 @@ public: > types of jump functions supported. */ > struct GTY (()) ipa_jump_func > { > - /* Aggregate contants description. See struct ipa_agg_jump_function and > its > - description. */ > + /* Aggregate jump function description. See struct ipa_agg_jump_function > + and its description. */ > struct ipa_agg_jump_function agg; > > /* Information about zero/non-zero bits. The pointed to structure is shared > @@ -742,9 +820,9 @@ 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<tree> , > + vec<tree>, > vec<ipa_polymorphic_call_context>, > - vec<ipa_agg_jump_function_p>, > + vec<ipa_agg_value_set>, > bool *); > struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, > tree, > bool speculative = false); > @@ -757,7 +835,7 @@ ipa_bits *ipa_get_ipa_bits_for_value (const widest_int > &value, > void ipa_analyze_node (struct cgraph_node *); > > /* Aggregate jump function related functions. */ > -tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree > scalar, > +tree ipa_find_agg_cst_for_param (struct ipa_agg_value_set *agg, tree scalar, > HOST_WIDE_INT offset, bool by_ref, > bool *from_global_constant = NULL); > bool ipa_load_from_parm_agg (struct ipa_func_body_info *fbi, > @@ -803,6 +881,9 @@ ipa_polymorphic_call_context ipa_context_from_jfunc > (ipa_node_params *, > cgraph_edge *, > int, > ipa_jump_func *); > +ipa_agg_value_set ipa_agg_value_set_from_jfunc (ipa_node_params *, > + cgraph_node *, > + ipa_agg_jump_function *); > void ipa_dump_param (FILE *, class ipa_node_params *info, int i); > void ipa_release_body_info (struct ipa_func_body_info *); > tree ipa_get_callee_param_type (struct cgraph_edge *e, int i); > diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-10.c > b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-10.c > index 16d62e72c9a..c61e96a842b 100644 > --- a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-10.c > +++ b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-10.c > @@ -72,7 +72,7 @@ int caller2(void) > return sum; > } > > -/* { dg-final { scan-ipa-dump-times "offset: 0, cst: 1" 1 "cp" } } */ > -/* { dg-final { scan-ipa-dump-times "offset: 0, cst: 2" 1 "cp" } } */ > -/* { dg-final { scan-ipa-dump-times "offset: 0, cst: 3" 1 "cp" } } */ > -/* { dg-final { scan-ipa-dump-times "offset: 64, cst: 4" 1 "cp" } } */ > +/* { dg-final { scan-ipa-dump-times "offset: 0, type: int, CONST: 1" 1 "cp" > } } */ > +/* { dg-final { scan-ipa-dump-times "offset: 0, type: int, CONST: 2" 1 "cp" > } } */ > +/* { dg-final { scan-ipa-dump-times "offset: 0, type: int, CONST: 3" 1 "cp" > } } */ > +/* { dg-final { scan-ipa-dump-times "offset: 64, type: int, CONST: 4" 1 "cp" > } } */ > diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c > b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c > new file mode 100644 > index 00000000000..3c496eeef39 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c > @@ -0,0 +1,77 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O3 -fno-ipa-sra -fdump-ipa-cp-details -fno-early-inlining" > } */ > +/* { dg-add-options bind_pic_locally } */ > + > +struct S > +{ > + int a, b, c; > +}; > + > +void *blah(int, void *); > + > +#define foo_body(p)\ > +{ \ > + int i, c = (p)->c; \ > + int b = (p)->b; \ > + void *v = (void *) (p); \ > + \ > + for (i= 0; i< c; i++) \ > + v = blah(b + i, v); \ > +} > + > +static void __attribute__ ((noinline)) > +foo_v (struct S s) > +{ > + foo_body (&s); > +} > + > +static void __attribute__ ((noinline)) > +foo_r (struct S *p) > +{ > + foo_body (p); > +} > + > +static void > +goo_v (int a, int *p) > +{ > + struct S s; > + s.a = 101; > + s.b = a % 7; > + s.c = *p + 6; > + foo_v (s); > +} > + > +static void > +goo_r (int a, struct S n) > +{ > + struct S s; > + s.a = 1; > + s.b = a + 5; > + s.c = -n.b; > + foo_r (&s); > +} > + > +void > +entry () > +{ > + int a; > + int v; > + struct S s; > + > + a = 9; > + v = 3; > + goo_v (a, &v); > + > + a = 100; > + s.b = 18; > + goo_r (a, s); > +} > + > +/* { dg-final { scan-ipa-dump "offset: 0, type: int, CONST: 1" "cp" } } */ > +/* { dg-final { scan-ipa-dump "offset: 32, type: int, PASS THROUGH: 0, op > plus_expr 5" "cp" } } */ > +/* { dg-final { scan-ipa-dump "offset: 64, type: int, LOAD AGG: 1 > \\\[offset: 32, by value], op negate_expr" "cp" } } */ > +/* { dg-final { scan-ipa-dump "offset: 0, type: int, CONST: 101" "cp" } } */ > +/* { dg-final { scan-ipa-dump "offset: 32, type: int, PASS THROUGH: 0, op > trunc_mod_expr 7" "cp" } } */ > +/* { dg-final { scan-ipa-dump "offset: 64, type: int, LOAD AGG: 1 > \\\[offset: 0, by reference], op plus_expr 6" "cp" } } */ > +/* { dg-final { scan-ipa-dump "Aggregate replacements: 0\\\[0]=1, > 0\\\[32]=105, 0\\\[64]=-18" "cp" } } */ > +/* { dg-final { scan-ipa-dump "Aggregate replacements: 0\\\[0]=101, > 0\\\[32]=2, 0\\\[64]=9" "cp" } } */ > -- > 2.17.1 >