PowerPC bugfixes (#520)
authorSamuel Holland <samuel@sholland.org>
Tue, 8 Oct 2019 10:57:28 +0000 (05:57 -0500)
committerAnthony Green <green@moxielogic.com>
Tue, 8 Oct 2019 10:57:28 +0000 (06:57 -0400)
* powerpc: Silence warnings about unused labels

* powerpc: Fix a couple of comments

* powerpc: Fix alignment after float structs

* powerpc: Don't pad rvalues copied from FP regs

* powerpc: Add missing check in struct alignment

* powerpc: Support homogeneous long double structs

src/powerpc/ffi.c
src/powerpc/ffi_linux64.c
src/powerpc/linux64_closure.S

index 7eb543e4b3b78976579d0dfa9c907944e2826ad8..94a11700422217e8726b0925051d2f310b08fdc0 100644 (file)
@@ -121,8 +121,9 @@ ffi_call_int (ffi_cif *cif,
 # endif
        /* The SYSV ABI returns a structure of up to 8 bytes in size
           left-padded in r3/r4, and the ELFv2 ABI similarly returns a
-          structure of up to 8 bytes in size left-padded in r3.  */
-       if (rsize <= 8)
+          structure of up to 8 bytes in size left-padded in r3. But
+          note that a structure of a single float is not paddded.  */
+       if (rsize <= 8 && (cif->flags & FLAG_RETURNS_FP) == 0)
          memcpy (rvalue, (char *) smst_buffer + 8 - rsize, rsize);
        else
 #endif
index 93a31f952fe83d2d608c7c97c05f6201f7a78bc1..4cf59a434cbf7fca39711cd57a65fc1e5fad3c23 100644 (file)
@@ -63,10 +63,30 @@ ffi_prep_types_linux64 (ffi_abi abi)
 
 
 static unsigned int
-discover_homogeneous_aggregate (const ffi_type *t, unsigned int *elnum)
+discover_homogeneous_aggregate (ffi_abi abi,
+                                const ffi_type *t,
+                                unsigned int *elnum)
 {
   switch (t->type)
     {
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+    case FFI_TYPE_LONGDOUBLE:
+      /* 64-bit long doubles are equivalent to doubles. */
+      if ((abi & FFI_LINUX_LONG_DOUBLE_128) == 0)
+        {
+          *elnum = 1;
+          return FFI_TYPE_DOUBLE;
+        }
+      /* IBM extended precision values use unaligned pairs
+         of FPRs, but according to the ABI must be considered
+         distinct from doubles. They are also limited to a
+         maximum of four members in a homogeneous aggregate. */
+      else
+        {
+          *elnum = 2;
+          return FFI_TYPE_LONGDOUBLE;
+        }
+#endif
     case FFI_TYPE_FLOAT:
     case FFI_TYPE_DOUBLE:
       *elnum = 1;
@@ -79,7 +99,7 @@ discover_homogeneous_aggregate (const ffi_type *t, unsigned int *elnum)
        while (*el)
          {
            unsigned int el_elt, el_elnum = 0;
-           el_elt = discover_homogeneous_aggregate (*el, &el_elnum);
+           el_elt = discover_homogeneous_aggregate (abi, *el, &el_elnum);
            if (el_elt == 0
                || (base_elt && base_elt != el_elt))
              return 0;
@@ -112,7 +132,7 @@ ffi_prep_cif_linux64_core (ffi_cif *cif)
   unsigned bytes;
   unsigned i, fparg_count = 0, intarg_count = 0;
   unsigned flags = cif->flags;
-  unsigned int elt, elnum;
+  unsigned elt, elnum, rtype;
 
 #if FFI_TYPE_LONGDOUBLE == FFI_TYPE_DOUBLE
   /* If compiled without long double support..  */
@@ -138,7 +158,11 @@ ffi_prep_cif_linux64_core (ffi_cif *cif)
 #endif
 
   /* Return value handling.  */
-  switch (cif->rtype->type)
+  rtype = cif->rtype->type;
+#if _CALL_ELF == 2
+homogeneous:
+#endif
+  switch (rtype)
     {
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
     case FFI_TYPE_LONGDOUBLE:
@@ -164,19 +188,18 @@ ffi_prep_cif_linux64_core (ffi_cif *cif)
 
     case FFI_TYPE_STRUCT:
 #if _CALL_ELF == 2
-      elt = discover_homogeneous_aggregate (cif->rtype, &elnum);
+      elt = discover_homogeneous_aggregate (cif->abi, cif->rtype, &elnum);
       if (elt)
-       {
-         if (elt == FFI_TYPE_DOUBLE)
-           flags |= FLAG_RETURNS_64BITS;
-         flags |= FLAG_RETURNS_FP | FLAG_RETURNS_SMST;
-         break;
-       }
+        {
+          flags |= FLAG_RETURNS_SMST;
+          rtype = elt;
+          goto homogeneous;
+        }
       if (cif->rtype->size <= 16)
-       {
-         flags |= FLAG_RETURNS_SMST;
-         break;
-       }
+        {
+          flags |= FLAG_RETURNS_SMST;
+          break;
+        }
 #endif
       intarg_count++;
       flags |= FLAG_RETVAL_REFERENCE;
@@ -224,7 +247,7 @@ ffi_prep_cif_linux64_core (ffi_cif *cif)
                intarg_count = FFI_ALIGN (intarg_count, align);
            }
          intarg_count += ((*ptr)->size + 7) / 8;
-         elt = discover_homogeneous_aggregate (*ptr, &elnum);
+         elt = discover_homogeneous_aggregate (cif->abi, *ptr, &elnum);
          if (elt)
            {
              fparg_count += elnum;
@@ -391,7 +414,7 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
   valp rest;
   valp next_arg;
 
-  /* 'fpr_base' points at the space for fpr3, and grows upwards as
+  /* 'fpr_base' points at the space for f1, and grows upwards as
      we use FPR registers.  */
   valp fpr_base;
   unsigned int fparg_count;
@@ -492,7 +515,9 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
          /* Fall through.  */
 #endif
        case FFI_TYPE_DOUBLE:
+#if _CALL_ELF != 2
        do_double:
+#endif
          double_tmp = **p_argv.d;
          if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
            {
@@ -511,7 +536,9 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
          break;
 
        case FFI_TYPE_FLOAT:
+#if _CALL_ELF != 2
        do_float:
+#endif
          double_tmp = **p_argv.f;
          if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
            {
@@ -548,9 +575,13 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
              if (align > 16)
                align = 16;
              if (align > 1)
-               next_arg.p = FFI_ALIGN (next_arg.p, align);
+                {
+                  next_arg.p = FFI_ALIGN (next_arg.p, align);
+                  if (next_arg.ul == gpr_end.ul)
+                    next_arg.ul = rest.ul;
+                }
            }
-         elt = discover_homogeneous_aggregate (*ptr, &elnum);
+         elt = discover_homogeneous_aggregate (ecif->cif->abi, *ptr, &elnum);
          if (elt)
            {
 #if _CALL_ELF == 2
@@ -576,11 +607,9 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
                      fparg_count++;
                    }
                  while (--elnum != 0);
-                 if ((next_arg.p & 3) != 0)
-                   {
-                     if (++next_arg.f == gpr_end.f)
-                       next_arg.f = rest.f;
-                   }
+                 if ((next_arg.p & 7) != 0)
+                    if (++next_arg.f == gpr_end.f)
+                      next_arg.f = rest.f;
                }
              else
                do
@@ -813,7 +842,7 @@ ffi_closure_helper_LINUX64 (ffi_cif *cif,
              if (align > 1)
                pst = (unsigned long *) FFI_ALIGN ((size_t) pst, align);
            }
-         elt = discover_homogeneous_aggregate (arg_types[i], &elnum);
+         elt = discover_homogeneous_aggregate (cif->abi, arg_types[i], &elnum);
          if (elt)
            {
 #if _CALL_ELF == 2
@@ -915,7 +944,9 @@ ffi_closure_helper_LINUX64 (ffi_cif *cif,
          /* Fall through.  */
 #endif
        case FFI_TYPE_DOUBLE:
+#if _CALL_ELF != 2
        do_double:
+#endif
          /* On the outgoing stack all values are aligned to 8 */
          /* there are 13 64bit floating point registers */
 
@@ -930,7 +961,9 @@ ffi_closure_helper_LINUX64 (ffi_cif *cif,
          break;
 
        case FFI_TYPE_FLOAT:
+#if _CALL_ELF != 2
        do_float:
+#endif
          if (pfr < end_pfr && i < nfixedargs)
            {
              /* Float values are stored as doubles in the
index 6487d2a2970a11a6d71d8b2759a0b490d194ba87..7f2a214df01e0afa075c2c48966db5e8c6621906 100644 (file)
@@ -143,7 +143,7 @@ ffi_closure_LINUX64:
        stfd    %f12, -104+(11*8)(%r1)
        stfd    %f13, -104+(12*8)(%r1)
 
-       # load up the pointer to the saved fpr registers */
+       # load up the pointer to the saved fpr registers
        addi    %r8, %r1, -104
 
        # load up the pointer to the result storage