Blob Blame History Raw
commit e765b44c3853ed228506fc22c276becd63198238
Author: Ulrich Weigand <ulrich.weigand@de.ibm.com>
Date:   Tue Feb 4 18:24:42 2014 +0100

    Refactor ppc64 function call and return value handling
    
    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.

Index: gdb-7.6.1/gdb/ppc-sysv-tdep.c
===================================================================
--- gdb-7.6.1.orig/gdb/ppc-sysv-tdep.c
+++ gdb-7.6.1/gdb/ppc-sysv-tdep.c
@@ -609,8 +609,7 @@ ppc_sysv_abi_push_dummy_call (struct gdb
   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,
@@ -1101,80 +1100,287 @@ convert_code_addr_to_desc_addr (CORE_ADD
   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.
 
-   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.  */
+   If ALIGN is nonzero, it specifies the minimum alignment required
+   for the on-stack copy of the 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_val (struct gdbarch *gdbarch,
+			 const bfd_byte *val, int len, int align,
+			 struct ppc64_sysv_argpos *argpos)
 {
-  gdb_byte regval[MAX_REGISTER_SIZE];
-  const gdb_byte *p;
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int offset = 0;
 
-  if (TYPE_LENGTH (type) <= 8)
+  /* 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)
     {
-      /* Version 1.7 of the 64-bit PowerPC ELF ABI says:
+      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_integer (struct gdbarch *gdbarch, ULONGEST val,
+			     struct ppc64_sysv_argpos *argpos)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  gdb_byte buf[MAX_REGISTER_SIZE];
 
-	 "Single precision floating point values are mapped to
-	 the first word in a single doubleword."
+  if (argpos->regcache)
+    store_unsigned_integer (buf, tdep->wordsize, byte_order, val);
+  ppc64_sysv_abi_push_val (gdbarch, buf, tdep->wordsize, 0, argpos);
+}
 
-	 And version 1.9 says:
+/* 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.  */
 
-	 "Single precision floating point values are mapped to
-	 the second word in a single doubleword."
+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;
 
-	 GDB then writes single precision floating point values
-	 at both words in a doubleword, to support both ABIs.  */
-      if (TYPE_LENGTH (type) == 4)
+  if (TYPE_LENGTH (type) <= 8
+      && TYPE_CODE (type) == TYPE_CODE_FLT)
+    {
+      /* Floats and doubles go in f1 .. f13.  32-bit floats are converted
+ 	 to double first.  */
+      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;
+	  struct type *regtype = register_type (gdbarch, regnum);
+	  gdb_byte regval[MAX_REGISTER_SIZE];
+
+	  convert_typed_floating (val, type, regval, regtype);
+	  regcache_cooked_write (argpos->regcache, regnum, regval);
 	}
-      else
-	p = val;
 
-      /* Write value in the stack's parameter save area.  */
-      write_memory (gparam, p, 8);
+      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);
 
-      /* Floats and Doubles go in f1 .. f13.  They also consume a left aligned
-	 GREG, and can end up in memory.  */
-      if (freg <= 13)
+	  regcache_cooked_write_part (argpos->regcache, regnum,
+				      offset, TYPE_LENGTH (type), val);
+	}
+
+      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)
 	{
-	  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);
+	  if (argpos->freg <= 12)
+	    regcache_cooked_write (argpos->regcache, regnum + 1, val + 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;
+
+      if (argpos->regcache && argpos->freg <= 12)
+	{
+	  int regnum = tdep->ppc_fp0_regnum + argpos->freg;
+
+	  regcache_cooked_write (argpos->regcache, regnum, val);
+	  regcache_cooked_write (argpos->regcache, regnum + 1, val + 8);
+	}
+
+      argpos->freg += 2;
+    }
+}
+
+/* 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)
+    {
+      /* 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)
+	{
+	  /* 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)
-	regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + greg, regval);
+
+      ppc64_sysv_abi_push_integer (gdbarch, word, argpos);
     }
   else
     {
-      /* IBM long double stored in two doublewords of the
-	 parameter save area and corresponding registers.  */
-      if (!tdep->soft_float && freg <= 13)
-	{
-	  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);
-	}
-      if (greg <= 10)
-	{
-	  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);
+      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)
+	{
+	  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));
     }
 }
 
