rs6000.c (rs6000_mixed_function_arg): Rewrite.
authorAlan Modra <amodra@bigpond.net.au>
Thu, 1 Jul 2004 03:17:31 +0000 (03:17 +0000)
committerAlan Modra <amodra@gcc.gnu.org>
Thu, 1 Jul 2004 03:17:31 +0000 (12:47 +0930)
* config/rs6000/rs6000.c (rs6000_mixed_function_arg): Rewrite.
(function_arg): Use rs6000_arg_size rather than CLASS_MAX_NREGS in
calculating gpr size for altivec.  Simplify and correct
rs6000_mixed_function_arg calls.  Call rs6000_mixed_function_arg
for ABI_V4 gpr case too.  Fix off-by-one error in long double
reg test.  Generate the correct PARALLEL to handle long double
for ABI_AIX 32-bit.  Use this for -m32 -mpowerpc64 fpr case too.
(function_arg_partial_nregs): Align before calculating regs left.
Don't return info on partial fprs when we need info on gprs.
Correct long double fpr off-by-one error.

From-SVN: r83951

gcc/ChangeLog
gcc/config/rs6000/rs6000.c

index c1a19dda5b4d061e8287ae0fb6f258c34c56617f..31e9c5cc848392b7f7ab545c616ee1208fd82e2f 100644 (file)
@@ -1,3 +1,16 @@
+2004-07-01  Alan Modra  <amodra@bigpond.net.au>
+
+       * config/rs6000/rs6000.c (rs6000_mixed_function_arg): Rewrite.
+       (function_arg): Use rs6000_arg_size rather than CLASS_MAX_NREGS in
+       calculating gpr size for altivec.  Simplify and correct
+       rs6000_mixed_function_arg calls.  Call rs6000_mixed_function_arg
+       for ABI_V4 gpr case too.  Fix off-by-one error in long double
+       reg test.  Generate the correct PARALLEL to handle long double
+       for ABI_AIX 32-bit.  Use this for -m32 -mpowerpc64 fpr case too.
+       (function_arg_partial_nregs): Align before calculating regs left.
+       Don't return info on partial fprs when we need info on gprs.
+       Correct long double fpr off-by-one error.
+
 2004-06-30  John David Anglin  <dave.anglin@nrc-cnrc.gc.ca>
 
        * pa-protos.h (prefetch_operand): Add prototype.
index 3102e54569a13464316ef8c791ece8bbc18d2806..5b7cd3f1723bc7c373cb921585a6503d0ff7018e 100644 (file)
@@ -425,8 +425,7 @@ static int rs6000_get_some_local_dynamic_name_1 (rtx *, void *);
 static rtx rs6000_complex_function_value (enum machine_mode);
 static rtx rs6000_spe_function_arg (CUMULATIVE_ARGS *,
                                    enum machine_mode, tree);
