285 lines
7.2 KiB
C
285 lines
7.2 KiB
C
/* -----------------------------------------------------------------------
|
|
ffi.c - 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
|
|
``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 AUTHORS OR COPYRIGHT
|
|
HOLDERS 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>
|
|
|
|
/* Force FFI_TYPE_LONGDOUBLE to be different than FFI_TYPE_DOUBLE;
|
|
all further uses in this file will refer to the 128-bit type. */
|
|
#if defined(__LONG_DOUBLE_128__)
|
|
# if FFI_TYPE_LONGDOUBLE != 4
|
|
# error FFI_TYPE_LONGDOUBLE out of date
|
|
# endif
|
|
#else
|
|
# undef FFI_TYPE_LONGDOUBLE
|
|
# define FFI_TYPE_LONGDOUBLE 4
|
|
#endif
|
|
|
|
extern void ffi_call_osf(void *, unsigned long, unsigned, void *, void (*)(void))
|
|
FFI_HIDDEN;
|
|
extern void ffi_closure_osf(void) FFI_HIDDEN;
|
|
|
|
|
|
ffi_status
|
|
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;
|
|
|
|
/* Set the return type flag */
|
|
switch (cif->rtype->type)
|
|
{
|
|
case FFI_TYPE_STRUCT:
|
|
case FFI_TYPE_FLOAT:
|
|
case FFI_TYPE_DOUBLE:
|
|
cif->flags = cif->rtype->type;
|
|
break;
|
|
|
|
case FFI_TYPE_LONGDOUBLE:
|
|
/* 128-bit long double is returned in memory, like a struct. */
|
|
cif->flags = FFI_TYPE_STRUCT;
|
|
break;
|
|
|
|
default:
|
|
cif->flags = FFI_TYPE_INT;
|
|
break;
|
|
}
|
|
|
|
return FFI_OK;
|
|
}
|
|
|
|
|
|
void
|
|
ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
|
|
{
|
|
unsigned long *stack, *argp;
|
|
long i, avn;
|
|
ffi_type **arg_types;
|
|
|
|
/* 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)
|
|
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);
|
|
|
|
if (cif->flags == FFI_TYPE_STRUCT)
|
|
*(void **) argp++ = rvalue;
|
|
|
|
i = 0;
|
|
avn = cif->nargs;
|
|
arg_types = cif->arg_types;
|
|
|
|
while (i < avn)
|
|
{
|
|
size_t size = (*arg_types)->size;
|
|
|
|
switch ((*arg_types)->type)
|
|
{
|
|
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);
|
|
break;
|
|
|
|
case FFI_TYPE_LONGDOUBLE:
|
|
/* 128-bit long double is passed by reference. */
|
|
*(long double **) argp = (long double *)(* avalue);
|
|
size = sizeof (long double *);
|
|
break;
|
|
|
|
case FFI_TYPE_STRUCT:
|
|
memcpy(argp, *avalue, (*arg_types)->size);
|
|
break;
|
|
|
|
default:
|
|
FFI_ASSERT(0);
|
|
}
|
|
|
|
argp += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
|
|
i++, arg_types++, avalue++;
|
|
}
|
|
|
|
ffi_call_osf(stack, cif->bytes, cif->flags, rvalue, fn);
|
|
}
|
|
|
|
|
|
ffi_status
|
|
ffi_prep_closure_loc (ffi_closure* closure,
|
|
ffi_cif* cif,
|
|
void (*fun)(ffi_cif*, void*, void**, void*),
|
|
void *user_data,
|
|
void *codeloc)
|
|
{
|
|
unsigned int *tramp;
|
|
|
|
tramp = (unsigned int *) &closure->tramp[0];
|
|
tramp[0] = 0x47fb0401; /* mov $27,$1 */
|
|
tramp[1] = 0xa77b0010; /* ldq $27,16($27) */
|
|
tramp[2] = 0x6bfb0000; /* jmp $31,($27),0 */
|
|
tramp[3] = 0x47ff041f; /* nop */
|
|
*(void **) &tramp[4] = ffi_closure_osf;
|
|
|
|
closure->cif = cif;
|
|
closure->fun = fun;
|
|
closure->user_data = user_data;
|
|
|
|
/* Flush the Icache.
|
|
|
|
Tru64 UNIX as doesn't understand the imb mnemonic, so use call_pal
|
|
instead, since both Compaq as and gas can handle it.
|
|
|
|
0x86 is PAL_imb in Tru64 UNIX <alpha/pal.h>. */
|
|
asm volatile ("call_pal 0x86" : : : "memory");
|
|
|
|
return FFI_OK;
|
|
}
|
|
|
|
|
|
long FFI_HIDDEN
|
|
ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
|
|
{
|
|
ffi_cif *cif;
|
|
void **avalue;
|
|
ffi_type **arg_types;
|
|
long i, avn, argn;
|
|
|
|
cif = closure->cif;
|
|
avalue = alloca(cif->nargs * sizeof(void *));
|
|
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
size_t size = arg_types[i]->size;
|
|
|
|
switch (arg_types[i]->type)
|
|
{
|
|
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_STRUCT:
|
|
avalue[i] = &argp[argn];
|
|
break;
|
|
|
|
case FFI_TYPE_FLOAT:
|
|
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];
|
|
}
|
|
else
|
|
avalue[i] = &argp[argn];
|
|
break;
|
|
|
|
case FFI_TYPE_DOUBLE:
|
|
avalue[i] = &argp[argn - (argn < 6 ? 6 : 0)];
|
|
break;
|
|
|
|
case FFI_TYPE_LONGDOUBLE:
|
|
/* 128-bit long double is passed by reference. */
|
|
avalue[i] = (long double *) argp[argn];
|
|
size = sizeof (long double *);
|
|
break;
|
|
|
|
default:
|
|
abort ();
|
|
}
|
|
|
|
argn += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
|
|
i++;
|
|
}
|
|
|
|
/* Invoke the closure. */
|
|
closure->fun (cif, rvalue, avalue, closure->user_data);
|
|
|
|
/* Tell ffi_closure_osf how to perform return type promotions. */
|
|
return cif->rtype->type;
|
|
}
|