https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120074

--- Comment #6 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The releases/gcc-15 branch has been updated by Jakub Jelinek
<ja...@gcc.gnu.org>:

https://gcc.gnu.org/g:a14d65f81e18e70144ceddfc3142a8103984919d

commit r15-9625-ga14d65f81e18e70144ceddfc3142a8103984919d
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Tue May 6 13:00:10 2025 +0200

    gimple-fold: Fix fold_truth_andor_for_ifcombine [PR120074]

    The following testcase ICEs because of a mismatch between wide_int
    precision, in particular lr_and_mask has 32-bit precision while sign has
    16-bit.

    decode_field_reference ensures that {ll,lr,rl,rr}_and_mask has
    {ll,lr,rl,rr}_bitsize precision, so the
            ll_and_mask |= sign;
    and
            rl_and_mask |= sign;
    and
            ll_and_mask &= sign;
    and
            rl_and_mask &= sign;
    cases should work right, sign has in those cases {ll,rl}_bitsize
    precision.  The problem is that nothing until much later guarantees
    that ll_bitsize == lr_bitsize or rl_bitsize == rr_bitsize.
    In the testcase there is
    ((b ^ a) & 3) < 0
    where a is 16-bit and b is 32-bit, so it is the lsignbit handling,
    and because of the xor the xor operand is moved to the *r_and_mask, so
    with ll_and_mask being 16-bit 3 and lr_and_mask being 32-bit 3.

    Now, either b in the above case would be INTEGER_CST, in that case
    if rr_arg was also INTEGER_CST we'd use the l_const && r_const case
    and try to handle it, or we'd run into (though much later)
          if (ll_bitsize != lr_bitsize || rl_bitsize != rr_bitsize
    ...
            return 0;

    One possibility is dealing with a different precision using wide_int::from.

    Another option used in this patch as it is safest is
    +         if (ll_bitsize != lr_bitsize)
    +           return 0;
              if (!lr_and_mask.get_precision ())
                lr_and_mask = sign;
              else
                lr_and_mask &= sign;
    and similarly in the other hunk, i.e. punt if there is a mismatch
    early.

    And yet another option would be to compute
    the sign
          wide_int sign = wi::mask (ll_bitsize - 1, true, ll_bitsize);
          /* If ll_arg is zero-extended and we're testing the sign bit, we know
             what the result should be.  Shifting the sign bit out of sign will
get
             us to mask the entire field out, yielding zero, i.e., the sign bit
of
             the zero-extended value.  We know the masked value is being
compared
             with zero, so the compare will get us the result we're looking
             for: TRUE if EQ_EXPR, FALSE if NE_EXPR.  */
          if (lsignbit > ll_bitsize && ll_unsignedp)
            sign <<= 1;
    once again for the lr_and_mask and rr_and_mask cases using rl_bitsize.

    As we just return 0; anyway unless l_const && r_const, if l_const & r_const
    are false it doesn't really matter what is chosen, but for the const
    cases it matters and I'm not sure what is right.  So the second option
    might be safest.

    2025-05-06  Jakub Jelinek  <ja...@redhat.com>

            PR tree-optimization/120074
            * gimple-fold.cc (fold_truth_andor_for_ifcombine): For
            lsignbit && l_xor case, punt if ll_bitsize != lr_bitsize. 
Similarly
            for rsignbit && r_xor case, punt if rl_bitsize != rr_bitsize.
            Formatting fix.

            * gcc.dg/pr120074.c: New test.

    (cherry picked from commit 81475602c3dd57ff6987e5f902814e8e3a0a0dde)

Reply via email to