https://gcc.gnu.org/g:ae0bd821107ba30ea58c7abc075e15a004180bc4

commit ae0bd821107ba30ea58c7abc075e15a004180bc4
Author: Alexandre Oliva <ol...@adacore.com>
Date:   Tue Dec 17 22:18:43 2024 -0300

    ifcombine field merge: handle bitfield zero tests in range tests
    
    Some bitfield compares with zero are optimized to range tests, so
    instead of X & ~(Bit - 1) != 0 what reaches ifcombine is X > (Bit - 1),
    where Bit is a power of two and X is unsigned.
    
    This patch recognizes this optimized form of masked compares, and
    attempts to merge them like masked compares, which enables some more
    field merging that a folder version of fold_truth_andor used to handle
    without additional effort.
    
    I haven't seen X & ~(Bit - 1) == 0 become X <= (Bit - 1), or X < Bit
    for that matter, but it was easy enough to handle the former
    symmetrically to the above.
    
    The latter was also easy enough, and so was its symmetric, X >= Bit,
    that is handled like X & ~(Bit - 1) != 0.
    
    
    for  gcc/ChangeLog
    
            * gimple-fold.cc (decode_field_reference): Accept incoming
            mask.
            (fold_truth_andor_for_ifcombine): Handle some compares with
            powers of two, minus 1 or 0, like masked compares with zero.
    
    for  gcc/testsuite/ChangeLog
    
            * gcc.dg/field-merge-15.c: New.

Diff:
---
 gcc/gimple-fold.cc                    | 71 +++++++++++++++++++++++++++++++++--
 gcc/testsuite/gcc.dg/field-merge-15.c | 36 ++++++++++++++++++
 2 files changed, 104 insertions(+), 3 deletions(-)

diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 74273b83cf81..ee1da47b080b 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -7509,8 +7509,10 @@ gimple_binop_def_p (enum tree_code code, tree t, tree 
op[2])
 
    *PREVERSEP is set to the storage order of the field.
 
-   *PAND_MASK is set to the mask found in a BIT_AND_EXPR, if any.
-   If PAND_MASK *is NULL, BIT_AND_EXPR is not recognized.
+   *PAND_MASK is set to the mask found in a BIT_AND_EXPR, if any.  If
+   PAND_MASK *is NULL, BIT_AND_EXPR is not recognized.  If *PAND_MASK
+   is initially set to a mask with nonzero precision, that mask is
+   combined with the found mask, or adjusted in precision to match.
 
    *XOR_P is to be FALSE if EXP might be a XOR used in a compare, in which
    case, if XOR_CMP_OP is a zero constant, it will be overridden with *PEXP,
@@ -7565,14 +7567,30 @@ decode_field_reference (tree *pexp, HOST_WIDE_INT 
*pbitsize,
       exp = res_ops[0];
     }
 
-  /* Recognize and save a masking operation.  */
+  /* Recognize and save a masking operation.  Combine it with an
+     incoming mask.  */
   if (pand_mask && gimple_binop_def_p (BIT_AND_EXPR, exp, res_ops)
       && uniform_integer_cst_p (res_ops[1]))
     {
       loc[1] = gimple_location (SSA_NAME_DEF_STMT (exp));
       exp = res_ops[0];
       and_mask = wi::to_wide (res_ops[1]);
+      unsigned prec_in = pand_mask->get_precision ();
+      if (prec_in)
+       {
+         unsigned prec_op = and_mask.get_precision ();
+         if (prec_in >= prec_op)
+           {
+             if (prec_in > prec_op)
+               and_mask = wide_int::from (and_mask, prec_in, UNSIGNED);
+             and_mask &= *pand_mask;
+           }
+         else
+           and_mask &= wide_int::from (*pand_mask, prec_op, UNSIGNED);
+       }
     }
+  else if (pand_mask)
+    and_mask = *pand_mask;
 
   /* Turn (a ^ b) [!]= 0 into a [!]= b.  */
   if (xor_p && gimple_binop_def_p (BIT_XOR_EXPR, exp, res_ops)
@@ -8023,6 +8041,8 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree 
truth_type,
       return 0;
     }
 
