fix rtl-opt/15289, part 4

Richard Henderson rth@redhat.com
Wed Dec 1 18:26:00 GMT 2004


Final part, and the bit that actually fixes the bug.

As promised, gen_realpart and gen_imagpart are gone.  I was pleasently
surprised to find that these routines were only used in three places,
which made replacing them fairly easy.

The hardest part here, actually, was making sense of emit_move_insn_1.
I wound up splitting out seven subroutines in order to help.

At the same time, I added a couple of improvements to movement of complex
numbers.  Namely, under certain situations we can move the complex number
as a unit rather than breaking it apart into components.  For instance,
memory-to-memory moves now get expanded as a block move.  Under certain
conditions we can implement the complex move as an integer move, e.g.

	(set (subreg:DI (reg:SC 1001) 0)
	     (subreg:DI (reg:SC 1002) 0))

Tested on i686, alpha, and ia64 linux.


r~


        PR rtl-opt/15289
        * emit-rtl.c (gen_complex_constant_part): Remove.
        (gen_realpart, gen_imagpart): Remove.
        * rtl.h (gen_realpart, gen_imagpart): Remove.
        * expmed.c (extract_bit_field): Remove CONCAT hack catering to
        gen_realpart/gen_imagpart.
        * expr.c (write_complex_part, read_complex_part): New.
        (emit_move_via_alt_mode, emit_move_via_integer, emit_move_resolve_push,
        emit_move_complex_push, emit_move_complex, emit_move_ccmode,
        emit_move_multi_word): Split out from ...
        (emit_move_insn_1): ... here.
        (expand_expr_real_1) <COMPLEX_EXPR>: Use write_complex_part.
        <REALPART_EXPR, IMAGPART_EXPR>: Use read_complex_part.
        * function.c (assign_parm_setup_reg): Hard-code transformations
        instead of using gen_realpart/gen_imagpart.

