aboutsummaryrefslogtreecommitdiff
path: root/libffi/src/alpha/ffi.c
diff options
context:
space:
mode:
Diffstat (limited to 'libffi/src/alpha/ffi.c')
-rw-r--r--libffi/src/alpha/ffi.c432
1 files changed, 333 insertions, 99 deletions
diff --git a/libffi/src/alpha/ffi.c b/libffi/src/alpha/ffi.c
index cf0a730..efae4cc 100644
--- a/libffi/src/alpha/ffi.c
+++ b/libffi/src/alpha/ffi.c
@@ -1,8 +1,8 @@
/* -----------------------------------------------------------------------
ffi.c - Copyright (c) 2012 Anthony Green
- Copyright (c) 1998, 2001, 2007, 2008 Red Hat, Inc.
-
- Alpha Foreign Function Interface
+ Copyright (c) 1998, 2001, 2007, 2008 Red Hat, Inc.
+
+ Alpha Foreign Function Interface
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -27,6 +27,8 @@
#include <ffi.h>
#include <ffi_common.h>
+#include <stdlib.h>
+#include "internal.h"
/* Force FFI_TYPE_LONGDOUBLE to be different than FFI_TYPE_DOUBLE;
all further uses in this file will refer to the 128-bit type. */
@@ -39,135 +41,286 @@
# define FFI_TYPE_LONGDOUBLE 4
#endif
-extern void ffi_call_osf(void *, unsigned long, unsigned, void *, void (*)(void))
- FFI_HIDDEN;
+extern void ffi_call_osf(void *stack, void *frame, unsigned flags,
+ void *raddr, void (*fn)(void), void *closure)
+ FFI_HIDDEN;
extern void ffi_closure_osf(void) FFI_HIDDEN;
+extern void ffi_go_closure_osf(void) FFI_HIDDEN;
+/* Promote a float value to its in-register double representation.
+ Unlike actually casting to double, this does not trap on NaN. */
+static inline UINT64 lds(void *ptr)
+{
+ UINT64 ret;
+ asm("lds %0,%1" : "=f"(ret) : "m"(*(UINT32 *)ptr));
+ return ret;
+}
-ffi_status
+/* And the reverse. */
+static inline void sts(void *ptr, UINT64 val)
+{
+ asm("sts %1,%0" : "=m"(*(UINT32 *)ptr) : "f"(val));
+}
+
+ffi_status FFI_HIDDEN
ffi_prep_cif_machdep(ffi_cif *cif)
{
- /* Adjust cif->bytes to represent a minimum 6 words for the temporary
- register argument loading area. */
- if (cif->bytes < 6*FFI_SIZEOF_ARG)
- cif->bytes = 6*FFI_SIZEOF_ARG;
+ size_t bytes = 0;
+ int flags, i, avn;
+ ffi_type *rtype, *itype;
+
+ if (cif->abi != FFI_OSF)
+ return FFI_BAD_ABI;
+
+ /* Compute the size of the argument area. */
+ for (i = 0, avn = cif->nargs; i < avn; i++)
+ {
+ itype = cif->arg_types[i];
+ switch (itype->type)
+ {
+ case FFI_TYPE_INT:
+ case FFI_TYPE_SINT8:
+ case FFI_TYPE_UINT8:
+ case FFI_TYPE_SINT16:
+ case FFI_TYPE_UINT16:
+ case FFI_TYPE_SINT32:
+ case FFI_TYPE_UINT32:
+ case FFI_TYPE_SINT64:
+ case FFI_TYPE_UINT64:
+ case FFI_TYPE_POINTER:
+ case FFI_TYPE_FLOAT:
+ case FFI_TYPE_DOUBLE:
+ case FFI_TYPE_LONGDOUBLE:
+ /* All take one 8 byte slot. */
+ bytes += 8;
+ break;
+
+ case FFI_TYPE_VOID:
+ case FFI_TYPE_STRUCT:
+ /* Passed by value in N slots. */
+ bytes += ALIGN(itype->size, FFI_SIZEOF_ARG);
+ break;
+
+ case FFI_TYPE_COMPLEX:
+ /* _Complex long double passed by reference; others in 2 slots. */
+ if (itype->elements[0]->type == FFI_TYPE_LONGDOUBLE)
+ bytes += 8;
+ else
+ bytes += 16;
+ break;
+
+ default:
+ abort();
+ }
+ }
/* Set the return type flag */
- switch (cif->rtype->type)
+ rtype = cif->rtype;
+ switch (rtype->type)
{
- case FFI_TYPE_STRUCT:
+ case FFI_TYPE_VOID:
+ flags = ALPHA_FLAGS(ALPHA_ST_VOID, ALPHA_LD_VOID);
+ break;
+ case FFI_TYPE_INT:
+ case FFI_TYPE_UINT32:
+ case FFI_TYPE_SINT32:
+ flags = ALPHA_FLAGS(ALPHA_ST_INT, ALPHA_LD_INT32);
+ break;
case FFI_TYPE_FLOAT:
+ flags = ALPHA_FLAGS(ALPHA_ST_FLOAT, ALPHA_LD_FLOAT);
+ break;
case FFI_TYPE_DOUBLE:
- cif->flags = cif->rtype->type;
+ flags = ALPHA_FLAGS(ALPHA_ST_DOUBLE, ALPHA_LD_DOUBLE);
+ break;
+ case FFI_TYPE_UINT8:
+ flags = ALPHA_FLAGS(ALPHA_ST_INT, ALPHA_LD_UINT8);
+ break;
+ case FFI_TYPE_SINT8:
+ flags = ALPHA_FLAGS(ALPHA_ST_INT, ALPHA_LD_SINT8);
+ break;
+ case FFI_TYPE_UINT16:
+ flags = ALPHA_FLAGS(ALPHA_ST_INT, ALPHA_LD_UINT16);
+ break;
+ case FFI_TYPE_SINT16:
+ flags = ALPHA_FLAGS(ALPHA_ST_INT, ALPHA_LD_SINT16);
+ break;
+ case FFI_TYPE_UINT64:
+ case FFI_TYPE_SINT64:
+ case FFI_TYPE_POINTER:
+ flags = ALPHA_FLAGS(ALPHA_ST_INT, ALPHA_LD_INT64);
break;
-
case FFI_TYPE_LONGDOUBLE:
- /* 128-bit long double is returned in memory, like a struct. */
- cif->flags = FFI_TYPE_STRUCT;
+ case FFI_TYPE_STRUCT:
+ /* Passed in memory, with a hidden pointer. */
+ flags = ALPHA_RET_IN_MEM;
break;
-
- default:
- cif->flags = FFI_TYPE_INT;
+ case FFI_TYPE_COMPLEX:
+ itype = rtype->elements[0];
+ switch (itype->type)
+ {
+ case FFI_TYPE_FLOAT:
+ flags = ALPHA_FLAGS(ALPHA_ST_CPLXF, ALPHA_LD_CPLXF);
+ break;
+ case FFI_TYPE_DOUBLE:
+ flags = ALPHA_FLAGS(ALPHA_ST_CPLXD, ALPHA_LD_CPLXD);
+ break;
+ default:
+ if (rtype->size <= 8)
+ flags = ALPHA_FLAGS(ALPHA_ST_INT, ALPHA_LD_INT64);
+ else
+ flags = ALPHA_RET_IN_MEM;
+ break;
+ }
break;
+ default:
+ abort();
}
-
+ cif->flags = flags;
+
+ /* Include the hidden structure pointer in args requirement. */
+ if (flags == ALPHA_RET_IN_MEM)
+ bytes += 8;
+ /* Minimum size is 6 slots, so that ffi_call_osf can pop them. */
+ if (bytes < 6*8)
+ bytes = 6*8;
+ cif->bytes = bytes;
+
return FFI_OK;
}
+static unsigned long
+extend_basic_type(void *valp, int type, int argn)
+{
+ switch (type)
+ {
+ case FFI_TYPE_SINT8:
+ return *(SINT8 *)valp;
+ case FFI_TYPE_UINT8:
+ return *(UINT8 *)valp;
+ case FFI_TYPE_SINT16:
+ return *(SINT16 *)valp;
+ case FFI_TYPE_UINT16:
+ return *(UINT16 *)valp;
+
+ case FFI_TYPE_FLOAT:
+ if (argn < 6)
+ return lds(valp);
+ /* FALLTHRU */
+
+ case FFI_TYPE_INT:
+ case FFI_TYPE_SINT32:
+ case FFI_TYPE_UINT32:
+ /* Note that unsigned 32-bit quantities are sign extended. */
+ return *(SINT32 *)valp;
+
+ case FFI_TYPE_SINT64:
+ case FFI_TYPE_UINT64:
+ case FFI_TYPE_POINTER:
+ case FFI_TYPE_DOUBLE:
+ return *(UINT64 *)valp;
-void
-ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
+ default:
+ abort();
+ }
+}
+
+static void
+ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
+ void **avalue, void *closure)
{
- unsigned long *stack, *argp;
- long i, avn;
+ unsigned long *argp;
+ long i, avn, argn, flags = cif->flags;
ffi_type **arg_types;
-
+ void *frame;
+
/* 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->flags == FFI_TYPE_STRUCT)
+ if (rvalue == NULL && flags == ALPHA_RET_IN_MEM)
rvalue = alloca(cif->rtype->size);
/* Allocate the space for the arguments, plus 4 words of temp
space for ffi_call_osf. */
- argp = stack = alloca(cif->bytes + 4*FFI_SIZEOF_ARG);
+ argp = frame = alloca(cif->bytes + 4*FFI_SIZEOF_ARG);
+ frame += cif->bytes;
- if (cif->flags == FFI_TYPE_STRUCT)
- *(void **) argp++ = rvalue;
+ argn = 0;
+ if (flags == ALPHA_RET_IN_MEM)
+ argp[argn++] = (unsigned long)rvalue;
- i = 0;
avn = cif->nargs;
arg_types = cif->arg_types;
- while (i < avn)
+ for (i = 0, avn = cif->nargs; i < avn; i++)
{
- size_t size = (*arg_types)->size;
+ ffi_type *ty = arg_types[i];
+ void *valp = avalue[i];
+ int type = ty->type;
+ size_t size;
- switch ((*arg_types)->type)
+ switch (type)
{
+ case FFI_TYPE_INT:
case FFI_TYPE_SINT8:
- *(SINT64 *) argp = *(SINT8 *)(* avalue);
- break;
-
case FFI_TYPE_UINT8:
- *(SINT64 *) argp = *(UINT8 *)(* avalue);
- break;
-
case FFI_TYPE_SINT16:
- *(SINT64 *) argp = *(SINT16 *)(* avalue);
- break;
-
case FFI_TYPE_UINT16:
- *(SINT64 *) argp = *(UINT16 *)(* avalue);
- break;
-
case FFI_TYPE_SINT32:
case FFI_TYPE_UINT32:
- /* Note that unsigned 32-bit quantities are sign extended. */
- *(SINT64 *) argp = *(SINT32 *)(* avalue);
- break;
-
case FFI_TYPE_SINT64:
case FFI_TYPE_UINT64:
case FFI_TYPE_POINTER:
- *(UINT64 *) argp = *(UINT64 *)(* avalue);
- break;
-
case FFI_TYPE_FLOAT:
- if (argp - stack < 6)
- {
- /* Note the conversion -- all the fp regs are loaded as
- doubles. The in-register format is the same. */
- *(double *) argp = *(float *)(* avalue);
- }
- else
- *(float *) argp = *(float *)(* avalue);
- break;
-
case FFI_TYPE_DOUBLE:
- *(double *) argp = *(double *)(* avalue);
+ argp[argn] = extend_basic_type(valp, type, argn);
+ argn++;
break;
case FFI_TYPE_LONGDOUBLE:
- /* 128-bit long double is passed by reference. */
- *(long double **) argp = (long double *)(* avalue);
- size = sizeof (long double *);
+ by_reference:
+ /* Note that 128-bit long double is passed by reference. */
+ argp[argn++] = (unsigned long)valp;
break;
+ case FFI_TYPE_VOID:
case FFI_TYPE_STRUCT:
- memcpy(argp, *avalue, (*arg_types)->size);
+ size = ty->size;
+ memcpy(argp + argn, valp, size);
+ argn += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
+ break;
+
+ case FFI_TYPE_COMPLEX:
+ type = ty->elements[0]->type;
+ if (type == FFI_TYPE_LONGDOUBLE)
+ goto by_reference;
+
+ /* Most complex types passed as two separate arguments. */
+ size = ty->elements[0]->size;
+ argp[argn] = extend_basic_type(valp, type, argn);
+ argp[argn + 1] = extend_basic_type(valp + size, type, argn + 1);
+ argn += 2;
break;
default:
- FFI_ASSERT(0);
+ abort();
}
-
- argp += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
- i++, arg_types++, avalue++;
}
- ffi_call_osf(stack, cif->bytes, cif->flags, rvalue, fn);
+ flags = (flags >> ALPHA_ST_SHIFT) & 0xff;
+ ffi_call_osf(argp, frame, flags, rvalue, fn, closure);
+}
+
+void
+ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
+{
+ ffi_call_int(cif, fn, rvalue, avalue, NULL);
}
+void
+ffi_call_go (ffi_cif *cif, void (*fn)(void), void *rvalue,
+ void **avalue, void *closure)
+{
+ ffi_call_int(cif, fn, rvalue, avalue, closure);
+}
ffi_status
ffi_prep_closure_loc (ffi_closure* closure,
@@ -203,39 +356,56 @@ ffi_prep_closure_loc (ffi_closure* closure,
return FFI_OK;
}
+ffi_status
+ffi_prep_go_closure (ffi_go_closure* closure,
+ ffi_cif* cif,
+ void (*fun)(ffi_cif*, void*, void**, void*))
+{
+ if (cif->abi != FFI_OSF)
+ return FFI_BAD_ABI;
+
+ closure->tramp = (void *)ffi_go_closure_osf;
+ closure->cif = cif;
+ closure->fun = fun;
+
+ return FFI_OK;
+}
long FFI_HIDDEN
-ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
+ffi_closure_osf_inner (ffi_cif *cif,
+ void (*fun)(ffi_cif*, void*, void**, void*),
+ void *user_data,
+ void *rvalue, unsigned long *argp)
{
- ffi_cif *cif;
void **avalue;
ffi_type **arg_types;
- long i, avn, argn;
+ long i, avn, argn, flags;
- cif = closure->cif;
avalue = alloca(cif->nargs * sizeof(void *));
-
+ flags = cif->flags;
argn = 0;
/* Copy the caller's structure return address to that the closure
returns the data directly to the caller. */
- if (cif->flags == FFI_TYPE_STRUCT)
+ if (flags == ALPHA_RET_IN_MEM)
{
rvalue = (void *) argp[0];
argn = 1;
}
- i = 0;
- avn = cif->nargs;
arg_types = cif->arg_types;
-
+
/* Grab the addresses of the arguments from the stack frame. */
- while (i < avn)
+ for (i = 0, avn = cif->nargs; i < avn; i++)
{
- size_t size = arg_types[i]->size;
+ ffi_type *ty = arg_types[i];
+ int type = ty->type;
+ void *valp = &argp[argn];
+ size_t size;
- switch (arg_types[i]->type)
+ switch (type)
{
+ case FFI_TYPE_INT:
case FFI_TYPE_SINT8:
case FFI_TYPE_UINT8:
case FFI_TYPE_SINT16:
@@ -245,43 +415,107 @@ ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
case FFI_TYPE_SINT64:
case FFI_TYPE_UINT64:
case FFI_TYPE_POINTER:
+ argn += 1;
+ break;
+
+ case FFI_TYPE_VOID:
case FFI_TYPE_STRUCT:
- avalue[i] = &argp[argn];
+ size = ty->size;
+ argn += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
break;
case FFI_TYPE_FLOAT:
+ /* Floats coming from registers need conversion from double
+ back to float format. */
if (argn < 6)
{
- /* Floats coming from registers need conversion from double
- back to float format. */
- *(float *)&argp[argn - 6] = *(double *)&argp[argn - 6];
- avalue[i] = &argp[argn - 6];
+ valp = &argp[argn - 6];
+ sts(valp, argp[argn - 6]);
}
- else
- avalue[i] = &argp[argn];
+ argn += 1;
break;
case FFI_TYPE_DOUBLE:
- avalue[i] = &argp[argn - (argn < 6 ? 6 : 0)];
+ if (argn < 6)
+ valp = &argp[argn - 6];
+ argn += 1;
break;
case FFI_TYPE_LONGDOUBLE:
+ by_reference:
/* 128-bit long double is passed by reference. */
- avalue[i] = (long double *) argp[argn];
- size = sizeof (long double *);
+ valp = (void *)argp[argn];
+ argn += 1;
+ break;
+
+ case FFI_TYPE_COMPLEX:
+ type = ty->elements[0]->type;
+ switch (type)
+ {
+ case FFI_TYPE_SINT64:
+ case FFI_TYPE_UINT64:
+ /* Passed as separate arguments, but they wind up sequential. */
+ break;
+
+ case FFI_TYPE_INT:
+ case FFI_TYPE_SINT8:
+ case FFI_TYPE_UINT8:
+ case FFI_TYPE_SINT16:
+ case FFI_TYPE_UINT16:
+ case FFI_TYPE_SINT32:
+ case FFI_TYPE_UINT32:
+ /* Passed as separate arguments. Disjoint, but there's room
+ enough in one slot to hold the pair. */
+ size = ty->elements[0]->size;
+ memcpy(valp + size, valp + 8, size);
+ break;
+
+ case FFI_TYPE_FLOAT:
+ /* Passed as separate arguments. Disjoint, and each piece
+ may need conversion back to float. */
+ if (argn < 6)
+ {
+ valp = &argp[argn - 6];
+ sts(valp, argp[argn - 6]);
+ }
+ if (argn + 1 < 6)
+ sts(valp + 4, argp[argn + 1 - 6]);
+ else
+ *(UINT32 *)(valp + 4) = argp[argn + 1];
+ break;
+
+ case FFI_TYPE_DOUBLE:
+ /* Passed as separate arguments. Only disjoint if one part
+ is in fp regs and the other is on the stack. */
+ if (argn < 5)
+ valp = &argp[argn - 6];
+ else if (argn == 5)
+ {
+ valp = alloca(16);
+ ((UINT64 *)valp)[0] = argp[5 - 6];
+ ((UINT64 *)valp)[1] = argp[6];
+ }
+ break;
+
+ case FFI_TYPE_LONGDOUBLE:
+ goto by_reference;
+
+ default:
+ abort();
+ }
+ argn += 2;
break;
default:
- FFI_ASSERT (0);
+ abort ();
}
- argn += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
- i++;
+ avalue[i] = valp;
}
/* Invoke the closure. */
- closure->fun (cif, rvalue, avalue, closure->user_data);
+ fun (cif, rvalue, avalue, user_data);
/* Tell ffi_closure_osf how to perform return type promotions. */
- return cif->rtype->type;
+ return (flags >> ALPHA_LD_SHIFT) & 0xff;
}