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]

RFA: fix mode switching for functions returning values (patch forPR gcc/16482)


I've written this patch after the ST Copyright Assignment for past and future changes to gcc has been signed,
with help from Kaz Kojima, who has also tested this patch for i686-linux-gnu and sh4-unknown-linux-gnu.


The issue is that the mode switching code might need the return value register, and even if it doesn't, placing
the mode switching code after the USE of the return value, or between the return value copy and its USE,
prevents the first scheduling pass from protecting the copy / USE sequence as a scheduling group.


Thus, we have to place the final mode switch before the return value copy. Things get a bit more
'interesting' because that is not always possible, for various reasons explained in the comments.


But even in these cases, we have to place the mode switch code in front of the USE, because otherwise
the return value might be clobbered.
2004-11-12  J"orn Rennecke <joern.rennecke@st.com>
	    Kaz Kojima  <kkojima@gcc.gnu.org>

	* lcm.c (optimize_mode_switching): In MODE_ENTRY / MODE_EXIT
	case, set ENTRY_EXIT_EXTRA to 3.  If the function has a return value,
	put the final mode switch in front of the return value, inasmuch
	as that is possible.

Index: lcm.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/lcm.c,v
retrieving revision 1.67
diff -p -r1.67 lcm.c
*** lcm.c	30 Sep 2004 23:40:15 -0000	1.67
--- lcm.c	12 Nov 2004 12:11:54 -0000
*************** optimize_mode_switching (FILE *file)
*** 1007,1013 ****
  	   If NORMAL_MODE is defined, allow for two extra
  	   blocks split from the entry and exit block.  */
  #if defined (MODE_ENTRY) && defined (MODE_EXIT)
! 	entry_exit_extra = 2;
  #endif
  	bb_info[n_entities]
  	  = xcalloc (last_basic_block + entry_exit_extra, sizeof **bb_info);
--- 1007,1013 ----
  	   If NORMAL_MODE is defined, allow for two extra
  	   blocks split from the entry and exit block.  */
  #if defined (MODE_ENTRY) && defined (MODE_EXIT)
! 	entry_exit_extra = 3;
  #endif
  	bb_info[n_entities]
  	  = xcalloc (last_basic_block + entry_exit_extra, sizeof **bb_info);
*************** optimize_mode_switching (FILE *file)
*** 1034,1045 ****
      FOR_EACH_EDGE (eg, ei, EXIT_BLOCK_PTR->preds)
        if (eg->flags & EDGE_FALLTHRU)
  	{
! 	  regset live_at_end = eg->src->global_live_at_end;
  
  	  gcc_assert (!pre_exit);
! 	  pre_exit = split_edge (eg);
! 	  COPY_REG_SET (pre_exit->global_live_at_start, live_at_end);
! 	  COPY_REG_SET (pre_exit->global_live_at_end, live_at_end);
  	}
    }
  #endif