Index: emit-rtl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/emit-rtl.c,v
retrieving revision 1.429
diff -c -p -d -r1.429 emit-rtl.c
*** emit-rtl.c	24 Nov 2004 11:32:18 -0000	1.429
--- emit-rtl.c	1 Dec 2004 18:01:23 -0000
*************** static int reg_attrs_htab_eq (const void
*** 184,190 ****
  static reg_attrs *get_reg_attrs (tree, int);
  static tree component_ref_for_mem_expr (tree);
  static rtx gen_const_vector (enum machine_mode, int);
- static rtx gen_complex_constant_part (enum machine_mode, rtx, int);
  static void copy_rtx_if_shared_1 (rtx *orig);
  
  /* Probability of the conditional branch currently proceeded by try_split.
--- 184,189 ----
*************** gen_lowpart_common (enum machine_mode mo
*** 1169,1249 ****
    return 0;
  }
  
- /* Return the constant real or imaginary part (which has mode MODE)
-    of a complex value X.  The IMAGPART_P argument determines whether
-    the real or complex component should be returned.  This function
-    returns NULL_RTX if the component isn't a constant.  */
- 
- static rtx
- gen_complex_constant_part (enum machine_mode mode, rtx x, int imagpart_p)
- {
-   tree decl, part;
- 
-   if (MEM_P (x)
-       && GET_CODE (XEXP (x, 0)) == SYMBOL_REF)
-     {
-       decl = SYMBOL_REF_DECL (XEXP (x, 0));
-       if (decl != NULL_TREE && TREE_CODE (decl) == COMPLEX_CST)
- 	{
- 	  part = imagpart_p ? TREE_IMAGPART (decl) : TREE_REALPART (decl);
- 	  if (TREE_CODE (part) == REAL_CST
- 	      || TREE_CODE (part) == INTEGER_CST)
- 	    return expand_expr (part, NULL_RTX, mode, 0);
- 	}
-     }
-   return NULL_RTX;
- }
- 
- /* Return the real part (which has mode MODE) of a complex value X.
-    This always comes at the low address in memory.  */
- 
- rtx
- gen_realpart (enum machine_mode mode, rtx x)
- {
-   rtx part;
- 
-   /* Handle complex constants.  */
-   part = gen_complex_constant_part (mode, x, 0);
-   if (part != NULL_RTX)
-     return part;
- 
-   if (WORDS_BIG_ENDIAN
-       && GET_MODE_BITSIZE (mode) < BITS_PER_WORD
-       && REG_P (x)
-       && REGNO (x) < FIRST_PSEUDO_REGISTER)
-     internal_error
-       ("can't access real part of complex value in hard register");
-   else if (WORDS_BIG_ENDIAN)
-     return gen_highpart (mode, x);
-   else
-     return gen_lowpart (mode, x);
- }
- 
- /* Return the imaginary part (which has mode MODE) of a complex value X.
-    This always comes at the high address in memory.  */
- 
- rtx
- gen_imagpart (enum machine_mode mode, rtx x)
- {
-   rtx part;
- 
-   /* Handle complex constants.  */
-   part = gen_complex_constant_part (mode, x, 1);
-   if (part != NULL_RTX)
-     return part;
- 
-   if (WORDS_BIG_ENDIAN)
-     return gen_lowpart (mode, x);
-   else if (! WORDS_BIG_ENDIAN
- 	   && GET_MODE_BITSIZE (mode) < BITS_PER_WORD
- 	   && REG_P (x)
- 	   && REGNO (x) < FIRST_PSEUDO_REGISTER)
-     internal_error
-       ("can't access imaginary part of complex value in hard register");
-   else
-     return gen_highpart (mode, x);
- }
- 
  rtx
  gen_highpart (enum machine_mode mode, rtx x)
  {
--- 1168,1173 ----
Index: expmed.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/expmed.c,v
retrieving revision 1.210
diff -c -p -d -r1.210 expmed.c
*** expmed.c	1 Dec 2004 17:49:20 -0000	1.210
--- expmed.c	1 Dec 2004 18:01:24 -0000
*************** extract_bit_field (rtx str_rtx, unsigned
*** 1611,1638 ****
      return spec_target;
    if (GET_MODE (target) != tmode && GET_MODE (target) != mode)
      {
-       /* If the target mode is complex, then extract the two scalar elements
- 	 from the value now.  Creating (subreg:SC (reg:DI) 0), as we would do
- 	 with the clause below, will cause gen_realpart or gen_imagpart to
- 	 fail, since those functions must return lvalues.  */
-       if (COMPLEX_MODE_P (tmode))
- 	{
- 	  rtx realpart, imagpart;
- 	  enum machine_mode itmode = GET_MODE_INNER (tmode);
- 
- 	  target = convert_to_mode (mode_for_size (GET_MODE_BITSIZE (tmode),
- 						   MODE_INT, 0),
- 				    target, unsignedp);
- 
- 	  realpart = extract_bit_field (target, GET_MODE_BITSIZE (itmode), 0,
- 					unsignedp, NULL, itmode, itmode);
- 	  imagpart = extract_bit_field (target, GET_MODE_BITSIZE (itmode),
- 					GET_MODE_BITSIZE (itmode), unsignedp,
- 					NULL, itmode, itmode);
- 
- 	  return gen_rtx_CONCAT (tmode, realpart, imagpart);
- 	}
- 
        /* If the target mode is not a scalar integral, first convert to the
  	 integer mode of that size and then access it as a floating-point
  	 value via a SUBREG.  */
--- 1611,1616 ----
Index: expr.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/expr.c,v
retrieving revision 1.749
diff -c -p -d -r1.749 expr.c
*** expr.c	1 Dec 2004 17:57:29 -0000	1.749
--- expr.c	1 Dec 2004 18:01:25 -0000
*************** clear_storage_libcall_fn (int for_call)
*** 2574,2977 ****
    return block_clear_fn;
  }
  
! /* Generate code to copy Y into X.
!    Both Y and X must have the same mode, except that
!    Y can be a constant with VOIDmode.
!    This mode cannot be BLKmode; use emit_block_move for that.
  
!    Return the last instruction emitted.  */
  
! rtx
! emit_move_insn (rtx x, rtx y)
  {
!   enum machine_mode mode = GET_MODE (x);
!   rtx y_cst = NULL_RTX;
!   rtx last_insn, set;
  
!   gcc_assert (mode != BLKmode
! 	      && (GET_MODE (y) == mode || GET_MODE (y) == VOIDmode));
  
!   if (CONSTANT_P (y))
      {
!       if (optimize
! 	  && SCALAR_FLOAT_MODE_P (GET_MODE (x))
! 	  && (last_insn = compress_float_constant (x, y)))
! 	return last_insn;
  
!       y_cst = y;
  
!       if (!LEGITIMATE_CONSTANT_P (y))
  	{
! 	  y = force_const_mem (mode, y);
  
! 	  /* If the target's cannot_force_const_mem prevented the spill,
! 	     assume that the target's move expanders will also take care
! 	     of the non-legitimate constant.  */
! 	  if (!y)
! 	    y = y_cst;
  	}
      }
  
!   /* If X or Y are memory references, verify that their addresses are valid
!      for the machine.  */
!   if (MEM_P (x)
!       && ((! memory_address_p (GET_MODE (x), XEXP (x, 0))
! 	   && ! push_operand (x, GET_MODE (x)))
! 	  || (flag_force_addr
! 	      && CONSTANT_ADDRESS_P (XEXP (x, 0)))))
!     x = validize_mem (x);
  
!   if (MEM_P (y)
!       && (! memory_address_p (GET_MODE (y), XEXP (y, 0))
! 	  || (flag_force_addr
! 	      && CONSTANT_ADDRESS_P (XEXP (y, 0)))))
!     y = validize_mem (y);
  
!   gcc_assert (mode != BLKmode);
  
!   last_insn = emit_move_insn_1 (x, y);
  
!   if (y_cst && REG_P (x)
!       && (set = single_set (last_insn)) != NULL_RTX
!       && SET_DEST (set) == x
!       && ! rtx_equal_p (y_cst, SET_SRC (set)))
!     set_unique_reg_note (last_insn, REG_EQUAL, y_cst);
  
!   return last_insn;
  }
  
! /* Low level part of emit_move_insn.
!    Called just like emit_move_insn, but assumes X and Y
!    are basically valid.  */
  
! rtx
! emit_move_insn_1 (rtx x, rtx y)
  {
!   enum machine_mode mode = GET_MODE (x);
!   enum machine_mode submode;
  
!   gcc_assert ((unsigned int) mode < (unsigned int) MAX_MACHINE_MODE);
  
!   if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
!     return
!       emit_insn (GEN_FCN (mov_optab->handlers[(int) mode].insn_code) (x, y));
  
!   /* Expand complex moves by moving real part and imag part, if possible.  */
!   else if (COMPLEX_MODE_P (mode)
! 	   && BLKmode != (submode = GET_MODE_INNER (mode))
! 	   && (mov_optab->handlers[(int) submode].insn_code
! 	       != CODE_FOR_nothing))
      {
!       unsigned int modesize = GET_MODE_SIZE (mode);
!       unsigned int submodesize = GET_MODE_SIZE (submode);
  
!       /* Don't split destination if it is a stack push.  */
!       int stack = push_operand (x, mode);
  
  #ifdef PUSH_ROUNDING
!       /* In case we output to the stack, but the size is smaller than the
! 	 machine can push exactly, we need to use move instructions.  */
!       if (stack && PUSH_ROUNDING (submodesize) != submodesize)
! 	{
! 	  rtx temp;
! 	  HOST_WIDE_INT offset1, offset2;
  
! 	  /* Do not use anti_adjust_stack, since we don't want to update
! 	     stack_pointer_delta.  */
! 	  temp = expand_binop (Pmode,
! #ifdef STACK_GROWS_DOWNWARD
! 			       sub_optab,
! #else
! 			       add_optab,
  #endif
- 			       stack_pointer_rtx,
- 			       GEN_INT (PUSH_ROUNDING (modesize)),
- 			       stack_pointer_rtx, 0, OPTAB_LIB_WIDEN);
  
! 	  if (temp != stack_pointer_rtx)
! 	    emit_move_insn (stack_pointer_rtx, temp);
  
! #ifdef STACK_GROWS_DOWNWARD
! 	  offset1 = 0;
! 	  offset2 = submodesize;
! #else
! 	  offset1 = -PUSH_ROUNDING (modesize);
! 	  offset2 = -PUSH_ROUNDING (modesize) + submodesize;
! #endif
  
! 	  emit_move_insn (change_address (x, submode,
! 					  gen_rtx_PLUS (Pmode,
! 						        stack_pointer_rtx,
! 							GEN_INT (offset1))),
! 			  gen_realpart (submode, y));
! 	  emit_move_insn (change_address (x, submode,
! 					  gen_rtx_PLUS (Pmode,
! 						        stack_pointer_rtx,
! 							GEN_INT (offset2))),
! 			  gen_imagpart (submode, y));
! 	}
!       else
! #endif
!       /* If this is a stack, push the highpart first, so it
! 	 will be in the argument order.
  
! 	 In that case, change_address is used only to convert
! 	 the mode, not to change the address.  */
!       if (stack)
! 	{
! 	  /* Note that the real part always precedes the imag part in memory
! 	     regardless of machine's endianness.  */
! #ifdef STACK_GROWS_DOWNWARD
! 	  emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
! 			  gen_imagpart (submode, y));
! 	  emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
! 			  gen_realpart (submode, y));
! #else
! 	  emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
! 			  gen_realpart (submode, y));
! 	  emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
! 			  gen_imagpart (submode, y));
! #endif
! 	}
!       else
! 	{
! 	  rtx realpart_x, realpart_y;
! 	  rtx imagpart_x, imagpart_y;
  
! 	  /* If this is a complex value with each part being smaller than a
! 	     word, the usual calling sequence will likely pack the pieces into
! 	     a single register.  Unfortunately, SUBREG of hard registers only
! 	     deals in terms of words, so we have a problem converting input
! 	     arguments to the CONCAT of two registers that is used elsewhere
! 	     for complex values.  If this is before reload, we can copy it into
! 	     memory and reload.  FIXME, we should see about using extract and
! 	     insert on integer registers, but complex short and complex char
! 	     variables should be rarely used.  */
! 	  if ((reload_in_progress | reload_completed) == 0
! 	      && (!validate_subreg (submode, mode, NULL, submodesize)
! 		  || !validate_subreg (submode, mode, NULL, 0)))
! 	    {
! 	      if (REG_P (x) || REG_P (y))
! 		{
! 		  rtx mem, cmem;
! 		  enum machine_mode reg_mode
! 		    = mode_for_size (GET_MODE_BITSIZE (mode), MODE_INT, 1);
  
! 		  gcc_assert (reg_mode != BLKmode);
  
! 		  mem = assign_stack_temp (reg_mode, modesize, 0);
! 		  cmem = adjust_address (mem, mode, 0);
  
! 		  if (REG_P (x))
! 		    {
! 		      rtx sreg = gen_rtx_SUBREG (reg_mode, x, 0);
! 		      emit_move_insn_1 (cmem, y);
! 		      return emit_move_insn_1 (sreg, mem);
! 		    }
! 		  else
! 		    {
! 		      rtx sreg = gen_rtx_SUBREG (reg_mode, y, 0);
! 		      emit_move_insn_1 (mem, sreg);
! 		      return emit_move_insn_1 (x, cmem);
! 		    }
! 		}
! 	    }
  
! 	  realpart_x = gen_realpart (submode, x);
! 	  realpart_y = gen_realpart (submode, y);
! 	  imagpart_x = gen_imagpart (submode, x);
! 	  imagpart_y = gen_imagpart (submode, y);
  
! 	  /* Show the output dies here.  This is necessary for SUBREGs
! 	     of pseudos since we cannot track their lifetimes correctly;
! 	     hard regs shouldn't appear here except as return values.
! 	     We never want to emit such a clobber after reload.  */
! 	  if (x != y
! 	      && ! (reload_in_progress || reload_completed)
! 	      && (GET_CODE (realpart_x) == SUBREG
! 		  || GET_CODE (imagpart_x) == SUBREG))
! 	    emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
  
! 	  emit_move_insn (realpart_x, realpart_y);
! 	  emit_move_insn (imagpart_x, imagpart_y);
! 	}
  
!       return get_last_insn ();
!     }
  
