| /* ----------------------------------------------------------------------- | |
| ffi_sysv.c - Copyright (C) 2013 IBM | |
| Copyright (C) 2011 Anthony Green | |
| Copyright (C) 2011 Kyle Moffett | |
| Copyright (C) 2008 Red Hat, Inc | |
| Copyright (C) 2007, 2008 Free Software Foundation, Inc | |
| Copyright (c) 1998 Geoffrey Keating | |
| PowerPC 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 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 <tramp.h> | |
| #ifndef POWERPC64 | |
| #include "ffi_common.h" | |
| #include "ffi_powerpc.h" | |
| /* About the SYSV ABI. */ | |
| #define ASM_NEEDS_REGISTERS 6 | |
| #define NUM_GPR_ARG_REGISTERS 8 | |
| #define NUM_FPR_ARG_REGISTERS 8 | |
| #if HAVE_LONG_DOUBLE_VARIANT && FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
| /* Adjust size of ffi_type_longdouble. */ | |
| void FFI_HIDDEN | |
| ffi_prep_types_sysv (ffi_abi abi) | |
| { | |
| if ((abi & (FFI_SYSV | FFI_SYSV_LONG_DOUBLE_128)) == FFI_SYSV) | |
| { | |
| ffi_type_longdouble.size = 8; | |
| ffi_type_longdouble.alignment = 8; | |
| } | |
| else | |
| { | |
| ffi_type_longdouble.size = 16; | |
| ffi_type_longdouble.alignment = 16; | |
| } | |
| } | |
| #endif | |
| /* Transform long double, double and float to other types as per abi. */ | |
| static int | |
| translate_float (int abi, int type) | |
| { | |
| #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
| if (type == FFI_TYPE_LONGDOUBLE | |
| && (abi & FFI_SYSV_LONG_DOUBLE_128) == 0) | |
| type = FFI_TYPE_DOUBLE; | |
| #endif | |
| if ((abi & FFI_SYSV_SOFT_FLOAT) != 0) | |
| { | |
| if (type == FFI_TYPE_FLOAT) | |
| type = FFI_TYPE_UINT32; | |
| else if (type == FFI_TYPE_DOUBLE) | |
| type = FFI_TYPE_UINT64; | |
| #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
| else if (type == FFI_TYPE_LONGDOUBLE) | |
| type = FFI_TYPE_UINT128; | |
| } | |
| else if ((abi & FFI_SYSV_IBM_LONG_DOUBLE) == 0) | |
| { | |
| if (type == FFI_TYPE_LONGDOUBLE) | |
| type = FFI_TYPE_STRUCT; | |
| #endif | |
| } | |
| return type; | |
| } | |
| /* Perform machine dependent cif processing */ | |
| static ffi_status | |
| ffi_prep_cif_sysv_core (ffi_cif *cif) | |
| { | |
| ffi_type **ptr; | |
| unsigned bytes; | |
| unsigned i, fpr_count = 0, gpr_count = 0, stack_count = 0; | |
| unsigned flags = cif->flags; | |
| unsigned struct_copy_size = 0; | |
| unsigned type = cif->rtype->type; | |
| unsigned size = cif->rtype->size; | |
| /* The machine-independent calculation of cif->bytes doesn't work | |
| for us. Redo the calculation. */ | |
| /* 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 for SYSV 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; | |
| - Larger structures are allocated space and a pointer is passed as | |
| the first argument. | |
| - Single/double FP values are returned in fpr1; | |
| - long doubles (if not equivalent to double) are returned in | |
| fpr1,fpr2 for Linux and as for large structs for SysV. */ | |
| type = translate_float (cif->abi, type); | |
| switch (type) | |
| { | |
| #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
| case FFI_TYPE_LONGDOUBLE: | |
| flags |= FLAG_RETURNS_128BITS; | |
| /* Fall through. */ | |
| #endif | |
| case FFI_TYPE_DOUBLE: | |
| flags |= FLAG_RETURNS_64BITS; | |
| /* Fall through. */ | |
| case FFI_TYPE_FLOAT: | |
| flags |= FLAG_RETURNS_FP; | |
| #ifdef __NO_FPRS__ | |
| return FFI_BAD_ABI; | |
| #endif | |
| break; | |
| case FFI_TYPE_UINT128: | |
| flags |= FLAG_RETURNS_128BITS; | |
| /* Fall through. */ | |
| case FFI_TYPE_UINT64: | |
| case FFI_TYPE_SINT64: | |
| flags |= FLAG_RETURNS_64BITS; | |
| break; | |
| case FFI_TYPE_STRUCT: | |
| /* The final SYSV ABI says that structures smaller or equal 8 bytes | |
| are returned in r3/r4. A draft ABI used by linux instead | |
| returns them in memory. */ | |
| if ((cif->abi & FFI_SYSV_STRUCT_RET) != 0 && size <= 8) | |
| { | |
| flags |= FLAG_RETURNS_SMST; | |
| break; | |
| } | |
| gpr_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++) | |
| { | |
| unsigned short typenum = (*ptr)->type; | |
| typenum = translate_float (cif->abi, typenum); | |
| switch (typenum) | |
| { | |
| #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
| case FFI_TYPE_LONGDOUBLE: | |
| if (fpr_count >= NUM_FPR_ARG_REGISTERS - 1) | |
| { | |
| fpr_count = NUM_FPR_ARG_REGISTERS; | |
| /* 8-byte align long doubles. */ | |
| stack_count += stack_count & 1; | |
| stack_count += 4; | |
| } | |
| else | |
| fpr_count += 2; | |
| #ifdef __NO_FPRS__ | |
| return FFI_BAD_ABI; | |
| #endif | |
| break; | |
| #endif | |
| case FFI_TYPE_DOUBLE: | |
| if (fpr_count >= NUM_FPR_ARG_REGISTERS) | |
| { | |
| /* 8-byte align doubles. */ | |
| stack_count += stack_count & 1; | |
| stack_count += 2; | |
| } | |
| else | |
| fpr_count += 1; | |
| #ifdef __NO_FPRS__ | |
| return FFI_BAD_ABI; | |
| #endif | |
| break; | |
| case FFI_TYPE_FLOAT: | |
| if (fpr_count >= NUM_FPR_ARG_REGISTERS) | |
| /* Yes, we don't follow the ABI, but neither does gcc. */ | |
| stack_count += 1; | |
| else | |
| fpr_count += 1; | |
| #ifdef __NO_FPRS__ | |
| return FFI_BAD_ABI; | |
| #endif | |
| break; | |
| case FFI_TYPE_UINT128: | |
| /* A long double in FFI_LINUX_SOFT_FLOAT can use only a set | |
| of four consecutive gprs. If we do not have enough, we | |
| have to adjust the gpr_count value. */ | |
| if (gpr_count >= NUM_GPR_ARG_REGISTERS - 3) | |
| gpr_count = NUM_GPR_ARG_REGISTERS; | |
| if (gpr_count >= NUM_GPR_ARG_REGISTERS) | |
| stack_count += 4; | |
| else | |
| gpr_count += 4; | |
| 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. | |
| Also, only certain register pairs can be used for | |
| passing long long int -- specifically (r3,r4), (r5,r6), | |
| (r7,r8), (r9,r10). */ | |
| gpr_count += gpr_count & 1; | |
| if (gpr_count >= NUM_GPR_ARG_REGISTERS) | |
| { | |
| stack_count += stack_count & 1; | |
| stack_count += 2; | |
| } | |
| else | |
| gpr_count += 2; | |
| break; | |
| case FFI_TYPE_STRUCT: | |
| /* 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). */ | |
| case FFI_TYPE_POINTER: | |
| case FFI_TYPE_INT: | |
| case FFI_TYPE_UINT32: | |
| case FFI_TYPE_SINT32: | |
| case FFI_TYPE_UINT16: | |
| case FFI_TYPE_SINT16: | |
| case FFI_TYPE_UINT8: | |
| case FFI_TYPE_SINT8: | |
| /* Everything else is passed as a 4-byte word in a GPR, either | |
| the object itself or a pointer to it. */ | |
| if (gpr_count >= NUM_GPR_ARG_REGISTERS) | |
| stack_count += 1; | |
| else | |
| gpr_count += 1; | |
| break; | |
| default: | |
| FFI_ASSERT (0); | |
| } | |
| } | |
| if (fpr_count != 0) | |
| flags |= FLAG_FP_ARGUMENTS; | |
| if (gpr_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 (fpr_count != 0) | |
| bytes += NUM_FPR_ARG_REGISTERS * sizeof (double); | |
| /* Stack space. */ | |
| bytes += stack_count * sizeof (int); | |
| /* 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; | |
| } | |
| ffi_status FFI_HIDDEN | |
| ffi_prep_cif_sysv (ffi_cif *cif) | |
| { | |
| if ((cif->abi & FFI_SYSV) == 0) | |
| { | |
| /* This call is from old code. Translate to new ABI values. */ | |
| cif->flags |= FLAG_COMPAT; | |
| switch (cif->abi) | |
| { | |
| default: | |
| return FFI_BAD_ABI; | |
| case FFI_COMPAT_SYSV: | |
| cif->abi = FFI_SYSV | FFI_SYSV_STRUCT_RET | FFI_SYSV_LONG_DOUBLE_128; | |
| break; | |
| case FFI_COMPAT_GCC_SYSV: | |
| cif->abi = FFI_SYSV | FFI_SYSV_LONG_DOUBLE_128; | |
| break; | |
| case FFI_COMPAT_LINUX: | |
| cif->abi = (FFI_SYSV | FFI_SYSV_IBM_LONG_DOUBLE | |
| | FFI_SYSV_LONG_DOUBLE_128); | |
| break; | |
| case FFI_COMPAT_LINUX_SOFT_FLOAT: | |
| cif->abi = (FFI_SYSV | FFI_SYSV_SOFT_FLOAT | FFI_SYSV_IBM_LONG_DOUBLE | |
| | FFI_SYSV_LONG_DOUBLE_128); | |
| break; | |
| } | |
| } | |
| return ffi_prep_cif_sysv_core (cif); | |
| } | |
| /* ffi_prep_args_SYSV 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 | |
| */ | |
| void FFI_HIDDEN | |
| ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack) | |
| { | |
| const unsigned bytes = ecif->cif->bytes; | |
| const unsigned flags = ecif->cif->flags; | |
| typedef union | |
| { | |
| char *c; | |
| unsigned *u; | |
| long long *ll; | |
| float *f; | |
| double *d; | |
| } valp; | |
| /* 'stacktop' points at the previous backchain pointer. */ | |
| valp stacktop; | |
| /* 'gpr_base' points at the space for gpr3, and grows upwards as | |
| we use GPR registers. */ | |
| valp gpr_base; | |
| valp gpr_end; | |
| #ifndef __NO_FPRS__ | |
| /* 'fpr_base' points at the space for fpr1, and grows upwards as | |
| we use FPR registers. */ | |
| valp fpr_base; | |
| valp fpr_end; | |
| #endif | |
| /* 'copy_space' grows down as we put structures in it. It should | |
| stay 16-byte aligned. */ | |
| valp copy_space; | |
| /* 'next_arg' grows up as we put parameters in it. */ | |
| valp next_arg; | |
| int i; | |
| ffi_type **ptr; | |
| #ifndef __NO_FPRS__ | |
| double double_tmp; | |
| #endif | |
| union | |
| { | |
| void **v; | |
| char **c; | |
| signed char **sc; | |
| unsigned char **uc; | |
| signed short **ss; | |
| unsigned short **us; | |
| unsigned int **ui; | |
| long long **ll; | |
| float **f; | |
| double **d; | |
| } p_argv; | |
| size_t struct_copy_size; | |
| unsigned gprvalue; | |
| stacktop.c = (char *) stack + bytes; | |
| gpr_end.u = stacktop.u - ASM_NEEDS_REGISTERS; | |
| gpr_base.u = gpr_end.u - NUM_GPR_ARG_REGISTERS; | |
| #ifndef __NO_FPRS__ | |
| fpr_end.d = gpr_base.d; | |
| fpr_base.d = fpr_end.d - NUM_FPR_ARG_REGISTERS; | |
| copy_space.c = ((flags & FLAG_FP_ARGUMENTS) ? fpr_base.c : gpr_base.c); | |
| #else | |
| copy_space.c = gpr_base.c; | |
| #endif | |
| next_arg.u = stack + 2; | |
| /* Check that everything starts aligned properly. */ | |
| FFI_ASSERT (((unsigned long) (char *) stack & 0xF) == 0); | |
| FFI_ASSERT (((unsigned long) copy_space.c & 0xF) == 0); | |
| FFI_ASSERT (((unsigned long) stacktop.c & 0xF) == 0); | |
| FFI_ASSERT ((bytes & 0xF) == 0); | |
| FFI_ASSERT (copy_space.c >= next_arg.c); | |
| /* Deal with return values that are actually pass-by-reference. */ | |
| if (flags & FLAG_RETVAL_REFERENCE) | |
| *gpr_base.u++ = (unsigned) (char *) ecif->rvalue; | |
| /* Now for the arguments. */ | |
| p_argv.v = ecif->avalue; | |
| for (ptr = ecif->cif->arg_types, i = ecif->cif->nargs; | |
| i > 0; | |
| i--, ptr++, p_argv.v++) | |
| { | |
| unsigned int typenum = (*ptr)->type; | |
| typenum = translate_float (ecif->cif->abi, typenum); | |
| /* Now test the translated value */ | |
| switch (typenum) | |
| { | |
| #ifndef __NO_FPRS__ | |
| # if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
| case FFI_TYPE_LONGDOUBLE: | |
| double_tmp = (*p_argv.d)[0]; | |
| if (fpr_base.d >= fpr_end.d - 1) | |
| { | |
| fpr_base.d = fpr_end.d; | |
| if (((next_arg.u - stack) & 1) != 0) | |
| next_arg.u += 1; | |
| *next_arg.d = double_tmp; | |
| next_arg.u += 2; | |
| double_tmp = (*p_argv.d)[1]; | |
| *next_arg.d = double_tmp; | |
| next_arg.u += 2; | |
| } | |
| else | |
| { | |
| *fpr_base.d++ = double_tmp; | |
| double_tmp = (*p_argv.d)[1]; | |
| *fpr_base.d++ = double_tmp; | |
| } | |
| FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); | |
| break; | |
| # endif | |
| case FFI_TYPE_DOUBLE: | |
| double_tmp = **p_argv.d; | |
| if (fpr_base.d >= fpr_end.d) | |
| { | |
| if (((next_arg.u - stack) & 1) != 0) | |
| next_arg.u += 1; | |
| *next_arg.d = double_tmp; | |
| next_arg.u += 2; | |
| } | |
| else | |
| *fpr_base.d++ = double_tmp; | |
| FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); | |
| break; | |
| case FFI_TYPE_FLOAT: | |
| double_tmp = **p_argv.f; | |
| if (fpr_base.d >= fpr_end.d) | |
| { | |
| *next_arg.f = (float) double_tmp; | |
| next_arg.u += 1; | |
| } | |
| else | |
| *fpr_base.d++ = double_tmp; | |
| FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); | |
| break; | |
| #endif /* have FPRs */ | |
| case FFI_TYPE_UINT128: | |
| /* The soft float ABI for long doubles works like this, a long double | |
| is passed in four consecutive GPRs if available. A maximum of 2 | |
| long doubles can be passed in gprs. If we do not have 4 GPRs | |
| left, the long double is passed on the stack, 4-byte aligned. */ | |
| if (gpr_base.u >= gpr_end.u - 3) | |
| { | |
| unsigned int ii; | |
| gpr_base.u = gpr_end.u; | |
| for (ii = 0; ii < 4; ii++) | |
| { | |
| unsigned int int_tmp = (*p_argv.ui)[ii]; | |
| *next_arg.u++ = int_tmp; | |
| } | |
| } | |
| else | |
| { | |
| unsigned int ii; | |
| for (ii = 0; ii < 4; ii++) | |
| { | |
| unsigned int int_tmp = (*p_argv.ui)[ii]; | |
| *gpr_base.u++ = int_tmp; | |
| } | |
| } | |
| break; | |
| case FFI_TYPE_UINT64: | |
| case FFI_TYPE_SINT64: | |
| if (gpr_base.u >= gpr_end.u - 1) | |
| { | |
| gpr_base.u = gpr_end.u; | |
| if (((next_arg.u - stack) & 1) != 0) | |
| next_arg.u++; | |
| *next_arg.ll = **p_argv.ll; | |
| next_arg.u += 2; | |
| } | |
| else | |
| { | |
| /* The abi states only certain register pairs can be | |
| used for passing long long int specifically (r3,r4), | |
| (r5,r6), (r7,r8), (r9,r10). If next arg is long long | |
| but not correct starting register of pair then skip | |
| until the proper starting register. */ | |
| if (((gpr_end.u - gpr_base.u) & 1) != 0) | |
| gpr_base.u++; | |
| *gpr_base.ll++ = **p_argv.ll; | |
| } | |
| break; | |
| case FFI_TYPE_STRUCT: | |
| struct_copy_size = ((*ptr)->size + 15) & ~0xF; | |
| copy_space.c -= struct_copy_size; | |
| memcpy (copy_space.c, *p_argv.c, (*ptr)->size); | |
| gprvalue = (unsigned long) copy_space.c; | |
| FFI_ASSERT (copy_space.c > next_arg.c); | |
| FFI_ASSERT (flags & FLAG_ARG_NEEDS_COPY); | |
| goto putgpr; | |
| case FFI_TYPE_UINT8: | |
| gprvalue = **p_argv.uc; | |
| goto putgpr; | |
| case FFI_TYPE_SINT8: | |
| gprvalue = **p_argv.sc; | |
| goto putgpr; | |
| case FFI_TYPE_UINT16: | |
| gprvalue = **p_argv.us; | |
| goto putgpr; | |
| case FFI_TYPE_SINT16: | |
| gprvalue = **p_argv.ss; | |
| goto putgpr; | |
| case FFI_TYPE_INT: | |
| case FFI_TYPE_UINT32: | |
| case FFI_TYPE_SINT32: | |
| case FFI_TYPE_POINTER: | |
| gprvalue = **p_argv.ui; | |
| putgpr: | |
| if (gpr_base.u >= gpr_end.u) | |
| *next_arg.u++ = gprvalue; | |
| else | |
| *gpr_base.u++ = gprvalue; | |
| break; | |
| } | |
| } | |
| /* Check that we didn't overrun the stack... */ | |
| FFI_ASSERT (copy_space.c >= next_arg.c); | |
| FFI_ASSERT (gpr_base.u <= gpr_end.u); | |
| #ifndef __NO_FPRS__ | |
| FFI_ASSERT (fpr_base.u <= fpr_end.u); | |
| #endif | |
| FFI_ASSERT (((flags & FLAG_4_GPR_ARGUMENTS) != 0) | |
| == (gpr_end.u - gpr_base.u < 4)); | |
| } | |
| #define MIN_CACHE_LINE_SIZE 8 | |
| static void | |
| flush_icache (char *wraddr, char *xaddr, int size) | |
| { | |
| int i; | |
| for (i = 0; i < size; i += MIN_CACHE_LINE_SIZE) | |
| __asm__ volatile ("icbi 0,%0;" "dcbf 0,%1;" | |
| : : "r" (xaddr + i), "r" (wraddr + i) : "memory"); | |
| __asm__ volatile ("icbi 0,%0;" "dcbf 0,%1;" "sync;" "isync;" | |
| : : "r"(xaddr + size - 1), "r"(wraddr + size - 1) | |
| : "memory"); | |
| } | |
| ffi_status FFI_HIDDEN | |
| ffi_prep_closure_loc_sysv (ffi_closure *closure, | |
| ffi_cif *cif, | |
| void (*fun) (ffi_cif *, void *, void **, void *), | |
| void *user_data, | |
| void *codeloc) | |
| { | |
| if (cif->abi < FFI_SYSV || cif->abi >= FFI_LAST_ABI) | |
| return FFI_BAD_ABI; | |
| #ifdef FFI_EXEC_STATIC_TRAMP | |
| if (ffi_tramp_is_present(closure)) | |
| { | |
| /* Initialize the static trampoline's parameters. */ | |
| void (*dest)(void) = ffi_closure_SYSV; | |
| ffi_tramp_set_parms (closure->ftramp, dest, closure); | |
| } | |
| else | |
| #endif | |
| { | |
| unsigned int *tramp = (unsigned int *) &closure->tramp[0]; | |
| tramp[0] = 0x7c0802a6; /* mflr r0 */ | |
| tramp[1] = 0x429f0005; /* bcl 20,31,.+4 */ | |
| tramp[2] = 0x7d6802a6; /* mflr r11 */ | |
| tramp[3] = 0x7c0803a6; /* mtlr r0 */ | |
| tramp[4] = 0x800b0018; /* lwz r0,24(r11) */ | |
| tramp[5] = 0x816b001c; /* lwz r11,28(r11) */ | |
| tramp[6] = 0x7c0903a6; /* mtctr r0 */ | |
| tramp[7] = 0x4e800420; /* bctr */ | |
| *(void **) &tramp[8] = (void *) ffi_closure_SYSV; /* function */ | |
| *(void **) &tramp[9] = codeloc; /* context */ | |
| /* Flush the icache. */ | |
| flush_icache ((char *)tramp, (char *)codeloc, 8 * 4); | |
| } | |
| closure->cif = cif; | |
| closure->fun = fun; | |
| closure->user_data = user_data; | |
| return FFI_OK; | |
| } | |
| /* Basically the trampoline invokes ffi_closure_SYSV, and on | |
| entry, r11 holds the address of the closure. | |
| After storing the registers that could possibly contain | |
| parameters to be passed into the stack frame and setting | |
| up space for a return value, ffi_closure_SYSV invokes the | |
| following helper function to do most of the work. */ | |
| int | |
| ffi_closure_helper_SYSV (ffi_cif *cif, | |
| void (*fun) (ffi_cif *, void *, void **, void *), | |
| void *user_data, | |
| void *rvalue, | |
| unsigned long *pgr, | |
| ffi_dblfl *pfr, | |
| unsigned long *pst) | |
| { | |
| /* rvalue is the pointer to space for return value in closure assembly */ | |
| /* pgr is the pointer to where r3-r10 are stored in ffi_closure_SYSV */ | |
| /* pfr is the pointer to where f1-f8 are stored in ffi_closure_SYSV */ | |
| /* pst is the pointer to outgoing parameter stack in original caller */ | |
| void ** avalue; | |
| ffi_type ** arg_types; | |
| long i, avn; | |
| #ifndef __NO_FPRS__ | |
| long nf = 0; /* number of floating registers already used */ | |
| #endif | |
| long ng = 0; /* number of general registers already used */ | |
| unsigned size = cif->rtype->size; | |
| unsigned short rtypenum = cif->rtype->type; | |
| avalue = alloca (cif->nargs * sizeof (void *)); | |
| /* First translate for softfloat/nonlinux */ | |
| rtypenum = translate_float (cif->abi, rtypenum); | |
| /* Copy the caller's structure return value address so that the closure | |
| returns the data directly to the caller. | |
| For FFI_SYSV the result is passed in r3/r4 if the struct size is less | |
| or equal 8 bytes. */ | |
| if (rtypenum == FFI_TYPE_STRUCT | |
| && !((cif->abi & FFI_SYSV_STRUCT_RET) != 0 && size <= 8)) | |
| { | |
| rvalue = (void *) *pgr; | |
| ng++; | |
| pgr++; | |
| } | |
| i = 0; | |
| avn = cif->nargs; | |
| arg_types = cif->arg_types; | |
| /* Grab the addresses of the arguments from the stack frame. */ | |
| while (i < avn) { | |
| unsigned short typenum = arg_types[i]->type; | |
| /* We may need to handle some values depending on ABI. */ | |
| typenum = translate_float (cif->abi, typenum); | |
| switch (typenum) | |
| { | |
| #ifndef __NO_FPRS__ | |
| case FFI_TYPE_FLOAT: | |
| /* Unfortunately float values are stored as doubles | |
| in the ffi_closure_SYSV code (since we don't check | |
| the type in that routine). */ | |
| if (nf < NUM_FPR_ARG_REGISTERS) | |
| { | |
| /* FIXME? here we are really changing the values | |
| stored in the original calling routines outgoing | |
| parameter stack. This is probably a really | |
| naughty thing to do but... */ | |
| double temp = pfr->d; | |
| pfr->f = (float) temp; | |
| avalue[i] = pfr; | |
| nf++; | |
| pfr++; | |
| } | |
| else | |
| { | |
| avalue[i] = pst; | |
| pst += 1; | |
| } | |
| break; | |
| case FFI_TYPE_DOUBLE: | |
| if (nf < NUM_FPR_ARG_REGISTERS) | |
| { | |
| avalue[i] = pfr; | |
| nf++; | |
| pfr++; | |
| } | |
| else | |
| { | |
| if (((long) pst) & 4) | |
| pst++; | |
| avalue[i] = pst; | |
| pst += 2; | |
| } | |
| break; | |
| # if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
| case FFI_TYPE_LONGDOUBLE: | |
| if (nf < NUM_FPR_ARG_REGISTERS - 1) | |
| { | |
| avalue[i] = pfr; | |
| pfr += 2; | |
| nf += 2; | |
| } | |
| else | |
| { | |
| if (((long) pst) & 4) | |
| pst++; | |
| avalue[i] = pst; | |
| pst += 4; | |
| nf = 8; | |
| } | |
| break; | |
| # endif | |
| #endif | |
| case FFI_TYPE_UINT128: | |
| /* Test if for the whole long double, 4 gprs are available. | |
| otherwise the stuff ends up on the stack. */ | |
| if (ng < NUM_GPR_ARG_REGISTERS - 3) | |
| { | |
| avalue[i] = pgr; | |
| pgr += 4; | |
| ng += 4; | |
| } | |
| else | |
| { | |
| avalue[i] = pst; | |
| pst += 4; | |
| ng = 8+4; | |
| } | |
| break; | |
| case FFI_TYPE_SINT8: | |
| case FFI_TYPE_UINT8: | |
| #ifndef __LITTLE_ENDIAN__ | |
| if (ng < NUM_GPR_ARG_REGISTERS) | |
| { | |
| avalue[i] = (char *) pgr + 3; | |
| ng++; | |
| pgr++; | |
| } | |
| else | |
| { | |
| avalue[i] = (char *) pst + 3; | |
| pst++; | |
| } | |
| break; | |
| #endif | |
| case FFI_TYPE_SINT16: | |
| case FFI_TYPE_UINT16: | |
| #ifndef __LITTLE_ENDIAN__ | |
| if (ng < NUM_GPR_ARG_REGISTERS) | |
| { | |
| avalue[i] = (char *) pgr + 2; | |
| ng++; | |
| pgr++; | |
| } | |
| else | |
| { | |
| avalue[i] = (char *) pst + 2; | |
| pst++; | |
| } | |
| break; | |
| #endif | |
| case FFI_TYPE_SINT32: | |
| case FFI_TYPE_UINT32: | |
| case FFI_TYPE_POINTER: | |
| if (ng < NUM_GPR_ARG_REGISTERS) | |
| { | |
| avalue[i] = pgr; | |
| ng++; | |
| pgr++; | |
| } | |
| else | |
| { | |
| avalue[i] = pst; | |
| pst++; | |
| } | |
| break; | |
| case FFI_TYPE_STRUCT: | |
| /* Structs are passed by reference. The address will appear in a | |
| gpr if it is one of the first 8 arguments. */ | |
| if (ng < NUM_GPR_ARG_REGISTERS) | |
| { | |
| avalue[i] = (void *) *pgr; | |
| ng++; | |
| pgr++; | |
| } | |
| else | |
| { | |
| avalue[i] = (void *) *pst; | |
| pst++; | |
| } | |
| break; | |
| case FFI_TYPE_SINT64: | |
| case FFI_TYPE_UINT64: | |
| /* Passing long long ints are complex, they must | |
| be passed in suitable register pairs such as | |
| (r3,r4) or (r5,r6) or (r6,r7), or (r7,r8) or (r9,r10) | |
| and if the entire pair aren't available then the outgoing | |
| parameter stack is used for both but an alignment of 8 | |
| must will be kept. So we must either look in pgr | |
| or pst to find the correct address for this type | |
| of parameter. */ | |
| if (ng < NUM_GPR_ARG_REGISTERS - 1) | |
| { | |
| if (ng & 1) | |
| { | |
| /* skip r4, r6, r8 as starting points */ | |
| ng++; | |
| pgr++; | |
| } | |
| avalue[i] = pgr; | |
| ng += 2; | |
| pgr += 2; | |
| } | |
| else | |
| { | |
| if (((long) pst) & 4) | |
| pst++; | |
| avalue[i] = pst; | |
| pst += 2; | |
| ng = NUM_GPR_ARG_REGISTERS; | |
| } | |
| break; | |
| default: | |
| FFI_ASSERT (0); | |
| } | |
| i++; | |
| } | |
| (*fun) (cif, rvalue, avalue, user_data); | |
| /* Tell ffi_closure_SYSV how to perform return type promotions. | |
| Because the FFI_SYSV ABI returns the structures <= 8 bytes in | |
| r3/r4 we have to tell ffi_closure_SYSV how to treat them. We | |
| combine the base type FFI_SYSV_TYPE_SMALL_STRUCT with the size of | |
| the struct less one. We never have a struct with size zero. | |
| See the comment in ffitarget.h about ordering. */ | |
| if (rtypenum == FFI_TYPE_STRUCT | |
| && (cif->abi & FFI_SYSV_STRUCT_RET) != 0 && size <= 8) | |
| return FFI_SYSV_TYPE_SMALL_STRUCT - 1 + size; | |
| return rtypenum; | |
| } | |
| #endif |