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] reload1.c: Improve register elimination.


Hi,

Attached is a patch to improve register elimination.

I have a testcase for which h8300-hms-gcc generates a dreadful
assembly code corresponding to

  ;; load 0 into register 0
  (set (reg:SI 0)
       (const_int 0))

  ;; add sp to register 0, which is known to contain 0.
  (set (reg:SI 0)
       (plus:SI (reg:SI 0)
                (reg:SI sp)))

Upon investigation, I found that the register elimination is not done
optimally.  RTL generation starts out with

    :

  (set (reg:SI xxx)
       (plus:SI (reg:SI 11 fp)
                (const_int -8)))

    :
    : some other insns
    :

  (set (reg:SI yyy)
       (plus:SI (reg:SI 11 fp)
                (const_int -8)))

    :

As expected, CSE will eliminate the common subexpression and replaces
the second occurence with something like

  (set (reg:SI yyy)
       (reg:SI xxx)

with a REG_EQUAL note being

  (plus:SI (reg:SI 11 fp)
           (const_int -8)))

In this particular test case, we happen to have FP - 8 == SP, so the
best register elimination would yield

  (set (reg:SI yyy)
       (reg:SI sp))

but eliminate_regs() does not want to do so and attempts to generate

  (set (reg:SI yyy)
       (plus:SI (reg:SI sp) (const_int 0)))

(See the comment containing string "special-case" in reload1.c.)  H8
does not have an instruction to load (plus (reg SP) (const_int 0)).
At the end of the day, I get the suboptimal sequence.

It turns out that eliminate_regs_in_insn() will do special register
elimination for a set like

  (set (reg)
       (plus (reg) (const_int)))

if the reg in the source happens to be an eliminable reg, but this
elimination does not apply to a set if the PLUS is hidden in a
REG_EQUAL note.

The patch tweaks eliminate_regs_in_insn() so that if it sees a set
with a REG_EQUAL note containing (plus (reg) (const_int)), where reg
is an eliminable reg, then it does the same register elimination
provided that no offset is generated.  (If I allow an offset, then we
end up complicating the source with PLUS when the source pseudo reg
has a hard reg assigned to it.)

This patch changes the suboptimal sequence above into

  (set (reg:SI 0)
       (reg:SI sp))

on h8300-hms, but testing with GCC source code shows no difference on
i686-pc-linux-gnu.

Tested on h8300-hms and i686-pc-linux-gnu.  OK to apply?

Kazu Hirata

2004-01-29  Kazu Hirata  <kazu@cs.umass.edu>

	* reload1.c (eliminate_regs_in_insn): If a set has a REG_EQUAL
	note containing (plus (reg) (const_int)), where reg is an
	eliminable reg, then perform the register elimination without
	depending on eliminate_regs().

Index: reload1.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/reload1.c,v
retrieving revision 1.420
diff -c -r1.420 reload1.c
*** reload1.c	17 Jan 2004 22:14:17 -0000	1.420
--- reload1.c	28 Jan 2004 04:05:06 -0000
***************
*** 2879,2884 ****
--- 2879,2885 ----
    rtx substed_operand[MAX_RECOG_OPERANDS];
    rtx orig_operand[MAX_RECOG_OPERANDS];
    struct elim_table *ep;
+   rtx plus_src;
  
    if (! insn_is_asm && icode < 0)
      {
***************
*** 2982,2998 ****
      }
  
    /* We allow one special case which happens to work on all machines we
!      currently support: a single set with the source being a PLUS of an
!      eliminable register and a constant.  */
!   if (old_set
!       && GET_CODE (SET_DEST (old_set)) == REG
!       && GET_CODE (SET_SRC (old_set)) == PLUS
!       && GET_CODE (XEXP (SET_SRC (old_set), 0)) == REG
!       && GET_CODE (XEXP (SET_SRC (old_set), 1)) == CONST_INT
!       && REGNO (XEXP (SET_SRC (old_set), 0)) < FIRST_PSEUDO_REGISTER)
      {
!       rtx reg = XEXP (SET_SRC (old_set), 0);
!       HOST_WIDE_INT offset = INTVAL (XEXP (SET_SRC (old_set), 1));
  
        for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
  	if (ep->from_rtx == reg && ep->can_eliminate)
--- 2983,3022 ----
      }
  
    /* We allow one special case which happens to work on all machines we
!      currently support: a single set with the source or a REG_EQUAL
!      note being a PLUS of an eliminable register and a constant.  */
!   plus_src = 0;
!   if (old_set && GET_CODE (SET_DEST (old_set)) == REG)
      {
!       /* First see if the source is of the form (plus (reg) CST).  */
!       if (GET_CODE (SET_SRC (old_set)) == PLUS
! 	  && GET_CODE (XEXP (SET_SRC (old_set), 0)) == REG
! 	  && GET_CODE (XEXP (SET_SRC (old_set), 1)) == CONST_INT
! 	  && REGNO (XEXP (SET_SRC (old_set), 0)) < FIRST_PSEUDO_REGISTER)
! 	plus_src = SET_SRC (old_set);
!       else if (GET_CODE (SET_SRC (old_set)) == REG)
! 	{
! 	  /* Otherwise, see if we have a REG_EQUAL note of the form
! 	     (plus (reg) CST).  */
! 	  rtx links;
! 	  for (links = REG_NOTES (insn); links; links = XEXP (links, 1))
! 	    {
! 	      if (REG_NOTE_KIND (links) == REG_EQUAL
! 		  && GET_CODE (XEXP (links, 0)) == PLUS
! 		  && GET_CODE (XEXP (XEXP (links, 0), 0)) == REG
! 		  && GET_CODE (XEXP (XEXP (links, 0), 1)) == CONST_INT
! 		  && REGNO (XEXP (XEXP (links, 0), 0)) < FIRST_PSEUDO_REGISTER)
! 		{
! 		  plus_src = XEXP (links, 0);
! 		  break;
! 		}
! 	    }
! 	}
!     }
!   if (plus_src)
!     {
!       rtx reg = XEXP (plus_src, 0);
!       HOST_WIDE_INT offset = INTVAL (XEXP (plus_src, 1));
  
        for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
  	if (ep->from_rtx == reg && ep->can_eliminate)
***************
*** 3022,3028 ****
  		if (INSN_CODE (insn) < 0)
  		  abort ();
  	      }
! 	    else
  	      {
  		new_body = old_body;
  		if (! replace)
--- 3046,3057 ----
  		if (INSN_CODE (insn) < 0)
  		  abort ();
  	      }
! 	    /* If we have a nonzero offset, and the source is already
! 	       a simple REG, the following transformation would
! 	       increase the cost of the insn by replacing a simple REG
! 	       with (plus (reg sp) CST).  So try only when plus_src
! 	       comes from old_set proper, not REG_NOTES.  */
! 	    else if (SET_SRC (old_set) == plus_src)
  	      {
  		new_body = old_body;
  		if (! replace)
***************
*** 3037,3042 ****
--- 3066,3074 ----
  		XEXP (SET_SRC (old_set), 0) = ep->to_rtx;
  		XEXP (SET_SRC (old_set), 1) = GEN_INT (offset);
  	      }
+ 	    else
+ 	      break;
+ 
  	    val = 1;
  	    /* This can't have an effect on elimination offsets, so skip right
  	       to the end.  */


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