356 lines
10 KiB
C
356 lines
10 KiB
C
|
/* -----------------------------------------------------------------------
|
||
|
ffi.c - Copyright (c) 2012 Tilera Corp.
|
||
|
|
||
|
TILE 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>
|
||
|
#include <stdint.h>
|
||
|
#include <unistd.h>
|
||
|
#include <arch/abi.h>
|
||
|
#include <arch/icache.h>
|
||
|
#include <arch/opcode.h>
|
||
|
|
||
|
|
||
|
/* The first 10 registers are used to pass arguments and return values. */
|
||
|
#define NUM_ARG_REGS 10
|
||
|
|
||
|
/* Performs a raw function call with the given NUM_ARG_REGS register arguments
|
||
|
and the specified additional stack arguments (if any). */
|
||
|
extern void ffi_call_tile(ffi_sarg reg_args[NUM_ARG_REGS],
|
||
|
const ffi_sarg *stack_args,
|
||
|
size_t stack_args_bytes,
|
||
|
void (*fnaddr)(void))
|
||
|
FFI_HIDDEN;
|
||
|
|
||
|
/* This handles the raw call from the closure stub, cleaning up the
|
||
|
parameters and delegating to ffi_closure_tile_inner. */
|
||
|
extern void ffi_closure_tile(void) FFI_HIDDEN;
|
||
|
|
||
|
|
||
|
ffi_status
|
||
|
ffi_prep_cif_machdep(ffi_cif *cif)
|
||
|
{
|
||
|
/* We always allocate room for all registers. Even if we don't
|
||
|
use them as parameters, they get returned in the same array
|
||
|
as struct return values so we need to make room. */
|
||
|
if (cif->bytes < NUM_ARG_REGS * FFI_SIZEOF_ARG)
|
||
|
cif->bytes = NUM_ARG_REGS * FFI_SIZEOF_ARG;
|
||
|
|
||
|
if (cif->rtype->size > NUM_ARG_REGS * FFI_SIZEOF_ARG)
|
||
|
cif->flags = FFI_TYPE_STRUCT;
|
||
|
else
|
||
|
cif->flags = FFI_TYPE_INT;
|
||
|
|
||
|
/* Nothing to do. */
|
||
|
return FFI_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
static long
|
||
|
assign_to_ffi_arg(ffi_sarg *out, void *in, const ffi_type *type,
|
||
|
int write_to_reg)
|
||
|
{
|
||
|
switch (type->type)
|
||
|
{
|
||
|
case FFI_TYPE_SINT8:
|
||
|
*out = *(SINT8 *)in;
|
||
|
return 1;
|
||
|
|
||
|
case FFI_TYPE_UINT8:
|
||
|
*out = *(UINT8 *)in;
|
||
|
return 1;
|
||
|
|
||
|
case FFI_TYPE_SINT16:
|
||
|
*out = *(SINT16 *)in;
|
||
|
return 1;
|
||
|
|
||
|
case FFI_TYPE_UINT16:
|
||
|
*out = *(UINT16 *)in;
|
||
|
return 1;
|
||
|
|
||
|
case FFI_TYPE_SINT32:
|
||
|
case FFI_TYPE_UINT32:
|
||
|
#ifndef __LP64__
|
||
|
case FFI_TYPE_POINTER:
|
||
|
#endif
|
||
|
/* Note that even unsigned 32-bit quantities are sign extended
|
||
|
on tilegx when stored in a register. */
|
||
|
*out = *(SINT32 *)in;
|
||
|
return 1;
|
||
|
|
||
|
case FFI_TYPE_FLOAT:
|
||
|
#ifdef __tilegx__
|
||
|
if (write_to_reg)
|
||
|
{
|
||
|
/* Properly sign extend the value. */
|
||
|
union { float f; SINT32 s32; } val;
|
||
|
val.f = *(float *)in;
|
||
|
*out = val.s32;
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
*(float *)out = *(float *)in;
|
||
|
}
|
||
|
return 1;
|
||
|
|
||
|
case FFI_TYPE_SINT64:
|
||
|
case FFI_TYPE_UINT64:
|
||
|
case FFI_TYPE_DOUBLE:
|
||
|
#ifdef __LP64__
|
||
|
case FFI_TYPE_POINTER:
|
||
|
#endif
|
||
|
*(UINT64 *)out = *(UINT64 *)in;
|
||
|
return sizeof(UINT64) / FFI_SIZEOF_ARG;
|
||
|
|
||
|
case FFI_TYPE_STRUCT:
|
||
|
memcpy(out, in, type->size);
|
||
|
return (type->size + FFI_SIZEOF_ARG - 1) / FFI_SIZEOF_ARG;
|
||
|
|
||
|
case FFI_TYPE_VOID:
|
||
|
/* Must be a return type. Nothing to do. */
|
||
|
return 0;
|
||
|
|
||
|
default:
|
||
|
FFI_ASSERT(0);
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
|
||
|
{
|
||
|
ffi_sarg * const arg_mem = alloca(cif->bytes);
|
||
|
ffi_sarg * const reg_args = arg_mem;
|
||
|
ffi_sarg * const stack_args = ®_args[NUM_ARG_REGS];
|
||
|
ffi_sarg *argp = arg_mem;
|
||
|
ffi_type ** const arg_types = cif->arg_types;
|
||
|
const long num_args = cif->nargs;
|
||
|
long i;
|
||
|
|
||
|
if (cif->flags == FFI_TYPE_STRUCT)
|
||
|
{
|
||
|
/* Pass a hidden pointer to the return value. We make sure there
|
||
|
is scratch space for the callee to store the return value even if
|
||
|
our caller doesn't care about it. */
|
||
|
*argp++ = (intptr_t)(rvalue ? rvalue : alloca(cif->rtype->size));
|
||
|
|
||
|
/* No more work needed to return anything. */
|
||
|
rvalue = NULL;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < num_args; i++)
|
||
|
{
|
||
|
ffi_type *type = arg_types[i];
|
||
|
void * const arg_in = avalue[i];
|
||
|
ptrdiff_t arg_word = argp - arg_mem;
|
||
|
|
||
|
#ifndef __tilegx__
|
||
|
/* Doubleword-aligned values are always in an even-number register
|
||
|
pair, or doubleword-aligned stack slot if out of registers. */
|
||
|
long align = arg_word & (type->alignment > FFI_SIZEOF_ARG);
|
||
|
argp += align;
|
||
|
arg_word += align;
|
||
|
#endif
|
||
|
|
||
|
if (type->type == FFI_TYPE_STRUCT)
|
||
|
{
|
||
|
const size_t arg_size_in_words =
|
||
|
(type->size + FFI_SIZEOF_ARG - 1) / FFI_SIZEOF_ARG;
|
||
|
|
||
|
if (arg_word < NUM_ARG_REGS &&
|
||
|
arg_word + arg_size_in_words > NUM_ARG_REGS)
|
||
|
{
|
||
|
/* Args are not allowed to span registers and the stack. */
|
||
|
argp = stack_args;
|
||
|
}
|
||
|
|
||
|
memcpy(argp, arg_in, type->size);
|
||
|
argp += arg_size_in_words;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
argp += assign_to_ffi_arg(argp, arg_in, arg_types[i], 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Actually do the call. */
|
||
|
ffi_call_tile(reg_args, stack_args,
|
||
|
cif->bytes - (NUM_ARG_REGS * FFI_SIZEOF_ARG), fn);
|
||
|
|
||
|
if (rvalue != NULL)
|
||
|
assign_to_ffi_arg(rvalue, reg_args, cif->rtype, 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Template code for closure. */
|
||
|
extern const UINT64 ffi_template_tramp_tile[] FFI_HIDDEN;
|
||
|
|
||
|
|
||
|
ffi_status
|
||
|
ffi_prep_closure_loc (ffi_closure *closure,
|
||
|
ffi_cif *cif,
|
||
|
void (*fun)(ffi_cif*, void*, void**, void*),
|
||
|
void *user_data,
|
||
|
void *codeloc)
|
||
|
{
|
||
|
#ifdef __tilegx__
|
||
|
/* TILE-Gx */
|
||
|
SINT64 c;
|
||
|
SINT64 h;
|
||
|
int s;
|
||
|
UINT64 *out;
|
||
|
|
||
|
if (cif->abi != FFI_UNIX)
|
||
|
return FFI_BAD_ABI;
|
||
|
|
||
|
out = (UINT64 *)closure->tramp;
|
||
|
|
||
|
c = (intptr_t)closure;
|
||
|
h = (intptr_t)ffi_closure_tile;
|
||
|
s = 0;
|
||
|
|
||
|
/* Find the smallest shift count that doesn't lose information
|
||
|
(i.e. no need to explicitly insert high bits of the address that
|
||
|
are just the sign extension of the low bits). */
|
||
|
while ((c >> s) != (SINT16)(c >> s) || (h >> s) != (SINT16)(h >> s))
|
||
|
s += 16;
|
||
|
|
||
|
#define OPS(a, b, shift) \
|
||
|
(create_Imm16_X0((a) >> (shift)) | create_Imm16_X1((b) >> (shift)))
|
||
|
|
||
|
/* Emit the moveli. */
|
||
|
*out++ = ffi_template_tramp_tile[0] | OPS(c, h, s);
|
||
|
for (s -= 16; s >= 0; s -= 16)
|
||
|
*out++ = ffi_template_tramp_tile[1] | OPS(c, h, s);
|
||
|
|
||
|
#undef OPS
|
||
|
|
||
|
*out++ = ffi_template_tramp_tile[2];
|
||
|
|
||
|
#else
|
||
|
/* TILEPro */
|
||
|
UINT64 *out;
|
||
|
intptr_t delta;
|
||
|
|
||
|
if (cif->abi != FFI_UNIX)
|
||
|
return FFI_BAD_ABI;
|
||
|
|
||
|
out = (UINT64 *)closure->tramp;
|
||
|
delta = (intptr_t)ffi_closure_tile - (intptr_t)codeloc;
|
||
|
|
||
|
*out++ = ffi_template_tramp_tile[0] | create_JOffLong_X1(delta >> 3);
|
||
|
#endif
|
||
|
|
||
|
closure->cif = cif;
|
||
|
closure->fun = fun;
|
||
|
closure->user_data = user_data;
|
||
|
|
||
|
invalidate_icache(closure->tramp, (char *)out - closure->tramp,
|
||
|
getpagesize());
|
||
|
|
||
|
return FFI_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* This is called by the assembly wrapper for closures. This does
|
||
|
all of the work. On entry reg_args[0] holds the values the registers
|
||
|
had when the closure was invoked. On return reg_args[1] holds the register
|
||
|
values to be returned to the caller (many of which may be garbage). */
|
||
|
void FFI_HIDDEN
|
||
|
ffi_closure_tile_inner(ffi_closure *closure,
|
||
|
ffi_sarg reg_args[2][NUM_ARG_REGS],
|
||
|
ffi_sarg *stack_args)
|
||
|
{
|
||
|
ffi_cif * const cif = closure->cif;
|
||
|
void ** const avalue = alloca(cif->nargs * sizeof(void *));
|
||
|
void *rvalue;
|
||
|
ffi_type ** const arg_types = cif->arg_types;
|
||
|
ffi_sarg * const reg_args_in = reg_args[0];
|
||
|
ffi_sarg * const reg_args_out = reg_args[1];
|
||
|
ffi_sarg * argp;
|
||
|
long i, arg_word, nargs = cif->nargs;
|
||
|
/* Use a union to guarantee proper alignment for double. */
|
||
|
union { ffi_sarg arg[NUM_ARG_REGS]; double d; UINT64 u64; } closure_ret;
|
||
|
|
||
|
/* Start out reading register arguments. */
|
||
|
argp = reg_args_in;
|
||
|
|
||
|
/* Copy the caller's structure return address to that the closure
|
||
|
returns the data directly to the caller. */
|
||
|
if (cif->flags == FFI_TYPE_STRUCT)
|
||
|
{
|
||
|
/* Return by reference via hidden pointer. */
|
||
|
rvalue = (void *)(intptr_t)*argp++;
|
||
|
arg_word = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Return the value in registers. */
|
||
|
rvalue = &closure_ret;
|
||
|
arg_word = 0;
|
||
|
}
|
||
|
|
||
|
/* Grab the addresses of the arguments. */
|
||
|
for (i = 0; i < nargs; i++)
|
||
|
{
|
||
|
ffi_type * const type = arg_types[i];
|
||
|
const size_t arg_size_in_words =
|
||
|
(type->size + FFI_SIZEOF_ARG - 1) / FFI_SIZEOF_ARG;
|
||
|
|
||
|
#ifndef __tilegx__
|
||
|
/* Doubleword-aligned values are always in an even-number register
|
||
|
pair, or doubleword-aligned stack slot if out of registers. */
|
||
|
long align = arg_word & (type->alignment > FFI_SIZEOF_ARG);
|
||
|
argp += align;
|
||
|
arg_word += align;
|
||
|
#endif
|
||
|
|
||
|
if (arg_word == NUM_ARG_REGS ||
|
||
|
(arg_word < NUM_ARG_REGS &&
|
||
|
arg_word + arg_size_in_words > NUM_ARG_REGS))
|
||
|
{
|
||
|
/* Switch to reading arguments from the stack. */
|
||
|
argp = stack_args;
|
||
|
arg_word = NUM_ARG_REGS;
|
||
|
}
|
||
|
|
||
|
avalue[i] = argp;
|
||
|
argp += arg_size_in_words;
|
||
|
arg_word += arg_size_in_words;
|
||
|
}
|
||
|
|
||
|
/* Invoke the closure. */
|
||
|
closure->fun(cif, rvalue, avalue, closure->user_data);
|
||
|
|
||
|
if (cif->flags != FFI_TYPE_STRUCT)
|
||
|
{
|
||
|
/* Canonicalize for register representation. */
|
||
|
assign_to_ffi_arg(reg_args_out, &closure_ret, cif->rtype, 1);
|
||
|
}
|
||
|
}
|