regmove bug fix

Richard Henderson rth@cygnus.com
Fri Feb 19 23:08:00 GMT 1999


Regmove was not designed to cope with a two-address target that
represents a condition codes register in rtl as a hard register,
and whose addsi3 clobbers that hard reg.  It will unknowingly
emit such an instruction between a compare and branch while the
condition codes are live.

I've committed this patch to cure this failing.  It has been
reviewed by Joern and Jim.


r~


        * regmove.c (discover_flags_reg): New function.
        (flags_set_1, mark_flags_life_zones): New functions.
        (regmove_optimize): Call them.
        (fixup_match_1): Use insn modes rather than sets_cc0_p.

Index: regmove.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/regmove.c,v
retrieving revision 1.52
diff -c -p -d -r1.52 regmove.c
*** regmove.c	1999/01/23 19:45:43	1.52
--- regmove.c	1999/02/20 07:00:49
*************** struct match {
*** 53,58 ****
--- 53,62 ----
    int early_clobber[MAX_RECOG_OPERANDS];
  };
  
+ static rtx discover_flags_reg PROTO((void));
+ static void mark_flags_life_zones PROTO((rtx));
+ static void flags_set_1 PROTO((rtx, rtx));
+ 
  static int try_auto_increment PROTO((rtx, rtx, rtx, rtx, HOST_WIDE_INT, int));
  static int find_matches PROTO((rtx, struct match *));
  static int fixup_match_1 PROTO((rtx, rtx, rtx, rtx, rtx, int, int, int, FILE *))
*************** try_auto_increment (insn, inc_insn, inc_
*** 150,156 ****
--- 154,325 ----
      }
    return 0;
  }
+ 
+ /* Determine if the pattern generated by add_optab has a clobber,
+    such as might be issued for a flags hard register.  To make the
+    code elsewhere simpler, we handle cc0 in this same framework.
+ 
+    Return the register if one was discovered.  Return NULL_RTX if
+    if no flags were found.  Return pc_rtx if we got confused.  */
+ 
+ static rtx
+ discover_flags_reg ()
+ {
+   rtx tmp;
+   tmp = gen_rtx_REG (SImode, 10000);
+   tmp = gen_add3_insn (tmp, tmp, GEN_INT (2));
+ 
+   /* If we get something that isn't a simple set, or a 
+      [(set ..) (clobber ..)], this whole function will go wrong.  */
+   if (GET_CODE (tmp) == SET)
+     return NULL_RTX;
+   else if (GET_CODE (tmp) == PARALLEL)
+     {
+       int found;
+ 
+       if (XVECLEN (tmp, 0) != 2)
+ 	return pc_rtx;
+       tmp = XVECEXP (tmp, 0, 1);
+       if (GET_CODE (tmp) != CLOBBER)
+ 	return pc_rtx;
+       tmp = XEXP (tmp, 0);
+ 
+       /* Don't do anything foolish if the md wanted to clobber a
+ 	 scratch or something.  We only care about hard regs.
+ 	 Moreover we don't like the notion of subregs of hard regs.  */
+       if (GET_CODE (tmp) == SUBREG
+ 	  && GET_CODE (SUBREG_REG (tmp)) == REG
+ 	  && REGNO (SUBREG_REG (tmp)) < FIRST_PSEUDO_REGISTER)
+ 	return pc_rtx;
+       found = (GET_CODE (tmp) == REG && REGNO (tmp) < FIRST_PSEUDO_REGISTER);
+ 
+ #ifdef HAVE_cc0
+       /* If we're cc0, and we found a potential flags reg, bail.  */
+       return (found ? pc_rtx : cc0_rtx);
+ #else
+       return (found ? tmp : NULL_RTX);
+ #endif
+     }
+ 
+   return pc_rtx;
+ }
+ 
+ /* It is a tedious task identifying when the flags register is live and
+    when it is safe to optimize.  Since we process the instruction stream
+    multiple times, locate and record these live zones by marking the
+    mode of the instructions -- 
+ 
+    QImode is used on the instruction at which the flags becomes live.
+ 
+    HImode is used within the range (exclusive) that the flags are
+    live.  Thus the user of the flags is not marked.
+ 
+    All other instructions are cleared to VOIDmode.  */
+ 
+ /* Used to communicate with flags_set_1.  */
+ static rtx flags_set_1_rtx;
+ static int flags_set_1_set;
  
