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]

New function: simplify_subreg


Hi
This code breaks out the SUBREG simplification code out of combine and
recog and puts it to separate function simplify_subreg saving over 300
lines of code.

It is really wonderfull how quickly that pieces of code diverged, since
I've created it by copying and modyfing the existing 3 simplifiers from
combine.c just few months ago.

Since such patch is really anoying to review, here is list of important
points I've changed

1) I've changed all handcoded subreg_lowpart_p tests to subreg_lowpart_parts_p.
2) combine.c contains following simplifier:
- 	  /* If the innermost mode is the same as the goal mode,
- 	     and the low word is being referenced in both SUBREGs,
- 	     return the innermost element.  */
- 	  if (mode == GET_MODE (SUBREG_REG (SUBREG_REG (x))))
- 	    {
- 	      int inner_word = SUBREG_BYTE (SUBREG_REG (x));
- 	      int outer_word = SUBREG_BYTE (x);
- 
- 	      inner_word = (inner_word / UNITS_PER_WORD) * UNITS_PER_WORD;
- 	      outer_word = (outer_word / UNITS_PER_WORD) * UNITS_PER_WORD;
- 	      if (inner_word == 0
- 		  && outer_word == 0)
- 		return SUBREG_REG (SUBREG_REG (x));
- 	    }
  IMO it is incorect to round the SUBREG_BYTE to word, as it it currect only
  for both words equal to 0
3) combine.c takes care to compensate size difference on combining
   subreg to paradoxical subreg, recog don't.  I now use that code
   for both and believe it is correct
4) combine.c sometimes simply round byte to multiple of GET_MODE_SIZE (innermode).
   I suspect it is both incorrect and unneeded, so I don't.

Note that I plan to reorganize the simplify_subreg itself, but for now
I just made it superset of both codes to keep patch revieable.

In future I would like to kill simplifiers in functions like gen_lowpart,
gen_highpart etc and move them all here.

I am still running the regtesting, but it has got past point I got no
failures before fixing previous bug.  I will let you know if something
breaks.

Wed May  2 17:12:49 CEST 2001  Jan Hubicka  <jh@suse.cz>
	* simplify-rtx.c (simplify_subreg): Break out from ...
	* combine.c (combine_splify_rtx) ... here and ...
	* recog.c (validate_replace_rtx_1): ... here;
	* rtl.h (subreg_lowpart_parts_p, simplify_subreg): Declare.
	* emit-rtl.c (subreg_lowpart_parts_p): Break out from ...
	(subreg_lowpart_p): ... here.
Index: combine.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/combine.c,v
retrieving revision 1.197
diff -c -3 -p -r1.197 combine.c
*** combine.c	2001/05/01 12:11:31	1.197
--- combine.c	2001/05/02 15:23:36
*************** combine_simplify_rtx (x, op0_mode, last,
*** 3764,3769 ****
--- 3765,3773 ----
        break;
  
      case SUBREG:
+       if (op0_mode == VOIDmode)
+ 	op0_mode = GET_MODE (SUBREG_REG (x));
+ 
        /* (subreg:A (mem:B X) N) becomes a modified MEM unless the SUBREG
  	 is paradoxical.  If we can't do that safely, then it becomes
  	 something nonsensical so that this combination won't take place.  */
*************** combine_simplify_rtx (x, op0_mode, last,
*** 3772,3924 ****
  	  && (GET_MODE_SIZE (mode)
  	      <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))))
  	{
! 	  rtx inner = SUBREG_REG (x);
! 	  int offset = SUBREG_BYTE (x);
! 	  /* Don't change the mode of the MEM
! 	     if that would change the meaning of the address.  */
! 	  if (MEM_VOLATILE_P (SUBREG_REG (x))
! 	      || mode_dependent_address_p (XEXP (inner, 0)))
! 	    return gen_rtx_CLOBBER (mode, const0_rtx);
! 
! 	  /* Note if the plus_constant doesn't make a valid address
! 	     then this combination won't be accepted.  */
! 	  x = gen_rtx_MEM (mode,
! 			   plus_constant (XEXP (inner, 0), offset));
! 	  MEM_COPY_ATTRIBUTES (x, inner);
! 	  return x;
  	}
  
        /* If we are in a SET_DEST, these other cases can't apply.  */
        if (in_dest)
  	return x;
