Rewrite of 64-bit Darwin structure-by-value pass/return.
authorDale Johannesen <dalej@apple.com>
Tue, 1 Mar 2005 02:56:35 +0000 (02:56 +0000)
committerStan Shebs <shebs@gcc.gnu.org>
Tue, 1 Mar 2005 02:56:35 +0000 (02:56 +0000)
2005-02-28  Dale Johannesen  <dalej@apple.com>
            Stan Shebs  <shebs@apple.com>

        Rewrite of 64-bit Darwin structure-by-value pass/return.
        * config/rs6000/rs6000.h (CUMULATIVE_ARGS): New fields
        intoffset, use_stack, named.
        * config/rs6000/rs6000.c (rs6000_darwin64_function_arg):  Remove.
        (rs6000_darwin64_record_arg_advance_flush):  New.
        (rs6000_darwin64_record_arg_advance_recurse): New.
        (rs6000_darwin64_record_arg_flush):  New.
        (rs6000_darwin64_record_arg_recurse):  New.
        (rs6000_darwin64_record_arg):  New.
        (rs6000_return_in_memory):  Remove AGGREGATE_TYPE_P check.
        (function_arg_boundary):  Handle 128-bit aligned structs.
        (function_arg_advance):  Rewrite darwin64 struct handling.
        (function_arg):  Call rs6000_darwin64_record_arg.
        (function_arg_partial_nregs):  Handle darwin64 structs.
        (rs6000_darwin64_function_value):  Remove.
        (rs6000_function_value):  Call rs6000_darwin64_record_arg.

Co-Authored-By: Stan Shebs <shebs@apple.com>
From-SVN: r95723

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

index 7eed83204604f5f532742dec31765be8690d7c31..18a3070fff6bd982ab1e3b09f889b0e458362ec9 100644 (file)
@@ -1,3 +1,23 @@
+2005-02-28  Dale Johannesen  <dalej@apple.com>
+           Stan Shebs  <shebs@apple.com>
+
+       Rewrite of 64-bit Darwin structure-by-value pass/return.
+       * config/rs6000/rs6000.h (CUMULATIVE_ARGS): New fields
+       intoffset, use_stack, named.
+       * config/rs6000/rs6000.c (rs6000_darwin64_function_arg):  Remove.
+       (rs6000_darwin64_record_arg_advance_flush):  New.
+       (rs6000_darwin64_record_arg_advance_recurse): New.
+       (rs6000_darwin64_record_arg_flush):  New.
+       (rs6000_darwin64_record_arg_recurse):  New.
+       (rs6000_darwin64_record_arg):  New.
+       (rs6000_return_in_memory):  Remove AGGREGATE_TYPE_P check.
+       (function_arg_boundary):  Handle 128-bit aligned structs.
+       (function_arg_advance):  Rewrite darwin64 struct handling.
+       (function_arg):  Call rs6000_darwin64_record_arg.
+       (function_arg_partial_nregs):  Handle darwin64 structs.
+       (rs6000_darwin64_function_value):  Remove.
+       (rs6000_function_value):  Call rs6000_darwin64_record_arg.
+
 2005-02-28  Roger Sayle  <roger@eyesopen.com>
 
        PR tree-optimization/20216
index f0b7c185b4422fef3402057623412ac7d372c7a6..ff6d09d3ba03735c94b559081ad481c245d1c34e 100644 (file)
@@ -737,8 +737,17 @@ 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_darwin64_function_arg (CUMULATIVE_ARGS *,
-                                        enum machine_mode, tree, int);
+static void rs6000_darwin64_record_arg_advance_flush (CUMULATIVE_ARGS *,
+                                                     HOST_WIDE_INT);
+static void rs6000_darwin64_record_arg_advance_recurse (CUMULATIVE_ARGS *,
+                                                       tree, HOST_WIDE_INT);
+static void rs6000_darwin64_record_arg_flush (CUMULATIVE_ARGS *,
+                                             HOST_WIDE_INT,
+                                             rtx[], int *);
+static void rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *,
+                                              tree, HOST_WIDE_INT,
+                                              rtx[], int *);
+static rtx rs6000_darwin64_record_arg (CUMULATIVE_ARGS *, tree, int, bool);
 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 *,