-static rtx rs6000_mixed_function_arg (CUMULATIVE_ARGS *,
-                                     enum machine_mode, tree, int);
+static rtx rs6000_mixed_function_arg (enum machine_mode, tree, int);
 static void rs6000_move_block_from_reg (int regno, rtx x, int nregs);
 static void setup_incoming_varargs (CUMULATIVE_ARGS *,
                                    enum machine_mode, tree,
@@ -4424,124 +4423,49 @@ rs6000_spe_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 /* Determine where to place an argument in 64-bit mode with 32-bit ABI.  */
 
 static rtx
-rs6000_mixed_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, 
-                          tree type, int align_words)
-{
-  if (mode == DFmode)
-    {
-      /* -mpowerpc64 with 32bit ABI splits up a DFmode argument
-        in vararg list into zero, one or two GPRs */
-      if (align_words >= GP_ARG_NUM_REG)
-       return gen_rtx_PARALLEL (DFmode,
-                gen_rtvec (2,
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              NULL_RTX, const0_rtx), 
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_REG (mode,
-                                                           cum->fregno),
-                                              const0_rtx)));
-      else if (align_words + rs6000_arg_size (mode, type)
-              > GP_ARG_NUM_REG)
-       /* If this is partially on the stack, then we only
-          include the portion actually in registers here.  */
-       return gen_rtx_PARALLEL (DFmode,
-                gen_rtvec (2,   
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_REG (SImode,
-                                                           GP_ARG_MIN_REG
-                                                           + align_words),
-                                              const0_rtx),
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_REG (mode,
-                                                           cum->fregno),
-                                              const0_rtx)));
-
-      /* split a DFmode arg into two GPRs */
-      return gen_rtx_PARALLEL (DFmode,
-              gen_rtvec (3,
-                         gen_rtx_EXPR_LIST (VOIDmode,       
-                                            gen_rtx_REG (SImode,
-                                                         GP_ARG_MIN_REG
-                                                         + align_words),
-                                            const0_rtx),
-                         gen_rtx_EXPR_LIST (VOIDmode,
-                                            gen_rtx_REG (SImode,
-                                                         GP_ARG_MIN_REG
-                                                         + align_words + 1),
-                                            GEN_INT (4)),
-                         gen_rtx_EXPR_LIST (VOIDmode,
-                                            gen_rtx_REG (mode, cum->fregno),
-                                            const0_rtx)));
-    }
-  /* -mpowerpc64 with 32bit ABI splits up a DImode argument into one
-     or two GPRs */
-  else if (mode == DImode)
+rs6000_mixed_function_arg (enum machine_mode mode, tree type, int align_words)
+{
+  int n_units;
+  int i, k;
+  rtx rvec[GP_ARG_NUM_REG + 1];
+
+  if (align_words >= GP_ARG_NUM_REG)
+    return NULL_RTX;
+
+  n_units = rs6000_arg_size (mode, type);
+
+  /* Optimize the simple case where the arg fits in one gpr, except in
+     the case of BLKmode due to assign_parms assuming that registers are
+     BITS_PER_WORD wide.  */
+  if (n_units == 0
+      || (n_units == 1 && mode != BLKmode))
+    return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
+
+  k = 0;
+  if (align_words + n_units > GP_ARG_NUM_REG)
+    /* Not all of the arg fits in gprs.  Say that it goes in memory too,
+       using a magic NULL_RTX component.
+       FIXME: This is not strictly correct.  Only some of the arg
+       belongs in memory, not all of it.  However, there isn't any way
+       to do this currently, apart from building rtx descriptions for
+       the pieces of memory we want stored.  Due to bugs in the generic
+       code we can't use the normal function_arg_partial_nregs scheme
+       with the PARALLEL arg description we emit here.
+       In any case, the code to store the whole arg to memory is often
+       more efficient than code to store pieces, and we know that space
+       is available in the right place for the whole arg.  */
+    rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
+
+  i = 0;
+  do
     {
-      if (align_words < GP_ARG_NUM_REG - 1)
-       return gen_rtx_PARALLEL (DImode,
-                gen_rtvec (2,
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_REG (SImode,
-                                                           GP_ARG_MIN_REG
-                                                           + align_words),
-                                              const0_rtx),
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_REG (SImode,
-                                                           GP_ARG_MIN_REG
-                                                           + align_words + 1),
-                                              GEN_INT (4))));
-      else if (align_words == GP_ARG_NUM_REG - 1)
-         return gen_rtx_PARALLEL (DImode,
-                  gen_rtvec (2,
-                             gen_rtx_EXPR_LIST (VOIDmode,
-                                                NULL_RTX, const0_rtx),
-                             gen_rtx_EXPR_LIST (VOIDmode,
-                                                gen_rtx_REG (SImode,
-                                                             GP_ARG_MIN_REG
-                                                             + align_words),
-                                                const0_rtx)));
-    }
-  else if (ALTIVEC_VECTOR_MODE (mode) && align_words == GP_ARG_NUM_REG - 2)
-    {
-      /* Varargs vector regs must be saved in R9-R10.  */
-      return gen_rtx_PARALLEL (mode,
-                              gen_rtvec (3,
-                                gen_rtx_EXPR_LIST (VOIDmode,
-                                                    NULL_RTX, const0_rtx),
-                                gen_rtx_EXPR_LIST (VOIDmode,
-                                                   gen_rtx_REG (SImode,
-                                                                GP_ARG_MIN_REG
-                                                                + align_words),
-                                                   const0_rtx),
-                                gen_rtx_EXPR_LIST (VOIDmode,
-                                                   gen_rtx_REG (SImode,
-                                                                GP_ARG_MIN_REG
-                                                                + align_words + 1),
-                                                   GEN_INT (4))));
-    }
-  else if ((mode == BLKmode || ALTIVEC_VECTOR_MODE (mode))
-           && align_words <= (GP_ARG_NUM_REG - 1))
-    {
-      /* AltiVec vector regs are saved in R5-R8. */
-      int k;
-      int size = int_size_in_bytes (type);
-      int no_units = ((size - 1) / 4) + 1;
-      int max_no_words = GP_ARG_NUM_REG - align_words;
-      int rtlvec_len = no_units < max_no_words ? no_units : max_no_words;
-      rtx *rtlvec = (rtx *) alloca (rtlvec_len * sizeof (rtx));
-
-      memset ((char *) rtlvec, 0, rtlvec_len * sizeof (rtx));
-
-      for (k=0; k < rtlvec_len; k++)
-       rtlvec[k] = gen_rtx_EXPR_LIST (VOIDmode,
-                                      gen_rtx_REG (SImode,
-                                                   GP_ARG_MIN_REG
-                                                   + align_words + k),
-                                      k == 0 ? const0_rtx : GEN_INT (k*4));
-
-      return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rtlvec));
+      rtx r = gen_rtx_REG (SImode, GP_ARG_MIN_REG + align_words);
+      rtx off = GEN_INT (i++ * 4);
+      rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off);
     }
