https://gcc.gnu.org/g:5c55e923bcf191dbfdf65467b04e21c27adba1d6

commit r17-2057-g5c55e923bcf191dbfdf65467b04e21c27adba1d6
Author: Stefan Schulze Frielinghaus <[email protected]>
Date:   Wed Jul 1 15:10:28 2026 +0200

    s390: Fix *extzv_<mode>_{srl,sll}<clobbercc_or_nocc> [PR126054]
    
    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.

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

diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md
index 1d41b52ee834..746188530d79 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 000000000000..3b8325da8889
--- /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 ();
+}

Reply via email to