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 (); +}
