public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088)
@ 2019-07-12 11:34 Feng Xue OS
  2019-08-29 17:02 ` Martin Jambor
  0 siblings, 1 reply; 11+ messages in thread
From: Feng Xue OS @ 2019-07-12 11:34 UTC (permalink / raw)
  To: gcc-patches, Jan Hubicka, Martin Jambor

Current IPA-cp only generates cost-evaluating predicate for conditional statement like
"if (param cmp const_val)", it is too simple and conservative. This patch generalizes the
process to handle the form as T(param), a mathematical transformation on the function
parameter, in which the parameter occurs once, and other operands are constant value.

Feng

----
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 3d92250b520..0110446e09e 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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
diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
index 09986211a1d..faf8bd39090 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -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, &param_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,
+			     &param_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++)
diff --git a/gcc/ipa-predicate.c b/gcc/ipa-predicate.c
index 49622e9cd33..537dd81685f 100644
--- a/gcc/ipa-predicate.c
+++ b/gcc/ipa-predicate.c
@@ -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);
diff --git a/gcc/ipa-predicate.h b/gcc/ipa-predicate.h
index c2adba30551..f00a64f3a59 100644
--- a/gcc/ipa-predicate.h
+++ b/gcc/ipa-predicate.h
@@ -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);
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 6c40496db9c..39886498305 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
diff --git a/gcc/testsuite/gcc.dg/ipa/pr91088.c b/gcc/testsuite/gcc.dg/ipa/pr91088.c
new file mode 100644
index 00000000000..1cbf0d2fa23
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/pr91088.c
@@ -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" } } */
-- 
2.17.1

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088)
  2019-07-12 11:34 [PATCH] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088) Feng Xue OS
@ 2019-08-29 17:02 ` Martin Jambor
  2019-08-30  8:42   ` Feng Xue OS
  2019-09-04 10:08   ` [PATCH V2] " Feng Xue OS
  0 siblings, 2 replies; 11+ messages in thread
From: Martin Jambor @ 2019-08-29 17:02 UTC (permalink / raw)
  To: Feng Xue OS, gcc-patches, Jan Hubicka

Hi,

On Fri, Jul 12 2019, Feng Xue OS wrote:
> Current IPA-cp only generates cost-evaluating predicate for conditional statement like
> "if (param cmp const_val)", it is too simple and conservative. This patch generalizes the
> process to handle the form as T(param), a mathematical transformation on the function
> parameter, in which the parameter occurs once, and other operands are constant value.

thanks for working on this.  I cannot approve this, but I have had a
brief look and have the following comments:
>
> ----
> diff --git a/gcc/ChangeLog b/gcc/ChangeLog
> index 3d92250b520..0110446e09e 100644
> --- a/gcc/ChangeLog
> +++ b/gcc/ChangeLog
> @@ -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.
> +

(It's a bad idea to make ChangeLog entries part of the patch, it won't
apply to anyone, not even to you nowadays. )


>  2019-07-11  Sunil K Pandey  <sunil.k.pandey@intel.com>
>  
>  	PR target/90980
> diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
> index 09986211a1d..faf8bd39090 100644
> --- a/gcc/ipa-fnsummary.c
> +++ b/gcc/ipa-fnsummary.c
> @@ -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.  */

I understand describing these things is difficult, but flatten is
strange way to describe what the function does.  What about somthing
like the following?

Analyze EXPR if it represents a series of simple operations performed on
a function parameter and return true if so.  FBI, STMT, INDEX_P, SIZE_P
and AGGPOS have the same meaning like in
unmodified_parm_or_parm_agg_item.  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, 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);

If we get here in the first iteration of the loop, could we not insert
anything into the vector and handle such cases in
evaluate_conditions_for_known_args like we do today (well, with
fold_convert might be better)?  It could save quite some memory and it
is important to try keep the memory footprint down in IPA summaries.

Also, I think you want a parameter to limit the maximum length of
param_ops_p, at some point someone will come with some crazy
machine-generated code that will create huge vectors.

Thanks,

Martin

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088)
  2019-08-29 17:02 ` Martin Jambor
@ 2019-08-30  8:42   ` Feng Xue OS
  2019-08-30  9:00     ` Martin Jambor
  2019-09-04 10:08   ` [PATCH V2] " Feng Xue OS
  1 sibling, 1 reply; 11+ messages in thread