-  return NULL_RTX;
+  while (++align_words < GP_ARG_NUM_REG && --n_units != 0);
+
+  return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rvec));
 }
 
 /* Determine where to put an argument to a function.
@@ -4636,8 +4560,8 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
        {
          /* Vector parameters to varargs functions under AIX or Darwin
             get passed in memory and possibly also in GPRs.  */
-         int align, align_words;
-         enum machine_mode part_mode = mode;
+         int align, align_words, n_words;
+         enum machine_mode part_mode;
 
          /* Vector parameters must be 16-byte aligned.  This places them at
             2 mod 4 in terms of words in 32-bit mode, since the parameter
@@ -4653,20 +4577,20 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
          /* Out of registers?  Memory, then.  */
          if (align_words >= GP_ARG_NUM_REG)
            return NULL_RTX;
-         
+
+         if (TARGET_32BIT && TARGET_POWERPC64)
+           return rs6000_mixed_function_arg (mode, type, align_words);
+
          /* The vector value goes in GPRs.  Only the part of the
             value in GPRs is reported here.  */
-         if (align_words + CLASS_MAX_NREGS (mode, GENERAL_REGS)
-             > GP_ARG_NUM_REG)
+         part_mode = mode;
+         n_words = rs6000_arg_size (mode, type);
+         if (align_words + n_words > GP_ARG_NUM_REG)
            /* Fortunately, there are only two possibilities, the value
               is either wholly in GPRs or half in GPRs and half not.  */
            part_mode = DImode;
