A compare with zero may be taken as a sign bit test by fold_truth_andor_for_ifcombine, but the operand may be extended from a narrower field. If the operand was narrower, the bitsize will reflect the narrowing conversion, but if it was wider, we'll only know whether the field is sign- or zero-extended from unsignedp, but we won't know whether it needed to be extended, because arg will have changed to the narrower variable when we get to the point in which we can compute the arg width. If it's sign-extended, we're testing the right bit, but if it's zero-extended, there isn't any bit we can test.
Instead of punting and leaving the foldable compare to be figured out by another pass, arrange for the sign bit resulting from the widening zero-extension to be taken as zero, so that the modified compare will yield the desired result. While at that, avoid swapping the right-hand compare operands when we've already determined that it was a signbit test: it no use to even try. Regstrapped on x86_64-linux-gnu. Ok to install? for gcc/ChangeLog PR tree-optimization/118805 * gimple-fold.cc (fold_truth_andor_for_combine): Detect and cope with zero-extension in signbit tests. Reject swapping right-compare operands if rsignbit. for gcc/testsuite/ChangeLog PR tree-optimization/118805 * gcc.dg/field-merge-26.c: New. --- gcc/gimple-fold.cc | 22 +++++++++++++++++----- gcc/testsuite/gcc.dg/field-merge-26.c | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/field-merge-26.c diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc index 29191685a43c5..0380c7af4c213 100644 --- a/gcc/gimple-fold.cc +++ b/gcc/gimple-fold.cc @@ -8090,14 +8090,16 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, /* Prepare to turn compares of signed quantities with zero into sign-bit tests. We need not worry about *_reversep here for these compare - rewrites: loads will have already been reversed before compares. */ - bool lsignbit = false, rsignbit = false; + rewrites: loads will have already been reversed before compares. Save the + precision, because [lr]l_arg may change and we won't be able to tell how + wide it was originally. */ + unsigned lsignbit = 0, rsignbit = 0; if ((lcode == LT_EXPR || lcode == GE_EXPR) && integer_zerop (lr_arg) && INTEGRAL_TYPE_P (TREE_TYPE (ll_arg)) && !TYPE_UNSIGNED (TREE_TYPE (ll_arg))) { - lsignbit = true; + lsignbit = TYPE_PRECISION (TREE_TYPE (ll_arg)); lcode = (lcode == LT_EXPR ? NE_EXPR : EQ_EXPR); } /* Turn compares of unsigned quantities with powers of two into @@ -8130,7 +8132,7 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, && INTEGRAL_TYPE_P (TREE_TYPE (rl_arg)) && !TYPE_UNSIGNED (TREE_TYPE (rl_arg))) { - rsignbit = true; + rsignbit = TYPE_PRECISION (TREE_TYPE (rl_arg)); rcode = (rcode == LT_EXPR ? NE_EXPR : EQ_EXPR); } else if ((rcode == LT_EXPR || rcode == GE_EXPR) @@ -8204,7 +8206,7 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, || ! operand_equal_p (ll_inner, rl_inner, 0)) { /* Try swapping the operands. */ - if (ll_reversep != rr_reversep + if (ll_reversep != rr_reversep || rsignbit || !operand_equal_p (ll_inner, rr_inner, 0)) return 0; @@ -8284,6 +8286,14 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, if (lsignbit) { wide_int sign = wi::mask (ll_bitsize - 1, true, ll_bitsize); + /* If ll_arg is zero-extended and we're testing the sign bit, we know + what the result should be. Shifting the sign bit out of sign will get + us to mask the entire field out, yielding zero, i.e., the sign bit of + the zero-extended value. We know the masked value is being compared + with zero, so the compare will get us the result we're looking + for: TRUE if EQ_EXPR, FALSE if NE_EXPR. */ + if (lsignbit > ll_bitsize && ll_unsignedp) + sign <<= 1; if (!ll_and_mask.get_precision ()) ll_and_mask = sign; else @@ -8303,6 +8313,8 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, if (rsignbit) { wide_int sign = wi::mask (rl_bitsize - 1, true, rl_bitsize); + if (rsignbit > rl_bitsize && ll_unsignedp) + sign <<= 1; if (!rl_and_mask.get_precision ()) rl_and_mask = sign; else diff --git a/gcc/testsuite/gcc.dg/field-merge-26.c b/gcc/testsuite/gcc.dg/field-merge-26.c new file mode 100644 index 0000000000000..96d7e7205c5f2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/field-merge-26.c @@ -0,0 +1,20 @@ +/* { dg-do run } */ +/* { dg-options "-O1 -fno-tree-ccp -fno-tree-copy-prop -fno-tree-forwprop -fno-tree-fre" } */ + +/* PR tree-optimization/118805 */ + +/* Test that ifcombine doesn't get confused by tests for the sign bit of + extended values that would normally be folded before. */ + +unsigned char a = 255; +int b; +int main() { + int c = 0; + if (c > a && a >= 255) + __builtin_abort (); + if (c <= a && a == 254) + __builtin_abort (); + if (a < c || a != 255) + __builtin_abort (); + return 0; +} -- Alexandre Oliva, happy hacker https://FSFLA.org/blogs/lxo/ Free Software Activist GNU Toolchain Engineer More tolerance and less prejudice are key for inclusion and diversity Excluding neuro-others for not behaving ""normal"" is *not* inclusive