+  /* Prepare to turn compares of signed quantities with zero into
+     sign-bit tests.  */
   bool lsignbit = false, rsignbit = false;
   if ((lcode == LT_EXPR || lcode == GE_EXPR)
       && integer_zerop (lr_arg)
@@ -8032,6 +8052,31 @@ fold_truth_andor_for_ifcombine (enum tree_code code, 
tree truth_type,
       lsignbit = true;
       lcode = (lcode == LT_EXPR ? NE_EXPR : EQ_EXPR);
     }
+  /* Turn compares of unsigned quantities with powers of two into
+     equality tests of masks.  */
+  else if ((lcode == LT_EXPR || lcode == GE_EXPR)
+          && INTEGRAL_TYPE_P (TREE_TYPE (ll_arg))
+          && TYPE_UNSIGNED (TREE_TYPE (ll_arg))
+          && uniform_integer_cst_p (lr_arg)
+          && wi::popcount (wi::to_wide (lr_arg)) == 1)
+    {
+      ll_and_mask = ~(wi::to_wide (lr_arg) - 1);
+      lcode = (lcode == GE_EXPR ? NE_EXPR : EQ_EXPR);
+      lr_arg = wide_int_to_tree (TREE_TYPE (ll_arg), ll_and_mask * 0);
+    }
+  /* Turn compares of unsigned quantities with powers of two minus one
+     into equality tests of masks.  */
+  else if ((lcode == LE_EXPR || lcode == GT_EXPR)
+          && INTEGRAL_TYPE_P (TREE_TYPE (ll_arg))
+          && TYPE_UNSIGNED (TREE_TYPE (ll_arg))
+          && uniform_integer_cst_p (lr_arg)
+          && wi::popcount (wi::to_wide (lr_arg) + 1) == 1)
+    {
+      ll_and_mask = ~wi::to_wide (lr_arg);
+      lcode = (lcode == GT_EXPR ? NE_EXPR : EQ_EXPR);
+      lr_arg = wide_int_to_tree (TREE_TYPE (ll_arg), ll_and_mask * 0);
+    }
+  /* Likewise for the second compare.  */
   if ((rcode == LT_EXPR || rcode == GE_EXPR)
       && integer_zerop (rr_arg)
       && INTEGRAL_TYPE_P (TREE_TYPE (rl_arg))
@@ -8040,6 +8085,26 @@ fold_truth_andor_for_ifcombine (enum tree_code code, 
tree truth_type,
       rsignbit = true;
       rcode = (rcode == LT_EXPR ? NE_EXPR : EQ_EXPR);
     }
+  else if ((rcode == LT_EXPR || rcode == GE_EXPR)
+          && INTEGRAL_TYPE_P (TREE_TYPE (rl_arg))
+          && TYPE_UNSIGNED (TREE_TYPE (rl_arg))
+          && uniform_integer_cst_p (rr_arg)
+          && wi::popcount (wi::to_wide (rr_arg)) == 1)
+    {
+      rl_and_mask = ~(wi::to_wide (rr_arg) - 1);
+      rcode = (rcode == GE_EXPR ? NE_EXPR : EQ_EXPR);
+      rr_arg = wide_int_to_tree (TREE_TYPE (rl_arg), rl_and_mask * 0);
+    }
+  else if ((rcode == LE_EXPR || rcode == GT_EXPR)
+          && INTEGRAL_TYPE_P (TREE_TYPE (rl_arg))
+          && TYPE_UNSIGNED (TREE_TYPE (rl_arg))
+          && uniform_integer_cst_p (rr_arg)
+          && wi::popcount (wi::to_wide (rr_arg) + 1) == 1)
+    {
+      rl_and_mask = ~wi::to_wide (rr_arg);
+      rcode = (rcode == GT_EXPR ? NE_EXPR : EQ_EXPR);
+      rr_arg = wide_int_to_tree (TREE_TYPE (rl_arg), rl_and_mask * 0);
+    }
 
   /* See if the comparisons can be merged.  Then get all the parameters for
      each side.  */
diff --git a/gcc/testsuite/gcc.dg/field-merge-15.c 
b/gcc/testsuite/gcc.dg/field-merge-15.c
new file mode 100644
index 000000000000..34641e893c92
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-15.c
@@ -0,0 +1,36 @@
+/* { dg-do run } */
+/* { dg-options "-O -fdump-tree-ifcombine-details" } */
+
+/* Check that bitfield compares-with-zero turned into GT and LE compares with
+   powers-of-two minus 1 are optimized.  */
+
+struct s {
+  short a : sizeof (short) * __CHAR_BIT__ - 3;
+  short b : 3;
+  short c : 3;
+  short d : sizeof (short) * __CHAR_BIT__ - 3;
+} __attribute__ ((aligned (4)));
+
+struct s p = { 15, 7, 3, 1 };
+struct s q = { 0, 0, 0, 0 };
+
+void f ()
+{
+  if (p.a || p.b || p.c || p.d)
+    return;
+  __builtin_abort ();
+}
+
+void g ()
+{
+  if (q.a || q.b || q.c || q.d)
+    __builtin_abort ();
+}
+
+int main () {
+  f ();
+  g ();
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "optimizing" 6 "ifcombine" } } */

Reply via email to