Refactor ppc64 function call and return value handling
authorUlrich Weigand <ulrich.weigand@de.ibm.com>
Tue, 4 Feb 2014 17:24:42 +0000 (18:24 +0100)
committerUlrich Weigand <ulrich.weigand@de.ibm.com>
Tue, 4 Feb 2014 17:24:42 +0000 (18:24 +0100)
This patch refactors the ppc64 function call and return value handling code
in ppc-sysv-tdep.c.  The main problem to be addressed by this refactoring
is the code duplication caused by certain aggregate types:

According to the ABI, some types are to be decomposed into component types
for parameter and return value handling.  For example, complex types are
to be passed as if the real and imaginary component were separate arguments.
Similarly, certain OpenCL vector types are passed as if they were multiple
separate arguments of the vector element type.  With the new ELFv2 ABI,
there is another case: "homogeneous aggregates" (e.g. a struct containing
4 floats) are passed in multiple floating point registers as well.

Unfortunately, the current code is not structured to easily model these
ABI properties.  For example, code to pass complex values re-implements
code to pass the underlying (floating-point) type.  This has already
led to some unfortunate code duplication, and with the addition of
ELFv2 ABI support, I would have had to add yet more such duplication.

To avoid that, I've decided to refactor the code in order to re-use
subroutines that handle the "base" types when handling those aggregate
types.  This was not intended to cause any difference on current
(ELFv1) ABI code, but in fact it fixes a bug:

FAIL: gdb.base/varargs.exp: print find_max_float_real(4, fc1, fc2, fc3, fc4)

This was caused by the old code in ppc64_sysv_abi_push_float incorrectly
handling floating-point arguments to vararg routines, which just happens
to work out correctly automatically in the refactored code ...

gdb/ChangeLog:

* ppc-sysv-tdep.c (get_decimal_float_return_value): Update comment.
(struct ppc64_sysv_argpos): New data structure.
(ppc64_sysv_abi_push_float): Remove.
(ppc64_sysv_abi_push_val): New function.
(ppc64_sysv_abi_push_integer): Likewise.
(ppc64_sysv_abi_push_freg): Likewise.
(ppc64_sysv_abi_push_vreg): Likewise.
(ppc64_sysv_abi_push_param): Likewise.
(ppc64_sysv_abi_push_dummy_call): Refactor to use those new routines.
(ppc64_sysv_abi_return_value_base): New function.
(ppc64_sysv_abi_return_value): Refactor to use it.

gdb/ChangeLog
gdb/ppc-sysv-tdep.c

index 0b019f7c956321d52cc84ac215242f8843454b48..281c441ff434affe539f70c0bae3e4e137c80895 100644 (file)
@@ -1,3 +1,17 @@
+2014-02-04  Ulrich Weigand  <uweigand@de.ibm.com>
+
+       * ppc-sysv-tdep.c (get_decimal_float_return_value): Update comment.
+       (struct ppc64_sysv_argpos): New data structure.
+       (ppc64_sysv_abi_push_float): Remove.
+       (ppc64_sysv_abi_push_val): New function.
+       (ppc64_sysv_abi_push_integer): Likewise.
+       (ppc64_sysv_abi_push_freg): Likewise.
+       (ppc64_sysv_abi_push_vreg): Likewise.
+       (ppc64_sysv_abi_push_param): Likewise.
+       (ppc64_sysv_abi_push_dummy_call): Refactor to use those new routines.
+       (ppc64_sysv_abi_return_value_base): New function.
+       (ppc64_sysv_abi_return_value): Refactor to use it.
+
 2014-02-04  Ulrich Weigand  <uweigand@de.ibm.com>
 
        * NEWS: Document new target powerpc64le-*-linux*.
index 90f3471f25f53c2638d2863907a029357fcc92eb..77a71bc987a09afa50362a4e276e273d402b381e 100644 (file)
@@ -609,8 +609,7 @@ ppc_sysv_abi_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
   return sp;
 }
 
-/* Handle the return-value conventions for Decimal Floating Point values
-   in both ppc32 and ppc64, which are the same.  */
+/* Handle the return-value conventions for Decimal Floating Point values.  */
 static int
 get_decimal_float_return_value (struct gdbarch *gdbarch, struct type *valtype,
                                struct regcache *regcache, gdb_byte *readbuf,
@@ -1102,80 +1101,287 @@ convert_code_addr_to_desc_addr (CORE_ADDR code_addr, CORE_ADDR *desc_addr)
   return 1;
 }
 
-/* Push a float in either registers, or in the stack.  Using the ppc 64 bit
-   SysV ABI.
+/* Structure holding the next argument position.  */
+struct ppc64_sysv_argpos
+  {
+    /* Register cache holding argument registers.  If this is NULL,
+       we only simulate argument processing without actually updating
+       any registers or memory.  */
+    struct regcache *regcache;
+    /* Next available general-purpose argument register.  */
+    int greg;
+    /* Next available floating-point argument register.  */
+    int freg;
+    /* Next available vector argument register.  */
+    int vreg;
+    /* The address, at which the next general purpose parameter
+       (integer, struct, float, vector, ...) should be saved.  */
+    CORE_ADDR gparam;
+    /* The address, at which the next by-reference parameter
+       (non-Altivec vector, variably-sized type) should be saved.  */
+    CORE_ADDR refparam;
+  };
+
+/* VAL is a value of length LEN.  Store it into the argument area on the
+   stack and load it into the corresponding general-purpose registers
+   required by the ABI, and update ARGPOS.
+
+   If ALIGN is nonzero, it specifies the minimum alignment required
+   for the on-stack copy of the argument.  */
 
-   This implements a dumbed down version of the ABI.  It always writes
-   values to memory, GPR and FPR, even when not necessary.  Doing this
-   greatly simplifies the logic.  */
+static void
+ppc64_sysv_abi_push_val (struct gdbarch *gdbarch,
+                        const bfd_byte *val, int len, int align,
+                        struct ppc64_sysv_argpos *argpos)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int offset = 0;
+
+  /* Enforce alignment of stack location, if requested.  */
+  if (align > tdep->wordsize)
+    {
+      CORE_ADDR aligned_gparam = align_up (argpos->gparam, align);
+
+      argpos->greg += (aligned_gparam - argpos->gparam) / tdep->wordsize;
+      argpos->gparam = aligned_gparam;
+    }
+
+  /* The ABI (version 1.9) specifies that values smaller than one
+     doubleword are right-aligned and those larger are left-aligned.
+     GCC versions before 3.4 implemented this incorrectly; see
+     <http://gcc.gnu.org/gcc-3.4/powerpc-abi.html>.  */
+  if (len < tdep->wordsize)
+    offset = tdep->wordsize - len;
+
+  if (argpos->regcache)
+    write_memory (argpos->gparam + offset, val, len);
+  argpos->gparam = align_up (argpos->gparam + len, tdep->wordsize);
+
+  while (len >= tdep->wordsize)
+    {
+      if (argpos->regcache && argpos->greg <= 10)
+       regcache_cooked_write (argpos->regcache,
+                              tdep->ppc_gp0_regnum + argpos->greg, val);
+      argpos->greg++;
+      len -= tdep->wordsize;
+      val += tdep->wordsize;
+    }
+
+  if (len > 0)
+    {
+      if (argpos->regcache && argpos->greg <= 10)
+       regcache_cooked_write_part (argpos->regcache,
+                                   tdep->ppc_gp0_regnum + argpos->greg,
+                                   offset, len, val);
+      argpos->greg++;
+    }
+}
+
+/* The same as ppc64_sysv_abi_push_val, but using a single-word integer
+   value VAL as argument.  */
 
 static void
