aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog27
-rw-r--r--gcc/doc/invoke.texi7
-rw-r--r--gcc/ipa-fnsummary.c296
-rw-r--r--gcc/ipa-predicate.c153
-rw-r--r--gcc/ipa-predicate.h36
-rw-r--r--gcc/params.def6
-rw-r--r--gcc/testsuite/ChangeLog7
-rw-r--r--gcc/testsuite/g++.dg/tree-ssa/ivopts-3.C2
-rw-r--r--gcc/testsuite/gcc.dg/ipa/pr91088.c120
-rw-r--r--gcc/testsuite/gcc.dg/ipa/pr91089.c16
10 files changed, 589 insertions, 81 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 175ecd9..2862958 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,30 @@
+2019-10-16 Feng Xue <fxue@os.amperecomputing.com>
+
+ 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 <segher@kernel.crashing.org>
PR rtl-optimization/92107
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 4776926..1407d01 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 a438679..40a9e0f 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, &param_type, &aggpos,
+ &param_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, &param_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 <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, &param_type, &aggpos,
+ &param_ops))
return;
auto_vec<std::pair<tree, tree> > 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, &param_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 8a9851a..b5e3cf4 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 237306d..4121218 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 e4b70ba..322c37f 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 6dc4f00..b2b92de 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,10 @@
+2019-10-16 Feng Xue <fxue@os.amperecomputing.com>
+
+ 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 <apinski@marvell.com>
* 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 6760a5b..b0da5e6 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 0000000..a81c59f
--- /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 7509c62..a54cbdf 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" } } */