!   /* Handle MODE_CC modes:  If we don't have a special move insn for this mode,
!      find a mode to do it in.  If we have a movcc, use it.  Otherwise,
!      find the MODE_INT mode of the same width.  */
!   else if (GET_MODE_CLASS (mode) == MODE_CC
! 	   && mov_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
      {
!       enum insn_code insn_code;
!       enum machine_mode tmode = VOIDmode;
!       rtx x1 = x, y1 = y;
  
!       if (mode != CCmode
! 	  && mov_optab->handlers[(int) CCmode].insn_code != CODE_FOR_nothing)
! 	tmode = CCmode;
!       else
! 	for (tmode = QImode; tmode != VOIDmode;
! 	     tmode = GET_MODE_WIDER_MODE (tmode))
! 	  if (GET_MODE_SIZE (tmode) == GET_MODE_SIZE (mode))
! 	    break;
  
!       gcc_assert (tmode != VOIDmode);
  
!       /* Get X and Y in TMODE.  We can't use gen_lowpart here because it
! 	 may call change_address which is not appropriate if we were
! 	 called when a reload was in progress.  We don't have to worry
! 	 about changing the address since the size in bytes is supposed to
! 	 be the same.  Copy the MEM to change the mode and move any
! 	 substitutions from the old MEM to the new one.  */
  
!       if (reload_in_progress)
! 	{
! 	  x = gen_lowpart_common (tmode, x1);
! 	  if (x == 0 && MEM_P (x1))
! 	    {
! 	      x = adjust_address_nv (x1, tmode, 0);
! 	      copy_replacements (x1, x);
! 	    }
  
! 	  y = gen_lowpart_common (tmode, y1);
! 	  if (y == 0 && MEM_P (y1))
! 	    {
! 	      y = adjust_address_nv (y1, tmode, 0);
! 	      copy_replacements (y1, y);
! 	    }
! 	}
!       else
  	{
! 	  x = gen_lowpart (tmode, x);
! 	  y = gen_lowpart (tmode, y);
  	}
  
!       insn_code = mov_optab->handlers[(int) tmode].insn_code;
!       return emit_insn (GEN_FCN (insn_code) (x, y));
      }
  
    /* Try using a move pattern for the corresponding integer mode.  This is
       only safe when simplify_subreg can convert MODE constants into integer
       constants.  At present, it can only do this reliably if the value
       fits within a HOST_WIDE_INT.  */
!   else if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
! 	   && (submode = int_mode_for_mode (mode)) != BLKmode
! 	   && mov_optab->handlers[submode].insn_code != CODE_FOR_nothing)
!     return emit_insn (GEN_FCN (mov_optab->handlers[submode].insn_code)
! 		      (simplify_gen_subreg (submode, x, mode, 0),
! 		       simplify_gen_subreg (submode, y, mode, 0)));
! 
!   /* This will handle any multi-word or full-word mode that lacks a move_insn
!      pattern.  However, you will get better code if you define such patterns,
!      even if they must turn into multiple assembler instructions.  */
!   else
      {
!       rtx last_insn = 0;
!       rtx seq, inner;
!       int need_clobber;
!       int i;
!       
!       gcc_assert (GET_MODE_SIZE (mode) >= UNITS_PER_WORD);
!       
! #ifdef PUSH_ROUNDING
! 
!       /* If X is a push on the stack, do the push now and replace
! 	 X with a reference to the stack pointer.  */
!       if (push_operand (x, GET_MODE (x)))
! 	{
! 	  rtx temp;
! 	  enum rtx_code code;
  
! 	  /* Do not use anti_adjust_stack, since we don't want to update
! 	     stack_pointer_delta.  */
! 	  temp = expand_binop (Pmode,
! #ifdef STACK_GROWS_DOWNWARD
! 			       sub_optab,
! #else
! 			       add_optab,
! #endif
! 			       stack_pointer_rtx,
! 			       GEN_INT
! 				 (PUSH_ROUNDING
! 				  (GET_MODE_SIZE (GET_MODE (x)))),
! 			       stack_pointer_rtx, 0, OPTAB_LIB_WIDEN);
  
! 	  if (temp != stack_pointer_rtx)
! 	    emit_move_insn (stack_pointer_rtx, temp);
  
! 	  code = GET_CODE (XEXP (x, 0));
  
! 	  /* Just hope that small offsets off SP are OK.  */
! 	  if (code == POST_INC)
! 	    temp = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
! 				GEN_INT (-((HOST_WIDE_INT)
! 					   GET_MODE_SIZE (GET_MODE (x)))));
! 	  else if (code == POST_DEC)
! 	    temp = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
! 				GEN_INT (GET_MODE_SIZE (GET_MODE (x))));
! 	  else
! 	    temp = stack_pointer_rtx;
  