-ppc64_sysv_abi_push_float (struct gdbarch *gdbarch, struct regcache *regcache,
-                          struct gdbarch_tdep *tdep, struct type *type, 
-                          const bfd_byte *val, int freg, int greg,
-                          CORE_ADDR gparam)
+ppc64_sysv_abi_push_integer (struct gdbarch *gdbarch, ULONGEST val,
+                            struct ppc64_sysv_argpos *argpos)
 {
-  gdb_byte regval[MAX_REGISTER_SIZE];
-  const gdb_byte *p;
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  gdb_byte buf[MAX_REGISTER_SIZE];
 
-  if (TYPE_LENGTH (type) <= 8)
+  if (argpos->regcache)
+    store_unsigned_integer (buf, tdep->wordsize, byte_order, val);
+  ppc64_sysv_abi_push_val (gdbarch, buf, tdep->wordsize, 0, argpos);
+}
+
+/* VAL is a value of TYPE, a (binary or decimal) floating-point type.
+   Load it into a floating-point register if required by the ABI,
+   and update ARGPOS.  */
+
+static void
+ppc64_sysv_abi_push_freg (struct gdbarch *gdbarch,
+                         struct type *type, const bfd_byte *val,
+                         struct ppc64_sysv_argpos *argpos)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  if (tdep->soft_float)
+    return;
+
+  if (TYPE_LENGTH (type) <= 8
+      && TYPE_CODE (type) == TYPE_CODE_FLT)
     {
-      /* Version 1.7 of the 64-bit PowerPC ELF ABI says:
+      /* Floats and doubles go in f1 .. f13.  32-bit floats are converted
+        to double first.  */
+      if (argpos->regcache && argpos->freg <= 13)
+       {
+         int regnum = tdep->ppc_fp0_regnum + argpos->freg;
+         struct type *regtype = register_type (gdbarch, regnum);
+         gdb_byte regval[MAX_REGISTER_SIZE];
 
-        "Single precision floating point values are mapped to
-        the first word in a single doubleword."
+         convert_typed_floating (val, type, regval, regtype);
+         regcache_cooked_write (argpos->regcache, regnum, regval);
+       }
 
-        And version 1.9 says:
+      argpos->freg++;
+    }
+  else if (TYPE_LENGTH (type) <= 8
+          && TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
+    {
+      /* Floats and doubles go in f1 .. f13.  32-bit decimal floats are
+        placed in the least significant word.  */
+      if (argpos->regcache && argpos->freg <= 13)
+       {
+         int regnum = tdep->ppc_fp0_regnum + argpos->freg;
+         int offset = 8 - TYPE_LENGTH (type);
 
-        "Single precision floating point values are mapped to
-        the second word in a single doubleword."
+         regcache_cooked_write_part (argpos->regcache, regnum,
+                                     offset, TYPE_LENGTH (type), val);
+       }
 
-        GDB then writes single precision floating point values
-        at both words in a doubleword, to support both ABIs.  */
-      if (TYPE_LENGTH (type) == 4)
+      argpos->freg++;
+    }
+  else if (TYPE_LENGTH (type) == 16
+          && TYPE_CODE (type) == TYPE_CODE_FLT
+          && (gdbarch_long_double_format (gdbarch)
+              == floatformats_ibm_long_double))
+    {
+      /* IBM long double stored in two consecutive FPRs.  */
+      if (argpos->regcache && argpos->freg <= 13)
        {
-         memcpy (regval, val, 4);
-         memcpy (regval + 4, val, 4);
-         p = regval;
+         int regnum = tdep->ppc_fp0_regnum + argpos->freg;
+
+         regcache_cooked_write (argpos->regcache, regnum, val);
+         if (argpos->freg <= 12)
+           regcache_cooked_write (argpos->regcache, regnum + 1, val + 8);
        }
-      else
-       p = val;
 
-      /* Write value in the stack's parameter save area.  */
-      write_memory (gparam, p, 8);
+      argpos->freg += 2;
+    }
+  else if (TYPE_LENGTH (type) == 16
+          && TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
+    {
+      /* 128-bit decimal floating-point values are stored in and even/odd
+        pair of FPRs, with the even FPR holding the most significant half.  */
+      argpos->freg += argpos->freg & 1;
 
-      /* Floats and Doubles go in f1 .. f13.  They also consume a left aligned
-        GREG, and can end up in memory.  */
-      if (freg <= 13)
+      if (argpos->regcache && argpos->freg <= 12)
        {
-         struct type *regtype;
+         int regnum = tdep->ppc_fp0_regnum + argpos->freg;
 
-         regtype = register_type (gdbarch, tdep->ppc_fp0_regnum + freg);
-         convert_typed_floating (val, type, regval, regtype);
-         regcache_cooked_write (regcache, tdep->ppc_fp0_regnum + freg, regval);
+         regcache_cooked_write (argpos->regcache, regnum, val);
+         regcache_cooked_write (argpos->regcache, regnum + 1, val + 8);
        }
-      if (greg <= 10)
-       regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + greg, regval);
+
+      argpos->freg += 2;
     }
-  else
+}
+
+/* VAL is a value of AltiVec vector type.  Load it into a vector register
+   if required by the ABI, and update ARGPOS.  */
+
+static void
+ppc64_sysv_abi_push_vreg (struct gdbarch *gdbarch, const bfd_byte *val,
+                         struct ppc64_sysv_argpos *argpos)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  if (argpos->regcache && argpos->vreg <= 13)
+    regcache_cooked_write (argpos->regcache,
+                          tdep->ppc_vr0_regnum + argpos->vreg, val);
+
+  argpos->vreg++;
+}
+
+/* VAL is a value of TYPE.  Load it into memory and/or registers
+   as required by the ABI, and update ARGPOS.  */
+
+static void
+ppc64_sysv_abi_push_param (struct gdbarch *gdbarch,
+                          struct type *type, const bfd_byte *val,
+                          struct ppc64_sysv_argpos *argpos)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  if (TYPE_CODE (type) == TYPE_CODE_FLT
+      || TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
+    {
+      /* Floating-point scalars are passed in floating-point registers.  */
+      ppc64_sysv_abi_push_val (gdbarch, val, TYPE_LENGTH (type), 0, argpos);
+      ppc64_sysv_abi_push_freg (gdbarch, type, val, argpos);
+    }
+  else if (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type)
+          && tdep->vector_abi == POWERPC_VEC_ALTIVEC
+          && TYPE_LENGTH (type) == 16)
+    {
+      /* AltiVec vectors are passed aligned, and in vector registers.  */
+      ppc64_sysv_abi_push_val (gdbarch, val, TYPE_LENGTH (type), 16, argpos);
+      ppc64_sysv_abi_push_vreg (gdbarch, val, argpos);
+    }
+  else if (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type)
+          && TYPE_LENGTH (type) >= 16)
     {
-      /* IBM long double stored in two doublewords of the
-        parameter save area and corresponding registers.  */
-      if (!tdep->soft_float && freg <= 13)
+      /* Non-Altivec vectors are passed by reference.  */
+
+      /* Copy value onto the stack ...  */
+      CORE_ADDR addr = align_up (argpos->refparam, 16);
+      if (argpos->regcache)
+       write_memory (addr, val, TYPE_LENGTH (type));
+      argpos->refparam = align_up (addr + TYPE_LENGTH (type), tdep->wordsize);
+
+      /* ... and pass a pointer to the copy as parameter.  */
+      ppc64_sysv_abi_push_integer (gdbarch, addr, argpos);
+    }
+  else if ((TYPE_CODE (type) == TYPE_CODE_INT
+           || TYPE_CODE (type) == TYPE_CODE_ENUM
+           || TYPE_CODE (type) == TYPE_CODE_BOOL
+           || TYPE_CODE (type) == TYPE_CODE_CHAR
+           || TYPE_CODE (type) == TYPE_CODE_PTR
+           || TYPE_CODE (type) == TYPE_CODE_REF)
+          && TYPE_LENGTH (type) <= tdep->wordsize)
+    {
+      ULONGEST word = 0;
+
+      if (argpos->regcache)
        {
-         regcache_cooked_write (regcache, tdep->ppc_fp0_regnum + freg, val);
-         if (freg <= 12)
-           regcache_cooked_write (regcache, tdep->ppc_fp0_regnum + freg + 1,
-                                  val + 8);
+         /* Sign extend the value, then store it unsigned.  */
+         word = unpack_long (type, val);
+
+         /* Convert any function code addresses into descriptors.  */
+         if (TYPE_CODE (type) == TYPE_CODE_PTR
+             || TYPE_CODE (type) == TYPE_CODE_REF)
+           {
+             struct type *target_type
+               = check_typedef (TYPE_TARGET_TYPE (type));
+
+             if (TYPE_CODE (target_type) == TYPE_CODE_FUNC
+                 || TYPE_CODE (target_type) == TYPE_CODE_METHOD)
+               {
+                 CORE_ADDR desc = word;
+
+                 convert_code_addr_to_desc_addr (word, &desc);
+                 word = desc;
+               }
+           }
        }
-      if (greg <= 10)
+
+      ppc64_sysv_abi_push_integer (gdbarch, word, argpos);
+    }
+  else
+    {
+      ppc64_sysv_abi_push_val (gdbarch, val, TYPE_LENGTH (type), 0, argpos);
+
+      /* The ABI (version 1.9) specifies that structs containing a
+        single floating-point value, at any level of nesting of
+        single-member structs, are passed in floating-point registers.  */
+      if (TYPE_CODE (type) == TYPE_CODE_STRUCT
+         && TYPE_NFIELDS (type) == 1)
        {
-         regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + greg, val);
-         if (greg <= 9)
-           regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + greg + 1,
-                                  val + 8);
+         while (TYPE_CODE (type) == TYPE_CODE_STRUCT
+                && TYPE_NFIELDS (type) == 1)
+           type = check_typedef (TYPE_FIELD_TYPE (type, 0));
+
+         if (TYPE_CODE (type) == TYPE_CODE_FLT)
+           ppc64_sysv_abi_push_freg (gdbarch, type, val, argpos);
        }
