fold-const.c (fold_binary): Fold BIT_AND_EXPR's with a pointer operand.
authorOllie Wild <aaw@google.com>
Sun, 23 Sep 2007 20:05:40 +0000 (20:05 +0000)
committerOllie Wild <aaw@gcc.gnu.org>
Sun, 23 Sep 2007 20:05:40 +0000 (20:05 +0000)
gcc/
fold-const.c (fold_binary): Fold BIT_AND_EXPR's with a pointer operand.
(get_pointer_modulus_and_residue): New function.

gcc/testsuite/
gcc.dg/fold-bitand-1.c: New test.
gcc.dg/fold-bitand-2.c: New test.
gcc.dg/fold-bitand-3.c: New test.
gcc.dg/fold-bitand-4.c: New test.

From-SVN: r128701

gcc/ChangeLog
gcc/fold-const.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/fold-bitand-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fold-bitand-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fold-bitand-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fold-bitand-4.c [new file with mode: 0644]

index 2837ede708b7bb6765ab887cb31456b465a03323..b510bddf42914894d273facda161f93f8f94ef94 100644 (file)
@@ -1,3 +1,8 @@
+2007-09-23  Ollie Wild  <aaw@google.com>
+
+       fold-const.c (fold_binary): Fold BIT_AND_EXPR's with a pointer operand.
+       (get_pointer_modulus_and_residue): New function.
+
 2007-09-23  Richard Sandiford  <rsandifo@nildram.co.uk>
 
        * config/mips/mips.c (build_mips16_call_stub): On 64-bit targets,
index 426aad4ced1b2c29e6d640ee43ed98204116e082..12413d833d00cd0f3ecf996345011cfd547de2eb 100644 (file)
@@ -9384,6 +9384,97 @@ fold_mult_zconjz (tree type, tree expr)
 }
 
 
+/* Subroutine of fold_binary.  If P is the value of EXPR, computes
+   power-of-two M and (arbitrary) N such that M divides (P-N).  This condition
+   guarantees that P and N have the same least significant log2(M) bits.
+   N is not otherwise constrained.  In particular, N is not normalized to
+   0 <= N < M as is common.  In general, the precise value of P is unknown.
+   M is chosen as large as possible such that constant N can be determined.
+
+   Returns M and sets *RESIDUE to N.  */
+
+static unsigned HOST_WIDE_INT
+get_pointer_modulus_and_residue (tree expr, unsigned HOST_WIDE_INT *residue)
+{
+  enum tree_code code;
+
+  *residue = 0;
+
+  code = TREE_CODE (expr);
+  if (code == ADDR_EXPR)
+    {
+      expr = TREE_OPERAND (expr, 0);
+      if (handled_component_p (expr))
+       {
+         HOST_WIDE_INT bitsize, bitpos;
+         tree offset;
+         enum machine_mode mode;
+         int unsignedp, volatilep;
+
+         expr = get_inner_reference (expr, &bitsize, &bitpos, &offset,
+                                     &mode, &unsignedp, &volatilep, false);
+         *residue = bitpos / BITS_PER_UNIT;
+         if (offset)
+           {
+             if (TREE_CODE (offset) == INTEGER_CST)
+               *residue += TREE_INT_CST_LOW (offset);
+             else
+               /* We don't handle more complicated offset expressions.  */
+               return 1;
+           }
+       }
+
+      if (DECL_P (expr))
+       return DECL_ALIGN_UNIT (expr);
+    }
+  else if (code == POINTER_PLUS_EXPR)
+    {
+      tree op0, op1;
+      unsigned HOST_WIDE_INT modulus;
+      enum tree_code inner_code;
+      
+      op0 = TREE_OPERAND (expr, 0);
+      STRIP_NOPS (op0);
+      modulus = get_pointer_modulus_and_residue (op0, residue);
+
+      op1 = TREE_OPERAND (expr, 1);
+      STRIP_NOPS (op1);
+      inner_code = TREE_CODE (op1);
+      if (inner_code == INTEGER_CST)
+       {
+         *residue += TREE_INT_CST_LOW (op1);
+         return modulus;
+       }
+      else if (inner_code == MULT_EXPR)
+       {
+         op1 = TREE_OPERAND (op1, 1);
+         if (TREE_CODE (op1) == INTEGER_CST)
+           {
+             unsigned HOST_WIDE_INT align;
+             
+             /* Compute the greatest power-of-2 divisor of op1.  */
+             align = TREE_INT_CST_LOW (op1);
+             align &= -align;
+
+             /* If align is non-zero and less than *modulus, replace
+                *modulus with align., If align is 0, then either op1 is 0
+                or the greatest power-of-2 divisor of op1 doesn't fit in an
+                unsigned HOST_WIDE_INT.  In either case, no additional
+                constraint is imposed.  */
+             if (align)
+               modulus = MIN (modulus, align);
+
+             return modulus;
+           }
+       }
+    }
+
+    /* If we get here, we were unable to determine anything useful about the
+       expression.  */
+    return 1;
+}
+
+
 /* Fold a binary expression of code CODE and type TYPE with operands
    OP0 and OP1.  Return the folded expression if folding is
    successful.  Otherwise, return NULL_TREE.  */