@@ -3833,12 +3842,23 @@ rs6000_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
 {
   /* In the darwin64 abi, try to use registers for larger structs
      if possible.  */
-  if (AGGREGATE_TYPE_P (type)
-      && rs6000_darwin64_abi
+  if (rs6000_darwin64_abi
       && TREE_CODE (type) == RECORD_TYPE
-      && ((unsigned HOST_WIDE_INT) int_size_in_bytes (type) <= 32)
-      && ((unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 0))
-    return false;
+      && int_size_in_bytes (type) > 0)
+    {
+      CUMULATIVE_ARGS valcum;
+      rtx valret;
+
+      valcum.words = 0;
+      valcum.fregno = FP_ARG_MIN_REG;
+      valcum.vregno = ALTIVEC_ARG_MIN_REG;
+      /* Do a trial code generation as if this were going to be passed
+        as an argument; if any part goes in memory, we return NULL.  */
+      valret = rs6000_darwin64_record_arg (&valcum, type, 1, true);
+      if (valret)
+       return false;
+      /* Otherwise fall through to more conventional ABI rules.  */
+    }
 
   if (AGGREGATE_TYPE_P (type)
       && (TARGET_AIX_STRUCT_RET
@@ -4029,6 +4049,9 @@ function_arg_boundary (enum machine_mode mode, tree type)
           || (type && TREE_CODE (type) == VECTOR_TYPE
               && int_size_in_bytes (type) >= 16))
     return 128;
+  else if (rs6000_darwin64_abi && mode == BLKmode
+          && type && TYPE_ALIGN (type) > 64)
+    return 128;
   else
     return PARM_BOUNDARY;
 }
@@ -4051,46 +4074,84 @@ rs6000_arg_size (enum machine_mode mode, tree type)
     return (size + 7) >> 3;
 }
 \f
-/* The darwin64 ABI calls for us to recurse down through structs,
-   applying the same rules to struct elements as if a reference to
-   each were being passed directly.  */
+/* Use this to flush pending int fields.  */
 
 static void
-darwin64_function_arg_advance (CUMULATIVE_ARGS *cum, tree type,
-                              int named, int depth)
+rs6000_darwin64_record_arg_advance_flush (CUMULATIVE_ARGS *cum,
+                                         HOST_WIDE_INT bitpos)
 {
-  tree f, ftype;
-  int i, tot;
+  unsigned int startbit, endbit;
+  int intregs, intoffset;
+  enum machine_mode mode;
 
-  switch (TREE_CODE (type))
-    {
-    case RECORD_TYPE:
-      for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f))
-       if (TREE_CODE (f) == FIELD_DECL)
-         {
-           ftype = TREE_TYPE (f);
-           function_arg_advance (cum, TYPE_MODE (ftype), ftype,
-                                 named, depth + 1);
-         }
-      break;
+  if (cum->intoffset == -1)
+    return;
 
-    case ARRAY_TYPE:
-      tot = int_size_in_bytes (type);
-      if (tot <= 0)
-       return;
-      ftype = TREE_TYPE (type);
-      tot /= int_size_in_bytes (ftype);
-      
-      for (i = 0; i < tot; ++i)
+  intoffset = cum->intoffset;
+  cum->intoffset = -1;
+
+  if (intoffset % BITS_PER_WORD != 0)
+    {
+      mode = mode_for_size (BITS_PER_WORD - intoffset % BITS_PER_WORD,
+                           MODE_INT, 0);
+      if (mode == BLKmode)
        {
-         function_arg_advance (cum, TYPE_MODE (ftype), ftype,
-                               named, depth + 1);
+         /* We couldn't find an appropriate mode, which happens,
+            e.g., in packed structs when there are 3 bytes to load.
+            Back intoffset back to the beginning of the word in this
+            case.  */
+         intoffset = intoffset & -BITS_PER_WORD;
        }
-      break;
-
-    default:
-      abort ();
     }
+
+  startbit = intoffset & -BITS_PER_WORD;
+  endbit = (bitpos + BITS_PER_WORD - 1) & -BITS_PER_WORD;
+  intregs = (endbit - startbit) / BITS_PER_WORD;
+  cum->words += intregs;
+}
+
+/* The darwin64 ABI calls for us to recurse down through structs,
+   looking for elements passed in registers.  Unfortunately, we have
+   to track int register count here also because of misalignments
+   in powerpc alignment mode.  */
+
+static void
+rs6000_darwin64_record_arg_advance_recurse (CUMULATIVE_ARGS *cum,
+                                           tree type,
+                                           HOST_WIDE_INT startbitpos)
+{
+  tree f;
+
+  for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f))
+    if (TREE_CODE (f) == FIELD_DECL)
+      {
+       HOST_WIDE_INT bitpos = startbitpos;
+       tree ftype = TREE_TYPE (f);
+       enum machine_mode mode = TYPE_MODE (ftype);
+
+       if (DECL_SIZE (f) != 0
+           && host_integerp (bit_position (f), 1))
+         bitpos += int_bit_position (f);
+
+       /* ??? FIXME: else assume zero offset.  */
+
+       if (TREE_CODE (ftype) == RECORD_TYPE)
+         rs6000_darwin64_record_arg_advance_recurse (cum, ftype, bitpos);
+       else if (USE_FP_FOR_ARG_P (cum, mode, ftype))
+         {
+           rs6000_darwin64_record_arg_advance_flush (cum, bitpos);
+           cum->fregno += (GET_MODE_SIZE (mode) + 7) >> 3;
+           cum->words += (GET_MODE_SIZE (mode) + 7) >> 3;
+         }
+       else if (USE_ALTIVEC_FOR_ARG_P (cum, mode, type, 1))
+         {
+           rs6000_darwin64_record_arg_advance_flush (cum, bitpos);
+           cum->vregno++;
+           cum->words += 2;
+         }
+       else if (cum->intoffset == -1)
+         cum->intoffset = bitpos;
+      }
 }
 
 /* Update the data in CUM to advance over an argument
@@ -4105,6 +4166,8 @@ void
 function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
                      tree type, int named, int depth)
 {
+  int size;
+
   /* Only tick off an argument if we're not recursing.  */
   if (depth == 0)
     cum->nargs_prototype--;