! 	  x = change_address (x, VOIDmode, temp);
! 	}
! #endif
  
!       /* If we are in reload, see if either operand is a MEM whose address
! 	 is scheduled for replacement.  */
!       if (reload_in_progress && MEM_P (x)
! 	  && (inner = find_replacement (&XEXP (x, 0))) != XEXP (x, 0))
! 	x = replace_equiv_address_nv (x, inner);
!       if (reload_in_progress && MEM_P (y)
! 	  && (inner = find_replacement (&XEXP (y, 0))) != XEXP (y, 0))
! 	y = replace_equiv_address_nv (y, inner);
  
!       start_sequence ();
  
!       need_clobber = 0;
!       for (i = 0;
! 	   i < (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
! 	   i++)
  	{
! 	  rtx xpart = operand_subword (x, i, 1, mode);
! 	  rtx ypart = operand_subword (y, i, 1, mode);
! 
! 	  /* If we can't get a part of Y, put Y into memory if it is a
! 	     constant.  Otherwise, force it into a register.  If we still
! 	     can't get a part of Y, abort.  */
! 	  if (ypart == 0 && CONSTANT_P (y))
! 	    {
! 	      y = force_const_mem (mode, y);
! 	      ypart = operand_subword (y, i, 1, mode);
! 	    }
! 	  else if (ypart == 0)
! 	    ypart = operand_subword_force (y, i, mode);
  
! 	  gcc_assert (xpart && ypart);
  
! 	  need_clobber |= (GET_CODE (xpart) == SUBREG);
  
! 	  last_insn = emit_move_insn (xpart, ypart);
! 	}
  
!       seq = get_insns ();
!       end_sequence ();
  
!       /* Show the output dies here.  This is necessary for SUBREGs
! 	 of pseudos since we cannot track their lifetimes correctly;
! 	 hard regs shouldn't appear here except as return values.
! 	 We never want to emit such a clobber after reload.  */
!       if (x != y
! 	  && ! (reload_in_progress || reload_completed)
! 	  && need_clobber != 0)
! 	emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
  
!       emit_insn (seq);
  
!       return last_insn;
!     }
  }
  
  /* If Y is representable exactly in a narrower mode, and the target can
--- 2574,3040 ----
    return block_clear_fn;
  }
  
! /* Write to one of the components of the complex value CPLX.  Write VAL to
!    the real part if IMAG_P is false, and the imaginary part if its true.  */
  