-         
-         if (TARGET_32BIT
-             && (TARGET_POWERPC64 || (align_words == GP_ARG_NUM_REG - 2)))
-           return rs6000_mixed_function_arg (cum, part_mode, type, align_words);
-         else
-           return gen_rtx_REG (part_mode, GP_ARG_MIN_REG + align_words);
+
+         return gen_rtx_REG (part_mode, GP_ARG_MIN_REG + align_words);
        }
     }
   else if (TARGET_SPE_ABI && TARGET_SPE && SPE_VECTOR_MODE (mode))
@@ -4693,10 +4617,13 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
            gregno += (1 - gregno) & 1;
 
          /* Multi-reg args are not split between registers and stack.  */
-         if (gregno + n_words - 1 <= GP_ARG_MAX_REG)
-           return gen_rtx_REG (mode, gregno);
-         else
+         if (gregno + n_words - 1 > GP_ARG_MAX_REG)
            return NULL_RTX;
+
+         if (TARGET_32BIT && TARGET_POWERPC64)
+           return rs6000_mixed_function_arg (mode, type,
+                                             gregno - GP_ARG_MIN_REG);
+         return gen_rtx_REG (mode, gregno);
        }
     }
   else
@@ -4706,25 +4633,23 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 
       if (USE_FP_FOR_ARG_P (cum, mode, type))
        {
-         rtx fpr[2];
-         rtx *r;
+         rtx rvec[GP_ARG_NUM_REG + 1];
+         rtx r;
+         int k;
          bool needs_psave;
          enum machine_mode fmode = mode;
-         int n;
          unsigned long n_fpreg = (GET_MODE_SIZE (mode) + 7) >> 3;
 
          if (cum->fregno + n_fpreg > FP_ARG_MAX_REG + 1)
            {
-             /* Long double split over regs and memory.  */
-             if (fmode == TFmode)
-               fmode = DFmode;
-
              /* Currently, we only ever need one reg here because complex
                 doubles are split.  */
-             if (cum->fregno != FP_ARG_MAX_REG - 1)
+             if (cum->fregno != FP_ARG_MAX_REG || fmode != TFmode)
                abort ();
+
+             /* Long double split over regs and memory.  */
+             fmode = DFmode;
            }
-         fpr[1] = gen_rtx_REG (fmode, cum->fregno);
 
          /* Do we also need to pass this arg in the parameter save
             area?  */
@@ -4735,46 +4660,55 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
                                 && align_words >= GP_ARG_NUM_REG)));
 
          if (!needs_psave && mode == fmode)
-           return fpr[1];
-
-          if (TARGET_32BIT && TARGET_POWERPC64
-              && mode == DFmode && cum->stdarg)
-            return rs6000_mixed_function_arg (cum, mode, type, align_words);
-
-         /* Describe where this piece goes.  */
-         r = fpr + 1;
-         *r = gen_rtx_EXPR_LIST (VOIDmode, *r, const0_rtx);
-         n = 1;
+           return gen_rtx_REG (fmode, cum->fregno);
 
+         k = 0;
          if (needs_psave)
            {
-             /* Now describe the part that goes in gprs or the stack.
+             /* Describe the part that goes in gprs or the stack.
                 This piece must come first, before the fprs.  */
-             rtx reg = NULL_RTX;
              if (align_words < GP_ARG_NUM_REG)
                {
                  unsigned long n_words = rs6000_arg_size (mode, type);
-                 enum machine_mode rmode = mode;
-
-                 if (align_words + n_words > GP_ARG_NUM_REG)
-                   /* If this is partially on the stack, then we only
-                      include the portion actually in registers here.
-                      We know this can only be one register because
-                      complex doubles are splt.  */
-                   rmode = Pmode;
-                 reg = gen_rtx_REG (rmode, GP_ARG_MIN_REG + align_words);
+
+                 if (align_words + n_words > GP_ARG_NUM_REG
+                     || (TARGET_32BIT && TARGET_POWERPC64))
+                   {
+                     /* If this is partially on the stack, then we only
+                        include the portion actually in registers here.  */
+                     enum machine_mode rmode = TARGET_32BIT ? SImode : DImode;
+                     rtx off;
+                     do
+                       {
+                         r = gen_rtx_REG (rmode,
+                                          GP_ARG_MIN_REG + align_words);
+                         off = GEN_INT (k * GET_MODE_SIZE (rmode));
+                         rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off);
+                       }
+                     while (++align_words < GP_ARG_NUM_REG && --n_words != 0);
+                   }
+                 else
+                   {
+                     /* The whole arg fits in gprs.  */
+                     r = gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
+                     rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, const0_rtx);
+                   }
                }