@@ -4168,10 +4231,30 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 
   else if (rs6000_darwin64_abi
           && mode == BLKmode
-          && (TREE_CODE (type) == RECORD_TYPE
-              || TREE_CODE (type) == ARRAY_TYPE))
-    darwin64_function_arg_advance (cum, type, named, depth);
-
+          && TREE_CODE (type) == RECORD_TYPE
+          && (size = int_size_in_bytes (type)) > 0)
+    {
+      /* Variable sized types have size == -1 and are
+        treated as if consisting entirely of ints.
+        Pad to 16 byte boundary if needed.  */
+      if (TYPE_ALIGN (type) >= 2 * BITS_PER_WORD
+         && (cum->words % 2) != 0)
+       cum->words++;
+      /* For varargs, we can just go up by the size of the struct. */
+      if (!named)
+       cum->words += (size + 7) / 8;
+      else
+       {
+         /* It is tempting to say int register count just goes up by
+            sizeof(type)/8, but this is wrong in a case such as
+            { int; double; int; } [powerpc alignment].  We have to
+            grovel through the fields for these too.  */
+         cum->intoffset = 0;
+         rs6000_darwin64_record_arg_advance_recurse (cum, type, 0);
+         rs6000_darwin64_record_arg_advance_flush (cum, 
+                                                   size * BITS_PER_UNIT);
+       }
+    }
   else if (DEFAULT_ABI == ABI_V4)
     {
       if (TARGET_HARD_FLOAT && TARGET_FPRS
@@ -4330,136 +4413,184 @@ rs6000_spe_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
     }
 }
 
-/* For the darwin64 ABI, we want to construct a PARALLEL consisting of
-   the register(s) to be used for each field and subfield of a struct
-   being passed by value, along with the offset of where the
-   register's value may be found in the block.  */
+/* A subroutine of rs6000_darwin64_record_arg.  Assign the bits of the
+   structure between cum->intoffset and bitpos to integer registers.  */
 
