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.

Reply via email to