https://gcc.gnu.org/g:30cf6dfbb6e10943a11785e0c056d5df0a9c583a
commit 30cf6dfbb6e10943a11785e0c056d5df0a9c583a Author: Alexandre Oliva <ol...@adacore.com> Date: Tue Dec 17 11:22:23 2024 -0300 ifcombine field merge: handle bitfield zero tests in range tests Diff: --- gcc/gimple-fold.cc | 46 ++++++++++++++++++++++++++++++++--- gcc/testsuite/gcc.dg/field-merge-15.c | 36 +++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc index 06913d57f8ae..514d19ef37cb 100644 --- a/gcc/gimple-fold.cc +++ b/gcc/gimple-fold.cc @@ -7509,8 +7509,10 @@ gimple_binop_def_p (enum tree_code code, tree t, tree op[2]) *PREVERSEP is set to the storage order of the field. - *PAND_MASK is set to the mask found in a BIT_AND_EXPR, if any. - If PAND_MASK *is NULL, BIT_AND_EXPR is not recognized. + *PAND_MASK is set to the mask found in a BIT_AND_EXPR, if any. If + PAND_MASK *is NULL, BIT_AND_EXPR is not recognized. If *PAND_MASK + is initially set to a mask with nonzero precision, that mask is + combined with the found mask, or adjusted in precision to match. *XOR_P is to be FALSE if EXP might be a XOR used in a compare, in which case, if XOR_CMP_OP is a zero constant, it will be overridden with *PEXP, @@ -7561,14 +7563,30 @@ decode_field_reference (tree *pexp, HOST_WIDE_INT *pbitsize, exp = res_ops[0]; } - /* Recognize and save a masking operation. */ + /* Recognize and save a masking operation. Combine it with an + incoming mask. */ if (pand_mask && gimple_binop_def_p (BIT_AND_EXPR, exp, res_ops) && uniform_integer_cst_p (res_ops[1])) { loc[1] = gimple_location (SSA_NAME_DEF_STMT (exp)); exp = res_ops[0]; and_mask = wi::to_wide (res_ops[1]); + unsigned prec_in = pand_mask->get_precision (); + if (prec_in) + { + unsigned prec_op = and_mask.get_precision (); + if (prec_in >= prec_op) + { + if (prec_in > prec_op) + and_mask = wide_int::from (and_mask, prec_in, UNSIGNED); + and_mask &= *pand_mask; + } + else + and_mask &= wide_int::from (*pand_mask, prec_op, UNSIGNED); + } } + else if (pand_mask) + and_mask = *pand_mask; /* Turn (a ^ b) [!]= 0 into a [!]= b. */ if (xor_p && gimple_binop_def_p (BIT_XOR_EXPR, exp, res_ops) @@ -8019,6 +8037,8 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, return 0; } + /* Prepare to turn compares of signed quantities with zero into + sign-bit tests, */ bool lsignbit = false, rsignbit = false; if ((lcode == LT_EXPR || lcode == GE_EXPR) && integer_zerop (lr_arg) @@ -8028,6 +8048,16 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, lsignbit = true; lcode = (lcode == LT_EXPR ? NE_EXPR : EQ_EXPR); } + else if ((lcode == LE_EXPR || lcode == GT_EXPR) + && INTEGRAL_TYPE_P (TREE_TYPE (ll_arg)) + && TYPE_UNSIGNED (TREE_TYPE (ll_arg)) + && uniform_integer_cst_p (lr_arg) + && wi::popcount (wi::to_wide (lr_arg) + 1) == 1) + { + ll_and_mask = ~wi::to_wide (lr_arg); + lcode = (lcode == GT_EXPR ? NE_EXPR : EQ_EXPR); + lr_arg = wide_int_to_tree (TREE_TYPE (ll_arg), ll_and_mask * 0); + } if ((rcode == LT_EXPR || rcode == GE_EXPR) && integer_zerop (rr_arg) && INTEGRAL_TYPE_P (TREE_TYPE (rl_arg)) @@ -8036,6 +8066,16 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, rsignbit = true; rcode = (rcode == LT_EXPR ? NE_EXPR : EQ_EXPR); } + else if ((rcode == LE_EXPR || rcode == GT_EXPR) + && INTEGRAL_TYPE_P (TREE_TYPE (rl_arg)) + && TYPE_UNSIGNED (TREE_TYPE (rl_arg)) + && uniform_integer_cst_p (rr_arg) + && wi::popcount (wi::to_wide (rr_arg) + 1) == 1) + { + rl_and_mask = ~wi::to_wide (rr_arg); + rcode = (rcode == GT_EXPR ? NE_EXPR : EQ_EXPR); + rr_arg = wide_int_to_tree (TREE_TYPE (rl_arg), rl_and_mask * 0); + } /* See if the comparisons can be merged. Then get all the parameters for each side. */ diff --git a/gcc/testsuite/gcc.dg/field-merge-15.c b/gcc/testsuite/gcc.dg/field-merge-15.c new file mode 100644 index 000000000000..34641e893c92 --- /dev/null +++ b/gcc/testsuite/gcc.dg/field-merge-15.c @@ -0,0 +1,36 @@ +/* { dg-do run } */ +/* { dg-options "-O -fdump-tree-ifcombine-details" } */ + +/* Check that bitfield compares-with-zero turned into GT and LE compares with + powers-of-two minus 1 are optimized. */ + +struct s { + short a : sizeof (short) * __CHAR_BIT__ - 3; + short b : 3; + short c : 3; + short d : sizeof (short) * __CHAR_BIT__ - 3; +} __attribute__ ((aligned (4))); + +struct s p = { 15, 7, 3, 1 }; +struct s q = { 0, 0, 0, 0 }; + +void f () +{ + if (p.a || p.b || p.c || p.d) + return; + __builtin_abort (); +} + +void g () +{ + if (q.a || q.b || q.c || q.d) + __builtin_abort (); +} + +int main () { + f (); + g (); + return 0; +} + +/* { dg-final { scan-tree-dump-times "optimizing" 6 "ifcombine" } } */