-static rtx
-rs6000_darwin64_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
-                             tree type, int named)
+static void
+rs6000_darwin64_record_arg_flush (CUMULATIVE_ARGS *cum, 
+                                 HOST_WIDE_INT bitpos, rtx rvec[], int *k)
 {
-  tree f, ftype, offset;
-  rtx rvec[FIRST_PSEUDO_REGISTER], sub, suboff, roffset;
-  int k = 0, i, j, bytepos, subbytepos, tot;
-  CUMULATIVE_ARGS saved_cum = *cum;
-  enum machine_mode submode;
+  enum machine_mode mode;
+  unsigned int regno;
+  unsigned int startbit, endbit;
+  int this_regno, intregs, intoffset;
+  rtx reg;
 
-  switch (TREE_CODE (type))
+  if (cum->intoffset == -1)
+    return;
+
+  intoffset = cum->intoffset;
+  cum->intoffset = -1;
+
+  /* If this is the trailing part of a word, try to only load that
+     much into the register.  Otherwise load the whole register.  Note
+     that in the latter case we may pick up unwanted bits.  It's not a
+     problem at the moment but may wish to revisit.  */
+
+  if (intoffset % BITS_PER_WORD != 0)
     {
-    case RECORD_TYPE:
-      for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f))
-       if (TREE_CODE (f) == FIELD_DECL)
+      mode = mode_for_size (BITS_PER_WORD - intoffset % BITS_PER_WORD,
+                         MODE_INT, 0);
+      if (mode == BLKmode)
+       {
+         /* We couldn't find an appropriate mode, which happens,
+            e.g., in packed structs when there are 3 bytes to load.
+            Back intoffset back to the beginning of the word in this
+            case.  */
+        intoffset = intoffset & -BITS_PER_WORD;
+        mode = word_mode;
+       }
+    }
+  else
+    mode = word_mode;
+
+  startbit = intoffset & -BITS_PER_WORD;
+  endbit = (bitpos + BITS_PER_WORD - 1) & -BITS_PER_WORD;
+  intregs = (endbit - startbit) / BITS_PER_WORD;
+  this_regno = cum->words + intoffset / BITS_PER_WORD;
+
+  if (intregs > 0 && intregs > GP_ARG_NUM_REG - this_regno)
+    cum->use_stack = 1;
+    
+  intregs = MIN (intregs, GP_ARG_NUM_REG - this_regno);
+  if (intregs <= 0)
+    return;
+
+  intoffset /= BITS_PER_UNIT;
+  do
+    {
+      regno = GP_ARG_MIN_REG + this_regno;
+      reg = gen_rtx_REG (mode, regno);
+      rvec[(*k)++] =
+       gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (intoffset));
+
+      this_regno += 1;
+      intoffset = (intoffset | (UNITS_PER_WORD-1)) + 1;
+      mode = word_mode;
+      intregs -= 1;
+    }
+  while (intregs > 0);
+}
+
+/* Recursive workhorse for the following.  */
+
+static void
+rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *cum, tree type, 
+                                   HOST_WIDE_INT startbitpos, rtx rvec[],
+                                   int *k)
+{
+  tree f;
+
+  for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f))
+    if (TREE_CODE (f) == FIELD_DECL)
+      {
+       HOST_WIDE_INT bitpos = startbitpos;
+       tree ftype = TREE_TYPE (f);
+       enum machine_mode mode = TYPE_MODE (ftype);
+
+       if (DECL_SIZE (f) != 0
+           && host_integerp (bit_position (f), 1))
+         bitpos += int_bit_position (f);
+
+       /* ??? FIXME: else assume zero offset.  */
+
+       if (TREE_CODE (ftype) == RECORD_TYPE)
+         rs6000_darwin64_record_arg_recurse (cum, ftype, bitpos, rvec, k);
+       else if (cum->named && USE_FP_FOR_ARG_P (cum, mode, ftype))
          {
-           ftype = TREE_TYPE (f);
-           offset = DECL_FIELD_OFFSET (f);
-           bytepos = int_bit_position (f) / BITS_PER_UNIT;
-           /* Force substructs to be handled as BLKmode even if
-              they're small enough to be recorded as DImode, so we
-              drill through to non-record fields.  */
-           submode = TYPE_MODE (ftype);
-           if (TREE_CODE (ftype) == RECORD_TYPE)
-             submode = BLKmode;
-           sub = function_arg (cum, submode, ftype, named);
-           if (sub == NULL_RTX)
-             return NULL_RTX;
-           if (GET_CODE (sub) == PARALLEL)
-             {
-               for (i = 0; i < XVECLEN (sub, 0); i++)
-                 {
-                   rtx subsub = XVECEXP (sub, 0, i);
-                   suboff = XEXP (subsub, 1);
-                   subbytepos = INTVAL (suboff);
-                   subbytepos += bytepos;
-                   roffset = gen_rtx_CONST_INT (SImode, subbytepos);
-                   subsub = XEXP (subsub, 0);
-                   rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, subsub, roffset);
-                 }
-             }
-           else
+#if 0
+           switch (mode)
              {
-               roffset = gen_rtx_CONST_INT (SImode, bytepos);
-               rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, sub, roffset);
+             case SCmode: mode = SFmode; break;
+             case DCmode: mode = DFmode; break;
+             case TCmode: mode = TFmode; break;
+             default: break;
              }
