Generalized IPA predicate on parameter reference
authorFeng Xue <fxue@os.amperecomputing.com>
Wed, 16 Oct 2019 07:27:50 +0000 (07:27 +0000)
committerFeng Xue <fxue@gcc.gnu.org>
Wed, 16 Oct 2019 07:27:50 +0000 (07:27 +0000)
2019-10-16  Feng Xue  <fxue@os.amperecomputing.com>

        PR ipa/91088
        * doc/invoke.texi (ipa-max-param-expr-ops): Document new option.
        * params.def (PARAM_IPA_MAX_PARAM_EXPR_OPS): New.
        * ipa-predicat.h (struct expr_eval_op): New struct.
        (expr_eval_ops): New typedef.
        (struct condition): Add type and param_ops fields, remove size field.
        (add_condition): Replace size parameter with type parameter, add
        param_ops parameter.
        * ipa-predicat.c (expr_eval_ops_equal_p): New function.
        (predicate::add_clause): Add comparisons on type and param_ops.
        (dump_condition): Add debug dump for param_ops.
        (remap_after_inlining): Adjust call arguments to add_condition.
        (add_condition): Replace size parameter with type parameter, add
        param_ops parameter. Unshare constant value used in conditions.
        * ipa-fnsummary.c (evaluate_conditions_for_known_args): Fold
        parameter expressions using param_ops.
        (decompose_param_expr):  New function.
        (set_cond_stmt_execution_predicate): Use call to decompose_param_expr
        to replace call to unmodified_parm_or_parm_agg_item.
        (set_switch_stmt_execution_predicate): Likewise.
        (will_be_nonconstant_expr_predicate): Likewise. Replace usage of size
        with type.
        (inline_read_section): Read param_ops from summary stream.
        (ipa_fn_summary_write): Write param_ops to summary stream.

2019-10-16  Feng Xue  <fxue@os.amperecomputing.com>

        PR ipa/91088
        * gcc.dg/ipa/pr91088.c: New test.
        * gcc.dg/ipa/pr91089.c: Add sub-test for range analysis.
        * g++.dg/tree-ssa/ivopts-3.C: Force a function to be noinline.

From-SVN: r277054

gcc/ChangeLog
gcc/doc/invoke.texi
gcc/ipa-fnsummary.c
gcc/ipa-predicate.c
gcc/ipa-predicate.h
gcc/params.def
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/tree-ssa/ivopts-3.C
gcc/testsuite/gcc.dg/ipa/pr91088.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/pr91089.c

index 175ecd9224f43d517ad8154ee57816bdc4d50a43..28629582a023a38740936aab36ffc5b775bddb37 100644 (file)
@@ -1,3 +1,30 @@
+2019-10-16  Feng Xue  <fxue@os.amperecomputing.com>
+
+       PR ipa/91088
+       * doc/invoke.texi (ipa-max-param-expr-ops): Document new option.
+       * params.def (PARAM_IPA_MAX_PARAM_EXPR_OPS): New.
+       * ipa-predicat.h (struct expr_eval_op): New struct.
+       (expr_eval_ops): New typedef.
+       (struct condition): Add type and param_ops fields, remove size field.
+       (add_condition): Replace size parameter with type parameter, add
+       param_ops parameter.
+       * ipa-predicat.c (expr_eval_ops_equal_p): New function.
+       (predicate::add_clause): Add comparisons on type and param_ops.
+       (dump_condition): Add debug dump for param_ops.
+       (remap_after_inlining): Adjust call arguments to add_condition.
+       (add_condition): Replace size parameter with type parameter, add
+       param_ops parameter. Unshare constant value used in conditions.
+       * ipa-fnsummary.c (evaluate_conditions_for_known_args): Fold
+       parameter expressions using param_ops.
+       (decompose_param_expr):  New function.
+       (set_cond_stmt_execution_predicate): Use call to decompose_param_expr
+       to replace call to unmodified_parm_or_parm_agg_item.
+       (set_switch_stmt_execution_predicate): Likewise.
+       (will_be_nonconstant_expr_predicate): Likewise. Replace usage of size
+       with type.
+       (inline_read_section): Read param_ops from summary stream.
+       (ipa_fn_summary_write): Write param_ops to summary stream.
+
 2019-10-15  Segher Boessenkool  <segher@kernel.crashing.org>
 
        PR rtl-optimization/92107
