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;
+}

Reply via email to