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.
Regstrapped on x86_64-linux-gnu and ppc64-linux-gnu, along with 4 other
ifcombine patches. Ok to install?
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.
---
gcc/gimple-fold.cc | 71 ++++++++++++++++++++++++++++++++-
gcc/testsuite/gcc.dg/field-merge-15.c | 36 +++++++++++++++++
2 files changed, 104 insertions(+), 3 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/field-merge-15.c
diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 06913d57f8aec..856ee5369a8c9 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,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))
@@ -8036,6 +8081,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 0000000000000..34641e893c92f
--- /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" } } */
--
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