-           /* Now do an arg advance to get all the cumulative arg
-              stuff set correctly for the next subfield. Note that it
-              has no lasting effect, because it is being done on a
-              temporary copy of the cumulative arg data.  */
-           function_arg_advance (cum, submode, ftype, named, 1);
+#endif
+           rs6000_darwin64_record_arg_flush (cum, bitpos, rvec, k);
+           rvec[(*k)++]
+             = gen_rtx_EXPR_LIST (VOIDmode, 
+                                  gen_rtx_REG (mode, cum->fregno++),
+                                  GEN_INT (bitpos / BITS_PER_UNIT));
+           if (mode == TFmode)
+             cum->fregno++;
          }
-      break;
+       else if (cum->named && USE_ALTIVEC_FOR_ARG_P (cum, mode, ftype, 1))
+         {
+           rs6000_darwin64_record_arg_flush (cum, bitpos, rvec, k);
+           rvec[(*k)++]
+             = gen_rtx_EXPR_LIST (VOIDmode, 
+                                  gen_rtx_REG (mode, cum->vregno++), 
+                                  GEN_INT (bitpos / BITS_PER_UNIT));
+         }
+       else if (cum->intoffset == -1)
+         cum->intoffset = bitpos;
+      }
+}
 
-    case UNION_TYPE:
-      tot = rs6000_arg_size (mode, type);
-      if (tot <= 0)
-       return NULL_RTX;
-      bytepos = 0;
+/* For the darwin64 ABI, we want to construct a PARALLEL consisting of
+   the register(s) to be used for each field and subfield of a struct
+   being passed by value, along with the offset of where the
+   register's value may be found in the block.  FP fields go in FP
+   register, vector fields go in vector registers, and everything
+   else goes in int registers, packed as in memory.  
 
-      for (j = 0; j < tot; ++j)
-       {
-         sub = gen_rtx_REG ((TARGET_64BIT ? DImode : SImode), GP_ARG_MIN_REG + cum->words++);
-         roffset = gen_rtx_CONST_INT (SImode, bytepos);
-         rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, sub, roffset);
-         if (cum->words >= GP_ARG_NUM_REG)
-           break;
-         bytepos += (TARGET_64BIT ? 8 : 4);
-       }
-      break;
+   This code is also used for function return values.  RETVAL indicates
+   whether this is the case.
 
-    case ARRAY_TYPE:
-      tot = int_size_in_bytes (type);
-      if (tot <= 0)
-       return NULL_RTX;
-      ftype = TREE_TYPE (type);
-      tot /= int_size_in_bytes (ftype);
-      bytepos = 0;
+   Much of this is taken from the Sparc V9 port, which has a similar
+   calling convention.  */
 
