https://gcc.gnu.org/g:96292bfbef1913981df899b792a8e784363c9a43
commit 96292bfbef1913981df899b792a8e784363c9a43 Author: Alexandre Oliva <ol...@adacore.com> Date: Thu Jan 23 02:51:41 2025 -0300 [ifcombine] check for more zero-extension cases [PR118572] When comparing a signed narrow variable with a wider constant that has the bit corresponding to the variable's sign bit set, we would check that the constant is a sign-extended from that sign bit, and conclude that the compare fails if it isn't. When the signed variable is masked without getting the signbit variable set, or when the sign bit itself is masked out, we know the sign-extension bits from the extended variable are going to be zero, so the constant will only compare equal if it is zero- rather than sign-extended from the narrow variable's precision, so check that it satisfies this property, and yield a false compare result otherwise. for gcc/ChangeLog PR tree-optimization/118572 * gimple-fold.cc (fold_truth_andor_for_ifcombine): Compare as unsigned the variables whose extension bits are masked out. for gcc/testsuite/ChangeLog PR tree-optimization/118572 * gcc.dg/field-merge-24.c: New. Diff: --- gcc/gimple-fold.cc | 20 ++++++++++++++++--- gcc/testsuite/gcc.dg/field-merge-24.c | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc index cd9d35034918..13541fe1ef74 100644 --- a/gcc/gimple-fold.cc +++ b/gcc/gimple-fold.cc @@ -8552,12 +8552,21 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, { /* Before clipping upper bits of the right-hand operand of the compare, check that they're sign or zero extensions, depending on how the - left-hand operand would be extended. */ + left-hand operand would be extended. If it is unsigned, or if there's + a mask that zeroes out extension bits, whether because we've checked + for upper bits in the mask and did not set ll_signbit, or because the + sign bit itself is masked out, check that the right-hand operand is + zero-extended. */ bool l_non_ext_bits = false; if (ll_bitsize < lr_bitsize) { wide_int zext = wi::zext (l_const, ll_bitsize); - if ((ll_unsignedp ? zext : wi::sext (l_const, ll_bitsize)) == l_const) + if ((ll_unsignedp + || (ll_and_mask.get_precision () + && (!ll_signbit + || ((ll_and_mask & wi::mask (ll_bitsize - 1, true, ll_bitsize)) + == 0))) + ? zext : wi::sext (l_const, ll_bitsize)) == l_const) l_const = zext; else l_non_ext_bits = true; @@ -8583,7 +8592,12 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, if (rl_bitsize < rr_bitsize) { wide_int zext = wi::zext (r_const, rl_bitsize); - if ((rl_unsignedp ? zext : wi::sext (r_const, rl_bitsize)) == r_const) + if ((rl_unsignedp + || (rl_and_mask.get_precision () + && (!rl_signbit + || ((rl_and_mask & wi::mask (rl_bitsize - 1, true, rl_bitsize)) + == 0))) + ? zext : wi::sext (r_const, rl_bitsize)) == r_const) r_const = zext; else r_non_ext_bits = true; diff --git a/gcc/testsuite/gcc.dg/field-merge-24.c b/gcc/testsuite/gcc.dg/field-merge-24.c new file mode 100644 index 000000000000..ce5bce7d0b49 --- /dev/null +++ b/gcc/testsuite/gcc.dg/field-merge-24.c @@ -0,0 +1,36 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +/* PR tree-optimization/118572 */ +/* Check that signed compares with constants that seem signed in the other + compare operand's width get treated as unsigned if its upper bits are masked + out. */ + +__attribute__((noipa)) +int test(signed char c) +{ + return (((0x80 & (c&0xff)) != 0) && ((0xc0 & (c&0xff)) == 0x80)); +} + +__attribute__((noipa)) +int test2(signed char c) +{ + return (((-128 & (c&-1)) != 0) && ((-64 & (c&-1)) == -128)); +} + +__attribute__((noipa)) +int test3(signed char c) +{ + return (((0x80 & (c&-1)) != 0) && ((0x1248c0 & (c&-1)) == 0x124880)); +} + +__attribute__((noipa)) +int test4(signed char c) +{ + return (((0x400 & (c&-1)) == 0) && ((0x40 & (c&-1)) == 0x40)); +} + +int main() { + if (test(0x80) == 0 || test2(-128) == 0 || test3(-128) == 0 || test4(64) == 0) + __builtin_abort(); +}