aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.8/libffi/src/powerpc/ffi_sysv.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-4.8/libffi/src/powerpc/ffi_sysv.c')
-rw-r--r--gcc-4.8/libffi/src/powerpc/ffi_sysv.c931
1 files changed, 931 insertions, 0 deletions
diff --git a/gcc-4.8/libffi/src/powerpc/ffi_sysv.c b/gcc-4.8/libffi/src/powerpc/ffi_sysv.c
new file mode 100644
index 000000000..fbe85fe91
--- /dev/null
+++ b/gcc-4.8/libffi/src/powerpc/ffi_sysv.c
@@ -0,0 +1,931 @@
+/* -----------------------------------------------------------------------
+ ffi_sysv.c - Copyright (C) 2013 IBM
+ 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
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ ``Software''), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+ ----------------------------------------------------------------------- */
+
+#include "ffi.h"
+
+#ifndef POWERPC64
+#include "ffi_common.h"
+#include "ffi_powerpc.h"
+
+
+/* About the SYSV ABI. */
+#define ASM_NEEDS_REGISTERS 4
+#define NUM_GPR_ARG_REGISTERS 8
+#define NUM_FPR_ARG_REGISTERS 8
+
+
+#if HAVE_LONG_DOUBLE_VARIANT && FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+/* Adjust size of ffi_type_longdouble. */
+void FFI_HIDDEN
+ffi_prep_types_sysv (ffi_abi abi)
+{
+ if ((abi & (FFI_SYSV | FFI_SYSV_LONG_DOUBLE_128)) == FFI_SYSV)
+ {
+ ffi_type_longdouble.size = 8;
+ ffi_type_longdouble.alignment = 8;
+ }
+ else
+ {
+ ffi_type_longdouble.size = 16;
+ ffi_type_longdouble.alignment = 16;
+ }
+}
+#endif
+
+/* Transform long double, double and float to other types as per abi. */
+static int
+translate_float (int abi, int type)
+{
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ if (type == FFI_TYPE_LONGDOUBLE
+ && (abi & FFI_SYSV_LONG_DOUBLE_128) == 0)
+ type = FFI_TYPE_DOUBLE;
+#endif
+ if ((abi & FFI_SYSV_SOFT_FLOAT) != 0)
+ {
+ if (type == FFI_TYPE_FLOAT)
+ type = FFI_TYPE_UINT32;
+ else if (type == FFI_TYPE_DOUBLE)
+ type = FFI_TYPE_UINT64;
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ else if (type == FFI_TYPE_LONGDOUBLE)
+ type = FFI_TYPE_UINT128;
+ }
+ else if ((abi & FFI_SYSV_IBM_LONG_DOUBLE) == 0)
+ {
+ if (type == FFI_TYPE_LONGDOUBLE)
+ type = FFI_TYPE_STRUCT;
+#endif
+ }
+ return type;
+}
+
+/* Perform machine dependent cif processing */
+static ffi_status
+ffi_prep_cif_sysv_core (ffi_cif *cif)
+{
+ ffi_type **ptr;
+ unsigned bytes;
+ unsigned i, fparg_count = 0, intarg_count = 0;
+ unsigned flags = cif->flags;
+ unsigned struct_copy_size = 0;
+ unsigned type = cif->rtype->type;
+ unsigned size = cif->rtype->size;
+
+ /* The machine-independent calculation of cif->bytes doesn't work
+ for us. Redo the calculation. */
+
+ /* Space for the frame pointer, callee's LR, and the asm's temp regs. */
+ bytes = (2 + ASM_NEEDS_REGISTERS) * sizeof (int);
+
+ /* Space for the GPR registers. */
+ bytes += NUM_GPR_ARG_REGISTERS * sizeof (int);
+
+ /* Return value handling. The rules for SYSV are as follows:
+ - 32-bit (or less) integer values are returned in gpr3;
+ - Structures of size <= 4 bytes also returned in gpr3;
+ - 64-bit integer values and structures between 5 and 8 bytes are returned
+ in gpr3 and gpr4;
+ - Larger structures are allocated space and a pointer is passed as
+ the first argument.
+ - Single/double FP values are returned in fpr1;
+ - long doubles (if not equivalent to double) are returned in
+ fpr1,fpr2 for Linux and as for large structs for SysV. */
+
+ type = translate_float (cif->abi, type);
+
+ switch (type)
+ {
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ case FFI_TYPE_LONGDOUBLE:
+ flags |= FLAG_RETURNS_128BITS;
+ /* Fall through. */
+#endif
+ case FFI_TYPE_DOUBLE:
+ flags |= FLAG_RETURNS_64BITS;
+ /* Fall through. */
+ case FFI_TYPE_FLOAT:
+ flags |= FLAG_RETURNS_FP;
+#ifdef __NO_FPRS__
+ return FFI_BAD_ABI;
+#endif
+ break;
+
+ 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:
+ /* The final SYSV ABI says that structures smaller or equal 8 bytes
+ are returned in r3/r4. A draft ABI used by linux instead
+ returns them in memory. */
+ if ((cif->abi & FFI_SYSV_STRUCT_RET) != 0 && size <= 8)
+ {
+ flags |= FLAG_RETURNS_SMST;
+ break;
+ }
+ intarg_count++;
+ flags |= FLAG_RETVAL_REFERENCE;
+ /* Fall through. */
+ case FFI_TYPE_VOID:
+ flags |= FLAG_RETURNS_NOTHING;
+ break;
+
+ default:
+ /* Returns 32-bit integer, or similar. Nothing to do here. */
+ break;
+ }
+
+ /* The first NUM_GPR_ARG_REGISTERS words of integer arguments, and the
+ first NUM_FPR_ARG_REGISTERS fp arguments, go in registers; the rest
+ goes on the stack. Structures and long doubles (if not equivalent
+ to double) are passed as a pointer to a copy of the structure.
+ Stuff on the stack needs to keep proper alignment. */
+ for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++)
+ {
+ unsigned short typenum = (*ptr)->type;
+
+ typenum = translate_float (cif->abi, typenum);
+
+ switch (typenum)
+ {
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ case FFI_TYPE_LONGDOUBLE:
+ fparg_count++;
+ /* Fall thru */
+#endif
+ case FFI_TYPE_DOUBLE:
+ fparg_count++;
+ /* If this FP arg is going on the stack, it must be
+ 8-byte-aligned. */
+ if (fparg_count > NUM_FPR_ARG_REGISTERS
+ && intarg_count >= NUM_GPR_ARG_REGISTERS
+ && intarg_count % 2 != 0)
+ intarg_count++;
+#ifdef __NO_FPRS__
+ return FFI_BAD_ABI;
+#endif
+ break;
+
+ case FFI_TYPE_FLOAT:
+ fparg_count++;
+#ifdef __NO_FPRS__
+ return FFI_BAD_ABI;
+#endif
+ break;
+
+ 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:
+ /* '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
+ be 8-byte-aligned.
+
+ Also, only certain register pairs can be used for
+ passing long long int -- specifically (r3,r4), (r5,r6),
+ (r7,r8), (r9,r10). */
+ if (intarg_count == NUM_GPR_ARG_REGISTERS-1
+ || intarg_count % 2 != 0)
+ intarg_count++;
+ intarg_count += 2;
+ break;
+
+ case FFI_TYPE_STRUCT:
+ /* 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
+ the SYSV ABI). */
+ struct_copy_size += ((*ptr)->size + 15) & ~0xF;
+ /* Fall through (allocate space for the pointer). */
+
+ 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);
+ }
+ }
+
+ if (fparg_count != 0)
+ flags |= FLAG_FP_ARGUMENTS;
+ if (intarg_count > 4)
+ flags |= FLAG_4_GPR_ARGUMENTS;
+ if (struct_copy_size != 0)
+ flags |= FLAG_ARG_NEEDS_COPY;
+
+ /* Space for the FPR registers, if needed. */
+ if (fparg_count != 0)
+ bytes += NUM_FPR_ARG_REGISTERS * sizeof (double);
+
+ /* Stack space. */
+ if (intarg_count > NUM_GPR_ARG_REGISTERS)
+ bytes += (intarg_count - NUM_GPR_ARG_REGISTERS) * sizeof (int);
+ if (fparg_count > NUM_FPR_ARG_REGISTERS)
+ bytes += (fparg_count - NUM_FPR_ARG_REGISTERS) * sizeof (double);
+
+ /* The stack space allocated needs to be a multiple of 16 bytes. */
+ bytes = (bytes + 15) & ~0xF;
+
+ /* Add in the space for the copied structures. */
+ bytes += struct_copy_size;
+
+ cif->flags = flags;
+ cif->bytes = bytes;
+
+ return FFI_OK;
+}
+
+ffi_status FFI_HIDDEN
+ffi_prep_cif_sysv (ffi_cif *cif)
+{
+ if ((cif->abi & FFI_SYSV) == 0)
+ {
+ /* This call is from old code. Translate to new ABI values. */
+ cif->flags |= FLAG_COMPAT;
+ switch (cif->abi)
+ {
+ default:
+ return FFI_BAD_ABI;
+
+ case FFI_COMPAT_SYSV:
+ cif->abi = FFI_SYSV | FFI_SYSV_STRUCT_RET | FFI_SYSV_LONG_DOUBLE_128;
+ break;
+
+ case FFI_COMPAT_GCC_SYSV:
+ cif->abi = FFI_SYSV | FFI_SYSV_LONG_DOUBLE_128;
+ break;
+
+ case FFI_COMPAT_LINUX:
+ cif->abi = (FFI_SYSV | FFI_SYSV_IBM_LONG_DOUBLE
+ | FFI_SYSV_LONG_DOUBLE_128);
+ break;
+
+ case FFI_COMPAT_LINUX_SOFT_FLOAT:
+ cif->abi = (FFI_SYSV | FFI_SYSV_SOFT_FLOAT | FFI_SYSV_IBM_LONG_DOUBLE
+ | FFI_SYSV_LONG_DOUBLE_128);
+ break;
+ }
+ }
+ return ffi_prep_cif_sysv_core (cif);
+}
+
+/* ffi_prep_args_SYSV is called by the assembly routine once stack space
+ has been allocated for the function's arguments.
+
+ The stack layout we want looks like this:
+
+ | Return address from ffi_call_SYSV 4bytes | higher addresses
+ |--------------------------------------------|
+ | Previous backchain pointer 4 | stack pointer here
+ |--------------------------------------------|<+ <<< on entry to
+ | Saved r28-r31 4*4 | | ffi_call_SYSV
+ |--------------------------------------------| |
+ | GPR registers r3-r10 8*4 | | ffi_call_SYSV
+ |--------------------------------------------| |
+ | FPR registers f1-f8 (optional) 8*8 | |
+ |--------------------------------------------| | stack |
+ | Space for copied structures | | grows |
+ |--------------------------------------------| | down V
+ | Parameters that didn't fit in registers | |
+ |--------------------------------------------| | lower addresses
+ | Space for callee's LR 4 | |
+ |--------------------------------------------| | stack pointer here
+ | Current backchain pointer 4 |-/ during
+ |--------------------------------------------| <<< ffi_call_SYSV
+
+*/
+
+void FFI_HIDDEN
+ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
+{
+ const unsigned bytes = ecif->cif->bytes;
+ const unsigned flags = ecif->cif->flags;
+
+ typedef union
+ {
+ char *c;
+ unsigned *u;
+ long long *ll;
+ float *f;
+ double *d;
+ } valp;
+
+ /* 'stacktop' points at the previous backchain pointer. */
+ valp stacktop;
+
+ /* 'gpr_base' points at the space for gpr3, and grows upwards as
+ we use GPR registers. */
+ 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. */
+ valp copy_space;
+
+ /* 'next_arg' grows up as we put parameters in it. */
+ valp next_arg;
+
+ int i;
+ ffi_type **ptr;
+#ifndef __NO_FPRS__
+ double double_tmp;
+#endif
+ union
+ {
+ void **v;
+ char **c;
+ signed char **sc;
+ unsigned char **uc;
+ signed short **ss;
+ unsigned short **us;
+ unsigned int **ui;
+ long long **ll;
+ float **f;
+ double **d;
+ } p_argv;
+ size_t struct_copy_size;
+ unsigned gprvalue;
+
+ 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. */
+ FFI_ASSERT (((unsigned long) (char *) stack & 0xF) == 0);
+ FFI_ASSERT (((unsigned long) copy_space.c & 0xF) == 0);
+ FFI_ASSERT (((unsigned long) stacktop.c & 0xF) == 0);
+ FFI_ASSERT ((bytes & 0xF) == 0);
+ FFI_ASSERT (copy_space.c >= next_arg.c);
+
+ /* Deal with return values that are actually pass-by-reference. */
+ if (flags & FLAG_RETVAL_REFERENCE)
+ {
+ *gpr_base.u++ = (unsigned long) (char *) ecif->rvalue;
+ intarg_count++;
+ }
+
+ /* Now for the arguments. */
+ p_argv.v = ecif->avalue;
+ for (ptr = ecif->cif->arg_types, i = ecif->cif->nargs;
+ i > 0;
+ i--, ptr++, p_argv.v++)
+ {
+ unsigned int typenum = (*ptr)->type;
+
+ typenum = translate_float (ecif->cif->abi, typenum);
+
+ /* Now test the translated value */
+ switch (typenum)
+ {
+#ifndef __NO_FPRS__
+# if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ case FFI_TYPE_LONGDOUBLE:
+ double_tmp = (*p_argv.d)[0];
+
+ 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_DOUBLE:
+ double_tmp = **p_argv.d;
+
+ if (fparg_count >= NUM_FPR_ARG_REGISTERS)
+ {
+ 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;
+ }
+ else
+ *fpr_base.d++ = double_tmp;
+ fparg_count++;
+ FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
+ break;
+
+ case FFI_TYPE_FLOAT:
+ double_tmp = **p_argv.f;
+ if (fparg_count >= NUM_FPR_ARG_REGISTERS)
+ {
+ *next_arg.f = (float) double_tmp;
+ next_arg.u += 1;
+ intarg_count++;
+ }
+ else
+ *fpr_base.d++ = double_tmp;
+ fparg_count++;
+ FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
+ break;
+#endif /* have FPRs */
+
+ case FFI_TYPE_UINT128:
+ /* 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. */
+ {
+ unsigned int int_tmp;
+ unsigned int ii;
+ if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3)
+ {
+ if (intarg_count < NUM_GPR_ARG_REGISTERS)
+ intarg_count = NUM_GPR_ARG_REGISTERS;
+ for (ii = 0; ii < 4; ii++)
+ {
+ int_tmp = (*p_argv.ui)[ii];
+ *next_arg.u++ = int_tmp;
+ }
+ }
+ else
+ {
+ for (ii = 0; 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:
+ if (intarg_count == NUM_GPR_ARG_REGISTERS-1)
+ intarg_count++;
+ if (intarg_count >= NUM_GPR_ARG_REGISTERS)
+ {
+ if (intarg_count % 2 != 0)
+ {
+ intarg_count++;
+ next_arg.u++;
+ }
+ *next_arg.ll = **p_argv.ll;
+ next_arg.u += 2;
+ }
+ else
+ {
+ /* The abi states only certain register pairs can be
+ used for passing long long int specifically (r3,r4),
+ (r5,r6), (r7,r8), (r9,r10). If next arg is long long
+ but not correct starting register of pair then skip
+ until the proper starting register. */
+ if (intarg_count % 2 != 0)
+ {
+ intarg_count ++;
+ gpr_base.u++;
+ }
+ *gpr_base.ll++ = **p_argv.ll;
+ }
+ intarg_count += 2;
+ break;
+
+ case FFI_TYPE_STRUCT:
+ struct_copy_size = ((*ptr)->size + 15) & ~0xF;
+ copy_space.c -= struct_copy_size;
+ memcpy (copy_space.c, *p_argv.c, (*ptr)->size);
+
+ gprvalue = (unsigned long) copy_space.c;
+
+ FFI_ASSERT (copy_space.c > next_arg.c);
+ FFI_ASSERT (flags & FLAG_ARG_NEEDS_COPY);
+ goto putgpr;
+
+ case FFI_TYPE_UINT8:
+ gprvalue = **p_argv.uc;
+ goto putgpr;
+ case FFI_TYPE_SINT8:
+ gprvalue = **p_argv.sc;
+ goto putgpr;
+ case FFI_TYPE_UINT16:
+ gprvalue = **p_argv.us;
+ goto putgpr;
+ case FFI_TYPE_SINT16:
+ gprvalue = **p_argv.ss;
+ goto putgpr;
+
+ case FFI_TYPE_INT:
+ case FFI_TYPE_UINT32:
+ case FFI_TYPE_SINT32:
+ case FFI_TYPE_POINTER:
+
+ gprvalue = **p_argv.ui;
+
+ putgpr:
+ if (intarg_count >= NUM_GPR_ARG_REGISTERS)
+ *next_arg.u++ = gprvalue;
+ else
+ *gpr_base.u++ = gprvalue;
+ intarg_count++;
+ break;
+ }
+ }
+
+ /* 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);
+ /* The assert below is testing that the number of integer arguments agrees
+ with the number found in ffi_prep_cif_machdep(). However, intarg_count
+ is incremented whenever we place an FP arg on the stack, so account for
+ that before our assert test. */
+#ifndef __NO_FPRS__
+ if (fparg_count > NUM_FPR_ARG_REGISTERS)
+ intarg_count -= fparg_count - NUM_FPR_ARG_REGISTERS;
+ 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);
+}
+
+#define MIN_CACHE_LINE_SIZE 8
+
+static void
+flush_icache (char *wraddr, char *xaddr, int size)
+{
+ int i;
+ for (i = 0; i < size; i += MIN_CACHE_LINE_SIZE)
+ __asm__ volatile ("icbi 0,%0;" "dcbf 0,%1;"
+ : : "r" (xaddr + i), "r" (wraddr + i) : "memory");
+ __asm__ volatile ("icbi 0,%0;" "dcbf 0,%1;" "sync;" "isync;"
+ : : "r"(xaddr + size - 1), "r"(wraddr + size - 1)
+ : "memory");
+}
+
+ffi_status FFI_HIDDEN
+ffi_prep_closure_loc_sysv (ffi_closure *closure,
+ ffi_cif *cif,
+ void (*fun) (ffi_cif *, void *, void **, void *),
+ void *user_data,
+ void *codeloc)
+{
+ unsigned int *tramp;
+
+ if (cif->abi < FFI_SYSV || cif->abi >= FFI_LAST_ABI)
+ return FFI_BAD_ABI;
+
+ tramp = (unsigned int *) &closure->tramp[0];
+ tramp[0] = 0x7c0802a6; /* mflr r0 */
+ tramp[1] = 0x4800000d; /* bl 10 <trampoline_initial+0x10> */
+ tramp[4] = 0x7d6802a6; /* mflr r11 */
+ tramp[5] = 0x7c0803a6; /* mtlr r0 */
+ tramp[6] = 0x800b0000; /* lwz r0,0(r11) */
+ tramp[7] = 0x816b0004; /* lwz r11,4(r11) */
+ tramp[8] = 0x7c0903a6; /* mtctr r0 */
+ tramp[9] = 0x4e800420; /* bctr */
+ *(void **) &tramp[2] = (void *) ffi_closure_SYSV; /* function */
+ *(void **) &tramp[3] = codeloc; /* context */
+
+ /* Flush the icache. */
+ flush_icache ((char *)tramp, (char *)codeloc, FFI_TRAMPOLINE_SIZE);
+
+ closure->cif = cif;
+ closure->fun = fun;
+ closure->user_data = user_data;
+
+ return FFI_OK;
+}
+
+/* Basically the trampoline invokes ffi_closure_SYSV, and on
+ entry, r11 holds the address of the closure.
+ After storing the registers that could possibly contain
+ parameters to be passed into the stack frame and setting
+ up space for a return value, ffi_closure_SYSV invokes the
+ following helper function to do most of the work. */
+
+int
+ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue,
+ unsigned long *pgr, ffi_dblfl *pfr,
+ unsigned long *pst)
+{
+ /* rvalue is the pointer to space for return value in closure assembly */
+ /* pgr is the pointer to where r3-r10 are stored in ffi_closure_SYSV */
+ /* pfr is the pointer to where f1-f8 are stored in ffi_closure_SYSV */
+ /* pst is the pointer to outgoing parameter stack in original caller */
+
+ void ** avalue;
+ ffi_type ** arg_types;
+ long i, avn;
+#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;
+
+ avalue = alloca (cif->nargs * sizeof (void *));
+
+ /* First translate for softfloat/nonlinux */
+ rtypenum = translate_float (cif->abi, rtypenum);
+
+ /* 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 (rtypenum == FFI_TYPE_STRUCT
+ && !((cif->abi & FFI_SYSV_STRUCT_RET) != 0 && size <= 8))
+ {
+ rvalue = (void *) *pgr;
+ ng++;
+ pgr++;
+ }
+
+ i = 0;
+ avn = cif->nargs;
+ arg_types = cif->arg_types;
+
+ /* Grab the addresses of the arguments from the stack frame. */
+ while (i < avn) {
+ unsigned short typenum = arg_types[i]->type;
+
+ /* We may need to handle some values depending on ABI. */
+ typenum = translate_float (cif->abi, typenum);
+
+ 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). */
+ if (nf < NUM_FPR_ARG_REGISTERS)
+ {
+ /* 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... */
+ double temp = pfr->d;
+ pfr->f = (float) temp;
+ avalue[i] = pfr;
+ nf++;
+ pfr++;
+ }
+ else
+ {
+ avalue[i] = pst;
+ pst += 1;
+ }
+ break;
+
+ case FFI_TYPE_DOUBLE:
+ if (nf < NUM_FPR_ARG_REGISTERS)
+ {
+ 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 < NUM_FPR_ARG_REGISTERS - 1)
+ {
+ avalue[i] = pfr;
+ pfr += 2;
+ nf += 2;
+ }
+ else
+ {
+ if (((long) pst) & 4)
+ pst++;
+ avalue[i] = pst;
+ pst += 4;
+ nf = 8;
+ }
+ break;
+# endif
+#endif
+
+ 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 < NUM_GPR_ARG_REGISTERS - 3)
+ {
+ 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:
+#ifndef __LITTLE_ENDIAN__
+ if (ng < NUM_GPR_ARG_REGISTERS)
+ {
+ avalue[i] = (char *) pgr + 3;
+ ng++;
+ pgr++;
+ }
+ else
+ {
+ avalue[i] = (char *) pst + 3;
+ pst++;
+ }
+ break;
+#endif
+
+ case FFI_TYPE_SINT16:
+ case FFI_TYPE_UINT16:
+#ifndef __LITTLE_ENDIAN__
+ if (ng < NUM_GPR_ARG_REGISTERS)
+ {
+ avalue[i] = (char *) pgr + 2;
+ ng++;
+ pgr++;
+ }
+ else
+ {
+ avalue[i] = (char *) pst + 2;
+ pst++;
+ }
+ break;
+#endif
+
+ case FFI_TYPE_SINT32:
+ case FFI_TYPE_UINT32:
+ case FFI_TYPE_POINTER:
+ if (ng < NUM_GPR_ARG_REGISTERS)
+ {
+ avalue[i] = pgr;
+ ng++;
+ pgr++;
+ }
+ else
+ {
+ avalue[i] = pst;
+ pst++;
+ }
+ break;
+
+ case FFI_TYPE_STRUCT:
+ /* Structs are passed by reference. The address will appear in a
+ gpr if it is one of the first 8 arguments. */
+ if (ng < NUM_GPR_ARG_REGISTERS)
+ {
+ avalue[i] = (void *) *pgr;
+ ng++;
+ pgr++;
+ }
+ else
+ {
+ avalue[i] = (void *) *pst;
+ pst++;
+ }
+ break;
+
+ case FFI_TYPE_SINT64:
+ case FFI_TYPE_UINT64:
+ /* 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)
+ and if the entire pair aren't available then the outgoing
+ parameter stack is used for both but an alignment of 8
+ must will be kept. So we must either look in pgr
+ or pst to find the correct address for this type
+ of parameter. */
+ if (ng < NUM_GPR_ARG_REGISTERS - 1)
+ {
+ if (ng & 1)
+ {
+ /* skip r4, r6, r8 as starting points */
+ ng++;
+ pgr++;
+ }
+ avalue[i] = pgr;
+ ng += 2;
+ pgr += 2;
+ }
+ else
+ {
+ if (((long) pst) & 4)
+ pst++;
+ avalue[i] = pst;
+ pst += 2;
+ ng = NUM_GPR_ARG_REGISTERS;
+ }
+ break;
+
+ default:
+ FFI_ASSERT (0);
+ }
+
+ i++;
+ }
+
+ (closure->fun) (cif, rvalue, avalue, closure->user_data);
+
+ /* Tell ffi_closure_SYSV how to perform return type promotions.
+ Because the FFI_SYSV ABI returns the structures <= 8 bytes in
+ r3/r4 we have to tell ffi_closure_SYSV how to treat them. We
+ combine the base type FFI_SYSV_TYPE_SMALL_STRUCT with the size of
+ the struct less one. We never have a struct with size zero.
+ See the comment in ffitarget.h about ordering. */
+ if (rtypenum == FFI_TYPE_STRUCT
+ && (cif->abi & FFI_SYSV_STRUCT_RET) != 0 && size <= 8)
+ return FFI_SYSV_TYPE_SMALL_STRUCT - 1 + size;
+ return rtypenum;
+}
+#endif