public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
From: Weiwen Liu <weiwen.liu@yale.edu>
To: egcs@cygnus.com
Subject: Patch for COMPLEX support (part 2)
Date: Tue, 10 Feb 1998 03:34:00 -0000	[thread overview]
Message-ID: <Pine.OSF.3.96.980107191012.10607C-100000@ylws39> (raw)

Wed Jan  7 17:30:00 1998  Weiwen Liu <liu@hepunix.physics.yale.edu>

	 * machmode.h: define COMPLEX_MODE_P, COMPLEX_SUBMODE.

	 * regs.h (REG_SIZE): Consider complex modes.

	 * calls.c (arg_data): Add partial_real and partial_imag to
	 record the number of registers used for real/imaginary part.

	 * combine.c (expand_field_assignment): Exclude SUBREG of a
         COMPLEX.

	 * explow.c (hard_function_value): Promote COMPLEX return
	 value if required.

	 * calls.c (expand_call): Deal with arguments of complex modes.
	   (store_one_arg): Likewise.
	 * emit-rtl.c (gen_lowpart_common): Likewise.
	 (gen_highpart): Likewise.
	 * expr.c (store_expr): Likewise.
	 (expand_expr): Likewise.
	 * function.c (aggregate_value_p): Likewise.
	 (assign_parms): Likewise.
	 (locate_and_pad_parm): Likewise.
	 (expand_function_start): Likewise.
	 * integrate.c (expand_inline_function): Likewise.
	 * reload.c (find_reloads): Likewise.
	 * stmt.c (expand_value_return): Likewise.

--- gcc/calls.c.orig	Wed Dec 24 16:39:40 1997
+++ gcc/calls.c	Wed Jan  7 10:57:01 1998
@@ -71,6 +71,10 @@ struct arg_data
   /* Number of registers to use.  0 means put the whole arg in registers.
      Also 0 if not passed in registers.  */
   int partial;
+  /* Number of registers to use for real part. */
+  int partial_real;
+  /* Number of registers to use for imaginary part. */
+  int partial_imag;
   /* Non-zero if argument must be passed on stack.
      Note that some arguments may be passed on the stack
      even though pass_on_stack is zero, just because FUNCTION_ARG says so.
@@ -1012,7 +1016,9 @@ expand_call (exp, target, ignore)
       tree type = TREE_TYPE (TREE_VALUE (p));
       int unsignedp;
       enum machine_mode mode;
-
+      enum machine_mode submode;
+      tree subtype;
+      
       args[i].tree_value = TREE_VALUE (p);
 
       /* Replace erroneous argument with constant zero.  */
@@ -1044,8 +1050,13 @@ expand_call (exp, target, ignore)
 	   && contains_placeholder_p (TYPE_SIZE (type)))
 	  || TREE_ADDRESSABLE (type)
 #ifdef FUNCTION_ARG_PASS_BY_REFERENCE
-	  || FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, TYPE_MODE (type),
-					     type, argpos < n_named_args)
+	  || FUNCTION_ARG_PASS_BY_REFERENCE
+	     (args_so_far,
+	      (! COMPLEX_MODE_P (TYPE_MODE (type)))?
+	      TYPE_MODE (type) : COMPLEX_SUBMODE (TYPE_MODE (type)),
+	      (! COMPLEX_MODE_P (TYPE_MODE (type)))?
+	      type : type_for_mode (COMPLEX_SUBMODE (TYPE_MODE (type)), 0),
+	      argpos < n_named_args)
 #endif
 	  )
 	{
@@ -1053,8 +1064,14 @@ expand_call (exp, target, ignore)
              references instead of making a copy.  */
 	  if (current_function_is_thunk
 #ifdef FUNCTION_ARG_CALLEE_COPIES
-	      || (FUNCTION_ARG_CALLEE_COPIES (args_so_far, TYPE_MODE (type),
-					     type, argpos < n_named_args)
+	      || (FUNCTION_ARG_CALLEE_COPIES
+		  (args_so_far,
+		   COMPLEX_MODE_P (TYPE_MODE (type))?
+		   COMPLEX_SUBMODE (TYPE_MODE (type)) : TYPE_MODE (type),
+		   COMPLEX_MODE_P (TYPE_MODE (type))?
+		   type_for_mode (COMPLEX_SUBMODE (TYPE_MODE (type)), 0)
+		   : type,
+		   argpos < n_named_args)
 		  /* If it's in a register, we must make a copy of it too.  */
 		  /* ??? Is this a sufficient test?  Is there a better one? */
 		  && !(TREE_CODE (args[i].tree_value) == VAR_DECL
@@ -1117,21 +1134,81 @@ expand_call (exp, target, ignore)
 
       mode = TYPE_MODE (type);
       unsignedp = TREE_UNSIGNED (type);
-
+      if (COMPLEX_MODE_P (mode))
+	{
+	  submode = COMPLEX_SUBMODE (mode);
+	  subtype = type_for_mode (submode, unsignedp);
+	}
+      
 #ifdef PROMOTE_FUNCTION_ARGS
-      mode = promote_mode (type, mode, &unsignedp, 1);
+      if (! COMPLEX_MODE_P (mode))
+	{
+	  mode = promote_mode (type, mode, &unsignedp, 1);
+	}
+      else
+	{
+	  submode = promote_mode (subtype, submode, &unsignedp, 1);
+	  subtype = type_for_mode (submode, unsignedp);
+	  mode = mode_for_size (2 * GET_MODE_SIZE (submode) * BITS_PER_UNIT,
+				GET_MODE_CLASS (mode), 0);
+	}
 #endif
 
       args[i].unsignedp = unsignedp;
       args[i].mode = mode;
-      args[i].reg = FUNCTION_ARG (args_so_far, mode, type,
-				  argpos < n_named_args);
+      if (! COMPLEX_MODE_P (mode))
+	{
+	  args[i].reg = FUNCTION_ARG (args_so_far, mode, type,
+				      argpos < n_named_args);
 #ifdef FUNCTION_ARG_PARTIAL_NREGS
-      if (args[i].reg)
-	args[i].partial
-	  = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, type,
-					argpos < n_named_args);
+	  if (args[i].reg)
+	    args[i].partial
+	      = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, type,
+					    argpos < n_named_args);
 #endif
+      /* Increment ARGS_SO_FAR, which has info about which arg-registers
+	 have been used, etc.  */
+	  FUNCTION_ARG_ADVANCE (args_so_far, TYPE_MODE (type), type,
+				argpos < n_named_args);
+	}
+      else
+	{
+	  rtx realpart;
+	  rtx imagpart;
+	  
+	  realpart = FUNCTION_ARG (args_so_far, submode, subtype,
+				   argpos < n_named_args);
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+	  args[i].partial_real = 0;
+	  if (realpart)
+	    {
+	      args[i].partial_real
+		= FUNCTION_ARG_PARTIAL_NREGS (args_so_far, submode, subtype,
+					      argpos < n_named_args);
+	      args[i].partial = args[i].partial_real;
+	    }
+#endif
+	  FUNCTION_ARG_ADVANCE (args_so_far, submode,
+				type, argpos < n_named_args);
+	  imagpart = FUNCTION_ARG (args_so_far, submode, subtype,
+				   argpos < n_named_args);
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+	  args[i].partial_imag = 0;
+	  if (imagpart)
+	    {
+	      args[i].partial_imag
+		= FUNCTION_ARG_PARTIAL_NREGS (args_so_far, submode, subtype,
+					      argpos < n_named_args);
+	      args[i].partial = args[i].partial_imag;
+	    }
+#endif
+	  FUNCTION_ARG_ADVANCE (args_so_far, submode,
+				type, argpos < n_named_args);
+	  if (realpart != 0)
+	    args[i].reg = gen_rtx (CONCAT, mode, realpart, imagpart);
+	  else
+	    args[i].reg = 0;
+	}
 
       args[i].pass_on_stack = MUST_PASS_IN_STACK (mode, type);
 
@@ -1158,19 +1235,45 @@ expand_call (exp, target, ignore)
 
       /* Compute the stack-size of this argument.  */
       if (args[i].reg == 0 || args[i].partial != 0
+	  /* Allocate stack for a complex argument if its real part is
+	     passed in register and its imaginary part is not. */
+	  || (COMPLEX_MODE_P (mode) && XEXP (args[i].reg, 1) == 0)
 #ifdef REG_PARM_STACK_SPACE
 	  || reg_parm_stack_space > 0
 #endif
 	  || args[i].pass_on_stack)
-	locate_and_pad_parm (mode, type,
+	{
+	  enum machine_mode passed_mode = mode;
+	  tree passed_type = type;
+	  rtx reg = args[i].reg;
+	  
+	  if (COMPLEX_MODE_P (mode) && args[i].reg != 0
+#if defined (REG_PARM_STACK_SPACE) && ! defined (MAYBE_REG_PARM_STACK_SPACE)
+	    && REG_PARM_STACK_SPACE (fndecl) == 0
+#endif
+	    )
+	    {
+	      /* If we do not need to allocate stack space for
+		 arguments passing in register, and the real part
+		 of a complex argument is passing in register,
+		 we only allocate stack space for the imaginary part. */
+	      if (XEXP (args[i].reg, 0) != 0 && args[i].partial_real == 0)
+		{
+		  passed_mode = submode;
+		  passed_type = subtype;
+		  reg = XEXP (args[i].reg, 1);
+		}
+	    }
+	  locate_and_pad_parm (passed_mode, passed_type,
 #ifdef STACK_PARMS_IN_REG_PARM_AREA
-			     1,
+			       1,
 #else
-			     args[i].reg != 0,
+			       reg != 0,
 #endif
-			     fndecl, &args_size, &args[i].offset,
-			     &args[i].size);
-
+			       fndecl, &args_size, &args[i].offset,
+			       &args[i].size);
+	}
+      
 #ifndef ARGS_GROW_DOWNWARD
       args[i].slot_offset = args_size;
 #endif
@@ -1203,12 +1306,6 @@ expand_call (exp, target, ignore)
 	  SUB_PARM_SIZE (args[i].slot_offset, args_size.var);
 	}
 #endif
-
-      /* Increment ARGS_SO_FAR, which has info about which arg-registers
-	 have been used, etc.  */
-
-      FUNCTION_ARG_ADVANCE (args_so_far, TYPE_MODE (type), type,
-			    argpos < n_named_args);
     }
 
 #ifdef FINAL_REG_PARM_STACK_SPACE
@@ -1375,10 +1472,33 @@ expand_call (exp, target, ignore)
 	  = protect_from_queue (args[i].initial_value, 0);
 
 	if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) != args[i].mode)
-	  args[i].value
-	    = convert_modes (args[i].mode, 
-			     TYPE_MODE (TREE_TYPE (args[i].tree_value)),
-			     args[i].value, args[i].unsignedp);
+	  {
+	    if (! COMPLEX_MODE_P (args[i].mode))
+	      args[i].value
+		= convert_modes (args[i].mode, 
+				 TYPE_MODE (TREE_TYPE (args[i].tree_value)),
+				 args[i].value, args[i].unsignedp);
+	    else
+	      {
+		enum machine_mode submode_orig
+		  = COMPLEX_SUBMODE (TYPE_MODE
+				       (TREE_TYPE (args[i].tree_value)));
+		enum machine_mode submode_final
+		  = COMPLEX_SUBMODE (args[i].mode);
+		rtx realpart;
+		rtx imagpart;
+		realpart = convert_modes (submode_final, submode_orig,
+					  gen_realpart (submode_orig,
+							args[i].value),
+					  args[i].unsignedp);
+		imagpart = convert_modes (submode_final, submode_orig,
+					  gen_imagpart (submode_orig,
+							args[i].value),
+					  args[i].unsignedp);
+		args[i].value = gen_rtx (CONCAT, args[i].mode,
+					 realpart, imagpart);
+	      }
+	  }
       }
 
   /* Now we are about to start emitting insns that can be deleted
@@ -1665,10 +1785,33 @@ expand_call (exp, target, ignore)
 	   do it now.  */
 
 	if (args[i].mode != TYPE_MODE (TREE_TYPE (args[i].tree_value)))
