https://gcc.gnu.org/g:ae0bd821107ba30ea58c7abc075e15a004180bc4
commit ae0bd821107ba30ea58c7abc075e15a004180bc4 Author: Alexandre Oliva <ol...@adacore.com> Date: Tue Dec 17 22:18:43 2024 -0300 ifcombine field merge: handle bitfield zero tests in range tests Some bitfield compares with zero are optimized to range tests, so instead of X & ~(Bit - 1) != 0 what reaches ifcombine is X > (Bit - 1), where Bit is a power of two and X is unsigned. This patch recognizes this optimized form of masked compares, and attempts to merge them like masked compares, which enables some more field merging that a folder version of fold_truth_andor used to handle without additional effort. I haven't seen X & ~(Bit - 1) == 0 become X <= (Bit - 1), or X < Bit for that matter, but it was easy enough to handle the former symmetrically to the above. The latter was also easy enough, and so was its symmetric, X >= Bit, that is handled like X & ~(Bit - 1) != 0. for gcc/ChangeLog * gimple-fold.cc (decode_field_reference): Accept incoming mask. (fold_truth_andor_for_ifcombine): Handle some compares with powers of two, minus 1 or 0, like masked compares with zero. for gcc/testsuite/ChangeLog * gcc.dg/field-merge-15.c: New. Diff: --- gcc/gimple-fold.cc | 71 +++++++++++++++++++++++++++++++++-- gcc/testsuite/gcc.dg/field-merge-15.c | 36 ++++++++++++++++++ 2 files changed, 104 insertions(+), 3 deletions(-) diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc index 74273b83cf81..ee1da47b080b 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, @@ -7565,14 +7567,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) @@ -8023,6 +8041,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) @@ -8032,6 +8052,31 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, lsignbit = true; lcode = (lcode == LT_EXPR ? NE_EXPR : EQ_EXPR); } + /* Turn compares of unsigned quantities with powers of two into + equality tests of masks. */ + else if ((lcode == LT_EXPR || lcode == GE_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) + { + ll_and_mask = ~(wi::to_wide (lr_arg) - 1); + lcode = (lcode == GE_EXPR ? NE_EXPR : EQ_EXPR); + lr_arg = wide_int_to_tree (TREE_TYPE (ll_arg), ll_and_mask * 0); + } + /* Turn compares of unsigned quantities with powers of two minus one + into equality tests of masks. */ + 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); + } + /* Likewise for the second compare. */ if ((rcode == LT_EXPR || rcode == GE_EXPR) && integer_zerop (rr_arg) && INTEGRAL_TYPE_P (TREE_TYPE (rl_arg)) @@ -8040,6 +8085,26 @@ 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 == LT_EXPR || rcode == GE_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) + { + rl_and_mask = ~(wi::to_wide (rr_arg) - 1); + rcode = (rcode == GE_EXPR ? NE_EXPR : EQ_EXPR); + rr_arg = wide_int_to_tree (TREE_TYPE (rl_arg), rl_and_mask * 0); + } + 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" } } */