From: Feng Xue Date: Wed, 16 Oct 2019 07:27:50 +0000 (+0000) Subject: Generalized IPA predicate on parameter reference X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=4307a485c39fd1c317d6cead2707a903052c4753;p=gcc.git Generalized IPA predicate on parameter reference 2019-10-16 Feng Xue PR ipa/91088 * doc/invoke.texi (ipa-max-param-expr-ops): Document new option. * params.def (PARAM_IPA_MAX_PARAM_EXPR_OPS): New. * ipa-predicat.h (struct expr_eval_op): New struct. (expr_eval_ops): New typedef. (struct condition): Add type and param_ops fields, remove size field. (add_condition): Replace size parameter with type parameter, add param_ops parameter. * ipa-predicat.c (expr_eval_ops_equal_p): New function. (predicate::add_clause): Add comparisons on type and param_ops. (dump_condition): Add debug dump for param_ops. (remap_after_inlining): Adjust call arguments to add_condition. (add_condition): Replace size parameter with type parameter, add param_ops parameter. Unshare constant value used in conditions. * ipa-fnsummary.c (evaluate_conditions_for_known_args): Fold parameter expressions using param_ops. (decompose_param_expr): New function. (set_cond_stmt_execution_predicate): Use call to decompose_param_expr to replace call to unmodified_parm_or_parm_agg_item. (set_switch_stmt_execution_predicate): Likewise. (will_be_nonconstant_expr_predicate): Likewise. Replace usage of size with type. (inline_read_section): Read param_ops from summary stream. (ipa_fn_summary_write): Write param_ops to summary stream. 2019-10-16 Feng Xue PR ipa/91088 * gcc.dg/ipa/pr91088.c: New test. * gcc.dg/ipa/pr91089.c: Add sub-test for range analysis. * g++.dg/tree-ssa/ivopts-3.C: Force a function to be noinline. From-SVN: r277054 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 175ecd9224f..28629582a02 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,30 @@ +2019-10-16 Feng Xue + + PR ipa/91088 + * doc/invoke.texi (ipa-max-param-expr-ops): Document new option. + * params.def (PARAM_IPA_MAX_PARAM_EXPR_OPS): New. + * ipa-predicat.h (struct expr_eval_op): New struct. + (expr_eval_ops): New typedef. + (struct condition): Add type and param_ops fields, remove size field. + (add_condition): Replace size parameter with type parameter, add + param_ops parameter. + * ipa-predicat.c (expr_eval_ops_equal_p): New function. + (predicate::add_clause): Add comparisons on type and param_ops. + (dump_condition): Add debug dump for param_ops. + (remap_after_inlining): Adjust call arguments to add_condition. + (add_condition): Replace size parameter with type parameter, add + param_ops parameter. Unshare constant value used in conditions. + * ipa-fnsummary.c (evaluate_conditions_for_known_args): Fold + parameter expressions using param_ops. + (decompose_param_expr): New function. + (set_cond_stmt_execution_predicate): Use call to decompose_param_expr + to replace call to unmodified_parm_or_parm_agg_item. + (set_switch_stmt_execution_predicate): Likewise. + (will_be_nonconstant_expr_predicate): Likewise. Replace usage of size + with type. + (inline_read_section): Read param_ops from summary stream. + (ipa_fn_summary_write): Write param_ops to summary stream. + 2019-10-15 Segher Boessenkool PR rtl-optimization/92107 diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 47769262b8e..1407d019d14 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -12034,6 +12034,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 a43867937bd..40a9e0f3ee3 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; @@ -1155,6 +1180,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. */ @@ -1165,15 +1311,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) @@ -1181,10 +1327,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)); @@ -1205,13 +1350,13 @@ set_cond_stmt_execution_predicate (struct ipa_func_body_info *fbi, && !dominated_by_p (CDI_POST_DOMINATORS, bb, e->dest)) { 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) @@ -1234,12 +1379,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; @@ -1258,19 +1402,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 (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 > ranges; @@ -1302,6 +1448,8 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi, tree max = CASE_HIGH (cl); predicate p; + e = gimple_switch_edge (cfun, last, case_idx); + /* The case value might not have same type as switch expression, extend the value based on the expression type. */ if (TREE_TYPE (min) != type) @@ -1318,18 +1466,17 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi, if (dominated_by_p (CDI_POST_DOMINATORS, bb, e->dest)) p = true; else if (min == 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; } - e = gimple_switch_edge (cfun, last, case_idx); *(class predicate *) e->aux = p.or_with (summary->conds, *(class predicate *) e->aux); @@ -1378,6 +1525,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; } @@ -1407,16 +1555,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); } @@ -1428,14 +1576,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); } @@ -1552,15 +1702,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) @@ -1624,10 +1773,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 @@ -1648,11 +1797,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 @@ -1677,21 +1824,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; } @@ -1823,7 +1969,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"); } @@ -3452,15 +3598,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); } @@ -3606,9 +3786,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); @@ -3616,6 +3799,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_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 e4b70bac22a..322c37f8b96 100644 --- a/gcc/params.def +++ b/gcc/params.def @@ -1165,6 +1165,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/ChangeLog b/gcc/testsuite/ChangeLog index 6dc4f002769..b2b92deb134 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2019-10-16 Feng Xue + + PR ipa/91088 + * gcc.dg/ipa/pr91088.c: New test. + * gcc.dg/ipa/pr91089.c: Add sub-test for range analysis. + * g++.dg/tree-ssa/ivopts-3.C: Force a function to be noinline. + 2019-10-15 Andrew Pinski * gcc.c-torture/compile/20191015-1.c: New test. diff --git a/gcc/testsuite/g++.dg/tree-ssa/ivopts-3.C b/gcc/testsuite/g++.dg/tree-ssa/ivopts-3.C index 6760a5b1851..b0da5e68abc 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" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/pr91089.c b/gcc/testsuite/gcc.dg/ipa/pr91089.c index 7509c622263..a54cbdf5390 100644 --- a/gcc/testsuite/gcc.dg/ipa/pr91089.c +++ b/gcc/testsuite/gcc.dg/ipa/pr91089.c @@ -7,7 +7,7 @@ int data; int callee (int i) { - switch (i) + switch (i % 128) { case -126: return i + 13; case -127: return i + 5; @@ -45,7 +45,7 @@ int fn2 (); int callee_complex_predicate (int i) { - switch (i ) + switch (i) { case 0: fn (); @@ -100,10 +100,10 @@ int caller () } /* { dg-final { scan-ipa-dump-times "Creating a specialized node of callee" 7 "cp" } } */ -/* { dg-final { scan-ipa-dump "op0 < -127" "fnsummary" } } */ -/* { dg-final { scan-ipa-dump "op0 > -126" "fnsummary" } } */ -/* { dg-final { scan-ipa-dump "op0 != -8" "fnsummary" } } */ -/* { dg-final { scan-ipa-dump "op0 != 0" "fnsummary" } } */ -/* { dg-final { scan-ipa-dump "op0 < 5" "fnsummary" } } */ -/* { dg-final { scan-ipa-dump "op0 > 7" "fnsummary" } } */ +/* { dg-final { scan-ipa-dump-not "op0,\\(# % 128\\) < -127" "fnsummary" } } */ +/* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) > -126" "fnsummary" } } */ +/* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) != -8" "fnsummary" } } */ +/* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) != 0" "fnsummary" } } */ +/* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) < 5" "fnsummary" } } */ +/* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) > 7" "fnsummary" } } */ /* { dg-final { scan-ipa-dump "loop depth: 1 .+ time:\[ \]*\[0-9\]+ predicate: \\(op0 == 1000\\)\[\r\n]+" "fnsummary" } } */