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