-      write_memory (gparam, val, TYPE_LENGTH (type));
     }
 }
 
@@ -1237,20 +1443,11 @@ ppc64_sysv_abi_push_dummy_call (struct gdbarch *gdbarch,
   for (write_pass = 0; write_pass < 2; write_pass++)
     {
       int argno;
-      /* Next available floating point register for float and double
-         arguments.  */
-      int freg = 1;
-      /* Next available general register for non-vector (but possibly
-         float) arguments.  */
-      int greg = 3;
-      /* Next available vector register for vector arguments.  */
-      int vreg = 2;
-      /* The address, at which the next general purpose parameter
-         (integer, struct, float, vector, ...) should be saved.  */
-      CORE_ADDR gparam;
-      /* The address, at which the next by-reference parameter
-        (non-Altivec vector, variably-sized type) should be saved.  */
-      CORE_ADDR refparam;
+
+      struct ppc64_sysv_argpos argpos;
+      argpos.greg = 3;
+      argpos.freg = 1;
+      argpos.vreg = 2;
 
       if (!write_pass)
        {
@@ -1258,19 +1455,21 @@ ppc64_sysv_abi_push_dummy_call (struct gdbarch *gdbarch,
             offsets (start address zero) than addresses.  That way
             they accumulate the total stack space each region
             requires.  */
-         gparam = 0;
-         refparam = 0;
+         argpos.regcache = NULL;
+         argpos.gparam = 0;
+         argpos.refparam = 0;
        }
       else
        {
          /* Decrement the stack pointer making space for the Altivec
             and general on-stack parameters.  Set refparam and gparam
             to their corresponding regions.  */
-         refparam = align_down (sp - refparam_size, 16);
-         gparam = align_down (refparam - gparam_size, 16);
+         argpos.regcache = regcache;
+         argpos.refparam = align_down (sp - refparam_size, 16);
+         argpos.gparam = align_down (argpos.refparam - gparam_size, 16);
          /* Add in space for the TOC, link editor double word,
             compiler double word, LR save area, CR save area.  */
-         sp = align_down (gparam - 48, 16);
+         sp = align_down (argpos.gparam - 48, 16);
        }
 
       /* If the function is returning a `struct', then there is an
@@ -1279,14 +1478,7 @@ ppc64_sysv_abi_push_dummy_call (struct gdbarch *gdbarch,
          should advance one word and start from r4 register to copy
          parameters.  This also consumes one on-stack parameter slot.  */
       if (struct_return)
-       {
-         if (write_pass)
-           regcache_cooked_write_signed (regcache,
-                                         tdep->ppc_gp0_regnum + greg,
-                                         struct_addr);
-         greg++;
-         gparam = align_up (gparam + tdep->wordsize, tdep->wordsize);
-       }
+       ppc64_sysv_abi_push_integer (gdbarch, struct_addr, &argpos);
 
       for (argno = 0; argno < nargs; argno++)
        {
@@ -1294,432 +1486,54 @@ ppc64_sysv_abi_push_dummy_call (struct gdbarch *gdbarch,
          struct type *type = check_typedef (value_type (arg));
          const bfd_byte *val = value_contents (arg);
 
-         if (TYPE_CODE (type) == TYPE_CODE_FLT && TYPE_LENGTH (type) <= 8)
+         if (TYPE_CODE (type) == TYPE_CODE_COMPLEX)
            {
-             if (write_pass)
-                 ppc64_sysv_abi_push_float (gdbarch, regcache, tdep, type,
-                                            val, freg, greg, gparam);
-
-             freg++;
-             greg++;
-             /* Always consume parameter stack space.  */
-             gparam = align_up (gparam + 8, tdep->wordsize);
-           }
-         else if (TYPE_CODE (type) == TYPE_CODE_FLT
-                  && TYPE_LENGTH (type) == 16
-                  && (gdbarch_long_double_format (gdbarch)
-                      == floatformats_ibm_long_double))
-           {
-             if (write_pass)
-               ppc64_sysv_abi_push_float (gdbarch, regcache, tdep, type,
-                                          val, freg, greg, gparam);
-             freg += 2;
-             greg += 2;
-             gparam = align_up (gparam + TYPE_LENGTH (type), tdep->wordsize);
-           }
-         else if (TYPE_CODE (type) == TYPE_CODE_COMPLEX
-             && (TYPE_LENGTH (type) == 8 || TYPE_LENGTH (type) == 16))
-           {
-             int i;
-
-             for (i = 0; i < 2; i++)
-               {
-                 if (write_pass)
-                   {
-                     struct type *target_type;
-
-                     target_type = check_typedef (TYPE_TARGET_TYPE (type));
-                     ppc64_sysv_abi_push_float (gdbarch, regcache, tdep,
-                                                target_type, val + i *
-                                                TYPE_LENGTH (target_type),
-                                                freg, greg, gparam);
-                   }
-                 freg++;
-                 greg++;
-                 /* Always consume parameter stack space.  */
-                 gparam = align_up (gparam + 8, tdep->wordsize);
-               }
-           }
-         else if (TYPE_CODE (type) == TYPE_CODE_COMPLEX
-                  && TYPE_LENGTH (type) == 32
-                  && (gdbarch_long_double_format (gdbarch)
-                      == floatformats_ibm_long_double))
-           {
-             int i;
-
-             for (i = 0; i < 2; i++)
-               {
-                 struct type *target_type;
-
-                 target_type = check_typedef (TYPE_TARGET_TYPE (type));
-                 if (write_pass)
-                   ppc64_sysv_abi_push_float (gdbarch, regcache, tdep,
-                                              target_type, val + i *
-                                              TYPE_LENGTH (target_type),
-                                              freg, greg, gparam);
-                 freg += 2;
-                 greg += 2;
-                 gparam = align_up (gparam + TYPE_LENGTH (target_type),
-                                    tdep->wordsize);
-               }
-           }
-         else if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT
-                  && TYPE_LENGTH (type) <= 8)
-           {
-             /* 32-bit and 64-bit decimal floats go in f1 .. f13.  They can
-                end up in memory.  */
-             if (write_pass)
-               {
-                 gdb_byte regval[MAX_REGISTER_SIZE];
-                 const gdb_byte *p;
-
-                 /* 32-bit decimal floats are right aligned in the
-                    doubleword.  */
-                 if (TYPE_LENGTH (type) == 4)
-                   {
-                     memcpy (regval + 4, val, 4);
-                     p = regval;
-                   }
-                 else
-                   p = val;
-
-                 /* Write value in the stack's parameter save area.  */
-                 write_memory (gparam, p, 8);
-
-                 if (freg <= 13)
-                   regcache_cooked_write (regcache,
-                                          tdep->ppc_fp0_regnum + freg, p);
-               }
-
-             freg++;
-             greg++;
-             /* Always consume parameter stack space.  */
-             gparam = align_up (gparam + 8, tdep->wordsize);
-           }
-         else if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT &&
-                  TYPE_LENGTH (type) == 16)
-           {
-             /* 128-bit decimal floats go in f2 .. f12, always in even/odd
-                pairs.  They can end up in memory, using two doublewords.  */
-             if (write_pass)
-               {
-                 if (freg <= 12)
-                   {
-                     /* Make sure freg is even.  */
-                     freg += freg & 1;
-                     regcache_cooked_write (regcache,
-                                             tdep->ppc_fp0_regnum + freg, val);
-                     regcache_cooked_write (regcache,
-                         tdep->ppc_fp0_regnum + freg + 1, val + 8);
-                   }
-
-                 write_memory (gparam, val, TYPE_LENGTH (type));
-               }
+             /* Complex types are passed as if two independent scalars.  */
+             struct type *eltype = check_typedef (TYPE_TARGET_TYPE (type));
 
-             freg += 2;
-             greg += 2;
-             gparam = align_up (gparam + TYPE_LENGTH (type), tdep->wordsize);
+             ppc64_sysv_abi_push_param (gdbarch, eltype, val, &argpos);
+             ppc64_sysv_abi_push_param (gdbarch, eltype,
+                                        val + TYPE_LENGTH (eltype), &argpos);
            }
-         else if (TYPE_LENGTH (type) < 16
-                  && TYPE_CODE (type) == TYPE_CODE_ARRAY
-                  && TYPE_VECTOR (type)
+         else if (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type)
                   && opencl_abi)
            {
              /* OpenCL vectors shorter than 16 bytes are passed as if
-                a series of independent scalars.  */
-             struct type *eltype = check_typedef (TYPE_TARGET_TYPE (type));
-             int i, nelt = TYPE_LENGTH (type) / TYPE_LENGTH (eltype);
+                a series of independent scalars; OpenCL vectors 16 bytes
+                or longer are passed as if a series of AltiVec vectors.  */
+             struct type *eltype;
+             int i, nelt;
 
+             if (TYPE_LENGTH (type) < 16)
+               eltype = check_typedef (TYPE_TARGET_TYPE (type));
+             else
+               eltype = register_type (gdbarch, tdep->ppc_vr0_regnum);
+
+             nelt = TYPE_LENGTH (type) / TYPE_LENGTH (eltype);
              for (i = 0; i < nelt; i++)
                {
                  const gdb_byte *elval = val + i * TYPE_LENGTH (eltype);
 
-                 if (TYPE_CODE (eltype) == TYPE_CODE_FLT)
-                   {
-                     if (write_pass)
-                       {
-                         gdb_byte regval[MAX_REGISTER_SIZE];
-                         const gdb_byte *p;
-
-                         if (TYPE_LENGTH (eltype) == 4)
-                           {
-                             memcpy (regval, elval, 4);
-                             memcpy (regval + 4, elval, 4);
-                             p = regval;
-                           }
-                         else
-                           p = elval;
-
-                         write_memory (gparam, p, 8);
-
-                         if (freg <= 13)
-                           {
-                             int regnum = tdep->ppc_fp0_regnum + freg;
-                             struct type *regtype
-                               = register_type (gdbarch, regnum);
-
-                             convert_typed_floating (elval, eltype,
-                                                     regval, regtype);
-                             regcache_cooked_write (regcache, regnum, regval);
-                           }
-
-                         if (greg <= 10)
-                           regcache_cooked_write (regcache,
-                                                  tdep->ppc_gp0_regnum + greg,
-                                                  regval);
-                       }
-
-                     freg++;
-                     greg++;
-                     gparam = align_up (gparam + 8, tdep->wordsize);
-                   }
-                 else
-                   {
-                     if (write_pass)
-                       {
-                         ULONGEST word = unpack_long (eltype, elval);
-                         if (greg <= 10)
-                           regcache_cooked_write_unsigned
-                             (regcache, tdep->ppc_gp0_regnum + greg, word);
-
-                         write_memory_unsigned_integer
-                           (gparam, tdep->wordsize, byte_order, word);
-                       }
-
-                     greg++;
-                     gparam = align_up (gparam + TYPE_LENGTH (eltype),
-                                        tdep->wordsize);
-                   }
-               }
-           }
-         else if (TYPE_LENGTH (type) >= 16
-                  && TYPE_CODE (type) == TYPE_CODE_ARRAY
-                  && TYPE_VECTOR (type)
-                  && opencl_abi)
-           {
-             /* OpenCL vectors 16 bytes or longer are passed as if
-                a series of AltiVec vectors.  */
-             int i;
-
-             for (i = 0; i < TYPE_LENGTH (type) / 16; i++)
-               {
-                 const gdb_byte *elval = val + i * 16;
-
-                 gparam = align_up (gparam, 16);
-                 greg += greg & 1;
-
-                 if (write_pass)
-                   {
-                     if (vreg <= 13)
-                       regcache_cooked_write (regcache,
-                                              tdep->ppc_vr0_regnum + vreg,
-                                              elval);
-
-                     write_memory (gparam, elval, 16);
-                   }
-
-                 greg += 2;
-                 vreg++;
-                 gparam += 16;
-               }
-           }
-         else if (TYPE_LENGTH (type) == 16 && TYPE_VECTOR (type)
-                  && TYPE_CODE (type) == TYPE_CODE_ARRAY
-                  && tdep->vector_abi == POWERPC_VEC_ALTIVEC)
-           {
-             /* In the Altivec ABI, vectors go in the vector registers
-                v2 .. v13, as well as the parameter area -- always at
-                16-byte aligned addresses.  */
-
-             gparam = align_up (gparam, 16);
-             greg += greg & 1;
-
-             if (write_pass)
-               {
-                 if (vreg <= 13)
-                   regcache_cooked_write (regcache,
-                                          tdep->ppc_vr0_regnum + vreg, val);
-
-                 write_memory (gparam, val, TYPE_LENGTH (type));
-               }
-
-             greg += 2;
-             vreg++;
-             gparam += 16;
-           }
-         else if (TYPE_LENGTH (type) >= 16 && TYPE_VECTOR (type)
-                  && TYPE_CODE (type) == TYPE_CODE_ARRAY)
-           {
-             /* Non-Altivec vectors are passed by reference.  */
-
-             /* Copy value onto the stack ...  */
-             refparam = align_up (refparam, 16);
-             if (write_pass)
-               write_memory (refparam, val, TYPE_LENGTH (type));
-
-             /* ... and pass a pointer to the copy as parameter.  */
-             if (write_pass)
-               {
-                 if (greg <= 10)
-                   regcache_cooked_write_unsigned (regcache,
-                                                   tdep->ppc_gp0_regnum +
-                                                   greg, refparam);
-                 write_memory_unsigned_integer (gparam, tdep->wordsize,
-                                                byte_order, refparam);
-               }
-             greg++;
-             gparam = align_up (gparam + tdep->wordsize, tdep->wordsize);
-             refparam = align_up (refparam + TYPE_LENGTH (type), tdep->wordsize);
-           }
-         else if ((TYPE_CODE (type) == TYPE_CODE_INT
-                   || TYPE_CODE (type) == TYPE_CODE_ENUM
-                   || TYPE_CODE (type) == TYPE_CODE_BOOL
-                   || TYPE_CODE (type) == TYPE_CODE_CHAR
-                   || TYPE_CODE (type) == TYPE_CODE_PTR
-                   || TYPE_CODE (type) == TYPE_CODE_REF)
-                  && TYPE_LENGTH (type) <= 8)
-           {
-             /* Scalars and Pointers get sign[un]extended and go in
-                gpr3 .. gpr10.  They can also end up in memory.  */
-             if (write_pass)
-               {
-                 /* Sign extend the value, then store it unsigned.  */
-                 ULONGEST word = unpack_long (type, val);
-                 /* Convert any function code addresses into
-                    descriptors.  */
-                 if (TYPE_CODE (type) == TYPE_CODE_PTR
-                     || TYPE_CODE (type) == TYPE_CODE_REF)
-                   {
-                     struct type *target_type;
-                     target_type = check_typedef (TYPE_TARGET_TYPE (type));
-
-                     if (TYPE_CODE (target_type) == TYPE_CODE_FUNC
-                         || TYPE_CODE (target_type) == TYPE_CODE_METHOD)
-                       {
-                         CORE_ADDR desc = word;
-                         convert_code_addr_to_desc_addr (word, &desc);
-                         word = desc;
-                       }
-                   }
-                 if (greg <= 10)
-                   regcache_cooked_write_unsigned (regcache,
-                                                   tdep->ppc_gp0_regnum +
-                                                   greg, word);
-                 write_memory_unsigned_integer (gparam, tdep->wordsize,
-                                                byte_order, word);
+                 ppc64_sysv_abi_push_param (gdbarch, eltype, elval, &argpos);
                }
-             greg++;
-             gparam = align_up (gparam + TYPE_LENGTH (type), tdep->wordsize);
            }
          else
            {
-             int byte;
-             for (byte = 0; byte < TYPE_LENGTH (type);
-                  byte += tdep->wordsize)
-               {
-                 if (write_pass && greg <= 10)
-                   {
-                     gdb_byte regval[MAX_REGISTER_SIZE];
-                     int len = TYPE_LENGTH (type) - byte;
-                     if (len > tdep->wordsize)
-                       len = tdep->wordsize;
-                     memset (regval, 0, sizeof regval);
-                     /* The ABI (version 1.9) specifies that values
-                        smaller than one doubleword are right-aligned
-                        and those larger are left-aligned.  GCC
-                        versions before 3.4 implemented this
-                        incorrectly; see
-                        <http://gcc.gnu.org/gcc-3.4/powerpc-abi.html>.  */
-                     if (byte == 0)
-                       memcpy (regval + tdep->wordsize - len,
-                               val + byte, len);
-                     else
-                       memcpy (regval, val + byte, len);
-                     regcache_cooked_write (regcache, greg, regval);
-                   }
-                 greg++;
-               }
-             if (write_pass)
-               {
-                 /* WARNING: cagney/2003-09-21: Strictly speaking, this
-                    isn't necessary, unfortunately, GCC appears to get
-                    "struct convention" parameter passing wrong putting
-                    odd sized structures in memory instead of in a
-                    register.  Work around this by always writing the
-                    value to memory.  Fortunately, doing this
-                    simplifies the code.  */
-                 int len = TYPE_LENGTH (type);
-                 if (len < tdep->wordsize)
-                   write_memory (gparam + tdep->wordsize - len, val, len);
-                 else
-                   write_memory (gparam, val, len);
-               }
-             if (freg <= 13
-                 && TYPE_CODE (type) == TYPE_CODE_STRUCT
-                 && TYPE_NFIELDS (type) == 1
-                 && TYPE_LENGTH (type) <= 16)
-               {
-                 /* The ABI (version 1.9) specifies that structs
-                    containing a single floating-point value, at any
-                    level of nesting of single-member structs, are
-                    passed in floating-point registers.  */
-                 while (TYPE_CODE (type) == TYPE_CODE_STRUCT
-                        && TYPE_NFIELDS (type) == 1)
-                   type = check_typedef (TYPE_FIELD_TYPE (type, 0));
-                 if (TYPE_CODE (type) == TYPE_CODE_FLT)
-                   {
-                     if (TYPE_LENGTH (type) <= 8)
-                       {
-                         if (write_pass)
-                           {
-                             gdb_byte regval[MAX_REGISTER_SIZE];
-                             struct type *regtype
-                               = register_type (gdbarch,
-                                                tdep->ppc_fp0_regnum);
-                             convert_typed_floating (val, type, regval,
-                                                     regtype);
-                             regcache_cooked_write (regcache,
-                                                    (tdep->ppc_fp0_regnum
-                                                     + freg),
-                                                    regval);
-                           }
-                         freg++;
-                       }
-                     else if (TYPE_LENGTH (type) == 16
-                              && (gdbarch_long_double_format (gdbarch)
-                                  == floatformats_ibm_long_double))
-                       {
-                         if (write_pass)
-                           {
-                             regcache_cooked_write (regcache,
-                                                    (tdep->ppc_fp0_regnum
-                                                     + freg),
-                                                    val);
-                             if (freg <= 12)
-                               regcache_cooked_write (regcache,
-                                                      (tdep->ppc_fp0_regnum
-                                                       + freg + 1),
-                                                      val + 8);
-                           }
-                         freg += 2;
-                       }
-                   }
-               }
-             /* Always consume parameter stack space.  */
-             gparam = align_up (gparam + TYPE_LENGTH (type), tdep->wordsize);
+             /* All other types are passed as single arguments.  */
+             ppc64_sysv_abi_push_param (gdbarch, type, val, &argpos);
            }
        }
 
       if (!write_pass)
        {
          /* Save the true region sizes ready for the second pass.  */
-         refparam_size = refparam;
+         refparam_size = argpos.refparam;
          /* Make certain that the general parameter save area is at
             least the minimum 8 registers (or doublewords) in size.  */
-         if (greg < 8)
+         if (argpos.greg < 8)
            gparam_size = 8 * tdep->wordsize;
          else
-           gparam_size = gparam;
+           gparam_size = argpos.gparam;
        }
     }
 
@@ -1755,271 +1569,252 @@ ppc64_sysv_abi_push_dummy_call (struct gdbarch *gdbarch,
   return sp;
 }
 
+/* Subroutine of ppc64_sysv_abi_return_value that handles "base" types:
+   integer, floating-point, and AltiVec vector types.
 
-/* The 64 bit ABI return value convention.
-
-   Return non-zero if the return-value is stored in a register, return
-   0 if the return-value is instead stored on the stack (a.k.a.,
-   struct return convention).
+   This routine also handles components of aggregate return types;
+   INDEX describes which part of the aggregate is to be handled.
 
-   For a return-value stored in a register: when WRITEBUF is non-NULL,
-   copy the buffer to the corresponding register return-value location
-   location; when READBUF is non-NULL, fill the buffer from the
-   corresponding register return-value location.  */
-enum return_value_convention
-ppc64_sysv_abi_return_value (struct gdbarch *gdbarch, struct value *function,
-                            struct type *valtype, struct regcache *regcache,
-                            gdb_byte *readbuf, const gdb_byte *writebuf)
+   Returns true if VALTYPE is some such base type that could be handled,
+   false otherwise.  */
+static int
+ppc64_sysv_abi_return_value_base (struct gdbarch *gdbarch, struct type *valtype,
+                                 struct regcache *regcache, gdb_byte *readbuf,
+                                 const gdb_byte *writebuf, int index)
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  struct type *func_type = function ? value_type (function) : NULL;
-  int opencl_abi = func_type? ppc_sysv_use_opencl_abi (func_type) : 0;
 
-  /* This function exists to support a calling convention that
-     requires floating-point registers.  It shouldn't be used on
-     processors that lack them.  */
-  gdb_assert (ppc_floating_point_unit_p (gdbarch));
-
-  /* Floats and doubles in F1.  */
-  if (TYPE_CODE (valtype) == TYPE_CODE_FLT && TYPE_LENGTH (valtype) <= 8)
-    {
-      gdb_byte regval[MAX_REGISTER_SIZE];
-      struct type *regtype = register_type (gdbarch, tdep->ppc_fp0_regnum);
-      if (writebuf != NULL)
-       {
-         convert_typed_floating (writebuf, valtype, regval, regtype);
-         regcache_cooked_write (regcache, tdep->ppc_fp0_regnum + 1, regval);
-       }
-      if (readbuf != NULL)
-       {
-         regcache_cooked_read (regcache, tdep->ppc_fp0_regnum + 1, regval);
-         convert_typed_floating (regval, regtype, readbuf, valtype);
-       }
-      return RETURN_VALUE_REGISTER_CONVENTION;
-    }
-  if (TYPE_CODE (valtype) == TYPE_CODE_DECFLOAT)
-    return get_decimal_float_return_value (gdbarch, valtype, regcache, readbuf,
-                                          writebuf);
-  /* Integers in r3.  */
+  /* Integers live in GPRs starting at r3.  */
   if ((TYPE_CODE (valtype) == TYPE_CODE_INT
        || TYPE_CODE (valtype) == TYPE_CODE_ENUM
        || TYPE_CODE (valtype) == TYPE_CODE_CHAR
        || TYPE_CODE (valtype) == TYPE_CODE_BOOL)
       && TYPE_LENGTH (valtype) <= 8)
     {
+      int regnum = tdep->ppc_gp0_regnum + 3 + index;
+
       if (writebuf != NULL)
        {
          /* Be careful to sign extend the value.  */
-         regcache_cooked_write_unsigned (regcache, tdep->ppc_gp0_regnum + 3,
+         regcache_cooked_write_unsigned (regcache, regnum,
                                          unpack_long (valtype, writebuf));
        }
       if (readbuf != NULL)
        {
-         /* Extract the integer from r3.  Since this is truncating the
+         /* Extract the integer from GPR.  Since this is truncating the
             value, there isn't a sign extension problem.  */
          ULONGEST regval;
-         regcache_cooked_read_unsigned (regcache, tdep->ppc_gp0_regnum + 3,
-                                        &regval);
-         store_unsigned_integer (readbuf, TYPE_LENGTH (valtype), byte_order,
-                                 regval);
+
+         regcache_cooked_read_unsigned (regcache, regnum, &regval);
+         store_unsigned_integer (readbuf, TYPE_LENGTH (valtype),
+                                 gdbarch_byte_order (gdbarch), regval);
        }
-      return RETURN_VALUE_REGISTER_CONVENTION;
+      return 1;
     }
-  /* All pointers live in r3.  */
-  if (TYPE_CODE (valtype) == TYPE_CODE_PTR
-      || TYPE_CODE (valtype) == TYPE_CODE_REF)
+
+  /* Floats and doubles go in f1 .. f13.  32-bit floats are converted
+     to double first.  */
+  if (TYPE_LENGTH (valtype) <= 8
+      && TYPE_CODE (valtype) == TYPE_CODE_FLT)
     {
-      /* All pointers live in r3.  */
+      int regnum = tdep->ppc_fp0_regnum + 1 + index;
+      struct type *regtype = register_type (gdbarch, regnum);
+      gdb_byte regval[MAX_REGISTER_SIZE];
+
       if (writebuf != NULL)
-       regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + 3, writebuf);
+       {
+         convert_typed_floating (writebuf, valtype, regval, regtype);
+         regcache_cooked_write (regcache, regnum, regval);
+       }
       if (readbuf != NULL)
-       regcache_cooked_read (regcache, tdep->ppc_gp0_regnum + 3, readbuf);
-      return RETURN_VALUE_REGISTER_CONVENTION;
+       {
+         regcache_cooked_read (regcache, regnum, regval);
+         convert_typed_floating (regval, regtype, readbuf, valtype);
+       }
+      return 1;
     }
-  /* OpenCL vectors < 16 bytes are returned as distinct
-     scalars in f1..f2 or r3..r10.  */
-  if (TYPE_CODE (valtype) == TYPE_CODE_ARRAY
-      && TYPE_VECTOR (valtype)
-      && TYPE_LENGTH (valtype) < 16
-      && opencl_abi)
-    {
-      struct type *eltype = check_typedef (TYPE_TARGET_TYPE (valtype));
-      int i, nelt = TYPE_LENGTH (valtype) / TYPE_LENGTH (eltype);
 
-      for (i = 0; i < nelt; i++)
-       {
-         int offset = i * TYPE_LENGTH (eltype);
+  /* Floats and doubles go in f1 .. f13.  32-bit decimal floats are
+     placed in the least significant word.  */
+  if (TYPE_LENGTH (valtype) <= 8
+      && TYPE_CODE (valtype) == TYPE_CODE_DECFLOAT)
+    {
+      int regnum = tdep->ppc_fp0_regnum + 1 + index;
+      int offset = 8 - TYPE_LENGTH (valtype);
 
-         if (TYPE_CODE (eltype) == TYPE_CODE_FLT)
-           {
-             int regnum = tdep->ppc_fp0_regnum + 1 + i;
-             gdb_byte regval[MAX_REGISTER_SIZE];
-             struct type *regtype = register_type (gdbarch, regnum);
+      if (writebuf != NULL)
+       regcache_cooked_write_part (regcache, regnum,
+                                   offset, TYPE_LENGTH (valtype), writebuf);
+      if (readbuf != NULL)
+       regcache_cooked_read_part (regcache, regnum,
+                                  offset, TYPE_LENGTH (valtype), readbuf);
+      return 1;
+    }
 
-             if (writebuf != NULL)
-               {
-                 convert_typed_floating (writebuf + offset, eltype,
-                                         regval, regtype);
-                 regcache_cooked_write (regcache, regnum, regval);
-               }
-             if (readbuf != NULL)
-               {
-                 regcache_cooked_read (regcache, regnum, regval);
-                 convert_typed_floating (regval, regtype,
-                                         readbuf + offset, eltype);
-               }
-           }
-         else
-           {
-             int regnum = tdep->ppc_gp0_regnum + 3 + i;
-             ULONGEST regval;
+  /* IBM long double stored in two consecutive FPRs.  */
+  if (TYPE_LENGTH (valtype) == 16
+      && TYPE_CODE (valtype) == TYPE_CODE_FLT
+      && (gdbarch_long_double_format (gdbarch)
+         == floatformats_ibm_long_double))
+    {
+      int regnum = tdep->ppc_fp0_regnum + 1 + 2 * index;
 
-             if (writebuf != NULL)
-               {
-                 regval = unpack_long (eltype, writebuf + offset);
-                 regcache_cooked_write_unsigned (regcache, regnum, regval);
-               }
-             if (readbuf != NULL)
-               {
-                 regcache_cooked_read_unsigned (regcache, regnum, &regval);
-                 store_unsigned_integer (readbuf + offset,
-                                         TYPE_LENGTH (eltype), byte_order,
-                                         regval);
-               }
-           }
+      if (writebuf != NULL)
+       {
+         regcache_cooked_write (regcache, regnum, writebuf);
+         regcache_cooked_write (regcache, regnum + 1, writebuf + 8);
        }
-
-      return RETURN_VALUE_REGISTER_CONVENTION;
+      if (readbuf != NULL)
+       {
+         regcache_cooked_read (regcache, regnum, readbuf);
+         regcache_cooked_read (regcache, regnum + 1, readbuf + 8);
+       }
+      return 1;
     }
-  /* OpenCL vectors >= 16 bytes are returned in v2..v9.  */
-  if (TYPE_CODE (valtype) == TYPE_CODE_ARRAY
-      && TYPE_VECTOR (valtype)
-      && TYPE_LENGTH (valtype) >= 16
-      && opencl_abi)
+
+  /* 128-bit decimal floating-point values are stored in an even/odd
+     pair of FPRs, with the even FPR holding the most significant half.  */
+  if (TYPE_LENGTH (valtype) == 16
+      && TYPE_CODE (valtype) == TYPE_CODE_DECFLOAT)
     {
-      int n_regs = TYPE_LENGTH (valtype) / 16;
-      int i;
+      int regnum = tdep->ppc_fp0_regnum + 2 + 2 * index;
 
-      for (i = 0; i < n_regs; i++)
+      if (writebuf != NULL)
        {
-         int offset = i * 16;
-         int regnum = tdep->ppc_vr0_regnum + 2 + i;
-
-         if (writebuf != NULL)
-           regcache_cooked_write (regcache, regnum, writebuf + offset);
-         if (readbuf != NULL)
-           regcache_cooked_read (regcache, regnum, readbuf + offset);
+         regcache_cooked_write (regcache, regnum, writebuf);
+         regcache_cooked_write (regcache, regnum + 1, writebuf + 8);
        }
-
-      return RETURN_VALUE_REGISTER_CONVENTION;
+      if (readbuf != NULL)
+       {
+         regcache_cooked_read (regcache, regnum, readbuf);
+         regcache_cooked_read (regcache, regnum + 1, readbuf + 8);
+       }
+      return 1;
     }
-  /* Array type has more than one use.  */
-  if (TYPE_CODE (valtype) == TYPE_CODE_ARRAY)
+
+  /* AltiVec vectors are returned in VRs starting at v2.  */
+  if (TYPE_CODE (valtype) == TYPE_CODE_ARRAY && TYPE_VECTOR (valtype)
+      && tdep->vector_abi == POWERPC_VEC_ALTIVEC)
     {
-      /* Small character arrays are returned, right justified, in r3.  */
-      if (TYPE_LENGTH (valtype) <= 8
-        && TYPE_CODE (TYPE_TARGET_TYPE (valtype)) == TYPE_CODE_INT
-        && TYPE_LENGTH (TYPE_TARGET_TYPE (valtype)) == 1)
-        {
-          int offset = (register_size (gdbarch, tdep->ppc_gp0_regnum + 3)
-                       - TYPE_LENGTH (valtype));
-          if (writebuf != NULL)
-           regcache_cooked_write_part (regcache, tdep->ppc_gp0_regnum + 3,
-                                      offset, TYPE_LENGTH (valtype), writebuf);
-          if (readbuf != NULL)
-           regcache_cooked_read_part (regcache, tdep->ppc_gp0_regnum + 3,
-                                      offset, TYPE_LENGTH (valtype), readbuf);
-          return RETURN_VALUE_REGISTER_CONVENTION;
-       }
-      /* A VMX vector is returned in v2.  */
-      if (TYPE_CODE (valtype) == TYPE_CODE_ARRAY
-         && TYPE_VECTOR (valtype)
-         && tdep->vector_abi == POWERPC_VEC_ALTIVEC)
-        {
-          if (readbuf)
-            regcache_cooked_read (regcache, tdep->ppc_vr0_regnum + 2, readbuf);
-          if (writebuf)
-            regcache_cooked_write (regcache, tdep->ppc_vr0_regnum + 2,
-                                  writebuf);
-          return RETURN_VALUE_REGISTER_CONVENTION;
-        }
+      int regnum = tdep->ppc_vr0_regnum + 2 + index;
+
+      if (writebuf != NULL)
+       regcache_cooked_write (regcache, regnum, writebuf);
+      if (readbuf != NULL)
+       regcache_cooked_read (regcache, regnum, readbuf);
+      return 1;
     }
-  /* Big floating point values get stored in adjacent floating
-     point registers, starting with F1.  */
-  if (TYPE_CODE (valtype) == TYPE_CODE_FLT
-      && (TYPE_LENGTH (valtype) == 16 || TYPE_LENGTH (valtype) == 32))
+
+  return 0;
+}
+
+/* The 64 bit ABI return value convention.
+
+   Return non-zero if the return-value is stored in a register, return
+   0 if the return-value is instead stored on the stack (a.k.a.,
+   struct return convention).
+
+   For a return-value stored in a register: when WRITEBUF is non-NULL,
+   copy the buffer to the corresponding register return-value location
+   location; when READBUF is non-NULL, fill the buffer from the
+   corresponding register return-value location.  */
+enum return_value_convention
+ppc64_sysv_abi_return_value (struct gdbarch *gdbarch, struct value *function,
+                            struct type *valtype, struct regcache *regcache,
+                            gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  struct type *func_type = function ? value_type (function) : NULL;
+  int opencl_abi = func_type? ppc_sysv_use_opencl_abi (func_type) : 0;
+  struct type *eltype;
+  int nelt, i, ok;
+
+  /* This function exists to support a calling convention that
+     requires floating-point registers.  It shouldn't be used on
+     processors that lack them.  */
+  gdb_assert (ppc_floating_point_unit_p (gdbarch));
+
+  /* Complex types are returned as if two independent scalars.  */
+  if (TYPE_CODE (valtype) == TYPE_CODE_COMPLEX)
     {
-      if (writebuf || readbuf != NULL)
+      eltype = check_typedef (TYPE_TARGET_TYPE (valtype));
+
+      for (i = 0; i < 2; i++)
        {
-         int i;
-         for (i = 0; i < TYPE_LENGTH (valtype) / 8; i++)
-           {
-             if (writebuf != NULL)
-               regcache_cooked_write (regcache, tdep->ppc_fp0_regnum + 1 + i,
-                                      (const bfd_byte *) writebuf + i * 8);
-             if (readbuf != NULL)
-               regcache_cooked_read (regcache, tdep->ppc_fp0_regnum + 1 + i,
-                                     (bfd_byte *) readbuf + i * 8);
-           }
+         ok = ppc64_sysv_abi_return_value_base (gdbarch, eltype, regcache,
+                                                readbuf, writebuf, i);
+         gdb_assert (ok);
+
+         if (readbuf)
+           readbuf += TYPE_LENGTH (eltype);
+         if (writebuf)
+           writebuf += TYPE_LENGTH (eltype);
        }
       return RETURN_VALUE_REGISTER_CONVENTION;
     }
-  /* Complex values get returned in f1:f2, need to convert.  */
-  if (TYPE_CODE (valtype) == TYPE_CODE_COMPLEX
-      && (TYPE_LENGTH (valtype) == 8 || TYPE_LENGTH (valtype) == 16))
+
+  /* OpenCL vectors shorter than 16 bytes are returned as if
+     a series of independent scalars; OpenCL vectors 16 bytes
+     or longer are returned as if a series of AltiVec vectors.  */
+  if (TYPE_CODE (valtype) == TYPE_CODE_ARRAY && TYPE_VECTOR (valtype)
+      && opencl_abi)
     {
-      if (regcache != NULL)
+      if (TYPE_LENGTH (valtype) < 16)
+       eltype = check_typedef (TYPE_TARGET_TYPE (valtype));
+      else
+       eltype = register_type (gdbarch, tdep->ppc_vr0_regnum);
+
+      nelt = TYPE_LENGTH (valtype) / TYPE_LENGTH (eltype);
+      for (i = 0; i < nelt; i++)
        {
-         int i;
-         for (i = 0; i < 2; i++)
-           {
-             gdb_byte regval[MAX_REGISTER_SIZE];
-             struct type *regtype =
-               register_type (gdbarch, tdep->ppc_fp0_regnum);
-             struct type *target_type;
-             target_type = check_typedef (TYPE_TARGET_TYPE (valtype));
-             if (writebuf != NULL)
-               {
-                 convert_typed_floating ((const bfd_byte *) writebuf +
-                                         i * TYPE_LENGTH (target_type), 
-                                         target_type, regval, regtype);
-                 regcache_cooked_write (regcache,
-                                         tdep->ppc_fp0_regnum + 1 + i,
-                                        regval);
-               }
-             if (readbuf != NULL)
-               {
-                 regcache_cooked_read (regcache,
-                                        tdep->ppc_fp0_regnum + 1 + i,
-                                        regval);
-                 convert_typed_floating (regval, regtype,
-                                         (bfd_byte *) readbuf +
-                                         i * TYPE_LENGTH (target_type),
-                                         target_type);
-               }
-           }
+         ok = ppc64_sysv_abi_return_value_base (gdbarch, eltype, regcache,
+                                                readbuf, writebuf, i);
+         gdb_assert (ok);
+
+         if (readbuf)
+           readbuf += TYPE_LENGTH (eltype);
+         if (writebuf)
+           writebuf += TYPE_LENGTH (eltype);
        }
       return RETURN_VALUE_REGISTER_CONVENTION;
     }
-  /* Big complex values get stored in f1:f4.  */
-  if (TYPE_CODE (valtype) == TYPE_CODE_COMPLEX && TYPE_LENGTH (valtype) == 32)
+
+  /* All pointers live in r3.  */
+  if (TYPE_CODE (valtype) == TYPE_CODE_PTR
+      || TYPE_CODE (valtype) == TYPE_CODE_REF)
     {
-      if (regcache != NULL)
-       {
-         int i;
-         for (i = 0; i < 4; i++)
-           {
-             if (writebuf != NULL)
-               regcache_cooked_write (regcache, tdep->ppc_fp0_regnum + 1 + i,
-                                      (const bfd_byte *) writebuf + i * 8);
-             if (readbuf != NULL)
-               regcache_cooked_read (regcache, tdep->ppc_fp0_regnum + 1 + i,
-                                     (bfd_byte *) readbuf + i * 8);
-           }
-       }
+      int regnum = tdep->ppc_gp0_regnum + 3;
+
+      if (writebuf != NULL)
+       regcache_cooked_write (regcache, regnum, writebuf);
+      if (readbuf != NULL)
+       regcache_cooked_read (regcache, regnum, readbuf);
       return RETURN_VALUE_REGISTER_CONVENTION;
     }
+
+  /* Small character arrays are returned, right justified, in r3.  */
+  if (TYPE_CODE (valtype) == TYPE_CODE_ARRAY
+      && TYPE_LENGTH (valtype) <= 8
+      && TYPE_CODE (TYPE_TARGET_TYPE (valtype)) == TYPE_CODE_INT
+      && TYPE_LENGTH (TYPE_TARGET_TYPE (valtype)) == 1)
+    {
+      int regnum = tdep->ppc_gp0_regnum + 3;
+      int offset = (register_size (gdbarch, regnum) - TYPE_LENGTH (valtype));
+
+      if (writebuf != NULL)
+       regcache_cooked_write_part (regcache, regnum,
+                                   offset, TYPE_LENGTH (valtype), writebuf);
+      if (readbuf != NULL)
+       regcache_cooked_read_part (regcache, regnum,
+                                  offset, TYPE_LENGTH (valtype), readbuf);
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+
+  /* Handle plain base types.  */
+  if (ppc64_sysv_abi_return_value_base (gdbarch, valtype, regcache,
+                                       readbuf, writebuf, 0))
+    return RETURN_VALUE_REGISTER_CONVENTION;
+
   return RETURN_VALUE_STRUCT_CONVENTION;
 }