> >> + if (unmodified_parm_or_parm_agg_item (fbi, stmt, expr, index_p,
> >> &size,
> >> + aggpos))
> >> + {
> >> + tree type = TREE_TYPE (expr);
> >> +
> >> + /* Stop if found bit-field whose size does not equal any natural
> >> + integral type. */
> >> + if (maybe_ne (tree_to_poly_int64 (TYPE_SIZE (type)), size))
> >> + break;
>
> > Why is this needed?
> This is to exclude bit-field reference. Actually, the restrict is not
> required, a TODO to remove it. Now I directly check bit-field attribute.
>
> >> - = add_condition (summary, index, size, &aggpos, this_code,
> >> - unshare_expr_without_location
> >> - (gimple_cond_rhs (last)));
> >> + = add_condition (summary, index, type, &aggpos, this_code,
> >> + gimple_cond_rhs (last), param_ops);
>
> > An why unsharing is no longer needed here?
> > It is importnat to avoid anything which reffers to function local blocks
> > to get into the global LTO stream.
> Do unsharing inside add_condition, since constants in param_ops also need to
> be unshared.
>
> >> + if (op1.code != op2.code
> >> + || op1.val_is_rhs != op2.val_is_rhs
> >> + || !vrp_operand_equal_p (op1.val, op2.val)
>
> > Why you need vrp_operand_equal_p instead of operand_equal_p here?
> op1.val and op2.val might be NULL_TREE.
>
> > Overall the patch looks very reasonable. We may end up with bit more
> > general expressions (i.e. supporting arithmetics involving more than one
> > operand).
> If you means more than one constant operand, I'v changed the patch to support
> ternary operation.
>
> And if you means more than one parameter operand, this will involve much more
> code change in ipa-fnsummary, it's better to let it be another TODO.
>
> > I see you also fixed a lot of typos in comments, please go head and
> > commit them separately as obvious.
> Removed.
>
> Thank for your comments.
This is OK now. please add changelog.
Honza
> Feng
> ---
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 0e3693598e7..05b1bb97c6b 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -11943,6 +11943,13 @@ For switch exceeding this limit, IPA-CP will not
> construct cloning cost
> predicate, which is used to estimate cloning benefit, for default case
> of the switch statement.
>
> +@item ipa-max-param-expr-ops
> +IPA-CP will analyze conditional statement that references some function
> +parameter to estimate benefit for cloning upon certain constant value.
> +But if number of operations in a parameter expression exceeds
> +@option{ipa-max-param-expr-ops}, the expression is treated as complicated
> +one, and is not handled by IPA analysis.
> +
> @item lto-partitions
> Specify desired number of partitions produced during WHOPR compilation.
> The number of partitions should exceed the number of CPUs used for
> compilation.
> diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
> index 1bf1806eaf8..c25e3395f59 100644
> --- a/gcc/ipa-fnsummary.c
> +++ b/gcc/ipa-fnsummary.c
> @@ -331,6 +331,8 @@ evaluate_conditions_for_known_args (struct cgraph_node
> *node,
> {
> tree val;
> tree res;
> + int j;
> + struct expr_eval_op *op;
>
> /* We allow call stmt to have fewer arguments than the callee function
> (especially for K&R style programs). So bound check here (we assume
> @@ -382,7 +384,7 @@ evaluate_conditions_for_known_args (struct cgraph_node
> *node,
> continue;
> }
>
> - if (maybe_ne (tree_to_poly_int64 (TYPE_SIZE (TREE_TYPE (val))),
> c->size))
> + if (TYPE_SIZE (c->type) != TYPE_SIZE (TREE_TYPE (val)))
> {
> clause |= 1 << (i + predicate::first_dynamic_condition);
> nonspec_clause |= 1 << (i + predicate::first_dynamic_condition);
> @@ -394,7 +396,30 @@ evaluate_conditions_for_known_args (struct cgraph_node
> *node,
> continue;
> }
>
> - val = fold_unary (VIEW_CONVERT_EXPR, TREE_TYPE (c->val), val);
> + val = fold_unary (VIEW_CONVERT_EXPR, c->type, val);
> + for (j = 0; vec_safe_iterate (c->param_ops, j, &op); j++)
> + {
> + if (!val)
> + break;
> + if (!op->val[0])
> + val = fold_unary (op->code, op->type, val);
> + else if (!op->val[1])
> + val = fold_binary (op->code, op->type,
> + op->index ? op->val[0] : val,
> + op->index ? val : op->val[0]);
> + else if (op->index == 0)
> + val = fold_ternary (op->code, op->type,
> + val, op->val[0], op->val[1]);
> + else if (op->index == 1)
> + val = fold_ternary (op->code, op->type,
> + op->val[0], val, op->val[1]);
> + else if (op->index == 2)
> + val = fold_ternary (op->code, op->type,
> + op->val[0], op->val[1], val);
> + else
> + val = NULL_TREE;
> + }
> +
> res = val
> ? fold_binary_to_constant (c->code, boolean_type_node, val, c->val)
> : NULL;
> @@ -1157,6 +1182,127 @@ eliminated_by_inlining_prob (ipa_func_body_info *fbi,
> gimple *stmt)
> }
> }
>
> +/* Analyze EXPR if it represents a series of simple operations performed on
> + a function parameter and return true if so. FBI, STMT, EXPR, INDEX_P and
> + AGGPOS have the same meaning like in unmodified_parm_or_parm_agg_item.
> + Type of the parameter or load from an aggregate via the parameter is
> + stored in *TYPE_P. Operations on the parameter are recorded to
> + PARAM_OPS_P if it is not NULL. */
> +
> +static bool
> +decompose_param_expr (struct ipa_func_body_info *fbi,
> + gimple *stmt, tree expr,
> + int *index_p, tree *type_p,
> + struct agg_position_info *aggpos,
> + expr_eval_ops *param_ops_p = NULL)
> +{
> + int op_limit = PARAM_VALUE (PARAM_IPA_MAX_PARAM_EXPR_OPS);
> + int op_count = 0;
> +
> + if (param_ops_p)
> + *param_ops_p = NULL;
> +
> + while (true)
> + {
> + expr_eval_op eval_op;
> + unsigned rhs_count;
> + unsigned cst_count = 0;
> +
> + if (unmodified_parm_or_parm_agg_item (fbi, stmt, expr, index_p, NULL,
> + aggpos))
> + {
> + tree type = TREE_TYPE (expr);
> +
> + if (aggpos->agg_contents)
> + {
> + /* Stop if containing bit-field. */
> + if (TREE_CODE (expr) == BIT_FIELD_REF
> + || contains_bitfld_component_ref_p (expr))
> + break;
> + }
> +
> + *type_p = type;
> + return true;
> + }
> +
> + if (TREE_CODE (expr) != SSA_NAME || SSA_NAME_IS_DEFAULT_DEF (expr))
> + break;
> +
> + if (!is_gimple_assign (stmt = SSA_NAME_DEF_STMT (expr)))
> + break;
> +
> + switch (gimple_assign_rhs_class (stmt))
> + {
> + case GIMPLE_SINGLE_RHS:
> + expr = gimple_assign_rhs1 (stmt);
> + continue;
> +
> + case GIMPLE_UNARY_RHS:
> + rhs_count = 1;
> + break;
> +
> + case GIMPLE_BINARY_RHS:
> + rhs_count = 2;
> + break;
> +
> + case GIMPLE_TERNARY_RHS:
> + rhs_count = 3;
> + break;
> +
> + default:
> + goto fail;
> + }
> +
> + /* Stop if expression is too complex. */
> + if (op_count++ == op_limit)
> + break;
> +
> + if (param_ops_p)
> + {
> + eval_op.code = gimple_assign_rhs_code (stmt);
> + eval_op.type = TREE_TYPE (gimple_assign_lhs (stmt));
> + eval_op.val[0] = NULL_TREE;
> + eval_op.val[1] = NULL_TREE;
> + }
> +
> + expr = NULL_TREE;
> + for (unsigned i = 0; i < rhs_count; i++)
> + {
> + tree op = gimple_op (stmt, i + 1);
> +
> + gcc_assert (op && !TYPE_P (op));
> + if (is_gimple_ip_invariant (op))
> + {
> + if (++cst_count == rhs_count)
> + goto fail;
> +
> + eval_op.val[cst_count - 1] = op;
> + }
> + else if (!expr)
> + {
> + /* Found a non-constant operand, and record its index in rhs
> + operands. */
> + eval_op.index = i;
> + expr = op;
> + }
> + else
> + {
> + /* Found more than one non-constant operands. */
> + goto fail;
> + }
> + }
> +
> + if (param_ops_p)
> + vec_safe_insert (*param_ops_p, 0, eval_op);
> + }
> +
> + /* Failed to decompose, free resource and return. */
> +fail:
> + if (param_ops_p)
> + vec_free (*param_ops_p);
> +
> + return false;
> +}
>
> /* If BB ends by a conditional we can turn into predicates, attach
> corresponding
> predicates to the CFG edges. */
> @@ -1167,15 +1313,15 @@ set_cond_stmt_execution_predicate (struct
> ipa_func_body_info *fbi,
> basic_block bb)
> {
> gimple *last;
> - tree op;
> + tree op, op2;
> int index;
> - poly_int64 size;
> struct agg_position_info aggpos;
> enum tree_code code, inverted_code;
> edge e;
> edge_iterator ei;
> gimple *set_stmt;
> - tree op2;
> + tree param_type;
> + expr_eval_ops param_ops;
>
> last = last_stmt (bb);
> if (!last || gimple_code (last) != GIMPLE_COND)
> @@ -1183,10 +1329,9 @@ set_cond_stmt_execution_predicate (struct
> ipa_func_body_info *fbi,
> if (!is_gimple_ip_invariant (gimple_cond_rhs (last)))
> return;
> op = gimple_cond_lhs (last);
> - /* TODO: handle conditionals like
> - var = op0 < 4;
> - if (var != 0). */
> - if (unmodified_parm_or_parm_agg_item (fbi, last, op, &index, &size,
> &aggpos))
> +
> + if (decompose_param_expr (fbi, last, op, &index, ¶m_type, &aggpos,
> + ¶m_ops))
> {
> code = gimple_cond_code (last);
> inverted_code = invert_tree_comparison (code, HONOR_NANS (op));
> @@ -1201,13 +1346,13 @@ set_cond_stmt_execution_predicate (struct
> ipa_func_body_info *fbi,
> if (this_code != ERROR_MARK)
> {
> predicate p
> - = add_condition (summary, index, size, &aggpos, this_code,
> - unshare_expr_without_location
> - (gimple_cond_rhs (last)));
> + = add_condition (summary, index, param_type, &aggpos,
> + this_code, gimple_cond_rhs (last), param_ops);
> e->aux = edge_predicate_pool.allocate ();
> *(predicate *) e->aux = p;
> }
> }
> + vec_free (param_ops);
> }
>
> if (TREE_CODE (op) != SSA_NAME)
> @@ -1230,12 +1375,11 @@ set_cond_stmt_execution_predicate (struct
> ipa_func_body_info *fbi,
> || gimple_call_num_args (set_stmt) != 1)
> return;
> op2 = gimple_call_arg (set_stmt, 0);
> - if (!unmodified_parm_or_parm_agg_item (fbi, set_stmt, op2, &index, &size,
> - &aggpos))
> + if (!decompose_param_expr (fbi, set_stmt, op2, &index, ¶m_type,
> &aggpos))
> return;
> FOR_EACH_EDGE (e, ei, bb->succs) if (e->flags & EDGE_FALSE_VALUE)
> {
> - predicate p = add_condition (summary, index, size, &aggpos,
> + predicate p = add_condition (summary, index, param_type, &aggpos,
> predicate::is_not_constant, NULL_TREE);
> e->aux = edge_predicate_pool.allocate ();
> *(predicate *) e->aux = p;
> @@ -1254,19 +1398,21 @@ set_switch_stmt_execution_predicate (struct
> ipa_func_body_info *fbi,
> gimple *lastg;
> tree op;
> int index;
> - poly_int64 size;
> struct agg_position_info aggpos;
> edge e;
> edge_iterator ei;
> size_t n;
> size_t case_idx;
> + tree param_type;
> + expr_eval_ops param_ops;
>
> lastg = last_stmt (bb);
> if (!lastg || gimple_code (lastg) != GIMPLE_SWITCH)
> return;
> gswitch *last = as_a <gswitch *> (lastg);
> op = gimple_switch_index (last);
> - if (!unmodified_parm_or_parm_agg_item (fbi, last, op, &index, &size,
> &aggpos))
> + if (!decompose_param_expr (fbi, last, op, &index, ¶m_type, &aggpos,
> + ¶m_ops))
> return;
>
> auto_vec<std::pair<tree, tree> > ranges;
> @@ -1294,15 +1440,15 @@ set_switch_stmt_execution_predicate (struct
> ipa_func_body_info *fbi,
> max = CASE_HIGH (cl);
>
> if (!max)
> - p = add_condition (summary, index, size, &aggpos, EQ_EXPR,
> - unshare_expr_without_location (min));
> + p = add_condition (summary, index, param_type, &aggpos, EQ_EXPR,
> + min, param_ops);
> else
> {
> predicate p1, p2;
> - p1 = add_condition (summary, index, size, &aggpos, GE_EXPR,
> - unshare_expr_without_location (min));
> - p2 = add_condition (summary, index, size, &aggpos, LE_EXPR,
> - unshare_expr_without_location (max));
> + p1 = add_condition (summary, index, param_type, &aggpos, GE_EXPR,
> + min, param_ops);
> + p2 = add_condition (summary, index, param_type, &aggpos, LE_EXPR,
> + max, param_ops);
> p = p1 & p2;
> }
> *(class predicate *) e->aux
> @@ -1356,6 +1502,7 @@ set_switch_stmt_execution_predicate (struct
> ipa_func_body_info *fbi,
> if (bound_count > bound_limit)
> {
> *(class predicate *) e->aux = true;
> + vec_free (param_ops);
> return;
> }
>
> @@ -1385,16 +1532,16 @@ set_switch_stmt_execution_predicate (struct
> ipa_func_body_info *fbi,
> tree max = ranges[i].second;
>
> if (min == max)
> - p_seg &= add_condition (summary, index, size, &aggpos, NE_EXPR,
> - unshare_expr_without_location (min));
> + p_seg &= add_condition (summary, index, param_type, &aggpos, NE_EXPR,
> + min, param_ops);
> else
> {
> /* Do not create sub-predicate for range that is beyond low bound
> of switch index. */
> if (wi::lt_p (vr_wmin, wi::to_wide (min), TYPE_SIGN (type)))
> {
> - p_seg &= add_condition (summary, index, size, &aggpos, LT_EXPR,
> - unshare_expr_without_location (min));
> + p_seg &= add_condition (summary, index, param_type, &aggpos,
> + LT_EXPR, min, param_ops);
> p_all = p_all.or_with (summary->conds, p_seg);
> }
>
> @@ -1406,14 +1553,16 @@ set_switch_stmt_execution_predicate (struct
> ipa_func_body_info *fbi,
> break;
> }
>
> - p_seg = add_condition (summary, index, size, &aggpos, GT_EXPR,
> - unshare_expr_without_location (max));
> + p_seg = add_condition (summary, index, param_type, &aggpos, GT_EXPR,
> + max, param_ops);
> }
> }
>
> p_all = p_all.or_with (summary->conds, p_seg);
> *(class predicate *) e->aux
> = p_all.or_with (summary->conds, *(class predicate *) e->aux);
> +
> + vec_free (param_ops);
> }
>
>
> @@ -1502,15 +1651,14 @@ will_be_nonconstant_expr_predicate
> (ipa_func_body_info *fbi,
> {
> tree parm;
> int index;
> - poly_int64 size;
>
> while (UNARY_CLASS_P (expr))
> expr = TREE_OPERAND (expr, 0);
>
> - parm = unmodified_parm (fbi, NULL, expr, &size);
> + parm = unmodified_parm (fbi, NULL, expr, NULL);
> if (parm && (index = ipa_get_param_decl_index (fbi->info, parm)) >= 0)
> - return add_condition (summary, index, size, NULL, predicate::changed,
> - NULL_TREE);
> + return add_condition (summary, index, TREE_TYPE (parm), NULL,
> + predicate::changed, NULL_TREE);
> if (is_gimple_min_invariant (expr))
> return false;
> if (TREE_CODE (expr) == SSA_NAME)
> @@ -1574,10 +1722,10 @@ will_be_nonconstant_predicate (struct
> ipa_func_body_info *fbi,
> predicate p = true;
> ssa_op_iter iter;
> tree use;
> + tree param_type = NULL_TREE;
> predicate op_non_const;
> bool is_load;
> int base_index;
> - poly_int64 size;
> struct agg_position_info aggpos;
>
> /* What statments might be optimized away
> @@ -1598,11 +1746,9 @@ will_be_nonconstant_predicate (struct
> ipa_func_body_info *fbi,
> /* Loads can be optimized when the value is known. */
> if (is_load)
> {
> - tree op;
> - gcc_assert (gimple_assign_single_p (stmt));
> - op = gimple_assign_rhs1 (stmt);
> - if (!unmodified_parm_or_parm_agg_item (fbi, stmt, op, &base_index,
> &size,
> - &aggpos))
> + tree op = gimple_assign_rhs1 (stmt);
> + if (!decompose_param_expr (fbi, stmt, op, &base_index, ¶m_type,
> + &aggpos))
> return p;
> }
> else
> @@ -1627,21 +1773,20 @@ will_be_nonconstant_predicate (struct
> ipa_func_body_info *fbi,
>
> if (is_load)
> op_non_const =
> - add_condition (summary, base_index, size, &aggpos, predicate::changed,
> - NULL);
> + add_condition (summary, base_index, param_type, &aggpos,
> + predicate::changed, NULL_TREE);
> else
> op_non_const = false;
> FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
> {
> - poly_int64 size;
> - tree parm = unmodified_parm (fbi, stmt, use, &size);
> + tree parm = unmodified_parm (fbi, stmt, use, NULL);
> int index;
>
> if (parm && (index = ipa_get_param_decl_index (fbi->info, parm)) >= 0)
> {
> if (index != base_index)
> - p = add_condition (summary, index, size, NULL, predicate::changed,
> - NULL_TREE);
> + p = add_condition (summary, index, TREE_TYPE (parm), NULL,
> + predicate::changed, NULL_TREE);
> else
> continue;
> }
> @@ -1773,7 +1918,7 @@ param_change_prob (ipa_func_body_info *fbi, gimple
> *stmt, int i)
> return REG_BR_PROB_BASE;
> if (dump_file)
> {
> - fprintf (dump_file, " Analyzing param change probablity of ");
> + fprintf (dump_file, " Analyzing param change probability of ");
> print_generic_expr (dump_file, op, TDF_SLIM);
> fprintf (dump_file, "\n");
> }
> @@ -3400,15 +3545,49 @@ inline_read_section (struct lto_file_decl_data
> *file_data, const char *data,
> for (j = 0; j < count2; j++)
> {
> struct condition c;
> + unsigned int k, count3;
> c.operand_num = streamer_read_uhwi (&ib);
> - c.size = streamer_read_poly_uint64 (&ib);
> c.code = (enum tree_code) streamer_read_uhwi (&ib);
> + c.type = stream_read_tree (&ib, data_in);
> c.val = stream_read_tree (&ib, data_in);
> bp = streamer_read_bitpack (&ib);
> c.agg_contents = bp_unpack_value (&bp, 1);
> c.by_ref = bp_unpack_value (&bp, 1);
> if (c.agg_contents)
> c.offset = streamer_read_uhwi (&ib);
> + c.param_ops = NULL;
> + count3 = streamer_read_uhwi (&ib);
> + for (k = 0; k < count3; k++)
> + {
> + struct expr_eval_op op;
> + enum gimple_rhs_class rhs_class;
> + op.code = (enum tree_code) streamer_read_uhwi (&ib);
> + op.type = stream_read_tree (&ib, data_in);
> + switch (rhs_class = get_gimple_rhs_class (op.code))
> + {
> + case GIMPLE_UNARY_RHS:
> + op.index = 0;
> + op.val[0] = NULL_TREE;
> + op.val[1] = NULL_TREE;
> + break;
> +
> + case GIMPLE_BINARY_RHS:
> + case GIMPLE_TERNARY_RHS:
> + bp = streamer_read_bitpack (&ib);
> + op.index = bp_unpack_value (&bp, 2);
> + op.val[0] = stream_read_tree (&ib, data_in);
> + if (rhs_class == GIMPLE_BINARY_RHS)
> + op.val[1] = NULL_TREE;
> + else
> + op.val[1] = stream_read_tree (&ib, data_in);
> + break;
> +
> + default:
> + fatal_error (UNKNOWN_LOCATION,
> + "invalid fnsummary in LTO stream");
> + }
> + vec_safe_push (c.param_ops, op);
> + }
> if (info)
> vec_safe_push (info->conds, c);
> }
> @@ -3554,9 +3733,12 @@ ipa_fn_summary_write (void)
> streamer_write_uhwi (ob, vec_safe_length (info->conds));
> for (i = 0; vec_safe_iterate (info->conds, i, &c); i++)
> {
> + int j;
> + struct expr_eval_op *op;
> +
> streamer_write_uhwi (ob, c->operand_num);
> - streamer_write_poly_uint64 (ob, c->size);
> streamer_write_uhwi (ob, c->code);
> + stream_write_tree (ob, c->type, true);
> stream_write_tree (ob, c->val, true);
> bp = bitpack_create (ob->main_stream);
> bp_pack_value (&bp, c->agg_contents, 1);
> @@ -3564,6 +3746,21 @@ ipa_fn_summary_write (void)
> streamer_write_bitpack (&bp);
> if (c->agg_contents)
> streamer_write_uhwi (ob, c->offset);
> + streamer_write_uhwi (ob, vec_safe_length (c->param_ops));
> + for (j = 0; vec_safe_iterate (c->param_ops, j, &op); j++)
> + {
> + streamer_write_uhwi (ob, op->code);
> + stream_write_tree (ob, op->type, true);
> + if (op->val[0])
> + {
> + bp = bitpack_create (ob->main_stream);
> + bp_pack_value (&bp, op->index, 2);
> + streamer_write_bitpack (&bp);
> + stream_write_tree (ob, op->val[0], true);
> + if (op->val[1])
> + stream_write_tree (ob, op->val[1], true);
> + }
> + }
> }
> streamer_write_uhwi (ob, vec_safe_length (info->size_time_table));
> for (i = 0; vec_safe_iterate (info->size_time_table, i, &e); i++)
> diff --git a/gcc/ipa-predicate.c b/gcc/ipa-predicate.c
> index 8a9851a2040..b5e3cf44323 100644
> --- a/gcc/ipa-predicate.c
> +++ b/gcc/ipa-predicate.c
> @@ -33,9 +33,36 @@ along with GCC; see the file COPYING3. If not see
> #include "fold-const.h"
> #include "tree-pretty-print.h"
> #include "gimple.h"
> +#include "gimplify.h"
> #include "data-streamer.h"
>
>
> +/* Check whether two set of operations have same effects. */
> +static bool
> +expr_eval_ops_equal_p (expr_eval_ops ops1, expr_eval_ops ops2)
> +{
> + if (ops1)
> + {
> + if (!ops2 || ops1->length () != ops2->length ())
> + return false;
> +
> + for (unsigned i = 0; i < ops1->length (); i++)
> + {
> + expr_eval_op &op1 = (*ops1)[i];
> + expr_eval_op &op2 = (*ops2)[i];
> +
> + if (op1.code != op2.code
> + || op1.index != op2.index
> + || !vrp_operand_equal_p (op1.val[0], op2.val[0])
> + || !vrp_operand_equal_p (op1.val[1], op2.val[1])
> + || !types_compatible_p (op1.type, op2.type))
> + return false;
> + }
> + return true;
> + }
> + return !ops2;
> +}
> +
> /* Add clause CLAUSE into the predicate P.
> When CONDITIONS is NULL do not perform checking whether NEW_CLAUSE
> is obviously true. This is useful only when NEW_CLAUSE is known to be
> @@ -110,14 +137,16 @@ predicate::add_clause (conditions conditions, clause_t
> new_clause)
> for (c2 = c1 + 1; c2 < num_conditions; c2++)
> if (new_clause & (1 << c2))
> {
> - condition *cc1 =
> - &(*conditions)[c1 - predicate::first_dynamic_condition];
> condition *cc2 =
> &(*conditions)[c2 - predicate::first_dynamic_condition];
> if (cc1->operand_num == cc2->operand_num
> - && cc1->val == cc2->val
> + && vrp_operand_equal_p (cc1->val, cc2->val)
> && cc2->code != is_not_constant
> - && cc2->code != predicate::changed
> + && cc2->code != changed
> + && expr_eval_ops_equal_p (cc1->param_ops, cc2->param_ops)
> + && cc2->agg_contents == cc1->agg_contents
> + && cc2->by_ref == cc1->by_ref
> + && types_compatible_p (cc2->type, cc1->type)
> && cc1->code == invert_tree_comparison (cc2->code,
> HONOR_NANS
> (cc1->val)))
> return;
> @@ -300,6 +329,83 @@ dump_condition (FILE *f, conditions conditions, int cond)
> if (c->agg_contents)
> fprintf (f, "[%soffset: " HOST_WIDE_INT_PRINT_DEC "]",
> c->by_ref ? "ref " : "", c->offset);
> +
> + for (unsigned i = 0; i < vec_safe_length (c->param_ops); i++)
> + {
> + expr_eval_op &op = (*(c->param_ops))[i];
> + const char *op_name = op_symbol_code (op.code);
> +
> + if (op_name == op_symbol_code (ERROR_MARK))
> + op_name = get_tree_code_name (op.code);
> +
> + fprintf (f, ",(");
> +
> + if (!op.val[0])
> + {
> + switch (op.code)
> + {
> + case FLOAT_EXPR:
> + case FIX_TRUNC_EXPR:
> + case FIXED_CONVERT_EXPR:
> + case VIEW_CONVERT_EXPR:
> + CASE_CONVERT:
> + if (op.code == VIEW_CONVERT_EXPR)
> + fprintf (f, "VCE");
> + fprintf (f, "(");
> + print_generic_expr (f, op.type);
> + fprintf (f, ")" );
> + break;
> +
> + default:
> + fprintf (f, "%s", op_name);
> + }
> + fprintf (f, " #");
> + }
> + else if (!op.val[1])
> + {
> + if (op.index)
> + {
> + print_generic_expr (f, op.val[0]);
> + fprintf (f, " %s #", op_name);
> + }
> + else
> + {
> + fprintf (f, "# %s ", op_name);
> + print_generic_expr (f, op.val[0]);
> + }
> + }
> + else
> + {
> + fprintf (f, "%s ", op_name);
> + switch (op.index)
> + {
> + case 0:
> + fprintf (f, "#, ");
> + print_generic_expr (f, op.val[0]);
> + fprintf (f, ", ");
> + print_generic_expr (f, op.val[1]);
> + break;
> +
> + case 1:
> + print_generic_expr (f, op.val[0]);
> + fprintf (f, ", #, ");
> + print_generic_expr (f, op.val[1]);
> + break;
> +
> + case 2:
> + print_generic_expr (f, op.val[0]);
> + fprintf (f, ", ");
> + print_generic_expr (f, op.val[1]);
> + fprintf (f, ", #");
> + break;
> +
> + default:
> + fprintf (f, "*, *, *");
> + }
> + }
> + fprintf (f, ")");
> + }
> +
> if (c->code == predicate::is_not_constant)
> {
> fprintf (f, " not constant");
> @@ -462,8 +568,8 @@ predicate::remap_after_inlining (class ipa_fn_summary
> *info,
> ap.by_ref = c->by_ref;
> cond_predicate = add_condition (info,
> operand_map[c->operand_num],
> - c->size, &ap, c->code,
> - c->val);
> + c->type, &ap, c->code,
> + c->val, c->param_ops);
> }
> }
> /* Fixed conditions remains same, construct single
> @@ -516,21 +622,23 @@ predicate::stream_out (struct output_block *ob)
> }
>
>
> -/* Add condition to condition list SUMMARY. OPERAND_NUM, SIZE, CODE and VAL
> - correspond to fields of condition structure. AGGPOS describes whether the
> - used operand is loaded from an aggregate and where in the aggregate it is.
> - It can be NULL, which means this not a load from an aggregate. */
> +/* Add condition to condition list SUMMARY. OPERAND_NUM, TYPE, CODE, VAL and
> + PARAM_OPS correspond to fields of condition structure. AGGPOS describes
> + whether the used operand is loaded from an aggregate and where in the
> + aggregate it is. It can be NULL, which means this not a load from an
> + aggregate. */
>
> predicate
> add_condition (class ipa_fn_summary *summary, int operand_num,
> - poly_int64 size, struct agg_position_info *aggpos,
> - enum tree_code code, tree val)
> + tree type, struct agg_position_info *aggpos,
> + enum tree_code code, tree val, expr_eval_ops param_ops)
> {
> - int i;
> + int i, j;
> struct condition *c;
> struct condition new_cond;
> HOST_WIDE_INT offset;
> bool agg_contents, by_ref;
> + expr_eval_op *op;
>
> if (aggpos)
> {
> @@ -549,10 +657,11 @@ add_condition (class ipa_fn_summary *summary, int
> operand_num,
> for (i = 0; vec_safe_iterate (summary->conds, i, &c); i++)
> {
> if (c->operand_num == operand_num
> - && known_eq (c->size, size)
> && c->code == code
> - && c->val == val
> + && types_compatible_p (c->type, type)
> + && vrp_operand_equal_p (c->val, val)
> && c->agg_contents == agg_contents
> + && expr_eval_ops_equal_p (c->param_ops, param_ops)
> && (!agg_contents || (c->offset == offset && c->by_ref == by_ref)))
> return predicate::predicate_testing_cond (i);
> }
> @@ -562,11 +671,21 @@ add_condition (class ipa_fn_summary *summary, int
> operand_num,
>
> new_cond.operand_num = operand_num;
> new_cond.code = code;
> - new_cond.val = val;
> + new_cond.type = unshare_expr_without_location (type);
> + new_cond.val = val ? unshare_expr_without_location (val) : val;
> new_cond.agg_contents = agg_contents;
> new_cond.by_ref = by_ref;
> new_cond.offset = offset;
> - new_cond.size = size;
> + new_cond.param_ops = vec_safe_copy (param_ops);
> +
> + for (j = 0; vec_safe_iterate (new_cond.param_ops, j, &op); j++)
> + {
> + if (op->val[0])
> + op->val[0] = unshare_expr_without_location (op->val[0]);
> + if (op->val[1])
> + op->val[1] = unshare_expr_without_location (op->val[1]);
> + }
> +
> vec_safe_push (summary->conds, new_cond);
>
> return predicate::predicate_testing_cond (i);
> diff --git a/gcc/ipa-predicate.h b/gcc/ipa-predicate.h
> index 237306dc9fe..4121218ac04 100644
> --- a/gcc/ipa-predicate.h
> +++ b/gcc/ipa-predicate.h
> @@ -22,16 +22,36 @@ along with GCC; see the file COPYING3. If not see
> inlined into (i.e. known constant values of function parameters.
>
> Conditions that are interesting for function body are collected into CONDS
> - vector. They are of simple for function_param OP VAL, where VAL is
> - IPA invariant. The conditions are then referred by predicates. */
> + vector. They are of simple as kind of a mathematical transformation on
> + function parameter, T(function_param), in which the parameter occurs only
> + once, and other operands are IPA invariant. The conditions are then
> + referred by predicates. */
> +
> +
> +/* A simplified representation of tree node, for unary, binary and ternary
> + operation. Computations on parameter are decomposed to a series of this
> + kind of structure. */
> +struct GTY(()) expr_eval_op
> +{
> + /* Result type of expression. */
> + tree type;
> + /* Constant operands in expression, there are at most two. */
> + tree val[2];
> + /* Index of parameter operand in expression. */
> + unsigned index : 2;
> + /* Operation code of expression. */
> + ENUM_BITFIELD(tree_code) code : 16;
> +};
> +
> +typedef vec<expr_eval_op, va_gc> *expr_eval_ops;
>
> struct GTY(()) condition
> {
> /* If agg_contents is set, this is the offset from which the used data was
> loaded. */
> HOST_WIDE_INT offset;
> - /* Size of the access reading the data (or the PARM_DECL SSA_NAME). */
> - poly_int64 size;
> + /* Type of the access reading the data (or the PARM_DECL SSA_NAME). */
> + tree type;
> tree val;
> int operand_num;
> ENUM_BITFIELD(tree_code) code : 16;
> @@ -41,6 +61,9 @@ struct GTY(()) condition
> /* If agg_contents is set, this differentiates between loads from data
> passed by reference and by value. */
> unsigned by_ref : 1;
> + /* A set of sequential operations on the parameter, which can be seen as
> + a mathmatical function on the parameter. */
> + expr_eval_ops param_ops;
> };
>
> /* Information kept about parameter of call site. */
> @@ -228,5 +251,6 @@ private:
>
> void dump_condition (FILE *f, conditions conditions, int cond);
> predicate add_condition (class ipa_fn_summary *summary, int operand_num,
> - poly_int64 size, struct agg_position_info *aggpos,
> - enum tree_code code, tree val);
> + tree type, struct agg_position_info *aggpos,
> + enum tree_code code, tree val,
> + expr_eval_ops param_ops = NULL);
> diff --git a/gcc/params.def b/gcc/params.def
> index 5fe33976b37..103df3b5458 100644
> --- a/gcc/params.def
> +++ b/gcc/params.def
> @@ -1129,6 +1129,12 @@ DEFPARAM (PARAM_IPA_MAX_SWITCH_PREDICATE_BOUNDS,
> "statement used during IPA functoin summary generation.",
> 5, 0, 0)
>
> +DEFPARAM (PARAM_IPA_MAX_PARAM_EXPR_OPS,
> + "ipa-max-param-expr-ops",
> + "Maximum number of operations in a parameter expression that can "
> + "be handled by IPA analysis. ",
> + 10, 0, 0)
> +
> /* WHOPR partitioning configuration. */
>
> DEFPARAM (PARAM_LTO_PARTITIONS,
> diff --git a/gcc/testsuite/g++.dg/tree-ssa/ivopts-3.C
> b/gcc/testsuite/g++.dg/tree-ssa/ivopts-3.C
> index 07ff1b770f8..cbb6c850baa 100644
> --- a/gcc/testsuite/g++.dg/tree-ssa/ivopts-3.C
> +++ b/gcc/testsuite/g++.dg/tree-ssa/ivopts-3.C
> @@ -25,7 +25,7 @@ protected:
> double stuff;
>
> public:
> - explicit MinimalVector ( int length ) {
> + __attribute__((noinline)) explicit MinimalVector ( int length ) {
> _pData = new double[length];
> for (int i = 0; i < length; ++i) _pData[i] = 0.;
> }
> diff --git a/gcc/testsuite/gcc.dg/ipa/pr91088.c
> b/gcc/testsuite/gcc.dg/ipa/pr91088.c
> new file mode 100644
> index 00000000000..a81c59f98b1
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/ipa/pr91088.c
> @@ -0,0 +1,120 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O3 -fdump-ipa-cp-details -fno-inline" } */
> +
> +int foo();
> +
> +#define large_code \
> +do { \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> + foo (); \
> +} while (1)
> +
> +
> +struct A
> +{
> + char f1;
> + short f2 : 5;
> + int f3;
> +};
> +
> +int callee1 (struct A a)
> +{
> + if ((a.f2 + 7) & 17)
> + foo ();
> +
> + if ((1300 / (short)a.f3) == 19)
> + large_code;
> +
> + return 1;
> +}
> +
> +int callee2 (short *p)
> +{
> + if ((*p ^ 1) < 8)
> + large_code;
> +
> + return 2;
> +}
> +
> +int callee3 (int v)
> +{
> + if ((27 % ((1 - (char) v) * 3)) < 6)
> + {
> + large_code;
> + return v + 2;
> + }
> + else
> + return v + 1;
> +}
> +
> +int caller ()
> +{
> + struct A a;
> + short b;
> +
> + a.f2 = -7;
> + a.f3 = 68;
> + if (callee1 (a))
> + foo ();
> +
> + a.f2 = 3;
> + a.f3 = 10;
> + if (callee1 (a))
> + foo ();
> +
> + b = 9;
> + if (callee2 (&b))
> + foo ();
> +
> + b = 2;
> + if (callee2 (&b))
> + foo ();
> +
> + return callee3 (-5) +
> + callee3 (0);
> +}
> +
> +/* { dg-final { scan-ipa-dump-times "Creating a specialized node of callee1"
> 1 "cp" } } */
> +/* { dg-final { scan-ipa-dump-times "Creating a specialized node of callee2"
> 1 "cp" } } */
> +/* { dg-final { scan-ipa-dump-times "Creating a specialized node of callee3"
> 1 "cp" } } */
> +/* { dg-final { scan-ipa-dump "op0\\\[offset: 32],\\(\\(short int\\)
> #\\),\\(\\(int\\) #\\),\\(1300 / #\\) == 19" "cp" } } */
> +/* { dg-final { scan-ipa-dump "op0\\\[ref offset: 0],\\(# \\^ 1\\) <" "cp" }
> } */
> +/* { dg-final { scan-ipa-dump "op0,\\(\\(char\\) #\\),\\(\\(int\\) #\\),\\(1
> - #\\),\\(# \\* 3\\),\\(27 % #\\) <" "cp" } } */
> --
> 2.17.1
>