@@ -1,3 +1,25 @@
+2019-07-12 Feng Xue <fxue@os.amperecomputing.com>
+
+ PR ipa/91088
+ * ipa-predicat.h (struct expr_eval_op): New struct.
+ (expr_eval_ops): New typedef.
+ (struct condition): Add param_ops member.
+ (add_condition): Add param_ops parameter.
+ * ipa-predicat.c (expr_eval_ops_equal_p): New function.
+ (predicate::add_clause): Add param_ops comparison.
+ (dump_condition): Add debug dump for param_ops.
+ (remap_after_inlining): Add param_ops argument to call to
+ add_condition.
+ (add_condition): Add parameter param_ops.
+ * 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.
+ (inline_read_section): Read param_ops from summary stream.
+ (ipa_fn_summary_write): Write param_ops to summary stream.
+
2019-07-11 Sunil K Pandey <sunil.k.pandey@intel.com>
PR target/90980
@@ -301,9 +301,9 @@ set_hint_predicate (predicate **p, predicate new_predicate)
}
-/* Compute what conditions may or may not hold given invormation about
+/* Compute what conditions may or may not hold given information about
parameters. RET_CLAUSE returns truths that may hold in a specialized copy,
- whie RET_NONSPEC_CLAUSE returns truths that may hold in an nonspecialized
+ while RET_NONSPEC_CLAUSE returns truths that may hold in an nonspecialized
copy when called in a given context. It is a bitmask of conditions. Bit
0 means that condition is known to be false, while bit 1 means that condition
may or may not be true. These differs - for example NOT_INLINED condition
@@ -336,6 +336,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
@@ -399,7 +401,18 @@ evaluate_conditions_for_known_args (struct cgraph_node *node,
continue;
}
- val = fold_unary (VIEW_CONVERT_EXPR, TREE_TYPE (c->val), val);
+ for (j = 0; vec_safe_iterate (c->param_ops, j, &op); j++)
+ {
+ if (!op->val)
+ val = fold_unary (op->code, op->type, val);
+ else if (op->val_is_rhs)
+ val = fold_binary_to_constant (op->code, op->type, val, op->val);
+ else
+ val = fold_binary_to_constant (op->code, op->type, op->val, val);
+ if (!val)
+ break;
+ }
+
res = val
? fold_binary_to_constant (c->code, boolean_type_node, val, c->val)
: NULL;
@@ -1177,6 +1190,105 @@ eliminated_by_inlining_prob (ipa_func_body_info *fbi, gimple *stmt)
}
}
+/* Flatten a tree expression on parameter into a set of sequential operations.
+ we only handle expression that is a mathematical transformation on the
+ parameter, and in the expression, parameter occurs only once, and other
+ operands are IPA invariant. */
+
+static bool
+decompose_param_expr (struct ipa_func_body_info *fbi,
+ gimple *stmt, tree expr,
+ int *index_p, HOST_WIDE_INT *size_p,
+ struct agg_position_info *aggpos,
+ expr_eval_ops *param_ops_p)
+{
+ if (param_ops_p)
+ *param_ops_p = NULL;
+
+ while (true)
+ {
+ gimple_rhs_class rhs_class;
+ expr_eval_op eval_op;
+
+ if (unmodified_parm_or_parm_agg_item (fbi, stmt, expr, index_p,
+ size_p, aggpos))
+ {
+ if (param_ops_p)
+ {
+ /* Find use of parameter, add a convert operation to describe
+ result type, which may not be same as parameter type. */
+ eval_op.val_is_rhs = false;
+ eval_op.val = NULL_TREE;
+ eval_op.code = VIEW_CONVERT_EXPR;
+ eval_op.type = TREE_TYPE (expr);
+
+ vec_safe_insert (*param_ops_p, 0, eval_op);
+ }
+ 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;
+
+ rhs_class = gimple_assign_rhs_class (stmt);
+
+ if (rhs_class == GIMPLE_SINGLE_RHS)
+ expr = gimple_assign_rhs1 (stmt);
+ else if (rhs_class == GIMPLE_UNARY_RHS)
+ {
+ expr = gimple_assign_rhs1 (stmt);
+
+ if (param_ops_p)
+ {
+ eval_op.val_is_rhs = false;
+ eval_op.val = NULL_TREE;
+ eval_op.code = gimple_assign_rhs_code (stmt);
+ eval_op.type = TREE_TYPE (expr);
+
+ vec_safe_insert (*param_ops_p, 0, eval_op);
+ }
+ }
+ else if (rhs_class == GIMPLE_BINARY_RHS)
+ {
+ tree op1 = gimple_assign_rhs1 (stmt);
+ tree op2 = gimple_assign_rhs2 (stmt);
+
+ if (is_gimple_ip_invariant (op1))
+ {
+ eval_op.val_is_rhs = false;
+ eval_op.val = op1;
+ expr = op2;
+ }
+ else if (is_gimple_ip_invariant (op2))
+ {
+ eval_op.val_is_rhs = true;
+ eval_op.val = op2;
+ expr = op1;
+ }
+ else
+ break;
+
+ if (param_ops_p)
+ {
+ eval_op.code = gimple_assign_rhs_code (stmt);
+ eval_op.type = TREE_TYPE (expr);
+
+ vec_safe_insert (*param_ops_p, 0, eval_op);
+ }
+ }
+ else
+ break;
+ }
+
+ /* Failed to decompose, free resource and return. */
+ 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. */
@@ -1196,6 +1308,7 @@ set_cond_stmt_execution_predicate (struct ipa_func_body_info *fbi,
edge_iterator ei;
gimple *set_stmt;
tree op2;
+ expr_eval_ops param_ops;
last = last_stmt (bb);
if (!last || gimple_code (last) != GIMPLE_COND)
@@ -1203,10 +1316,8 @@ 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, &size, &aggpos, ¶m_ops))
{
code = gimple_cond_code (last);
inverted_code = invert_tree_comparison (code, HONOR_NANS (op));
@@ -1222,13 +1333,13 @@ set_cond_stmt_execution_predicate (struct ipa_func_body_info *fbi,
{
predicate p
= add_condition (summary, index, size, &aggpos, this_code,
- unshare_expr_without_location
- (gimple_cond_rhs (last)));
+ 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)
return;
@@ -1240,7 +1351,7 @@ set_cond_stmt_execution_predicate (struct ipa_func_body_info *fbi,
Here we can predicate nonconstant_code. We can't
really handle constant_code since we have no predicate
for this and also the constant code is not known to be
- optimized away when inliner doen't see operand is constant.
+ optimized away when inliner doesn't see operand is constant.
Other optimizers might think otherwise. */
if (gimple_cond_code (last) != NE_EXPR
|| !integer_zerop (gimple_cond_rhs (last)))
@@ -1250,8 +1361,7 @@ 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, &size, &aggpos, NULL))
return;
FOR_EACH_EDGE (e, ei, bb->succs) if (e->flags & EDGE_FALSE_VALUE)
{
@@ -1280,13 +1390,15 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi,
edge_iterator ei;
size_t n;
size_t case_idx;
+ 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, &size, &aggpos,
+ ¶m_ops))
return;
FOR_EACH_EDGE (e, ei, bb->succs)
@@ -1312,19 +1424,20 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi,
p = true;
else if (!max)
p = add_condition (summary, index, size, &aggpos, EQ_EXPR,
- unshare_expr_without_location (min));
+ min, param_ops);
else
{
predicate p1, p2;
p1 = add_condition (summary, index, size, &aggpos, GE_EXPR,
- unshare_expr_without_location (min));
+ min, param_ops);
p2 = add_condition (summary, index, size, &aggpos, LE_EXPR,
- unshare_expr_without_location (max));
+ max, param_ops);
p = p1 & p2;
}
*(class predicate *) e->aux
= p.or_with (summary->conds, *(class predicate *) e->aux);
}
+ vec_free (param_ops);
}
@@ -1491,7 +1604,7 @@ will_be_nonconstant_predicate (struct ipa_func_body_info *fbi,
HOST_WIDE_INT size;
struct agg_position_info aggpos;
- /* What statments might be optimized away
+ /* What statements might be optimized away
when their arguments are constant. */
if (gimple_code (stmt) != GIMPLE_ASSIGN
&& gimple_code (stmt) != GIMPLE_COND
@@ -1539,7 +1652,7 @@ 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);
+ NULL_TREE);
else
op_non_const = false;
FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
@@ -3333,6 +3446,7 @@ 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_uhwi (&ib);
c.code = (enum tree_code) streamer_read_uhwi (&ib);
@@ -3342,6 +3456,18 @@ inline_read_section (struct lto_file_decl_data *file_data, const char *data,
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;
+ op.code = (enum tree_code) streamer_read_uhwi (&ib);
+ bp = streamer_read_bitpack (&ib);
+ op.val_is_rhs = bp_unpack_value (&bp, 1);
+ op.val = stream_read_tree (&ib, data_in);
+ op.type = stream_read_tree (&ib, data_in);
+ vec_safe_push (c.param_ops, op);
+ }
if (info)
vec_safe_push (info->conds, c);
}
@@ -3490,6 +3616,9 @@ 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_uhwi (ob, c->size);
streamer_write_uhwi (ob, c->code);
@@ -3500,6 +3629,16 @@ 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);
+ bp = bitpack_create (ob->main_stream);
+ bp_pack_value (&bp, op->val_is_rhs, 1);
+ streamer_write_bitpack (&bp);
+ stream_write_tree (ob, op->val, true);
+ stream_write_tree (ob, op->type, 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++)
@@ -33,9 +33,35 @@ 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.val_is_rhs != op2.val_is_rhs
+ || !vrp_operand_equal_p (op1.val, op2.val)
+ || 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 +136,13 @@ 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)
&& cc1->code == invert_tree_comparison (cc2->code,
HONOR_NANS (cc1->val)))
return;
@@ -300,6 +325,46 @@ 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 = 1; 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)
+ {
+ switch (op.code)
+ {
+ case VIEW_CONVERT_EXPR:
+ CASE_CONVERT:
+ fprintf (f, "(");
+ print_generic_expr (f, op.type);
+ fprintf (f, ")" );
+ break;
+
+ default:
+ fprintf (f, "%s ", op_name);
+ }
+ fprintf (f, "@%u", i);
+ }
+ else if (op.val_is_rhs)
+ {
+ fprintf (f, "@%u %s ", i, op_name);
+ print_generic_expr (f, op.val);
+ }
+ else
+ {
+ print_generic_expr (f, op.val);
+ fprintf (f, " %s @%u", op_name, i);
+ }
+ fprintf (f, ")");
+ }
+
if (c->code == predicate::is_not_constant)
{
fprintf (f, " not constant");
@@ -338,8 +403,8 @@ dump_clause (FILE *f, conditions conds, clause_t clause)
}
-/* Dump THIS to F. CONDS a vector of conditions used when evauating
- predicats. When NL is true new line is output at the end of dump. */
+/* Dump THIS to F. CONDS a vector of conditions used when evaluating
+ predicates. When NL is true new line is output at the end of dump. */
void
predicate::dump (FILE *f, conditions conds, bool nl) const
@@ -389,7 +454,7 @@ predicate::remap_after_duplication (clause_t possible_truths)
INFO is ipa_fn_summary of function we are adding predicate into, CALLEE_INFO
is summary of function predicate P is from. OPERAND_MAP is array giving
- callee formal IDs the caller formal IDs. POSSSIBLE_TRUTHS is clausule of all
+ callee formal IDs the caller formal IDs. POSSSIBLE_TRUTHS is clause of all
callee conditions that may be true in caller context. TOPLEV_PREDICATE is
predicate under which callee is executed. OFFSET_MAP is an array of of
offsets that need to be added to conditions, negative offset means that
@@ -463,7 +528,7 @@ predicate::remap_after_inlining (class ipa_fn_summary *info,
cond_predicate = add_condition (info,
operand_map[c->operand_num],
c->size, &ap, c->code,
- c->val);
+ c->val, c->param_ops);
}
}
/* Fixed conditions remains same, construct single
@@ -516,21 +581,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, SIZE, 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,
HOST_WIDE_INT size, struct agg_position_info *aggpos,
- enum tree_code code, tree val)
+ 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)
{
@@ -551,8 +618,9 @@ add_condition (class ipa_fn_summary *summary, int operand_num,
if (c->operand_num == operand_num
&& c->size == size
&& c->code == code
- && c->val == val
+ && 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 +630,17 @@ 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.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)
+ op->val = unshare_expr_without_location (op->val);
+
vec_safe_push (summary->conds, new_cond);
return predicate::predicate_testing_cond (i);
@@ -22,8 +22,28 @@ 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, only for unary and binary
+ operation. A tree expression is flattened to a vector of this kind
+ of structure. */
+struct GTY(()) expr_eval_op
+{
+ /* Operation code of expression. */
+ ENUM_BITFIELD(tree_code) code : 16;
+ /* For binary expression, the below constant operand is rhs or lhs. */
+ unsigned val_is_rhs : 1;
+ /* For binary expression, this is a constant value. */
+ tree val;
+ /* Result type of expression. */
+ tree type;
+};
+
+typedef vec<expr_eval_op, va_gc> *expr_eval_ops;
struct GTY(()) condition
{
@@ -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 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. */
@@ -58,7 +81,7 @@ struct inline_param_summary
typedef vec<condition, va_gc> *conditions;
-/* Predicates are used to repesent function parameters (such as runtime)
+/* Predicates are used to represent function parameters (such as runtime)
which depend on a context function is called in.
Predicates are logical formulas in conjunctive-disjunctive form consisting
@@ -229,4 +252,5 @@ private:
void dump_condition (FILE *f, conditions conditions, int cond);
predicate add_condition (class ipa_fn_summary *summary, int operand_num,
HOST_WIDE_INT size, struct agg_position_info *aggpos,
- enum tree_code code, tree val);
+ enum tree_code code, tree val,
+ expr_eval_ops param_ops = NULL);
@@ -1,3 +1,8 @@
+2019-07-12 Feng Xue <fxue@os.amperecomputing.com>
+
+ PR ipa/91088
+ * gcc.dg/ipa/pr91088.c: New test.
+
2019-07-11 Sunil K Pandey <sunil.k.pandey@intel.com>
PR target/90980
new file mode 100644
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-cp-details -fdump-ipa-fnsummary-details -fno-inline" } */
+
+int foo();
+
+int callee(int v)
+{
+ if ((27 % ((1 - v) * 3)) < 6)
+ {
+ foo();
+ foo();
+ foo();
+ foo();
+ foo();
+ foo();
+ foo();
+ foo();
+ foo();
+ }
+ else
+ return 1;
+}
+
+int caller()
+{
+ return callee (-5);
+}
+
+/* { dg-final { scan-ipa-dump "Creating a specialized node of callee" "cp" } } */