From: Feng Xue OS @ 2019-08-30  8:42 UTC (permalink / raw)
  To: Martin Jambor, gcc-patches, Jan Hubicka

> (It's a bad idea to make ChangeLog entries part of the patch, it won't
> apply to anyone, not even to you nowadays. )
Got it. Will not include this kind of info in later patches.

> I understand describing these things is difficult, but flatten is
> strange way to describe what the function does.  What about somthing
> like the following?
> 
> Analyze EXPR if it represents a series of simple operations performed on
> a function parameter and return true if so.  FBI, STMT, INDEX_P, SIZE_P
> and AGGPOS have the same meaning like in
> unmodified_parm_or_parm_agg_item.  Operations on the parameter are
> recorded to PARAM_OPS_P if it is not NULL.
Operations should be recorded in some place, and this is why PARAM_OPS_P
is used. Not quite understand this point.

>> +           /* 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);

> If we get here in the first iteration of the loop, could we not insert
> anything into the vector and handle such cases in
> evaluate_conditions_for_known_args like we do today (well, with
> fold_convert might be better)?  It could save quite some memory and it
> is important to try keep the memory footprint down in IPA summaries.
Here is a little trick to make code of folding in evaluate_conditions_for_known_args ()
be simple. It does consume some memory for most cases. Will consider other way
and remove this.

> Also, I think you want a parameter to limit the maximum length of
> param_ops_p, at some point someone will come with some crazy
> machine-generated code that will create huge vectors.
Yes. Exactly.

Thanks,

Martin

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088)
  2019-08-30  8:42   ` Feng Xue OS
@ 2019-08-30  9:00     ` Martin Jambor
  2019-08-30  9:02       ` Feng Xue OS
  0 siblings, 1 reply; 11+ messages in thread
From: Martin Jambor @ 2019-08-30  9:00 UTC (permalink / raw)
  To: Feng Xue OS, gcc-patches, Jan Hubicka

Hi,

On Fri, Aug 30 2019, Feng Xue OS wrote:
>> (It's a bad idea to make ChangeLog entries part of the patch, it won't
>> apply to anyone, not even to you nowadays. )
> Got it. Will not include this kind of info in later patches.
>
>> I understand describing these things is difficult, but flatten is
>> strange way to describe what the function does.  What about somthing
>> like the following?
>> 
>> Analyze EXPR if it represents a series of simple operations performed on
>> a function parameter and return true if so.  FBI, STMT, INDEX_P, SIZE_P
>> and AGGPOS have the same meaning like in
>> unmodified_parm_or_parm_agg_item.  Operations on the parameter are
>> recorded to PARAM_OPS_P if it is not NULL.
> Operations should be recorded in some place, and this is why PARAM_OPS_P
> is used. Not quite understand this point.

I was merely suggesting a better comment describing the function you are
introducing.

>
>>> +           /* 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);
>
>> If we get here in the first iteration of the loop, could we not insert
>> anything into the vector and handle such cases in
>> evaluate_conditions_for_known_args like we do today (well, with
>> fold_convert might be better)?  It could save quite some memory and it
>> is important to try keep the memory footprint down in IPA summaries.
> Here is a little trick to make code of folding in evaluate_conditions_for_known_args ()
> be simple. It does consume some memory for most cases. Will consider other way
> and remove this.

Thinking about it a bit more, I think you simply do not want to ever
push the extra VIEW_CONVERT_EXPR to the vector and in
evaluate_conditions_for_known_args always do a fold_convert to the
desired type (similarly like we do today).

Thanks,

Martin

>
>> Also, I think you want a parameter to limit the maximum length of
>> param_ops_p, at some point someone will come with some crazy
>> machine-generated code that will create huge vectors.
> Yes. Exactly.
>
> Thanks,
>
> Martin

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088)
  2019-08-30  9:00     ` Martin Jambor
@ 2019-08-30  9:02       ` Feng Xue OS
  0 siblings, 0 replies; 11+ messages in thread
From: Feng Xue OS @ 2019-08-30  9:02 UTC (permalink / raw)
  To: Martin Jambor, gcc-patches, Jan Hubicka

> I was merely suggesting a better comment describing the function you are
> introducing.
Oh. I know.  Good wording. 

> Thinking about it a bit more, I think you simply do not want to ever
> push the extra VIEW_CONVERT_EXPR to the vector and in
> evaluate_conditions_for_known_args always do a fold_convert to the
> desired type (similarly like we do today).
Yes. It is, but at cost of creating an extra memory slot.

Feng

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH V2] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088)
  2019-08-29 17:02 ` Martin Jambor
  2019-08-30  8:42   ` Feng Xue OS
@ 2019-09-04 10:08   ` Feng Xue OS
  2019-09-04 10:20     ` [PATCH V3] " Feng Xue OS
  1 sibling, 1 reply; 11+ messages in thread
From: Feng Xue OS @ 2019-09-04 10:08 UTC (permalink / raw)
  To: Martin Jambor, gcc-patches, Jan Hubicka

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

> If we get here in the first iteration of the loop, could we not insert
> anything into the vector and handle such cases in
> evaluate_conditions_for_known_args like we do today (well, with
> fold_convert might be better)?  It could save quite some memory and it
> is important to try keep the memory footprint down in IPA summaries.

This tricky element was removed, instead, TREE_TYPE of EXPR is stored
in CONDITION struct.

> Also, I think you want a parameter to limit the maximum length of
> param_ops_p, at some point someone will come with some crazy
> machine-generated code that will create huge vectors.
Added.

Feng
---
diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
index 278bf606661..65a6befbcd2 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -296,9 +296,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
@@ -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,19 @@ 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)
+	    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);
+	}
+
       res = val
 	? fold_binary_to_constant (c->code, boolean_type_node, val, c->val)
 	: NULL;
@@ -1157,6 +1171,110 @@ 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;
+      poly_int64 size;
+
+      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;
+
+	  *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:
+	    expr = gimple_assign_rhs1 (stmt);
+
+	    eval_op.val_is_rhs = false;
+	    eval_op.val = NULL_TREE;
+	    break;
+
+	  case 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;
+		  break;
+		}
+
+	      if (is_gimple_ip_invariant (op2))
+		{
+		  eval_op.val_is_rhs = true;
+		  eval_op.val = op2;
+		  expr = op1;
+		  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));
+
+	  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 +1285,15 @@ set_cond_stmt_execution_predicate (struct ipa_func_body_info *fbi,
 				   basic_block bb)
 {
   gimple *last;
-  tree op;
+  tree op, op2;
+  tree type;
   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;
+  expr_eval_ops param_ops;
 
   last = last_stmt (bb);
   if (!last || gimple_code (last) != GIMPLE_COND)
@@ -1183,10 +1301,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, &type, &aggpos, &param_ops))
     {
       code = gimple_cond_code (last);
       inverted_code = invert_tree_comparison (code, HONOR_NANS (op));
@@ -1201,14 +1317,14 @@ 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, 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)
     return;
@@ -1220,7 +1336,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)))
@@ -1230,12 +1346,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, &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, type, &aggpos,
 				   predicate::is_not_constant, NULL_TREE);
       e->aux = edge_predicate_pool.allocate ();
       *(predicate *) e->aux = p;
@@ -1253,20 +1368,22 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi,
 {
   gimple *lastg;
   tree op;
+  tree type;
   int index;
-  poly_int64 size;
   struct agg_position_info aggpos;
   edge e;
   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, &type, &aggpos,
+			     &param_ops))
     return;
 
   FOR_EACH_EDGE (e, ei, bb->succs)
@@ -1291,15 +1408,15 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi,
       if (!min && !max)
 	p = true;
       else if (!max)
-	p = add_condition (summary, index, size, &aggpos, EQ_EXPR,
-			   unshare_expr_without_location (min));
+	p = add_condition (summary, index, 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, type, &aggpos, GE_EXPR,
+			      min, param_ops);
+	  p2 = add_condition (summary, index, type, &aggpos, LE_EXPR,
+			      max, param_ops);
 	  p = p1 & p2;
 	}
       *(class predicate *) e->aux
@@ -1393,15 +1510,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)
@@ -1465,13 +1581,13 @@ will_be_nonconstant_predicate (struct ipa_func_body_info *fbi,
   predicate p = true;
   ssa_op_iter iter;
   tree use;
+  tree 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
+  /* What statements might be optimized away
      when their arguments are constant.  */
   if (gimple_code (stmt) != GIMPLE_ASSIGN
       && gimple_code (stmt) != GIMPLE_COND
@@ -1489,11 +1605,8 @@ 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, &type, &aggpos))
 	return p;
     }
   else
