> >> + 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 >