[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