- 
-       /* Changing mode twice with SUBREG => just change it once,
- 	 or not at all if changing back to starting mode.  */
-       if (GET_CODE (SUBREG_REG (x)) == SUBREG)
- 	{
- 	  int final_offset;
- 	  enum machine_mode outer_mode, inner_mode;
- 
- 	  /* If the innermost mode is the same as the goal mode,
- 	     and the low word is being referenced in both SUBREGs,
- 	     return the innermost element.  */
- 	  if (mode == GET_MODE (SUBREG_REG (SUBREG_REG (x))))
- 	    {
- 	      int inner_word = SUBREG_BYTE (SUBREG_REG (x));
- 	      int outer_word = SUBREG_BYTE (x);
- 
- 	      inner_word = (inner_word / UNITS_PER_WORD) * UNITS_PER_WORD;
- 	      outer_word = (outer_word / UNITS_PER_WORD) * UNITS_PER_WORD;
- 	      if (inner_word == 0
- 		  && outer_word == 0)
- 		return SUBREG_REG (SUBREG_REG (x));
- 	    }
- 
- 	  outer_mode = GET_MODE (SUBREG_REG (x));
- 	  inner_mode = GET_MODE (SUBREG_REG (SUBREG_REG (x)));
- 	  final_offset = SUBREG_BYTE (x) + SUBREG_BYTE (SUBREG_REG(x));
- 
- 	  if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN)
- 	      && GET_MODE_SIZE (outer_mode) > GET_MODE_SIZE (mode)
- 	      && GET_MODE_SIZE (outer_mode) > GET_MODE_SIZE (inner_mode))
- 	    {
- 	      /* Inner SUBREG is paradoxical, outer is not.  On big endian
- 		 we have to special case this.  */
- 	      if (SUBREG_BYTE (SUBREG_REG (x)))
- 		abort(); /* Can a paradoxical subreg have nonzero offset? */
- 	      if (WORDS_BIG_ENDIAN && BYTES_BIG_ENDIAN)
- 	        final_offset = SUBREG_BYTE (x) - GET_MODE_SIZE (outer_mode)
- 			       + GET_MODE_SIZE (inner_mode);
- 	      else if (WORDS_BIG_ENDIAN)
- 		final_offset = (final_offset % UNITS_PER_WORD)
- 			       + ((SUBREG_BYTE (x) - GET_MODE_SIZE (outer_mode)
- 				   + GET_MODE_SIZE (inner_mode))
- 				  * UNITS_PER_WORD) / UNITS_PER_WORD;
- 	      else
- 		final_offset = ((final_offset * UNITS_PER_WORD)
- 				/ UNITS_PER_WORD)
- 			       + ((SUBREG_BYTE (x) - GET_MODE_SIZE (outer_mode)
- 				   + GET_MODE_SIZE (inner_mode))
- 				  % UNITS_PER_WORD);
- 	    }
- 
- 	  /* The SUBREG rules are that the byte offset must be
- 	     some multiple of the toplevel SUBREG's mode.  */
- 	  final_offset = (final_offset / GET_MODE_SIZE (mode));
- 	  final_offset = (final_offset * GET_MODE_SIZE (mode));
  