! static void
! write_complex_part (rtx cplx, rtx val, bool imag_p)
! {
!   if (GET_CODE (cplx) == CONCAT)
!     emit_move_insn (XEXP (cplx, imag_p), val);
!   else
!     {
!       enum machine_mode cmode = GET_MODE (cplx);
!       enum machine_mode imode = GET_MODE_INNER (cmode);
!       unsigned ibitsize = GET_MODE_BITSIZE (imode);
  
!       store_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0, imode, val);
!     }
! }
! 
! /* Extract one of the components of the complex value CPLX.  Extract the
!    real part if IMAG_P is false, and the imaginary part if it's true.  */
! 
! static rtx
! read_complex_part (rtx cplx, bool imag_p)
  {
!   enum machine_mode cmode, imode;
!   unsigned ibitsize;
  
!   if (GET_CODE (cplx) == CONCAT)
!     return XEXP (cplx, imag_p);
  
!   cmode = GET_MODE (cplx);
!   imode = GET_MODE_INNER (cmode);
!   ibitsize = GET_MODE_BITSIZE (imode);
! 
!   /* Special case reads from complex constants that got spilled to memory.  */
!   if (MEM_P (cplx) && GET_CODE (XEXP (cplx, 0)) == SYMBOL_REF)
      {
!       tree decl = SYMBOL_REF_DECL (XEXP (cplx, 0));
!       if (decl && TREE_CODE (decl) == COMPLEX_CST)
! 	{
! 	  tree part = imag_p ? TREE_IMAGPART (decl) : TREE_REALPART (decl);
! 	  if (CONSTANT_CLASS_P (part))
! 	    return expand_expr (part, NULL_RTX, imode, EXPAND_NORMAL);
! 	}
!     }
  
!   return extract_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0,
! 			    true, NULL_RTX, imode, imode);
! }
! 
! /* A subroutine of emit_move_insn_1.  Generate a move from Y into X using
!    ALT_MODE instead of the operand's natural mode, MODE.  CODE is the insn
!    code for the move in ALT_MODE, and is known to be valid.  Returns the
!    instruction emitted.  */
  
! static rtx
! emit_move_via_alt_mode (enum machine_mode alt_mode, enum machine_mode mode,
! 			enum insn_code code, rtx x, rtx y)
! {
!   /* Get X and Y in ALT_MODE.  We can't use gen_lowpart here because it
!      may call change_address which is not appropriate if we were
!      called when a reload was in progress.  We don't have to worry
!      about changing the address since the size in bytes is supposed to
!      be the same.  Copy the MEM to change the mode and move any
!      substitutions from the old MEM to the new one.  */
! 
!   if (reload_in_progress)
!     {
!       rtx x1 = x, y1 = y;
! 
!       x = gen_lowpart_common (alt_mode, x1);
!       if (x == 0 && MEM_P (x1))
  	{
! 	  x = adjust_address_nv (x1, alt_mode, 0);
! 	  copy_replacements (x1, x);
! 	}
  
!       y = gen_lowpart_common (alt_mode, y1);
!       if (y == 0 && MEM_P (y1))
! 	{
! 	  y = adjust_address_nv (y1, alt_mode, 0);
! 	  copy_replacements (y1, y);
  	}
      }
+   else
+     {
+       x = simplify_gen_subreg (alt_mode, x, mode, 0);
+       y = simplify_gen_subreg (alt_mode, y, mode, 0);
+     }
  
!   return emit_insn (GEN_FCN (code) (x, y));
! }
  
! /* A subroutine of emit_move_insn_1.  Generate a move from Y into X using
!    an integer mode of the same size as MODE.  Returns the instruction
!    emitted, or NULL if such a move could not be generated.  */
  
! static rtx
! emit_move_via_integer (enum machine_mode mode, rtx x, rtx y)
! {
!   enum machine_mode imode;
!   enum insn_code code;
  
!   /* There must exist a mode of the exact size we require.  */
!   imode = int_mode_for_mode (mode);
!   if (imode == BLKmode)
!     return NULL_RTX;
  
!   /* The target must support moves in this mode.  */
!   code = mov_optab->handlers[imode].insn_code;
!   if (code == CODE_FOR_nothing)
!     return NULL_RTX;
  
!   return emit_move_via_alt_mode (imode, mode, code, x, y);
  }
  
! /* A subroutine of emit_move_insn_1.  X is a push_operand in MODE.
!    Return an equivalent MEM that does not use an auto-increment.  */
  
