On Fri, Oct 26, 2012 at 7:27 PM, Jakub Jelinek <ja...@redhat.com> wrote:
> Hi!
>
> This patch extends optimize_range_tests optimization, so that it
> handles also the cases where the truth && or || has been gimplifed
> as a series of GIMPLE_CONDs or mixture thereof and BIT_{AND,IOR}_EXPR
> stmts.
>
> Example of code it handles is e.g.:
>   <bb 2>:
>   v1_3 = a_2(D) != 3;
>   v2_4 = a_2(D) != 1;
>   v3_5 = a_2(D) != 4;
>   v4_6 = a_2(D) != 2;
>   v5_7 = a_2(D) != 7;
>   v6_8 = a_2(D) != 5;
>   v7_9 = a_2(D) != 8;
>   v8_10 = a_2(D) != 6;
>   _11 = v1_3 & v2_4;
>   if (_11 != 0)
>     goto <bb 3>;
>   else
>     goto <bb 7>;
>
>   <bb 3>:
>   _13 = v3_5 & v4_6;
>   if (_13 != 0)
>     goto <bb 4>;
>   else
>     goto <bb 7>;
>
>   <bb 4>:
>   _14 = v5_7 & v6_8;
>   if (_14 != 0)
>     goto <bb 5>;
>   else
>     goto <bb 7>;
>
>   <bb 5>:
>   _15 = v7_9 & v8_10;
>   if (_15 != 0)
>     goto <bb 6>;
>   else
>     goto <bb 7>;
>
> or:
>
>   <bb 2>:
>   _3 = c_2(D) == 34;
>   _4 = c_2(D) == 32;
>   _5 = _3 | _4;
>   if (_5 != 0)
>     goto <bb 4>;
>   else
>     goto <bb 3>;
>
>   <bb 3>:
>   _8 = c_2(D) <= 31;
>   _7 = (int) _8;
>
>   <bb 4>:
>   # _1 = PHI <_7(3), 1(2)>
>
> It is implemented in reassociate_bb, as that is where all the
> infrastructure already is, but isn't done as part of normal
> reassociation, but before reassociating first bb that represents
> a series of && or || operands.  As post-dominator sons aren't
> particularly ordered, it can happen that the optimization is performed
> on the first, last or middle bb of the series of bbs, so it searches
> both forward and backward to find suitable basic blocks.
> The last bb in the series (last_bb) can be of the form of bb 3 in the
> second example, which is how certain sequencies are gimplified when
> assigning && or || result to a variable or returning it.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

Ok, but the code could really really have some more comments - functions
not fitting in my 80x24 terminal without seeing any comment what happens
here are a maintainance nightmare.

Thanks,
Richard.