! 	  SUBST_INT (SUBREG_BYTE (x), final_offset);
! 	  SUBST (SUBREG_REG (x), SUBREG_REG (SUBREG_REG (x)));
! 	}
! 
!       /* SUBREG of a hard register => just change the register number
! 	 and/or mode.  If the hard register is not valid in that mode,
! 	 suppress this combination.  If the hard register is the stack,
! 	 frame, or argument pointer, leave this as a SUBREG.  */
! 
!       if (GET_CODE (SUBREG_REG (x)) == REG
! 	  && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER
! 	  && REGNO (SUBREG_REG (x)) != FRAME_POINTER_REGNUM
! #if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
! 	  && REGNO (SUBREG_REG (x)) != HARD_FRAME_POINTER_REGNUM
! #endif
! #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
! 	  && REGNO (SUBREG_REG (x)) != ARG_POINTER_REGNUM
! #endif
! 	  && REGNO (SUBREG_REG (x)) != STACK_POINTER_REGNUM)
! 	{
! 	  int final_regno = subreg_hard_regno (x, 0);
! 
! 	  if (HARD_REGNO_MODE_OK (final_regno, mode))
! 	    return gen_rtx_REG (mode, final_regno);
! 	  else
! 	    return gen_rtx_CLOBBER (mode, const0_rtx);
! 	}
! 
!       /* For a constant, try to pick up the part we want.  Handle a full
! 	 word and low-order part.  Only do this if we are narrowing
! 	 the constant; if it is being widened, we have no idea what
! 	 the extra bits will have been set to.  */
! 
!       if (CONSTANT_P (SUBREG_REG (x)) && op0_mode != VOIDmode
! 	  && GET_MODE_SIZE (mode) == UNITS_PER_WORD
! 	  && GET_MODE_SIZE (op0_mode) > UNITS_PER_WORD
! 	  && GET_MODE_CLASS (mode) == MODE_INT)
! 	{
! 	  temp = operand_subword (SUBREG_REG (x),
! 				  (SUBREG_BYTE (x) / UNITS_PER_WORD),
! 				  0, op0_mode);
! 	  if (temp)
! 	    return temp;
! 	}
! 
!       /* If we want a subreg of a constant, at offset 0,
! 	 take the low bits.  On a little-endian machine, that's
! 	 always valid.  On a big-endian machine, it's valid
! 	 only if the constant's mode fits in one word.   Note that we
! 	 cannot use subreg_lowpart_p since SUBREG_REG may be VOIDmode.  */
        if (CONSTANT_P (SUBREG_REG (x))
! 	  && ((GET_MODE_SIZE (op0_mode) <= UNITS_PER_WORD
! 	      || ! WORDS_BIG_ENDIAN)
! 	      ? SUBREG_BYTE (x) == 0
! 	      : (SUBREG_BYTE (x)
! 		 == (GET_MODE_SIZE (op0_mode) - GET_MODE_SIZE (mode))))
! 	  && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (op0_mode)
! 	  && (! WORDS_BIG_ENDIAN
! 	      || GET_MODE_BITSIZE (op0_mode) <= BITS_PER_WORD))
  	return gen_lowpart_for_combine (mode, SUBREG_REG (x));
  
!       /* A paradoxical SUBREG of a VOIDmode constant is the same constant,
! 	 since we are saying that the high bits don't matter.  */
!       if (CONSTANT_P (SUBREG_REG (x)) && GET_MODE (SUBREG_REG (x)) == VOIDmode
! 	  && GET_MODE_SIZE (mode) > GET_MODE_SIZE (op0_mode))
! 	{
! 	  if (GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) > UNITS_PER_WORD
! 	      && (WORDS_BIG_ENDIAN || SUBREG_BYTE (x) != 0))
! 	    return constant_subword (SUBREG_REG (x), 
! 				     SUBREG_BYTE (x) / UNITS_PER_WORD, mode);
! 	  return SUBREG_REG (x);
! 	}
  
        /* Note that we cannot do any narrowing for non-constants since
  	 we might have been counting on using the fact that some bits were
--- 3776,3805 ----
  	  && (GET_MODE_SIZE (mode)
  	      <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))))
  	{
! 	  rtx temp;
! 	  temp = simplify_subreg (mode, SUBREG_REG (x), op0_mode,
! 				  SUBREG_BYTE (x));
! 	  if (temp)
! 	    return temp;
  	}
  
        /* If we are in a SET_DEST, these other cases can't apply.  */
        if (in_dest)
  	return x;
  
!       /* An SIMPLIFY_SUBREG is able to handle this case as well, but
!          we want to go throught gen_lowpart_for_combine.  */
        if (CONSTANT_P (SUBREG_REG (x))
! 	  && subreg_lowpart_parts_p (mode, op0_mode, SUBREG_BYTE (x)))
  	return gen_lowpart_for_combine (mode, SUBREG_REG (x));
  
