https://gcc.gnu.org/g:3822120f4977e999269531c67f4678f2657aeb2f
commit r17-5015-g3822120f4977e999269531c67f4678f2657aeb2f Author: Richard Biener <[email protected]> Date: Fri Oct 9 09:43:20 2026 +0200 Convert XOR constant operand to outer_type in ifcombine When decode_field_reference decodes an equality test of the form (outer_type)(a ^ c) == 0 into a == c, it strips the conversion outer_type on the first pass (decoding the left-hand operand 'a') and stores outer_type for field sizing. On the second pass (*pxorp == true, decoding the right-hand operand 'c'), if 'c' is an INTEGER_CST, it was left with its original unconverted type and width. This meant that if 'c' was wider than outer_type (e.g. a 16-bit constant cast to uint8_t), 'c' retained non-zero bits in the upper positions. In fold_truth_andor_for_ifcombine, because both operands' bitsizes had been adjusted to outer_type, rl_bitsize < rr_bitsize was false, leaving the constant unclipped. The subsequent check for non-zero bits outside the field mask then erroneously concluded that the comparison was tautologically false and optimized the condition to 0. Fix this by converting exp to outer_type when decoding the right-hand XOR operand if outer_type is set and exp is an INTEGER_CST. PR tree-optimization/127447 PR tree-optimization/125444 PR tree-optimization/124937 * gimple-fold.cc (decode_field_reference): Convert exp to outer_type when decoding an INTEGER_CST right-hand XOR operand. * gcc.dg/field-merge-28.c: New test. Diff: --- gcc/gimple-fold.cc | 2 ++ gcc/testsuite/gcc.dg/field-merge-28.c | 47 +++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc index 63e289c0d449..0650c198fc8c 100644 --- a/gcc/gimple-fold.cc +++ b/gcc/gimple-fold.cc @@ -8005,6 +8005,8 @@ decode_field_reference (tree *pexp, HOST_WIDE_INT *pbitsize, { exp = res_ops[1]; gcc_checking_assert (!pxor_cmp_op && !pxor_and_mask); + if (outer_type && TREE_CODE (exp) == INTEGER_CST) + exp = fold_convert (outer_type, exp); } else if (!pxor_cmp_op) /* Not much we can do when xor appears in the right-hand compare diff --git a/gcc/testsuite/gcc.dg/field-merge-28.c b/gcc/testsuite/gcc.dg/field-merge-28.c new file mode 100644 index 000000000000..b31bbf9de439 --- /dev/null +++ b/gcc/testsuite/gcc.dg/field-merge-28.c @@ -0,0 +1,47 @@ +/* { dg-do run } */ +/* { dg-options "-O1" } */ + +unsigned short b1, b2; +int c1 = 0, c2 = 0; + +__attribute__((noipa)) void m1 () { c1++; } +__attribute__((noipa)) void m2 () { c2++; } + +__attribute__((noipa)) +void test1 () +{ + for (int i = 0; i < 9; i++) + { + unsigned short val = (b1 ^= 65533); + unsigned char k = val; + if ((val & 240) || k) + ; + else + m1 (); + } +} + +__attribute__((noipa)) +void test2 () +{ + for (int i = 0; i < 9; i++) + { + unsigned short val = (b2 ^= 65533); + signed char k = val; + if ((val & 240) || k) + ; + else + m2 (); + } +} + +int main () +{ + test1 (); + if (c1 != 4) + __builtin_abort (); + test2 (); + if (c2 != 4) + __builtin_abort (); + return 0; +}
