HI to SI mode miss conversion on MIPS.
Clinton Popetz
cpopetz@cpopetz.com
Thu Feb 10 18:45:00 GMT 2000
On Thu, Feb 10, 2000 at 09:03:25PM +0900, Hiroyuki Machida wrote:
>
> Hi,
> I found the problem about HI to SI mode conversion on MIPS.
>
> REPEATED BY
> gcc -O2 -g -df -dc hisi.c -o hisi
> ./hisi
>
> VERSION
> gcc version 2.95.2 19991024 (release) for MIPS
> gcc version 2.96 20000124 (experimental) for MIPS
>
> The attached test program shows that gcc produces the incorrect
> object for swapping lower and upper byte. This test code derived
> from linux kernel linux/drivers/net/slhc.c.
>
> The test swaps 0x0000235f twice, expceted results is 0x0000235f. But
> you will get 0x005f235f on MIPSEB box. (This problem is occured even
> if on MIPSEL box)
>
> I tracked down in rough. In my observation, required "zero_extend"
> or somthing was vanished on the "combination-phase".
>
> Followings are interested portions of the test programe and
> corresponding RTL codes. The problem is occured at the line 65 of
> the C source code.
>
> In the hisi.c.08.flow, RTLs look good. But #78, #86 and #99 in the
> hisi.c.09.combine produce incorrect result.
> In my concern, The RTL corresspoindig to #95 in the hisi.c.08.flow,
> (set (reg:SI 111) (zero_extend:SI (reg:HI 111)))
> must be put at the next of
> #86 (set (subreg:SI (reg:HI 111) 0) (ior:SI (reg:SI 114) (reg:SI 117)))
> in the hisi.c.09.combine.
>
> The attached patch is a workaround for this problem. Does anyone
> have better solution or real fix ?
>
Yes, I think so. The problem seems to be in force_to_mode for ANDs with
a constant. There is the following block to make cheaper constants:
if (GET_CODE (x) == AND && GET_CODE (XEXP (x, 1)) == CONST_INT
&& GET_MODE_MASK (GET_MODE (x)) != mask
&& GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT)
{
HOST_WIDE_INT cval = (INTVAL (XEXP (x, 1))
| (GET_MODE_MASK (GET_MODE (x)) & ~ mask));
int width = GET_MODE_BITSIZE (GET_MODE (x));
rtx y;
/* If MODE is narrower that HOST_WIDE_INT and CVAL is a negative
number, sign extend it. */
if (width > 0 && width < HOST_BITS_PER_WIDE_INT
&& (cval & ((HOST_WIDE_INT) 1 << (width - 1))) != 0)
cval |= (HOST_WIDE_INT) -1 << width;
y = gen_binary (AND, GET_MODE (x), XEXP (x, 0), GEN_INT (cval));
if (GET_CODE (y) == AND && rtx_cost (y, SET) < rtx_cost (x, SET))
x = y;
}
So let's say we have something like this:
(and:SI (ashift:SI (reg:SI 111)
(const_int 8 [0x8]))
(const_int 65280 [0xff00]))
and we are forcing to HI, so mask is 0xffff. This makes cval 0xffffff00.
But even if combine knows nothing about reg 111, so that nonzero_bits for
it is 0xffffffff, then nonzero_bits for the ashift will be 0xffffff00, and
gen_binary will ellide the AND, returning the ashift, which is wrong.
We need to check that the result of gen_binary is still an AND before
using it.
-Clint
2000-02-10 Clinton Popetz <cpopetz@cygnus.com>
* combine.c (force_to_mode, case AND): When trying to
substitute a cheaper constant, make sure the result is
still an AND.
Index: combine.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/combine.c,v
retrieving revision 1.114
diff -c -2 -p -r1.114 combine.c
*** combine.c 2000/02/06 03:40:45 1.114
--- combine.c 2000/02/11 02:33:01
*************** force_to_mode (x, mode, mask, reg, just_
*** 6705,6709 ****
y = gen_binary (AND, GET_MODE (x), XEXP (x, 0), GEN_INT (cval));
! if (rtx_cost (y, SET) < rtx_cost (x, SET))
x = y;
}
--- 6705,6709 ----
y = gen_binary (AND, GET_MODE (x), XEXP (x, 0), GEN_INT (cval));
! if (GET_CODE (y) == AND && rtx_cost (y, SET) < rtx_cost (x, SET))
x = y;
}
More information about the Gcc-patches
mailing list