Hi! 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; The following patch fixes it by dealing with a different precision using wide_int::from. The other option would be + 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. 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. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk or shall I go with the second option? 2025-05-05 Jakub Jelinek <ja...@redhat.com> PR tree-optimization/120074 * gimple-fold.cc (fold_truth_andor_for_ifcombine): Use wide_int::from when trying to mask ?r_and_mask with sign in case it has different precision. Formatting fix. * gcc.dg/pr120074.c: New test. --- gcc/gimple-fold.cc.jj 2025-04-21 17:04:48.000000000 +0200 +++ gcc/gimple-fold.cc 2025-05-03 12:43:29.643908582 +0200 @@ -8337,7 +8337,8 @@ fold_truth_andor_for_ifcombine (enum tre if (!lr_and_mask.get_precision ()) lr_and_mask = sign; else - lr_and_mask &= sign; + lr_and_mask &= wide_int::from (sign, lr_and_mask.get_precision (), + UNSIGNED); if (l_const.get_precision ()) l_const &= wide_int::from (lr_and_mask, l_const.get_precision (), UNSIGNED); @@ -8358,7 +8359,8 @@ fold_truth_andor_for_ifcombine (enum tre if (!rr_and_mask.get_precision ()) rr_and_mask = sign; else - rr_and_mask &= sign; + rr_and_mask &= wide_int::from (sign, rr_and_mask.get_precision (), + UNSIGNED); if (r_const.get_precision ()) r_const &= wide_int::from (rr_and_mask, r_const.get_precision (), UNSIGNED); @@ -8762,7 +8764,7 @@ fold_truth_andor_for_ifcombine (enum tre wide_int lr_mask, rr_mask; if (lr_and_mask.get_precision ()) lr_mask = wi::lshift (wide_int::from (lr_and_mask, rnprec, UNSIGNED), - xlr_bitpos); + xlr_bitpos); else lr_mask = wi::shifted_mask (xlr_bitpos, lr_bitsize, false, rnprec); if (rr_and_mask.get_precision ()) --- gcc/testsuite/gcc.dg/pr120074.c.jj 2025-05-03 13:55:45.374319266 +0200 +++ gcc/testsuite/gcc.dg/pr120074.c 2025-05-03 13:54:53.264995823 +0200 @@ -0,0 +1,20 @@ +/* PR tree-optimization/120074 */ +/* { dg-do compile } */ +/* { dg-options "-O1 -fno-tree-copy-prop -fno-tree-forwprop -fno-tree-ccp" } */ + +int foo (int); +short a; +int b; + +int +bar (int d, int e) +{ + return d < 0 || d > __INT_MAX__ >> e; +} + +int +main () +{ + int f = bar ((b ^ a) & 3, __SIZEOF_INT__ * __CHAR_BIT__ - 2); + foo (f); +} Jakub