-      for (j = 0; j < tot; ++j)
-       {
-         /* Force substructs to be handled as BLKmode even if
-            they're small enough to be recorded as DImode, so we
-            drill through to non-record fields.  */
-         submode = TYPE_MODE (ftype);
-         if (TREE_CODE (ftype) == RECORD_TYPE)
-           submode = BLKmode;
-         sub = function_arg (cum, submode, ftype, named);
-         if (sub == NULL_RTX)
-           return NULL_RTX;
-         if (GET_CODE (sub) == PARALLEL)
-           {
-             for (i = 0; i < XVECLEN (sub, 0); i++)
-               {
-                 rtx subsub = XVECEXP (sub, 0, i);
-
-                 suboff = XEXP (subsub, 1);
-                 subbytepos = INTVAL (suboff);
-                 subbytepos += bytepos;
-                 roffset = gen_rtx_CONST_INT (SImode, subbytepos);
-                 subsub = XEXP (subsub, 0);
-                 rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, subsub, roffset);
-               }
-           }
-         else
-           {
-             roffset = gen_rtx_CONST_INT (SImode, bytepos);
-             rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, sub, roffset);
-           }
-           /* Now do an arg advance to get all the cumulative arg
-              stuff set correctly for the next subfield. Note that it
-              has no lasting effect, because it is being done on a
-              temporary copy of the cumulative arg data.  */
-           function_arg_advance (cum, submode, ftype, named, 1);
-           bytepos += int_size_in_bytes (ftype);
-       }
-      break;
-
-    default:
-      abort ();
-  }
-
-  *cum = saved_cum;
-  if (k > 0)
-    return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rvec));
+static rtx
+rs6000_darwin64_record_arg (CUMULATIVE_ARGS *orig_cum, tree type,
+                           int named, bool retval)
+{
+  rtx rvec[FIRST_PSEUDO_REGISTER];
+  int k = 1, kbase = 1;
+  HOST_WIDE_INT typesize = int_size_in_bytes (type);
+  /* This is a copy; modifications are not visible to our caller.  */
+  CUMULATIVE_ARGS copy_cum = *orig_cum;
+  CUMULATIVE_ARGS *cum = &copy_cum;
+
+  /* Pad to 16 byte boundary if needed.  */
+  if (!retval && TYPE_ALIGN (type) >= 2 * BITS_PER_WORD
+      && (cum->words % 2) != 0)
+    cum->words++;
+
+  cum->intoffset = 0;
+  cum->use_stack = 0;
+  cum->named = named;
+
+  /* Put entries into rvec[] for individual FP and vector fields, and
+     for the chunks of memory that go in int regs.  Note we start at
+     element 1; 0 is reserved for an indication of using memory, and
+     may or may not be filled in below. */
+  rs6000_darwin64_record_arg_recurse (cum, type, 0, rvec, &k);
+  rs6000_darwin64_record_arg_flush (cum, typesize * BITS_PER_UNIT, rvec, &k);
+
+  /* If any part of the struct went on the stack put all of it there.
+     This hack is because the generic code for
+     FUNCTION_ARG_PARTIAL_NREGS cannot handle cases where the register
+     parts of the struct are not at the beginning.  */
+  if (cum->use_stack)
+    {
+      if (retval)
+       return NULL_RTX;    /* doesn't go in registers at all */
+      kbase = 0;
+      rvec[0] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
+    }
+  if (k > 1 || cum->use_stack)
+    return gen_rtx_PARALLEL (BLKmode, gen_rtvec_v (k - kbase, &rvec[kbase]));
   else
     return NULL_RTX;
 }
@@ -4523,7 +4654,8 @@ rs6000_mixed_function_arg (enum machine_mode mode, tree type, int align_words)
     This is null for libcalls where that information may
     not be available.
    CUM is a variable of type CUMULATIVE_ARGS which gives info about
-    the preceding args and about the function being called.
+    the preceding args and about the function being called.  It is
+    not modified in this routine.
    NAMED is nonzero if this argument is a named parameter
     (otherwise it is an extra parameter matching an ellipsis).
 
@@ -4571,13 +4703,10 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       return GEN_INT (cum->call_cookie);
     }
 
