[gnu.gcc.bug] Is

wood@dg-rtp.dg.com (Tom Wood) (10/20/89)

I'm thoroughly stumped with this one.  We're attempting to rev up to
1.36 with the m88k compiler.  The code generated for this procedure
generates a bus error for the line "p->a = 5".  Here's the test case:

	typedef struct { int a:5; double b; } T;

	void set_T(p)
	     T *p;
	{
	  p->a = 5;
	  p->b = 2.0;
	}

The body of the procedure is wrong when it is optimized.  It is:

        ld.b     r11,r0,r2
        st.b     r11,r30,0x0008
        ld       r11,r30,0x0005			; This is the bug.
        mask     r11,r11,0x0007
        or       r11,r11,0x0028
        st       r11,r30,0x000c
        ld.b     r11,r30,0x000f
        st.b     r11,r0,r2
        or.u     r11,r0,hi16(0x40000000)        ; double 2.00000
        or       r12,r0,r0
        st.d     r11,r2,0x0008

A number of things are going wrong.  I'll work backwards from the bogus
instruction.  The instruction comes from the insn,

	(insn 20 8 9 (set (reg:SI 11)
	       (mem:SI (plus:SI (reg:SI 30)
		       (const_int 5)))) 83 (nil)
	   (nil))

generated by alter_subreg.  final_scan_insn uses alter_subreg on the insn

	(insn 20 8 9 (set (reg:SI 11)
	       (subreg:SI (mem:QI (plus:SI (reg:SI 30)
			   (const_int 8))) 0)) 83 (nil)
	   (nil))

What is wrong here?  Is this a valid translation?  If so, then the insn
being altered is wrong.  This insn is produced during global_alloc as a
result of spilling a pseudo register.  This in itself is bogus.  A
trivial program is causing a register spill!?  Anyway, the insn (from
.c.lreg) in question,

	(insn 8 6 9 (set (reg:QI 33)
	       (mem:QI (reg/v:SI 32))) 87 (insn_list 4 (nil))
	   (nil))

is translated into the three insns (from .c.greg),

	(insn 19 6 8 (set (reg:QI 11)
	       (mem:QI (reg/v:SI 2))) -1 (nil)
	   (nil))

	(insn:QI 8 19 20 (set (mem:QI (plus:SI (reg:SI 30)
		       (const_int 8)))
	       (reg:QI 11)) 87 (insn_list 4 (nil))
	   (nil))

	(insn 20 8 9 (set (reg:SI 11)
	       (subreg:SI (mem:QI (plus:SI (reg:SI 30)
			   (const_int 8))) 0)) -1 (nil)
	   (nil))

Here are the insns after reload_as_needed has completed, but before
eliminating all pseudo registers.  reload was called as the last action
of global_alloc.

	(insn 19 6 8 (set (reg:QI 11)
	       (mem:QI (reg/v:SI 2))) -1 (nil)
	   (nil))

	(insn:QI 8 19 20 (set (reg:QI 33)
	       (reg:QI 11)) 87 (insn_list 4 (nil))
	   (nil))

	(insn 20 8 9 (set (reg:SI 11)
	       (subreg:SI (reg:QI 33) 0)) -1 (nil)
	   (nil))

I see two problems happening:

     1) global_alloc is choosing to spill values it shouldn't.
     2) When a register widening subreg is applied to a pseudo register
	that is spilled, the subreg should go away.

All comments, suggestions, clues, and patches are welcome!
---
			Tom Wood	(919) 248-6067
			Data General, Research Triangle Park, NC
			{the known world}!rti!xyzzy!wood

wood@dg-rtp.dg.com (Tom Wood) (10/21/89)

I found more about what is causing the widening subreg problem.  A
change to expand_binop\optabs.c attempts to use widening subreg around
narrow operands to logical operators instead of only using convert_move
as was the case in 1.35.  This works fine, and in fact saves
instructions in the non-optimized case.  The trouble is that combine is
unable to deal with this construct in the same way it deals with
sign_extend & zero_extend.  Here again is the test case:

	typedef struct { unsigned int a:5; double b; } T;
	
	void set_T(p)
	     T *p; 
	{
	  p->a = 5;
	  p->b = 2.0;
	}

Here's the RTL after flow analysis (just before combine):
		
	(insn 8 6 9 (set (reg:QI 33)
	       (mem:QI (reg/v:SI 32))) -1 (insn_list 4 (nil))
	   (nil))
	
	(insn 9 8 10 (set (reg:SI 34)
	       (and:SI (subreg:SI (reg:QI 33) 0)
		   (const_int 7))) -1 (insn_list 8 (nil))
	   (expr_list:REG_DEAD (reg:QI 33)
	       (nil)))
	
	(insn 10 9 11 (set (reg:QI 35)
	       (subreg:QI (reg:SI 34) 0)) -1 (insn_list 9 (nil))
	   (expr_list:REG_DEAD (reg:SI 34)
	       (nil)))
	
Combine produces this:	
	
	(insn 8 6 9 (set (reg:QI 33)
	       (mem:QI (reg/v:SI 32))) -1 (insn_list 4 (nil))
	   (nil))
	
	(insn 9 8 10 (set (reg:SI 34)
	       (and:SI (subreg:SI (reg:QI 33) 0)
		   (const_int 7))) -1 (insn_list 8 (nil))
	   (expr_list:REG_DEAD (reg:QI 33)
	       (nil)))
	
The trouble is that unless these are combined further, later passes
go wrong.  The RTL wants to look like this.

	(insn 12 11 13 (set (reg:SI 38)
	       (and:SI (subreg:SI (mem:QI (reg/v:SI 32)) 0)
		   (const_int 7))) 236 (insn_list 11 (insn_list 10
					(insn_list 9 (insn_list 8
					 (insn_list 4 (nil))))))
	   (nil))

My fix is truly a kludge.  If you're not optimizing, go ahead with
the widening subreg form.  Otherwise, explictitly use zero_extend or
sign_extend which combine is able to deal with.  Here's the patch:

*** optabs.c.orig	Fri Oct 20 17:37:58 1989
--- optabs.c	Fri Oct 20 17:41:17 1989
***************
*** 308,314 ****
  		  if (no_extend)
  		    {
  		      temp = force_reg (GET_MODE (xop0), xop0);
! 		      xop0 = gen_rtx (optimize ? unsignedp ? ZERO_EXTEND : SIGN_EXTEND : SUBREG, wider_mode, temp, 0);
  		    }
  		  else
  		    {
--- 308,314 ----
  		  if (no_extend)
  		    {
  		      temp = force_reg (GET_MODE (xop0), xop0);
! 		      xop0 = gen_rtx (SUBREG, wider_mode, temp, 0);
  		    }
  		  else
  		    {
***************
*** 322,328 ****
  		  if (no_extend)
  		    {
  		      temp = force_reg (GET_MODE (xop1), xop1);
! 		      xop1 = gen_rtx (optimize ? unsignedp ? ZERO_EXTEND : SIGN_EXTEND : SUBREG, wider_mode, temp, 0);
  		    }
  		  else
  		    {
--- 322,328 ----
  		  if (no_extend)
  		    {
  		      temp = force_reg (GET_MODE (xop1), xop1);
! 		      xop1 = gen_rtx (SUBREG, wider_mode, temp, 0);
  		    }
  		  else
  		    {

Comments?
---
			Tom Wood	(919) 248-6067
			Data General, Research Triangle Park, NC
			{the known world}!rti!xyzzy!wood