re PR rtl-optimization/19296 (Range check on short miscompiled at -O)
authorEric Botcazou <ebotcazou@libertysurf.fr>
Tue, 18 Jan 2005 08:26:21 +0000 (09:26 +0100)
committerEric Botcazou <ebotcazou@gcc.gnu.org>
Tue, 18 Jan 2005 08:26:21 +0000 (08:26 +0000)
PR rtl-optimization/19296
* combine.c (simplify_comparison): Rewrite the condition under
which a non-paradoxical SUBREG of a PLUS can be lifted when
compared against a constant.

From-SVN: r93818

gcc/ChangeLog
gcc/combine.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/short-compare-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/short-compare-2.c [new file with mode: 0644]

index 533b424ea2aa78b35e8b54a71e1166620f2ce9d2..ad34b3f17d0c06840d012cc04a12523464e22b1a 100644 (file)
@@ -1,3 +1,10 @@
+2005-01-18  Eric Botcazou  <ebotcazou@libertysurf.fr>
+
+       PR rtl-optimization/19296
+       * combine.c (simplify_comparison): Rewrite the condition under
+       which a non-paradoxical SUBREG of a PLUS can be lifted when
+       compared against a constant.
+
 2005-01-18  Andi Kleen <ak@muc.de>
 
        * c-typeck.c: (convert_for_assignment): Check warn_pointer_sign.
index a870ff3751c7f42bc0e48b333b00e17cee47b792..4f9ddfbd8e0673166e4536df76450eb3c0a903e2 100644 (file)
@@ -10041,34 +10041,61 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1)
          break;
 
        case SUBREG:
-         /* Check for the case where we are comparing A - C1 with C2,
-            both constants are smaller than 1/2 the maximum positive
-            value in MODE, and the comparison is equality or unsigned.
-            In that case, if A is either zero-extended to MODE or has
-            sufficient sign bits so that the high-order bit in MODE
-            is a copy of the sign in the inner mode, we can prove that it is
-            safe to do the operation in the wider mode.  This simplifies
-            many range checks.  */
+         /* Check for the case where we are comparing A - C1 with C2, that is
+
+              (subreg:MODE (plus (A) (-C1))) op (C2)
+
+            with C1 a constant, and try to lift the SUBREG, i.e. to do the
+            comparison in the wider mode.  One of the following two conditions
+            must be true in order for this to be valid:
+
+              1. The mode extension results in the same bit pattern being added
+                 on both sides and the comparison is equality or unsigned.  As
+                 C2 has been truncated to fit in MODE, the pattern can only be
+                 all 0s or all 1s.
+
+              2. The mode extension results in the sign bit being copied on
+                 each side.
+
+            The difficulty here is that we have predicates for A but not for
+            (A - C1) so we need to check that C1 is within proper bounds so
+            as to perturbate A as little as possible.  */
 
          if (mode_width <= HOST_BITS_PER_WIDE_INT
              && subreg_lowpart_p (op0)
+             && GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) > mode_width
              && GET_CODE (SUBREG_REG (op0)) == PLUS
