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.

Regstrapped on x86_64-linux-gnu.  Ok to install?


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.
---
 gcc/gimple-fold.cc                    |   22 +++++++++++++++++-----
 gcc/testsuite/gcc.dg/field-merge-26.c |   20 ++++++++++++++++++++
 2 files changed, 37 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/field-merge-26.c

diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 29191685a43c5..0380c7af4c213 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 && ll_unsignedp)
+       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 0000000000000..96d7e7205c5f2
--- /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 == 254)
+    __builtin_abort ();
+  if (a < c || a != 255)
+    __builtin_abort ();
+  return 0;
+}


-- 
Alexandre Oliva, happy hacker            https://FSFLA.org/blogs/lxo/
   Free Software Activist                   GNU Toolchain Engineer
More tolerance and less prejudice are key for inclusion and diversity
Excluding neuro-others for not behaving ""normal"" is *not* inclusive

Reply via email to