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

Reply via email to