-  if (mode == BLKmode
-      && rs6000_darwin64_abi
-      && (TREE_CODE (type) == RECORD_TYPE
-         || TREE_CODE (type) == UNION_TYPE
-         || TREE_CODE (type) == ARRAY_TYPE))
+  if (rs6000_darwin64_abi && mode == BLKmode
+      && TREE_CODE (type) == RECORD_TYPE)
     {
-      rtx rslt = rs6000_darwin64_function_arg (cum, mode, type, named);
+      rtx rslt = rs6000_darwin64_record_arg (cum, type, named, false);
       if (rslt != NULL_RTX)
        return rslt;
       /* Else fall through to usual handling.  */
@@ -4818,6 +4947,12 @@ rs6000_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       && cum->nargs_prototype >= 0)
     return 0;
 
+  /* In this complicated case we just disable the partial_nregs code.  */
+  if (rs6000_darwin64_abi && mode == BLKmode
+      && TREE_CODE (type) == RECORD_TYPE
+      && int_size_in_bytes (type) > 0)
+    return 0;
+
   align = function_arg_boundary (mode, type) / PARM_BOUNDARY - 1;
   parm_offset = TARGET_32BIT ? 2 : 0;
   align_words = cum->words + ((parm_offset - cum->words) & align);
@@ -17147,128 +17282,6 @@ rs6000_complex_function_value (enum machine_mode mode)
   return gen_rtx_PARALLEL (mode, gen_rtvec (2, r1, r2));
 }
 