@@ -1518,21 +1631,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, 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;
 	}
@@ -1664,7 +1776,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");
 	}
@@ -2154,7 +2266,7 @@ analyze_function_body (struct cgraph_node *node, bool early)
 		}
 	    }
 
-	  /* TODO: When conditional jump or swithc is known to be constant, but
+	  /* TODO: When conditional jump or switch is known to be constant, but
 	     we did not translate it into the predicates, we really can account
 	     just maximum of the possible paths.  */
 	  if (fbi.info)
@@ -2635,7 +2747,7 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size,
    POSSIBLE_TRUTHS clause, and KNOWN_VALS, KNOWN_AGGS and KNOWN_CONTEXTS
    information about NODE's arguments.  If non-NULL use also probability
    information present in INLINE_PARAM_SUMMARY vector.
-   Additionally detemine hints determined by the context.  Finally compute
+   Additionally determine hints determined by the context.  Finally compute
    minimal size needed for the call that is independent on the call context and
    can be used for fast estimates.  Return the values in RET_SIZE,
    RET_MIN_SIZE, RET_TIME and RET_HINTS.  */
@@ -3291,15 +3403,36 @@ 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;
+	      op.code = (enum tree_code) streamer_read_uhwi (&ib);
+	      op.type = stream_read_tree (&ib, data_in);
+	      if (TREE_CODE_CLASS (op.code) == tcc_binary)
+	        {
+		  bp = streamer_read_bitpack (&ib);
+		  op.val_is_rhs = bp_unpack_value (&bp, 1);
+		  op.val = stream_read_tree (&ib, data_in);
+		}
+	      else
+	        {
+		  op.val_is_rhs = false;
+		  op.val = NULL_TREE;
+		}
+	      vec_safe_push (c.param_ops, op);
+	    }
 	  if (info)
 	    vec_safe_push (info->conds, c);
 	}
