This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] Fix force_to_mode (PR rtl-optimization/17099)
- From: Jakub Jelinek <jakub at redhat dot com>
- To: Richard Henderson <rth at redhat dot com>
- Cc: gcc-patches at gcc dot gnu dot org
- Date: Fri, 20 Aug 2004 08:34:54 -0400
- Subject: [PATCH] Fix force_to_mode (PR rtl-optimization/17099)
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
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