!       {
! 	rtx temp;
! 	temp = simplify_subreg (mode, SUBREG_REG (x), op0_mode,
! 		       		SUBREG_BYTE (x));
! 	if (temp)
! 	  return temp;
!       }
  
        /* Note that we cannot do any narrowing for non-constants since
  	 we might have been counting on using the fact that some bits were
Index: emit-rtl.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/emit-rtl.c,v
retrieving revision 1.173
diff -c -3 -p -r1.173 emit-rtl.c
*** emit-rtl.c	2001/04/30 16:07:25	1.173
--- emit-rtl.c	2001/05/02 15:23:41
*************** gen_highpart (mode, x)
*** 1278,1284 ****
--- 1278,1305 ----
    else
      abort ();
  }
+ /* Return 1 iff (SUBREG:outermode (OP:innermode) byte)
+    refers to the least significant part of its containing reg.  */
  
+ int
+ subreg_lowpart_parts_p (outermode, innermode, byte)
+      enum machine_mode outermode, innermode;
+      unsigned int byte;
+ {
+   unsigned int offset = 0;
+   int difference = (GET_MODE_SIZE (innermode) - GET_MODE_SIZE (outermode));
+ 
+   if (difference > 0)
+     {
+       if (WORDS_BIG_ENDIAN)
+ 	offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
+       if (BYTES_BIG_ENDIAN)
+ 	offset += difference % UNITS_PER_WORD;
+     }
+ 
+   return byte == offset;
+ }
+ 
  /* Return 1 iff X, assumed to be a SUBREG,
     refers to the least significant part of its containing reg.
     If X is not a SUBREG, always return 1 (it is its own low part!).  */
*************** subreg_lowpart_p (x)
*** 1295,1310 ****
      return 1;
    else if (GET_MODE (SUBREG_REG (x)) == VOIDmode)
      return 0;
- 
-   if (difference > 0)
-     {
-       if (WORDS_BIG_ENDIAN)
- 	offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
-       if (BYTES_BIG_ENDIAN)
- 	offset += difference % UNITS_PER_WORD;
-     }
  
!   return SUBREG_BYTE (x) == offset;
  }
  
  
--- 1316,1324 ----
      return 1;
    else if (GET_MODE (SUBREG_REG (x)) == VOIDmode)
      return 0;
  
!   return subreg_lowpart_parts_p (GET_MODE (x), GET_MODE (SUBREG_REG (x)),
! 		 		 SUBREG_BYTE (x));
  }
  
  