-/* Compose a PARALLEL for a darwin64 struct being returned by
-   value.  */
-
-static rtx
-rs6000_darwin64_function_value (CUMULATIVE_ARGS *cum, tree valtype)
-{
-  tree f, ftype;
-  rtx rvec[FIRST_PSEUDO_REGISTER], sub, roffset, suboff;
-  int k = 0, bytepos, tot, elt, i, subbytepos;
-  enum machine_mode fmode;
-
-  switch (TREE_CODE (valtype))
-    {
-    case RECORD_TYPE:
-      for (f = TYPE_FIELDS (valtype); f ; f = TREE_CHAIN (f))
-       if (TREE_CODE (f) == FIELD_DECL)
-         {
-           ftype = TREE_TYPE (f);
-           fmode = TYPE_MODE (ftype);
-           bytepos = int_bit_position (f) / BITS_PER_UNIT;
-           if (USE_FP_FOR_ARG_P (cum, fmode, ftype))
-             {
-               sub = gen_rtx_REG (fmode, cum->fregno++);
-               cum->sysv_gregno++;
-             }
-           else if (USE_ALTIVEC_FOR_ARG_P (cum, fmode, ftype, 1))
-             {
-               sub = gen_rtx_REG (fmode, cum->vregno++);
-               cum->sysv_gregno++;
-             }
-           else if (fmode == BLKmode
-                    && (TREE_CODE (ftype) == RECORD_TYPE
-                        || TREE_CODE (ftype) == ARRAY_TYPE))
-             sub = rs6000_darwin64_function_value (cum, ftype);
-           else
-             sub = gen_rtx_REG (fmode, cum->sysv_gregno++);
-           if (sub == NULL_RTX)
-             return sub;
-           else if (GET_CODE (sub) == PARALLEL)
-             {
-               for (i = 0; i < XVECLEN (sub, 0); i++)
-                 {
-                   rtx subsub = XVECEXP (sub, 0, i);
-
-                   suboff = XEXP (subsub, 1);
-                   subbytepos = INTVAL (suboff);
-                   subbytepos += bytepos;
-                   roffset = gen_rtx_CONST_INT (SImode, subbytepos);
-                   subsub = XEXP (subsub, 0);
-                   rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, subsub, roffset);
-                 }
-             }
-           else
-             {
-               roffset = gen_rtx_CONST_INT (SImode, bytepos);
-               rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, sub, roffset);
-             }
-         }
-      if (k > 0)
-       return gen_rtx_PARALLEL (TYPE_MODE (valtype), gen_rtvec_v (k, rvec));
-      else
-       return NULL_RTX;
-
-    case ARRAY_TYPE:
-      /* If passing by value won't work, give up.  */
-      if (int_size_in_bytes (valtype) <= 0)
-       return NULL_RTX;
-      ftype = TREE_TYPE (valtype);
-      fmode = TYPE_MODE (ftype);
-      tot = int_size_in_bytes (valtype) / int_size_in_bytes (ftype);
-      bytepos = 0;
-      for (elt = 0; elt < tot; ++elt)
-       {
-         if (USE_FP_FOR_ARG_P (cum, fmode, ftype))
-           {
-             sub = gen_rtx_REG (fmode, cum->fregno++);
-             cum->sysv_gregno++;
-           }
-         else if (USE_ALTIVEC_FOR_ARG_P (cum, fmode, ftype, 1))
-           {
-             sub = gen_rtx_REG (fmode, cum->vregno++);
-             cum->sysv_gregno++;
-           }
-         else if (fmode == BLKmode
-                  && (TREE_CODE (ftype) == RECORD_TYPE
-                      || TREE_CODE (ftype) == ARRAY_TYPE))
-           sub = rs6000_darwin64_function_value (cum, ftype);
-         else
-           sub = gen_rtx_REG (fmode, cum->sysv_gregno++);
-         if (sub == NULL_RTX)
-           return sub;
-         else if (GET_CODE (sub) == PARALLEL)
-           {
-             for (i = 0; i < XVECLEN (sub, 0); i++)
-               {
-                 rtx subsub = XVECEXP (sub, 0, i);
-
-                 suboff = XEXP (subsub, 1);
-                 subbytepos = INTVAL (suboff);
-                 subbytepos += bytepos;
-                 roffset = gen_rtx_CONST_INT (SImode, subbytepos);
-                 subsub = XEXP (subsub, 0);
-                 rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, subsub, roffset);
-               }
-             }
-           else
-             {
-               roffset = gen_rtx_CONST_INT (SImode, bytepos);
-               rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, sub, roffset);
-             }
-         bytepos += int_size_in_bytes (ftype);
-       }
-      if (k > 0)
-       return gen_rtx_PARALLEL (TYPE_MODE (valtype), gen_rtvec_v (k, rvec));
-      else
-       return NULL_RTX;
-
-    default:
-      abort ();
-    }
-}
-
 /* Define how to find the value returned by a function.
    VALTYPE is the data type of the value (as a tree).
    If the precise function being called is known, FUNC is its FUNCTION_DECL;
@@ -17288,16 +17301,18 @@ rs6000_function_value (tree valtype, tree func ATTRIBUTE_UNUSED)
   /* Special handling for structs in darwin64.  */
   if (rs6000_darwin64_abi
       && TYPE_MODE (valtype) == BLKmode
-      && (TREE_CODE (valtype) == RECORD_TYPE
-         || TREE_CODE (valtype) == ARRAY_TYPE))
+      && TREE_CODE (valtype) == RECORD_TYPE
+      && int_size_in_bytes (valtype) > 0)
     {
       CUMULATIVE_ARGS valcum;
       rtx valret;
 
-      valcum.sysv_gregno = GP_ARG_RETURN;
+      valcum.words = 0;
       valcum.fregno = FP_ARG_MIN_REG;
       valcum.vregno = ALTIVEC_ARG_MIN_REG;
-      valret = rs6000_darwin64_function_value (&valcum, valtype);
+      /* Do a trial code generation as if this were going to be passed as
+        an argument; if any part goes in memory, we return NULL.  */
+      valret = rs6000_darwin64_record_arg (&valcum, valtype, 1, true);
       if (valret)
        return valret;
       /* Otherwise fall through to standard ABI rules.  */
index 9b8f1fd710f5afaf79e6621e6054b8847d62c0bb..d9c2e8e24d208cbf4243c40d84b7219dc9eaefc0 100644 (file)
@@ -1700,6 +1700,9 @@ typedef struct rs6000_args
   int stdarg;                  /* Whether function is a stdarg function.  */
   int call_cookie;             /* Do special things for this call */
   int sysv_gregno;             /* next available GP register */
+  int intoffset;               /* running offset in struct (darwin64) */
+  int use_stack;               /* any part of struct on stack (darwin64) */
+  int named;                   /* false for varargs params */
 } CUMULATIVE_ARGS;
 
 /* Initialize a variable CUM of type CUMULATIVE_ARGS