mirror of
https://github.com/partic2/libffi
synced 2026-11-11 02:27:19 +00:00
Add Meta processor support
This commit is contained in:
+330
@@ -0,0 +1,330 @@
|
||||
/* ----------------------------------------------------------------------
|
||||
ffi.c - Copyright (c) 2013 Imagination Technologies
|
||||
|
||||
Meta 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 SIMON POSNJAK 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>
|
||||
|
||||
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
/*
|
||||
* ffi_prep_args is called by the assembly routine once stack space has been
|
||||
* allocated for the function's arguments
|
||||
*/
|
||||
|
||||
unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
|
||||
{
|
||||
register unsigned int i;
|
||||
register void **p_argv;
|
||||
register char *argp;
|
||||
register ffi_type **p_arg;
|
||||
|
||||
argp = stack;
|
||||
|
||||
/* Store return value */
|
||||
if ( ecif->cif->flags == FFI_TYPE_STRUCT ) {
|
||||
argp -= 4;
|
||||
*(void **) argp = ecif->rvalue;
|
||||
}
|
||||
|
||||
p_argv = ecif->avalue;
|
||||
|
||||
/* point to next location */
|
||||
for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types; (i != 0); i--, p_arg++, p_argv++)
|
||||
{
|
||||
size_t z;
|
||||
|
||||
/* Move argp to address of argument */
|
||||
z = (*p_arg)->size;
|
||||
argp -= z;
|
||||
|
||||
/* Align if necessary */
|
||||
argp = (char *) ALIGN_DOWN(ALIGN_DOWN(argp, (*p_arg)->alignment), 4);
|
||||
|
||||
if (z < sizeof(int)) {
|
||||
z = sizeof(int);
|
||||
switch ((*p_arg)->type)
|
||||
{
|
||||
case FFI_TYPE_SINT8:
|
||||
*(signed int *) argp = (signed int)*(SINT8 *)(* p_argv);
|
||||
break;
|
||||
case FFI_TYPE_UINT8:
|
||||
*(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv);
|
||||
break;
|
||||
case FFI_TYPE_SINT16:
|
||||
*(signed int *) argp = (signed int)*(SINT16 *)(* p_argv);
|
||||
break;
|
||||
case FFI_TYPE_UINT16:
|
||||
*(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv);
|
||||
case FFI_TYPE_STRUCT:
|
||||
memcpy(argp, *p_argv, (*p_arg)->size);
|
||||
break;
|
||||
default:
|
||||
FFI_ASSERT(0);
|
||||
}
|
||||
} else if ( z == sizeof(int)) {
|
||||
*(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv);
|
||||
} else {
|
||||
memcpy(argp, *p_argv, z);
|
||||
}
|
||||
}
|
||||
|
||||
/* return the size of the arguments to be passed in registers,
|
||||
padded to an 8 byte boundary to preserve stack alignment */
|
||||
return ALIGN(MIN(stack - argp, 6*4), 8);
|
||||
}
|
||||
|
||||
/* Perform machine dependent cif processing */
|
||||
ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
|
||||
{
|
||||
ffi_type **ptr;
|
||||
unsigned i, bytes = 0;
|
||||
|
||||
for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) {
|
||||
if ((*ptr)->size == 0)
|
||||
return FFI_BAD_TYPEDEF;
|
||||
|
||||
/* Perform a sanity check on the argument type, do this
|
||||
check after the initialization. */
|
||||
FFI_ASSERT_VALID_TYPE(*ptr);
|
||||
|
||||
/* Add any padding if necessary */
|
||||
if (((*ptr)->alignment - 1) & bytes)
|
||||
bytes = ALIGN(bytes, (*ptr)->alignment);
|
||||
|
||||
bytes += ALIGN((*ptr)->size, 4);
|
||||
}
|
||||
|
||||
/* Ensure arg space is aligned to an 8-byte boundary */
|
||||
bytes = ALIGN(bytes, 8);
|
||||
|
||||
/* Make space for the return structure pointer */
|
||||
if (cif->rtype->type == FFI_TYPE_STRUCT) {
|
||||
bytes += sizeof(void*);
|
||||
|
||||
/* Ensure stack is aligned to an 8-byte boundary */
|
||||
bytes = ALIGN(bytes, 8);
|
||||
}
|
||||
|
||||
cif->bytes = bytes;
|
||||
|
||||
/* Set the return type flag */
|
||||
switch (cif->rtype->type) {
|
||||
case FFI_TYPE_VOID:
|
||||
case FFI_TYPE_FLOAT:
|
||||
case FFI_TYPE_DOUBLE:
|
||||
cif->flags = (unsigned) cif->rtype->type;
|
||||
break;
|
||||
case FFI_TYPE_SINT64:
|
||||
case FFI_TYPE_UINT64:
|
||||
cif->flags = (unsigned) FFI_TYPE_SINT64;
|
||||
break;
|
||||
case FFI_TYPE_STRUCT:
|
||||
/* Meta can store return values which are <= 64 bits */
|
||||
if (cif->rtype->size <= 4)
|
||||
/* Returned to D0Re0 as 32-bit value */
|
||||
cif->flags = (unsigned)FFI_TYPE_INT;
|
||||
else if ((cif->rtype->size > 4) && (cif->rtype->size <= 8))
|
||||
/* Returned valued is stored to D1Re0|R0Re0 */
|
||||
cif->flags = (unsigned)FFI_TYPE_DOUBLE;
|
||||
else
|
||||
/* value stored in memory */
|
||||
cif->flags = (unsigned)FFI_TYPE_STRUCT;
|
||||
break;
|
||||
default:
|
||||
cif->flags = (unsigned)FFI_TYPE_INT;
|
||||
break;
|
||||
}
|
||||
return FFI_OK;
|
||||
}
|
||||
|
||||
extern void ffi_call_SYSV(void (*fn)(void), extended_cif *, unsigned, unsigned, double *);
|
||||
|
||||
/*
|
||||
* Exported in API. Entry point
|
||||
* cif -> ffi_cif object
|
||||
* fn -> function pointer
|
||||
* rvalue -> pointer to return value
|
||||
* avalue -> vector of void * pointers pointing to memory locations holding the
|
||||
* arguments
|
||||
*/
|
||||
void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
|
||||
{
|
||||
extended_cif ecif;
|
||||
|
||||
int small_struct = (((cif->flags == FFI_TYPE_INT) || (cif->flags == FFI_TYPE_DOUBLE)) && (cif->rtype->type == FFI_TYPE_STRUCT));
|
||||
ecif.cif = cif;
|
||||
ecif.avalue = avalue;
|
||||
|
||||
double temp;
|
||||
|
||||
/*
|
||||
* 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))
|
||||
ecif.rvalue = alloca(cif->rtype->size);
|
||||
else if (small_struct)
|
||||
ecif.rvalue = &temp;
|
||||
else
|
||||
ecif.rvalue = rvalue;
|
||||
|
||||
switch (cif->abi) {
|
||||
case FFI_SYSV:
|
||||
ffi_call_SYSV(fn, &ecif, cif->bytes, cif->flags, ecif.rvalue);
|
||||
break;
|
||||
default:
|
||||
FFI_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (small_struct)
|
||||
memcpy (rvalue, &temp, cif->rtype->size);
|
||||
}
|
||||
|
||||
/* private members */
|
||||
|
||||
static void ffi_prep_incoming_args_SYSV (char *, void **, void **,
|
||||
ffi_cif*, float *);
|
||||
|
||||
void ffi_closure_SYSV (ffi_closure *);
|
||||
|
||||
/* Do NOT change that without changing the FFI_TRAMPOLINE_SIZE */
|
||||
extern unsigned int ffi_metag_trampoline[10]; /* 10 instructions */
|
||||
|
||||
/* end of private members */
|
||||
|
||||
/*
|
||||
* __tramp: trampoline memory location
|
||||
* __fun: assembly routine
|
||||
* __ctx: memory location for wrapper
|
||||
*
|
||||
* At this point, tramp[0] == __ctx !
|
||||
*/
|
||||
void ffi_init_trampoline(unsigned char *__tramp, unsigned int __fun, unsigned int __ctx) {
|
||||
memcpy (__tramp, ffi_metag_trampoline, sizeof(ffi_metag_trampoline));
|
||||
*(unsigned int*) &__tramp[40] = __ctx;
|
||||
*(unsigned int*) &__tramp[44] = __fun;
|
||||
/* This will flush the instruction cache */
|
||||
__builtin_meta2_cachewd(&__tramp[0], 1);
|
||||
__builtin_meta2_cachewd(&__tramp[47], 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* the cif must already be prepared */
|
||||
|
||||
ffi_status
|
||||
ffi_prep_closure_loc (ffi_closure *closure,
|
||||
ffi_cif* cif,
|
||||
void (*fun)(ffi_cif*,void*,void**,void*),
|
||||
void *user_data,
|
||||
void *codeloc)
|
||||
{
|
||||
void (*closure_func)(ffi_closure*) = NULL;
|
||||
|
||||
if (cif->abi == FFI_SYSV)
|
||||
closure_func = &ffi_closure_SYSV;
|
||||
else
|
||||
return FFI_BAD_ABI;
|
||||
|
||||
ffi_init_trampoline(
|
||||
(unsigned char*)&closure->tramp[0],
|
||||
(unsigned int)closure_func,
|
||||
(unsigned int)codeloc);
|
||||
|
||||
closure->cif = cif;
|
||||
closure->user_data = user_data;
|
||||
closure->fun = fun;
|
||||
|
||||
return FFI_OK;
|
||||
}
|
||||
|
||||
|
||||
/* This function is jumped to by the trampoline */
|
||||
unsigned int ffi_closure_SYSV_inner (closure, respp, args, vfp_args)
|
||||
ffi_closure *closure;
|
||||
void **respp;
|
||||
void *args;
|
||||
void *vfp_args;
|
||||
{
|
||||
ffi_cif *cif;
|
||||
void **arg_area;
|
||||
|
||||
cif = closure->cif;
|
||||
arg_area = (void**) alloca (cif->nargs * sizeof (void*));
|
||||
|
||||
/*
|
||||
* This call will initialize ARG_AREA, such that each
|
||||
* element in that array points to the corresponding
|
||||
* value on the stack; and if the function returns
|
||||
* a structure, it will re-set RESP to point to the
|
||||
* structure return address.
|
||||
*/
|
||||
ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif, vfp_args);
|
||||
|
||||
(closure->fun) ( cif, *respp, arg_area, closure->user_data);
|
||||
|
||||
return cif->flags;
|
||||
}
|
||||
|
||||
static void ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
|
||||
void **avalue, ffi_cif *cif,
|
||||
float *vfp_stack)
|
||||
{
|
||||
register unsigned int i;
|
||||
register void **p_argv;
|
||||
register char *argp;
|
||||
register ffi_type **p_arg;
|
||||
|
||||
/* stack points to original arguments */
|
||||
argp = stack;
|
||||
|
||||
/* Store return value */
|
||||
if ( cif->flags == FFI_TYPE_STRUCT ) {
|
||||
argp -= 4;
|
||||
*rvalue = *(void **) argp;
|
||||
}
|
||||
|
||||
p_argv = avalue;
|
||||
|
||||
for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++) {
|
||||
size_t z;
|
||||
size_t alignment;
|
||||
|
||||
alignment = (*p_arg)->alignment;
|
||||
if (alignment < 4)
|
||||
alignment = 4;
|
||||
if ((alignment - 1) & (unsigned)argp)
|
||||
argp = (char *) ALIGN(argp, alignment);
|
||||
|
||||
z = (*p_arg)->size;
|
||||
*p_argv = (void*) argp;
|
||||
p_argv++;
|
||||
argp -= z;
|
||||
}
|
||||
return;
|
||||
}
|
||||
Reference in New Issue
Block a user