https://gcc.gnu.org/g:7a3878019f77ec4a41ca31ee8a09f7da6ab92470
commit r17-5023-g7a3878019f77ec4a41ca31ee8a09f7da6ab92470 Author: Richard Biener <[email protected]> Date: Fri Oct 9 12:35:52 2026 +0200 Do not strip conversions on bit test masks in ifcombine In recognize_bits_test, get_name_for_bit_test was unconditionally called on gimple_assign_rhs1 of the BIT_AND_EXPR. Because bitwise AND is commutative, rhs1 is not necessarily the name being tested; when testing a variable against a non-constant mask, rhs1 may be the mask rather than the tested value. When rhs1 was a mask defined by a single-use narrowing conversion (such as m = (int) mm), get_name_for_bit_test stripped the conversion and returned the wider variable (e.g. mm). If the subsequent matching in ifcombine_ifandif then matched the other operand with the other condition's tested variable via a commutativity swap, the stripped operand was swapped into the mask position. Because the mask had a wider type than the tested variable, ifcombine widened the tested variable (e.g. (unsigned long) y) and bitwise-ORed the masks together. This resurrected upper bits from the wider variable that had been explicitly truncated in the original program, leading to wrong code when the tested variable sign-extended into those bit positions. Fix this by having recognize_bits_test return the raw operands of the BIT_AND_EXPR. In ifcombine_ifandif, first attempt to match the operands directly under commutativity. Only if that fails, look through conversions on the two names, so that a stripped operand never becomes part of the combined mask. Looking through conversions of the names is valid for arbitrary masks since the name is truncated and the masks are zero-extended to the common type. PR tree-optimization/126563 * tree-ssa-ifcombine.cc (recognize_bits_test): Do not call get_name_for_bit_test on rhs1. (ifcombine_ifandif): Check all operands for abnormal PHIs. Look through conversions via get_name_for_bit_test on the names only when direct matching fails. * gcc.dg/tree-ssa/ssa-ifcombine-14.c: New test. * gcc.dg/tree-ssa/ssa-ifcombine-15.c: New test. Diff: --- gcc/testsuite/gcc.dg/tree-ssa/ssa-ifcombine-14.c | 41 ++++++++++++++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/ssa-ifcombine-15.c | 25 +++++++++++++++ gcc/tree-ssa-ifcombine.cc | 22 +++++++++++-- 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-ifcombine-14.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-ifcombine-14.c new file mode 100644 index 000000000000..75d660503a0a --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-ifcombine-14.c @@ -0,0 +1,41 @@ +/* { dg-do run } */ +/* { dg-options "-O1" } */ + +__attribute__((noipa)) int +f (long long mm, int a, int b) +{ + int m = (int) mm; + int y = a + b; + if ((m & y) == 0 && (y & 4) == 0) + return 1; + return 0; +} + +__attribute__((noipa)) int +ref (long long mm, int a, int b) +{ + volatile int m = (int) mm; + volatile int y = a + b; + volatile int t1 = (m & y) == 0; + if (!t1) return 0; + volatile int t2 = (y & 4) == 0; + if (!t2) return 0; + return 1; +} + +int +main (void) +{ + long long mm = 0x100000000LL; + int bad = 0; + for (int a = -16; a <= 16; a++) + for (int b = -16; b <= 16; b++) + { + int got = f (mm, a, b), want = ref (mm, a, b); + if (got != want) + bad++; + } + if (bad) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-ifcombine-15.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-ifcombine-15.c new file mode 100644 index 000000000000..57c5edb10927 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-ifcombine-15.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-ifcombine-details" } */ + +/* Looking through the narrowing conversion of the tested name is valid + even with non-constant masks. */ + +int +f1 (long x, int m) +{ + int t = (int) x; + if ((t & m) == 0 && (x & 4) == 0) + return 1; + return 0; +} + +int +f2 (long x, int m, long m2) +{ + int t = (int) x; + if ((t & m) == 0 && (x & m2) == 0) + return 1; + return 0; +} + +/* { dg-final { scan-tree-dump-times "optimizing bits or bits test" 2 "ifcombine" } } */ diff --git a/gcc/tree-ssa-ifcombine.cc b/gcc/tree-ssa-ifcombine.cc index 0b1fdc7960b0..28bb5bf09989 100644 --- a/gcc/tree-ssa-ifcombine.cc +++ b/gcc/tree-ssa-ifcombine.cc @@ -378,7 +378,7 @@ recognize_bits_test (gcond *cond, tree *name, tree *bits, bool inv) || gimple_assign_rhs_code (stmt) != BIT_AND_EXPR) return false; - *name = get_name_for_bit_test (gimple_assign_rhs1 (stmt)); + *name = gimple_assign_rhs1 (stmt); *bits = gimple_assign_rhs2 (stmt); return true; @@ -1015,7 +1015,11 @@ ifcombine_ifandif (basic_block inner_cond_bb, bool inner_inv, if ((TREE_CODE (name1) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (name1)) || (TREE_CODE (name2) == SSA_NAME - && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (name2))) + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (name2)) + || (TREE_CODE (bits1) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (bits1)) + || (TREE_CODE (bits2) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (bits2))) return false; /* Find the common name which is bit-tested. */ @@ -1031,7 +1035,19 @@ ifcombine_ifandif (basic_block inner_cond_bb, bool inner_inv, else if (bits1 == name2) std::swap (name1, bits1); else - goto bits_test_failed; + { + /* Look through non-widening conversions, but only on the names + so that a stripped operand never becomes part of the mask. */ + tree t1 = get_name_for_bit_test (name1); + tree t2 = get_name_for_bit_test (name2); + if (t1 != t2) + goto bits_test_failed; + name1 = name2 = t1; + } + + if (TREE_CODE (name1) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (name1)) + return false; /* As we strip non-widening conversions in finding a common name that is tested make sure to end up with an integral