> 2012-10-26  Jakub Jelinek  <ja...@redhat.com>
>
>         PR tree-optimization/19105
>         PR tree-optimization/21643
>         PR tree-optimization/46309
>         * tree-ssa-reassoc.c (init_range_entry): Add STMT argument
>         and use it if EXP is NULL.
>         (update_range_test): Handle OPCODE equal to ERROR_MARK
>         and oe->op NULL.
>         (optimize_range_tests): Likewise.
>         (final_range_test_p, suitable_cond_bb, no_side_effect_bb, get_ops,
>         maybe_optimize_range_tests): New functions.
>         (reassociate_bb): Call maybe_optimize_range_tests if last
>         stmt of bb is GIMPLE_COND that hasn't been visited yet.
>
>         * gcc.dg/pr19105.c: New test.
>         * gcc.dg/pr21643.c: New test.
>         * gcc.dg/pr46309-2.c: New test.
>         * gcc.c-torture/execute/pr46309.c: New test.
>
> --- gcc/tree-ssa-reassoc.c.jj   2012-10-25 09:21:08.657049321 +0200
> +++ gcc/tree-ssa-reassoc.c      2012-10-26 15:51:13.398025229 +0200
> @@ -1,5 +1,5 @@
>  /* Reassociation for trees.
> -   Copyright (C) 2005, 2007, 2008, 2009, 2010, 2011
> +   Copyright (C) 2005, 2007, 2008, 2009, 2010, 2011, 2012
>     Free Software Foundation, Inc.
>     Contributed by Daniel Berlin <d...@dberlin.org>
>
> @@ -1713,10 +1713,12 @@ struct range_entry
>  };
>
>  /* This is similar to make_range in fold-const.c, but on top of
> -   GIMPLE instead of trees.  */
> +   GIMPLE instead of trees.  If EXP is non-NULL, it should be
> +   an SSA_NAME and STMT argument is ignored, otherwise STMT
> +   argument should be a GIMPLE_COND.  */
>
>  static void
> -init_range_entry (struct range_entry *r, tree exp)
> +init_range_entry (struct range_entry *r, tree exp, gimple stmt)
>  {
>    int in_p;
>    tree low, high;
> @@ -1727,7 +1729,8 @@ init_range_entry (struct range_entry *r,
>    r->strict_overflow_p = false;
>    r->low = NULL_TREE;
>    r->high = NULL_TREE;
> -  if (TREE_CODE (exp) != SSA_NAME || !INTEGRAL_TYPE_P (TREE_TYPE (exp)))
> +  if (exp != NULL_TREE
> +      && (TREE_CODE (exp) != SSA_NAME || !INTEGRAL_TYPE_P (TREE_TYPE (exp))))
>      return;
>
>    /* Start with simply saying "EXP != 0" and then look at the code of EXP
> @@ -1735,12 +1738,14 @@ init_range_entry (struct range_entry *r,
>       happen, but it doesn't seem worth worrying about this.  We "continue"
>       the outer loop when we've changed something; otherwise we "break"
>       the switch, which will "break" the while.  */
> -  low = build_int_cst (TREE_TYPE (exp), 0);
> +  low = exp ? build_int_cst (TREE_TYPE (exp), 0) : boolean_false_node;
>    high = low;
>    in_p = 0;
>    strict_overflow_p = false;
>    is_bool = false;
> -  if (TYPE_PRECISION (TREE_TYPE (exp)) == 1)
> +  if (exp == NULL_TREE)
> +    is_bool = true;
> +  else if (TYPE_PRECISION (TREE_TYPE (exp)) == 1)
>      {
>        if (TYPE_UNSIGNED (TREE_TYPE (exp)))
>         is_bool = true;
> @@ -1752,25 +1757,35 @@ init_range_entry (struct range_entry *r,
>
>    while (1)
>      {
> -      gimple stmt;
>        enum tree_code code;
>        tree arg0, arg1, exp_type;
>        tree nexp;
>        location_t loc;
>
> -      if (TREE_CODE (exp) != SSA_NAME)
> -       break;
> +      if (exp != NULL_TREE)
> +       {
> +         if (TREE_CODE (exp) != SSA_NAME)
> +           break;
>
> -      stmt = SSA_NAME_DEF_STMT (exp);
> -      if (!is_gimple_assign (stmt))
> -       break;
> +         stmt = SSA_NAME_DEF_STMT (exp);
> +         if (!is_gimple_assign (stmt))
> +           break;
> +
> +         code = gimple_assign_rhs_code (stmt);
> +         arg0 = gimple_assign_rhs1 (stmt);
> +         arg1 = gimple_assign_rhs2 (stmt);
> +         exp_type = TREE_TYPE (exp);
> +       }
> +      else
> +       {
> +         code = gimple_cond_code (stmt);
> +         arg0 = gimple_cond_lhs (stmt);
> +         arg1 = gimple_cond_rhs (stmt);
> +         exp_type = boolean_type_node;
> +       }
>
> -      code = gimple_assign_rhs_code (stmt);
> -      arg0 = gimple_assign_rhs1 (stmt);
>        if (TREE_CODE (arg0) != SSA_NAME)
>         break;
> -      arg1 = gimple_assign_rhs2 (stmt);
> -      exp_type = TREE_TYPE (exp);
>        loc = gimple_location (stmt);
>        switch (code)
>         {
> @@ -1916,7 +1931,11 @@ range_entry_cmp (const void *a, const vo
>     [EXP, IN_P, LOW, HIGH, STRICT_OVERFLOW_P] is a merged range for
>     RANGE and OTHERRANGE through OTHERRANGE + COUNT - 1 ranges,
>     OPCODE and OPS are arguments of optimize_range_tests.  Return
> -   true if the range merge has been successful.  */
> +   true if the range merge has been successful.
> +   If OPCODE is ERROR_MARK, this is called from within
> +   maybe_optimize_range_tests and is performing inter-bb range optimization.
> +   Changes should be then performed right away, and whether an op is
> +   BIT_AND_EXPR or BIT_IOR_EXPR is found in oe->rank.  */
>
>  static bool
>  update_range_test (struct range_entry *range, struct range_entry *otherrange,
> @@ -1924,9 +1943,12 @@ update_range_test (struct range_entry *r
>                    VEC (operand_entry_t, heap) **ops, tree exp, bool in_p,
>                    tree low, tree high, bool strict_overflow_p)
>  {
> -  tree op = VEC_index (operand_entry_t, *ops, range->idx)->op;
> -  location_t loc = gimple_location (SSA_NAME_DEF_STMT (op));
> -  tree tem = build_range_check (loc, TREE_TYPE (op), exp, in_p, low, high);
> +  operand_entry_t oe = VEC_index (oeprand_entry_t, *ops, range->idx);
> +  tree op = oe->op;
> +  gimple stmt = op ? SSA_NAME_DEF_STMT (op) : last_stmt (BASIC_BLOCK 
> (oe->id));
> +  location_t loc = gimple_location (stmt);
> +  tree optype = op ? TREE_TYPE (op) : boolean_type_node;
> +  tree tem = build_range_check (loc, optype, exp, in_p, low, high);
>    enum warn_strict_overflow_code wc = WARN_STRICT_OVERFLOW_COMPARISON;
>    gimple_stmt_iterator gsi;
>
> @@ -1961,15 +1983,41 @@ update_range_test (struct range_entry *r
>        fprintf (dump_file, "\n");
>      }
>
> -  if (opcode == BIT_IOR_EXPR)
> +  if (opcode == BIT_IOR_EXPR
> +      || (opcode == ERROR_MARK && oe->rank == BIT_IOR_EXPR))
>      tem = invert_truthvalue_loc (loc, tem);
>
> -  tem = fold_convert_loc (loc, TREE_TYPE (op), tem);
> -  gsi = gsi_for_stmt (SSA_NAME_DEF_STMT (op));
> +  tem = fold_convert_loc (loc, optype, tem);
> +  gsi = gsi_for_stmt (stmt);
>    tem = force_gimple_operand_gsi (&gsi, tem, true, NULL_TREE, true,
>                                   GSI_SAME_STMT);
>
> -  VEC_index (operand_entry_t, *ops, range->idx)->op = tem;
> +  if (opcode == ERROR_MARK)
> +    {
> +      if (op)
> +       {
> +         imm_use_iterator iter;
> +         use_operand_p use_p;
> +         gimple use_stmt;
> +
> +         FOR_EACH_IMM_USE_STMT (use_stmt, iter, op)
> +           {
> +             if (is_gimple_debug (use_stmt))
> +               continue;
> +             FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
> +               SET_USE (use_p, tem);
> +             update_stmt (use_stmt);
> +           }
> +       }
> +      else
> +       {
> +         gimple_cond_set_code (stmt, NE_EXPR);
> +         gimple_cond_set_lhs (stmt, tem);
> +         gimple_cond_set_rhs (stmt, boolean_false_node);
> +         update_stmt (stmt);
> +       }
> +    }
> +  oe->op = tem;
>    range->exp = exp;
>    range->low = low;
>    range->high = high;
> @@ -1978,7 +2026,73 @@ update_range_test (struct range_entry *r
>
>    for (range = otherrange; range < otherrange + count; range++)
>      {
> -      VEC_index (operand_entry_t, *ops, range->idx)->op = error_mark_node;
> +      oe = VEC_index (oeprand_entry_t, *ops, range->idx);
> +      if (opcode == ERROR_MARK)
> +       {
> +         if (oe->op)
> +           {
> +             imm_use_iterator iter;
> +             use_operand_p use_p;
> +             gimple use_stmt;
> +
> +             FOR_EACH_IMM_USE_STMT (use_stmt, iter, oe->op)
> +               {
> +                 if (is_gimple_debug (use_stmt))
> +                   continue;
> +                 if (is_gimple_assign (use_stmt)
> +                     && gimple_assign_rhs_code (use_stmt) == oe->rank)
> +                   {
> +                     tree expr = NULL_TREE;
> +                     if (oe->op == gimple_assign_rhs1 (use_stmt))
> +                       expr = gimple_assign_rhs2 (use_stmt);
> +                     else if (oe->op == gimple_assign_rhs2 (use_stmt))
> +                       expr = gimple_assign_rhs1 (use_stmt);
> +                     if (expr
> +                         && expr != oe->op
> +                         && TREE_CODE (expr) == SSA_NAME)
> +                       {
> +                         gimple_stmt_iterator gsi2 = gsi_for_stmt (use_stmt);
> +                         gimple_assign_set_rhs_with_ops (&gsi2, SSA_NAME,
> +                                                         expr, NULL_TREE);
> +                         update_stmt (use_stmt);
> +                         continue;
> +                       }
> +                     if (gimple_assign_cast_p (use_stmt)
> +                         && oe->op == gimple_assign_rhs1 (use_stmt))
> +                       {
> +                         tree lhs = gimple_assign_lhs (use_stmt);
> +                         if (INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
> +                           {
> +                             gimple_stmt_iterator gsi2
> +                               = gsi_for_stmt (use_stmt);
> +                             expr = build_int_cst (TREE_TYPE (lhs),
> +                                                   oe->rank == BIT_IOR_EXPR
> +                                                   ? 0 : 1);
> +                             gimple_assign_set_rhs_with_ops (&gsi2,
> +                                                             INTEGER_CST,
> +                                                             expr, 
> NULL_TREE);
> +                           }
> +                       }
> +                   }
> +                 FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
> +                   SET_USE (use_p,
> +                            build_int_cst (TREE_TYPE (oe->op),
> +                                           oe->rank == BIT_IOR_EXPR
> +                                           ? 0 : 1));
> +                 update_stmt (use_stmt);
> +               }
> +           }
> +         else
> +           {
> +             stmt = last_stmt (BASIC_BLOCK (oe->id));
> +             if (oe->rank == BIT_IOR_EXPR)
> +               gimple_cond_make_false (stmt);
> +             else
> +               gimple_cond_make_true (stmt);
> +             update_stmt (stmt);
> +           }
> +       }
> +      oe->op = error_mark_node;
>        range->exp = NULL_TREE;
>      }
>    return true;
> @@ -1986,7 +2100,12 @@ update_range_test (struct range_entry *r
>
>  /* Optimize range tests, similarly how fold_range_test optimizes
>     it on trees.  The tree code for the binary
> -   operation between all the operands is OPCODE.  */
> +   operation between all the operands is OPCODE.
> +   If OPCODE is ERROR_MARK, optimize_range_tests is called from within
> +   maybe_optimize_range_tests for inter-bb range optimization.
> +   In that case if oe->op is NULL, oe->id is bb->index whose
> +   GIMPLE_COND is && or ||ed into the test, and oe->rank says
> +   the actual opcode.  */
>
>  static void
>  optimize_range_tests (enum tree_code opcode,
> @@ -2003,11 +2122,14 @@ optimize_range_tests (enum tree_code opc
>    ranges = XNEWVEC (struct range_entry, length);
>    for (i = 0; i < length; i++)
>      {
> +      oe = VEC_index (operand_entry_t, *ops, i);
>        ranges[i].idx = i;
> -      init_range_entry (ranges + i, VEC_index (operand_entry_t, *ops, 
> i)->op);
> +      init_range_entry (ranges + i, oe->op,
> +                       oe->op ? NULL : last_stmt (BASIC_BLOCK (oe->id)));
>        /* For | invert it now, we will invert it again before emitting
>          the optimized expression.  */
> -      if (opcode == BIT_IOR_EXPR)
> +      if (opcode == BIT_IOR_EXPR
> +         || (opcode == ERROR_MARK && oe->rank == BIT_IOR_EXPR))
>         ranges[i].in_p = !ranges[i].in_p;
>      }
>
> @@ -2124,7 +2246,7 @@ optimize_range_tests (enum tree_code opc
>         }
>      }
>
> -  if (any_changes)
> +  if (any_changes && opcode != ERROR_MARK)
>      {
>        j = 0;
>        FOR_EACH_VEC_ELT (operand_entry_t, *ops, i, oe)
> @@ -2141,6 +2263,398 @@ optimize_range_tests (enum tree_code opc
>    XDELETEVEC (ranges);
>  }
>
> +/* Return true if STMT is a cast like:
> +   <bb N>:
> +   ...
> +   _123 = (int) _234;
> +
> +   <bb M>:
> +   # _345 = PHI <_123(N), 1(...), 1(...)>
> +   where _234 has bool type, _123 has single use and
> +   bb N has a single successor M.  This is commonly used in
> +   the last block of a range test.  */
> +
> +static bool
> +final_range_test_p (gimple stmt)
> +{
> +  basic_block bb, rhs_bb;
> +  edge e;
> +  tree lhs, rhs;
> +  use_operand_p use_p;
> +  gimple use_stmt;
> +
> +  if (!gimple_assign_cast_p (stmt))
> +    return false;
> +  bb = gimple_bb (stmt);
> +  if (!single_succ_p (bb))
> +    return false;
> +  e = single_succ_edge (bb);
> +  if (e->flags & EDGE_COMPLEX)
> +    return false;
> +
> +  lhs = gimple_assign_lhs (stmt);
> +  rhs = gimple_assign_rhs1 (stmt);
> +  if (!INTEGRAL_TYPE_P (TREE_TYPE (lhs))
> +      || TREE_CODE (rhs) != SSA_NAME
> +      || TREE_CODE (TREE_TYPE (rhs)) != BOOLEAN_TYPE)
> +    return false;
> +
> +  if (!single_imm_use (lhs, &use_p, &use_stmt))
> +    return false;
> +
> +  if (gimple_code (use_stmt) != GIMPLE_PHI
> +      || gimple_bb (use_stmt) != e->dest)
> +    return false;
> +
> +  rhs_bb = gimple_bb (SSA_NAME_DEF_STMT (rhs));
> +  if (rhs_bb == NULL
> +      || !flow_bb_inside_loop_p (loop_containing_stmt (stmt), rhs_bb))
> +    return false;
> +
> +  return true;
> +}
> +
> +/* Return true if BB is suitable basic block for inter-bb range test
> +   optimization.  If BACKWARD is true, BB should be the only predecessor
> +   of TEST_BB, and *OTHER_BB is either NULL and filled by the routine,
> +   or compared with to find a common basic block to which all conditions
> +   branch to if true resp. false.  If BACKWARD is false, TEST_BB should
> +   be the only predecessor of BB.  */
> +
> +static bool
> +suitable_cond_bb (basic_block bb, basic_block test_bb, basic_block *other_bb,
> +                 bool backward)
> +{
> +  edge_iterator ei, ei2;
> +  edge e, e2;
> +  gimple stmt;
> +  gimple_stmt_iterator gsi;
> +  bool other_edge_seen = false;
> +  bool is_cond;
> +
> +  if (test_bb == bb)
> +    return false;
> +  stmt = last_stmt (bb);
> +  if (stmt == NULL
> +      || (gimple_code (stmt) != GIMPLE_COND
> +         && (backward || !final_range_test_p (stmt)))
> +      || gimple_visited_p (stmt)
> +      || stmt_could_throw_p (stmt)
> +      || *other_bb == bb)
> +    return false;
> +  is_cond = gimple_code (stmt) == GIMPLE_COND;
> +  if (is_cond)
> +    {
> +      if (EDGE_COUNT (bb->succs) != 2)
> +       return false;
> +      FOR_EACH_EDGE (e, ei, bb->succs)
> +       {
> +         if (!(e->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
> +           return false;
> +         if (e->dest == test_bb)
> +           {
> +             if (backward)
> +               continue;
> +             else
> +               return false;
> +           }
> +         if (e->dest == bb)
> +           return false;
> +         if (*other_bb == NULL)
> +           {
> +             FOR_EACH_EDGE (e2, ei2, test_bb->succs)
> +               if (!(e2->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
> +                 return false;
> +               else if (e->dest == e2->dest)
> +                 *other_bb = e->dest;
> +             if (*other_bb == NULL)
> +               return false;
> +           }
> +         if (e->dest == *other_bb)
> +           other_edge_seen = true;
> +         else if (backward)
> +           return false;
> +       }
> +      if (*other_bb == NULL || !other_edge_seen)
> +       return false;
> +    }
> +  else if (single_succ (bb) != *other_bb)
> +    return false;
> +
> +  e = find_edge (bb, *other_bb);
> +  e2 = find_edge (test_bb, *other_bb);
> +  for (gsi = gsi_start_phis (e->dest); !gsi_end_p (gsi); gsi_next (&gsi))
> +    {
> +      gimple phi = gsi_stmt (gsi);
> +      if (!operand_equal_p (gimple_phi_arg_def (phi, e->dest_idx),
> +                           gimple_phi_arg_def (phi, e2->dest_idx), 0))
> +       {
> +         if (!is_cond)
> +           {
> +             if (gimple_phi_arg_def (phi, e->dest_idx)
> +                 == gimple_assign_lhs (stmt)
> +                 && (integer_zerop (gimple_phi_arg_def (phi, e2->dest_idx))
> +                     || integer_onep (gimple_phi_arg_def (phi,
> +                                                          e2->dest_idx))))
> +               continue;
> +           }
> +         else
> +           {
> +             gimple test_last = last_stmt (test_bb);
> +             if (gimple_code (test_last) != GIMPLE_COND
> +                 && gimple_phi_arg_def (phi, e2->dest_idx)
> +                    == gimple_assign_lhs (test_last)
> +                 && (integer_zerop (gimple_phi_arg_def (phi, e->dest_idx))
> +                     || integer_onep (gimple_phi_arg_def (phi, 
> e->dest_idx))))
> +               continue;
> +           }
> +
> +         return false;
> +       }
> +    }
> +  return true;
> +}
> +
> +/* Return true if BB doesn't have side-effects that would disallow
> +   range test optimization, all SSA_NAMEs set in the bb are consumed
> +   in the bb and there are no PHIs.  */
> +
> +static bool
> +no_side_effect_bb (basic_block bb)
> +{
> +  gimple_stmt_iterator gsi;
> +  gimple last;
> +
> +  if (!gimple_seq_empty_p (phi_nodes (bb)))
> +    return false;
> +  last = last_stmt (bb);
> +  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
> +    {
> +      gimple stmt = gsi_stmt (gsi);
> +      tree lhs;
> +      imm_use_iterator imm_iter;
> +      use_operand_p use_p;
> +
> +      if (is_gimple_debug (stmt))
> +       continue;
> +      if (gimple_has_side_effects (stmt))
> +       return false;
> +      if (stmt == last)
> +       return true;
> +      if (!is_gimple_assign (stmt))
> +       return false;
> +      lhs = gimple_assign_lhs (stmt);
> +      if (TREE_CODE (lhs) != SSA_NAME)
> +       return false;
> +      if (gimple_assign_rhs_could_trap_p (stmt))
> +       return false;
> +      FOR_EACH_IMM_USE_FAST (use_p, imm_iter, lhs)
> +       {
> +         gimple use_stmt = USE_STMT (use_p);
> +         if (is_gimple_debug (use_stmt))
> +           continue;
> +         if (gimple_bb (use_stmt) != bb)
> +           return false;
> +       }
> +    }
> +  return false;
> +}
> +
> +/* If VAR is set by CODE (BIT_{AND,IOR}_EXPR) which is reassociable,
> +   return true and fill in *OPS recursively.  */
> +
> +static bool
> +get_ops (tree var, enum tree_code code, VEC(operand_entry_t, heap) **ops,
> +        struct loop *loop)
> +{
> +  gimple stmt = SSA_NAME_DEF_STMT (var);
> +  tree rhs[2];
> +  int i;
> +
> +  if (!is_reassociable_op (stmt, code, loop))
> +    return false;
> +
> +  rhs[0] = gimple_assign_rhs1 (stmt);
> +  rhs[1] = gimple_assign_rhs2 (stmt);
> +  gimple_set_visited (stmt, true);
> +  for (i = 0; i < 2; i++)
> +    if (TREE_CODE (rhs[i]) == SSA_NAME
> +       && !get_ops (rhs[i], code, ops, loop)
> +       && has_single_use (rhs[i]))
> +      {
> +       operand_entry_t oe = (operand_entry_t) pool_alloc 
> (operand_entry_pool);
> +
> +       oe->op = rhs[i];
> +       oe->rank = code;
> +       oe->id = 0;
> +       oe->count = 1;
> +       VEC_safe_push (operand_entry_t, heap, *ops, oe);
> +      }
> +  return true;
> +}
> +
> +/* Inter-bb range test optimization.  */
> +
> +static void
> +maybe_optimize_range_tests (gimple stmt)
> +{
> +  basic_block first_bb = gimple_bb (stmt);
> +  basic_block last_bb = first_bb;
> +  basic_block other_bb = NULL;
> +  basic_block bb;
> +  edge_iterator ei;
> +  edge e;
> +  VEC(operand_entry_t, heap) *ops = NULL;
> +
> +  if (gimple_code (stmt) == GIMPLE_COND)
> +    {
> +      if (EDGE_COUNT (first_bb->succs) != 2)
> +       return;
> +    }
> +  else if (final_range_test_p (stmt))
> +    other_bb = single_succ (first_bb);
> +  else
> +    return;
> +
> +  if (stmt_could_throw_p (stmt))
> +    return;
> +
> +  while (single_pred_p (first_bb))
> +    {
> +      basic_block pred_bb = single_pred (first_bb);
> +      if (!suitable_cond_bb (pred_bb, first_bb, &other_bb, true))
> +       break;
> +      if (!no_side_effect_bb (first_bb))
> +       break;
> +      first_bb = pred_bb;
> +    }
> +  if (first_bb == last_bb)
> +    {
> +      other_bb = NULL;
> +      if (gimple_code (stmt) != GIMPLE_COND)
> +       return;
> +      FOR_EACH_EDGE (e, ei, first_bb->succs)
> +       if (!(e->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE))
> +           || e->dest == first_bb)
> +         return;
> +       else if (single_pred_p (e->dest))
> +         {
> +           stmt = last_stmt (e->dest);
> +           if (stmt
> +               && gimple_code (stmt) == GIMPLE_COND
> +               && EDGE_COUNT (e->dest->succs) == 2)
> +             {
> +               if (suitable_cond_bb (first_bb, e->dest, &other_bb, true))
> +                 break;
> +               else
> +                 other_bb = NULL;
> +             }
> +           else if (stmt
> +                    && final_range_test_p (stmt)
> +                    && find_edge (first_bb, single_succ (e->dest)))
> +             {
> +               other_bb = single_succ (e->dest);
> +               if (other_bb == first_bb)
> +                 other_bb = NULL;
> +             }
> +         }
> +      if (other_bb == NULL)
> +       return;
> +    }
> +  while (EDGE_COUNT (last_bb->succs) == 2)
> +    {
> +      FOR_EACH_EDGE (e, ei, last_bb->succs)
> +       if (e->dest != other_bb)
> +         break;
> +      if (e == NULL)
> +       break;
> +      if (!single_pred_p (e->dest))
> +       break;
> +      if (!suitable_cond_bb (e->dest, last_bb, &other_bb, false))
> +       break;
> +      if (!no_side_effect_bb (e->dest))
> +       break;
> +      last_bb = e->dest;
> +    }
> +  if (first_bb == last_bb)
> +    return;
> +  for (bb = last_bb; ; bb = single_pred (bb))
> +    {
> +      enum tree_code code;
> +      tree lhs, rhs;
> +
> +      e = find_edge (bb, other_bb);
> +      stmt = last_stmt (bb);
> +      gimple_set_visited (stmt, true);
> +      if (gimple_code (stmt) != GIMPLE_COND)
> +       {
> +         use_operand_p use_p;
> +         gimple phi;
> +         edge e2;
> +         unsigned int d;
> +
> +         lhs = gimple_assign_lhs (stmt);
> +         rhs = gimple_assign_rhs1 (stmt);
> +         gcc_assert (bb == last_bb);
> +
> +         single_imm_use (lhs, &use_p, &phi);
> +         gcc_assert (gimple_code (phi) == GIMPLE_PHI);
> +         e2 = find_edge (first_bb, other_bb);
> +         d = e2->dest_idx;
> +         gcc_assert (gimple_phi_arg_def (phi, e->dest_idx) == lhs);
> +         if (integer_zerop (gimple_phi_arg_def (phi, d)))
> +           code = BIT_AND_EXPR;
> +         else
> +           {
> +             gcc_checking_assert (integer_onep (gimple_phi_arg_def (phi, 
> d)));
> +             code = BIT_IOR_EXPR;
> +           }
> +
> +         if (!get_ops (rhs, code, &ops,
> +                       loop_containing_stmt (stmt))
> +             && has_single_use (rhs))
> +           {
> +             operand_entry_t oe
> +               = (operand_entry_t) pool_alloc (operand_entry_pool);
> +
> +             oe->op = rhs;
> +             oe->rank = code;
> +             oe->id = 0;
> +             oe->count = 1;
> +             VEC_safe_push (operand_entry_t, heap, ops, oe);
> +           }
> +         continue;
> +       }
> +      code = gimple_cond_code (stmt);
> +      lhs = gimple_cond_lhs (stmt);
> +      rhs = gimple_cond_rhs (stmt);
> +      if (TREE_CODE (lhs) == SSA_NAME
> +         && INTEGRAL_TYPE_P (TREE_TYPE (lhs))
> +         && ((code != EQ_EXPR && code != NE_EXPR)
> +             || rhs != boolean_false_node
> +             || !get_ops (lhs, (((e->flags & EDGE_TRUE_VALUE) == 0)
> +                                ^ (code == EQ_EXPR))
> +                               ? BIT_AND_EXPR : BIT_IOR_EXPR, &ops,
> +                          loop_containing_stmt (stmt))))
> +       {
> +         operand_entry_t oe
> +           = (operand_entry_t) pool_alloc (operand_entry_pool);
> +
> +         oe->op = NULL;
> +         oe->rank = (e->flags & EDGE_TRUE_VALUE)
> +                    ? BIT_IOR_EXPR : BIT_AND_EXPR;
> +         oe->id = bb->index;
> +         oe->count = 1;
> +         VEC_safe_push (operand_entry_t, heap, ops, oe);
> +       }
> +      if (bb == first_bb)
> +       break;
> +    }
> +  if (VEC_length (operand_entry_t, ops) > 1)
> +    optimize_range_tests (ERROR_MARK, &ops);
> +  VEC_free (operand_entry_t, heap, ops);
> +}
> +
>  /* Return true if OPERAND is defined by a PHI node which uses the LHS
>     of STMT in it's operands.  This is also known as a "destructive
>     update" operation.  */
> @@ -3427,10 +3941,14 @@ reassociate_bb (basic_block bb)
>  {
>    gimple_stmt_iterator gsi;
>    basic_block son;
> +  gimple stmt = last_stmt (bb);
> +
> +  if (stmt && !gimple_visited_p (stmt))
> +    maybe_optimize_range_tests (stmt);
>
>    for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi); gsi_prev (&gsi))
>      {
> -      gimple stmt = gsi_stmt (gsi);
> +      stmt = gsi_stmt (gsi);
>
>        if (is_gimple_assign (stmt)
>           && !stmt_could_throw_p (stmt))
> --- gcc/testsuite/gcc.dg/pr19105.c.jj   2012-10-26 15:12:04.191828571 +0200
> +++ gcc/testsuite/gcc.dg/pr19105.c      2012-10-26 15:11:41.000000000 +0200
> @@ -0,0 +1,22 @@
> +/* PR tree-optimization/19105 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-reassoc1-details" } */
> +
> +enum e
> +{
> +  a, b, c, d, e, f, g, h
> +};
> +
> +int range1 (enum e v, int x)
> +{
> +  return x && v != c && v != d && v != e;
> +}
> +
> +int range2 (enum e v, int x)
> +{
> +  return x && (v != c && v != d && v != e);
> +}
> +
> +/* { dg-final { scan-tree-dump-times "Optimizing range tests v_\[0-9\]*.D. 
> -.2, 2. and -.3, 4.\[\n\r\]* into" 1 "reassoc1" } } */
> +/* { dg-final { cleanup-tree-dump "reassoc1" } } */
> +
> --- gcc/testsuite/gcc.dg/pr21643.c.jj   2012-10-26 15:00:14.588063088 +0200
> +++ gcc/testsuite/gcc.dg/pr21643.c      2012-10-26 15:01:31.199598891 +0200
> @@ -0,0 +1,90 @@
> +/* PR tree-optimization/21643 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-reassoc1-details" } */
> +
> +int
> +f1 (unsigned char c)
> +{
> +  if (c == 0x22 || c == 0x20 || c < 0x20)
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +f2 (unsigned char c)
> +{
> +  if (c == 0x22 || c <= 0x20)
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +f3 (unsigned char c)
> +{
> +  if (c == 0x22)
> +    return 1;
> +  if (c == 0x20)
> +    return 1;
> +  if (c < 0x20)
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +f4 (unsigned char c)
> +{
> +  if (c == 0x22 || c == 0x20 || c < 0x20)
> +    return 2;
> +  return 0;
> +}
> +
> +int
> +f5 (unsigned char c)
> +{
> +  if (c == 0x22 || c <= 0x20)
> +    return 2;
> +  return 0;
> +}
> +
> +int
> +f6 (unsigned char c)
> +{
> +  if (c == 0x22)
> +    return 2;
> +  if (c == 0x20)
> +    return 2;
> +  if (c < 0x20)
> +    return 2;
> +  return 0;
> +}
> +
> +int
> +f7 (unsigned char c)
> +{
> +  if (c != 0x22 && c != 0x20 && c >= 0x20)
> +    return 0;
> +  return 1;
> +}
> +
> +int
> +f8 (unsigned char c)
> +{
> +  if (c == 0x22 && c <= 0x20)
> +    return 0;
> +  return 1;
> +}
> +
> +int
> +f9 (unsigned char c)
> +{
> +  if (c == 0x22)
> +    return 0;
> +  if (c == 0x20)
> +    return 0;
> +  if (c < 0x20)
> +    return 0;
> +  return 1;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "Optimizing range tests c_\[0-9\]*.D. 
> -.0, 31. and -.32, 32.\[\n\r\]* into" 6 "reassoc1" } } */
> +/* { dg-final { cleanup-tree-dump "reassoc1" } } */
> --- gcc/testsuite/gcc.dg/pr46309-2.c.jj 2012-10-25 16:30:42.623578285 +0200
> +++ gcc/testsuite/gcc.dg/pr46309-2.c    2012-10-26 15:16:38.404212168 +0200
> @@ -0,0 +1,147 @@
> +/* PR tree-optimization/46309 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-reassoc-details" } */
> +
> +int foo (void);
> +
> +void
> +f1 (int a)
> +{
> +  _Bool v1 = (a == 3);
> +  _Bool v2 = (a == 1);
> +  _Bool v3 = (a == 4);
> +  _Bool v4 = (a == 2);
> +  if (v1 || v2 || v3 || v4)
> +    foo ();
> +}
> +
> +void
> +f2 (int a)
> +{
> +  _Bool v1 = (a == 1);
> +  _Bool v2 = (a == 2);
> +  _Bool v3 = (a == 3);
> +  _Bool v4 = (a == 4);
> +  if (v1 || v2 || v3 || v4)
> +    foo ();
> +}
> +
> +void
> +f3 (unsigned int a)
> +{
> +  _Bool v1 = (a <= 31);
> +  _Bool v2 = (a >= 64 && a <= 95);
> +  _Bool v3 = (a >= 128 && a <= 159);
> +  _Bool v4 = (a >= 192 && a <= 223);
> +  if (v1 || v2 || v3 || v4)
> +    foo ();
> +}
> +
> +void
> +f4 (int a)
> +{
> +  _Bool v1 = (a == 3);
> +  _Bool v2 = (a == 1);
> +  _Bool v3 = (a == 4);
> +  _Bool v4 = (a == 2);
> +  _Bool v5 = (a == 7);
> +  _Bool v6 = (a == 5);
> +  _Bool v7 = (a == 8);
> +  _Bool v8 = (a == 6);
> +  if (v1 || v2 || v3 || v4 || v5 || v6 || v7 || v8)
> +    foo ();
> +}
> +
> +void
> +f5 (int a)
> +{
> +  _Bool v1 = (a != 3);
> +  _Bool v2 = (a != 1);
> +  _Bool v3 = (a != 4);
> +  _Bool v4 = (a != 2);
> +  _Bool v5 = (a != 7);
> +  _Bool v6 = (a != 5);
> +  _Bool v7 = (a != 8);
> +  _Bool v8 = (a != 6);
> +  if (v1 && v2 && v3 && v4 && v5 && v6 && v7 && v8)
> +    foo ();
> +}
> +
> +void
> +f6 (int a)
> +{
> +  _Bool v1 = (a != 3);
> +  _Bool v2 = (a != 1);
> +  _Bool v3 = (a != 4);
> +  _Bool v4 = (a != 2);
> +  _Bool v5 = (a != 7);
> +  _Bool v6 = (a != 5);
> +  _Bool v7 = (a != 8);
> +  _Bool v8 = (a != 6);
> +  if ((v1 && v2 && v3 && v4) && (v5 && v6 && v7 && v8))
> +    foo ();
> +}
> +
> +int
> +f7 (int a)
> +{
> +  _Bool v1 = (a == 3);
> +  _Bool v2 = (a == 1);
> +  _Bool v3 = (a == 4);
> +  _Bool v4 = (a == 2);
> +  _Bool v5 = (a == 7);
> +  _Bool v6 = (a == 5);
> +  _Bool v7 = (a == 8);
> +  _Bool v8 = (a == 6);
> +  return v1 || v2 || v3 || v4 || v5 || v6 || v7 || v8;
> +}
> +
> +_Bool
> +f8 (int a)
> +{
> +  _Bool v1 = (a == 3);
> +  _Bool v2 = (a == 1);
> +  _Bool v3 = (a == 4);
> +  _Bool v4 = (a == 2);
> +  _Bool v5 = (a == 7);
> +  _Bool v6 = (a == 5);
> +  _Bool v7 = (a == 8);
> +  _Bool v8 = (a == 6);
> +  return v1 || v2 || v3 || v4 || v5 || v6 || v7 || v8;
> +}
> +
> +int
> +f9 (int a)
> +{
> +  _Bool v1 = (a != 3);
> +  _Bool v2 = (a != 1);
> +  _Bool v3 = (a != 4);
> +  _Bool v4 = (a != 2);
> +  _Bool v5 = (a != 7);
> +  _Bool v6 = (a != 5);
> +  _Bool v7 = (a != 8);
> +  _Bool v8 = (a != 6);
> +  return v1 && v2 && v3 && v4 && v5 && v6 && v7 && v8;
> +}
> +
> +_Bool
> +f10 (int a)
> +{
> +  _Bool v1 = (a != 3);
> +  _Bool v2 = (a != 1);
> +  _Bool v3 = (a != 4);
> +  _Bool v4 = (a != 2);
> +  _Bool v5 = (a != 7);
> +  _Bool v6 = (a != 5);
> +  _Bool v7 = (a != 8);
> +  _Bool v8 = (a != 6);
> +  return v1 && v2 && v3 && v4 && v5 && v6 && v7 && v8;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "Optimizing range tests a_\[0-9\]*.D. 
> -.1, 1. and -.2, 2. and -.3, 3. and -.4, 4.\[\n\r\]* into" 2 "reassoc1" } } */
> +/* { dg-final { scan-tree-dump-times "Optimizing range tests a_\[0-9\]*.D. 
> -.0, 31. and -.64, 95.\[\n\r\]* into" 1 "reassoc1" } } */
> +/* { dg-final { scan-tree-dump-times "Optimizing range tests a_\[0-9\]*.D. 
> -.128, 159. and -.192, 223.\[\n\r\]* into" 1 "reassoc1" } } */
> +/* { dg-final { scan-tree-dump-times "Optimizing range tests a_\[0-9\]*.D. 
> -.1, 1. and -.2, 2. and -.3, 3. and -.4, 4. and -.5, 5. and -.6, 6. and -.7, 
> 7. and -.8, 8.\[\n\r\]* into" 7 "reassoc1" } } */
> +/* { dg-final { scan-tree-dump-times "Optimizing range tests 
> \[^\r\n\]*_\[0-9\]* -.0, 31. and -.128, 159.\[\n\r\]* into" 1 "reassoc2" } } 
> */
> +/* { dg-final { cleanup-tree-dump "reassoc1" } } */
> +/* { dg-final { cleanup-tree-dump "reassoc2" } } */
> --- gcc/testsuite/gcc.c-torture/execute/pr46309.c.jj    2012-10-26 
> 10:10:28.403238295 +0200
> +++ gcc/testsuite/gcc.c-torture/execute/pr46309.c       2012-10-26 
> 10:10:11.000000000 +0200
> @@ -0,0 +1,31 @@
> +/* PR tree-optimization/46309 */
> +
> +extern void abort (void);
> +
> +unsigned int *q;
> +
> +__attribute__((noinline, noclone)) void
> +bar (unsigned int *p)
> +{
> +  if (*p != 2 && *p != 3)
> +    (!(!(*q & 263) || *p != 1)) ? abort () : 0;
> +}
> +
> +int
> +main ()
> +{
> +  unsigned int x, y;
> +  asm volatile ("" : : : "memory");
> +  x = 2;
> +  bar (&x);
> +  x = 3;
> +  bar (&x);
> +  y = 1;
> +  x = 0;
> +  q = &y;
> +  bar (&x);
> +  y = 0;
> +  x = 1;
> +  bar (&x);
> +  return 0;
> +}
>
>         Jakub

Reply via email to