--- 1034,1187 ----
      FOR_EACH_EDGE (eg, ei, EXIT_BLOCK_PTR->preds)
        if (eg->flags & EDGE_FALLTHRU)
  	{
! 	  basic_block src_bb = eg->src;
! 	  regset live_at_end = src_bb->global_live_at_end;
! 	  rtx last_insn, ret_reg;
  
  	  gcc_assert (!pre_exit);
! 	  /* If this function returns a value at the end, we have to
! 	     insert the final mode switch before the return value copy
! 	     to its hard register.  */
! 	  if (EDGE_COUNT (EXIT_BLOCK_PTR->preds) == 1
! 	      && GET_CODE ((last_insn = BB_END (src_bb))) == INSN
! 	      && GET_CODE (PATTERN (last_insn)) == USE
! 	      && GET_CODE ((ret_reg = XEXP (PATTERN (last_insn), 0))) == REG)
! 	    {
! 	      int ret_start = REGNO (ret_reg);
! 	      int nregs = hard_regno_nregs[ret_start][GET_MODE (ret_reg)];
! 	      int ret_end = ret_start + nregs;
! 	      int short_block = 0;
! 	      int maybe_builtin_apply = 0;
! 	      int forced_late_switch = 0;
! 	      rtx before_return_copy;
! 
! 	      do
! 		{
! 		  rtx return_copy = PREV_INSN (last_insn);
! 		  rtx return_copy_pat, copy_reg;
! 		  int copy_start, copy_num;
! 
! 		  if (INSN_P (return_copy))
! 		    {
! 		      if (GET_CODE (PATTERN (return_copy)) == USE
! 			  && GET_CODE (XEXP (PATTERN (return_copy), 0)) == REG
! 			  && (FUNCTION_VALUE_REGNO_P
! 			      (REGNO (XEXP (PATTERN (return_copy), 0)))))
! 			{
! 			  maybe_builtin_apply = 1;
! 			  last_insn = return_copy;
! 			  continue;
! 			}
! 		      /* If the return register is not (in its entirety)
! 			 likely spilled, the return copy might be
! 			 partially or completely optimized away.  */
! 		      return_copy_pat = single_set (return_copy);
! 		      if (!return_copy_pat)
! 			{
! 			  return_copy_pat = PATTERN (return_copy);
! 			  if (GET_CODE (return_copy_pat) != CLOBBER)
! 			    break;
! 			}
! 		      copy_reg = SET_DEST (return_copy_pat);
! 		      if (GET_CODE (copy_reg) == REG)
! 			copy_start = REGNO (copy_reg);
! 		      else if (GET_CODE (copy_reg) == SUBREG
! 			       && GET_CODE (SUBREG_REG (copy_reg)) == REG)
! 			copy_start = REGNO (SUBREG_REG (copy_reg));
! 		      else
! 			break;
! 		      if (copy_start >= FIRST_PSEUDO_REGISTER)
! 			break;
! 		      copy_num
! 			= hard_regno_nregs[copy_start][GET_MODE (copy_reg)];
! 
! 		      /* If the return register is not likely spilled, - as is
! 			 the case for floating point on SH4 - then it might
! 			 be set by an arithmetic operation that needs a
! 			 different mode than the exit block.  */
! 		      for (j = n_entities - 1; j >= 0; j--)
! 			{
! 			  int e = entity_map[j];
! 			  int mode = MODE_NEEDED (e, return_copy);
! 
! 			  if (mode != num_modes[e] && mode != MODE_EXIT (e))
! 			    break;
! 			}
! 		      if (j >= 0)
! 			{
! 			  /* For the SH4, floating point loads depend on fpscr,
! 			     thus we might need to put the final mode switch
! 			     after the return value copy.  That is still OK,
! 			     because a floating point return value does not
! 			     conflict with address reloads.  */
! 			  if (copy_start >= ret_start
! 			      && copy_start + copy_num <= ret_end
! 			      && OBJECT_P (SET_SRC (return_copy_pat)))
! 			    forced_late_switch = 1;
! 			  break;
! 			}
! 
! 		      if (copy_start >= ret_start
! 			  && copy_start + copy_num <= ret_end)
! 			nregs -= copy_num;
! 		      else if (!maybe_builtin_apply
! 			       || !FUNCTION_VALUE_REGNO_P (copy_start))
! 			break;
! 		      last_insn = return_copy;
! 		    }
! 		  /* ??? Exception handling can lead to the return value
! 		     copy being already separated from the return value use,
! 		     as in  unwind-dw2.c .
! 		     Similarly, conditionally returning without a value,
! 		     and conditionally using builtin_return can lead to an
! 		     isolated use.  */
! 		  if (return_copy == BB_HEAD (src_bb))
! 		    {
! 		      short_block = 1;
! 		      break;
! 		    }
! 		  last_insn = return_copy;
! 		}
! 	      while (nregs);
! 	      /* If we didn't see a full return value copy, verify that there
! 		 is a plausible reason for this.  If some, but not all of the
! 		 return register is likely spilled, we can expect that there
! 		 is a copy for the likely spilled part.  */
! 	      if (nregs
! 		  && ! forced_late_switch
! 		  && ! short_block
! 		  && CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (ret_start))
! 		  && nregs == hard_regno_nregs[ret_start][GET_MODE (ret_reg)]
! 		  /* For multi-hard-register floating point values,
! 		     sometimes the likely-spilled part is ordinarily copied
! 		     first, then the other part is set with an arithmetic
! 		     operation.  This doesn't actually cause reload failures,
! 		     so let it pass.  */
! 		  && (GET_MODE_CLASS (GET_MODE (ret_reg)) == MODE_INT
! 		      || nregs == 1))
! 		abort ();
! 	      if (INSN_P (last_insn))
! 		{
! 		  before_return_copy
! 		    = emit_note_before (NOTE_INSN_DELETED, last_insn);
! 		  /* Instructions preceding LAST_INSN in the same block might
! 		     require a different mode than MODE_EXIT, so if we might
! 		     have such instructions, keep them in a separate block
! 		     from pre_exit.  */
! 		  if (last_insn != BB_HEAD (src_bb))
! 		    src_bb = split_block (src_bb,
! 					  PREV_INSN (before_return_copy))->dest;
! 		}
! 	      else
! 		before_return_copy = last_insn;
! 	      pre_exit = split_block (src_bb, before_return_copy)->src;
! 	    }
! 	  else
! 	    {
! 	      pre_exit = split_edge (eg);
! 	      COPY_REG_SET (pre_exit->global_live_at_start, live_at_end);
! 	      COPY_REG_SET (pre_exit->global_live_at_end, live_at_end);
! 	    }
  	}
    }
  #endif

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