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

commit cf2e0f411bea79746e2f399bd5642ba0861a6d89
Author: Alexandre Oliva <ol...@adacore.com>
Date:   Thu Feb 13 07:36:04 2025 -0300

    [ifcombine] cope with signbit tests of extended values
    
    A compare with zero may be taken as a sign bit test by
    fold_truth_andor_for_ifcombine, but the operand may be extended from a
    narrower field.  If the operand was narrower, the bitsize will reflect
    the narrowing conversion, but if it was wider, we'll only know whether
    the field is sign- or zero-extended from unsignedp, but we won't know
    whether it needed to be extended, because arg will have changed to the
    narrower variable when we get to the point in which we can compute the
    arg width.  If it's sign-extended, we're testing the right bit, but if
    it's zero-extended, there isn't any bit we can test.
    
    Instead of punting and leaving the foldable compare to be figured out
    by another pass, arrange for the sign bit resulting from the widening
    zero-extension to be taken as zero, so that the modified compare will
    yield the desired result.
    
    While at that, avoid swapping the right-hand compare operands when
    we've already determined that it was a signbit test: it no use to even
    try.
    
    
    for  gcc/ChangeLog
    
            PR tree-optimization/118805
            * gimple-fold.cc (fold_truth_andor_for_combine): Detect and
            cope with zero-extension in signbit tests.  Reject swapping
            right-compare operands if rsignbit.
    
    for  gcc/testsuite/ChangeLog
    
            PR tree-optimization/118805
            * gcc.dg/field-merge-26.c: New.

Diff:
---
 gcc/gimple-fold.cc                    | 22 +++++++++++++++++-----
 gcc/testsuite/gcc.dg/field-merge-26.c | 20 ++++++++++++++++++++
 2 files changed, 37 insertions(+), 5 deletions(-)

diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 29191685a43c..90b0ec6d79a3 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -8090,14 +8090,16 @@ fold_truth_andor_for_ifcombine (enum tree_code code, 
tree truth_type,
 
   /* Prepare to turn compares of signed quantities with zero into sign-bit
      tests.  We need not worry about *_reversep here for these compare
-     rewrites: loads will have already been reversed before compares.  */
-  bool lsignbit = false, rsignbit = false;
+     rewrites: loads will have already been reversed before compares.  Save the
+     precision, because [lr]l_arg may change and we won't be able to tell how
+     wide it was originally.  */
+  unsigned lsignbit = 0, rsignbit = 0;
   if ((lcode == LT_EXPR || lcode == GE_EXPR)
       && integer_zerop (lr_arg)
       && INTEGRAL_TYPE_P (TREE_TYPE (ll_arg))
       && !TYPE_UNSIGNED (TREE_TYPE (ll_arg)))
     {
-      lsignbit = true;
+      lsignbit = TYPE_PRECISION (TREE_TYPE (ll_arg));
       lcode = (lcode == LT_EXPR ? NE_EXPR : EQ_EXPR);
     }
   /* Turn compares of unsigned quantities with powers of two into
@@ -8130,7 +8132,7 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree 
truth_type,
       && INTEGRAL_TYPE_P (TREE_TYPE (rl_arg))
       && !TYPE_UNSIGNED (TREE_TYPE (rl_arg)))
     {
-      rsignbit = true;
+      rsignbit = TYPE_PRECISION (TREE_TYPE (rl_arg));
       rcode = (rcode == LT_EXPR ? NE_EXPR : EQ_EXPR);
     }
   else if ((rcode == LT_EXPR || rcode == GE_EXPR)
@@ -8204,7 +8206,7 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree 
truth_type,
       || ! operand_equal_p (ll_inner, rl_inner, 0))
     {
       /* Try swapping the operands.  */
-      if (ll_reversep != rr_reversep
+      if (ll_reversep != rr_reversep || rsignbit
          || !operand_equal_p (ll_inner, rr_inner, 0))
        return 0;
 
@@ -8284,6 +8286,14 @@ fold_truth_andor_for_ifcombine (enum tree_code code, 
tree truth_type,
   if (lsignbit)
     {
       wide_int sign = wi::mask (ll_bitsize - 1, true, ll_bitsize);
+      /* If ll_arg is zero-extended and we're testing the sign bit, we know
+        what the result should be.  Shifting the sign bit out of sign will get
+        us to mask the entire field out, yielding zero, i.e., the sign bit of
+        the zero-extended value.  We know the masked value is being compared
+        with zero, so the compare will get us the result we're looking
+        for: TRUE if EQ_EXPR, FALSE if NE_EXPR.  */
+      if (lsignbit > ll_bitsize && ll_unsignedp)
+       sign <<= 1;
       if (!ll_and_mask.get_precision ())
        ll_and_mask = sign;
       else
@@ -8303,6 +8313,8 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree 
truth_type,
   if (rsignbit)
     {
       wide_int sign = wi::mask (rl_bitsize - 1, true, rl_bitsize);
+      if (rsignbit > rl_bitsize)
+       sign <<= 1;
       if (!rl_and_mask.get_precision ())
        rl_and_mask = sign;
       else
diff --git a/gcc/testsuite/gcc.dg/field-merge-26.c 
b/gcc/testsuite/gcc.dg/field-merge-26.c
new file mode 100644
index 000000000000..b37607391a52
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-26.c
@@ -0,0 +1,20 @@
+/* { dg-do run } */
+/* { dg-options "-O1 -fno-tree-ccp -fno-tree-copy-prop -fno-tree-forwprop 
-fno-tree-fre" } */
+
+/* PR tree-optimization/118805 */
+
+/* Test that ifcombine doesn't get confused by tests for the sign bit of
+   extended values that would normally be folded before.  */
+
+unsigned char a = 255;
+int b;
+int main() {
+  int c = 0;
+  if (c > a && a >= 255)
+    __builtin_abort ();
+  if (c <= a && a == -1)
+    __builtin_abort ();
+  if (a < c || a != 255)
+    __builtin_abort ();
+  return 0;
+}

Reply via email to