]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
re PR libffi/31937 (libffi doesn't support ppc without FPU)
authorAndreas Tobler <a.tobler@schweiz.org>
Sat, 1 Dec 2007 21:00:04 +0000 (21:00 +0000)
committerAndreas Tobler <andreast@gcc.gnu.org>
Sat, 1 Dec 2007 21:00:04 +0000 (22:00 +0100)
2007-12-01  Andreas Tobler  <a.tobler@schweiz.org>

PR libffi/31937
* src/powerpc/ffitarget.h: Introduce new ABI FFI_LINUX_SOFT_FLOAT.
Add local FFI_TYPE_UINT128 to handle soft-float long-double-128.
* src/powerpc/ffi.c: Distinguish between __NO_FPRS__ and not and
set the NUM_FPR_ARG_REGISTERS according to.
Add support for potential soft-float support under hard-float
architecture.
(ffi_prep_args_SYSV): Set NUM_FPR_ARG_REGISTERS to 0 in case of
FFI_LINUX_SOFT_FLOAT, handle float, doubles and long-doubles according
to the FFI_LINUX_SOFT_FLOAT ABI.
(ffi_prep_cif_machdep): Likewise.
(ffi_closure_helper_SYSV): Likewise.
* src/powerpc/ppc_closure.S: Make sure not to store float/double
on archs where __NO_FPRS__ is true.
Add FFI_TYPE_UINT128 support.
* src/powerpc/sysv.S: Add support for soft-float long-double-128.
Adjust copyright notice.

From-SVN: r130559

libffi/ChangeLog
libffi/src/powerpc/ffi.c
libffi/src/powerpc/ffitarget.h
libffi/src/powerpc/ppc_closure.S
libffi/src/powerpc/sysv.S

index 689bc452a7a20666511761a623f3dadf66b56105..91609e3a79b0713f73e9134f8b5a8fc61dc2169e 100644 (file)
@@ -1,3 +1,23 @@
+2007-12-01  Andreas Tobler  <a.tobler@schweiz.org>
+
+       PR libffi/31937
+       * src/powerpc/ffitarget.h: Introduce new ABI FFI_LINUX_SOFT_FLOAT.
+       Add local FFI_TYPE_UINT128 to handle soft-float long-double-128.
+       * src/powerpc/ffi.c: Distinguish between __NO_FPRS__ and not and
+       set the NUM_FPR_ARG_REGISTERS according to.
+       Add support for potential soft-float support under hard-float
+       architecture.
+       (ffi_prep_args_SYSV): Set NUM_FPR_ARG_REGISTERS to 0 in case of
+       FFI_LINUX_SOFT_FLOAT, handle float, doubles and long-doubles according
+       to the FFI_LINUX_SOFT_FLOAT ABI.
+       (ffi_prep_cif_machdep): Likewise.
+       (ffi_closure_helper_SYSV): Likewise.
+       * src/powerpc/ppc_closure.S: Make sure not to store float/double
+       on archs where __NO_FPRS__ is true.
+       Add FFI_TYPE_UINT128 support.
+       * src/powerpc/sysv.S: Add support for soft-float long-double-128.
+       Adjust copyright notice.
+
 2007-11-25  Andreas Tobler  <a.tobler@schweiz.org>
 
        * src/closures.c: Move defintion of MAYBE_UNUSED from here to ...
index eaa4c869be814be4178a1a6636717065fe3dd472..e6b869f8737c9c287cee20ca5f5a366c0c0156ba 100644 (file)
@@ -50,10 +50,13 @@ enum {
 };
 
 /* About the SYSV ABI.  */
-enum {
-  NUM_GPR_ARG_REGISTERS = 8,
-  NUM_FPR_ARG_REGISTERS = 8
-};
+unsigned int NUM_GPR_ARG_REGISTERS = 8;
+#ifndef __NO_FPRS__
+unsigned int NUM_FPR_ARG_REGISTERS = 8;
+#else
+unsigned int NUM_FPR_ARG_REGISTERS = 0;
+#endif
+
 enum { ASM_NEEDS_REGISTERS = 4 };
 
 /* ffi_prep_args_SYSV is called by the assembly routine once stack space
@@ -116,7 +119,7 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
   /* 'next_arg' grows up as we put parameters in it.  */
   valp next_arg;
 
-  int i;
+  int i, ii MAYBE_UNUSED;
   ffi_type **ptr;
   double double_tmp;
   union {
@@ -134,6 +137,9 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
   size_t struct_copy_size;
   unsigned gprvalue;
 
+  if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT)
+    NUM_FPR_ARG_REGISTERS = 0;
+
   stacktop.c = (char *) stack + bytes;
   gpr_base.u = stacktop.u - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS;
   intarg_count = 0;
@@ -165,6 +171,9 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
       switch ((*ptr)->type)
        {
        case FFI_TYPE_FLOAT:
+         /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32.  */
+         if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT)
+           goto soft_float_prep;
          double_tmp = **p_argv.f;
          if (fparg_count >= NUM_FPR_ARG_REGISTERS)
            {
@@ -178,6 +187,9 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
          break;
 
        case FFI_TYPE_DOUBLE:
+         /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64.  */
+         if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT)
+           goto soft_double_prep;
          double_tmp = **p_argv.d;
 
          if (fparg_count >= NUM_FPR_ARG_REGISTERS)
@@ -199,38 +211,75 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
 
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
        case FFI_TYPE_LONGDOUBLE:
-         if (ecif->cif->abi != FFI_LINUX)
+         if ((ecif->cif->abi != FFI_LINUX)
+               && (ecif->cif->abi != FFI_LINUX_SOFT_FLOAT))
            goto do_struct;
-         double_tmp = (*p_argv.d)[0];
-
-         if (fparg_count >= NUM_FPR_ARG_REGISTERS - 1)
+         /* The soft float ABI for long doubles works like this,
+            a long double is passed in four consecutive gprs if available.
+            A maximum of 2 long doubles can be passed in gprs.
+            If we do not have 4 gprs left, the long double is passed on the
+            stack, 4-byte aligned.  */
+         if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT)
            {
-             if (intarg_count >= NUM_GPR_ARG_REGISTERS
-                 && intarg_count % 2 != 0)
+             unsigned int int_tmp = (*p_argv.ui)[0];
+             if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3)
                {
-                 intarg_count++;
+                 if (intarg_count < NUM_GPR_ARG_REGISTERS)
+                   intarg_count += NUM_GPR_ARG_REGISTERS - intarg_count;
+                 *next_arg.u = int_tmp;
                  next_arg.u++;
+                 for (ii = 1; ii < 4; ii++)
+                   {
+                     int_tmp = (*p_argv.ui)[ii];
+                     *next_arg.u = int_tmp;
+                     next_arg.u++;
+                   }
                }