@@ -3445,9 +3578,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);
@@ -3455,6 +3591,19 @@ 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 (TREE_CODE_CLASS (op->code) == tcc_binary)
+		    {
+		      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);
+		    }
+		}
 	    }
 	  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..578d2ae306e 100644
--- a/gcc/ipa-predicate.c
+++ b/gcc/ipa-predicate.c
@@ -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)
+	      || !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 +136,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 +328,51 @@ 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)
+	    {
+	      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_is_rhs)
+	    {
+	      fprintf (f, "# %s ", op_name);
+	      print_generic_expr (f, op.val);
+	    }
+	  else
+	    {
+	      print_generic_expr (f, op.val);
+	      fprintf (f, " %s #", op_name);
+	    }
+	  fprintf (f, ")");
+	}
+
       if (c->code == predicate::is_not_constant)
 	{
 	  fprintf (f, " not constant");
@@ -338,8 +411,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 +462,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
@@ -462,8 +535,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 +589,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 +624,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 +638,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.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)
+      op->val = unshare_expr_without_location (op->val);
+
   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..c533700910d 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, only for unary and binary
+   operation.  A tree expression is flattened to a vector of this kind
+   of structure.  */
+struct GTY(()) expr_eval_op
+{
+  /* Result type of expression.  */
+  tree type;
+  /* For binary expression, this is a constant value.  */
+  tree val;
+  /* For binary expression, the below constant operand is rhs or lhs.  */
+  unsigned val_is_rhs : 1;
+  /* 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.  */
@@ -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
@@ -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 13001a7bb2d..2fe23b7d83e 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1123,6 +1123,12 @@ DEFPARAM (PARAM_IPA_MAX_AA_STEPS,
 	  "parameter analysis based on alias analysis in any given function.",
 	  25000, 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 bd9fe70d084..969acd1a2e1 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2019-09-28  Feng Xue  <fxue@os.amperecomputing.com>
+	PR ipa/91088
+	* gcc.dg/ipa/pr91088.c: New test.
+
 2019-08-28  Steven G. Kargl  <kargl@gcc.gnu.org>
 
 	PR fortran/91551
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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH V3] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088)
  2019-09-04 10:08   ` [PATCH V2] " Feng Xue OS
@ 2019-09-04 10:20     ` Feng Xue OS
  2019-09-14 17:18       ` Jan Hubicka
  0 siblings, 1 reply; 11+ messages in thread
From: Feng Xue OS @ 2019-09-04 10:20 UTC (permalink / raw)
  To: Martin Jambor, gcc-patches, Jan Hubicka

Some minor changes.

Feng
----
diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
index 278bf606661..89a6b8bf061 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -296,9 +296,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
@@ -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,19 @@ 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)
+	    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);
+	}
+
       res = val
 	? fold_binary_to_constant (c->code, boolean_type_node, val, c->val)
 	: NULL;
@@ -1157,6 +1171,110 @@ 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;
+      poly_int64 size;
+
+      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;
+
+	  *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:
+	    expr = gimple_assign_rhs1 (stmt);
+
+	    eval_op.val_is_rhs = false;
+	    eval_op.val = NULL_TREE;
+	    break;
+
+	  case 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;
+		  break;
+		}
+
+	      if (is_gimple_ip_invariant (op2))
+		{
+		  eval_op.val_is_rhs = true;
+		  eval_op.val = op2;
+		  expr = op1;
+		  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));
+
+	  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 +1285,15 @@ set_cond_stmt_execution_predicate (struct ipa_func_body_info *fbi,
 				   basic_block bb)
 {
   gimple *last;
-  tree op;
+  tree op, op2;
+  tree type;
   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;
+  expr_eval_ops param_ops;
 
   last = last_stmt (bb);
   if (!last || gimple_code (last) != GIMPLE_COND)
@@ -1183,10 +1301,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, &type, &aggpos, &param_ops))
     {
       code = gimple_cond_code (last);
       inverted_code = invert_tree_comparison (code, HONOR_NANS (op));
@@ -1201,13 +1317,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, 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)
@@ -1220,7 +1336,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)))
@@ -1230,12 +1346,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, &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, type, &aggpos,
 				   predicate::is_not_constant, NULL_TREE);
       e->aux = edge_predicate_pool.allocate ();
       *(predicate *) e->aux = p;
@@ -1253,20 +1368,22 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi,
 {
   gimple *lastg;
   tree op;
+  tree type;
   int index;
-  poly_int64 size;
   struct agg_position_info aggpos;
   edge e;
   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, &type, &aggpos,
+			     &param_ops))
     return;
 
   FOR_EACH_EDGE (e, ei, bb->succs)
@@ -1291,20 +1408,21 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi,
       if (!min && !max)
 	p = true;
       else if (!max)
-	p = add_condition (summary, index, size, &aggpos, EQ_EXPR,
-			   unshare_expr_without_location (min));
+	p = add_condition (summary, index, 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, type, &aggpos, GE_EXPR,
+			      min, param_ops);
+	  p2 = add_condition (summary, index, type, &aggpos, LE_EXPR,
+			      max, param_ops);
 	  p = p1 & p2;
 	}
       *(class predicate *) e->aux
 	= p.or_with (summary->conds, *(class predicate *) e->aux);
     }
+  vec_free (param_ops);
 }
 
 
@@ -1393,15 +1511,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)
@@ -1465,13 +1582,13 @@ will_be_nonconstant_predicate (struct ipa_func_body_info *fbi,
   predicate p = true;
   ssa_op_iter iter;
   tree use;
+  tree 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
+  /* What statements might be optimized away
      when their arguments are constant.  */
   if (gimple_code (stmt) != GIMPLE_ASSIGN
       && gimple_code (stmt) != GIMPLE_COND
@@ -1489,11 +1606,8 @@ 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, &type, &aggpos))
 	return p;
     }
   else
@@ -1518,21 +1632,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, 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;
 	}
@@ -1664,7 +1777,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");
 	}
@@ -2154,7 +2267,7 @@ analyze_function_body (struct cgraph_node *node, bool early)
 		}
 	    }
 
-	  /* TODO: When conditional jump or swithc is known to be constant, but
+	  /* TODO: When conditional jump or switch is known to be constant, but
 	     we did not translate it into the predicates, we really can account
 	     just maximum of the possible paths.  */
 	  if (fbi.info)
@@ -2635,7 +2748,7 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size,
    POSSIBLE_TRUTHS clause, and KNOWN_VALS, KNOWN_AGGS and KNOWN_CONTEXTS
    information about NODE's arguments.  If non-NULL use also probability
    information present in INLINE_PARAM_SUMMARY vector.
-   Additionally detemine hints determined by the context.  Finally compute
+   Additionally determine hints determined by the context.  Finally compute
    minimal size needed for the call that is independent on the call context and
    can be used for fast estimates.  Return the values in RET_SIZE,
    RET_MIN_SIZE, RET_TIME and RET_HINTS.  */
@@ -3291,15 +3404,36 @@ 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;
+	      op.code = (enum tree_code) streamer_read_uhwi (&ib);
+	      op.type = stream_read_tree (&ib, data_in);
+	      if (TREE_CODE_CLASS (op.code) == tcc_binary)
+	        {
+		  bp = streamer_read_bitpack (&ib);
+		  op.val_is_rhs = bp_unpack_value (&bp, 1);
+		  op.val = stream_read_tree (&ib, data_in);
+		}
+	      else
+	        {
+		  op.val_is_rhs = false;
+		  op.val = NULL_TREE;
+		}
+	      vec_safe_push (c.param_ops, op);
+	    }
 	  if (info)
 	    vec_safe_push (info->conds, c);
 	}
@@ -3445,9 +3579,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);
@@ -3455,6 +3592,19 @@ 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 (TREE_CODE_CLASS (op->code) == tcc_binary)
+		    {
+		      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);
+		    }
+		}
 	    }
 	  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..578d2ae306e 100644
--- a/gcc/ipa-predicate.c
+++ b/gcc/ipa-predicate.c
@@ -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)
+	      || !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 +136,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 +328,51 @@ 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)
+	    {
+	      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_is_rhs)
+	    {
+	      fprintf (f, "# %s ", op_name);
+	      print_generic_expr (f, op.val);
+	    }
+	  else
+	    {
+	      print_generic_expr (f, op.val);
+	      fprintf (f, " %s #", op_name);
+	    }
+	  fprintf (f, ")");
+	}
+
       if (c->code == predicate::is_not_constant)
 	{
 	  fprintf (f, " not constant");
@@ -338,8 +411,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 +462,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
@@ -462,8 +535,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 +589,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 +624,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 +638,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.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)
+      op->val = unshare_expr_without_location (op->val);
+
   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..c533700910d 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, only for unary and binary
+   operation.  A tree expression is flattened to a vector of this kind
+   of structure.  */
+struct GTY(()) expr_eval_op
+{
+  /* Result type of expression.  */
+  tree type;
+  /* For binary expression, this is a constant value.  */
+  tree val;
+  /* For binary expression, the below constant operand is rhs or lhs.  */
+  unsigned val_is_rhs : 1;
+  /* 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.  */
@@ -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
@@ -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 13001a7bb2d..2fe23b7d83e 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1123,6 +1123,12 @@ DEFPARAM (PARAM_IPA_MAX_AA_STEPS,
 	  "parameter analysis based on alias analysis in any given function.",
 	  25000, 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 bd9fe70d084..969acd1a2e1 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2019-09-28  Feng Xue  <fxue@os.amperecomputing.com>
+	PR ipa/91088
+	* gcc.dg/ipa/pr91088.c: New test.
+
 2019-08-28  Steven G. Kargl  <kargl@gcc.gnu.org>
 
 	PR fortran/91551
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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH V3] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088)
  2019-09-04 10:20     ` [PATCH V3] " Feng Xue OS