+ static void
+ mark_flags_life_zones (flags)
+      rtx flags;
+ {
+   int flags_regno;
+   int flags_nregs;
+   int block;
+ 
+   /* Simple cases first: if no flags, clear all modes.  If confusing,
+      mark the entire function as being in a flags shadow.  */
+   if (flags == NULL_RTX || flags == pc_rtx)
+     {
+       enum machine_mode mode = (flags ? HImode : VOIDmode);
+       rtx insn;
+       for (insn = get_insns(); insn; insn = NEXT_INSN (insn))
+ 	PUT_MODE (insn, mode);
+       return;
+     }
+ 
+ #ifdef HAVE_cc0
+   flags_regno = -1;
+   flags_nregs = 1;
+ #else
+   flags_regno = REGNO (flags);
+   flags_nregs = HARD_REGNO_NREGS (flags_regno, GET_MODE (flags));
+ #endif
+   flags_set_1_rtx = flags;
+ 
+   /* Process each basic block.  */
+   for (block = n_basic_blocks - 1; block >= 0; block--)
+     {
+       rtx insn, end;
+       int live;
+ 
+       insn = BLOCK_HEAD (block);
+       end = BLOCK_END (block);
+ 
+       /* Look out for the (unlikely) case of flags being live across
+ 	 basic block boundaries.  */
+       live = 0;
+ #ifndef HAVE_cc0
+       {
+ 	int i;
+ 	for (i = 0; i < flags_nregs; ++i)
+           live |= REGNO_REG_SET_P (basic_block_live_at_start[block],
+ 				   flags_regno + i);
+       }
+ #endif
+ 
+       while (1)
+ 	{
+ 	  /* Process liveness in reverse order of importance --
+ 	     alive, death, birth.  This lets more important info
+ 	     overwrite the mode of lesser info.  */
+ 
+ 	  if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ 	    {
+ #ifdef HAVE_cc0
+ 	      /* In the cc0 case, death is not marked in reg notes,
+ 		 but is instead the mere use of cc0 when it is alive.  */
+ 	      if (live && reg_mentioned_p (cc0_rtx, PATTERN (insn)))
+ 		live = 0;
+ #else
+ 	      /* In the hard reg case, we watch death notes.  */
+ 	      if (live && find_regno_note (insn, REG_DEAD, flags_regno))
+ 		live = 0;
+ #endif
+ 	      PUT_MODE (insn, (live ? HImode : VOIDmode));
+ 
+ 	      /* In either case, birth is denoted simply by it's presence
+ 		 as the destination of a set.  */
+ 	      flags_set_1_set = 0;
+ 	      note_stores (PATTERN (insn), flags_set_1);
+ 	      if (flags_set_1_set)
+ 		{
+ 		  live = 1;
+ 		  PUT_MODE (insn, QImode);
+ 		}
+ 	    }
+ 	  else
+ 	    PUT_MODE (insn, (live ? HImode : VOIDmode));
+ 
+ 	  if (insn == end)
+ 	    break;
+ 	  insn = NEXT_INSN (insn);
+ 	}
+     }
+ }
+ 
+ /* A subroutine of mark_flags_life_zones, called through note_stores.  */
+ 
+ static void
+ flags_set_1 (x, pat)
+      rtx x, pat;
+ {
+   if (GET_CODE (pat) == SET
+       && reg_overlap_mentioned_p (x, flags_set_1_rtx))
+     flags_set_1_set = 1;
+ }
+ 
  static int *regno_src_regno;
  
  /* Indicate how good a choice REG (which appears as a source) is to replace
*************** regmove_optimize (f, nregs, regmove_dump
*** 908,913 ****
--- 1077,1086 ----
    int i;
    rtx copy_src, copy_dst;
  
+   /* Find out where a potential flags register is live, and so that we
+      can supress some optimizations in those zones.  */
+   mark_flags_life_zones (discover_flags_reg ());
+ 
    regno_src_regno = (int *)alloca (sizeof *regno_src_regno * nregs);
    for (i = nregs; --i >= 0; ) regno_src_regno[i] = -1;
  
*************** fixup_match_1 (insn, set, src, src_subre
*** 1617,1629 ****
  			    && GET_CODE (SET_DEST (single_set (p))) == REG
  			    && (REGNO (SET_DEST (single_set (p)))
  				< FIRST_PSEUDO_REGISTER))
! #ifdef HAVE_cc0
! 		      /* We may not emit an insn directly
! 			 after P if the latter sets CC0.  */
! 		      && ! sets_cc0_p (PATTERN (p))
! #endif
! 		      )
! 
  		    {
  		      search_end = q;
  		      q = insn;
--- 1790,1798 ----
  			    && GET_CODE (SET_DEST (single_set (p))) == REG
  			    && (REGNO (SET_DEST (single_set (p)))
  				< FIRST_PSEUDO_REGISTER))
! 		      /* We may only emit an insn directly after P if we
! 			 are not in the shadow of a live flags register.  */
! 		      && GET_MODE (p) == VOIDmode)
  		    {
  		      search_end = q;
  		      q = insn;


More information about the Gcc-patches mailing list