summaryrefslogtreecommitdiffstats
path: root/libffi/src/powerpc
diff options
context:
space:
mode:
Diffstat (limited to 'libffi/src/powerpc')
-rw-r--r--libffi/src/powerpc/aix.S6
-rw-r--r--libffi/src/powerpc/aix_closure.S2
-rw-r--r--libffi/src/powerpc/asm.h2
-rw-r--r--libffi/src/powerpc/darwin.S2
-rw-r--r--libffi/src/powerpc/darwin_closure.S2
-rw-r--r--libffi/src/powerpc/ffi.c630
-rw-r--r--libffi/src/powerpc/ffitarget.h24
-rw-r--r--libffi/src/powerpc/ppc_closure.S19
-rw-r--r--libffi/src/powerpc/sysv.S27
9 files changed, 385 insertions, 329 deletions
diff --git a/libffi/src/powerpc/aix.S b/libffi/src/powerpc/aix.S
index c6f87644d94..213f2db39de 100644
--- a/libffi/src/powerpc/aix.S
+++ b/libffi/src/powerpc/aix.S
@@ -1,5 +1,5 @@
/* -----------------------------------------------------------------------
- aix.S - Copyright (c) 2002,2009 Free Software Foundation, Inc.
+ aix.S - Copyright (c) 2002, 2009 Free Software Foundation, Inc.
based on darwin.S by John Hornkvist
PowerPC Assembly glue.
@@ -79,6 +79,8 @@
.set f20,20
.set f21,21
+ .extern .ffi_prep_args
+
#define LIBFFI_ASM
#include <fficonfig.h>
#include <ffi.h>
@@ -125,6 +127,7 @@ ffi_call_AIX:
/* Call ffi_prep_args. */
mr r4, r1
bl .ffi_prep_args
+ nop
/* Now do the call. */
ld r0, 0(r29)
@@ -226,6 +229,7 @@ L(float_return_value):
/* Call ffi_prep_args. */
mr r4, r1
bl .ffi_prep_args
+ nop
/* Now do the call. */
lwz r0, 0(r29)
diff --git a/libffi/src/powerpc/aix_closure.S b/libffi/src/powerpc/aix_closure.S
index 5c74448f2b1..c906017c459 100644
--- a/libffi/src/powerpc/aix_closure.S
+++ b/libffi/src/powerpc/aix_closure.S
@@ -79,6 +79,8 @@
.set f20,20
.set f21,21
+ .extern .ffi_closure_helper_DARWIN
+
#define LIBFFI_ASM
#define JUMPTARGET(name) name
#define L(x) x
diff --git a/libffi/src/powerpc/asm.h b/libffi/src/powerpc/asm.h
index e86e6b09147..994f62d079d 100644
--- a/libffi/src/powerpc/asm.h
+++ b/libffi/src/powerpc/asm.h
@@ -42,7 +42,7 @@
/* If compiled for profiling, call `_mcount' at the start of each function. */
#ifdef PROF
-/* The mcount code relies on a the return address being on the stack
+/* The mcount code relies on the return address being on the stack
to locate our caller and so it can restore it; so store one just
for its benefit. */
#ifdef PIC
diff --git a/libffi/src/powerpc/darwin.S b/libffi/src/powerpc/darwin.S
index 3b425da78b8..4f987dc7482 100644
--- a/libffi/src/powerpc/darwin.S
+++ b/libffi/src/powerpc/darwin.S
@@ -335,7 +335,7 @@ LSCIE1:
.byte EH_DATA_ALIGN_FACT ; sleb128 -4; CIE Data Alignment Factor
.byte 0x41 ; CIE RA Column
.byte 0x1 ; uleb128 0x1; Augmentation size
- .byte 0x90 ; FDE Encoding (indirect pcrel)
+ .byte 0x10 ; FDE Encoding (indirect pcrel)
.byte 0xc ; DW_CFA_def_cfa
.byte 0x1 ; uleb128 0x1
.byte 0x0 ; uleb128 0x0
diff --git a/libffi/src/powerpc/darwin_closure.S b/libffi/src/powerpc/darwin_closure.S
index b43f9658fce..3f6790f6bb3 100644
--- a/libffi/src/powerpc/darwin_closure.S
+++ b/libffi/src/powerpc/darwin_closure.S
@@ -484,7 +484,7 @@ LSCIE1:
.byte EH_DATA_ALIGN_FACT ; sleb128 -4; CIE Data Alignment Factor
.byte 0x41 ; CIE RA Column
.byte 0x1 ; uleb128 0x1; Augmentation size
- .byte 0x90 ; FDE Encoding (indirect pcrel)
+ .byte 0x10 ; FDE Encoding (indirect pcrel)
.byte 0xc ; DW_CFA_def_cfa
.byte 0x1 ; uleb128 0x1
.byte 0x0 ; uleb128 0x0
diff --git a/libffi/src/powerpc/ffi.c b/libffi/src/powerpc/ffi.c
index 75784a96d3b..1920c91104d 100644
--- a/libffi/src/powerpc/ffi.c
+++ b/libffi/src/powerpc/ffi.c
@@ -1,7 +1,9 @@
/* -----------------------------------------------------------------------
- ffi.c - Copyright (c) 1998 Geoffrey Keating
- Copyright (C) 2007, 2008 Free Software Foundation, Inc
- Copyright (C) 2008 Red Hat, Inc
+ ffi.c - Copyright (C) 2011 Anthony Green
+ Copyright (C) 2011 Kyle Moffett
+ Copyright (C) 2008 Red Hat, Inc
+ Copyright (C) 2007, 2008 Free Software Foundation, Inc
+ Copyright (c) 1998 Geoffrey Keating
PowerPC Foreign Function Interface
@@ -39,32 +41,28 @@ enum {
/* The assembly depends on these exact flags. */
FLAG_RETURNS_SMST = 1 << (31-31), /* Used for FFI_SYSV small structs. */
FLAG_RETURNS_NOTHING = 1 << (31-30), /* These go in cr7 */
+#ifndef __NO_FPRS__
FLAG_RETURNS_FP = 1 << (31-29),
+#endif
FLAG_RETURNS_64BITS = 1 << (31-28),
FLAG_RETURNS_128BITS = 1 << (31-27), /* cr6 */
- FLAG_SYSV_SMST_R4 = 1 << (31-26), /* use r4 for FFI_SYSV 8 byte
- structs. */
- FLAG_SYSV_SMST_R3 = 1 << (31-25), /* use r3 for FFI_SYSV 4 byte
- structs. */
- /* Bits (31-24) through (31-19) store shift value for SMST */
FLAG_ARG_NEEDS_COPY = 1 << (31- 7),
+#ifndef __NO_FPRS__
FLAG_FP_ARGUMENTS = 1 << (31- 6), /* cr1.eq; specified by ABI */
+#endif
FLAG_4_GPR_ARGUMENTS = 1 << (31- 5),
FLAG_RETVAL_REFERENCE = 1 << (31- 4)
};
/* About the SYSV ABI. */
-unsigned int NUM_GPR_ARG_REGISTERS = 8;
+#define ASM_NEEDS_REGISTERS 4
+#define NUM_GPR_ARG_REGISTERS 8
#ifndef __NO_FPRS__
-unsigned int NUM_FPR_ARG_REGISTERS = 8;
-#else
-unsigned int NUM_FPR_ARG_REGISTERS = 0;
+# define NUM_FPR_ARG_REGISTERS 8
#endif
-enum { ASM_NEEDS_REGISTERS = 4 };
-
/* ffi_prep_args_SYSV is called by the assembly routine once stack space
has been allocated for the function's arguments.
@@ -113,10 +111,12 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
valp gpr_base;
int intarg_count;
+#ifndef __NO_FPRS__
/* 'fpr_base' points at the space for fpr1, and grows upwards as
we use FPR registers. */
valp fpr_base;
int fparg_count;
+#endif
/* 'copy_space' grows down as we put structures in it. It should
stay 16-byte aligned. */
@@ -125,9 +125,8 @@ 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, ii MAYBE_UNUSED;
+ int i;
ffi_type **ptr;
- double double_tmp;
union {
void **v;
char **c;
@@ -143,15 +142,16 @@ 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;
+#ifndef __NO_FPRS__
fpr_base.d = gpr_base.d - NUM_FPR_ARG_REGISTERS;
fparg_count = 0;
copy_space.c = ((flags & FLAG_FP_ARGUMENTS) ? fpr_base.c : gpr_base.c);
+#else
+ copy_space.c = gpr_base.c;
+#endif
next_arg.u = stack + 2;
/* Check that everything starts aligned properly. */
@@ -174,12 +174,28 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
i > 0;
i--, ptr++, p_argv.v++)
{
- switch ((*ptr)->type)
- {
+ unsigned short typenum = (*ptr)->type;
+
+ /* We may need to handle some values depending on ABI */
+ if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT) {
+ if (typenum == FFI_TYPE_FLOAT)
+ typenum = FFI_TYPE_UINT32;
+ if (typenum == FFI_TYPE_DOUBLE)
+ typenum = FFI_TYPE_UINT64;
+ if (typenum == FFI_TYPE_LONGDOUBLE)
+ typenum = FFI_TYPE_UINT128;
+ } else if (ecif->cif->abi != FFI_LINUX) {
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ if (typenum == FFI_TYPE_LONGDOUBLE)
+ typenum = FFI_TYPE_STRUCT;
+#endif
+ }
+
+ /* Now test the translated value */
+ switch (typenum) {
+#ifndef __NO_FPRS__
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)
{
@@ -218,43 +234,6 @@ 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)
- && (ecif->cif->abi != FFI_LINUX_SOFT_FLOAT))
- goto do_struct;
- /* 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)
- {
- unsigned int int_tmp = (*p_argv.ui)[0];
- if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3)
- {
- 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++;
- }
- }
- 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
- {
double_tmp = (*p_argv.d)[0];
if (fparg_count >= NUM_FPR_ARG_REGISTERS - 1)
@@ -280,13 +259,40 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
fparg_count += 2;
FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
- }
break;
#endif
+#endif /* have FPRs */
+
+ /*
+ * 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.
+ */
+ case FFI_TYPE_UINT128: {
+ unsigned int int_tmp = (*p_argv.ui)[0];
+ unsigned int ii;
+ if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3) {
+ if (intarg_count < NUM_GPR_ARG_REGISTERS)
+ intarg_count += NUM_GPR_ARG_REGISTERS - intarg_count;
+ *(next_arg.u++) = int_tmp;
+ for (ii = 1; ii < 4; ii++) {
+ int_tmp = (*p_argv.ui)[ii];
+ *(next_arg.u++) = int_tmp;
+ }
+ } 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;
+ break;
+ }
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)
@@ -319,9 +325,6 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
break;
case FFI_TYPE_STRUCT:
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
- do_struct:
-#endif
struct_copy_size = ((*ptr)->size + 15) & ~0xF;
copy_space.c -= struct_copy_size;
memcpy (copy_space.c, *p_argv.c, (*ptr)->size);
@@ -349,7 +352,6 @@ 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;
@@ -366,8 +368,10 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
/* Check that we didn't overrun the stack... */
FFI_ASSERT (copy_space.c >= next_arg.c);
FFI_ASSERT (gpr_base.u <= stacktop.u - ASM_NEEDS_REGISTERS);
+#ifndef __NO_FPRS__
FFI_ASSERT (fpr_base.u
<= stacktop.u - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS);
+#endif
FFI_ASSERT (flags & FLAG_4_GPR_ARGUMENTS || intarg_count <= 4);
}
@@ -604,9 +608,6 @@ 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.
@@ -646,62 +647,56 @@ ffi_prep_cif_machdep (ffi_cif *cif)
- 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. */
+ /* First translate for softfloat/nonlinux */
+ if (cif->abi == FFI_LINUX_SOFT_FLOAT) {
+ if (type == FFI_TYPE_FLOAT)
+ type = FFI_TYPE_UINT32;
+ if (type == FFI_TYPE_DOUBLE)
+ type = FFI_TYPE_UINT64;
+ if (type == FFI_TYPE_LONGDOUBLE)
+ type = FFI_TYPE_UINT128;
+ } else if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64) {
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ if (type == FFI_TYPE_LONGDOUBLE)
+ type = FFI_TYPE_STRUCT;
+#endif
+ }
+
switch (type)
{
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+#ifndef __NO_FPRS__
case FFI_TYPE_LONGDOUBLE:
- if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64
- && cif->abi != FFI_LINUX_SOFT_FLOAT)
- goto byref;
flags |= FLAG_RETURNS_128BITS;
/* Fall through. */
-#endif
case FFI_TYPE_DOUBLE:
flags |= FLAG_RETURNS_64BITS;
/* Fall through. */
case FFI_TYPE_FLOAT:
- /* With FFI_LINUX_SOFT_FLOAT no fp registers are used. */
- if (cif->abi != FFI_LINUX_SOFT_FLOAT)
- flags |= FLAG_RETURNS_FP;
+ flags |= FLAG_RETURNS_FP;
break;
+#endif
+ case FFI_TYPE_UINT128:
+ flags |= FLAG_RETURNS_128BITS;
+ /* Fall through. */
case FFI_TYPE_UINT64:
case FFI_TYPE_SINT64:
flags |= FLAG_RETURNS_64BITS;
break;
case FFI_TYPE_STRUCT:
- if (cif->abi == FFI_SYSV)
- {
- /* The final SYSV ABI says that structures smaller or equal 8 bytes
- are returned in r3/r4. The FFI_GCC_SYSV ABI instead returns them
- in memory. */
-
- /* Treat structs with size <= 8 bytes. */
- if (size <= 8)
- {
- flags |= FLAG_RETURNS_SMST;
- /* These structs are returned in r3. We pack the type and the
- precalculated shift value (needed in the sysv.S) into flags.
- The same applies for the structs returned in r3/r4. */
- if (size <= 4)
- {
- flags |= FLAG_SYSV_SMST_R3;
- flags |= 8 * (4 - size) << 8;
- break;
- }
- /* These structs are returned in r3 and r4. See above. */
- if (size <= 8)
- {
- flags |= FLAG_SYSV_SMST_R3 | FLAG_SYSV_SMST_R4;
- flags |= 8 * (8 - size) << 8;
- break;
- }
- }
- }
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
- byref:
-#endif
+ /*
+ * The final SYSV ABI says that structures smaller or equal 8 bytes
+ * are returned in r3/r4. The FFI_GCC_SYSV ABI instead returns them
+ * in memory.
+ *
+ * NOTE: The assembly code can safely assume that it just needs to
+ * store both r3 and r4 into a 8-byte word-aligned buffer, as
+ * we allocate a temporary buffer in ffi_call() if this flag is
+ * set.
+ */
+ if (cif->abi == FFI_SYSV && size <= 8)
+ flags |= FLAG_RETURNS_SMST;
intarg_count++;
flags |= FLAG_RETVAL_REFERENCE;
/* Fall through. */
@@ -722,39 +717,36 @@ ffi_prep_cif_machdep (ffi_cif *cif)
Stuff on the stack needs to keep proper alignment. */
for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++)
{
- switch ((*ptr)->type)
- {
+ unsigned short typenum = (*ptr)->type;
+
+ /* We may need to handle some values depending on ABI */
+ if (cif->abi == FFI_LINUX_SOFT_FLOAT) {
+ if (typenum == FFI_TYPE_FLOAT)
+ typenum = FFI_TYPE_UINT32;
+ if (typenum == FFI_TYPE_DOUBLE)
+ typenum = FFI_TYPE_UINT64;
+ if (typenum == FFI_TYPE_LONGDOUBLE)
+ typenum = FFI_TYPE_UINT128;
+ } else if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64) {
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ if (typenum == FFI_TYPE_LONGDOUBLE)
+ typenum = FFI_TYPE_STRUCT;
+#endif
+ }
+
+ switch (typenum) {
+#ifndef __NO_FPRS__
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 && cif->abi != FFI_LINUX_SOFT_FLOAT)
- goto do_struct;
- 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++;
+ 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. */
@@ -763,10 +755,21 @@ ffi_prep_cif_machdep (ffi_cif *cif)
&& intarg_count % 2 != 0)
intarg_count++;
break;
+#endif
+ case FFI_TYPE_UINT128:
+ /*
+ * A long double in FFI_LINUX_SOFT_FLOAT can use only a set
+ * of four consecutive gprs. If we do not have enough, we
+ * have to adjust the intarg_count value.
+ */
+ if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3
+ && intarg_count < NUM_GPR_ARG_REGISTERS)
+ intarg_count = NUM_GPR_ARG_REGISTERS;
+ intarg_count += 4;
+ break;
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
@@ -783,9 +786,6 @@ ffi_prep_cif_machdep (ffi_cif *cif)
break;
case FFI_TYPE_STRUCT:
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
- do_struct:
-#endif
/* We must allocate space for a copy of these to enforce
pass-by-value. Pad the space up to a multiple of 16
bytes (the maximum alignment required for anything under
@@ -793,12 +793,20 @@ ffi_prep_cif_machdep (ffi_cif *cif)
struct_copy_size += ((*ptr)->size + 15) & ~0xF;
/* Fall through (allocate space for the pointer). */
- default:
- soft_float_cif:
+ case FFI_TYPE_POINTER:
+ case FFI_TYPE_INT:
+ case FFI_TYPE_UINT32:
+ case FFI_TYPE_SINT32:
+ case FFI_TYPE_UINT16:
+ case FFI_TYPE_SINT16:
+ case FFI_TYPE_UINT8:
+ case FFI_TYPE_SINT8:
/* Everything else is passed as a 4-byte word in a GPR, either
the object itself or a pointer to it. */
intarg_count++;
break;
+ default:
+ FFI_ASSERT (0);
}
}
else
@@ -827,16 +835,29 @@ ffi_prep_cif_machdep (ffi_cif *cif)
intarg_count += ((*ptr)->size + 7) / 8;
break;
- default:
+ case FFI_TYPE_POINTER:
+ case FFI_TYPE_UINT64:
+ case FFI_TYPE_SINT64:
+ case FFI_TYPE_INT:
+ case FFI_TYPE_UINT32:
+ case FFI_TYPE_SINT32:
+ case FFI_TYPE_UINT16:
+ case FFI_TYPE_SINT16:
+ case FFI_TYPE_UINT8:
+ case FFI_TYPE_SINT8:
/* Everything else is passed as a 8-byte word in a GPR, either
the object itself or a pointer to it. */
intarg_count++;
break;
+ default:
+ FFI_ASSERT (0);
}
}
+#ifndef __NO_FPRS__
if (fparg_count != 0)
flags |= FLAG_FP_ARGUMENTS;
+#endif
if (intarg_count > 4)
flags |= FLAG_4_GPR_ARGUMENTS;
if (struct_copy_size != 0)
@@ -844,21 +865,27 @@ ffi_prep_cif_machdep (ffi_cif *cif)
if (cif->abi != FFI_LINUX64)
{
+#ifndef __NO_FPRS__
/* Space for the FPR registers, if needed. */
if (fparg_count != 0)
bytes += NUM_FPR_ARG_REGISTERS * sizeof (double);
+#endif
/* Stack space. */
if (intarg_count > NUM_GPR_ARG_REGISTERS)
bytes += (intarg_count - NUM_GPR_ARG_REGISTERS) * sizeof (int);
+#ifndef __NO_FPRS__
if (fparg_count > NUM_FPR_ARG_REGISTERS)
bytes += (fparg_count - NUM_FPR_ARG_REGISTERS) * sizeof (double);
+#endif
}
else
{
+#ifndef __NO_FPRS__
/* Space for the FPR registers, if needed. */
if (fparg_count != 0)
bytes += NUM_FPR_ARG_REGISTERS64 * sizeof (double);
+#endif
/* Stack space. */
if (intarg_count > NUM_GPR_ARG_REGISTERS64)
@@ -886,28 +913,41 @@ extern void FFI_HIDDEN ffi_call_LINUX64(extended_cif *, unsigned long,
void
ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
{
+ /*
+ * The final SYSV ABI says that structures smaller or equal 8 bytes
+ * are returned in r3/r4. The FFI_GCC_SYSV ABI instead returns them
+ * in memory.
+ *
+ * Just to keep things simple for the assembly code, we will always
+ * bounce-buffer struct return values less than or equal to 8 bytes.
+ * This allows the ASM to handle SYSV small structures by directly
+ * writing r3 and r4 to memory without worrying about struct size.
+ */
+ unsigned int smst_buffer[2];
extended_cif ecif;
+ unsigned int rsize;
ecif.cif = cif;
ecif.avalue = avalue;
- /* If the return value is a struct and we don't have a return */
- /* value address then we need to make one */
-
- if ((rvalue == NULL) && (cif->rtype->type == FFI_TYPE_STRUCT))
- {
- ecif.rvalue = alloca(cif->rtype->size);
- }
- else
- ecif.rvalue = rvalue;
-
+ /* Ensure that we have a valid struct return value */
+ ecif.rvalue = rvalue;
+ if (cif->rtype->type == FFI_TYPE_STRUCT) {
+ rsize = cif->rtype->size;
+ if (rsize <= 8)
+ ecif.rvalue = smst_buffer;
+ else if (!rvalue)
+ ecif.rvalue = alloca(rsize);
+ }
switch (cif->abi)
{
#ifndef POWERPC64
+# ifndef __NO_FPRS__
case FFI_SYSV:
case FFI_GCC_SYSV:
case FFI_LINUX:
+# endif
case FFI_LINUX_SOFT_FLOAT:
ffi_call_SYSV (&ecif, -cif->bytes, cif->flags, ecif.rvalue, fn);
break;
@@ -920,6 +960,10 @@ ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
FFI_ASSERT (0);
break;
}
+
+ /* Check for a bounce-buffered return value */
+ if (rvalue && ecif.rvalue == smst_buffer)
+ memcpy(rvalue, smst_buffer, rsize);
}
@@ -949,14 +993,19 @@ ffi_prep_closure_loc (ffi_closure *closure,
#ifdef POWERPC64
void **tramp = (void **) &closure->tramp[0];
- FFI_ASSERT (cif->abi == FFI_LINUX64);
+ if (cif->abi != FFI_LINUX64)
+ return FFI_BAD_ABI;
/* Copy function address and TOC from ffi_closure_LINUX64. */
memcpy (tramp, (char *) ffi_closure_LINUX64, 16);
tramp[2] = codeloc;
#else
unsigned int *tramp;
- FFI_ASSERT (cif->abi == FFI_GCC_SYSV || cif->abi == FFI_SYSV);
+ if (! (cif->abi == FFI_GCC_SYSV
+ || cif->abi == FFI_SYSV
+ || cif->abi == FFI_LINUX
+ || cif->abi == FFI_LINUX_SOFT_FLOAT))
+ return FFI_BAD_ABI;
tramp = (unsigned int *) &closure->tramp[0];
tramp[0] = 0x7c0802a6; /* mflr r0 */
@@ -1011,32 +1060,38 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue,
void ** avalue;
ffi_type ** arg_types;
long i, avn;
- long nf; /* number of floating registers already used */
- long ng; /* number of general registers already used */
- ffi_cif * cif;
- double temp;
- unsigned size;
+#ifndef __NO_FPRS__
+ long nf = 0; /* number of floating registers already used */
+#endif
+ long ng = 0; /* number of general registers already used */
+
+ ffi_cif *cif = closure->cif;
+ unsigned size = cif->rtype->size;
+ unsigned short rtypenum = cif->rtype->type;
- cif = closure->cif;
avalue = alloca (cif->nargs * sizeof (void *));
- size = cif->rtype->size;
- nf = 0;
- ng = 0;
+ /* First translate for softfloat/nonlinux */
+ if (cif->abi == FFI_LINUX_SOFT_FLOAT) {
+ if (rtypenum == FFI_TYPE_FLOAT)
+ rtypenum = FFI_TYPE_UINT32;
+ if (rtypenum == FFI_TYPE_DOUBLE)
+ rtypenum = FFI_TYPE_UINT64;
+ if (rtypenum == FFI_TYPE_LONGDOUBLE)
+ rtypenum = FFI_TYPE_UINT128;
+ } else if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64) {
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ if (rtypenum == FFI_TYPE_LONGDOUBLE)
+ rtypenum = FFI_TYPE_STRUCT;
+#endif
+ }
+
/* Copy the caller's structure return value address so that the closure
returns the data directly to the caller.
For FFI_SYSV the result is passed in r3/r4 if the struct size is less
or equal 8 bytes. */
-
- if ((cif->rtype->type == FFI_TYPE_STRUCT
- && !((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_SOFT_FLOAT)
-#endif
- )
- {
+ if (rtypenum == FFI_TYPE_STRUCT && ((cif->abi != FFI_SYSV) || (size > 8))) {
rvalue = (void *) *pgr;
ng++;
pgr++;
@@ -1047,10 +1102,109 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue,
arg_types = cif->arg_types;
/* Grab the addresses of the arguments from the stack frame. */
- while (i < avn)
- {
- switch (arg_types[i]->type)
- {
+ while (i < avn) {
+ unsigned short typenum = arg_types[i]->type;
+
+ /* We may need to handle some values depending on ABI */
+ if (cif->abi == FFI_LINUX_SOFT_FLOAT) {
+ if (typenum == FFI_TYPE_FLOAT)
+ typenum = FFI_TYPE_UINT32;
+ if (typenum == FFI_TYPE_DOUBLE)
+ typenum = FFI_TYPE_UINT64;
+ if (typenum == FFI_TYPE_LONGDOUBLE)
+ typenum = FFI_TYPE_UINT128;
+ } else if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64) {
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ if (typenum == FFI_TYPE_LONGDOUBLE)
+ typenum = FFI_TYPE_STRUCT;
+#endif
+ }
+
+ switch (typenum) {
+#ifndef __NO_FPRS__
+ case FFI_TYPE_FLOAT:
+ /* unfortunately float values are stored as doubles
+ * in the ffi_closure_SYSV code (since we don't check
+ * the type in that routine).
+ */
+
+ /* there are 8 64bit floating point registers */
+
+ if (nf < 8)
+ {
+ temp = pfr->d;
+ pfr->f = (float) temp;
+ avalue[i] = pfr;
+ nf++;
+ pfr++;
+ }
+ else
+ {
+ /* FIXME? here we are really changing the values
+ * stored in the original calling routines outgoing
+ * parameter stack. This is probably a really
+ * naughty thing to do but...
+ */
+ avalue[i] = pst;
+ pst += 1;
+ }
+ break;
+
+ case FFI_TYPE_DOUBLE:
+ /* On the outgoing stack all values are aligned to 8 */
+ /* there are 8 64bit floating point registers */
+
+ if (nf < 8)
+ {
+ avalue[i] = pfr;
+ nf++;
+ pfr++;
+ }
+ else
+ {
+ if (((long) pst) & 4)
+ pst++;
+ avalue[i] = pst;
+ pst += 2;
+ }
+ break;
+
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ case FFI_TYPE_LONGDOUBLE:
+ if (nf < 7)
+ {
+ avalue[i] = pfr;
+ pfr += 2;
+ nf += 2;
+ }
+ else
+ {
+ if (((long) pst) & 4)
+ pst++;
+ avalue[i] = pst;
+ pst += 4;
+ nf = 8;
+ }
+ break;
+#endif
+#endif /* have FPRS */
+
+ case FFI_TYPE_UINT128:
+ /*
+ * 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;
+ ng = 8+4;
+ }
+ break;
+
case FFI_TYPE_SINT8:
case FFI_TYPE_UINT8:
/* there are 8 gpr registers used to pass values */
@@ -1086,7 +1240,6 @@ 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)
{
@@ -1102,9 +1255,6 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue,
break;
case FFI_TYPE_STRUCT:
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
- do_struct:
-#endif
/* Structs are passed by reference. The address will appear in a
gpr if it is one of the first 8 arguments. */
if (ng < 8)
@@ -1122,7 +1272,6 @@ 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)
@@ -1154,99 +1303,8 @@ 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).
- */
-
- /* there are 8 64bit floating point registers */
-
- if (nf < 8)
- {
- temp = pfr->d;
- pfr->f = (float) temp;
- avalue[i] = pfr;
- nf++;
- pfr++;
- }
- else
- {
- /* FIXME? here we are really changing the values
- * stored in the original calling routines outgoing
- * parameter stack. This is probably a really
- * naughty thing to do but...
- */
- avalue[i] = pst;
- pst += 1;
- }
- 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 */
-
- if (nf < 8)
- {
- avalue[i] = pfr;
- nf++;
- pfr++;
- }
- else
- {
- if (((long) pst) & 4)
- pst++;
- avalue[i] = pst;
- pst += 2;
- }
- break;
-
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
- case FFI_TYPE_LONGDOUBLE:
- 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;
- ng = 8;
- }
- break;
- }
- if (nf < 7)
- {
- avalue[i] = pfr;
- pfr += 2;
- nf += 2;
- }
- else
- {
- if (((long) pst) & 4)
- pst++;
- avalue[i] = pst;
- pst += 4;
- nf = 8;
- }
- break;
-#endif
-
default:
- FFI_ASSERT (0);
+ FFI_ASSERT (0);
}
i++;
@@ -1263,39 +1321,9 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue,
already used and we never have a struct with size zero. That is the reason
for the subtraction of 1. See the comment in ffitarget.h about ordering.
*/
- if (cif->abi == FFI_SYSV && cif->rtype->type == FFI_TYPE_STRUCT
- && size <= 8)
+ if (cif->abi == FFI_SYSV && rtypenum == FFI_TYPE_STRUCT && size <= 8)
return (FFI_SYSV_TYPE_SMALL_STRUCT - 1) + size;
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
- else if (cif->rtype->type == FFI_TYPE_LONGDOUBLE
- && cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT)
- return FFI_TYPE_STRUCT;
-#endif
- /* 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;
- }
+ return rtypenum;
}
int FFI_HIDDEN ffi_closure_helper_LINUX64 (ffi_closure *, void *,
diff --git a/libffi/src/powerpc/ffitarget.h b/libffi/src/powerpc/ffitarget.h
index d17f7312d81..3c9db495f49 100644
--- a/libffi/src/powerpc/ffitarget.h
+++ b/libffi/src/powerpc/ffitarget.h
@@ -1,6 +1,8 @@
/* -----------------------------------------------------------------*-C-*-
- ffitarget.h - Copyright (c) 1996-2003 Red Hat, Inc.
- Copyright (C) 2007, 2008, 2010 Free Software Foundation, Inc
+ ffitarget.h - Copyright (c) 2012 Anthony Green
+ Copyright (C) 2007, 2008, 2010 Free Software Foundation, Inc
+ Copyright (c) 1996-2003 Red Hat, Inc.
+
Target configuration macros for PowerPC.
Permission is hereby granted, free of charge, to any person obtaining
@@ -28,6 +30,10 @@
#ifndef LIBFFI_TARGET_H
#define LIBFFI_TARGET_H
+#ifndef LIBFFI_H
+#error "Please do not include ffitarget.h directly into your source. Use ffi.h instead."
+#endif
+
/* ---- System specific configurations ----------------------------------- */
#if defined (POWERPC) && defined (__powerpc64__) /* linux64 */
@@ -60,18 +66,14 @@ typedef enum ffi_abi {
FFI_LINUX64,
FFI_LINUX,
FFI_LINUX_SOFT_FLOAT,
-# ifdef POWERPC64
+# if defined(POWERPC64)
FFI_DEFAULT_ABI = FFI_LINUX64,
-# else
-# if (!defined(__NO_FPRS__) && (__LDBL_MANT_DIG__ == 106))
- FFI_DEFAULT_ABI = FFI_LINUX,
-# else
-# ifdef __NO_FPRS__
+# elif defined(__NO_FPRS__)
FFI_DEFAULT_ABI = FFI_LINUX_SOFT_FLOAT,
-# else
+# elif (__LDBL_MANT_DIG__ == 106)
+ FFI_DEFAULT_ABI = FFI_LINUX,
+# else
FFI_DEFAULT_ABI = FFI_GCC_SYSV,
-# endif
-# endif
# endif
#endif
diff --git a/libffi/src/powerpc/ppc_closure.S b/libffi/src/powerpc/ppc_closure.S
index 56f7d1af2c8..41fb8851b62 100644
--- a/libffi/src/powerpc/ppc_closure.S
+++ b/libffi/src/powerpc/ppc_closure.S
@@ -122,22 +122,41 @@ ENTRY(ffi_closure_SYSV)
blr
# case FFI_TYPE_FLOAT
+#ifndef __NO_FPRS__
lfs %f1,112+0(%r1)
mtlr %r0
addi %r1,%r1,144
+#else
+ nop
+ nop
+ nop
+#endif
blr
# case FFI_TYPE_DOUBLE
+#ifndef __NO_FPRS__
lfd %f1,112+0(%r1)
mtlr %r0
addi %r1,%r1,144
+#else
+ nop
+ nop
+ nop
+#endif
blr
# case FFI_TYPE_LONGDOUBLE
+#ifndef __NO_FPRS__
lfd %f1,112+0(%r1)
lfd %f2,112+8(%r1)
mtlr %r0
b .Lfinish
+#else
+ nop
+ nop
+ nop
+ blr
+#endif
# case FFI_TYPE_UINT8
lbz %r3,112+3(%r1)
diff --git a/libffi/src/powerpc/sysv.S b/libffi/src/powerpc/sysv.S
index 96ea22b0b40..675ed03eeb9 100644
--- a/libffi/src/powerpc/sysv.S
+++ b/libffi/src/powerpc/sysv.S
@@ -83,6 +83,7 @@ ENTRY(ffi_call_SYSV)
nop
1:
+#ifndef __NO_FPRS__
/* Load all the FP registers. */
bf- 6,2f
lfd %f1,-16-(8*4)-(8*8)(%r28)
@@ -94,6 +95,7 @@ ENTRY(ffi_call_SYSV)
lfd %f6,-16-(8*4)-(3*8)(%r28)
lfd %f7,-16-(8*4)-(2*8)(%r28)
lfd %f8,-16-(8*4)-(1*8)(%r28)
+#endif
2:
/* Make the call. */
@@ -103,7 +105,9 @@ ENTRY(ffi_call_SYSV)
mtcrf 0x01,%r31 /* cr7 */
bt- 31,L(small_struct_return_value)
bt- 30,L(done_return_value)
+#ifndef __NO_FPRS__
bt- 29,L(fp_return_value)
+#endif
stw %r3,0(%r30)
bf+ 28,L(done_return_value)
stw %r4,4(%r30)
@@ -124,6 +128,7 @@ L(done_return_value):
lwz %r1,0(%r1)
blr
+#ifndef __NO_FPRS__
L(fp_return_value):
bf 28,L(float_return_value)
stfd %f1,0(%r30)
@@ -134,21 +139,17 @@ L(fp_return_value):
L(float_return_value):
stfs %f1,0(%r30)
b L(done_return_value)
+#endif
L(small_struct_return_value):
- extrwi %r6,%r31,2,19 /* number of bytes padding = shift/8 */
- mtcrf 0x02,%r31 /* copy flags to cr[24:27] (cr6) */
- extrwi %r5,%r31,5,19 /* r5 <- number of bits of padding */
- subfic %r6,%r6,4 /* r6 <- number of useful bytes in r3 */
- bf- 25,L(done_return_value) /* struct in r3 ? if not, done. */
-/* smst_one_register: */
- slw %r3,%r3,%r5 /* Left-justify value in r3 */
- mtxer %r6 /* move byte count to XER ... */
- stswx %r3,0,%r30 /* ... and store that many bytes */
- bf+ 26,L(done_return_value) /* struct in r3:r4 ? */
- add %r6,%r6,%r30 /* adjust pointer */
- stswi %r4,%r6,4 /* store last four bytes */
- b L(done_return_value)
+ /*
+ * The C code always allocates a properly-aligned 8-byte bounce
+ * buffer to make this assembly code very simple. Just write out
+ * r3 and r4 to the buffer to allow the C code to handle the rest.
+ */
+ stw %r3, 0(%r30)
+ stw %r4, 4(%r30)
+ b L(done_return_value)
.LFE1:
END(ffi_call_SYSV)
OpenPOWER on IntegriCloud