From: Stefan Schulze Frielinghaus <[email protected]>

Pattern *extzv_<mode>_sll<clobbercc_or_nocc> matches a left shift
followed by anding a contiguous bitmask which is supposed to be
implemented by instruction RISBG.  Vacated bits of a left shift are
zeroed whereas RISBG performs a left rotate, i.e., each bit shifted out
of the leftmost bit position is placed in the rightmost position of the
operand.  Thus, those bits are not necessarily zero.  However, the insn
does not adapt the bitmask in order to compensate for this.  For
example, the pattern matches

  r2 = (r3 << 1) & 255

 which leads to

  risbgn %r2,%r3,56,128+63,1

whereas expected is

  risbgn %r2,%r3,56,128+62,1

Since the bitmask isn't adjusted the end bit includes the supposedly
vacated bit which for RISBG means that this equals the highest bit
instead of being zero always.

So far combine was gentle enough to adjust the bitmask which covered
this up.  However, late_combine does not which is why this is being
exposed since r15-1579-g792f97b44ff.

A similar argument holds for *extzv_<mode>_srl<clobbercc_or_nocc>.

Fixed by adjusting the bitmasks for the output templates.

        PR target/126054

gcc/ChangeLog:

        * config/s390/s390.md: Fix
        extzv_<mode>_{srl,sll}<clobbercc_or_nocc> by adjusting the
        bitmasks in the output templates.

gcc/testsuite/ChangeLog:

        * gcc.target/s390/pr126054.c: New test.
---
 I will leave this in trunk for around two weeks and backport this to
 every open release branch since this fixes a latent bug.

 gcc/config/s390/s390.md                  | 33 +++++++++++++++++++++---
 gcc/testsuite/gcc.target/s390/pr126054.c | 19 ++++++++++++++
 2 files changed, 48 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/s390/pr126054.c

diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md
index 1d41b52ee83..746188530d7 100644
--- a/gcc/config/s390/s390.md
+++ b/gcc/config/s390/s390.md
@@ -8217,8 +8217,23 @@
   "<z10_or_zEC12_cond>
    /* Note that even for the SImode pattern, the rotate is always DImode.  */
    && s390_extzv_shift_ok (<bitsize>, -INTVAL (operands[2]),
-                          INTVAL (operands[3]))"
-  "<risbg_n>\t%0,%1,%<bfstart>3,128+%<bfend>3,64-%2"
+                          INTVAL (operands[3]))
+   /* Ensure that we end up with a non-zero bitmask.  */
+   && (<MODE>mode == DImode
+       ? ((UINTVAL (operands[3]) << UINTVAL (operands[2])) >> UINTVAL 
(operands[2])) > 0
+       : (((unsigned int)UINTVAL (operands[3]) << UINTVAL (operands[2])) >> 
UINTVAL (operands[2])) > 0)"
+{
+  unsigned HOST_WIDE_INT shiftamount = UINTVAL (operands[2]);
+  unsigned HOST_WIDE_INT bitmask = UINTVAL (operands[3]);
+  /* Clear the higher bits of the mask which overlap with the vacated bits of
+     the shift.  */
+  if (<MODE>mode == DImode)
+    bitmask = (bitmask << shiftamount) >> shiftamount;
+  else
+    bitmask = ((unsigned int)bitmask << shiftamount) >> shiftamount;
+  operands[3] = GEN_INT (bitmask);
+  return "<risbg_n>\t%0,%1,%<bfstart>3,128+%<bfend>3,64-%2";
+}
   [(set_attr "op_type" "RIE")
    (set_attr "z10prop" "z10_super_E1")])
 
@@ -8230,8 +8245,18 @@
                (match_operand:GPR 3 "contiguous_bitmask_nowrap_operand" "")))]
   "<z10_or_zEC12_cond>
    && s390_extzv_shift_ok (<bitsize>, INTVAL (operands[2]),
-                          INTVAL (operands[3]))"
-  "<risbg_n>\t%0,%1,%<bfstart>3,128+%<bfend>3,%2"
+                          INTVAL (operands[3]))
+   /* Ensure that we end up with a non-zero bitmask.  */
+   && ((UINTVAL (operands[3]) >> UINTVAL (operands[2])) << UINTVAL 
(operands[2])) > 0"
+{
+  unsigned HOST_WIDE_INT shiftamount = UINTVAL (operands[2]);
+  unsigned HOST_WIDE_INT bitmask = UINTVAL (operands[3]);
+  /* Clear the lower bits of the mask which overlap with the vacated bits of
+     the shift.  */
+  bitmask = (bitmask >> shiftamount) << shiftamount;
+  operands[3] = GEN_INT (bitmask);
+  return "<risbg_n>\t%0,%1,%<bfstart>3,128+%<bfend>3,%2";
+}
   [(set_attr "op_type" "RIE")
    (set_attr "z10prop" "z10_super_E1")])
 
diff --git a/gcc/testsuite/gcc.target/s390/pr126054.c 
b/gcc/testsuite/gcc.target/s390/pr126054.c
new file mode 100644
index 00000000000..3b8325da888
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/pr126054.c
@@ -0,0 +1,19 @@
+/* { dg-do run { target int128 } } */
+/* { dg-options "-O2" } */
+
+int a = 255, b;
+long c = 1, d;
+long *e = &d;
+int main() {
+  long f;
+  unsigned char g;
+  __int128 h = -10;
+  f = (long)h * (unsigned)(c | 56);
+  for (; b;)
+    ;
+  g = (unsigned char) f;
+  *e = 2540LL ^ g;
+  a = 16777215 ^ a ^ d & 255;
+  if ((a ^ (int) 4294967295) != 0xFF0000D5)
+    __builtin_abort ();
+}
-- 
2.54.0

Reply via email to