From a8302d2a4669984c7c287d12ef5b37cde6699c80 Mon Sep 17 00:00:00 2001
From: Jakub Jelinek <jakub@redhat.com>
Date: Tue, 9 May 2023 12:10:07 +0200
Subject: [PATCH] tree-ssa-ccp, wide-int: Fix up handling of [LR]ROTATE_EXPR in
 bitwise ccp [PR109778]

The following testcase is miscompiled, because bitwise ccp2 handles
a rotate with a signed type incorrectly.
Seems tree-ssa-ccp.cc has the only callers of wi::[lr]rotate with 3
arguments, all other callers just rotate in the right precision and
I think work correctly.  ccp works with widest_ints and so rotations
by the excessive precision certainly don't match what it wants
when it sees a rotate in some specific bitsize.  Still, if it is
unsigned rotate and the widest_int is zero extended from width,
the functions perform left shift and logical right shift on the value
and then at the end zero extend the result of left shift and uselessly
also the result of logical right shift and return | of that.
On the testcase we the signed char rrotate by 4 argument is
CONSTANT -75 i.e. 0xffffffff....fffffb5 with mask 2.
The mask is correctly rotated to 0x20, but because the 8-bit constant
is sign extended to 192-bit one, the logical right shift by 4 doesn't
yield expected 0xb, but gives 0xfffffffffff....ffffb, and then
return wi::zext (left, width) | wi::zext (right, width); where left is
0xfffffff....fb50, so we return 0xfb instead of the expected
0x5b.

The following patch fixes that by doing the zero extension in case of
the right variable before doing wi::lrshift rather than after it.

Also, wi::[lr]rotate widht width < precision always zero extends
the result.  I'm afraid it can't do better because it doesn't know
if it is done for an unsigned or signed type, but the caller in this
case knows that very well, so I've done the extension based on sgn
in the caller.  E.g. 0x5b rotated right (or left) by 4 with width 8
previously gave 0xb5, but sgn == SIGNED in widest_int it should be
0xffffffff....fffb5 instead.

2023-05-09  Jakub Jelinek  <jakub@redhat.com>

	PR tree-optimization/109778
	* wide-int.h (wi::lrotate, wi::rrotate): Call wi::lrshift on
	wi::zext (x, width) rather than x if width != precision, rather
	than using wi::zext (right, width) after the shift.
	* tree-ssa-ccp.cc (bit_value_binop): Call wi::ext on the results
	of wi::lrotate or wi::rrotate.

	* gcc.c-torture/execute/pr109778.c: New test.
---
 .../gcc.c-torture/execute/pr109778.c          | 26 +++++++++++++++++++
 gcc/tree-ssa-ccp.cc                           |  6 +++--
 gcc/wide-int.h                                | 11 +++++---
 3 files changed, 37 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/gcc.c-torture/execute/pr109778.c

diff --git a/gcc/testsuite/gcc.c-torture/execute/pr109778.c b/gcc/testsuite/gcc.c-torture/execute/pr109778.c
new file mode 100644
index 000000000000..309fbf413e01
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr109778.c
@@ -0,0 +1,26 @@
+/* PR tree-optimization/109778 */
+
+int a, b, c, d, *e = &c;
+
+static inline unsigned
+foo (unsigned char x)
+{
+  x = 1 | x << 1;
+  x = x >> 4 | x << 4;
+  return x;
+}
+
+static inline void
+bar (unsigned x)
+{
+  *e = 8 > foo (x + 86) - 86;
+}
+
+int
+main ()
+{
+  d = a && b;
+  bar (d + 4);
+  if (c != 1)
+    __builtin_abort ();
+}
diff --git a/gcc/tree-ssa-ccp.cc b/gcc/tree-ssa-ccp.cc
index 03a984f2adfd..2e552b330b74 100644
--- a/gcc/tree-ssa-ccp.cc
+++ b/gcc/tree-ssa-ccp.cc
@@ -1552,6 +1552,8 @@ bit_value_binop (enum tree_code code, signop sgn, int width,
 		  *mask = wi::lrotate (r1mask, shift, width);
 		  *val = wi::lrotate (r1val, shift, width);
 		}
+	      *mask = wi::ext (*mask, width, sgn);
+	      *val = wi::ext (*val, width, sgn);
 	    }
 	}
       else if (wi::ltu_p (r2val | r2mask, width)
@@ -1593,8 +1595,8 @@ bit_value_binop (enum tree_code code, signop sgn, int width,
 	      /* Accumulate the result.  */
 	      res_mask |= tmp_mask | (res_val ^ tmp_val);
 	    }
-	  *val = wi::bit_and_not (res_val, res_mask);
-	  *mask = res_mask;
+	  *val = wi::ext (wi::bit_and_not (res_val, res_mask), width, sgn);
+	  *mask = wi::ext (res_mask, width, sgn);
 	}
       break;
 
diff --git a/gcc/wide-int.h b/gcc/wide-int.h
index 6be343c0eb59..3d9b87cd91a1 100644
--- a/gcc/wide-int.h
+++ b/gcc/wide-int.h
@@ -3187,9 +3187,11 @@ wi::lrotate (const T1 &x, const T2 &y, unsigned int width)
     width = precision;
   WI_UNARY_RESULT (T2) ymod = umod_trunc (y, width);
   WI_UNARY_RESULT (T1) left = wi::lshift (x, ymod);
-  WI_UNARY_RESULT (T1) right = wi::lrshift (x, wi::sub (width, ymod));
+  WI_UNARY_RESULT (T1) right
+    = wi::lrshift (width != precision ? wi::zext (x, width) : x,
+		   wi::sub (width, ymod));
   if (width != precision)
-    return wi::zext (left, width) | wi::zext (right, width);
+    return wi::zext (left, width) | right;
   return left | right;
 }
 
@@ -3204,10 +3206,11 @@ wi::rrotate (const T1 &x, const T2 &y, unsigned int width)
   if (width == 0)
     width = precision;
   WI_UNARY_RESULT (T2) ymod = umod_trunc (y, width);
-  WI_UNARY_RESULT (T1) right = wi::lrshift (x, ymod);
+  WI_UNARY_RESULT (T1) right
+    = wi::lrshift (width != precision ? wi::zext (x, width) : x, ymod);
   WI_UNARY_RESULT (T1) left = wi::lshift (x, wi::sub (width, ymod));
   if (width != precision)
-    return wi::zext (left, width) | wi::zext (right, width);
+    return wi::zext (left, width) | right;
   return left | right;
 }
 
-- 
GitLab