[gcc(refs/users/aoliva/heads/testme)] handle bitxor
Alexandre Oliva
aoliva@gcc.gnu.org
Wed Sep 23 23:23:55 GMT 2020
https://gcc.gnu.org/g:e794de4da11d7ace63e93d95cbf87c458571f1d9
commit e794de4da11d7ace63e93d95cbf87c458571f1d9
Author: Alexandre Oliva <oliva@adacore.com>
Date: Thu Sep 17 09:35:11 2020 -0300
handle bitxor
Diff:
---
gcc/fold-const.c | 67 ++++++++++++++++++++++++++++++++----
gcc/testsuite/gcc.dg/field-merge-3.c | 36 +++++++++++++++++++
2 files changed, 96 insertions(+), 7 deletions(-)
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 2def8bcff42..0074bdef047 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -4603,6 +4603,41 @@ optimize_bit_field_compare (location_t loc, enum tree_code code,
return lhs;
}
+/* If *R_ARG is a constant zero, and L_ARG is a possibly masked
+ BIT_XOR_EXPR, return 1 and set *r_arg to l_arg.
+ Otherwise, return 0.
+
+ The returned value should be passed to decode_field_reference for it
+ to handle l_arg, and then doubled for r_arg. */
+static int
+prepare_xor (tree l_arg, tree *r_arg)
+{
+ int ret = 0;
+
+ if (!integer_zerop (*r_arg))
+ return ret;
+
+ tree exp = l_arg;
+ STRIP_NOPS (exp);
+
+ if (TREE_CODE (exp) == BIT_AND_EXPR)
+ {
+ tree and_mask = TREE_OPERAND (exp, 1);
+ exp = TREE_OPERAND (exp, 0);
+ STRIP_NOPS (exp); STRIP_NOPS (and_mask);
+ if (TREE_CODE (and_mask) != INTEGER_CST)
+ return ret;
+ }
+
+ if (TREE_CODE (exp) == BIT_XOR_EXPR)
+ {
+ *r_arg = l_arg;
+ return 1;
+ }
+
+ return ret;
+}
+
/* Subroutine for fold_truth_andor_1: decode a field reference.
If EXP is a comparison reference, we return the innermost reference.
@@ -4625,6 +4660,10 @@ optimize_bit_field_compare (location_t loc, enum tree_code code,
*PAND_MASK is set to the mask found in a BIT_AND_EXPR, if any.
+ XOR_WHICH is 1 or 2 if EXP was found to be a (possibly masked)
+ BIT_XOR_EXPR compared with zero. We're to take the first or second
+ operand thereof if so. It should be zero otherwise.
+
Return 0 if this is not a component reference or is one that we can't
do anything with. */
@@ -4632,7 +4671,7 @@ static tree
decode_field_reference (location_t loc, tree *exp_, HOST_WIDE_INT *pbitsize,
HOST_WIDE_INT *pbitpos, machine_mode *pmode,
int *punsignedp, int *preversep, int *pvolatilep,
- tree *pmask, tree *pand_mask)
+ tree *pmask, tree *pand_mask, int xor_which)
{
tree exp = *exp_;
tree outer_type = 0;
@@ -4665,13 +4704,25 @@ decode_field_reference (location_t loc, tree *exp_, HOST_WIDE_INT *pbitsize,
return NULL_TREE;
}
+ if (xor_which)
+ {
+ gcc_checking_assert (TREE_CODE (exp) == BIT_XOR_EXPR);
+ exp = TREE_OPERAND (exp, xor_which - 1);
+ STRIP_NOPS (exp);
+ }
+
if (TREE_CODE (exp) == RSHIFT_EXPR
&& TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST
&& tree_fits_shwi_p (TREE_OPERAND (exp, 1)))
{
- shiftrt = tree_to_shwi (TREE_OPERAND (exp, 1));
+ tree shift = TREE_OPERAND (exp, 1);
+ STRIP_NOPS (shift);
+ shiftrt = tree_to_shwi (shift);
if (shiftrt > 0)
- exp = TREE_OPERAND (exp, 0);
+ {
+ exp = TREE_OPERAND (exp, 0);
+ STRIP_NOPS (exp);
+ }
else
shiftrt = 0;
}
@@ -6480,22 +6531,24 @@ fold_truth_andor_1 (location_t loc, enum tree_code code, tree truth_type,
ll_reversep = lr_reversep = rl_reversep = rr_reversep = 0;
volatilep = 0;
+ int l_xor = prepare_xor (ll_arg, &lr_arg);
ll_inner = decode_field_reference (loc, &ll_arg,
&ll_bitsize, &ll_bitpos, &ll_mode,
&ll_unsignedp, &ll_reversep, &volatilep,
- &ll_mask, &ll_and_mask);
+ &ll_mask, &ll_and_mask, l_xor);
lr_inner = decode_field_reference (loc, &lr_arg,
&lr_bitsize, &lr_bitpos, &lr_mode,
&lr_unsignedp, &lr_reversep, &volatilep,
- &lr_mask, &lr_and_mask);
+ &lr_mask, &lr_and_mask, 2 * l_xor);
+ int r_xor = prepare_xor (rl_arg, &rr_arg);
rl_inner = decode_field_reference (loc, &rl_arg,
&rl_bitsize, &rl_bitpos, &rl_mode,
&rl_unsignedp, &rl_reversep, &volatilep,
- &rl_mask, &rl_and_mask);
+ &rl_mask, &rl_and_mask, r_xor);
rr_inner = decode_field_reference (loc, &rr_arg,
&rr_bitsize, &rr_bitpos, &rr_mode,
&rr_unsignedp, &rr_reversep, &volatilep,
- &rr_mask, &rr_and_mask);
+ &rr_mask, &rr_and_mask, 2 * r_xor);
/* It must be true that the inner operation on the lhs of each
comparison must be the same if we are to be able to do anything.
diff --git a/gcc/testsuite/gcc.dg/field-merge-3.c b/gcc/testsuite/gcc.dg/field-merge-3.c
new file mode 100644
index 00000000000..8fdbb9a396e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/field-merge-3.c
@@ -0,0 +1,36 @@
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+const int BIG_ENDIAN_P = (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__);
+
+struct T1 {
+ unsigned char p[2];
+ unsigned short a;
+ unsigned int z;
+} __attribute__((__aligned__(8)));
+
+struct T2 {
+ unsigned short p;
+ unsigned short a;
+ unsigned int z;
+} __attribute__((__aligned__(8)));
+
+#define vc 0xaa
+#define vi 0x12345678
+
+struct T1 v1 = { { vc + !BIG_ENDIAN_P, vc + BIG_ENDIAN_P }, vc, vi };
+struct T2 v2 = { (vc << 8) | (vc - 1), vc, vi };
+
+void f (void) {
+ if (0
+ || v1.p[!BIG_ENDIAN_P] != v2.p >> 8
+ || v1.a != v2.a
+ || (v1.z ^ v2.z) & 0xff00ff00 != 0
+ || (v1.z ^ v2.z) & 0x00ff00ff != 0)
+ __builtin_abort ();
+}
+
+int main () {
+ f ();
+ return 0;
+}
More information about the Gcc-cvs
mailing list