-             *next_arg.d = double_tmp;
-             next_arg.u += 2;
-             double_tmp = (*p_argv.d)[1];
-             *next_arg.d = double_tmp;
-             next_arg.u += 2;
+             else
+               {
+                 *gpr_base.u++ = int_tmp;
+                 for (ii = 1; ii < 4; ii++)
+                   {
+                     int_tmp = (*p_argv.ui)[ii];
+                     *gpr_base.u++ = int_tmp;
+                   }
+               }
+             intarg_count +=4;
            }
          else
            {
-             *fpr_base.d++ = double_tmp;
-             double_tmp = (*p_argv.d)[1];
-             *fpr_base.d++ = double_tmp;
-           }
+             double_tmp = (*p_argv.d)[0];
 
-         fparg_count += 2;
-         FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
+             if (fparg_count >= NUM_FPR_ARG_REGISTERS - 1)
+               {
+                 if (intarg_count >= NUM_GPR_ARG_REGISTERS
+                     && intarg_count % 2 != 0)
+                   {
+                     intarg_count++;
+                     next_arg.u++;
+                   }
+                 *next_arg.d = double_tmp;
+                 next_arg.u += 2;
+                 double_tmp = (*p_argv.d)[1];
+                 *next_arg.d = double_tmp;
+                 next_arg.u += 2;
+               }
+             else
+               {
+                 *fpr_base.d++ = double_tmp;
+                 double_tmp = (*p_argv.d)[1];
+                 *fpr_base.d++ = double_tmp;
+               }
+
+             fparg_count += 2;
+             FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
+           }
          break;
 #endif
 
        case FFI_TYPE_UINT64:
        case FFI_TYPE_SINT64:
+       soft_double_prep:
          if (intarg_count == NUM_GPR_ARG_REGISTERS-1)
            intarg_count++;
          if (intarg_count >= NUM_GPR_ARG_REGISTERS)
@@ -293,6 +342,8 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
        case FFI_TYPE_UINT32:
        case FFI_TYPE_SINT32:
        case FFI_TYPE_POINTER:
+       soft_float_prep:
+
          gprvalue = **p_argv.ui;
 
        putgpr:
@@ -546,6 +597,9 @@ ffi_prep_cif_machdep (ffi_cif *cif)
   unsigned type = cif->rtype->type;
   unsigned size = cif->rtype->size;
 
+  if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+    NUM_FPR_ARG_REGISTERS = 0;
+
   if (cif->abi != FFI_LINUX64)
     {
       /* All the machine-independent calculation of cif->bytes will be wrong.
@@ -582,14 +636,16 @@ ffi_prep_cif_machdep (ffi_cif *cif)
      For LINUX64:
      - integer values in gpr3;
      - Structures/Unions by reference;
-     - Single/double FP values in fpr1, long double in fpr1,fpr2.  */
+     - Single/double FP values in fpr1, long double in fpr1,fpr2.
+     - soft-float float/doubles are treated as UINT32/UINT64 respectivley.
+     - soft-float long doubles are returned in gpr3-gpr6.  */
   switch (type)
     {
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
     case FFI_TYPE_LONGDOUBLE:
-      if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64)
+      if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64
+       && cif->abi != FFI_LINUX_SOFT_FLOAT)
        goto byref;
-
       flags |= FLAG_RETURNS_128BITS;
       /* Fall through.  */
 #endif
@@ -597,7 +653,9 @@ ffi_prep_cif_machdep (ffi_cif *cif)
       flags |= FLAG_RETURNS_64BITS;
       /* Fall through.  */
     case FFI_TYPE_FLOAT:
-      flags |= FLAG_RETURNS_FP;
+      /* With FFI_LINUX_SOFT_FLOAT no fp registers are used.  */
+      if (cif->abi != FFI_LINUX_SOFT_FLOAT)
+       flags |= FLAG_RETURNS_FP;
       break;
 
     case FFI_TYPE_UINT64:
@@ -660,18 +718,36 @@ ffi_prep_cif_machdep (ffi_cif *cif)
        switch ((*ptr)->type)
          {
          case FFI_TYPE_FLOAT:
+           /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32.  */
+           if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+             goto soft_float_cif;
            fparg_count++;
            /* floating singles are not 8-aligned on stack */
            break;
 
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
          case FFI_TYPE_LONGDOUBLE:
-           if (cif->abi != FFI_LINUX)
+           if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT)
              goto do_struct;
-           fparg_count++;
+           if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+             {
+               if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3
+                 || intarg_count < NUM_GPR_ARG_REGISTERS)
+                 /* A long double in FFI_LINUX_SOFT_FLOAT can use only
+                    a set of four consecutive gprs. If we have not enough,
+                    we have to adjust the intarg_count value.  */
+                 intarg_count += NUM_GPR_ARG_REGISTERS - intarg_count;
+               intarg_count += 4;
+               break;
+             }
+           else
+             fparg_count++;
            /* Fall thru */
 #endif
          case FFI_TYPE_DOUBLE:
+           /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64.  */
+           if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+             goto soft_double_cif;
            fparg_count++;
            /* If this FP arg is going on the stack, it must be
               8-byte-aligned.  */
@@ -683,6 +759,7 @@ ffi_prep_cif_machdep (ffi_cif *cif)
 
          case FFI_TYPE_UINT64:
          case FFI_TYPE_SINT64:
+         soft_double_cif:
            /* 'long long' arguments are passed as two words, but
               either both words must fit in registers or both go
               on the stack.  If they go on the stack, they must
@@ -710,6 +787,7 @@ ffi_prep_cif_machdep (ffi_cif *cif)
            /* Fall through (allocate space for the pointer).  */
 
          default:
+         soft_float_cif:
            /* Everything else is passed as a 4-byte word in a GPR, either
               the object itself or a pointer to it.  */
            intarg_count++;
@@ -723,8 +801,13 @@ ffi_prep_cif_machdep (ffi_cif *cif)
          {
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
          case FFI_TYPE_LONGDOUBLE:
-           fparg_count += 2;
-           intarg_count += 2;
+           if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+             intarg_count += 4;
+           else
+             {
+               fparg_count += 2;
+               intarg_count += 2;
+             }
            break;
 #endif
          case FFI_TYPE_FLOAT:
@@ -818,6 +901,7 @@ ffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue)
     case FFI_SYSV:
     case FFI_GCC_SYSV:
     case FFI_LINUX:
+    case FFI_LINUX_SOFT_FLOAT:
       ffi_call_SYSV (&ecif, -cif->bytes, cif->flags, ecif.rvalue, fn);
       break;
 #else
@@ -942,7 +1026,7 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue,
        && !((cif->abi == FFI_SYSV) && (size <= 8)))
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
       || (cif->rtype->type == FFI_TYPE_LONGDOUBLE
-         && cif->abi != FFI_LINUX)
+         && cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT)
 #endif
       )
     {
@@ -995,6 +1079,7 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue,
        case FFI_TYPE_SINT32:
        case FFI_TYPE_UINT32:
        case FFI_TYPE_POINTER:
+       soft_float_closure:
          /* there are 8 gpr registers used to pass values */
          if (ng < 8)
            {
@@ -1030,6 +1115,7 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue,
 
        case FFI_TYPE_SINT64:
        case FFI_TYPE_UINT64:
+       soft_double_closure:
          /* passing long long ints are complex, they must
           * be passed in suitable register pairs such as
           * (r3,r4) or (r5,r6) or (r6,r7), or (r7,r8) or (r9,r10)
@@ -1061,6 +1147,9 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue,
          break;
 
        case FFI_TYPE_FLOAT:
+         /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32.  */
+         if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+           goto soft_float_closure;
          /* unfortunately float values are stored as doubles
           * in the ffi_closure_SYSV code (since we don't check
           * the type in that routine).
@@ -1089,6 +1178,9 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue,
          break;
 
        case FFI_TYPE_DOUBLE:
+         /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64.  */
+         if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+           goto soft_double_closure;
          /* On the outgoing stack all values are aligned to 8 */
          /* there are 8 64bit floating point registers */
 
@@ -1109,9 +1201,24 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue,
 
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
        case FFI_TYPE_LONGDOUBLE:
-         if (cif->abi != FFI_LINUX)
+         if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT)
            goto do_struct;
-
+         if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+           { /* Test if for the whole long double, 4 gprs are available.
+                otherwise the stuff ends up on the stack.  */
+             if (ng < 5)
+               {
+                 avalue[i] = pgr;
+                 pgr += 4;
+                 ng += 4;
+               }
+             else
+               {
+                 avalue[i] = pst;
+                 pst += 4;
+               }
+             break;
+           }
          if (nf < 7)
            {
              avalue[i] = pfr;
@@ -1147,10 +1254,34 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue,
     return FFI_SYSV_TYPE_SMALL_STRUCT + size;
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
   else if (cif->rtype->type == FFI_TYPE_LONGDOUBLE
-          && cif->abi != FFI_LINUX)
+          && cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT)
     return FFI_TYPE_STRUCT;
 #endif
-  return cif->rtype->type;
+  /* With FFI_LINUX_SOFT_FLOAT floats and doubles are handled like UINT32
+     respectivley UINT64.  */
+  if (cif->abi == FFI_LINUX_SOFT_FLOAT)
+    {
+      switch (cif->rtype->type)
+       {
+       case FFI_TYPE_FLOAT:
+         return FFI_TYPE_UINT32;
+         break;
+       case FFI_TYPE_DOUBLE:
+         return FFI_TYPE_UINT64;
+         break;
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+       case FFI_TYPE_LONGDOUBLE:
+         return FFI_TYPE_UINT128;
+         break;
+#endif
+       default:
+         return cif->rtype->type;
+       }
+    }
+  else
+    {
+      return cif->rtype->type;
+    }
 }
 
 int FFI_HIDDEN ffi_closure_helper_LINUX64 (ffi_closure *, void *,
index e7f62950371aa8454e976b0b08919e790b1f07f5..e3fa30be6b553e61140a404b0a9f0eede5636d25 100644 (file)
@@ -1,5 +1,6 @@
 /* -----------------------------------------------------------------*-C-*-
    ffitarget.h - Copyright (c) 1996-2003  Red Hat, Inc.
+   Copyright (C) 2007 Free Software Foundation, Inc
    Target configuration macros for PowerPC.
 
    Permission is hereby granted, free of charge, to any person obtaining
@@ -44,13 +45,18 @@ typedef enum ffi_abi {
   FFI_GCC_SYSV,
   FFI_LINUX64,
   FFI_LINUX,
+  FFI_LINUX_SOFT_FLOAT,
 # ifdef POWERPC64
   FFI_DEFAULT_ABI = FFI_LINUX64,
 # else
-#  if __LDBL_MANT_DIG__ == 106
+#  if (!defined(__NO_FPRS__) && (__LDBL_MANT_DIG__ == 106))
   FFI_DEFAULT_ABI = FFI_LINUX,
 #  else
+#   ifdef __NO_FPRS__
+  FFI_DEFAULT_ABI = FFI_LINUX_SOFT_FLOAT,
+#   else
   FFI_DEFAULT_ABI = FFI_GCC_SYSV,
+#   endif
 #  endif
 # endif
 #endif
@@ -83,8 +89,14 @@ typedef enum ffi_abi {
 #define FFI_CLOSURES 1
 #define FFI_NATIVE_RAW_API 0
 
+/* For additional types like the below, take care about the order in
+   ppc_closures.S. They must follow after the FFI_TYPE_LAST.  */
+
+/* Needed for soft-float long-double-128 support.  */
+#define FFI_TYPE_UINT128 (FFI_TYPE_LAST + 1)
+
 /* Needed for FFI_SYSV small structure returns.  */
-#define FFI_SYSV_TYPE_SMALL_STRUCT  (FFI_TYPE_LAST)
+#define FFI_SYSV_TYPE_SMALL_STRUCT (FFI_TYPE_LAST + 2)
 
 #if defined(POWERPC64) || defined(POWERPC_AIX)
 #define FFI_TRAMPOLINE_SIZE 24
index 356a0e326208b9edf80396a62d1d463f9cc4c7fd..c9f5742f5aa33fe72124b71c277839f9e718cdaf 100644 (file)
@@ -28,6 +28,7 @@ ENTRY(ffi_closure_SYSV)
        stw   %r9, 40(%r1)
        stw   %r10,44(%r1)
 
+#ifndef __NO_FPRS__
        # next save fpr 1 to fpr 8 (aligned to 8)
        stfd  %f1, 48(%r1)
        stfd  %f2, 56(%r1)
@@ -37,6 +38,7 @@ ENTRY(ffi_closure_SYSV)
        stfd  %f6, 88(%r1)
        stfd  %f7, 96(%r1)
        stfd  %f8, 104(%r1)
+#endif
 
        # set up registers for the routine that actually does the work
        # get the context pointer from the trampoline
@@ -171,6 +173,12 @@ ENTRY(ffi_closure_SYSV)
        addi %r1,%r1,144
        blr
 
+# case FFI_TYPE_UINT128
+       lwz %r3,112+0(%r1)
+       lwz %r4,112+4(%r1)
+       lwz %r5,112+8(%r1)
+       bl .Luint128
+
 # The return types below are only used when the ABI type is FFI_SYSV.
 # case FFI_SYSV_TYPE_SMALL_STRUCT + 1. One byte struct.
        lbz %r3,112+0(%r1)
@@ -230,6 +238,12 @@ ENTRY(ffi_closure_SYSV)
        addi %r1,%r1,144
        blr
 
+.Luint128:
+       lwz %r6,112+12(%r1)
+       mtlr %r0
+       addi %r1,%r1,144
+       blr
+
 END(ffi_closure_SYSV)
 
        .section        ".eh_frame",EH_FRAME_FLAGS,@progbits
index 9682016d2fa3628a4425a22dd1fe4ffa09381f30..21367145eb954d0faa3bff86b6aafd3f1cdd392b 100644 (file)
@@ -1,5 +1,6 @@
 /* -----------------------------------------------------------------------
-   sysv.h - Copyright (c) 1998 Geoffrey Keating
+   sysv.S - Copyright (c) 1998 Geoffrey Keating
+   Copyright (C) 2007 Free Software Foundation, Inc
 
    PowerPC Assembly glue.
 
@@ -98,13 +99,17 @@ ENTRY(ffi_call_SYSV)
        bctrl
 
        /* Now, deal with the return value.  */
-       mtcrf   0x01,%r31
+       mtcrf   0x01,%r31 /* cr7  */
        bt-     31,L(small_struct_return_value)
        bt-     30,L(done_return_value)
        bt-     29,L(fp_return_value)
        stw     %r3,0(%r30)
        bf+     28,L(done_return_value)
        stw     %r4,4(%r30)
+       mtcrf   0x02,%r31 /* cr6  */
+       bf      27,L(done_return_value)
+       stw     %r5,8(%r30)
+       stw     %r6,12(%r30)
        /* Fall through...  */
 
 L(done_return_value):