@@ -10915,6 +11006,23 @@ fold_binary (enum tree_code code, tree type, tree op0, tree op1)
                                      TREE_OPERAND (arg1, 0)));
        }
 
+      /* If arg0 is derived from the address of an object or function, we may
+        be able to fold this expression using the object or function's
+        alignment.  */
+      if (POINTER_TYPE_P (TREE_TYPE (arg0)) && host_integerp (arg1, 1))
+       {
+         unsigned HOST_WIDE_INT modulus, residue;
+         unsigned HOST_WIDE_INT low = TREE_INT_CST_LOW (arg1);
+
+         modulus = get_pointer_modulus_and_residue (arg0, &residue);
+
+         /* This works because modulus is a power of 2.  If this weren't the
+            case, we'd have to replace it by its greatest power-of-2
+            divisor: modulus & -modulus.  */
+         if (low < modulus)
+           return build_int_cst (type, residue & low);
+       }
+
       goto associate;
 
     case RDIV_EXPR:
index ff03ded25b7374d98fa3ca68cb55e39bb28cab02..76fa88681c160fac4fae2db3607a1977fc9ad9b2 100644 (file)
@@ -1,3 +1,10 @@
+2007-09-23  Ollie Wild  <aaw@google.com>
+
+       gcc.dg/fold-bitand-1.c: New test.
+       gcc.dg/fold-bitand-2.c: New test.
+       gcc.dg/fold-bitand-3.c: New test.
+       gcc.dg/fold-bitand-4.c: New test.
+
 2007-09-23  Richard Sandiford  <rsandifo@nildram.co.uk>
 
        * gcc.target/mips/gcc-have-sync-compare-and-swap-1.c: Don't expect