! static rtx
! emit_move_resolve_push (enum machine_mode mode, rtx x)
  {
!   enum rtx_code code = GET_CODE (XEXP (x, 0));
!   HOST_WIDE_INT adjust;
!   rtx temp;
  
!   adjust = GET_MODE_SIZE (mode);
! #ifdef PUSH_ROUNDING
!   adjust = PUSH_ROUNDING (adjust);
! #endif
!   if (code == PRE_DEC || code == POST_DEC)
!     adjust = -adjust;
  
!   /* Do not use anti_adjust_stack, since we don't want to update
!      stack_pointer_delta.  */
!   temp = expand_simple_binop (Pmode, PLUS, stack_pointer_rtx,
! 			      GEN_INT (adjust), stack_pointer_rtx,
! 			      0, OPTAB_LIB_WIDEN);
!   if (temp != stack_pointer_rtx)
!     emit_move_insn (stack_pointer_rtx, temp);
  
!   switch (code)
      {
!     case PRE_INC:
!     case PRE_DEC:
!       temp = stack_pointer_rtx;
!       break;
!     case POST_INC:
!       temp = plus_constant (stack_pointer_rtx, -GET_MODE_SIZE (mode));
!       break;
!     case POST_DEC:
!       temp = plus_constant (stack_pointer_rtx, GET_MODE_SIZE (mode));
!       break;
!     default:
!       gcc_unreachable ();
!     }
  
!   return replace_equiv_address (x, temp);
! }
! 
! /* A subroutine of emit_move_complex.  Generate a move from Y into X.
!    X is known to satisfy push_operand, and MODE is known to be complex.
!    Returns the last instruction emitted.  */
! 
! static rtx
! emit_move_complex_push (enum machine_mode mode, rtx x, rtx y)
! {
!   enum machine_mode submode = GET_MODE_INNER (mode);
!   bool imag_first;
  
  #ifdef PUSH_ROUNDING
!   unsigned int submodesize = GET_MODE_SIZE (submode);
  
!   /* In case we output to the stack, but the size is smaller than the
!      machine can push exactly, we need to use move instructions.  */
!   if (PUSH_ROUNDING (submodesize) != submodesize)
!     {
!       x = emit_move_resolve_push (mode, x);
!       return emit_move_insn (x, y);
!     }
  #endif
  
!   /* Note that the real part always precedes the imag part in memory
!      regardless of machine's endianness.  */
!   switch (GET_CODE (XEXP (x, 0)))
!     {
!     case PRE_DEC:
!     case POST_DEC:
!       imag_first = true;
!       break;
!     case PRE_INC:
!     case POST_INC:
!       imag_first = false;
!       break;
!     default:
!       gcc_unreachable ();
!     }
  
!   emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
! 		  read_complex_part (y, imag_first));
!   return emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
! 			 read_complex_part (y, !imag_first));
! }
  
! /* A subroutine of emit_move_insn_1.  Generate a move from Y into X.
!    MODE is known to be complex.  Returns the last instruction emitted.  */
  
! static rtx
! emit_move_complex (enum machine_mode mode, rtx x, rtx y)
! {
!   bool try_int;
  
!   /* Need to take special care for pushes, to maintain proper ordering
!      of the data, and possibly extra padding.  */
!   if (push_operand (x, mode))
!     return emit_move_complex_push (mode, x, y);
  
!   /* For memory to memory moves, optimial behaviour can be had with the
!      existing block move logic.  */
!   if (MEM_P (x) && MEM_P (y))
!     {
!       emit_block_move (x, y, GEN_INT (GET_MODE_SIZE (mode)),
! 		       BLOCK_OP_NO_LIBCALL);
!       return get_last_insn ();
!     }
  
!   /* See if we can coerce the target into moving both values at once.  */
  
!   /* Not possible if the values are inherently not adjacent.  */
!   if (GET_CODE (x) == CONCAT || GET_CODE (y) == CONCAT)
!     try_int = false;
!   /* Is possible if both are registers (or subregs of registers).  */
!   else if (register_operand (x, mode) && register_operand (y, mode))
!     try_int = true;
!   /* If one of the operands is a memory, and alignment constraints
!      are friendly enough, we may be able to do combined memory operations.
!      We do not attempt this if Y is a constant because that combination is
!      usually better with the by-parts thing below.  */
!   else if ((MEM_P (x) ? !CONSTANT_P (y) : MEM_P (y))
! 	   && (!STRICT_ALIGNMENT
! 	       || get_mode_alignment (mode) == BIGGEST_ALIGNMENT))
!     try_int = true;
!   else
!     try_int = false;
  
!   if (try_int)
!     {
!       rtx ret = emit_move_via_integer (mode, x, y);
!       if (ret)
! 	return ret;
!     }
  
!   /* Show the output dies here.  This is necessary for SUBREGs
!      of pseudos since we cannot track their lifetimes correctly;
!      hard regs shouldn't appear here except as return values.  */
!   if (!reload_completed && !reload_in_progress
!       && REG_P (x) && !reg_overlap_mentioned_p (x, y))
!     emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
  
!   write_complex_part (x, read_complex_part (y, false), false);
!   write_complex_part (x, read_complex_part (y, true), true);
!   return get_last_insn ();
! }
  
! /* A subroutine of emit_move_insn_1.  Generate a move from Y into X.
!    MODE is known to be MODE_CC.  Returns the last instruction emitted.  */
  
! static rtx
! emit_move_ccmode (enum machine_mode mode, rtx x, rtx y)
! {
!   rtx ret;
! 
!   /* Assume all MODE_CC modes are equivalent; if we have movcc, use it.  */
!   if (mode != CCmode)
      {
!       enum insn_code code = mov_optab->handlers[CCmode].insn_code;
!       if (code != CODE_FOR_nothing)
! 	return emit_move_via_alt_mode (CCmode, mode, code, x, y);
!     }
  
!   /* Otherwise, find the MODE_INT mode of the same width.  */
!   ret = emit_move_via_integer (mode, x, y);
!   gcc_assert (ret != NULL);
!   return ret;
! }
  