index 47769262b8e7bece23fd9253f428afebc9fcdc39..1407d019d1404ebbdb9630d6f541eab2e7a1bd01 100644 (file)
@@ -12034,6 +12034,13 @@ For switch exceeding this limit, IPA-CP will not construct cloning cost
 predicate, which is used to estimate cloning benefit, for default case
 of the switch statement.
 
+@item ipa-max-param-expr-ops
+IPA-CP will analyze conditional statement that references some function
+parameter to estimate benefit for cloning upon certain constant value.
+But if number of operations in a parameter expression exceeds
+@option{ipa-max-param-expr-ops}, the expression is treated as complicated
+one, and is not handled by IPA analysis.
+
 @item lto-partitions
 Specify desired number of partitions produced during WHOPR compilation.
 The number of partitions should exceed the number of CPUs used for compilation.
index a43867937bda7fe68eabd980df1327ddb4e2b731..40a9e0f3ee3872cdeb63b8aee500cec65a48dee8 100644 (file)
@@ -331,6 +331,8 @@ evaluate_conditions_for_known_args (struct cgraph_node *node,
     {
       tree val;
       tree res;
+      int j;
+      struct expr_eval_op *op;
 
       /* We allow call stmt to have fewer arguments than the callee function
          (especially for K&R style programs).  So bound check here (we assume
@@ -382,7 +384,7 @@ evaluate_conditions_for_known_args (struct cgraph_node *node,
          continue;
        }
 
-      if (maybe_ne (tree_to_poly_int64 (TYPE_SIZE (TREE_TYPE (val))), c->size))
+      if (TYPE_SIZE (c->type) != TYPE_SIZE (TREE_TYPE (val)))
        {
          clause |= 1 << (i + predicate::first_dynamic_condition);
          nonspec_clause |= 1 << (i + predicate::first_dynamic_condition);
@@ -394,7 +396,30 @@ evaluate_conditions_for_known_args (struct cgraph_node *node,
          continue;
        }
 
-      val = fold_unary (VIEW_CONVERT_EXPR, TREE_TYPE (c->val), val);
+      val = fold_unary (VIEW_CONVERT_EXPR, c->type, val);
+      for (j = 0; vec_safe_iterate (c->param_ops, j, &op); j++)
+       {
+         if (!val)
+           break;
+         if (!op->val[0])
+           val = fold_unary (op->code, op->type, val);
+         else if (!op->val[1])
+           val = fold_binary (op->code, op->type,
+                              op->index ? op->val[0] : val,
+                              op->index ? val : op->val[0]);
+         else if (op->index == 0)
+           val = fold_ternary (op->code, op->type,
+                               val, op->val[0], op->val[1]);
+         else if (op->index == 1)
+           val = fold_ternary (op->code, op->type,
+                               op->val[0], val, op->val[1]);
+         else if (op->index == 2)
+           val = fold_ternary (op->code, op->type,
+                               op->val[0], op->val[1], val);
+         else
+           val = NULL_TREE;
+       }
+
       res = val
        ? fold_binary_to_constant (c->code, boolean_type_node, val, c->val)
        : NULL;
@@ -1155,6 +1180,127 @@ eliminated_by_inlining_prob (ipa_func_body_info *fbi, gimple *stmt)
     }
 }
 
+/* Analyze EXPR if it represents a series of simple operations performed on
+   a function parameter and return true if so.  FBI, STMT, EXPR, INDEX_P and
+   AGGPOS have the same meaning like in unmodified_parm_or_parm_agg_item.
+   Type of the parameter or load from an aggregate via the parameter is
+   stored in *TYPE_P.  Operations on the parameter are recorded to
+   PARAM_OPS_P if it is not NULL.  */
+
+static bool
+decompose_param_expr (struct ipa_func_body_info *fbi,
+                     gimple *stmt, tree expr,
+                     int *index_p, tree *type_p,
+                     struct agg_position_info *aggpos,
+                     expr_eval_ops *param_ops_p = NULL)
+{
+  int op_limit = PARAM_VALUE (PARAM_IPA_MAX_PARAM_EXPR_OPS);
+  int op_count = 0;
+
+  if (param_ops_p)
+    *param_ops_p = NULL;
+
+  while (true)
+    {
+      expr_eval_op eval_op;
+      unsigned rhs_count;
+      unsigned cst_count = 0;
+
+      if (unmodified_parm_or_parm_agg_item (fbi, stmt, expr, index_p, NULL,
+                                           aggpos))
+       {
+         tree type = TREE_TYPE (expr);
+
+         if (aggpos->agg_contents)
+           {
+             /* Stop if containing bit-field.  */
+             if (TREE_CODE (expr) == BIT_FIELD_REF
+                 || contains_bitfld_component_ref_p (expr))
+               break;
+           }
+
+         *type_p = type;
+         return true;
+       }
+
+      if (TREE_CODE (expr) != SSA_NAME || SSA_NAME_IS_DEFAULT_DEF (expr))
+       break;
+
+      if (!is_gimple_assign (stmt = SSA_NAME_DEF_STMT (expr)))
+       break;
+
+      switch (gimple_assign_rhs_class (stmt))
+       {
+       case GIMPLE_SINGLE_RHS:
+         expr = gimple_assign_rhs1 (stmt);
+         continue;
+
+       case GIMPLE_UNARY_RHS:
+         rhs_count = 1;
+         break;
+
+       case GIMPLE_BINARY_RHS:
+         rhs_count = 2;
+         break;
+
+       case GIMPLE_TERNARY_RHS:
+         rhs_count = 3;
+         break;
+
+       default:
+         goto fail;
+       }
+
+      /* Stop if expression is too complex.  */
+      if (op_count++ == op_limit)
+       break;
+
+      if (param_ops_p)
+       {
+         eval_op.code = gimple_assign_rhs_code (stmt);
+         eval_op.type = TREE_TYPE (gimple_assign_lhs (stmt));
+         eval_op.val[0] = NULL_TREE;
+         eval_op.val[1] = NULL_TREE;
+       }
+
+      expr = NULL_TREE;
+      for (unsigned i = 0; i < rhs_count; i++)
+       {
+         tree op = gimple_op (stmt, i + 1);
+
+         gcc_assert (op && !TYPE_P (op));
+         if (is_gimple_ip_invariant (op))
+           {
+             if (++cst_count == rhs_count)
+               goto fail;
+
+             eval_op.val[cst_count - 1] = op;
+           }
+         else if (!expr)
+           {
+             /* Found a non-constant operand, and record its index in rhs
+                operands.  */
+             eval_op.index = i;
+             expr = op;
+           }
+         else
+           {
+             /* Found more than one non-constant operands.  */
+             goto fail;
+           }
+       }
+
+      if (param_ops_p)
+       vec_safe_insert (*param_ops_p, 0, eval_op);
+    }
+
+  /* Failed to decompose, free resource and return.  */
+fail:
+  if (param_ops_p)
+    vec_free (*param_ops_p);
+
+  return false;
+}
 
 /* If BB ends by a conditional we can turn into predicates, attach corresponding
    predicates to the CFG edges.   */
@@ -1165,15 +1311,15 @@ set_cond_stmt_execution_predicate (struct ipa_func_body_info *fbi,
                                   basic_block bb)
 {
   gimple *last;
-  tree op;
+  tree op, op2;
   int index;
-  poly_int64 size;
   struct agg_position_info aggpos;
   enum tree_code code, inverted_code;
   edge e;
   edge_iterator ei;
   gimple *set_stmt;
-  tree op2;
+  tree param_type;
+  expr_eval_ops param_ops;
 
   last = last_stmt (bb);
   if (!last || gimple_code (last) != GIMPLE_COND)
@@ -1181,10 +1327,9 @@ set_cond_stmt_execution_predicate (struct ipa_func_body_info *fbi,
   if (!is_gimple_ip_invariant (gimple_cond_rhs (last)))
     return;
   op = gimple_cond_lhs (last);
-  /* TODO: handle conditionals like
-     var = op0 < 4;
-     if (var != 0).  */
-  if (unmodified_parm_or_parm_agg_item (fbi, last, op, &index, &size, &aggpos))
+
+  if (decompose_param_expr (fbi, last, op, &index, &param_type, &aggpos,
+                           &param_ops))
     {
       code = gimple_cond_code (last);
       inverted_code = invert_tree_comparison (code, HONOR_NANS (op));
@@ -1205,13 +1350,13 @@ set_cond_stmt_execution_predicate (struct ipa_func_body_info *fbi,
              && !dominated_by_p (CDI_POST_DOMINATORS, bb, e->dest))
            {
              predicate p
-               = add_condition (summary, index, size, &aggpos, this_code,
-                                unshare_expr_without_location
-                                (gimple_cond_rhs (last)));
+               = add_condition (summary, index, param_type, &aggpos,
+                                this_code, gimple_cond_rhs (last), param_ops);
              e->aux = edge_predicate_pool.allocate ();
              *(predicate *) e->aux = p;
            }
        }
+      vec_free (param_ops);
     }
 
   if (TREE_CODE (op) != SSA_NAME)
@@ -1234,12 +1379,11 @@ set_cond_stmt_execution_predicate (struct ipa_func_body_info *fbi,
       || gimple_call_num_args (set_stmt) != 1)
     return;
   op2 = gimple_call_arg (set_stmt, 0);
-  if (!unmodified_parm_or_parm_agg_item (fbi, set_stmt, op2, &index, &size,
-                                        &aggpos))
+  if (!decompose_param_expr (fbi, set_stmt, op2, &index, &param_type, &aggpos))
     return;
   FOR_EACH_EDGE (e, ei, bb->succs) if (e->flags & EDGE_FALSE_VALUE)
     {
-      predicate p = add_condition (summary, index, size, &aggpos,
+      predicate p = add_condition (summary, index, param_type, &aggpos,
                                   predicate::is_not_constant, NULL_TREE);
       e->aux = edge_predicate_pool.allocate ();
       *(predicate *) e->aux = p;
@@ -1258,19 +1402,21 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi,
   gimple *lastg;
   tree op;
   int index;
-  poly_int64 size;
   struct agg_position_info aggpos;
   edge e;
   edge_iterator ei;
   size_t n;
   size_t case_idx;
+  tree param_type;
+  expr_eval_ops param_ops;
 
   lastg = last_stmt (bb);
   if (!lastg || gimple_code (lastg) != GIMPLE_SWITCH)
     return;
   gswitch *last = as_a <gswitch *> (lastg);
   op = gimple_switch_index (last);
-  if (!unmodified_parm_or_parm_agg_item (fbi, last, op, &index, &size, &aggpos))
+  if (!decompose_param_expr (fbi, last, op, &index, &param_type, &aggpos,
+                            &param_ops))
     return;
 
   auto_vec<std::pair<tree, tree> > ranges;
@@ -1302,6 +1448,8 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi,
       tree max = CASE_HIGH (cl);
       predicate p;
 
+      e = gimple_switch_edge (cfun, last, case_idx);
+
       /* The case value might not have same type as switch expression,
         extend the value based on the expression type.  */
       if (TREE_TYPE (min) != type)
@@ -1318,18 +1466,17 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi,
       if (dominated_by_p (CDI_POST_DOMINATORS, bb, e->dest))
        p = true;
       else if (min == max)
-       p = add_condition (summary, index, size, &aggpos, EQ_EXPR,
-                          unshare_expr_without_location (min));
+       p = add_condition (summary, index, param_type, &aggpos, EQ_EXPR,
+                          min, param_ops);
       else
        {
          predicate p1, p2;
-         p1 = add_condition (summary, index, size, &aggpos, GE_EXPR,
-                             unshare_expr_without_location (min));
-         p2 = add_condition (summary, index, size, &aggpos, LE_EXPR,
-                             unshare_expr_without_location (max));
+         p1 = add_condition (summary, index, param_type, &aggpos, GE_EXPR,
+                             min, param_ops);
+         p2 = add_condition (summary, index, param_type, &aggpos, LE_EXPR,
+                             max, param_ops);
          p = p1 & p2;
        }
-      e = gimple_switch_edge (cfun, last, case_idx);
       *(class predicate *) e->aux
        = p.or_with (summary->conds, *(class predicate *) e->aux);
 
@@ -1378,6 +1525,7 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi,
   if (bound_count > bound_limit)
     {
       *(class predicate *) e->aux = true;
+      vec_free (param_ops);
       return;
     }
 
@@ -1407,16 +1555,16 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi,
       tree max = ranges[i].second;
 
       if (min == max)
-       p_seg &= add_condition (summary, index, size, &aggpos, NE_EXPR,
-                               unshare_expr_without_location (min));
+       p_seg &= add_condition (summary, index, param_type, &aggpos, NE_EXPR,
+                               min, param_ops);
       else
        {
          /* Do not create sub-predicate for range that is beyond low bound
             of switch index.  */
          if (wi::lt_p (vr_wmin, wi::to_wide (min), TYPE_SIGN (type)))
            {
-             p_seg &= add_condition (summary, index, size, &aggpos, LT_EXPR,
-                                     unshare_expr_without_location (min));
+             p_seg &= add_condition (summary, index, param_type, &aggpos,
+                                     LT_EXPR, min, param_ops);
              p_all = p_all.or_with (summary->conds, p_seg);
            }
 
@@ -1428,14 +1576,16 @@ set_switch_stmt_execution_predicate (struct ipa_func_body_info *fbi,
              break;
            }
 
-         p_seg = add_condition (summary, index, size, &aggpos, GT_EXPR,
-                                unshare_expr_without_location (max));
+         p_seg = add_condition (summary, index, param_type, &aggpos, GT_EXPR,
+                                max, param_ops);
        }
     }
 
   p_all = p_all.or_with (summary->conds, p_seg);
   *(class predicate *) e->aux
     = p_all.or_with (summary->conds, *(class predicate *) e->aux);
+
+  vec_free (param_ops);
 }
 
 
@@ -1552,15 +1702,14 @@ will_be_nonconstant_expr_predicate (ipa_func_body_info *fbi,
 {
   tree parm;
   int index;
-  poly_int64 size;
 
   while (UNARY_CLASS_P (expr))
     expr = TREE_OPERAND (expr, 0);
 
-  parm = unmodified_parm (fbi, NULL, expr, &size);
+  parm = unmodified_parm (fbi, NULL, expr, NULL);
   if (parm && (index = ipa_get_param_decl_index (fbi->info, parm)) >= 0)
-    return add_condition (summary, index, size, NULL, predicate::changed,
-                         NULL_TREE);
+    return add_condition (summary, index, TREE_TYPE (parm), NULL,
+                         predicate::changed, NULL_TREE);
   if (is_gimple_min_invariant (expr))
     return false;
   if (TREE_CODE (expr) == SSA_NAME)
@@ -1624,10 +1773,10 @@ will_be_nonconstant_predicate (struct ipa_func_body_info *fbi,
   predicate p = true;
   ssa_op_iter iter;
   tree use;
+  tree param_type = NULL_TREE;
   predicate op_non_const;
   bool is_load;
   int base_index;
-  poly_int64 size;
   struct agg_position_info aggpos;
 
   /* What statments might be optimized away
@@ -1648,11 +1797,9 @@ will_be_nonconstant_predicate (struct ipa_func_body_info *fbi,
   /* Loads can be optimized when the value is known.  */
   if (is_load)
     {
-      tree op;
-      gcc_assert (gimple_assign_single_p (stmt));
-      op = gimple_assign_rhs1 (stmt);
-      if (!unmodified_parm_or_parm_agg_item (fbi, stmt, op, &base_index, &size,
-                                            &aggpos))
+      tree op = gimple_assign_rhs1 (stmt);
+      if (!decompose_param_expr (fbi, stmt, op, &base_index, &param_type,
+                                &aggpos))
        return p;
     }
   else
@@ -1677,21 +1824,20 @@ will_be_nonconstant_predicate (struct ipa_func_body_info *fbi,
 
   if (is_load)
     op_non_const =
-      add_condition (summary, base_index, size, &aggpos, predicate::changed,
-                    NULL);
+      add_condition (summary, base_index, param_type, &aggpos,
+                    predicate::changed, NULL_TREE);
   else
     op_non_const = false;
   FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
     {
-      poly_int64 size;
-      tree parm = unmodified_parm (fbi, stmt, use, &size);
+      tree parm = unmodified_parm (fbi, stmt, use, NULL);
       int index;
 
       if (parm && (index = ipa_get_param_decl_index (fbi->info, parm)) >= 0)
        {
          if (index != base_index)
-           p = add_condition (summary, index, size, NULL, predicate::changed,
-                              NULL_TREE);
+           p = add_condition (summary, index, TREE_TYPE (parm), NULL,
+                              predicate::changed, NULL_TREE);
          else
            continue;
        }
@@ -1823,7 +1969,7 @@ param_change_prob (ipa_func_body_info *fbi, gimple *stmt, int i)
        return REG_BR_PROB_BASE;
       if (dump_file)
        {
-         fprintf (dump_file, "     Analyzing param change probablity of ");
+         fprintf (dump_file, "     Analyzing param change probability of ");
           print_generic_expr (dump_file, op, TDF_SLIM);
          fprintf (dump_file, "\n");
        }
@@ -3452,15 +3598,49 @@ inline_read_section (struct lto_file_decl_data *file_data, const char *data,
       for (j = 0; j < count2; j++)
        {
          struct condition c;
+         unsigned int k, count3;
          c.operand_num = streamer_read_uhwi (&ib);
-         c.size = streamer_read_poly_uint64 (&ib);
          c.code = (enum tree_code) streamer_read_uhwi (&ib);
+         c.type = stream_read_tree (&ib, data_in);
          c.val = stream_read_tree (&ib, data_in);
          bp = streamer_read_bitpack (&ib);
          c.agg_contents = bp_unpack_value (&bp, 1);
          c.by_ref = bp_unpack_value (&bp, 1);
          if (c.agg_contents)
            c.offset = streamer_read_uhwi (&ib);
+         c.param_ops = NULL;
+         count3 = streamer_read_uhwi (&ib);
+         for (k = 0; k < count3; k++)
+           {
+             struct expr_eval_op op;
+             enum gimple_rhs_class rhs_class;
+             op.code = (enum tree_code) streamer_read_uhwi (&ib);
+             op.type = stream_read_tree (&ib, data_in);
+             switch (rhs_class = get_gimple_rhs_class (op.code))
+               {
+               case GIMPLE_UNARY_RHS:
+                 op.index = 0;
+                 op.val[0] = NULL_TREE;
+                 op.val[1] = NULL_TREE;
+                 break;
+
+               case GIMPLE_BINARY_RHS:
+               case GIMPLE_TERNARY_RHS:
+                 bp = streamer_read_bitpack (&ib);
+                 op.index = bp_unpack_value (&bp, 2);
+                 op.val[0] = stream_read_tree (&ib, data_in);
+                 if (rhs_class == GIMPLE_BINARY_RHS)
+                   op.val[1] = NULL_TREE;
+                 else
+                   op.val[1] = stream_read_tree (&ib, data_in);
+                 break;
+
+               default:
+                 fatal_error (UNKNOWN_LOCATION,
+                              "invalid fnsummary in LTO stream");
+               }
+             vec_safe_push (c.param_ops, op);
+           }
          if (info)
            vec_safe_push (info->conds, c);
        }
@@ -3606,9 +3786,12 @@ ipa_fn_summary_write (void)
          streamer_write_uhwi (ob, vec_safe_length (info->conds));
          for (i = 0; vec_safe_iterate (info->conds, i, &c); i++)
            {
+             int j;
+             struct expr_eval_op *op;
+
              streamer_write_uhwi (ob, c->operand_num);
-             streamer_write_poly_uint64 (ob, c->size);
              streamer_write_uhwi (ob, c->code);
+             stream_write_tree (ob, c->type, true);
              stream_write_tree (ob, c->val, true);
              bp = bitpack_create (ob->main_stream);
              bp_pack_value (&bp, c->agg_contents, 1);
@@ -3616,6 +3799,21 @@ ipa_fn_summary_write (void)
              streamer_write_bitpack (&bp);
              if (c->agg_contents)
                streamer_write_uhwi (ob, c->offset);
+             streamer_write_uhwi (ob, vec_safe_length (c->param_ops));
+             for (j = 0; vec_safe_iterate (c->param_ops, j, &op); j++)
+               {
+                 streamer_write_uhwi (ob, op->code);
+                 stream_write_tree (ob, op->type, true);
+                 if (op->val[0])
+                   {
+                     bp = bitpack_create (ob->main_stream);
+                     bp_pack_value (&bp, op->index, 2);
+                     streamer_write_bitpack (&bp);
+                     stream_write_tree (ob, op->val[0], true);
+                     if (op->val[1])
+                       stream_write_tree (ob, op->val[1], true);
+                   }
+               }
            }
          streamer_write_uhwi (ob, vec_safe_length (info->size_time_table));
          for (i = 0; vec_safe_iterate (info->size_time_table, i, &e); i++)
index 8a9851a2040d53d166ecba548464c9c8ac43cc4c..b5e3cf44323896849709e738962a93a0c5af9723 100644 (file)
@@ -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);
index 237306dc9fe5b837e7687517b14602fdc4a919ba..4121218ac041c631cc9751e9458b8fead6f37a27 100644 (file)
@@ -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);
index e4b70bac22a43a9b6098fde1d819f12d3cf9f08b..322c37f8b96c7c5ade92529b7e725ccb92787903 100644 (file)
@@ -1165,6 +1165,12 @@ DEFPARAM (PARAM_IPA_MAX_SWITCH_PREDICATE_BOUNDS,
          "statement used during IPA functoin summary generation.",
          5, 0, 0)
 
+DEFPARAM (PARAM_IPA_MAX_PARAM_EXPR_OPS,
+         "ipa-max-param-expr-ops",
+         "Maximum number of operations in a parameter expression that can "
+         "be handled by IPA analysis.",
+         10, 0, 0)
+
 /* WHOPR partitioning configuration.  */
 
 DEFPARAM (PARAM_LTO_PARTITIONS,
index 6dc4f00276913b239a2d84e28d27e55510987a52..b2b92deb1340346413e48b58acd1e62bf0800936 100644 (file)
@@ -1,3 +1,10 @@
+2019-10-16  Feng Xue  <fxue@os.amperecomputing.com>
+
+       PR ipa/91088
+       * gcc.dg/ipa/pr91088.c: New test.
+       * gcc.dg/ipa/pr91089.c: Add sub-test for range analysis.
+       * g++.dg/tree-ssa/ivopts-3.C: Force a function to be noinline.
+
 2019-10-15  Andrew Pinski  <apinski@marvell.com>
 
        * gcc.c-torture/compile/20191015-1.c: New test.
index 6760a5b185176e781afb3dded2e09793b3cf7ba6..b0da5e68abcc18d484d5176f0f43759f078c59b6 100644 (file)
@@ -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 (file)
index 0000000..a81c59f
--- /dev/null
@@ -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" } } */
index 7509c6222632ceacf3edc77296ab36d461928a87..a54cbdf5390b20304cf805d1b21606c81d698459 100644 (file)
@@ -7,7 +7,7 @@ int data;
 
 int callee (int i)
 {
-  switch (i)
+  switch (i % 128)
     {
       case -126:  return i + 13;
       case -127:  return i + 5;
@@ -45,7 +45,7 @@ int fn2 ();
 
 int callee_complex_predicate (int i)
 {
-  switch (i )
+  switch (i)
     {
       case 0:
        fn ();
@@ -100,10 +100,10 @@ int caller ()
 }
  
 /* { dg-final { scan-ipa-dump-times "Creating a specialized node of callee" 7 "cp" } } */
-/* { dg-final { scan-ipa-dump "op0 < -127" "fnsummary" } } */
-/* { dg-final { scan-ipa-dump "op0 > -126" "fnsummary" } } */
-/* { dg-final { scan-ipa-dump "op0 != -8"  "fnsummary" } } */
-/* { dg-final { scan-ipa-dump "op0 != 0"   "fnsummary" } } */
-/* { dg-final { scan-ipa-dump "op0 < 5"    "fnsummary" } } */
-/* { dg-final { scan-ipa-dump "op0 > 7"    "fnsummary" } } */
+/* { dg-final { scan-ipa-dump-not "op0,\\(# % 128\\) < -127" "fnsummary" } } */
+/* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) > -126" "fnsummary" } } */
+/* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) != -8"  "fnsummary" } } */
+/* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) != 0"   "fnsummary" } } */
+/* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) < 5"    "fnsummary" } } */
+/* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) > 7"    "fnsummary" } } */
 /* { dg-final { scan-ipa-dump "loop depth: 1 .+ time:\[ \]*\[0-9\]+ predicate: \\(op0 == 1000\\)\[\r\n]+" "fnsummary" } } */