@@ -1236,20 +1442,11 @@ ppc64_sysv_abi_push_dummy_call (struct g
   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)
 	{
@@ -1257,19 +1454,21 @@ ppc64_sysv_abi_push_dummy_call (struct g
 	     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
@@ -1278,14 +1477,7 @@ ppc64_sysv_abi_push_dummy_call (struct g
          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++)
 	{
@@ -1293,432 +1485,54 @@ ppc64_sysv_abi_push_dummy_call (struct g
 	  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 (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)
+	  if (TYPE_CODE (type) == TYPE_CODE_COMPLEX)
 	    {
-	      /* 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);
-		    }
+		  ppc64_sysv_abi_push_param (gdbarch, eltype, elval, &argpos);
 		}
 	    }
-	  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);
-		}
-	      greg++;
-	      gparam = align_up (gparam + TYPE_LENGTH (type), tdep->wordsize);
-	    }
 	  else
 	    {
-	      ssize_t byte;
-	      for (byte = 0; byte < TYPE_LENGTH (type);
-		   byte += tdep->wordsize)
-		{
-		  if (write_pass && greg <= 10)
-		    {
-		      gdb_byte regval[MAX_REGISTER_SIZE];
-		      ssize_t 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.  */
-		  ssize_t 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;
 	}
     }
 
@@ -1754,271 +1568,252 @@ ppc64_sysv_abi_push_dummy_call (struct g
   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.
+   This routine also handles components of aggregate return types;
+   INDEX describes which part of the aggregate is to be handled.
 
-   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)
+   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));
+  /* 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;
 
-  /* Floats and doubles in F1.  */
-  if (TYPE_CODE (valtype) == TYPE_CODE_FLT && TYPE_LENGTH (valtype) <= 8)
+      if (writebuf != NULL)
+	{
+	  /* Be careful to sign extend the value.  */
+	  regcache_cooked_write_unsigned (regcache, regnum,
+					  unpack_long (valtype, writebuf));
+	}
+      if (readbuf != NULL)
+	{
+	  /* 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, regnum, &regval);
+	  store_unsigned_integer (readbuf, TYPE_LENGTH (valtype),
+				  gdbarch_byte_order (gdbarch), regval);
+	}
+      return 1;
+    }
+
+  /* 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)
     {
+      int regnum = tdep->ppc_fp0_regnum + 1 + index;
+      struct type *regtype = register_type (gdbarch, regnum);
       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);
+	  regcache_cooked_write (regcache, regnum, regval);
 	}
       if (readbuf != NULL)
 	{
-	  regcache_cooked_read (regcache, tdep->ppc_fp0_regnum + 1, regval);
+	  regcache_cooked_read (regcache, regnum, regval);
 	  convert_typed_floating (regval, regtype, readbuf, valtype);
 	}
-      return RETURN_VALUE_REGISTER_CONVENTION;
+      return 1;
     }
-  if (TYPE_CODE (valtype) == TYPE_CODE_DECFLOAT)
-    return get_decimal_float_return_value (gdbarch, valtype, regcache, readbuf,
-					   writebuf);
-  /* Integers in 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)
+
+  /* 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 (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;
+    }
+
+  /* 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)
 	{
-	  /* Be careful to sign extend the value.  */
-	  regcache_cooked_write_unsigned (regcache, tdep->ppc_gp0_regnum + 3,
-					  unpack_long (valtype, writebuf));
+	  regcache_cooked_write (regcache, regnum, writebuf);
+	  regcache_cooked_write (regcache, regnum + 1, writebuf + 8);
 	}
       if (readbuf != NULL)
 	{
-	  /* Extract the integer from r3.  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 (regcache, regnum, readbuf);
+	  regcache_cooked_read (regcache, regnum + 1, readbuf + 8);
 	}
-      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)
+
+  /* 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)
     {
-      /* All pointers live in r3.  */
+      int regnum = tdep->ppc_fp0_regnum + 2 + 2 * index;
+
       if (writebuf != NULL)
-	regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + 3, writebuf);
+	{
+	  regcache_cooked_write (regcache, regnum, writebuf);
+	  regcache_cooked_write (regcache, regnum + 1, writebuf + 8);
+	}
       if (readbuf != NULL)
-	regcache_cooked_read (regcache, tdep->ppc_gp0_regnum + 3, readbuf);
-      return RETURN_VALUE_REGISTER_CONVENTION;
+	{
+	  regcache_cooked_read (regcache, regnum, readbuf);
+	  regcache_cooked_read (regcache, regnum + 1, readbuf + 8);
+	}
+      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)
+
+  /* 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)
     {
-      struct type *eltype = check_typedef (TYPE_TARGET_TYPE (valtype));
-      int i, nelt = TYPE_LENGTH (valtype) / TYPE_LENGTH (eltype);
+      int regnum = tdep->ppc_vr0_regnum + 2 + index;
 
-      for (i = 0; i < nelt; i++)
-	{
-	  int offset = i * TYPE_LENGTH (eltype);
+      if (writebuf != NULL)
+	regcache_cooked_write (regcache, regnum, writebuf);
+      if (readbuf != NULL)
+	regcache_cooked_read (regcache, regnum, readbuf);
+      return 1;
+    }
 
-	  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);
+  return 0;
+}
 
-	      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;
+/* The 64 bit ABI return value convention.
 
-	      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);
-		}
-	    }
-	}
+   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).
 
-      return RETURN_VALUE_REGISTER_CONVENTION;
-    }
-  /* 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)
+   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)
     {
-      int n_regs = TYPE_LENGTH (valtype) / 16;
-      int i;
+      eltype = check_typedef (TYPE_TARGET_TYPE (valtype));
 
-      for (i = 0; i < n_regs; i++)
+      for (i = 0; i < 2; i++)
 	{
-	  int offset = i * 16;
-	  int regnum = tdep->ppc_vr0_regnum + 2 + i;
+	  ok = ppc64_sysv_abi_return_value_base (gdbarch, eltype, regcache,
+						 readbuf, writebuf, i);
+	  gdb_assert (ok);
 
-	  if (writebuf != NULL)
-	    regcache_cooked_write (regcache, regnum, writebuf + offset);
-	  if (readbuf != NULL)
-	    regcache_cooked_read (regcache, regnum, readbuf + offset);
+	  if (readbuf)
+	    readbuf += TYPE_LENGTH (eltype);
+	  if (writebuf)
+	    writebuf += TYPE_LENGTH (eltype);
 	}
-
       return RETURN_VALUE_REGISTER_CONVENTION;
     }
-  /* Array type has more than one use.  */
-  if (TYPE_CODE (valtype) == TYPE_CODE_ARRAY)
-    {
-      /* 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;
-        }
-    }
-  /* 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))
+
+  /* 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 (writebuf || readbuf != 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 < 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))
+
+  /* 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 < 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);
-		}
-	    }
-	}
+      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;
     }
-  /* Big complex values get stored in f1:f4.  */
-  if (TYPE_CODE (valtype) == TYPE_CODE_COMPLEX && TYPE_LENGTH (valtype) == 32)
+
+  /* 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)
     {
-      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;
+      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;
 }