summaryrefslogtreecommitdiff
path: root/libffi/src/powerpc/ffi.c
diff options
context:
space:
mode:
authorgreen <green@138bc75d-0d04-0410-961f-82ee72b054a4>1999-08-08 13:27:18 +0000
committergreen <green@138bc75d-0d04-0410-961f-82ee72b054a4>1999-08-08 13:27:18 +0000
commit47c5fab6136c7525f4abe013644d7d20570d1b94 (patch)
tree7687d170c71097dc434304205f4ea24833298d64 /libffi/src/powerpc/ffi.c
parent6f5c3acb88fa13cbc8e086f7d8962641e5b35b10 (diff)
downloadgcc-47c5fab6136c7525f4abe013644d7d20570d1b94.tar.gz
Initial revision
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@28593 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libffi/src/powerpc/ffi.c')
-rw-r--r--libffi/src/powerpc/ffi.c423
1 files changed, 423 insertions, 0 deletions
diff --git a/libffi/src/powerpc/ffi.c b/libffi/src/powerpc/ffi.c
new file mode 100644
index 00000000000..6d12d653baa
--- /dev/null
+++ b/libffi/src/powerpc/ffi.c
@@ -0,0 +1,423 @@
+/* -----------------------------------------------------------------------
+ ffi.c - Copyright (c) 1998 Geoffrey Keating
+
+ PowerPC Foreign Function Interface
+
+ $Id: ffi.c,v 1.1.1.1 1998/11/29 16:48:16 green Exp $
+
+ 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>
+#include <ffi_common.h>
+
+#include <stdlib.h>
+
+enum {
+ /* The assembly depends on these exact flags. */
+ FLAG_RETURNS_NOTHING = 1 << (31-30), /* These go in cr7 */
+ FLAG_RETURNS_FP = 1 << (31-29),
+ FLAG_RETURNS_64BITS = 1 << (31-28),
+
+ FLAG_ARG_NEEDS_COPY = 1 << (31- 7),
+ FLAG_FP_ARGUMENTS = 1 << (31- 6), /* cr1.eq; specified by ABI */
+ FLAG_4_GPR_ARGUMENTS = 1 << (31- 5),
+ FLAG_RETVAL_REFERENCE = 1 << (31- 4)
+};
+
+/* About the SYSV ABI. */
+enum {
+ NUM_GPR_ARG_REGISTERS = 8,
+ NUM_FPR_ARG_REGISTERS = 8
+};
+enum { ASM_NEEDS_REGISTERS = 4 };
+
+/* ffi_prep_args 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
+
+ */
+
+/*@-exportheader@*/
+void ffi_prep_args(extended_cif *ecif, unsigned *const stack)
+/*@=exportheader@*/
+{
+ const unsigned bytes = ecif->cif->bytes;
+ const unsigned flags = ecif->cif->flags;
+
+ /* 'stacktop' points at the previous backchain pointer. */
+ unsigned *const stacktop = stack + (ecif->cif->bytes / sizeof(unsigned));
+
+ /* 'gpr_base' points at the space for gpr3, and grows upwards as
+ we use GPR registers. */
+ unsigned *gpr_base = stacktop - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS;
+ int intarg_count = 0;
+
+ /* 'fpr_base' points at the space for fpr1, and grows upwards as
+ we use FPR registers. */
+ double *fpr_base = (double *)gpr_base - NUM_FPR_ARG_REGISTERS;
+ int fparg_count = 0;
+
+ /* 'copy_space' grows down as we put structures in it. It should
+ stay 16-byte aligned. */
+ char *copy_space = ((flags & FLAG_FP_ARGUMENTS)
+ ? (char *)fpr_base
+ : (char *)gpr_base);
+
+ /* 'next_arg' grows up as we put parameters in it. */
+ unsigned *next_arg = stack + 2;
+
+ int i;
+ ffi_type **ptr;
+ double double_tmp;
+ void **p_argv;
+ size_t struct_copy_size;
+ unsigned gprvalue;
+
+ /* Check that everything starts aligned properly. */
+ FFI_ASSERT(((unsigned)(char *)stack & 0xF) == 0);
+ FFI_ASSERT(((unsigned)(char *)copy_space & 0xF) == 0);
+ FFI_ASSERT(((unsigned)(char *)stacktop & 0xF) == 0);
+ FFI_ASSERT((bytes & 0xF) == 0);
+ FFI_ASSERT(copy_space >= (char *)next_arg);
+
+ /* Deal with return values that are actually pass-by-reference. */
+ if (flags & FLAG_RETVAL_REFERENCE)
+ {
+ *gpr_base++ = (unsigned)(char *)ecif->rvalue;
+ intarg_count++;
+ }
+
+ /* Now for the arguments. */
+ p_argv = ecif->avalue;
+ for (ptr = ecif->cif->arg_types, i = ecif->cif->nargs;
+ i > 0;
+ i--, ptr++, p_argv++)
+ {
+ switch ((*ptr)->type)
+ {
+ case FFI_TYPE_FLOAT:
+ case FFI_TYPE_DOUBLE:
+ if ((*ptr)->type == FFI_TYPE_FLOAT)
+ double_tmp = *(float *)*p_argv;
+ else
+ double_tmp = *(double *)*p_argv;
+
+ if (fparg_count >= NUM_FPR_ARG_REGISTERS)
+ {
+ if (intarg_count%2 != 0)
+ {
+ intarg_count++;
+ next_arg++;
+ }
+ *(double *)next_arg = double_tmp;
+ next_arg += 2;
+ }
+ else
+ *fpr_base++ = double_tmp;
+ fparg_count++;
+ FFI_ASSERT(flags & FLAG_FP_ARGUMENTS);
+ 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++;
+ }
+ *(long long *)next_arg = *(long long *)*p_argv;
+ next_arg += 2;
+ }
+ else
+ {
+ *(long long *)gpr_base = *(long long *)*p_argv;
+ gpr_base += 2;
+ }
+ intarg_count += 2;
+ break;
+
+ case FFI_TYPE_STRUCT:
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ case FFI_TYPE_LONGDOUBLE:
+#endif
+ struct_copy_size = ((*ptr)->size + 15) & ~0xF;
+ copy_space -= struct_copy_size;
+ memcpy(copy_space, (char *)*p_argv, (*ptr)->size);
+
+ gprvalue = (unsigned)copy_space;
+
+ FFI_ASSERT(copy_space > (char *)next_arg);
+ FFI_ASSERT(flags & FLAG_ARG_NEEDS_COPY);
+ goto putgpr;
+
+ case FFI_TYPE_UINT8:
+ gprvalue = *(unsigned char *)*p_argv;
+ goto putgpr;
+ case FFI_TYPE_SINT8:
+ gprvalue = *(signed char *)*p_argv;
+ goto putgpr;
+ case FFI_TYPE_UINT16:
+ gprvalue = *(unsigned short *)*p_argv;
+ goto putgpr;
+ case FFI_TYPE_SINT16:
+ gprvalue = *(signed short *)*p_argv;
+ goto putgpr;
+
+ case FFI_TYPE_INT:
+ case FFI_TYPE_UINT32:
+ case FFI_TYPE_SINT32:
+ case FFI_TYPE_POINTER:
+ gprvalue = *(unsigned *)*p_argv;
+ putgpr:
+ if (intarg_count >= NUM_GPR_ARG_REGISTERS)
+ *next_arg++ = gprvalue;
+ else
+ *gpr_base++ = gprvalue;
+ intarg_count++;
+ break;
+ }
+ }
+
+ /* Check that we didn't overrun the stack... */
+ FFI_ASSERT(copy_space >= (char *)next_arg);
+ FFI_ASSERT(gpr_base <= stacktop - ASM_NEEDS_REGISTERS);
+ FFI_ASSERT((unsigned *)fpr_base
+ <= stacktop - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS);
+ FFI_ASSERT(flags & FLAG_4_GPR_ARGUMENTS || intarg_count <= 4);
+}
+
+/* Perform machine dependent cif processing */
+ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
+{
+ /* All this is for the SYSV ABI. */
+ int i;
+ ffi_type **ptr;
+ unsigned bytes;
+ int fparg_count = 0, intarg_count = 0;
+ unsigned flags = 0;
+ unsigned struct_copy_size = 0;
+
+ /* All the machine-independent calculation of cif->bytes will be wrong.
+ Redo the calculation for SYSV. */
+
+ /* 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 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;
+ - Single/double FP values are returned in fpr1;
+ - Larger structures and long double (if not equivalent to double) values
+ are allocated space and a pointer is passed as the first argument. */
+ switch (cif->rtype->type)
+ {
+ case FFI_TYPE_DOUBLE:
+ flags |= FLAG_RETURNS_64BITS;
+ /* Fall through. */
+ case FFI_TYPE_FLOAT:
+ flags |= FLAG_RETURNS_FP;
+ break;
+
+ case FFI_TYPE_UINT64:
+ case FFI_TYPE_SINT64:
+ flags |= FLAG_RETURNS_64BITS;
+ break;
+
+ case FFI_TYPE_STRUCT:
+ if (cif->abi != FFI_GCC_SYSV)
+ if (cif->rtype->size <= 4)
+ break;
+ else if (cif->rtype->size <= 8)
+ {
+ flags |= FLAG_RETURNS_64BITS;
+ break;
+ }
+ /* else fall through. */
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ case FFI_TYPE_LONGDOUBLE:
+#endif
+ 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++)
+ {
+ switch ((*ptr)->type)
+ {
+ case FFI_TYPE_FLOAT:
+ 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%2 != 0)
+ intarg_count++;
+ 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. */
+ if (intarg_count == NUM_GPR_ARG_REGISTERS-1
+ || intarg_count >= NUM_GPR_ARG_REGISTERS && intarg_count%2 != 0)
+ intarg_count++;
+ intarg_count += 2;
+ break;
+
+ case FFI_TYPE_STRUCT:
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+ case FFI_TYPE_LONGDOUBLE:
+#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
+ the SYSV ABI). */
+ struct_copy_size += ((*ptr)->size + 15) & ~0xF;
+ /* Fall through (allocate space for the pointer). */
+
+ default:
+ /* Everything else is passed as a 4-byte word in a GPR, either
+ the object itself or a pointer to it. */
+ intarg_count++;
+ break;
+ }
+ }
+
+ 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;
+}
+
+/*@-declundef@*/
+/*@-exportheader@*/
+extern void ffi_call_SYSV(/*@out@*/ extended_cif *,
+ unsigned, unsigned,
+ /*@out@*/ unsigned *,
+ void (*fn)());
+/*@=declundef@*/
+/*@=exportheader@*/
+
+void ffi_call(/*@dependent@*/ ffi_cif *cif,
+ void (*fn)(),
+ /*@out@*/ void *rvalue,
+ /*@dependent@*/ void **avalue)
+{
+ extended_cif ecif;
+
+ 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))
+ {
+ /*@-sysunrecog@*/
+ ecif.rvalue = alloca(cif->rtype->size);
+ /*@=sysunrecog@*/
+ }
+ else
+ ecif.rvalue = rvalue;
+
+
+ switch (cif->abi)
+ {
+ case FFI_SYSV:
+ case FFI_GCC_SYSV:
+ /*@-usedef@*/
+ ffi_call_SYSV(&ecif, -cif->bytes,
+ cif->flags, ecif.rvalue, fn);
+ /*@=usedef@*/
+ break;
+ default:
+ FFI_ASSERT(0);
+ break;
+ }
+}