-	  args[i].value
-	    = convert_modes (args[i].mode,
-			     TYPE_MODE (TREE_TYPE (args[i].tree_value)),
-			     args[i].value, args[i].unsignedp);
+	  {
+	    if (! COMPLEX_MODE_P (args[i].mode))
+	      args[i].value
+		= convert_modes (args[i].mode, 
+				 TYPE_MODE (TREE_TYPE (args[i].tree_value)),
+				 args[i].value, args[i].unsignedp);
+	    else
+	      {
+		enum machine_mode submode_orig
+		  = COMPLEX_SUBMODE (TYPE_MODE
+				       (TREE_TYPE (args[i].tree_value)));
+		enum machine_mode submode_final
+		  = COMPLEX_SUBMODE (args[i].mode);
+		rtx realpart;
+		rtx imagpart;
+		realpart = convert_modes (submode_final, submode_orig,
+					  gen_realpart (submode_orig,
+							args[i].value),
+					  args[i].unsignedp);
+		imagpart = convert_modes (submode_final, submode_orig,
+					  gen_imagpart (submode_orig,
+							args[i].value),
+					  args[i].unsignedp);
+		args[i].value = gen_rtx (CONCAT, args[i].mode,
+					 realpart, imagpart);
+	      }
+	  }
 
 	/* If the value is expensive, and we are inside an appropriately 
 	   short loop, put the value into a pseudo and then put the pseudo
@@ -1758,7 +1901,12 @@ expand_call (exp, target, ignore)
      but we do preallocate space here if they want that.  */
 
   for (i = 0; i < num_actuals; i++)
-    if (args[i].reg == 0 || args[i].pass_on_stack)
+    if (args[i].reg == 0 || args[i].pass_on_stack
+	/* Deal with the imaginary part of a complex argument
+	   that is passed in stack. */
+	|| (COMPLEX_MODE_P (args[i].mode)
+	    && args[i].reg != 0 && args[i].partial == 0
+	    && XEXP (args[i].reg, 1) == 0))
       store_one_arg (&args[i], argblock, may_be_alloca,
 		     args_size.var != 0, fndecl, reg_parm_stack_space);
 
@@ -1919,8 +2067,22 @@ expand_call (exp, target, ignore)
 	     load the register(s) from memory.  */
 
 	  else if (nregs == -1)
-	    emit_move_insn (reg, args[i].value);
-
+	    {
+	      if (GET_CODE (reg) != CONCAT)
+		emit_move_insn (reg, args[i].value);
+	      else
+		{
+		  enum machine_mode submode
+		    = COMPLEX_SUBMODE (args[i].value->mode);
+		  if (XEXP (reg, 0))
+		    emit_move_insn (XEXP (reg, 0),
+				    gen_realpart (submode, args[i].value));
+		  if (XEXP (reg, 1))
+		    emit_move_insn (XEXP (reg, 1),
+				    gen_imagpart (submode, args[i].value));
+		}
+	    }
+	  
 	  /* If we have pre-computed the values to put in the registers in
 	     the case of non-aligned structures, copy them in now.  */
 
@@ -1939,9 +2101,31 @@ expand_call (exp, target, ignore)
 	  if (GET_CODE (reg) == PARALLEL)
 	    use_group_regs (&call_fusage, reg);
 	  else if (nregs == -1)
-	    use_reg (&call_fusage, reg);
+	    {
+	      if (GET_CODE (reg) != CONCAT)
+		use_reg (&call_fusage, reg);
+	      else
+		{
+		  if (XEXP (reg, 0))
+		    use_reg (&call_fusage, XEXP (reg, 0));
+		  if (XEXP (reg, 1))
+		    use_reg (&call_fusage, XEXP (reg, 1));
+		}
+	    }
 	  else
-	    use_regs (&call_fusage, REGNO (reg), nregs == 0 ? 1 : nregs);
+	    {
+	      if (GET_CODE (reg) != CONCAT)
+		use_regs (&call_fusage, REGNO (reg), nregs == 0 ? 1 : nregs);
+	      else
+		{
+		  if (args[i].partial_real)
+		    use_regs (&call_fusage, REGNO (XEXP (reg, 0)),
+			      args[i].partial_real);
+		  else
+		    use_regs (&call_fusage, REGNO (XEXP (reg, 1)),
+			      args[i].partial_imag);
+		}
+	    }
 	}
     }
 
@@ -2193,6 +2377,41 @@ expand_call (exp, target, ignore)
       SUBREG_PROMOTED_VAR_P (target) = 1;
       SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp;
     }