! /* A subroutine of emit_move_insn_1.  Generate a move from Y into X.
!    MODE is any multi-word or full-word mode that lacks a move_insn
!    pattern.  Note that you will get better code if you define such
!    patterns, even if they must turn into multiple assembler instructions.  */
  
! static rtx
! emit_move_multi_word (enum machine_mode mode, rtx x, rtx y)
! {
!   rtx last_insn = 0;
!   rtx seq, inner;
!   bool need_clobber;
!   int i;
!       
!   gcc_assert (GET_MODE_SIZE (mode) >= UNITS_PER_WORD);
!       
!   /* If X is a push on the stack, do the push now and replace
!      X with a reference to the stack pointer.  */
!   if (push_operand (x, mode))
!     x = emit_move_resolve_push (mode, x);
  
!   /* If we are in reload, see if either operand is a MEM whose address
!      is scheduled for replacement.  */
!   if (reload_in_progress && MEM_P (x)
!       && (inner = find_replacement (&XEXP (x, 0))) != XEXP (x, 0))
!     x = replace_equiv_address_nv (x, inner);
!   if (reload_in_progress && MEM_P (y)
!       && (inner = find_replacement (&XEXP (y, 0))) != XEXP (y, 0))
!     y = replace_equiv_address_nv (y, inner);
  
!   start_sequence ();
! 
!   need_clobber = false;
!   for (i = 0;
!        i < (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
!        i++)
!     {
!       rtx xpart = operand_subword (x, i, 1, mode);
!       rtx ypart = operand_subword (y, i, 1, mode);
! 
!       /* If we can't get a part of Y, put Y into memory if it is a
! 	 constant.  Otherwise, force it into a register.  If we still
! 	 can't get a part of Y, abort.  */
!       if (ypart == 0 && CONSTANT_P (y))
  	{
! 	  y = force_const_mem (mode, y);
! 	  ypart = operand_subword (y, i, 1, mode);
  	}
+       else if (ypart == 0)
+ 	ypart = operand_subword_force (y, i, mode);
  
!       gcc_assert (xpart && ypart);
! 
!       need_clobber |= (GET_CODE (xpart) == SUBREG);
! 
!       last_insn = emit_move_insn (xpart, ypart);
      }
  
+   seq = get_insns ();
+   end_sequence ();
+ 
+   /* Show the output dies here.  This is necessary for SUBREGs
+      of pseudos since we cannot track their lifetimes correctly;
+      hard regs shouldn't appear here except as return values.
+      We never want to emit such a clobber after reload.  */
+   if (x != y
+       && ! (reload_in_progress || reload_completed)
+       && need_clobber != 0)
+     emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
+ 
+   emit_insn (seq);
+ 
+   return last_insn;
+ }
+ 
+ /* Low level part of emit_move_insn.
+    Called just like emit_move_insn, but assumes X and Y
+    are basically valid.  */
+ 
+ rtx
+ emit_move_insn_1 (rtx x, rtx y)
+ {
+   enum machine_mode mode = GET_MODE (x);
+   enum insn_code code;
+ 
+   gcc_assert ((unsigned int) mode < (unsigned int) MAX_MACHINE_MODE);
+ 
+   code = mov_optab->handlers[mode].insn_code;
+   if (code != CODE_FOR_nothing)
+     return emit_insn (GEN_FCN (code) (x, y));
+ 
+   /* Expand complex moves by moving real part and imag part.  */
+   if (COMPLEX_MODE_P (mode))
+     return emit_move_complex (mode, x, y);
+ 
+   if (GET_MODE_CLASS (mode) == MODE_CC)
+     return emit_move_ccmode (mode, x, y);
+ 
    /* Try using a move pattern for the corresponding integer mode.  This is
       only safe when simplify_subreg can convert MODE constants into integer
       constants.  At present, it can only do this reliably if the value
       fits within a HOST_WIDE_INT.  */
!   if (!CONSTANT_P (y) || GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
      {
!       rtx ret = emit_move_via_integer (mode, x, y);
!       if (ret)
! 	return ret;
!     }
  
!   return emit_move_multi_word (mode, x, y);
! }
  
! /* Generate code to copy Y into X.
!    Both Y and X must have the same mode, except that
!    Y can be a constant with VOIDmode.
!    This mode cannot be BLKmode; use emit_block_move for that.
  
!    Return the last instruction emitted.  */
  
! rtx
! emit_move_insn (rtx x, rtx y)
! {
!   enum machine_mode mode = GET_MODE (x);
!   rtx y_cst = NULL_RTX;
!   rtx last_insn, set;
  
!   gcc_assert (mode != BLKmode
! 	      && (GET_MODE (y) == mode || GET_MODE (y) == VOIDmode));
  
!   if (CONSTANT_P (y))
!     {
!       if (optimize
! 	  && SCALAR_FLOAT_MODE_P (GET_MODE (x))
! 	  && (last_insn = compress_float_constant (x, y)))
! 	return last_insn;
  
!       y_cst = y;
  
!       if (!LEGITIMATE_CONSTANT_P (y))
  	{
! 	  y = force_const_mem (mode, y);
  
! 	  /* If the target's cannot_force_const_mem prevented the spill,
! 	     assume that the target's move expanders will also take care
! 	     of the non-legitimate constant.  */
! 	  if (!y)
! 	    y = y_cst;
! 	}
!     }
  
!   /* If X or Y are memory references, verify that their addresses are valid
!      for the machine.  */
!   if (MEM_P (x)
!       && ((! memory_address_p (GET_MODE (x), XEXP (x, 0))
! 	   && ! push_operand (x, GET_MODE (x)))
! 	  || (flag_force_addr
! 	      && CONSTANT_ADDRESS_P (XEXP (x, 0)))))
!     x = validize_mem (x);
  
!   if (MEM_P (y)
!       && (! memory_address_p (GET_MODE (y), XEXP (y, 0))
! 	  || (flag_force_addr
! 	      && CONSTANT_ADDRESS_P (XEXP (y, 0)))))
!     y = validize_mem (y);
  
!   gcc_assert (mode != BLKmode);
  
!   last_insn = emit_move_insn_1 (x, y);
  
!   if (y_cst && REG_P (x)
!       && (set = single_set (last_insn)) != NULL_RTX
!       && SET_DEST (set) == x
!       && ! rtx_equal_p (y_cst, SET_SRC (set)))
!     set_unique_reg_note (last_insn, REG_EQUAL, y_cst);
  
!   return last_insn;
  }
  
  /* If Y is representable exactly in a narrower mode, and the target can
*************** expand_expr_real_1 (tree exp, rtx target
*** 8083,8129 ****
      case ADDR_EXPR:
        return expand_expr_addr_expr (exp, target, tmode, modifier);
  
-     /* COMPLEX type for Extended Pascal & Fortran  */
      case COMPLEX_EXPR:
!       {
! 	enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp)));
! 	rtx insns;
! 
! 	/* Get the rtx code of the operands.  */
! 	op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
! 	op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0);
! 
! 	if (! target)
! 	  target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
! 
! 	start_sequence ();
! 
! 	/* Move the real (op0) and imaginary (op1) parts to their location.  */
! 	emit_move_insn (gen_realpart (mode, target), op0);
! 	emit_move_insn (gen_imagpart (mode, target), op1);
  
