https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128597
--- Comment #3 from GCC Commits <cvs-commit at gcc dot gnu.org> --- The master branch has been updated by Richard Biener <[email protected]>: https://gcc.gnu.org/g:6c4ee1b2ba2330ab71c0f9fbfe146d5119afb290 commit r17-6122-g6c4ee1b2ba2330ab71c0f9fbfe146d5119afb290 Author: Richard Biener <[email protected]> Date: Tue Nov 10 10:19:36 2026 +0100 tree-optimization/128597 - mask bits dropped by narrowing as sign tests decode_field_reference sets *PSIGNBIT when the AND mask, before being clipped to the field width, has bits that would be extensions of the sign bit of a signed field. fold_truth_andor_for_ifcombine then adds the sign bit of the field to the test mask. That's correct when the mask applies to a sign-extended field, as in (int) s & M, but not when the mask applies before a narrowing conversion, as in (short) (x & M): there the upper bits of M select bits of X that the conversion discards, and they say nothing about the sign bit of the narrowed value. For instance, given int t = x & 0xc7ff3906; short s = t; if (s != 0) return 0; if (t < 0) return 0; return 1; ifcombine combined the two tests into (x & 0x8000b906) == 0 instead of (x & 0x80003906) == 0, because the nonzero upper half of the mask caused bit 15 to be added to it. We can't just ignore all upper bits of such masks, though: if the masked value was shifted right, as in (short) ((x >> 20) & 0x1006), the field is narrower than the conversion, and mask bits between the field width and the precision of the narrow type, such as bit 12 here, survive the conversion and do test extensions of the sign bit. Fix this by truncating the mask to the precision of the narrow type when the mask is found after a narrowing conversion was dropped, so that only the bits discarded by the conversion are ignored when computing SIGNBIT. That makes SIGNBIT -1 in cases such as (short) ((x >> 20) & 0xffff0806) >= 0, where it used to be 1 and the combination was rejected. This exposes a latent problem, also present without narrowing, as in ((x >> 20) & 0x0806) >= 0: when turning a sign test of a signed field into a bit test, fold_truth_andor_for_ifcombine tested the sign bit of the field, bit 11 here, even though the mask clears all the extension bits, so that the masked value can't be negative. Treat such fields like zero-extended ones, as we already do when checking constants. The same holds when comparing two fields: a signed field whose mask clears all the extension bits is effectively zero-extended, so it can't be compared with a sign-extended field, as in (short) ((x >> 20) & 0xffff0fff) == (short) (y >> 20) or, without narrowing, ((x >> 20) & 0xfff) == (y >> 20), by comparing the 12-bit fields only, because that drops the requirement that bit 11 of y be clear. The latter was already miscompiled, the former became exposed with the mask truncation. Compare whether the fields are zero-extended, rather than just their signedness, when combining compares of non-constant operands. PR tree-optimization/128597 * gimple-fold.cc (decode_field_reference): Truncate a mask applied before a narrowing conversion to the precision of the narrow type. (fold_truth_andor_for_ifcombine): Don't test the sign bit of a field whose extension bits are all masked out in sign tests. Reject combining compares of a zero-extended field with a sign-extended one, considering fields whose extension bits are masked out as zero-extended. * gcc.dg/torture/pr128597-1.c: New testcase. * gcc.dg/torture/pr128597-2.c: New testcase. * gcc.dg/pr128597-3.c: New testcase. * gcc.dg/pr128597-4.c: New testcase. * gcc.dg/pr128597-5.c: New testcase.