+  else if (GET_CODE (target) == CONCAT
+	   && GET_CODE (XEXP (target, 0)) == REG
+	   && TYPE_MODE (TREE_TYPE (exp)) != BLKmode
+	   && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
+    {
+      tree type = TREE_TYPE (exp);
+      int unsignedp = TREE_UNSIGNED (type);
+      enum machine_mode submode_orig = COMPLEX_SUBMODE (TYPE_MODE (type));
+      enum machine_mode submode_final;
+      enum machine_mode promoted_mode;
+      rtx realpart;
+      rtx imagpart;
+      
+      submode_final = promote_mode (type_for_mode (submode_orig, unsignedp),
+				     submode_orig, &unsignedp, 1);
+      promoted_mode = mode_for_size (2 * GET_MODE_SIZE (submode_final)
+				     * BITS_PER_UNIT,
+				     GET_MODE_CLASS (GET_MODE (target)), 0);
+      
+      /* If we don't promote as expected, something is wrong.  */
+      if (GET_MODE (target) != promoted_mode )
+	abort ();
+
+      realpart = gen_rtx (SUBREG, submode_orig,
+			  gen_realpart (submode_final, target), 0);
+      SUBREG_PROMOTED_VAR_P (realpart) = 1;
+      SUBREG_PROMOTED_UNSIGNED_P (realpart) = unsignedp;
+
+      imagpart = gen_rtx (SUBREG, submode_orig,
+			  gen_imagpart (submode_final, target), 0);
+      SUBREG_PROMOTED_VAR_P (imagpart) = 1;
+      SUBREG_PROMOTED_UNSIGNED_P (imagpart) = unsignedp;
+
+      target = gen_rtx (CONCAT, TYPE_MODE (type), realpart, imagpart);
+    }
 #endif
 
   /* If size of args is variable or this was a constructor call for a stack
@@ -3512,8 +3737,13 @@ store_one_arg (arg, argblock, may_be_all
   /* If this isn't going to be placed on both the stack and in registers,
      set up the register and number of words.  */
   if (! arg->pass_on_stack)
-    reg = arg->reg, partial = arg->partial;
-
+    {
+      reg = arg->reg, partial = arg->partial;
+      if (reg && COMPLEX_MODE_P (reg->mode) && partial == 0)
+	/* Deal with the imaginary part passing in stack. */
+	reg = 0;
+    }
+  
   if (reg != 0 && partial == 0)
     /* Being passed entirely in a register.  We shouldn't be called in
        this case.   */
@@ -3560,8 +3790,31 @@ store_one_arg (arg, argblock, may_be_all
 	 doesn't agree, convert the mode.  */
 
       if (arg->mode != TYPE_MODE (TREE_TYPE (pval)))
-	arg->value = convert_modes (arg->mode, TYPE_MODE (TREE_TYPE (pval)),
-				    arg->value, arg->unsignedp);
+	{
+	  if (! COMPLEX_MODE_P (arg->mode))
+	    arg->value
+	      = convert_modes (arg->mode, TYPE_MODE (TREE_TYPE (pval)),
+			       arg->value, arg->unsignedp);
+	  else
+	    {
+	      enum machine_mode submode_orig
+		= COMPLEX_SUBMODE (TYPE_MODE (TREE_TYPE (pval)));
+	      enum machine_mode submode_final
+		= COMPLEX_SUBMODE (arg->mode);
+	      rtx realpart;
+	      rtx imagpart;
+	      realpart = convert_modes (submode_final, submode_orig,
+					gen_realpart (submode_orig,
+						      arg->value),
+					arg->unsignedp);
+	      imagpart = convert_modes (submode_final, submode_orig,
+					gen_imagpart (submode_orig,
+						      arg->value),
+					arg->unsignedp);
+	      arg->value = gen_rtx (CONCAT, arg->mode,
+				    realpart, imagpart);
+	    }
+	}
 
 #ifdef ACCUMULATE_OUTGOING_ARGS
       if (arg->pass_on_stack)
@@ -3593,6 +3846,11 @@ store_one_arg (arg, argblock, may_be_all
   else if (arg->mode != BLKmode)
     {
       register int size;
+      enum machine_mode mode
+	= (!COMPLEX_MODE_P (arg->mode)) ?
+	arg->mode : COMPLEX_SUBMODE (arg->mode);
+      tree type = (!COMPLEX_MODE_P (arg->mode))?
+	TREE_TYPE (pval) : TREE_TYPE (TREE_TYPE (pval));
 
       /* Argument is a scalar, not entirely passed in registers.
 	 (If part is passed in registers, arg->partial says how much
@@ -3604,7 +3862,7 @@ store_one_arg (arg, argblock, may_be_all
 	 Note that in C the default argument promotions
 	 will prevent such mismatches.  */
 
-      size = GET_MODE_SIZE (arg->mode);
+      size = GET_MODE_SIZE (mode);
       /* Compute how much space the push instruction will push.
 	 On many machines, pushing a byte will advance the stack
 	 pointer by a halfword.  */
@@ -3615,16 +3873,57 @@ store_one_arg (arg, argblock, may_be_all
 
       /* Compute how much space the argument should get:
 	 round up to a multiple of the alignment for arguments.  */
-      if (none != FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval)))
+      if (none != FUNCTION_ARG_PADDING (mode, type))
 	used = (((size + PARM_BOUNDARY / BITS_PER_UNIT - 1)
 		 / (PARM_BOUNDARY / BITS_PER_UNIT))
 		* (PARM_BOUNDARY / BITS_PER_UNIT));
 
       /* This isn't already where we want it on the stack, so put it there.
 	 This can either be done with push or copy insns.  */
-      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
-		      0, partial, reg, used - size,
-		      argblock, ARGS_SIZE_RTX (arg->offset));
+      if (! COMPLEX_MODE_P (arg->mode))
+	emit_push_insn (arg->value, arg->mode, type, NULL_RTX,
+			0, partial, reg, used - size,
+			argblock, ARGS_SIZE_RTX (arg->offset));
+      else
+	{
+	  struct args_size offset;
+	  offset.constant = arg->offset.constant;
+	  offset.var = arg->offset.var;
+	  if (arg->partial_real > 0
+	      || arg->reg == 0
+	      || XEXP (arg->reg, 0) == 0)
+	    emit_push_insn (gen_realpart (mode, arg->value),
+			    mode, type, NULL_RTX,
+			    0, arg->partial_real,
+			    reg ? XEXP (arg->reg, 0) : reg, used - size,
+			    argblock, ARGS_SIZE_RTX (offset));
+	  
+	  if (!(arg->reg
+#if defined (REG_PARM_STACK_SPACE) && ! defined (MAYBE_REG_PARM_STACK_SPACE)
+		&& REG_PARM_STACK_SPACE (fndecl) == 0
+#endif
+		&& XEXP (arg->reg, 0) != 0 && arg->partial_real == 0)
+#ifdef ARGS_GROW_DOWNWARD
+	      /* If the real part is passing in register, the imaginary
+		 part in stack only, and ARGS_GROW_DOWNWARD is defined,
+		 allocate the stack space for the imaginary part close to
+		 next argument. */
+	      && !(arg->reg
+		   && XEXP (arg->reg, 0) != 0 && arg->partial_real == 0
+		   && (XEXP (arg->reg, 1) == 0 || arg->partial_imag > 0))
+#endif
+	      )
+	      offset.constant += arg->size.constant / 2;
+
+	  if (arg->partial_imag > 0
+	      || arg->reg == 0
+	      || XEXP (arg->reg, 1) == 0)
+	    emit_push_insn (gen_imagpart (mode, arg->value),
+			    mode, type, NULL_RTX,
+			    0, arg->partial_imag,
+			    reg ? XEXP (arg->reg, 1) : reg, used - size,
+			    argblock, ARGS_SIZE_RTX (offset));
+	}
     }
   else
     {
@@ -3666,7 +3965,10 @@ store_one_arg (arg, argblock, may_be_all
      ??? Note that this can change arg->value from arg->stack to
      arg->stack_slot and it matters when they are not the same.
      It isn't totally clear that this is correct in all cases.  */
-  if (partial == 0)
+  if (partial == 0
+      /* Do not include the case where the real/imaginary part is
+	 not partially in register. */
+      && arg->reg == 0)
     arg->value = arg->stack_slot;
 
   /* Once we have pushed something, pops can't safely
--- gcc/combine.c.orig	Sat Dec 20 06:31:23 1997
+++ gcc/combine.c	Tue Jan  6 17:26:53 1998
@@ -5297,8 +5297,10 @@ expand_field_assignment (x)
 	}
 
       /* A SUBREG between two modes that occupy the same numbers of words
-	 can be done by moving the SUBREG to the source.  */
+	 can be done by moving the SUBREG to the source.
+         But a SUBREG of a COMPLEX should not be done. */
       else if (GET_CODE (SET_DEST (x)) == SUBREG
+               && ! COMPLEX_MODE_P (GET_MODE (SUBREG_REG (SET_DEST (x))))
 	       && (((GET_MODE_SIZE (GET_MODE (SET_DEST (x)))
 		     + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
 		   == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (x))))
--- gcc/emit-rtl.c.orig	Sat Dec 20 06:31:27 1997
+++ gcc/emit-rtl.c	Tue Jan  6 17:42:46 1998
@@ -499,8 +499,7 @@ gen_reg_rtx (mode)
   if (reload_in_progress || reload_completed)
     abort ();
 
-  if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
-      || GET_MODE_CLASS (mode) == MODE_COMPLEX_INT)
+  if (COMPLEX_MODE_P (mode))
     {
       /* For complex modes, don't make a single pseudo.
 	 Instead, make a CONCAT of two pseudos.
@@ -637,10 +636,16 @@ gen_lowpart_common (mode, x)
 	     / UNITS_PER_WORD)))
     return 0;
 
-  if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
-    word = ((GET_MODE_SIZE (GET_MODE (x))
-	     - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD))
-	    / UNITS_PER_WORD);
+  if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (x)) > 0)
+    {
+      if (!COMPLEX_MODE_P (GET_MODE (x)))
+	word = ((GET_MODE_SIZE (GET_MODE (x))
+		 - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD))
+		/ UNITS_PER_WORD);
+      else
+	word = 2 * HARD_REGNO_NREGS (REGNO (x), COMPLEX_SUBMODE (GET_MODE (x)))
+	  - HARD_REGNO_NREGS (REGNO (x), mode);
+    }
 
   if ((GET_CODE (x) == ZERO_EXTEND || GET_CODE (x) == SIGN_EXTEND)
       && (GET_MODE_CLASS (mode) == MODE_INT
@@ -1017,10 +1022,17 @@ gen_highpart (mode, x)
       int word = 0;
 
       if (! WORDS_BIG_ENDIAN
-	  && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
-	word = ((GET_MODE_SIZE (GET_MODE (x))
-		 - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD))
-		/ UNITS_PER_WORD);
+	  && GET_MODE_SIZE (GET_MODE (x)) > 0)
+	{
+	  if (!COMPLEX_MODE_P (GET_MODE (x)))
+	    word = ((GET_MODE_SIZE (GET_MODE (x))
+		     - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD))
+		    / UNITS_PER_WORD);
+	  else
+	    word = 2 * HARD_REGNO_NREGS (REGNO (x),
+					 COMPLEX_SUBMODE (GET_MODE (x)))
+	      - HARD_REGNO_NREGS (REGNO (x), mode);
+	}
 
       /*
        * ??? This fails miserably for complex values being passed in registers
--- gcc/explow.c.orig	Sat Dec  6 12:19:52 1997
+++ gcc/explow.c	Wed Jan  7 16:34:26 1998
@@ -1322,7 +1322,29 @@ hard_function_value (valtype, func)
      tree valtype;
      tree func;
 {
-  rtx val = FUNCTION_VALUE (valtype, func);
+  rtx val;
+  enum machine_mode complex_mode = TYPE_MODE (valtype);
+  
+  if (COMPLEX_MODE_P (complex_mode))
+    {
+      enum machine_mode orig_word_mode = word_mode;
+      enum machine_mode submode = COMPLEX_SUBMODE (complex_mode);
+      int unsignedp = TREE_UNSIGNED (valtype);
+      word_mode = submode;
+      val = FUNCTION_VALUE (type_for_mode (submode, unsignedp), func);
+      word_mode = orig_word_mode;
+#ifdef PROMOTE_FUNCTION_RETURN
+      submode = promote_mode (type_for_mode (submode, unsignedp),
+			      submode, &unsignedp, 0);
+      complex_mode = mode_for_size (2 * GET_MODE_SIZE (submode)
+				    * BITS_PER_UNIT,
+				    GET_MODE_CLASS (complex_mode), 0);
+#endif
+      val = gen_rtx (REG, complex_mode, REGNO (val));
+    }
+  else
+    val = FUNCTION_VALUE (valtype, func);
+  
   if (GET_CODE (val) == REG
       && GET_MODE (val) == BLKmode)
     {
--- gcc/expr.c.orig	Fri Dec 19 11:41:58 1997
+++ gcc/expr.c	Wed Jan  7 16:53:23 1998
@@ -2262,12 +2262,8 @@ emit_move_insn_1 (x, y)
       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 ((class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)
-	   && BLKmode != (submode = mode_for_size ((GET_MODE_UNIT_SIZE (mode)
-						    * BITS_PER_UNIT),
-						   (class == MODE_COMPLEX_INT
-						    ? MODE_INT : MODE_FLOAT),
-						   0))
+  else if (COMPLEX_MODE_P (mode)
+	   && BLKmode != (submode = COMPLEX_SUBMODE (mode))
 	   && (mov_optab->handlers[(int) submode].insn_code
 	       != CODE_FOR_nothing))
     {
@@ -3296,6 +3292,81 @@ store_expr (exp, target, want_value)
 		    SUBREG_PROMOTED_UNSIGNED_P (target));
       return want_value ? temp : NULL_RTX;
     }
+  else if (GET_CODE (target) == CONCAT
+	   && COMPLEX_MODE_P (GET_MODE (target))
+	   && GET_CODE (XEXP (target, 0)) == SUBREG
+	   && SUBREG_PROMOTED_VAR_P (XEXP (target, 0)))
+    /* Do the same as for the previous case. */
+    {
+      rtx realpart;
+      rtx imagpart;
+      if (! want_value && INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (exp)))
+	  && TREE_TYPE (TREE_TYPE (TREE_TYPE (exp))) == 0)
+	{
+	  enum machine_mode mode;
+	  tree type;
+	  
+	  if (TREE_UNSIGNED (TREE_TYPE (exp))
+	      != SUBREG_PROMOTED_UNSIGNED_P (XEXP (target, 0)))
+	    exp
+	      = convert
+		(signed_or_unsigned_type (SUBREG_PROMOTED_UNSIGNED_P
+					  (XEXP (target, 0)),
+					  TREE_TYPE (exp)),
+		 exp);
+
+	  mode = GET_MODE (SUBREG_REG (XEXP (target, 0)));
+	  mode = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT * 2,
+				GET_MODE_CLASS (GET_MODE (target)),
+				0);
+	  
+	  exp = convert (type_for_mode (mode,
+					SUBREG_PROMOTED_UNSIGNED_P
+					(XEXP (target, 0))),
+					exp);
+	}
+	 
+      temp = expand_expr (exp, NULL_RTX, VOIDmode, 0);
+
+      realpart = gen_realpart (COMPLEX_SUBMODE (GET_MODE (temp)), temp);
+      imagpart = gen_imagpart (COMPLEX_SUBMODE (GET_MODE (temp)), temp);
+      if (GET_CODE (realpart) == MEM && want_value
+	  && (MEM_VOLATILE_P (realpart)
+	      || reg_mentioned_p (SUBREG_REG (XEXP (target, 0)),
+				  XEXP (realpart, 0))))
+	realpart = copy_to_reg (realpart);
+      if (GET_CODE (imagpart) == MEM && want_value
+	  && (MEM_VOLATILE_P (imagpart)
+	      || reg_mentioned_p (SUBREG_REG (XEXP (target, 1)),
+				  XEXP (imagpart, 0))))
+	imagpart = copy_to_reg (imagpart);
+
+      if (CONSTANT_P (realpart) && GET_MODE (realpart) == VOIDmode)
+	realpart = convert_modes (GET_MODE (SUBREG_REG
+						  (XEXP (target, 0))),
+					COMPLEX_SUBMODE (TYPE_MODE
+							   (TREE_TYPE (exp))),
+					realpart,
+					SUBREG_PROMOTED_UNSIGNED_P
+					(XEXP (target, 0)));
+      if (CONSTANT_P (imagpart) && GET_MODE (imagpart) == VOIDmode)
+	imagpart = convert_modes (GET_MODE (SUBREG_REG
+						  (XEXP (target, 1))),
+					COMPLEX_SUBMODE (TYPE_MODE
+							   (TREE_TYPE (exp))),
+					imagpart,
+					SUBREG_PROMOTED_UNSIGNED_P
+					(XEXP (target, 1)));
+
+      convert_move (SUBREG_REG (XEXP (target, 0)), realpart,
+		    SUBREG_PROMOTED_UNSIGNED_P (XEXP (target, 0)));
+      convert_move (SUBREG_REG (XEXP (target, 1)), imagpart,
+		    SUBREG_PROMOTED_UNSIGNED_P (XEXP (target, 1)));
+      temp = gen_rtx (CONCAT, TYPE_MODE (TREE_TYPE (exp)),
+		      realpart, imagpart);
+      
+      return want_value ? temp : NULL_RTX;
+    }
   else
     {
       temp = expand_expr (exp, target, GET_MODE (target), 0);
@@ -4978,10 +5049,21 @@ expand_expr (exp, target, tmode, modifie
      and 2) otherwise cse could produce an insn the machine
      cannot support.  */
 
-  if (! cse_not_expected && mode != BLKmode && target
-      && (GET_CODE (target) != REG || REGNO (target) < FIRST_PSEUDO_REGISTER))
-    target = subtarget;
+  if (! cse_not_expected && mode != BLKmode && target)
+    {
+      int flag = 1;
+      
+      if (GET_CODE (target) == REG && REGNO (target) > FIRST_PSEUDO_REGISTER)
+	flag = 0;
+      else if (GET_CODE (target) == CONCAT
+	       && GET_CODE (XEXP (target, 0)) == REG
+	       && REGNO (XEXP (target, 0)) > FIRST_PSEUDO_REGISTER)
+	flag = 0;
 
+      if (flag)
+	target = subtarget;
+    }
+  
   switch (code)
     {
     case LABEL_DECL:
@@ -5160,6 +5242,38 @@ expand_expr (exp, target, tmode, modifie
 	  return temp;
 	}
 
+      if (GET_CODE (DECL_RTL (exp)) == CONCAT
+	  && COMPLEX_MODE_P (GET_MODE (DECL_RTL (exp)))
+	  && GET_MODE (DECL_RTL (exp)) != mode)
+	{
+	  rtx realpart;
+	  rtx imagpart;
+	  enum machine_mode submode_orig = COMPLEX_SUBMODE (mode);
+	  enum machine_mode submode_final
+	    = promote_mode (type_for_mode (submode_orig,
+					   TREE_UNSIGNED (type)),
+			    submode_orig, &unsignedp, 0);
+	  enum machine_mode promoted_mode
+	    = mode_for_size (2 * GET_MODE_SIZE (submode_final)
+			     * BITS_PER_UNIT, GET_MODE_CLASS (mode), 0);
+	  /* Get the signedness used for this variable.  Ensure we get the
+	     same mode we got when the variable was declared.  */
+	  if (GET_MODE (DECL_RTL (exp)) != promoted_mode)
+	    abort ();
+
+	  realpart = gen_rtx (SUBREG, submode_orig,
+			      XEXP (DECL_RTL (exp), 0), 0);
+	  imagpart = gen_rtx (SUBREG, submode_orig,
+			      XEXP (DECL_RTL (exp), 1), 0);
+	  SUBREG_PROMOTED_VAR_P (realpart) = 1;
+	  SUBREG_PROMOTED_UNSIGNED_P (realpart) = unsignedp;
+	  SUBREG_PROMOTED_VAR_P (imagpart) = 1;
+	  SUBREG_PROMOTED_UNSIGNED_P (imagpart) = unsignedp;
+
+	  temp = gen_rtx (CONCAT, mode, realpart, imagpart);
+	  return temp;
+	}
+
       return DECL_RTL (exp);
 
     case INTEGER_CST:
@@ -5789,8 +5903,7 @@ expand_expr (exp, target, tmode, modifie
 	    || (modifier != EXPAND_CONST_ADDRESS
 		&& modifier != EXPAND_INITIALIZER
 		&& ((mode1 != BLKmode && ! direct_load[(int) mode1]
-		     && GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
-		     && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
+		     && ! COMPLEX_MODE_P (mode))
 		    /* If the field isn't aligned enough to fetch as a memref,
 		       fetch it as a bit field.  */
 		    || (SLOW_UNALIGNED_ACCESS
@@ -6493,8 +6606,7 @@ expand_expr (exp, target, tmode, modifie
       op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
 
       /* Handle complex values specially.  */
-      if (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
-	  || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+      if (COMPLEX_MODE_P (mode))
 	return expand_complex_abs (mode, op0, target, unsignedp);
 
       /* Unsigned abs is simply the operand.  Testing here means we don't
@@ -10396,8 +10508,7 @@ do_jump (exp, if_false_label, if_true_la
 
 	if (integer_zerop (TREE_OPERAND (exp, 1)))
 	  do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label);
-	else if (GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_COMPLEX_FLOAT
-		 || GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_COMPLEX_INT)
+	else if (COMPLEX_MODE_P (TYPE_MODE (inner_type)))
 	  do_jump
 	    (fold
 	     (build (TRUTH_ANDIF_EXPR, TREE_TYPE (exp),
@@ -10430,8 +10541,7 @@ do_jump (exp, if_false_label, if_true_la
 
 	if (integer_zerop (TREE_OPERAND (exp, 1)))
 	  do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label);
-	else if (GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_COMPLEX_FLOAT
-		 || GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_COMPLEX_INT)
+	else if (COMPLEX_MODE_P (TYPE_MODE (inner_type)))
 	  do_jump
 	    (fold
 	     (build (TRUTH_ORIF_EXPR, TREE_TYPE (exp),
--- gcc/function.c.orig	Mon Dec 15 02:04:57 1997
+++ gcc/function.c	Wed Jan  7 16:42:46 1998
@@ -3523,7 +3523,9 @@ aggregate_value_p (exp)
   else
     type = TREE_TYPE (exp);
 
-  if (RETURN_IN_MEMORY (type))
+  if (RETURN_IN_MEMORY (COMPLEX_MODE_P (TYPE_MODE (type))?
+			type_for_mode (COMPLEX_SUBMODE (TYPE_MODE (type)), 0)
+			: type))
     return 1;
   /* Types that are TREE_ADDRESSABLE must be constructed in memory,
      and thus can't be returned in registers.  */
@@ -3541,7 +3543,10 @@ aggregate_value_p (exp)
     return 0;
 
   regno = REGNO (reg);
-  nregs = HARD_REGNO_NREGS (regno, TYPE_MODE (type));
+  if (! COMPLEX_MODE_P (GET_MODE (reg)))
+    nregs = HARD_REGNO_NREGS (regno, TYPE_MODE (type));
+  else
+    nregs = 2*HARD_REGNO_NREGS (regno, COMPLEX_SUBMODE (TYPE_MODE (type)));
   for (i = 0; i < nregs; i++)
     if (! call_used_regs[regno + i])
       return 1;
@@ -3661,7 +3666,13 @@ assign_parms (fndecl, second_time)
       int did_conversion = 0;
       tree passed_type = DECL_ARG_TYPE (parm);
       tree nominal_type = TREE_TYPE (parm);
-
+      enum machine_mode submode;
+      tree subtype;
+      int nregs = 0;
+      int nregs_real = 0;
+      int nregs_imag = 0;
+      int in_regs;
+      
       /* Set LAST_NAMED if this is last named arg before some
 	 anonymous args.  We treat it as if it were anonymous too.  */
       int last_named = ((TREE_CHAIN (parm) == 0
@@ -3714,8 +3725,14 @@ assign_parms (fndecl, second_time)
 	   && contains_placeholder_p (TYPE_SIZE (passed_type)))
 	  || TREE_ADDRESSABLE (passed_type)
 #ifdef FUNCTION_ARG_PASS_BY_REFERENCE
-	  || FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, passed_mode,
-					      passed_type, ! last_named)
+	  || FUNCTION_ARG_PASS_BY_REFERENCE
+	     (args_so_far,
+	      (! COMPLEX_MODE_P (passed_mode))?
+	      passed_mode : COMPLEX_SUBMODE (passed_mode),
+	      (! COMPLEX_MODE_P (passed_mode))?
+	      passed_type : type_for_mode (COMPLEX_SUBMODE (passed_mode),
+					   0),
+	      ! last_named)
 #endif
 	  )
 	{
@@ -3725,25 +3742,31 @@ assign_parms (fndecl, second_time)
 	}
 
       promoted_mode = passed_mode;
+      unsignedp = TREE_UNSIGNED (passed_type);
 
+      if (COMPLEX_MODE_P (promoted_mode))
+	{
+	  submode = COMPLEX_SUBMODE (promoted_mode);
+	  subtype = type_for_mode (submode, unsignedp);
+	}
+      
 #ifdef PROMOTE_FUNCTION_ARGS
       /* Compute the mode in which the arg is actually extended to.  */
-      promoted_mode = promote_mode (passed_type, promoted_mode, &unsignedp, 1);
-#endif
-
-      /* Let machine desc say which reg (if any) the parm arrives in.
-	 0 means it arrives on the stack.  */
-#ifdef FUNCTION_INCOMING_ARG
-      entry_parm = FUNCTION_INCOMING_ARG (args_so_far, promoted_mode,
-					  passed_type, ! last_named);
-#else
-      entry_parm = FUNCTION_ARG (args_so_far, promoted_mode,
-				 passed_type, ! last_named);
+      if (! COMPLEX_MODE_P (promoted_mode))
+	{
+	  promoted_mode = promote_mode (passed_type, promoted_mode,
+					&unsignedp, 1);
+	}
+      else
+	{
+	  submode = promote_mode (subtype, submode, &unsignedp, 1);
+	  subtype = type_for_mode (submode, unsignedp);
+	  promoted_mode = mode_for_size (2 * GET_MODE_SIZE (submode)
+					 * BITS_PER_UNIT,
+					 GET_MODE_CLASS (promoted_mode), 0);
+	}
 #endif
 
-      if (entry_parm == 0)
-	promoted_mode = passed_mode;
-
 #ifdef SETUP_INCOMING_VARARGS
       /* If this is the last named parameter, do any required setup for
 	 varargs or stdargs.  We need to know about the case of this being an
@@ -3764,6 +3787,121 @@ assign_parms (fndecl, second_time)
 	}
 #endif
 
+      if (! COMPLEX_MODE_P (promoted_mode))
+	{
+	  /* Let machine desc say which reg (if any) the parm arrives in.
+	     0 means it arrives on the stack.  */
+#ifdef FUNCTION_INCOMING_ARG
+	  entry_parm = FUNCTION_INCOMING_ARG (args_so_far, promoted_mode,
+					      passed_type, ! last_named);
+#else
+	  entry_parm = FUNCTION_ARG (args_so_far, promoted_mode,
+				     passed_type, ! last_named);
+#endif
+
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+	  if (entry_parm)
+	    nregs = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, promoted_mode,
+						passed_type, ! last_named);
+#endif
+
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+	  in_regs = 1;
+#else
+#ifdef FUNCTION_INCOMING_ARG
+	  in_regs = FUNCTION_INCOMING_ARG (args_so_far, promoted_mode,
+					   passed_type,
+					   (! last_named
+					    || varargs_setup)) != 0;
+#else
+	  in_regs = FUNCTION_ARG (args_so_far, promoted_mode,
+				  passed_type,
+				  ! last_named || varargs_setup) != 0;
+#endif
+#endif
+
+	  /* Update info on where next arg arrives in registers.  */
+	  FUNCTION_ARG_ADVANCE (args_so_far, promoted_mode,
+				passed_type, ! last_named);
+	}
+      else
+	{
+	  rtx realpart;
+	  rtx imagpart;
+	  int in_regs_real;
+	  int in_regs_imag;
+	  
+#ifdef FUNCTION_INCOMING_ARG
+	  realpart = FUNCTION_INCOMING_ARG (args_so_far, submode,
+					    subtype, ! last_named);
+#else
+	  realpart = FUNCTION_ARG (args_so_far, submode,
+				   subtype, ! last_named);
+#endif
+
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+	  if (realpart)
+	    nregs_real = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, submode,
+						     subtype, ! last_named);
+#endif
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+	  in_regs_real = 1;
+#else
+#ifdef FUNCTION_INCOMING_ARG
+	  in_regs_real = FUNCTION_INCOMING_ARG (args_so_far, submode, subtype,
+						(! last_named
+						 || varargs_setup)) != 0;
+#else
+	  in_regs_real = FUNCTION_ARG (args_so_far, submode, subtype,
+				       ! last_named || varargs_setup) != 0;
+#endif
+#endif
+
+	  FUNCTION_ARG_ADVANCE (args_so_far, submode,
+				subtype, !last_named);
+#ifdef FUNCTION_INCOMING_ARG
+	  imagpart = FUNCTION_INCOMING_ARG (args_so_far, submode,
+					    subtype, ! last_named);
+#else
+	  imagpart = FUNCTION_ARG (args_so_far, submode,
+				   subtype, ! last_named);
+#endif
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+	  if (imagpart)
+	    nregs_imag = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, submode,
+						     subtype, ! last_named);
+#endif
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+	  in_regs_imag = 1;
+#else
+#ifdef FUNCTION_INCOMING_ARG
+	  in_regs_imag = FUNCTION_INCOMING_ARG (args_so_far, submode, subtype,
+						(! last_named
+						 || varargs_setup)) != 0;
+#else
+	  in_regs_imag = FUNCTION_ARG (args_so_far, submode, subtype,
+				       ! last_named || varargs_setup) != 0;
+#endif
+#endif
+
+	  FUNCTION_ARG_ADVANCE (args_so_far, submode,
+				subtype, !last_named);
+	  if (realpart == 0 && imagpart == 0)
+	    in_regs = in_regs_real;
+	  else if (imagpart)
+	    in_regs = in_regs_imag;
+	  else if (realpart)
+	    in_regs = in_regs_real;
+	  
+	  if (realpart != 0)
+	    entry_parm = gen_rtx (CONCAT, promoted_mode, realpart, imagpart);
+	  else
+	    entry_parm = 0;
+	}
+      
+      if (entry_parm == 0)
+	promoted_mode = passed_mode;
+
       /* Determine parm's home in the stack,
 	 in case it arrives in the stack or we should pretend it did.
 
@@ -3777,40 +3915,114 @@ assign_parms (fndecl, second_time)
 	 In this case, we call FUNCTION_ARG with NAMED set to 1 instead of
 	 0 as it was the previous time.  */
 
-      locate_and_pad_parm (promoted_mode, passed_type,
-#ifdef STACK_PARMS_IN_REG_PARM_AREA
-			   1,
-#else
-#ifdef FUNCTION_INCOMING_ARG
-			   FUNCTION_INCOMING_ARG (args_so_far, promoted_mode,
-						  passed_type,
-						  (! last_named
-						   || varargs_setup)) != 0,
-#else
-			   FUNCTION_ARG (args_so_far, promoted_mode,
-					 passed_type,
-					 ! last_named || varargs_setup) != 0,
-#endif
+      {
+	enum machine_mode pad_mode = promoted_mode;
+	tree pad_type = passed_type;
+	
+	if (COMPLEX_MODE_P (promoted_mode) && entry_parm != 0
+#if defined (REG_PARM_STACK_SPACE) && ! defined (MAYBE_REG_PARM_STACK_SPACE)
+	    && REG_PARM_STACK_SPACE (fndecl) == 0
 #endif
-			   fndecl, &stack_args_size, &stack_offset, &arg_size);
-
-      if (! second_time)
-	{
-	  rtx offset_rtx = ARGS_SIZE_RTX (stack_offset);
+	    )
+	  {
+	    /* If we do not need to allocate stack space for
+	       arguments passing in register, and the real part
+	       of a complex argument is passing in register,
+	       we only allocate stack space for the imaginary part. */
+	    if (XEXP (entry_parm, 0) != 0 && nregs_real == 0
+		&& (XEXP (entry_parm, 1) == 0 || nregs_imag > 0))
+	      {
+		pad_mode = COMPLEX_SUBMODE (passed_mode);
+		pad_type = type_for_mode (pad_mode, unsignedp);
+	      }
+	  }
+	locate_and_pad_parm (pad_mode, pad_type, in_regs,
+			     fndecl, &stack_args_size, &stack_offset,
+			     &arg_size);
+      
+	if (! second_time)
+	  {
+	    rtx offset_rtx = ARGS_SIZE_RTX (stack_offset);
 
-	  if (offset_rtx == const0_rtx)
-	    stack_parm = gen_rtx (MEM, promoted_mode, internal_arg_pointer);
-	  else
-	    stack_parm = gen_rtx (MEM, promoted_mode,
-				  gen_rtx (PLUS, Pmode,
-					   internal_arg_pointer, offset_rtx));
+	    if (! COMPLEX_MODE_P (promoted_mode))
+	      {
+		if (offset_rtx == const0_rtx)
+		  stack_parm = gen_rtx (MEM, promoted_mode,
+					internal_arg_pointer);
+		else
+		  stack_parm = gen_rtx (MEM, promoted_mode,
+					gen_rtx (PLUS, Pmode,
+						 internal_arg_pointer,
+						 offset_rtx));
+
+		/* If this is a memory ref that contains aggregate components,
+		   mark it as such for cse and loop optimize.  Likewise if it
+		   is readonly.  */
+		MEM_IN_STRUCT_P (stack_parm) = aggregate;
+		RTX_UNCHANGING_P (stack_parm) = TREE_READONLY (parm);
+	      }
+	    else
+	      {
+		rtx realpart;
+		rtx imagpart;
+		int imag_offset;
+		enum machine_mode stack_mode = promoted_mode;
+		enum machine_mode sub_stack_mode;
+	      
+		if (entry_parm
+		    && XEXP (entry_parm, 0) != 0 && nregs_real == 0)
+		    stack_mode = passed_mode;
+		sub_stack_mode = COMPLEX_SUBMODE (stack_mode);
+
+		if (offset_rtx == const0_rtx)
+		  realpart = gen_rtx (MEM, sub_stack_mode,
+				      internal_arg_pointer);
+		else
+		  realpart = gen_rtx (MEM, sub_stack_mode,
+				      gen_rtx (PLUS, Pmode,
+					       internal_arg_pointer,
+					       offset_rtx));
+		MEM_IN_STRUCT_P (realpart) = aggregate;
+		RTX_UNCHANGING_P (realpart) = TREE_READONLY (parm);
 
-	  /* If this is a memory ref that contains aggregate components,
-	     mark it as such for cse and loop optimize.  Likewise if it
-	     is readonly.  */
-	  MEM_IN_STRUCT_P (stack_parm) = aggregate;
-	  RTX_UNCHANGING_P (stack_parm) = TREE_READONLY (parm);
-	}
+		if (COMPLEX_MODE_P (pad_mode))
+		  {
+		    imag_offset = arg_size.constant / 2;
+		    stack_offset.constant += imag_offset;
+		    
+		    offset_rtx = ARGS_SIZE_RTX (stack_offset);
+		    imagpart = gen_rtx (MEM, sub_stack_mode,
+					gen_rtx (PLUS, Pmode,
+						 internal_arg_pointer,
+						 offset_rtx));
+		    MEM_IN_STRUCT_P (imagpart) = aggregate;
+		    RTX_UNCHANGING_P (imagpart) = TREE_READONLY (parm);
+		  }
+		else
+		  imagpart = 0;
+#ifdef ARGS_GROW_DOWNWARD
+		/* If the real part is passing in register, the imaginary
+		   part in stack only, and ARGS_GROW_DOWNWARD is defined,
+		   allocate the stack space for the imaginary part close to
+		   next argument. */
+	    if (COMPLEX_MODE_P (promoted_mode) && entry_parm != 0
+		&& XEXP (entry_parm, 0) != 0 && nregs_real == 0
+		&& (XEXP (entry_parm, 1) == 0 || nregs_imag > 0))
+	      {
+		rtx tmp = realpart;
+		realpart = imagpart;
+		imagpart = tmp;
+	      }
+#endif
+		if (COMPLEX_MODE_P (pad_mode))
+		  stack_parm = gen_rtx (CONCAT, stack_mode,
+					realpart, imagpart);
+		else
+		  stack_parm = gen_rtx (CONCAT, stack_mode,
+					imagpart, realpart);
+	      }
+	  }
+      }
 
       /* If this parameter was passed both in registers and in the stack,
 	 use the copy on the stack.  */
@@ -3826,15 +4038,49 @@ assign_parms (fndecl, second_time)
 	 we could put it together in a pseudoreg directly,
 	 but for now that's not worth bothering with.  */
 
-      if (entry_parm)
+      if (entry_parm != 0 && GET_CODE (entry_parm) == CONCAT
+	  && COMPLEX_MODE_P (GET_MODE (entry_parm)))
 	{
-	  int nregs = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, promoted_mode,
-						  passed_type, ! last_named);
+	  if (nregs_real > 0 || nregs_imag > 0
+	      || (XEXP (entry_parm, 0) && XEXP (entry_parm, 1) == 0))
+	    {
+	      if (nregs_real > 0 || nregs_imag > 0)
+		{
+		  current_function_pretend_args_size
+		    = ((((nregs_real + nregs_imag) * UNITS_PER_WORD)
+			+ (PARM_BOUNDARY / BITS_PER_UNIT) - 1)
+		       / (PARM_BOUNDARY / BITS_PER_UNIT)
+		       * (PARM_BOUNDARY / BITS_PER_UNIT));
+		}
+	      if (! second_time)
+		{
+		  if (nregs_real > 0)
+		    move_block_from_reg (REGNO (XEXP (entry_parm, 0)),
+					 validize_mem (XEXP (stack_parm, 0)),
+					 nregs_real,
+					 int_size_in_bytes (DECL_ARG_TYPE
+							    (parm))/2);
+		  else if (nregs_imag > 0)
+		    move_block_from_reg (REGNO (XEXP (entry_parm, 1)),
+					 validize_mem (XEXP (stack_parm, 1)),
+					 nregs_imag,
+					 int_size_in_bytes (DECL_ARG_TYPE
+							    (parm))/2);
+		}
 
+	      if (XEXP (entry_parm, 0) && nregs_real == 0)
+		XEXP (entry_parm, 1) = stack_parm ? XEXP (stack_parm, 1) : 0;
+	      else
+		entry_parm = stack_parm;
+	    }
+	}
+      else if (entry_parm)
+	{
 	  if (nregs > 0)
 	    {
 	      current_function_pretend_args_size
-		= (((nregs * UNITS_PER_WORD) + (PARM_BOUNDARY / BITS_PER_UNIT) - 1)
+		= (((nregs * UNITS_PER_WORD)
+		    + (PARM_BOUNDARY / BITS_PER_UNIT) - 1)
 		   / (PARM_BOUNDARY / BITS_PER_UNIT)
 		   * (PARM_BOUNDARY / BITS_PER_UNIT));
 
@@ -3869,6 +4115,8 @@ assign_parms (fndecl, second_time)
 	 to indicate there is no preallocated stack slot for the parm.  */
 
       if (entry_parm == stack_parm
+	  || (COMPLEX_MODE_P (promoted_mode) && entry_parm && stack_parm
+	      && XEXP (entry_parm, 1) == XEXP (stack_parm, 1))
 #if defined (REG_PARM_STACK_SPACE) && ! defined (MAYBE_REG_PARM_STACK_SPACE)
 	  /* On some machines, even if a parm value arrives in a register
 	     there is still an (uninitialized) stack slot allocated for it.
@@ -3891,11 +4139,6 @@ assign_parms (fndecl, second_time)
 	/* No stack slot was pushed for this parm.  */
 	stack_parm = 0;
 
-      /* Update info on where next arg arrives in registers.  */
-
-      FUNCTION_ARG_ADVANCE (args_so_far, promoted_mode,
-			    passed_type, ! last_named);
-
       /* If this is our second time through, we are done with this parm.  */
       if (second_time)
 	continue;
@@ -3970,6 +4213,16 @@ assign_parms (fndecl, second_time)
 
       if (GET_CODE (entry_parm) == REG && !(hide_last_arg && last_named))
 	  emit_insn (gen_rtx (USE, GET_MODE (entry_parm), entry_parm));
+      else if (GET_CODE (entry_parm) == CONCAT
+	       && !(hide_last_arg && last_named))
+	{
+	  if (GET_CODE (XEXP (entry_parm, 0)) == REG)
+	    emit_insn (gen_rtx (USE, GET_MODE (XEXP (entry_parm, 0)),
+				XEXP (entry_parm, 0)));
+	  if (GET_CODE (XEXP (entry_parm, 1)) == REG)
+	    emit_insn (gen_rtx (USE, GET_MODE (XEXP (entry_parm, 1)),
+				XEXP (entry_parm, 1)));
+	}
 #endif
 
       /* ENTRY_PARM is an RTX for the parameter as it arrives,
@@ -4056,12 +4309,38 @@ assign_parms (fndecl, second_time)
 
 	  unsignedp = TREE_UNSIGNED (TREE_TYPE (parm));
 
-	  promoted_nominal_mode
-	    = promote_mode (TREE_TYPE (parm), nominal_mode, &unsignedp, 0);
-
-	  parmreg = gen_reg_rtx (promoted_nominal_mode);
-	  mark_user_reg (parmreg);
-
+	  if (! COMPLEX_MODE_P (TYPE_MODE (TREE_TYPE (parm))))
+	    {
+	      promoted_nominal_mode
+		= promote_mode (TREE_TYPE (parm), nominal_mode, &unsignedp, 0);
+	      
+	      parmreg = gen_reg_rtx (promoted_nominal_mode);
+	      mark_user_reg (parmreg);
+	    }
+	  else
+	    {
+	      enum machine_mode submode_nominal
+		= COMPLEX_SUBMODE (nominal_mode);
+	      rtx realpart;
+	      rtx imagpart;
+	      
+	      submode_nominal
+		= promote_mode (type_for_mode (submode_nominal, unsignedp),
+				submode_nominal, &unsignedp, 0);
+	      promoted_nominal_mode
+		= mode_for_size (2 * GET_MODE_SIZE (submode_nominal)
+				 * BITS_PER_UNIT,
+				GET_MODE_CLASS (nominal_mode), 0);
+	      
+	      realpart = gen_reg_rtx (submode_nominal);
+	      imagpart = gen_reg_rtx (submode_nominal);
+
+	      parmreg = gen_rtx (CONCAT, promoted_nominal_mode,
+				 realpart, imagpart);
+	      mark_user_reg (realpart);
+	      mark_user_reg (imagpart);
+	    }
+	    
 	  /* If this was an item that we received a pointer to, set DECL_RTL
 	     appropriately.  */
 	  if (passed_pointer)
@@ -4075,7 +4354,10 @@ assign_parms (fndecl, second_time)
 
 	  /* Copy the value into the register.  */
 	  if (nominal_mode != passed_mode
-	      || promoted_nominal_mode != promoted_mode)
+	      || promoted_nominal_mode != promoted_mode
+	      || (COMPLEX_MODE_P (passed_mode)
+		  && GET_MODE (XEXP (entry_parm, 0))
+		  != GET_MODE (XEXP (entry_parm, 1))))
 	    {
 	      /* ENTRY_PARM has been converted to PROMOTED_MODE, its
 		 mode, by the caller.  We now have to convert it to 
@@ -4096,22 +4378,60 @@ assign_parms (fndecl, second_time)
 		 a pseudo reg here, and save the conversion until after all
 		 parameters have been moved.  */
 
-	      rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm));
+	      if (! COMPLEX_MODE_P (GET_MODE (entry_parm)))
+		{
+		  rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm));
 
-	      emit_move_insn (tempreg, validize_mem (entry_parm));
+		  emit_move_insn (tempreg, validize_mem (entry_parm));
 
-	      push_to_sequence (conversion_insns);
-	      tempreg = convert_to_mode (nominal_mode, tempreg, unsignedp);
+		  push_to_sequence (conversion_insns);
+		  tempreg = convert_to_mode (nominal_mode, tempreg, unsignedp);
 
-	      expand_assignment (parm,
-				 make_tree (nominal_type, tempreg), 0, 0);
-	      conversion_insns = get_insns ();
-	      did_conversion = 1;
-	      end_sequence ();
+		  expand_assignment (parm,
+				     make_tree (nominal_type, tempreg), 0, 0);
+		  conversion_insns = get_insns ();
+		  did_conversion = 1;
+		  end_sequence ();
+		}
+	      else
+		{
+		  rtx realpart = gen_reg_rtx (GET_MODE (XEXP (entry_parm, 0)));
+		  rtx imagpart = gen_reg_rtx (GET_MODE (XEXP (entry_parm, 1)));
+		  enum machine_mode submode_nominal
+		    = COMPLEX_SUBMODE (nominal_mode);
+		  rtx tempreg;
+		  
+		  emit_move_insn (realpart,
+				  validize_mem (XEXP (entry_parm, 0)));
+		  emit_move_insn (imagpart,
+				  validize_mem (XEXP (entry_parm, 1)));
+
+		  push_to_sequence (conversion_insns);
+		  realpart = convert_to_mode (submode_nominal, realpart, unsignedp);
+		  imagpart = convert_to_mode (submode_nominal, imagpart, unsignedp);
+		  tempreg = gen_rtx (CONCAT, nominal_mode,
+				     realpart, imagpart);
+		  
+		  expand_assignment (parm,
+				     make_tree (nominal_type, tempreg), 0, 0);
+		  conversion_insns = get_insns ();
+		  did_conversion = 1;
+		  end_sequence ();
+		}
 	    }
 	  else
-	    emit_move_insn (parmreg, validize_mem (entry_parm));
-
+	    {
+	      if (! COMPLEX_MODE_P (GET_MODE (entry_parm)))
+		emit_move_insn (parmreg, validize_mem (entry_parm));
+	      else
+		{
+		  emit_move_insn (XEXP (parmreg, 0),
+				  validize_mem (XEXP (entry_parm, 0)));
+		  emit_move_insn (XEXP (parmreg, 1),
+				  validize_mem (XEXP (entry_parm, 1)));
+		}
+	    }
+	  
 	  /* If we were passed a pointer but the actual value
 	     can safely live in a register, put it in one.  */
 	  if (passed_pointer && TYPE_MODE (TREE_TYPE (parm)) != BLKmode
@@ -4145,10 +4465,16 @@ assign_parms (fndecl, second_time)
 	     modified, don't do the copy.  */
 
 	  else if (passed_pointer
-		   && FUNCTION_ARG_CALLEE_COPIES (args_so_far,
-						  TYPE_MODE (DECL_ARG_TYPE (parm)),
-						  DECL_ARG_TYPE (parm),
-						  ! last_named)
+		   && FUNCTION_ARG_CALLEE_COPIES
+		   (args_so_far,
+		    (! COMPLEX_MODE_P (TYPE_MODE (DECL_ARG_TYPE (parm))))?
+		    TYPE_MODE (DECL_ARG_TYPE (parm))
+		    : COMPLEX_SUBMODE (TYPE_MODE (DECL_ARG_TYPE (parm))),
+		    (! COMPLEX_MODE_P (TYPE_MODE (DECL_ARG_TYPE (parm))))?
+		    DECL_ARG_TYPE (parm)
+		    : type_for_mode (COMPLEX_SUBMODE (TYPE_MODE (DECL_ARG_TYPE (parm))),
+				     0),
+		    ! last_named)
 		   && ! TREE_ADDRESSABLE (DECL_ARG_TYPE (parm)))
 	    {
 	      rtx copy;
@@ -4219,15 +4545,13 @@ assign_parms (fndecl, second_time)
 	    {
 	      enum machine_mode submode = GET_MODE (XEXP (parmreg, 0));
 
-	      regnor = REGNO (gen_realpart (submode, parmreg));
-	      regnoi = REGNO (gen_imagpart (submode, parmreg));
+	      regnor = REGNO (XEXP (parmreg, 0));
+	      regnoi = REGNO (XEXP (parmreg, 1));
 
 	      if (stack_parm != 0)
 		{
-		  parm_reg_stack_loc[regnor]
-		    = gen_realpart (submode, stack_parm);
-		  parm_reg_stack_loc[regnoi]
-		    = gen_imagpart (submode, stack_parm);
+		  parm_reg_stack_loc[regnor] = XEXP (stack_parm, 0);
+		  parm_reg_stack_loc[regnoi] = XEXP (stack_parm, 1);
 		}
 	      else
 		{
@@ -4294,53 +4618,157 @@ assign_parms (fndecl, second_time)
 	  /* Value must be stored in the stack slot STACK_PARM
 	     during function execution.  */
 
-	  if (promoted_mode != nominal_mode)
+	  if (! COMPLEX_MODE_P (promoted_mode))
 	    {
-	      /* Conversion is required.   */
-	      rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm));
-
-	      emit_move_insn (tempreg, validize_mem (entry_parm));
+	      if (promoted_mode != nominal_mode)
+		{
+		  /* Conversion is required.   */
+		  rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm));
+		  
+		  emit_move_insn (tempreg, validize_mem (entry_parm));
+		  
+		  push_to_sequence (conversion_insns);
+		  entry_parm
+		    = convert_to_mode (nominal_mode, tempreg,
+				       TREE_UNSIGNED (TREE_TYPE (parm)));
+		  if (stack_parm)
+		    {
+		      /* ??? This may need a big-endian conversion on sparc64.  */
+		      stack_parm = change_address (stack_parm, nominal_mode,
+						   NULL_RTX);
+		    }
+		  conversion_insns = get_insns ();
+		  did_conversion = 1;
+		  end_sequence ();
+		}
+	    }
+	  else
+	    {
+	      enum machine_mode submode_nominal
+		= COMPLEX_SUBMODE (nominal_mode);
+	      rtx realpart;
+	      rtx imagpart;
+	      int unsignedp1 = TREE_UNSIGNED (TREE_TYPE (parm));
 
-	      push_to_sequence (conversion_insns);
-	      entry_parm = convert_to_mode (nominal_mode, tempreg,
-					    TREE_UNSIGNED (TREE_TYPE (parm)));
-	      if (stack_parm)
+	      if (GET_MODE (XEXP (entry_parm, 0)) != submode_nominal)
 		{
-		  /* ??? This may need a big-endian conversion on sparc64.  */
-		  stack_parm = change_address (stack_parm, nominal_mode,
-					       NULL_RTX);
+		  realpart = gen_reg_rtx (submode);
+		  emit_move_insn (realpart,
+				  validize_mem (XEXP (entry_parm, 0)));
+		  push_to_sequence (conversion_insns);
+		  realpart = convert_to_mode (submode_nominal, realpart,
+					      unsignedp1);
+		  conversion_insns = get_insns ();
+		  did_conversion = 1;
+		  end_sequence ();
 		}
-	      conversion_insns = get_insns ();
-	      did_conversion = 1;
-	      end_sequence ();
+	      else
+		realpart = XEXP (entry_parm, 0);
+	      
+	      if (GET_MODE (XEXP (entry_parm, 1)) != submode_nominal)
+		{
+		  imagpart = gen_reg_rtx (submode);
+		  emit_move_insn (imagpart,
+				  validize_mem (XEXP (entry_parm, 1)));
+		  push_to_sequence (conversion_insns);
+		  imagpart = convert_to_mode (submode_nominal, imagpart,
+					      unsignedp1);
+		  conversion_insns = get_insns ();
+		  did_conversion = 1;
+		  end_sequence ();
+		}
+	      else
+		imagpart = XEXP (entry_parm, 1);
+
+	      if (GET_MODE (XEXP (entry_parm, 0)) != submode_nominal
+		  || GET_MODE (XEXP (entry_parm, 1)) != submode_nominal)
+		entry_parm = gen_rtx (CONCAT, nominal_mode,
+				      realpart, imagpart);
 	    }
 
 	  if (entry_parm != stack_parm)
 	    {
-	      if (stack_parm == 0)
+	      enum machine_mode entry_mode = GET_MODE (entry_parm);
+		  
+	      if (! COMPLEX_MODE_P (entry_mode))
 		{
-		  stack_parm
-		    = assign_stack_local (GET_MODE (entry_parm),
-					  GET_MODE_SIZE (GET_MODE (entry_parm)), 0);
-		  /* If this is a memory ref that contains aggregate components,
-		     mark it as such for cse and loop optimize.  */
-		  MEM_IN_STRUCT_P (stack_parm) = aggregate;
+		  if (stack_parm == 0)
+		    {
+		      stack_parm
+			= assign_stack_local (GET_MODE (entry_parm),
+					      GET_MODE_SIZE (GET_MODE
+							     (entry_parm)), 0);
+		      /* If this is a memory ref that contains aggregate components,
+			 mark it as such for cse and loop optimize.  */
+		      MEM_IN_STRUCT_P (stack_parm) = aggregate;
+		    }
+		}
+	      else
+		{
+		  rtx realpart;
+		  rtx imagpart;
+		  enum machine_mode submode
+		    = COMPLEX_SUBMODE (entry_mode);
+
+		  if (stack_parm == 0 || XEXP (stack_parm, 0) == 0)
+		    {
+		      realpart
+			= assign_stack_local (submode,
+					      GET_MODE_SIZE (submode), 0);
+		      MEM_IN_STRUCT_P (realpart) = aggregate;
+		    }
+		  if (stack_parm == 0 || XEXP (stack_parm, 1) == 0)
+		    {
+		      imagpart
+			= assign_stack_local (submode,
+					      GET_MODE_SIZE (submode), 0);
+		      MEM_IN_STRUCT_P (imagpart) = aggregate;
+		    }
+		  if (stack_parm == 0)
+		    stack_parm = gen_rtx (CONCAT, entry_mode,
+					  realpart, imagpart);
+		  else if (XEXP (stack_parm, 0) == 0)
+		    XEXP (stack_parm, 0) = realpart;
+		  else if (XEXP (stack_parm, 1) == 0)
+		    XEXP (stack_parm, 1) = imagpart;
 		}
-
 	      if (promoted_mode != nominal_mode)
 		{
 		  push_to_sequence (conversion_insns);
-		  emit_move_insn (validize_mem (stack_parm),
-				  validize_mem (entry_parm));
+		  if (! COMPLEX_MODE_P (entry_mode))
+		    {
+		      emit_move_insn (validize_mem (stack_parm),
+				      validize_mem (entry_parm));
+		    }
+		  else
+		    {
+		      emit_move_insn (validize_mem (XEXP (stack_parm, 0)),
+				      validize_mem (XEXP (entry_parm, 0)));
+		      emit_move_insn (validize_mem (XEXP (stack_parm, 1)),
+				      validize_mem (XEXP (entry_parm, 1)));
+		    }
+		  
 		  conversion_insns = get_insns ();
 		  end_sequence ();
 		}
 	      else
-		emit_move_insn (validize_mem (stack_parm),
-				validize_mem (entry_parm));
+		{
+		  if (! COMPLEX_MODE_P (entry_mode))
+		    {
+		      emit_move_insn (validize_mem (stack_parm),
+				      validize_mem (entry_parm));
+		    }
+		  else
+		    {
+		      emit_move_insn (validize_mem (XEXP (stack_parm, 0)),
+				      validize_mem (XEXP (entry_parm, 0)));
+		      emit_move_insn (validize_mem (XEXP (stack_parm, 1)),
+				      validize_mem (XEXP (entry_parm, 1)));
+		    }
+		}
 	    }
-	  if (flag_check_memory_usage)
-	    {
+  	  if (flag_check_memory_usage)
+  	    {
 	      push_to_sequence (conversion_insns);
 	      emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
 				 XEXP (stack_parm, 0), ptr_mode,
@@ -4511,12 +4939,32 @@ locate_and_pad_parm (passed_mode, type, 
      struct args_size *offset_ptr;
      struct args_size *arg_size_ptr;
 {
-  tree sizetree
-    = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
-  enum direction where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
-  int boundary = FUNCTION_ARG_BOUNDARY (passed_mode, type);
-  int boundary_in_bytes = boundary / BITS_PER_UNIT;
+  tree sizetree;
+  enum direction where_pad;
+  enum machine_mode mode;
+  int boundary;
+  int boundary_in_bytes;
   int reg_parm_stack_space = 0;
+  int i;
+  
+  if (! COMPLEX_MODE_P (passed_mode))
+    {
+      sizetree = type ? size_in_bytes (type)
+	: size_int (GET_MODE_SIZE (passed_mode));
+      where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
+      boundary = FUNCTION_ARG_BOUNDARY (passed_mode, type);
+      mode = passed_mode;
+    }
+  else
+    {
+      tree subtype;
+      mode = COMPLEX_SUBMODE (passed_mode);
+      subtype = type_for_mode (mode, 0);
+      sizetree = size_int (GET_MODE_SIZE (mode));
+      where_pad = FUNCTION_ARG_PADDING (mode, subtype);
+      boundary = FUNCTION_ARG_BOUNDARY (mode, subtype);
+    }
+  boundary_in_bytes = boundary / BITS_PER_UNIT;
 
 #ifdef REG_PARM_STACK_SPACE
   /* If we have found a stack parm before we reach the end of the
@@ -4562,7 +5010,13 @@ locate_and_pad_parm (passed_mode, type, 
       && (TREE_CODE (sizetree) != INTEGER_CST
 	  || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY)))
     sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
-  SUB_PARM_SIZE (*offset_ptr, sizetree);
+  if (COMPLEX_MODE_P (passed_mode))
+    {
+      SUB_PARM_SIZE (*offset_ptr, sizetree);
+      SUB_PARM_SIZE (*offset_ptr, sizetree);
+    }
+  else
+    SUB_PARM_SIZE (*offset_ptr, sizetree);
   if (where_pad != downward)
     pad_to_arg_alignment (offset_ptr, boundary);
   if (initial_offset_ptr->var)
@@ -4593,14 +5047,20 @@ locate_and_pad_parm (passed_mode, type, 
     /* However, BLKmode args passed in regs have their padding done elsewhere.
        The stack slot must be able to hold the entire register.  */
       && !(in_regs && passed_mode == BLKmode))
-    pad_below (offset_ptr, passed_mode, sizetree);
+    pad_below (offset_ptr, mode, sizetree);
 
   if (where_pad != none
       && (TREE_CODE (sizetree) != INTEGER_CST
 	  || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY)))
     sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
 
-  ADD_PARM_SIZE (*arg_size_ptr, sizetree);
+  if (COMPLEX_MODE_P (passed_mode))
+    {
+      ADD_PARM_SIZE (*arg_size_ptr, sizetree);
+      ADD_PARM_SIZE (*arg_size_ptr, sizetree);
+    }
+  else
+    ADD_PARM_SIZE (*arg_size_ptr, sizetree);    
 #endif /* ARGS_GROW_DOWNWARD */
 }
 
@@ -5586,10 +6046,61 @@ expand_function_start (subr, parms_have_
       tree type = TREE_TYPE (DECL_RESULT (subr));
       int unsignedp = TREE_UNSIGNED (type);
 
-      mode = promote_mode (type, mode, &unsignedp, 1);
+      if (! COMPLEX_MODE_P (mode))
+	{
+	  mode = promote_mode (type, mode, &unsignedp, 1);
+	}
+      else
+	{
+	  enum machine_mode submode = COMPLEX_SUBMODE (mode);
+	  submode = promote_mode (type_for_mode (submode,
+						 TREE_UNSIGNED (type)),
+				  submode, &unsignedp, 1);
+	  mode = mode_for_size (2 * GET_MODE_SIZE (submode) * BITS_PER_UNIT,
+				GET_MODE_CLASS (mode), 0);
+	}
 #endif
 
       DECL_RTL (DECL_RESULT (subr)) = gen_reg_rtx (mode);
+    }
+  else if (COMPLEX_MODE_P (DECL_MODE (DECL_RESULT (subr))))
+    /* To return a complex value, using two registers. */
+    {
+      enum machine_mode mode = DECL_MODE (DECL_RESULT (subr));
+      enum machine_mode submode = COMPLEX_SUBMODE (mode);
+      rtx realpart;
+      int unsignedp = TREE_UNSIGNED (TREE_TYPE (DECL_RESULT (subr)));
+      tree parttree = type_for_mode (submode, unsignedp);
+      
+#ifdef FUNCTION_OUTGOING_VALUE
+      realpart = FUNCTION_OUTGOING_VALUE (parttree, subr);
+#else
+      realpart = FUNCTION_VALUE (parttree, subr);
+#endif
+
+      if (GET_CODE (realpart) == REG)
+	{
+#ifdef PROMOTE_FUNCTION_RETURN
+	  submode = promote_mode (type_for_mode (submode, unsignedp),
+				  submode, &unsignedp, 1);
+	  mode = mode_for_size (2 * GET_MODE_SIZE (submode)
+				* BITS_PER_UNIT,
+				GET_MODE_CLASS (mode), 0);
+#endif
+	  DECL_RTL (DECL_RESULT (subr))
+	    = gen_rtx (REG, mode, REGNO (realpart));
+	}
+      else
+	abort ();
+      
+      /* Mark this reg as the function's return value.  */
+      if (GET_CODE (DECL_RTL (DECL_RESULT (subr))) == REG)
+	{
+	  REG_FUNCTION_VALUE_P (DECL_RTL (DECL_RESULT (subr))) = 1;
+	  /* Needed because we may need to move this to memory
+	     in case it's a named return value whose address is taken.  */
+	  DECL_REGISTER (DECL_RESULT (subr)) = 1;
+	}
     }
   else
     /* Scalar, returned in a register.  */
--- gcc/integrate.c.orig	Thu Dec 18 17:42:35 1997
+++ gcc/integrate.c	Wed Jan  7 16:45:26 1998
@@ -1382,11 +1382,32 @@ expand_inline_function (fndecl, parms, t
 	  if (GET_MODE (loc) != TYPE_MODE (TREE_TYPE (arg)))
 	    /* The mode if LOC and ARG can differ if LOC was a variable
 	       that had its mode promoted via PROMOTED_MODE.  */
-	    arg_vals[i] = convert_modes (GET_MODE (loc),
-					 TYPE_MODE (TREE_TYPE (arg)),
-					 expand_expr (arg, NULL_RTX, mode,
-						      EXPAND_SUM),
-					 TREE_UNSIGNED (TREE_TYPE (formal)));
+	    {
+	      if (! COMPLEX_MODE_P (GET_MODE (loc)))
+		arg_vals[i]
+		  = convert_modes (GET_MODE (loc),
+				   TYPE_MODE (TREE_TYPE (arg)),
+				   expand_expr (arg, NULL_RTX, mode,
+						EXPAND_SUM),
+				   TREE_UNSIGNED (TREE_TYPE (formal)));
+	      else
+		{
+		  rtx temp = expand_expr (arg, NULL_RTX, mode, EXPAND_SUM);
+		  enum machine_mode mode_orig
+		    = COMPLEX_SUBMODE (TYPE_MODE (TREE_TYPE (arg)));
+		  enum machine_mode mode_final
+		    = COMPLEX_SUBMODE (GET_MODE (loc));
+		  rtx realpart = gen_realpart (mode_orig, temp);
+		  rtx imagpart = gen_imagpart (mode_orig, temp);
+
+		  realpart = convert_modes (mode_final, mode_orig, realpart,
+					    TREE_UNSIGNED (TREE_TYPE (formal)));
+		  imagpart = convert_modes (mode_final, mode_orig, imagpart,
+					    TREE_UNSIGNED (TREE_TYPE (formal)));
+		  arg_vals[i] = gen_rtx (CONCAT, GET_MODE (loc),
+					 realpart, imagpart);
+		}
+	    }
 	  else
 	    arg_vals[i] = expand_expr (arg, NULL_RTX, mode, EXPAND_SUM);
 	}
@@ -1750,7 +1771,8 @@ expand_inline_function (fndecl, parms, t
       /* If function's value was promoted before return,
 	 avoid machine mode mismatch when we substitute INLINE_TARGET.
 	 But TARGET is what we will return to the caller.  */
-      if (arriving_mode != departing_mode)
+      if (arriving_mode != departing_mode
+	  && ! COMPLEX_MODE_P (arriving_mode))
 	{
 	  /* Avoid creating a paradoxical subreg wider than
 	     BITS_PER_WORD, since that is illegal.  */
@@ -1765,6 +1787,41 @@ expand_inline_function (fndecl, parms, t
 	    }
 	  else
 	    reg_to_map = gen_rtx (SUBREG, arriving_mode, target, 0);
+	}
+      else if (arriving_mode != departing_mode)
+	{
+	  /* Deal with complex return. */
+	  enum machine_mode submode = COMPLEX_SUBMODE (departing_mode);
+	  enum machine_mode promoted_submode
+	    = COMPLEX_SUBMODE (arriving_mode);
+	  rtx realpart;
+	  rtx imagpart;
+	  
+	  /* Avoid creating a paradoxical subreg wider than
+	     BITS_PER_WORD, since that is illegal.  */
+	  if (GET_MODE_BITSIZE (promoted_submode) > BITS_PER_WORD)
+	    {
+	      if (!TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (submode),
+					  GET_MODE_BITSIZE (promoted_submode)))
+		/* Maybe could be handled by using convert_move () ?  */
+		abort ();
+	      reg_to_map = gen_reg_rtx (arriving_mode);
+	      realpart = gen_lowpart (submode,
+				      gen_realpart (promoted_submode,
+						    reg_to_map));
+	      imagpart = gen_lowpart (submode,
+				      gen_imagpart (promoted_submode,
+						    reg_to_map));
+	      target = gen_rtx (CONCAT, departing_mode, realpart, imagpart);
+	    }
+	  else
+	    {
+	      realpart = gen_rtx (SUBREG, promoted_submode,
+				  gen_realpart (submode, target), 0);
+	      imagpart = gen_rtx (SUBREG, promoted_submode,
+				  gen_imagpart (submode, target), 0);
+	      reg_to_map = gen_rtx (CONCAT, arriving_mode, realpart, imagpart);
+	    }
 	}
       else
 	reg_to_map = target;
--- gcc/machmode.h.orig	Mon Aug 11 11:57:11 1997
+++ gcc/machmode.h	Wed Jan  7 11:48:11 1998
@@ -164,6 +164,17 @@ extern enum mode_class mode_class[];
   (GET_MODE_CLASS (MODE) == MODE_FLOAT	\
    || GET_MODE_CLASS (MODE) == MODE_COMPLEX_FLOAT)
 
+/* Nonzero if MODE is a complex mode.  */
+#define COMPLEX_MODE_P(MODE)			\
+  (GET_MODE_CLASS (MODE) == MODE_COMPLEX_INT	\
+   || GET_MODE_CLASS (MODE) == MODE_COMPLEX_FLOAT)
+
+/* Determine the mode for the imaginary and real part of a complex MODE. */
+#define COMPLEX_SUBMODE(MODE) \
+mode_for_size (GET_MODE_UNIT_SIZE(MODE)*BITS_PER_UNIT, \
+		(GET_MODE_CLASS (MODE) == MODE_COMPLEX_INT)? \
+		MODE_INT:MODE_FLOAT, 0)
+
 /* Get the size in bytes of an object of mode MODE.  */
 
 extern int mode_size[];
--- gcc/recog.c.orig	Sat Dec  6 12:21:30 1997
+++ gcc/recog.c	Tue Jan  6 17:27:00 1998
@@ -898,8 +898,7 @@ register_operand (op, mode)
 				REGNO (SUBREG_REG (op)))
 	  && (GET_MODE_SIZE (mode)
 	      != GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))))