-             *--r = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx);
-             ++n;
+             else
+               /* It's entirely in memory.  */
+               rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
            }
 
-         return gen_rtx_PARALLEL (mode, gen_rtvec_v (n, r));
+         /* Describe where this piece goes in the fprs.  */
+         r = gen_rtx_REG (fmode, cum->fregno);
+         rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, const0_rtx);
+
+         return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rvec));
        }
       else if (align_words < GP_ARG_NUM_REG)
        {
-         if (TARGET_32BIT && TARGET_POWERPC64
-             && (mode == DImode || mode == BLKmode))
-           return rs6000_mixed_function_arg (cum, mode, type, align_words);
+         if (TARGET_32BIT && TARGET_POWERPC64)
+           return rs6000_mixed_function_arg (mode, type, align_words);
 
          return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
        }
@@ -4783,15 +4717,20 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
     }
 }
 \f
-/* For an arg passed partly in registers and partly in memory,
-   this is the number of registers used.
-   For args passed entirely in registers or entirely in memory, zero.  */
+/* For an arg passed partly in registers and partly in memory, this is
+   the number of registers used.  For args passed entirely in registers
+   or entirely in memory, zero.  When an arg is described by a PARALLEL,
+   perhaps using more than one register type, this function returns the
+   number of registers used by the first element of the PARALLEL.  */
 
 int
 function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode, 
                            tree type, int named)
 {
   int ret = 0;
+  int align;
+  int parm_offset;
+  int align_words;
 
   if (DEFAULT_ABI == ABI_V4)
     return 0;
@@ -4800,17 +4739,29 @@ function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       && cum->nargs_prototype >= 0)
     return 0;
 
-  if (USE_FP_FOR_ARG_P (cum, mode, type))
+  align = function_arg_boundary (mode, type) / PARM_BOUNDARY - 1;
+  parm_offset = TARGET_32BIT ? 2 : 0;
+  align_words = cum->words + ((parm_offset - cum->words) & align);
+
+  if (USE_FP_FOR_ARG_P (cum, mode, type)
+      /* If we are passing this arg in gprs as well, then this function
+        should return the number of gprs (or memory) partially passed,
+        *not* the number of fprs.  */
+      && !(type
+          && (cum->nargs_prototype <= 0
+              || (DEFAULT_ABI == ABI_AIX
+                  && TARGET_XL_CALL
+                  && align_words >= GP_ARG_NUM_REG))))
     {
       if (cum->fregno + ((GET_MODE_SIZE (mode) + 7) >> 3) > FP_ARG_MAX_REG + 1)
-       ret = FP_ARG_MAX_REG - cum->fregno;
+       ret = FP_ARG_MAX_REG + 1 - cum->fregno;
       else if (cum->nargs_prototype >= 0)
        return 0;
     }
 
-  if (cum->words < GP_ARG_NUM_REG
-      && GP_ARG_NUM_REG < cum->words + rs6000_arg_size (mode, type))
-    ret = GP_ARG_NUM_REG - cum->words;
+  if (align_words < GP_ARG_NUM_REG
+      && GP_ARG_NUM_REG < align_words + rs6000_arg_size (mode, type))
+    ret = GP_ARG_NUM_REG - align_words;
 
   if (ret != 0 && TARGET_DEBUG_ARG)
     fprintf (stderr, "function_arg_partial_nregs: %d\n", ret);