Index: recog.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/recog.c,v
retrieving revision 1.98
diff -c -3 -p -r1.98 recog.c
*** recog.c	2001/05/01 12:11:33	1.98
--- recog.c	2001/05/02 15:23:42
*************** validate_replace_rtx_1 (loc, from, to, o
*** 574,718 ****
        /* In case we are replacing by constant, attempt to simplify it to
  	 non-SUBREG expression.  We can't do this later, since the information
  	 about inner mode may be lost.  */
!       if (CONSTANT_P (to) && rtx_equal_p (SUBREG_REG (x), from))
          {
! 	  int offset, part;
! 	  unsigned HOST_WIDE_INT val;
! 
! 	  /* A paradoxical SUBREG of a VOIDmode constant is the same constant,
! 	     since we are saying that the high bits don't matter.  */
! 	  if (GET_MODE (to) == VOIDmode
! 	      && (GET_MODE_SIZE (GET_MODE (x))
! 		  >= GET_MODE_SIZE (GET_MODE (from))))
  	    {
! 	      rtx new = gen_lowpart_if_possible (GET_MODE (x), to);
! 	      if (new)
! 		{
! 		  validate_change (object, loc, new, 1);
! 		  return;
! 		}
  	    }
! 
! 	  offset = SUBREG_BYTE (x) * BITS_PER_UNIT;
! 	  switch (GET_CODE (to))
  	    {
! 	    case CONST_DOUBLE:
! 	      if (GET_MODE (to) != VOIDmode)
! 		break;
! 
! 	      part = offset >= HOST_BITS_PER_WIDE_INT;
! 	      if ((BITS_PER_WORD > HOST_BITS_PER_WIDE_INT
! 		   && BYTES_BIG_ENDIAN)
! 		  || (BITS_PER_WORD <= HOST_BITS_PER_WIDE_INT
! 		      && WORDS_BIG_ENDIAN))
! 		part = !part;
! 	      val = part ? CONST_DOUBLE_HIGH (to) : CONST_DOUBLE_LOW (to);
! 	      offset %= HOST_BITS_PER_WIDE_INT;
! 
! 	      /* FALLTHROUGH */
! 	    case CONST_INT:
! 	      if (GET_CODE (to) == CONST_INT)
! 		val = INTVAL (to);
! 
! 	      {
! 		/* Avoid creating bogus SUBREGs */
! 		enum machine_mode mode = GET_MODE (x);
! 		enum machine_mode inner_mode = GET_MODE (from);
! 
! 		/* We've already picked the word we want from a double, so 
! 		   pretend this is actually an integer.  */
! 		if (GET_CODE (to) == CONST_DOUBLE)
! 		  inner_mode = SImode;
! 
! 		if (GET_MODE_CLASS (mode) != MODE_INT)
! 		  {
! 		    /* Substitute in something that we know won't be
! 		       recognized.  */
! 		    to = gen_rtx_CLOBBER (GET_MODE (x), const0_rtx);
! 		    validate_change (object, loc, to, 1);
! 		    return;
! 		  }
! 
! 		if (BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
! 		  {
! 		    if (WORDS_BIG_ENDIAN)
! 		      offset = GET_MODE_BITSIZE (inner_mode)
! 			       - GET_MODE_BITSIZE (mode) - offset;
! 		    if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN
! 			&& GET_MODE_SIZE (mode) < UNITS_PER_WORD)
! 		      offset = offset + BITS_PER_WORD - GET_MODE_BITSIZE (mode)
! 			       - 2 * (offset % BITS_PER_WORD);
! 		  }
! 
! 		if (offset >= HOST_BITS_PER_WIDE_INT)
! 		  to = ((HOST_WIDE_INT) val < 0) ? constm1_rtx : const0_rtx;
! 		else
! 		  {
! 		    val >>= offset;
! 		    if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT)
! 		      val = trunc_int_for_mode (val, mode);
! 		    to = GEN_INT (val);
! 		  }
! 
! 		validate_change (object, loc, to, 1);
! 		return;
! 	      }
! 
! 	    default:
! 	      break;
! 	    }
!         }
! 
!       /* Changing mode twice with SUBREG => just change it once,
! 	 or not at all if changing back to starting mode.  */
!       if (GET_CODE (to) == SUBREG
! 	  && rtx_equal_p (SUBREG_REG (x), from))
! 	{
! 	  if (GET_MODE (x) == GET_MODE (SUBREG_REG (to))
! 	      && SUBREG_BYTE (x) == 0 && SUBREG_BYTE (to) == 0)
! 	    {
! 	      validate_change (object, loc, SUBREG_REG (to), 1);
  	      return;
  	    }
! 
! 	  /* Make sure the 2 byte counts added together are an even unit
! 	     of x's mode, and combine them if so. Otherwise we run
! 	     into problems with something like:
! 		(subreg:HI (subreg:QI (SI:55) 3) 0)
! 	     we end up with an odd offset into a HI which is invalid.  */
! 
! 	  if (SUBREG_BYTE (to) % GET_MODE_SIZE (GET_MODE (x)) == 0)
! 	    validate_change (object, loc,
! 			     gen_rtx_SUBREG (GET_MODE (x), SUBREG_REG (to),
! 					     SUBREG_BYTE(x) + SUBREG_BYTE (to)),
! 			     1);
! 	  else
! 	    validate_change (object, loc, to, 1);	
! 
! 	  return;
! 	}
! 
!       /* If we have a SUBREG of a register that we are replacing and we are
! 	 replacing it with a MEM, make a new MEM and try replacing the
! 	 SUBREG with it.  Don't do this if the MEM has a mode-dependent address
! 	 or if we would be widening it.  */
! 
!       if (GET_CODE (from) == REG
! 	  && GET_CODE (to) == MEM
! 	  && rtx_equal_p (SUBREG_REG (x), from)
! 	  && ! mode_dependent_address_p (XEXP (to, 0))
! 	  && ! MEM_VOLATILE_P (to)
! 	  && GET_MODE_SIZE (GET_MODE (x)) <= GET_MODE_SIZE (GET_MODE (to)))
! 	{
! 	  int offset = SUBREG_BYTE (x);
! 	  enum machine_mode mode = GET_MODE (x);
! 	  rtx new;
! 
! 	  new = gen_rtx_MEM (mode, plus_constant (XEXP (to, 0), offset));
! 	  MEM_COPY_ATTRIBUTES (new, to);
! 	  validate_change (object, loc, new, 1);
! 	  return;
! 	}
        break;
  
      case ZERO_EXTRACT:
--- 574,599 ----
        /* In case we are replacing by constant, attempt to simplify it to
  	 non-SUBREG expression.  We can't do this later, since the information
  	 about inner mode may be lost.  */
!       if (rtx_equal_p (SUBREG_REG (x), from))
          {
! 	  rtx temp;
! 	  temp = simplify_subreg (GET_MODE (x), to, GET_MODE (SUBREG_REG (x)),
! 				  SUBREG_BYTE (x));
! 	  if (temp)
  	    {
! 	      validate_change (object, loc, temp, 1);
! 	      return;
  	    }
! 	  /* Avoid creating of invalid SUBREGS.  */
! 	  if (GET_MODE (from) == VOIDmode)
  	    {
! 	      /* Substitute in something that we know won't be
! 		 recognized.  */
! 	      to = gen_rtx_CLOBBER (GET_MODE (x), const0_rtx);
! 	      validate_change (object, loc, to, 1);
  	      return;
  	    }
!         }
        break;
  
      case ZERO_EXTRACT:
Index: rtl.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/rtl.h,v
retrieving revision 1.257
diff -c -3 -p -r1.257 rtl.h
*** rtl.h	2001/04/28 19:16:29	1.257
--- rtl.h	2001/05/02 15:23:43
*************** extern rtx constant_subword		PARAMS ((rt
*** 1194,1199 ****
--- 1194,1202 ----
  extern rtx operand_subword_force	PARAMS ((rtx, unsigned int,
  						 enum machine_mode));
  extern int subreg_lowpart_p		PARAMS ((rtx));
+ extern int subreg_lowpart_parts_p	PARAMS ((enum machine_mode,
+ 						 enum machine_mode,
+ 						 unsigned int));
  extern rtx make_safe_from		PARAMS ((rtx, rtx));
  extern rtx convert_memory_address	PARAMS ((enum machine_mode, rtx));
  extern rtx get_insns			PARAMS ((void));
*************** extern rtx simplify_gen_relational	PARAM
*** 1320,1325 ****
--- 1323,1332 ----
  						 enum machine_mode,
  						 enum machine_mode,
  						 rtx, rtx));
+ extern rtx simplify_subreg		PARAMS ((enum machine_mode,
+ 						 rtx,
+ 						 enum machine_mode,
+ 						 unsigned int));
  extern rtx simplify_replace_rtx		PARAMS ((rtx, rtx, rtx));
  extern rtx simplify_rtx			PARAMS ((rtx));
  
Index: simplify-rtx.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/simplify-rtx.c,v
retrieving revision 1.47
diff -c -3 -p -r1.47 simplify-rtx.c
*** simplify-rtx.c	2001/05/01 12:11:34	1.47
--- simplify-rtx.c	2001/05/02 15:23:44
*************** simplify_ternary_operation (code, mode, 
*** 2179,2184 ****
--- 2179,2378 ----
    return 0;
  }
  