-	  && GET_MODE_CLASS (GET_MODE (SUBREG_REG (op))) != MODE_COMPLEX_INT
-	  && GET_MODE_CLASS (GET_MODE (SUBREG_REG (op))) != MODE_COMPLEX_FLOAT)
+	  && ! COMPLEX_MODE_P (GET_MODE (SUBREG_REG (op))))
 	return 0;
 #endif
 
--- gcc/regs.h.orig	Sun Nov  2 03:40:47 1997
+++ gcc/regs.h	Tue Jan  6 17:27:01 1998
@@ -27,7 +27,10 @@ Boston, MA 02111-1307, USA.  */
    valid way to get this value.  You cannot get it from the regno.  */
 
 #define REG_SIZE(R) \
-  ((mode_size[(int) GET_MODE (R)] + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+  (GET_MODE_SIZE (GET_MODE (R)) == 0?					\
+   0:(((GET_MODE_UNIT_SIZE (GET_MODE (R)) + (UNITS_PER_WORD - 1))	\
+      / UNITS_PER_WORD) * (GET_MODE_SIZE (GET_MODE (R))			\
+      / GET_MODE_UNIT_SIZE (GET_MODE (R)))))
 
 #ifndef SMALL_REGISTER_CLASSES
 #define SMALL_REGISTER_CLASSES 0
--- gcc/reload.c.orig	Mon Dec 15 12:55:27 1997
+++ gcc/reload.c	Tue Jan  6 23:30:05 1998
@@ -2370,6 +2370,21 @@ find_reloads (insn, replace, ind_levels,
 	  && REGISTER_MOVE_COST (REGNO_REG_CLASS (REGNO (SET_SRC (body))),
 				 REGNO_REG_CLASS (REGNO (SET_DEST (body)))) == 2)
 	return;
+      if (GET_CODE (SET_DEST (body)) == SUBREG
+	  && GET_CODE (SUBREG_REG (SET_DEST (body))) == REG
+	  && COMPLEX_MODE_P (GET_MODE (SUBREG_REG (SET_DEST (body)))))
+	{
+	  rtx complex_dest = SUBREG_REG (SET_DEST (body));
+	  int regno = REGNO (complex_dest) + SUBREG_WORD (SET_DEST (body));
+	  
+	  if (regno < FIRST_PSEUDO_REGISTER
+	      && GET_CODE (SET_SRC (body)) == REG
+	      && REGNO (SET_SRC (body)) < FIRST_PSEUDO_REGISTER
+	      && REGISTER_MOVE_COST (REGNO_REG_CLASS (REGNO (SET_SRC (body))),
+				     REGNO_REG_CLASS (regno)) == 2)
+	    return;
+	}
+      
     case PARALLEL:
     case ASM_OPERANDS:
       reload_n_operands = noperands = asm_noperands (body);
@@ -2768,6 +2783,7 @@ find_reloads (insn, replace, ind_levels,
 		     or which would handle that mode in the wrong number of
 		     registers for subregging to work.  */
 		  || (GET_CODE (operand) == REG
+		      && !COMPLEX_MODE_P (GET_MODE (operand))
 		      && REGNO (operand) < FIRST_PSEUDO_REGISTER
 		      && ((GET_MODE_SIZE (operand_mode[i]) <= UNITS_PER_WORD
 			   && (GET_MODE_SIZE (GET_MODE (operand))
--- gcc/stmt.c.orig	Fri Dec 19 11:41:53 1997
+++ gcc/stmt.c	Wed Jan  7 16:47:02 1998
@@ -2620,19 +2620,59 @@ expand_value_return (val)
 #ifdef PROMOTE_FUNCTION_RETURN
       tree type = TREE_TYPE (DECL_RESULT (current_function_decl));
       int unsignedp = TREE_UNSIGNED (type);
-      enum machine_mode mode
-	= promote_mode (type, DECL_MODE (DECL_RESULT (current_function_decl)),
-			&unsignedp, 1);
+      enum machine_mode mode_orig
+	= DECL_MODE (DECL_RESULT (current_function_decl));
+      enum machine_mode mode;
+      enum machine_mode submode;
+      enum machine_mode promoted_submode;
 
+      if (! COMPLEX_MODE_P (mode_orig))
+	{
+	  mode = promote_mode (type, mode_orig, &unsignedp, 1);
+	}
+      else
+	{
+	  submode = COMPLEX_SUBMODE (mode_orig);
+	  promoted_submode
+	    = promote_mode (type_for_mode (submode, unsignedp),
+			    submode, &unsignedp, 1);
+	  mode = mode_for_size (2 * GET_MODE_SIZE (promoted_submode)
+				* BITS_PER_UNIT,
+				GET_MODE_CLASS (mode_orig), 0);
+	}
+      
       if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
-	convert_move (return_reg, val, unsignedp);
+	{
+	  if (! COMPLEX_MODE_P (mode))
+	    convert_move (return_reg, val, unsignedp);
+	  else
+	    {
+	      convert_move (gen_realpart (promoted_submode, return_reg),
+			    gen_realpart (submode, val), unsignedp);
+	      convert_move (gen_imagpart (promoted_submode, return_reg),
+			    gen_imagpart (submode, val), unsignedp);
+	    }
+	}   
       else
 #endif
 	emit_move_insn (return_reg, val);
     }
   if (GET_CODE (return_reg) == REG
       && REGNO (return_reg) < FIRST_PSEUDO_REGISTER)
-    emit_insn (gen_rtx (USE, VOIDmode, return_reg));
+    {
+      if (! COMPLEX_MODE_P (GET_MODE (return_reg)))
+	emit_insn (gen_rtx (USE, VOIDmode, return_reg));
+      else
+	{
+	  enum machine_mode submode = COMPLEX_SUBMODE (GET_MODE (return_reg));
+	  int n = 2 * HARD_REGNO_NREGS (REGNO (return_reg), submode);
+	  int k;
+
+	  for (k = 0; k < n; k ++)
+	    emit_insn (gen_rtx (USE, VOIDmode,
+			      gen_rtx (REG, submode, REGNO (return_reg) + k)));
+	}
+    }
   /* Handle calls that return values in multiple non-contiguous locations.
      The Irix 6 ABI has examples of this.  */
   else if (GET_CODE (return_reg) == PARALLEL)



             reply	other threads:[~1998-02-10  3:34 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1998-02-10  3:34 Weiwen Liu [this message]
  -- strict thread matches above, loose matches on Subject: below --
1998-01-08  6:05 Weiwen Liu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Pine.OSF.3.96.980107191012.10607C-100000@ylws39 \
    --to=weiwen.liu@yale.edu \
    --cc=egcs@cygnus.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).