@ 2019-09-14 17:18       ` Jan Hubicka
  2019-09-18 12:41         ` [PATCH V4] " Feng Xue OS
  0 siblings, 1 reply; 11+ messages in thread
From: Jan Hubicka @ 2019-09-14 17:18 UTC (permalink / raw)
  To: Feng Xue OS; +Cc: Martin Jambor, gcc-patches

> +/* 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;
> +      poly_int64 size;
> +
> +      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?
> +
> +	  *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:
> +	    expr = gimple_assign_rhs1 (stmt);
> +
> +	    eval_op.val_is_rhs = false;

I find val_is_rhs to be confusing, lhs/rhs is usually used for the
gimple assignments.  Here everything is rhs, but it depends whether
the val is operand0 or operand1.  So I guess we could do val_is_op1.
> @@ -1183,10 +1301,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, &type, &aggpos, &param_ops))
>      {
>        code = gimple_cond_code (last);
>        inverted_code = invert_tree_comparison (code, HONOR_NANS (op));
> @@ -1201,13 +1317,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, 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.
> +/* 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)

Why you need vrp_operand_equal_p instead of operand_equal_p here?

Overall the patch looks very reasonable. We may end up with bit more
general expressions (i.e. supporting arithmetics involving more than one
operand).

I see you also fixed a lot of typos in comments, please go head and
commit them separately as obvious.

Thank for all the work!
Honza

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH V4] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088)
  2019-09-14 17:18       ` Jan Hubicka
@ 2019-09-18 12:41         ` Feng Xue OS
  2019-09-30  8:51           ` Ping: " Feng Xue OS
  2019-10-15 17:04           ` Jan Hubicka
  0 siblings, 2 replies; 11+ messages in thread
From: Feng Xue OS @ 2019-09-18 12:41 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Martin Jambor, gcc-patches

>> +      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.
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, &param_type, &aggpos,
+			    &param_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, &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;
@@ -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, &param_type, &aggpos,
+			     &param_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, &param_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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Ping: [PATCH V4] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088)
  2019-09-18 12:41         ` [PATCH V4] " Feng Xue OS
@ 2019-09-30  8:51           ` Feng Xue OS
  2019-10-15 17:04           ` Jan Hubicka
  1 sibling, 0 replies; 11+ messages in thread
From: Feng Xue OS @ 2019-09-30  8:51 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Martin Jambor, gcc-patches

Hi, Honza & Martin,

  Would you please take some time to review this updated patch? Thanks.

Feng

________________________________________
From: Feng Xue OS <fxue@os.amperecomputing.com>
Sent: Wednesday, September 18, 2019 8:41 PM
To: Jan Hubicka
Cc: Martin Jambor; gcc-patches@gcc.gnu.org
Subject: [PATCH V4] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088)

>> +      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.
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, &param_type, &aggpos,
+                           &param_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, &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;
@@ -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, &param_type, &aggpos,
+                            &param_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, &param_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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH V4] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088)
  2019-09-18 12:41         ` [PATCH V4] " Feng Xue OS
  2019-09-30  8:51           ` Ping: " Feng Xue OS
@ 2019-10-15 17:04           ` Jan Hubicka
  1 sibling, 0 replies; 11+ messages in thread
From: Jan Hubicka @ 2019-10-15 17:04 UTC (permalink / raw)
  To: Feng Xue OS; +Cc: Martin Jambor, gcc-patches

> >> +      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, &param_type, &aggpos,
> +			    &param_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, &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;
> @@ -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, &param_type, &aggpos,
> +			     &param_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, &param_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
> 

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2019-10-15 16:53 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-12 11:34 [PATCH] Generalized predicate/condition for parameter reference in IPA (PR ipa/91088) Feng Xue OS
2019-08-29 17:02 ` Martin Jambor
2019-08-30  8:42   ` Feng Xue OS
2019-08-30  9:00     ` Martin Jambor
2019-08-30  9:02       ` Feng Xue OS
2019-09-04 10:08   ` [PATCH V2] " Feng Xue OS
2019-09-04 10:20     ` [PATCH V3] " Feng Xue OS
2019-09-14 17:18       ` Jan Hubicka
2019-09-18 12:41         ` [PATCH V4] " Feng Xue OS
2019-09-30  8:51           ` Ping: " Feng Xue OS
2019-10-15 17:04           ` Jan Hubicka

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).