+ /* Simplify SUBREG:OUTERMODE(OP:INNERMODE, BYTE)
+    Return 0 if no simplifications is possible.  */
+ rtx
+ simplify_subreg (outermode, op, innermode, byte)
+      rtx op;
+      unsigned int byte;
+      enum machine_mode outermode, innermode;
+ {
+   /* Little bit of sanity checking.  */
+   if (innermode == VOIDmode || outermode == VOIDmode
+       || innermode == BLKmode || outermode == BLKmode)
+     abort ();
+ 
+   if (GET_MODE (op) != innermode
+       && GET_MODE (op) != VOIDmode)
+     abort ();
+ 
+   if (byte % GET_MODE_SIZE (outermode)
+       || byte >= GET_MODE_SIZE (innermode))
+     abort ();
+ 
+   /* Attempt to simplify constant to non-SUBREG expression.  */
+   if (CONSTANT_P (op))
+     {
+       int offset, part;
+       unsigned HOST_WIDE_INT val;
+ 
+       /* ??? This code is partly redundant with code bellow, but can handle
+ 	 the subregs of floats and similar corner cases.
+ 	 Later it we should move all simplification code here and rewrite
+ 	 GEN_LOWPART_IF_POSSIBLE, GEN_HIGHPART, OPERAND_SUBWORD and friends
+ 	 using SIMPLIFY_SUBREG.  */
+       if (subreg_lowpart_parts_p (outermode, innermode, byte))
+ 	{
+ 	  rtx new = gen_lowpart_if_possible (outermode, op);
+ 	  if (new)
+ 	    return new;
+ 	}
+ 
+       /* Similar comment as above apply here.  */
+       if (GET_MODE_SIZE (outermode) == UNITS_PER_WORD
+ 	  && GET_MODE_SIZE (innermode) > UNITS_PER_WORD
+ 	  && GET_MODE_CLASS (outermode) == MODE_INT)
+ 	{
+ 	  rtx new = operand_subword (op,
+ 				     (byte / UNITS_PER_WORD),
+ 				     0, innermode);
+ 	  if (new)
+ 	    return new;
+ 	}
+ 
+       offset = byte * BITS_PER_UNIT;
+       switch (GET_CODE (op))
+ 	{
+ 	case CONST_DOUBLE:
+ 	  if (GET_MODE (op) != VOIDmode)
+ 	    break;
+ 
+ 	  /* We can't handle this case yet.  */
+ 	  if (GET_MODE_BITSIZE (outermode) >= HOST_BITS_PER_WIDE_INT)
+ 	    return NULL;
+ 
+ 	  part = offset >= HOST_BITS_PER_WIDE_INT;
+ 	  if ((BITS_PER_WORD > HOST_BITS_PER_WIDE_INT
+ 	       && BYTES_BIG_ENDIAN)
+ 	      || (BITS_PER_WORD <= HOST_BITS_PER_WIDE_INT
+ 		  && WORDS_BIG_ENDIAN))
+ 	    part = !part;
+ 	  val = part ? CONST_DOUBLE_HIGH (op) : CONST_DOUBLE_LOW (op);
+ 	  offset %= HOST_BITS_PER_WIDE_INT;
+ 
+ 	  /* We've already picked the word we want from a double, so 
+ 	     pretend this is actually an integer.  */
+ 	  innermode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0);
+ 
+ 	  /* FALLTHROUGH */
+ 	case CONST_INT:
+ 	  if (GET_CODE (op) == CONST_INT)
+ 	    val = INTVAL (op);
+ 
+ 	  /* We don't handle synthetizing of non-integral constants yet.  */
+ 	  if (GET_MODE_CLASS (outermode) != MODE_INT)
+ 	    return NULL;
+ 
+ 	  if (BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
+ 	    {
+ 	      if (WORDS_BIG_ENDIAN)
+ 		offset = (GET_MODE_BITSIZE (innermode)
+ 			  - GET_MODE_BITSIZE (outermode) - offset);
+ 	      if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN
+ 		  && GET_MODE_SIZE (outermode) < UNITS_PER_WORD)
+ 		offset = (offset + BITS_PER_WORD - GET_MODE_BITSIZE (outermode)
+ 			  - 2 * (offset % BITS_PER_WORD));
+ 	    }
+ 
+ 	  if (offset >= HOST_BITS_PER_WIDE_INT)
+ 	    return ((HOST_WIDE_INT) val < 0) ? constm1_rtx : const0_rtx;
+ 	  else
+ 	    {
+ 	      val >>= offset;
+ 	      if (GET_MODE_BITSIZE (outermode) < HOST_BITS_PER_WIDE_INT)
+ 		val = trunc_int_for_mode (val, outermode);
+ 	      return GEN_INT (val);
+ 	    }
+ 	default:
+ 	  break;
+ 	}
+     }
+ 
+   /* Changing mode twice with SUBREG => just change it once,
+      or not at all if changing back op starting mode.  */
+   if (GET_CODE (op) == SUBREG)
+     {
+       enum machine_mode innermostmode = GET_MODE (SUBREG_REG (op));
+       unsigned int final_offset = byte + SUBREG_BYTE (op);
+       rtx new;
+ 
+       if (outermode == innermostmode
+ 	  && byte == 0 && SUBREG_BYTE (op) == 0)
+ 	return SUBREG_REG (op);
+ 
+       if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN)
+ 	  && GET_MODE_SIZE (innermode) > GET_MODE_SIZE (outermode)
+ 	  && GET_MODE_SIZE (innermode) > GET_MODE_SIZE (innermostmode))
+ 	{
+ 	  /* Inner SUBREG is paradoxical, outer is not.  On big endian
+ 	     we have to special case this.  */
+ 	  if (SUBREG_BYTE (op))
+ 	    abort(); /* Can a paradoxical subreg have nonzero offset? */
+ 	  if (WORDS_BIG_ENDIAN && BYTES_BIG_ENDIAN)
+ 	    final_offset = (byte - GET_MODE_SIZE (innermode)
+ 			    + GET_MODE_SIZE (innermostmode));
+ 	  else if (WORDS_BIG_ENDIAN)
+ 	    final_offset = ((final_offset % UNITS_PER_WORD)
+ 			    + ((byte - GET_MODE_SIZE (innermode)
+ 			        + GET_MODE_SIZE (innermostmode))
+ 			       * UNITS_PER_WORD) / UNITS_PER_WORD);
+ 	  else
+ 	    final_offset = (((final_offset * UNITS_PER_WORD)
+ 			     / UNITS_PER_WORD)
+ 			    + ((byte - GET_MODE_SIZE (innermode)
+ 			        + GET_MODE_SIZE (innermostmode))
+ 			       % UNITS_PER_WORD));
+ 	}
+ 
+       /* Recurse for futher possible simplifications.  */
+       new = simplify_subreg (outermode, op, GET_MODE (op),
+ 			     final_offset);
+       if (new)
+ 	return new;
+       return gen_rtx_SUBREG (outermode, op, final_offset);
+     }
+ 
+   /* SUBREG of a hard register => just change the register number
+      and/or mode.  If the hard register is not valid in that mode,
+      suppress this simplification.  If the hard register is the stack,
+      frame, or argument pointer, leave this as a SUBREG.  */
+ 
+   if (REG_P (op) == REG
+       && REGNO (op) < FIRST_PSEUDO_REGISTER
+       && REGNO (op) != FRAME_POINTER_REGNUM
+ #if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+       && REGNO (op) != HARD_FRAME_POINTER_REGNUM
+ #endif
+ #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+       && REGNO (op) != ARG_POINTER_REGNUM
+ #endif
+       && REGNO (op) != STACK_POINTER_REGNUM)
+     {
+       int final_regno = subreg_hard_regno (gen_rtx_SUBREG (outermode, op, byte),
+ 					   0);
+ 
+       if (HARD_REGNO_MODE_OK (final_regno, outermode))
+ 	return gen_rtx_REG (outermode, final_regno);
+     }
+ 
+   /* If we have a SUBREG of a register that we are replacing and we are
+      replacing it with a MEM, make a new MEM and try replacing the
+      SUBREG with it.  Don't do this if the MEM has a mode-dependent address
+      or if we would be widening it.  */
+ 
+   if (GET_CODE (op) == MEM
+       && ! mode_dependent_address_p (XEXP (op, 0))
+       && ! MEM_VOLATILE_P (op)
+       && GET_MODE_SIZE (outermode) <= GET_MODE_SIZE (GET_MODE (op)))
+     {
+       rtx new;
+ 
+       new = gen_rtx_MEM (outermode, plus_constant (XEXP (op, 0), byte));
+       MEM_COPY_ATTRIBUTES (new, op);
+       return new;
+     }
+   return NULL_RTX;
+ }
  /* Simplify X, an rtx expression.
  
     Return the simplified expression or NULL if no simplifications


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