This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Fix force_to_mode (PR rtl-optimization/17099)


Hi!

force_to_mode (
(subreg:DI (ior:SI (lshiftrt:SI (reg:SI 125)
            (const_int 31 [0x1f]))
        (subreg/s:SI (reg:DI 134 [ iftmp.1 ]) 4)) 0)
, DImode, 3, 0, 0)
returns
(ior:DI (lshiftrt:DI (subreg:DI (reg:SI 125))
		     (const_int 31 [0x1f]))
	(reg:DI 134 [ iftmp.1 ]))
which is wrong, as in the first case the shift will produce
always either 0 or 1, while in the latter case the upper 32 bits
of (subreg:DI (reg:SI 125)) (i.e. the undefined ones)
are shifted down and the least significant bit from them is
still visible when ANDing with mask 0x3.
This bug breaks bootstrap on powerpc64-redhat-linux.

Looking at force_to_mode, there is:
  op_mode = ((GET_MODE_CLASS (mode) == GET_MODE_CLASS (GET_MODE (x))
              && have_insn_for (code, mode))
             ? mode : GET_MODE (x));
  if ((code == LSHIFTRT || code == ASHIFTRT)
      && GET_MODE_BITSIZE (mode) < GET_MODE_BITSIZE (GET_MODE (x)))
    op_mode = GET_MODE (x);
This means op_mode for LSHIFTRT will be different from GET_MODE (x) only
if op_mode is wider than GET_MODE (x).
So, the patch below will change anything only for cases like that,
and as shown above we must check for bits outside of the narrower,
not wider, mode set in inner_mask, because the original
LSHIFTRT in the narrower mode will clear any bits originally above
the mode, while LSHIFTRT in the wider mode will not.
The GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT check
(which would now need to be changed to
GET_MODE_BITSIZE (GET_MODE (x)) > HOST_BITS_PER_WIDE_INT
) is unnecessary, since a few lines above this we check for it:
      if (GET_CODE (XEXP (x, 1)) == CONST_INT
          && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT
          && GET_MODE_BITSIZE (op_mode) <= HOST_BITS_PER_WIDE_INT)
and we also know that
GET_MODE_BITSIZE (GET_MODE (x)) <= GET_MODE_BITSIZE (op_mode).

Things like:
force_to_mode (
(lshiftrt:SI (reg:SI 125)
	     (const_int 30 [0x1e]))
, DImode, 3, 0, 0)
will still be optimized just fine as previously, as there are no
bits outside of x's mode visible after shifting and ANDing with the mask
(== 3).

Bootstrapped/regtested on {x86_64,ppc64,ppc,s390,s390x,ia64}-redhat-linux.
Ok to commit?

2004-08-20  Jakub Jelinek  <jakub@redhat.com>

	PR rtl-optimization/17099
	* combine.c (force_to_mode): Check if inner_mask has any bits set
	outside of GET_MODE (x) instead of op_mode.

	* gcc.c-torture/execute/20040820-1.c: New test.

--- gcc/combine.c.jj	2004-08-20 12:09:15.895679199 +0200
+++ gcc/combine.c	2004-08-20 12:11:23.719686606 +0200
@@ -7151,9 +7151,8 @@ force_to_mode (rtx x, enum machine_mode 
 
 	  /* We can only change the mode of the shift if we can do arithmetic
 	     in the mode of the shift and INNER_MASK is no wider than the
-	     width of OP_MODE.  */
-	  if (GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT
-	      || (inner_mask & ~GET_MODE_MASK (op_mode)) != 0)
+	     width of X's mode.  */
+	  if ((inner_mask & ~GET_MODE_MASK (GET_MODE (x))) != 0)
 	    op_mode = GET_MODE (x);
 
 	  inner = force_to_mode (inner, op_mode, inner_mask, reg, next_select);
--- gcc/testsuite/gcc.c-torture/execute/20040820-1.c.jj	2004-08-20 12:20:50.108650386 +0200
+++ gcc/testsuite/gcc.c-torture/execute/20040820-1.c	2004-08-20 12:21:23.511474459 +0200
@@ -0,0 +1,24 @@
+/* PR rtl-optimization/17099 */
+
+extern void exit (int);
+extern void abort (void);
+
+void
+check (int a)
+{
+  if (a != 1)
+    abort ();
+}
+
+void
+test (int a, int b)
+{
+  check ((a ? 1 : 0) | (b ? 2 : 0));
+}
+
+int
+main (void)
+{
+  test (1, 0);
+  exit (0);
+}

	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]