-             && GET_CODE (XEXP (SUBREG_REG (op0), 1)) == CONST_INT
-             && INTVAL (XEXP (SUBREG_REG (op0), 1)) < 0
-             && (-INTVAL (XEXP (SUBREG_REG (op0), 1))
-                 < (HOST_WIDE_INT) (GET_MODE_MASK (mode) / 2))
-             && (unsigned HOST_WIDE_INT) const_op < GET_MODE_MASK (mode) / 2
-             && (0 == (nonzero_bits (XEXP (SUBREG_REG (op0), 0),
-                                     GET_MODE (SUBREG_REG (op0)))
-                       & ~GET_MODE_MASK (mode))
-                 || (num_sign_bit_copies (XEXP (SUBREG_REG (op0), 0),
-                                          GET_MODE (SUBREG_REG (op0)))
-                     > (unsigned int)
-                       (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
-                        - GET_MODE_BITSIZE (mode)))))
-           {
-             op0 = SUBREG_REG (op0);
-             continue;
+             && GET_CODE (XEXP (SUBREG_REG (op0), 1)) == CONST_INT)
+           {
+             enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
+             rtx a = XEXP (SUBREG_REG (op0), 0);
+             HOST_WIDE_INT c1 = -INTVAL (XEXP (SUBREG_REG (op0), 1));
+
+             if ((c1 > 0
+                  && (unsigned HOST_WIDE_INT) c1
+                      < (unsigned HOST_WIDE_INT) 1 << (mode_width - 1)
+                  && (equality_comparison_p || unsigned_comparison_p)
+                  /* (A - C1) zero-extends if it is positive and sign-extends
+                     if it is negative, C2 both zero- and sign-extends.  */
+                  && ((0 == (nonzero_bits (a, inner_mode)
+                             & ~GET_MODE_MASK (mode))
+                       && const_op >= 0)
+                      /* (A - C1) sign-extends if it is positive and 1-extends
+                         if it is negative, C2 both sign- and 1-extends.  */
+                      || (num_sign_bit_copies (a, inner_mode)
+                          > (unsigned int) (GET_MODE_BITSIZE (inner_mode)
+                                            - mode_width)
+                          && const_op < 0)))
+                 || ((unsigned HOST_WIDE_INT) c1
+                      < (unsigned HOST_WIDE_INT) 1 << (mode_width - 2)
+                     /* (A - C1) always sign-extends, like C2.  */
+                     && num_sign_bit_copies (a, inner_mode)
+                        > (unsigned int) (GET_MODE_BITSIZE (inner_mode)
+                                          - mode_width - 1)))
+               {
+                 op0 = SUBREG_REG (op0);
+                 continue;
+               }
            }
 
          /* If the inner mode is narrower and we are extracting the low part,
index 06441c8ac7c06023ef34d93e6d5020d29f26fb91..07c39cf43039db532833a973d7e315f7bf9406ce 100644 (file)
@@ -1,3 +1,8 @@
+2005-01-18  Eric Botcazou  <ebotcazou@libertysurf.fr>
+
+       * gcc.dg/short-compare-1.c: New test.
+       * gcc.dg/short-compare-2.c: Likewise.
+
 2005-01-18  Michael Matz  <matz@suse.de>
 
        * gcc.dg/Wno-pointer-sign.c: New test for -Wno-pointer-sign.
diff --git a/gcc/testsuite/gcc.dg/short-compare-1.c b/gcc/testsuite/gcc.dg/short-compare-1.c
new file mode 100644 (file)
index 0000000..6a4e388
--- /dev/null
@@ -0,0 +1,21 @@
+/* PR rtl-optimization/19296 */
+/* Origin: Falk Hueffner <falk@debian.org> */
+
+/* { dg-do run } */
+/* { dg-options "-O" } */
+/* { dg-options "-O -mtune=i686" { target i?86-*-* } } */
+/* { dg-options "-O -m32 -mtune=i686" { target x86_64-*-* } } */
+
+extern void abort(void);
+
+void f(unsigned short ad)
+{
+  if (ad >= 0x4000 && ad < 0xc000) 
+    abort();
+}
+
+int main(void)
+{
+  f(0xff00); 
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/short-compare-2.c b/gcc/testsuite/gcc.dg/short-compare-2.c
new file mode 100644 (file)
index 0000000..1c5963c
--- /dev/null
@@ -0,0 +1,22 @@
+/* PR rtl-optimization/19296 */
+/* Origin: Falk Hueffner <falk@debian.org> */
+/* Testcase by Andrew Pinski <pinskia@gcc.gnu.org> */
+
+/* { dg-do run } */
+/* { dg-options "-O" } */
+/* { dg-options "-O -mtune=i686" { target i?86-*-* } } */
+/* { dg-options "-O -m32 -mtune=i686" { target x86_64-*-* } } */
+
+extern void abort();
+
+void f(unsigned short ad)
+{
+  if ((short) (ad - 0x4000) >= 0)
+    abort();
+}
+
+int main(void)
+{
+  f(0xc000);
+  return 0;
+}