diff --git a/gcc/testsuite/gcc.dg/fold-bitand-1.c b/gcc/testsuite/gcc.dg/fold-bitand-1.c
new file mode 100644 (file)
index 0000000..413ed67
--- /dev/null
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-options "-fdump-tree-original" } */
+
+char c1 __attribute__ ((aligned (1)));
+char c2 __attribute__ ((aligned (2)));
+char c4 __attribute__ ((aligned (4)));
+char c8 __attribute__ ((aligned (8)));
+
+unsigned f1(void)
+{
+  return 3 & (__SIZE_TYPE__)&c1;
+}
+
+unsigned f2(void)
+{
+  return 3 & (__SIZE_TYPE__)&c2;
+}
+
+unsigned f3(void)
+{
+  return 3 & (__SIZE_TYPE__)&c4;
+}
+
+unsigned f4(void)
+{
+  return 3 & (__SIZE_TYPE__)&c8;
+}
+
+/* { dg-final { scan-tree-dump-times "\&c1 \& 3" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "\&c2 \& 3" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "\&c4 \& 3" 0 "original" } } */
+/* { dg-final { scan-tree-dump-times "\&c8 \& 3" 0 "original" } } */
+/* { dg-final { scan-tree-dump-times "return 0" 2 "original" } } */
+/* { dg-final { cleanup-tree-dump "original" } } */
diff --git a/gcc/testsuite/gcc.dg/fold-bitand-2.c b/gcc/testsuite/gcc.dg/fold-bitand-2.c
new file mode 100644 (file)
index 0000000..de2e674
--- /dev/null
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-fdump-tree-original" } */
+
+struct {
+  char c1;
+  char c2;
+  char c3;
+  char c4;
+} s __attribute__ ((aligned (4)));
+
+unsigned f1 (void)
+{
+  return 3 & (__SIZE_TYPE__)&s.c1;
+}
+
+unsigned f2 (void)
+{
+  return 3 & (__SIZE_TYPE__)&s.c2;
+}
+
+unsigned f3 (void)
+{
+  return 3 & (__SIZE_TYPE__)&s.c3;
+}
+
+unsigned f4 (void)
+{
+  return 3 & (__SIZE_TYPE__)&s.c4;
+}
+
+unsigned f5 (void)
+{
+  return 4 & (__SIZE_TYPE__)&s.c1;
+}
+
+/* { dg-final { scan-tree-dump-times "\& 3" 0 "original" } } */
+/* { dg-final { scan-tree-dump-times "\& 4" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "return 0" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "return 1" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "return 2" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "return 3" 1 "original" } } */
+/* { dg-final { cleanup-tree-dump "original" } } */
diff --git a/gcc/testsuite/gcc.dg/fold-bitand-3.c b/gcc/testsuite/gcc.dg/fold-bitand-3.c
new file mode 100644 (file)
index 0000000..43d765b
--- /dev/null
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-fdump-tree-original" } */
+
+char c[4] __attribute__ ((aligned (4)));
+
+struct S {
+  char c1;
+  char c2;
+  char c3;
+  char c4;
+};
+
+int f1 (void)
+{
+  return 3 & (__SIZE_TYPE__)&c[1];
+}
+
+int f2 (void)
+{
+  return 3 & (__SIZE_TYPE__)&((struct S *)&c)->c2;
+}
+
+/* { dg-final { scan-tree-dump-times "\& 3" 0 "original" } } */
+/* { dg-final { scan-tree-dump-times "return 1" 2 "original" } } */
+/* { dg-final { cleanup-tree-dump "original" } } */
diff --git a/gcc/testsuite/gcc.dg/fold-bitand-4.c b/gcc/testsuite/gcc.dg/fold-bitand-4.c
new file mode 100644 (file)
index 0000000..7d82426
--- /dev/null
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-fdump-tree-original" } */
+
+typedef char char4[4] __attribute__ ((aligned (4)));
+char4 c4[4] __attribute__ ((aligned (16)));
+
+typedef char char16[16] __attribute__ ((aligned (16)));
+char16 c16[4] __attribute__ ((aligned (4)));
+
+int f1 (void)
+{
+  /* 12 */
+  return 15 & (__SIZE_TYPE__)&c4[3];
+}
+
+int f2 (int i)
+{
+  /* Indeterminate */
+  return 15 & (__SIZE_TYPE__)&c4[i];
+}
+
+int f3 (int i)
+{
+  /* 0 */
+  return 3 & (__SIZE_TYPE__)&c4[i];
+}
+
+int f4 (int i)
+{
+  /* Indeterminate */
+  return 7 & (__SIZE_TYPE__)&c16[i];
+}
+
+int f5 (int i)
+{
+  /* 0 */
+  return 3 & (__SIZE_TYPE__)&c16[i];
+}
+
+/* { dg-final { scan-tree-dump-times "return 12" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "\& 15" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "return 0" 2 "original" } } */
+/* { dg-final { scan-tree-dump-times "\& 7" 1 "original" } } */
+/* { dg-final { cleanup-tree-dump "original" } } */