summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSamuel Holland <samuel@sholland.org>2019-10-08 05:57:28 -0500
committerAnthony Green <green@moxielogic.com>2019-10-08 06:57:28 -0400
commitea9b6639c69cbffeacd1ce0c1953c1997cf29d2e (patch)
tree9b03b662fb33f070fe5bfcd1b7221b81b931eeef /src
parent25cf9cc4a5ce4a272252de3f681d60dafd838ceb (diff)
downloadlibffi-ea9b6639c69cbffeacd1ce0c1953c1997cf29d2e.tar.gz
PowerPC bugfixes (#520)
* 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
Diffstat (limited to 'src')
-rw-r--r--src/powerpc/ffi.c5
-rw-r--r--src/powerpc/ffi_linux64.c83
-rw-r--r--src/powerpc/linux64_closure.S2
3 files changed, 62 insertions, 28 deletions
diff --git a/src/powerpc/ffi.c b/src/powerpc/ffi.c
index 7eb543e..94a1170 100644
--- a/src/powerpc/ffi.c
+++ b/src/powerpc/ffi.c
@@ -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
diff --git a/src/powerpc/ffi_linux64.c b/src/powerpc/ffi_linux64.c
index 93a31f9..4cf59a4 100644
--- a/src/powerpc/ffi_linux64.c
+++ b/src/powerpc/ffi_linux64.c
@@ -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
diff --git a/src/powerpc/linux64_closure.S b/src/powerpc/linux64_closure.S
index 6487d2a..7f2a214 100644
--- a/src/powerpc/linux64_closure.S
+++ b/src/powerpc/linux64_closure.S
@@ -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