! 	insns = get_insns ();
! 	end_sequence ();
  
! 	/* Complex construction should appear as a single unit.  */
! 	/* If TARGET is a CONCAT, we got insns like RD = RS, ID = IS,
! 	   each with a separate pseudo as destination.
! 	   It's not correct for flow to treat them as a unit.  */
! 	if (GET_CODE (target) != CONCAT)
! 	  emit_no_conflict_block (insns, target, op0, op1, NULL_RTX);
! 	else
! 	  emit_insn (insns);
  
! 	return target;
!       }
  
      case REALPART_EXPR:
        op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
!       return gen_realpart (mode, op0);
  
      case IMAGPART_EXPR:
        op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
!       return gen_imagpart (mode, op0);
  
      case RESX_EXPR:
        expand_resx_expr (exp);
--- 8146,8172 ----
      case ADDR_EXPR:
        return expand_expr_addr_expr (exp, target, tmode, modifier);
  
      case COMPLEX_EXPR:
!       /* Get the rtx code of the operands.  */
!       op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
!       op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0);
  
!       if (!target)
! 	target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
  
!       /* Move the real (op0) and imaginary (op1) parts to their location.  */
!       write_complex_part (target, op0, false);
!       write_complex_part (target, op1, true);
  
!       return target;
  
      case REALPART_EXPR:
        op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
!       return read_complex_part (op0, false);
  
      case IMAGPART_EXPR:
        op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
!       return read_complex_part (op0, true);
  
      case RESX_EXPR:
        expand_resx_expr (exp);
Index: function.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/function.c,v
retrieving revision 1.591
diff -c -p -d -r1.591 function.c
*** function.c	24 Nov 2004 18:50:21 -0000	1.591
--- function.c	1 Dec 2004 18:01:26 -0000
*************** assign_parm_setup_reg (struct assign_par
*** 2869,2878 ****
  	{
  	  enum machine_mode submode
  	    = GET_MODE_INNER (GET_MODE (parmreg));
! 	  int regnor = REGNO (gen_realpart (submode, parmreg));
! 	  int regnoi = REGNO (gen_imagpart (submode, parmreg));
! 	  rtx stackr = gen_realpart (submode, data->stack_parm);
! 	  rtx stacki = gen_imagpart (submode, data->stack_parm);
  
  	  /* Scan backwards for the set of the real and
  	     imaginary parts.  */
--- 2869,2879 ----
  	{
  	  enum machine_mode submode
  	    = GET_MODE_INNER (GET_MODE (parmreg));
! 	  int regnor = REGNO (XEXP (parmreg, 0));
! 	  int regnoi = REGNO (XEXP (parmreg, 1));
! 	  rtx stackr = adjust_address_nv (data->stack_parm, submode, 0);
! 	  rtx stacki = adjust_address_nv (data->stack_parm, submode,
! 					  GET_MODE_SIZE (submode));
  
  	  /* Scan backwards for the set of the real and
  	     imaginary parts.  */
Index: rtl.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/rtl.h,v
retrieving revision 1.528
diff -c -p -d -r1.528 rtl.h
*** rtl.h	24 Nov 2004 00:09:03 -0000	1.528
--- rtl.h	1 Dec 2004 18:01:27 -0000
*************** extern rtx gen_lowpart_if_possible (enum
*** 1383,1390 ****
  /* In emit-rtl.c */
  extern rtx gen_highpart (enum machine_mode, rtx);
  extern rtx gen_highpart_mode (enum machine_mode, enum machine_mode, rtx);
- extern rtx gen_realpart (enum machine_mode, rtx);
- extern rtx gen_imagpart (enum machine_mode, rtx);
  extern rtx operand_subword (rtx, unsigned int, int, enum machine_mode);
  
  /* In emit-rtl.c */
--- 1383,1388 ----



More information about the Gcc-patches mailing list