diff options
Diffstat (limited to 'src/powerpc')
-rw-r--r-- | src/powerpc/ffi.c | 534 | ||||
-rw-r--r-- | src/powerpc/ffi.c.orig | 1444 | ||||
-rw-r--r-- | src/powerpc/ffi.c.rej | 12 | ||||
-rw-r--r-- | src/powerpc/ffitarget.h | 14 | ||||
-rw-r--r-- | src/powerpc/ppc_closure.S | 19 | ||||
-rw-r--r-- | src/powerpc/sysv.S | 6 | ||||
-rw-r--r-- | src/powerpc/sysv.S.orig | 214 |
7 files changed, 1980 insertions, 263 deletions
diff --git a/src/powerpc/ffi.c b/src/powerpc/ffi.c index 53c7e8e..1920c91 100644 --- a/src/powerpc/ffi.c +++ b/src/powerpc/ffi.c @@ -41,27 +41,28 @@ enum { /* The assembly depends on these exact flags. */ FLAG_RETURNS_SMST = 1 << (31-31), /* Used for FFI_SYSV small structs. */ FLAG_RETURNS_NOTHING = 1 << (31-30), /* These go in cr7 */ +#ifndef __NO_FPRS__ FLAG_RETURNS_FP = 1 << (31-29), +#endif FLAG_RETURNS_64BITS = 1 << (31-28), FLAG_RETURNS_128BITS = 1 << (31-27), /* cr6 */ FLAG_ARG_NEEDS_COPY = 1 << (31- 7), +#ifndef __NO_FPRS__ FLAG_FP_ARGUMENTS = 1 << (31- 6), /* cr1.eq; specified by ABI */ +#endif FLAG_4_GPR_ARGUMENTS = 1 << (31- 5), FLAG_RETVAL_REFERENCE = 1 << (31- 4) }; /* About the SYSV ABI. */ -unsigned int NUM_GPR_ARG_REGISTERS = 8; +#define ASM_NEEDS_REGISTERS 4 +#define NUM_GPR_ARG_REGISTERS 8 #ifndef __NO_FPRS__ -unsigned int NUM_FPR_ARG_REGISTERS = 8; -#else -unsigned int NUM_FPR_ARG_REGISTERS = 0; +# define NUM_FPR_ARG_REGISTERS 8 #endif -enum { ASM_NEEDS_REGISTERS = 4 }; - /* ffi_prep_args_SYSV is called by the assembly routine once stack space has been allocated for the function's arguments. @@ -110,10 +111,12 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack) valp gpr_base; int intarg_count; +#ifndef __NO_FPRS__ /* 'fpr_base' points at the space for fpr1, and grows upwards as we use FPR registers. */ valp fpr_base; int fparg_count; +#endif /* 'copy_space' grows down as we put structures in it. It should stay 16-byte aligned. */ @@ -122,9 +125,8 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack) /* 'next_arg' grows up as we put parameters in it. */ valp next_arg; - int i, ii MAYBE_UNUSED; + int i; ffi_type **ptr; - double double_tmp; union { void **v; char **c; @@ -140,15 +142,16 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack) size_t struct_copy_size; unsigned gprvalue; - if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT) - NUM_FPR_ARG_REGISTERS = 0; - stacktop.c = (char *) stack + bytes; gpr_base.u = stacktop.u - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS; intarg_count = 0; +#ifndef __NO_FPRS__ fpr_base.d = gpr_base.d - NUM_FPR_ARG_REGISTERS; fparg_count = 0; 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. */ @@ -171,12 +174,28 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack) i > 0; i--, ptr++, p_argv.v++) { - switch ((*ptr)->type) - { + unsigned short typenum = (*ptr)->type; + + /* We may need to handle some values depending on ABI */ + if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT) { + if (typenum == FFI_TYPE_FLOAT) + typenum = FFI_TYPE_UINT32; + if (typenum == FFI_TYPE_DOUBLE) + typenum = FFI_TYPE_UINT64; + if (typenum == FFI_TYPE_LONGDOUBLE) + typenum = FFI_TYPE_UINT128; + } else if (ecif->cif->abi != FFI_LINUX) { +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + if (typenum == FFI_TYPE_LONGDOUBLE) + typenum = FFI_TYPE_STRUCT; +#endif + } + + /* Now test the translated value */ + switch (typenum) { +#ifndef __NO_FPRS__ case FFI_TYPE_FLOAT: /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32. */ - if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT) - goto soft_float_prep; double_tmp = **p_argv.f; if (fparg_count >= NUM_FPR_ARG_REGISTERS) { @@ -215,43 +234,6 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack) #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE case FFI_TYPE_LONGDOUBLE: - if ((ecif->cif->abi != FFI_LINUX) - && (ecif->cif->abi != FFI_LINUX_SOFT_FLOAT)) - goto do_struct; - /* 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 (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT) - { - unsigned int int_tmp = (*p_argv.ui)[0]; - if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3) - { - if (intarg_count < NUM_GPR_ARG_REGISTERS) - intarg_count += NUM_GPR_ARG_REGISTERS - intarg_count; - *next_arg.u = int_tmp; - next_arg.u++; - for (ii = 1; ii < 4; ii++) - { - int_tmp = (*p_argv.ui)[ii]; - *next_arg.u = int_tmp; - next_arg.u++; - } - } - else - { - *gpr_base.u++ = int_tmp; - for (ii = 1; ii < 4; ii++) - { - int_tmp = (*p_argv.ui)[ii]; - *gpr_base.u++ = int_tmp; - } - } - intarg_count +=4; - } - else - { double_tmp = (*p_argv.d)[0]; if (fparg_count >= NUM_FPR_ARG_REGISTERS - 1) @@ -277,13 +259,40 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack) fparg_count += 2; FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); - } break; #endif +#endif /* have FPRs */ + + /* + * 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. + */ + case FFI_TYPE_UINT128: { + unsigned int int_tmp = (*p_argv.ui)[0]; + unsigned int ii; + if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3) { + if (intarg_count < NUM_GPR_ARG_REGISTERS) + intarg_count += NUM_GPR_ARG_REGISTERS - intarg_count; + *(next_arg.u++) = int_tmp; + for (ii = 1; ii < 4; ii++) { + int_tmp = (*p_argv.ui)[ii]; + *(next_arg.u++) = int_tmp; + } + } else { + *(gpr_base.u++) = int_tmp; + for (ii = 1; ii < 4; ii++) { + int_tmp = (*p_argv.ui)[ii]; + *(gpr_base.u++) = int_tmp; + } + } + intarg_count += 4; + break; + } case FFI_TYPE_UINT64: case FFI_TYPE_SINT64: - soft_double_prep: if (intarg_count == NUM_GPR_ARG_REGISTERS-1) intarg_count++; if (intarg_count >= NUM_GPR_ARG_REGISTERS) @@ -316,9 +325,6 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack) break; case FFI_TYPE_STRUCT: -#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE - do_struct: -#endif struct_copy_size = ((*ptr)->size + 15) & ~0xF; copy_space.c -= struct_copy_size; memcpy (copy_space.c, *p_argv.c, (*ptr)->size); @@ -346,7 +352,6 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack) case FFI_TYPE_UINT32: case FFI_TYPE_SINT32: case FFI_TYPE_POINTER: - soft_float_prep: gprvalue = **p_argv.ui; @@ -363,8 +368,10 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack) /* Check that we didn't overrun the stack... */ FFI_ASSERT (copy_space.c >= next_arg.c); FFI_ASSERT (gpr_base.u <= stacktop.u - ASM_NEEDS_REGISTERS); +#ifndef __NO_FPRS__ FFI_ASSERT (fpr_base.u <= stacktop.u - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS); +#endif FFI_ASSERT (flags & FLAG_4_GPR_ARGUMENTS || intarg_count <= 4); } @@ -601,9 +608,6 @@ ffi_prep_cif_machdep (ffi_cif *cif) unsigned type = cif->rtype->type; unsigned size = cif->rtype->size; - if (cif->abi == FFI_LINUX_SOFT_FLOAT) - NUM_FPR_ARG_REGISTERS = 0; - if (cif->abi != FFI_LINUX64) { /* All the machine-independent calculation of cif->bytes will be wrong. @@ -643,25 +647,38 @@ ffi_prep_cif_machdep (ffi_cif *cif) - Single/double FP values in fpr1, long double in fpr1,fpr2. - soft-float float/doubles are treated as UINT32/UINT64 respectivley. - soft-float long doubles are returned in gpr3-gpr6. */ + /* First translate for softfloat/nonlinux */ + if (cif->abi == FFI_LINUX_SOFT_FLOAT) { + if (type == FFI_TYPE_FLOAT) + type = FFI_TYPE_UINT32; + if (type == FFI_TYPE_DOUBLE) + type = FFI_TYPE_UINT64; + if (type == FFI_TYPE_LONGDOUBLE) + type = FFI_TYPE_UINT128; + } else if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64) { +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + if (type == FFI_TYPE_LONGDOUBLE) + type = FFI_TYPE_STRUCT; +#endif + } + switch (type) { -#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE +#ifndef __NO_FPRS__ case FFI_TYPE_LONGDOUBLE: - if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64 - && cif->abi != FFI_LINUX_SOFT_FLOAT) - goto byref; flags |= FLAG_RETURNS_128BITS; /* Fall through. */ -#endif case FFI_TYPE_DOUBLE: flags |= FLAG_RETURNS_64BITS; /* Fall through. */ case FFI_TYPE_FLOAT: - /* With FFI_LINUX_SOFT_FLOAT no fp registers are used. */ - if (cif->abi != FFI_LINUX_SOFT_FLOAT) - flags |= FLAG_RETURNS_FP; + flags |= FLAG_RETURNS_FP; break; +#endif + case FFI_TYPE_UINT128: + flags |= FLAG_RETURNS_128BITS; + /* Fall through. */ case FFI_TYPE_UINT64: case FFI_TYPE_SINT64: flags |= FLAG_RETURNS_64BITS; @@ -680,10 +697,6 @@ ffi_prep_cif_machdep (ffi_cif *cif) */ if (cif->abi == FFI_SYSV && size <= 8) flags |= FLAG_RETURNS_SMST; - -#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE - byref: -#endif intarg_count++; flags |= FLAG_RETVAL_REFERENCE; /* Fall through. */ @@ -704,39 +717,36 @@ ffi_prep_cif_machdep (ffi_cif *cif) Stuff on the stack needs to keep proper alignment. */ for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) { - switch ((*ptr)->type) - { + unsigned short typenum = (*ptr)->type; + + /* We may need to handle some values depending on ABI */ + if (cif->abi == FFI_LINUX_SOFT_FLOAT) { + if (typenum == FFI_TYPE_FLOAT) + typenum = FFI_TYPE_UINT32; + if (typenum == FFI_TYPE_DOUBLE) + typenum = FFI_TYPE_UINT64; + if (typenum == FFI_TYPE_LONGDOUBLE) + typenum = FFI_TYPE_UINT128; + } else if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64) { +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + if (typenum == FFI_TYPE_LONGDOUBLE) + typenum = FFI_TYPE_STRUCT; +#endif + } + + switch (typenum) { +#ifndef __NO_FPRS__ case FFI_TYPE_FLOAT: - /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32. */ - if (cif->abi == FFI_LINUX_SOFT_FLOAT) - goto soft_float_cif; fparg_count++; /* floating singles are not 8-aligned on stack */ break; #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE case FFI_TYPE_LONGDOUBLE: - if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT) - goto do_struct; - if (cif->abi == FFI_LINUX_SOFT_FLOAT) - { - if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3 - || intarg_count < NUM_GPR_ARG_REGISTERS) - /* A long double in FFI_LINUX_SOFT_FLOAT can use only - a set of four consecutive gprs. If we have not enough, - we have to adjust the intarg_count value. */ - intarg_count += NUM_GPR_ARG_REGISTERS - intarg_count; - intarg_count += 4; - break; - } - else - fparg_count++; + fparg_count++; /* Fall thru */ #endif case FFI_TYPE_DOUBLE: - /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64. */ - if (cif->abi == FFI_LINUX_SOFT_FLOAT) - goto soft_double_cif; fparg_count++; /* If this FP arg is going on the stack, it must be 8-byte-aligned. */ @@ -745,10 +755,21 @@ ffi_prep_cif_machdep (ffi_cif *cif) && intarg_count % 2 != 0) intarg_count++; break; +#endif + 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 intarg_count value. + */ + if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3 + && intarg_count < NUM_GPR_ARG_REGISTERS) + intarg_count = NUM_GPR_ARG_REGISTERS; + intarg_count += 4; + break; case FFI_TYPE_UINT64: case FFI_TYPE_SINT64: - soft_double_cif: /* '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 @@ -765,9 +786,6 @@ ffi_prep_cif_machdep (ffi_cif *cif) break; case FFI_TYPE_STRUCT: -#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE - do_struct: -#endif /* 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 @@ -775,12 +793,20 @@ ffi_prep_cif_machdep (ffi_cif *cif) struct_copy_size += ((*ptr)->size + 15) & ~0xF; /* Fall through (allocate space for the pointer). */ - default: - soft_float_cif: + 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. */ intarg_count++; break; + default: + FFI_ASSERT (0); } } else @@ -809,16 +835,29 @@ ffi_prep_cif_machdep (ffi_cif *cif) intarg_count += ((*ptr)->size + 7) / 8; break; - default: + case FFI_TYPE_POINTER: + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + 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 8-byte word in a GPR, either the object itself or a pointer to it. */ intarg_count++; break; + default: + FFI_ASSERT (0); } } +#ifndef __NO_FPRS__ if (fparg_count != 0) flags |= FLAG_FP_ARGUMENTS; +#endif if (intarg_count > 4) flags |= FLAG_4_GPR_ARGUMENTS; if (struct_copy_size != 0) @@ -826,21 +865,27 @@ ffi_prep_cif_machdep (ffi_cif *cif) if (cif->abi != FFI_LINUX64) { +#ifndef __NO_FPRS__ /* Space for the FPR registers, if needed. */ if (fparg_count != 0) bytes += NUM_FPR_ARG_REGISTERS * sizeof (double); +#endif /* Stack space. */ if (intarg_count > NUM_GPR_ARG_REGISTERS) bytes += (intarg_count - NUM_GPR_ARG_REGISTERS) * sizeof (int); +#ifndef __NO_FPRS__ if (fparg_count > NUM_FPR_ARG_REGISTERS) bytes += (fparg_count - NUM_FPR_ARG_REGISTERS) * sizeof (double); +#endif } else { +#ifndef __NO_FPRS__ /* Space for the FPR registers, if needed. */ if (fparg_count != 0) bytes += NUM_FPR_ARG_REGISTERS64 * sizeof (double); +#endif /* Stack space. */ if (intarg_count > NUM_GPR_ARG_REGISTERS64) @@ -898,9 +943,11 @@ ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) switch (cif->abi) { #ifndef POWERPC64 +# ifndef __NO_FPRS__ case FFI_SYSV: case FFI_GCC_SYSV: case FFI_LINUX: +# endif case FFI_LINUX_SOFT_FLOAT: ffi_call_SYSV (&ecif, -cif->bytes, cif->flags, ecif.rvalue, fn); break; @@ -1013,32 +1060,38 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue, void ** avalue; ffi_type ** arg_types; long i, avn; - long nf; /* number of floating registers already used */ - long ng; /* number of general registers already used */ - ffi_cif * cif; - double temp; - unsigned size; +#ifndef __NO_FPRS__ + long nf = 0; /* number of floating registers already used */ +#endif + long ng = 0; /* number of general registers already used */ + + ffi_cif *cif = closure->cif; + unsigned size = cif->rtype->size; + unsigned short rtypenum = cif->rtype->type; - cif = closure->cif; avalue = alloca (cif->nargs * sizeof (void *)); - size = cif->rtype->size; - nf = 0; - ng = 0; + /* First translate for softfloat/nonlinux */ + if (cif->abi == FFI_LINUX_SOFT_FLOAT) { + if (rtypenum == FFI_TYPE_FLOAT) + rtypenum = FFI_TYPE_UINT32; + if (rtypenum == FFI_TYPE_DOUBLE) + rtypenum = FFI_TYPE_UINT64; + if (rtypenum == FFI_TYPE_LONGDOUBLE) + rtypenum = FFI_TYPE_UINT128; + } else if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64) { +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + if (rtypenum == FFI_TYPE_LONGDOUBLE) + rtypenum = FFI_TYPE_STRUCT; +#endif + } + /* 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 ((cif->rtype->type == FFI_TYPE_STRUCT - && !((cif->abi == FFI_SYSV) && (size <= 8))) -#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE - || (cif->rtype->type == FFI_TYPE_LONGDOUBLE - && cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT) -#endif - ) - { + if (rtypenum == FFI_TYPE_STRUCT && ((cif->abi != FFI_SYSV) || (size > 8))) { rvalue = (void *) *pgr; ng++; pgr++; @@ -1049,10 +1102,109 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue, arg_types = cif->arg_types; /* Grab the addresses of the arguments from the stack frame. */ - while (i < avn) - { - switch (arg_types[i]->type) - { + while (i < avn) { + unsigned short typenum = arg_types[i]->type; + + /* We may need to handle some values depending on ABI */ + if (cif->abi == FFI_LINUX_SOFT_FLOAT) { + if (typenum == FFI_TYPE_FLOAT) + typenum = FFI_TYPE_UINT32; + if (typenum == FFI_TYPE_DOUBLE) + typenum = FFI_TYPE_UINT64; + if (typenum == FFI_TYPE_LONGDOUBLE) + typenum = FFI_TYPE_UINT128; + } else if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64) { +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + if (typenum == FFI_TYPE_LONGDOUBLE) + typenum = FFI_TYPE_STRUCT; +#endif + } + + 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). + */ + + /* there are 8 64bit floating point registers */ + + if (nf < 8) + { + temp = pfr->d; + pfr->f = (float) temp; + avalue[i] = pfr; + nf++; + pfr++; + } + else + { + /* 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... + */ + avalue[i] = pst; + pst += 1; + } + break; + + case FFI_TYPE_DOUBLE: + /* On the outgoing stack all values are aligned to 8 */ + /* there are 8 64bit floating point registers */ + + if (nf < 8) + { + 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 < 7) + { + avalue[i] = pfr; + pfr += 2; + nf += 2; + } + else + { + if (((long) pst) & 4) + pst++; + avalue[i] = pst; + pst += 4; + nf = 8; + } + break; +#endif +#endif /* have FPRS */ + + 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 < 5) { + 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: /* there are 8 gpr registers used to pass values */ @@ -1088,7 +1240,6 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue, case FFI_TYPE_SINT32: case FFI_TYPE_UINT32: case FFI_TYPE_POINTER: - soft_float_closure: /* there are 8 gpr registers used to pass values */ if (ng < 8) { @@ -1104,9 +1255,6 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue, break; case FFI_TYPE_STRUCT: -#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE - do_struct: -#endif /* Structs are passed by reference. The address will appear in a gpr if it is one of the first 8 arguments. */ if (ng < 8) @@ -1124,7 +1272,6 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue, case FFI_TYPE_SINT64: case FFI_TYPE_UINT64: - soft_double_closure: /* 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) @@ -1156,99 +1303,8 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue, } break; - case FFI_TYPE_FLOAT: - /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32. */ - if (cif->abi == FFI_LINUX_SOFT_FLOAT) - goto soft_float_closure; - /* unfortunately float values are stored as doubles - * in the ffi_closure_SYSV code (since we don't check - * the type in that routine). - */ - - /* there are 8 64bit floating point registers */ - - if (nf < 8) - { - temp = pfr->d; - pfr->f = (float) temp; - avalue[i] = pfr; - nf++; - pfr++; - } - else - { - /* 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... - */ - avalue[i] = pst; - pst += 1; - } - break; - - case FFI_TYPE_DOUBLE: - /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64. */ - if (cif->abi == FFI_LINUX_SOFT_FLOAT) - goto soft_double_closure; - /* On the outgoing stack all values are aligned to 8 */ - /* there are 8 64bit floating point registers */ - - if (nf < 8) - { - 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 (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT) - goto do_struct; - if (cif->abi == FFI_LINUX_SOFT_FLOAT) - { /* Test if for the whole long double, 4 gprs are available. - otherwise the stuff ends up on the stack. */ - if (ng < 5) - { - avalue[i] = pgr; - pgr += 4; - ng += 4; - } - else - { - avalue[i] = pst; - pst += 4; - ng = 8; - } - break; - } - if (nf < 7) - { - avalue[i] = pfr; - pfr += 2; - nf += 2; - } - else - { - if (((long) pst) & 4) - pst++; - avalue[i] = pst; - pst += 4; - nf = 8; - } - break; -#endif - default: - FFI_ASSERT (0); + FFI_ASSERT (0); } i++; @@ -1265,39 +1321,9 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue, already used and we never have a struct with size zero. That is the reason for the subtraction of 1. See the comment in ffitarget.h about ordering. */ - if (cif->abi == FFI_SYSV && cif->rtype->type == FFI_TYPE_STRUCT - && size <= 8) + if (cif->abi == FFI_SYSV && rtypenum == FFI_TYPE_STRUCT && size <= 8) return (FFI_SYSV_TYPE_SMALL_STRUCT - 1) + size; -#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE - else if (cif->rtype->type == FFI_TYPE_LONGDOUBLE - && cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT) - return FFI_TYPE_STRUCT; -#endif - /* With FFI_LINUX_SOFT_FLOAT floats and doubles are handled like UINT32 - respectivley UINT64. */ - if (cif->abi == FFI_LINUX_SOFT_FLOAT) - { - switch (cif->rtype->type) - { - case FFI_TYPE_FLOAT: - return FFI_TYPE_UINT32; - break; - case FFI_TYPE_DOUBLE: - return FFI_TYPE_UINT64; - break; -#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE - case FFI_TYPE_LONGDOUBLE: - return FFI_TYPE_UINT128; - break; -#endif - default: - return cif->rtype->type; - } - } - else - { - return cif->rtype->type; - } + return rtypenum; } int FFI_HIDDEN ffi_closure_helper_LINUX64 (ffi_closure *, void *, diff --git a/src/powerpc/ffi.c.orig b/src/powerpc/ffi.c.orig new file mode 100644 index 0000000..53c7e8e --- /dev/null +++ b/src/powerpc/ffi.c.orig @@ -0,0 +1,1444 @@ +/* ----------------------------------------------------------------------- + ffi.c - 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 <ffi_common.h> + +#include <stdlib.h> +#include <stdio.h> + + +extern void ffi_closure_SYSV (void); +extern void FFI_HIDDEN ffi_closure_LINUX64 (void); + +enum { + /* The assembly depends on these exact flags. */ + FLAG_RETURNS_SMST = 1 << (31-31), /* Used for FFI_SYSV small structs. */ + FLAG_RETURNS_NOTHING = 1 << (31-30), /* These go in cr7 */ + FLAG_RETURNS_FP = 1 << (31-29), + FLAG_RETURNS_64BITS = 1 << (31-28), + + FLAG_RETURNS_128BITS = 1 << (31-27), /* cr6 */ + + FLAG_ARG_NEEDS_COPY = 1 << (31- 7), + FLAG_FP_ARGUMENTS = 1 << (31- 6), /* cr1.eq; specified by ABI */ + FLAG_4_GPR_ARGUMENTS = 1 << (31- 5), + FLAG_RETVAL_REFERENCE = 1 << (31- 4) +}; + +/* About the SYSV ABI. */ +unsigned int NUM_GPR_ARG_REGISTERS = 8; +#ifndef __NO_FPRS__ +unsigned int NUM_FPR_ARG_REGISTERS = 8; +#else +unsigned int NUM_FPR_ARG_REGISTERS = 0; +#endif + +enum { ASM_NEEDS_REGISTERS = 4 }; + +/* 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_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; + int intarg_count; + + /* 'fpr_base' points at the space for fpr1, and grows upwards as + we use FPR registers. */ + valp fpr_base; + int fparg_count; + + /* '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, ii MAYBE_UNUSED; + ffi_type **ptr; + double double_tmp; + 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; + + if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT) + NUM_FPR_ARG_REGISTERS = 0; + + stacktop.c = (char *) stack + bytes; + gpr_base.u = stacktop.u - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS; + intarg_count = 0; + fpr_base.d = gpr_base.d - NUM_FPR_ARG_REGISTERS; + fparg_count = 0; + copy_space.c = ((flags & FLAG_FP_ARGUMENTS) ? fpr_base.c : gpr_base.c); + next_arg.u = stack + 2; + + /* Check that everything starts aligned properly. */ + FFI_ASSERT (((unsigned) (char *) stack & 0xF) == 0); + FFI_ASSERT (((unsigned) copy_space.c & 0xF) == 0); + FFI_ASSERT (((unsigned) 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 long) (char *) ecif->rvalue; + intarg_count++; + } + + /* 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++) + { + switch ((*ptr)->type) + { + case FFI_TYPE_FLOAT: + /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32. */ + if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT) + goto soft_float_prep; + double_tmp = **p_argv.f; + if (fparg_count >= NUM_FPR_ARG_REGISTERS) + { + *next_arg.f = (float) double_tmp; + next_arg.u += 1; + intarg_count++; + } + else + *fpr_base.d++ = double_tmp; + fparg_count++; + FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); + break; + + case FFI_TYPE_DOUBLE: + /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64. */ + if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT) + goto soft_double_prep; + double_tmp = **p_argv.d; + + if (fparg_count >= NUM_FPR_ARG_REGISTERS) + { + if (intarg_count >= NUM_GPR_ARG_REGISTERS + && intarg_count % 2 != 0) + { + intarg_count++; + next_arg.u++; + } + *next_arg.d = double_tmp; + next_arg.u += 2; + } + else + *fpr_base.d++ = double_tmp; + fparg_count++; + FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); + break; + +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + case FFI_TYPE_LONGDOUBLE: + if ((ecif->cif->abi != FFI_LINUX) + && (ecif->cif->abi != FFI_LINUX_SOFT_FLOAT)) + goto do_struct; + /* 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 (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT) + { + unsigned int int_tmp = (*p_argv.ui)[0]; + if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3) + { + if (intarg_count < NUM_GPR_ARG_REGISTERS) + intarg_count += NUM_GPR_ARG_REGISTERS - intarg_count; + *next_arg.u = int_tmp; + next_arg.u++; + for (ii = 1; ii < 4; ii++) + { + int_tmp = (*p_argv.ui)[ii]; + *next_arg.u = int_tmp; + next_arg.u++; + } + } + else + { + *gpr_base.u++ = int_tmp; + for (ii = 1; ii < 4; ii++) + { + int_tmp = (*p_argv.ui)[ii]; + *gpr_base.u++ = int_tmp; + } + } + intarg_count +=4; + } + else + { + double_tmp = (*p_argv.d)[0]; + + if (fparg_count >= NUM_FPR_ARG_REGISTERS - 1) + { + if (intarg_count >= NUM_GPR_ARG_REGISTERS + && intarg_count % 2 != 0) + { + intarg_count++; + next_arg.u++; + } + *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; + } + + fparg_count += 2; + FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); + } + break; +#endif + + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + soft_double_prep: + if (intarg_count == NUM_GPR_ARG_REGISTERS-1) + intarg_count++; + if (intarg_count >= NUM_GPR_ARG_REGISTERS) + { + if (intarg_count % 2 != 0) + { + intarg_count++; + next_arg.u++; + } + *next_arg.ll = **p_argv.ll; + next_arg.u += 2; + } + else + { + /* whoops: abi states only certain register pairs + * can be used for passing long long int + * specifically (r3,r4), (r5,r6), (r7,r8), + * (r9,r10) and if next arg is long long but + * not correct starting register of pair then skip + * until the proper starting register + */ + if (intarg_count % 2 != 0) + { + intarg_count ++; + gpr_base.u++; + } + *gpr_base.ll++ = **p_argv.ll; + } + intarg_count += 2; + break; + + case FFI_TYPE_STRUCT: +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + do_struct: +#endif + 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: + soft_float_prep: + + gprvalue = **p_argv.ui; + + putgpr: + if (intarg_count >= NUM_GPR_ARG_REGISTERS) + *next_arg.u++ = gprvalue; + else + *gpr_base.u++ = gprvalue; + intarg_count++; + break; + } + } + + /* Check that we didn't overrun the stack... */ + FFI_ASSERT (copy_space.c >= next_arg.c); + FFI_ASSERT (gpr_base.u <= stacktop.u - ASM_NEEDS_REGISTERS); + FFI_ASSERT (fpr_base.u + <= stacktop.u - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS); + FFI_ASSERT (flags & FLAG_4_GPR_ARGUMENTS || intarg_count <= 4); +} + +/* About the LINUX64 ABI. */ +enum { + NUM_GPR_ARG_REGISTERS64 = 8, + NUM_FPR_ARG_REGISTERS64 = 13 +}; +enum { ASM_NEEDS_REGISTERS64 = 4 }; + +/* ffi_prep_args64 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: + + | Ret addr from ffi_call_LINUX64 8bytes | higher addresses + |--------------------------------------------| + | CR save area 8bytes | + |--------------------------------------------| + | Previous backchain pointer 8 | stack pointer here + |--------------------------------------------|<+ <<< on entry to + | Saved r28-r31 4*8 | | ffi_call_LINUX64 + |--------------------------------------------| | + | GPR registers r3-r10 8*8 | | + |--------------------------------------------| | + | FPR registers f1-f13 (optional) 13*8 | | + |--------------------------------------------| | + | Parameter save area | | + |--------------------------------------------| | + | TOC save area 8 | | + |--------------------------------------------| | stack | + | Linker doubleword 8 | | grows | + |--------------------------------------------| | down V + | Compiler doubleword 8 | | + |--------------------------------------------| | lower addresses + | Space for callee's LR 8 | | + |--------------------------------------------| | + | CR save area 8 | | + |--------------------------------------------| | stack pointer here + | Current backchain pointer 8 |-/ during + |--------------------------------------------| <<< ffi_call_LINUX64 + +*/ + +void FFI_HIDDEN +ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack) +{ + const unsigned long bytes = ecif->cif->bytes; + const unsigned long flags = ecif->cif->flags; + + typedef union { + char *c; + unsigned long *ul; + float *f; + double *d; + } valp; + + /* 'stacktop' points at the previous backchain pointer. */ + valp stacktop; + + /* 'next_arg' points at the space for gpr3, and grows upwards as + we use GPR registers, then continues at rest. */ + valp gpr_base; + valp gpr_end; + valp rest; + valp next_arg; + + /* 'fpr_base' points at the space for fpr3, and grows upwards as + we use FPR registers. */ + valp fpr_base; + int fparg_count; + + int i, words; + ffi_type **ptr; + double double_tmp; + union { + void **v; + char **c; + signed char **sc; + unsigned char **uc; + signed short **ss; + unsigned short **us; + signed int **si; + unsigned int **ui; + unsigned long **ul; + float **f; + double **d; + } p_argv; + unsigned long gprvalue; + + stacktop.c = (char *) stack + bytes; + gpr_base.ul = stacktop.ul - ASM_NEEDS_REGISTERS64 - NUM_GPR_ARG_REGISTERS64; + gpr_end.ul = gpr_base.ul + NUM_GPR_ARG_REGISTERS64; + rest.ul = stack + 6 + NUM_GPR_ARG_REGISTERS64; + fpr_base.d = gpr_base.d - NUM_FPR_ARG_REGISTERS64; + fparg_count = 0; + next_arg.ul = gpr_base.ul; + + /* Check that everything starts aligned properly. */ + FFI_ASSERT (((unsigned long) (char *) stack & 0xF) == 0); + FFI_ASSERT (((unsigned long) stacktop.c & 0xF) == 0); + FFI_ASSERT ((bytes & 0xF) == 0); + + /* Deal with return values that are actually pass-by-reference. */ + if (flags & FLAG_RETVAL_REFERENCE) + *next_arg.ul++ = (unsigned long) (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++) + { + switch ((*ptr)->type) + { + case FFI_TYPE_FLOAT: + double_tmp = **p_argv.f; + *next_arg.f = (float) double_tmp; + if (++next_arg.ul == gpr_end.ul) + next_arg.ul = rest.ul; + if (fparg_count < NUM_FPR_ARG_REGISTERS64) + *fpr_base.d++ = double_tmp; + fparg_count++; + FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); + break; + + case FFI_TYPE_DOUBLE: + double_tmp = **p_argv.d; + *next_arg.d = double_tmp; + if (++next_arg.ul == gpr_end.ul) + next_arg.ul = rest.ul; + if (fparg_count < NUM_FPR_ARG_REGISTERS64) + *fpr_base.d++ = double_tmp; + fparg_count++; + FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); + break; + +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + case FFI_TYPE_LONGDOUBLE: + double_tmp = (*p_argv.d)[0]; + *next_arg.d = double_tmp; + if (++next_arg.ul == gpr_end.ul) + next_arg.ul = rest.ul; + if (fparg_count < NUM_FPR_ARG_REGISTERS64) + *fpr_base.d++ = double_tmp; + fparg_count++; + double_tmp = (*p_argv.d)[1]; + *next_arg.d = double_tmp; + if (++next_arg.ul == gpr_end.ul) + next_arg.ul = rest.ul; + if (fparg_count < NUM_FPR_ARG_REGISTERS64) + *fpr_base.d++ = double_tmp; + fparg_count++; + FFI_ASSERT (__LDBL_MANT_DIG__ == 106); + FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); + break; +#endif + + case FFI_TYPE_STRUCT: + words = ((*ptr)->size + 7) / 8; + if (next_arg.ul >= gpr_base.ul && next_arg.ul + words > gpr_end.ul) + { + size_t first = gpr_end.c - next_arg.c; + memcpy (next_arg.c, *p_argv.c, first); + memcpy (rest.c, *p_argv.c + first, (*ptr)->size - first); + next_arg.c = rest.c + words * 8 - first; + } + else + { + char *where = next_arg.c; + + /* Structures with size less than eight bytes are passed + left-padded. */ + if ((*ptr)->size < 8) + where += 8 - (*ptr)->size; + + memcpy (where, *p_argv.c, (*ptr)->size); + next_arg.ul += words; + if (next_arg.ul == gpr_end.ul) + next_arg.ul = rest.ul; + } + break; + + 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_UINT32: + gprvalue = **p_argv.ui; + goto putgpr; + case FFI_TYPE_INT: + case FFI_TYPE_SINT32: + gprvalue = **p_argv.si; + goto putgpr; + + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + case FFI_TYPE_POINTER: + gprvalue = **p_argv.ul; + putgpr: + *next_arg.ul++ = gprvalue; + if (next_arg.ul == gpr_end.ul) + next_arg.ul = rest.ul; + break; + } + } + + FFI_ASSERT (flags & FLAG_4_GPR_ARGUMENTS + || (next_arg.ul >= gpr_base.ul + && next_arg.ul <= gpr_base.ul + 4)); +} + + + +/* Perform machine dependent cif processing */ +ffi_status +ffi_prep_cif_machdep (ffi_cif *cif) +{ + /* All this is for the SYSV and LINUX64 ABI. */ + int i; + ffi_type **ptr; + unsigned bytes; + int fparg_count = 0, intarg_count = 0; + unsigned flags = 0; + unsigned struct_copy_size = 0; + unsigned type = cif->rtype->type; + unsigned size = cif->rtype->size; + + if (cif->abi == FFI_LINUX_SOFT_FLOAT) + NUM_FPR_ARG_REGISTERS = 0; + + if (cif->abi != FFI_LINUX64) + { + /* All the machine-independent calculation of cif->bytes will be wrong. + Redo the calculation for SYSV. */ + + /* 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); + } + else + { + /* 64-bit ABI. */ + + /* Space for backchain, CR, LR, cc/ld doubleword, TOC and the asm's temp + regs. */ + bytes = (6 + ASM_NEEDS_REGISTERS64) * sizeof (long); + + /* Space for the mandatory parm save area and general registers. */ + bytes += 2 * NUM_GPR_ARG_REGISTERS64 * sizeof (long); + } + + /* 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; + - Single/double FP values are returned in fpr1; + - Larger structures are allocated space and a pointer is passed as + the first argument. + - long doubles (if not equivalent to double) are returned in + fpr1,fpr2 for Linux and as for large structs for SysV. + For LINUX64: + - integer values in gpr3; + - Structures/Unions by reference; + - Single/double FP values in fpr1, long double in fpr1,fpr2. + - soft-float float/doubles are treated as UINT32/UINT64 respectivley. + - soft-float long doubles are returned in gpr3-gpr6. */ + switch (type) + { +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + case FFI_TYPE_LONGDOUBLE: + if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64 + && cif->abi != FFI_LINUX_SOFT_FLOAT) + goto byref; + flags |= FLAG_RETURNS_128BITS; + /* Fall through. */ +#endif + case FFI_TYPE_DOUBLE: + flags |= FLAG_RETURNS_64BITS; + /* Fall through. */ + case FFI_TYPE_FLOAT: + /* With FFI_LINUX_SOFT_FLOAT no fp registers are used. */ + if (cif->abi != FFI_LINUX_SOFT_FLOAT) + flags |= FLAG_RETURNS_FP; + break; + + 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. The FFI_GCC_SYSV ABI instead returns them + * in memory. + * + * NOTE: The assembly code can safely assume that it just needs to + * store both r3 and r4 into a 8-byte word-aligned buffer, as + * we allocate a temporary buffer in ffi_call() if this flag is + * set. + */ + if (cif->abi == FFI_SYSV && size <= 8) + flags |= FLAG_RETURNS_SMST; + +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + byref: +#endif + intarg_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; + } + + if (cif->abi != FFI_LINUX64) + /* 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++) + { + switch ((*ptr)->type) + { + case FFI_TYPE_FLOAT: + /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32. */ + if (cif->abi == FFI_LINUX_SOFT_FLOAT) + goto soft_float_cif; + fparg_count++; + /* floating singles are not 8-aligned on stack */ + break; + +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + case FFI_TYPE_LONGDOUBLE: + if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT) + goto do_struct; + if (cif->abi == FFI_LINUX_SOFT_FLOAT) + { + if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3 + || intarg_count < NUM_GPR_ARG_REGISTERS) + /* A long double in FFI_LINUX_SOFT_FLOAT can use only + a set of four consecutive gprs. If we have not enough, + we have to adjust the intarg_count value. */ + intarg_count += NUM_GPR_ARG_REGISTERS - intarg_count; + intarg_count += 4; + break; + } + else + fparg_count++; + /* Fall thru */ +#endif + case FFI_TYPE_DOUBLE: + /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64. */ + if (cif->abi == FFI_LINUX_SOFT_FLOAT) + goto soft_double_cif; + fparg_count++; + /* If this FP arg is going on the stack, it must be + 8-byte-aligned. */ + if (fparg_count > NUM_FPR_ARG_REGISTERS + && intarg_count >= NUM_GPR_ARG_REGISTERS + && intarg_count % 2 != 0) + intarg_count++; + break; + + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + soft_double_cif: + /* '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). + */ + if (intarg_count == NUM_GPR_ARG_REGISTERS-1 + || intarg_count % 2 != 0) + intarg_count++; + intarg_count += 2; + break; + + case FFI_TYPE_STRUCT: +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + do_struct: +#endif + /* 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). */ + + default: + soft_float_cif: + /* Everything else is passed as a 4-byte word in a GPR, either + the object itself or a pointer to it. */ + intarg_count++; + break; + } + } + else + for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) + { + switch ((*ptr)->type) + { +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + case FFI_TYPE_LONGDOUBLE: + if (cif->abi == FFI_LINUX_SOFT_FLOAT) + intarg_count += 4; + else + { + fparg_count += 2; + intarg_count += 2; + } + break; +#endif + case FFI_TYPE_FLOAT: + case FFI_TYPE_DOUBLE: + fparg_count++; + intarg_count++; + break; + + case FFI_TYPE_STRUCT: + intarg_count += ((*ptr)->size + 7) / 8; + break; + + default: + /* Everything else is passed as a 8-byte word in a GPR, either + the object itself or a pointer to it. */ + intarg_count++; + break; + } + } + + if (fparg_count != 0) + flags |= FLAG_FP_ARGUMENTS; + if (intarg_count > 4) + flags |= FLAG_4_GPR_ARGUMENTS; + if (struct_copy_size != 0) + flags |= FLAG_ARG_NEEDS_COPY; + + if (cif->abi != FFI_LINUX64) + { + /* Space for the FPR registers, if needed. */ + if (fparg_count != 0) + bytes += NUM_FPR_ARG_REGISTERS * sizeof (double); + + /* Stack space. */ + if (intarg_count > NUM_GPR_ARG_REGISTERS) + bytes += (intarg_count - NUM_GPR_ARG_REGISTERS) * sizeof (int); + if (fparg_count > NUM_FPR_ARG_REGISTERS) + bytes += (fparg_count - NUM_FPR_ARG_REGISTERS) * sizeof (double); + } + else + { + /* Space for the FPR registers, if needed. */ + if (fparg_count != 0) + bytes += NUM_FPR_ARG_REGISTERS64 * sizeof (double); + + /* Stack space. */ + if (intarg_count > NUM_GPR_ARG_REGISTERS64) + bytes += (intarg_count - NUM_GPR_ARG_REGISTERS64) * sizeof (long); + } + + /* 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; +} + +extern void ffi_call_SYSV(extended_cif *, unsigned, unsigned, unsigned *, + void (*fn)(void)); +extern void FFI_HIDDEN ffi_call_LINUX64(extended_cif *, unsigned long, + unsigned long, unsigned long *, + void (*fn)(void)); + +void +ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) +{ + /* + * The final SYSV ABI says that structures smaller or equal 8 bytes + * are returned in r3/r4. The FFI_GCC_SYSV ABI instead returns them + * in memory. + * + * Just to keep things simple for the assembly code, we will always + * bounce-buffer struct return values less than or equal to 8 bytes. + * This allows the ASM to handle SYSV small structures by directly + * writing r3 and r4 to memory without worrying about struct size. + */ + unsigned int smst_buffer[2]; + extended_cif ecif; + unsigned int rsize; + + ecif.cif = cif; + ecif.avalue = avalue; + + /* Ensure that we have a valid struct return value */ + ecif.rvalue = rvalue; + if (cif->rtype->type == FFI_TYPE_STRUCT) { + rsize = cif->rtype->size; + if (rsize <= 8) + ecif.rvalue = smst_buffer; + else if (!rvalue) + ecif.rvalue = alloca(rsize); + } + + switch (cif->abi) + { +#ifndef POWERPC64 + case FFI_SYSV: + case FFI_GCC_SYSV: + case FFI_LINUX: + case FFI_LINUX_SOFT_FLOAT: + ffi_call_SYSV (&ecif, -cif->bytes, cif->flags, ecif.rvalue, fn); + break; +#else + case FFI_LINUX64: + ffi_call_LINUX64 (&ecif, -(long) cif->bytes, cif->flags, ecif.rvalue, fn); + break; +#endif + default: + FFI_ASSERT (0); + break; + } + + /* Check for a bounce-buffered return value */ + if (rvalue && ecif.rvalue == smst_buffer) + memcpy(rvalue, smst_buffer, rsize); +} + + +#ifndef POWERPC64 +#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"); +} +#endif + +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 POWERPC64 + void **tramp = (void **) &closure->tramp[0]; + + if (cif->abi != FFI_LINUX64) + return FFI_BAD_ABI; + /* Copy function address and TOC from ffi_closure_LINUX64. */ + memcpy (tramp, (char *) ffi_closure_LINUX64, 16); + tramp[2] = codeloc; +#else + unsigned int *tramp; + + if (! (cif->abi == FFI_GCC_SYSV + || cif->abi == FFI_SYSV + || cif->abi == FFI_LINUX + || cif->abi == FFI_LINUX_SOFT_FLOAT)) + return FFI_BAD_ABI; + + tramp = (unsigned int *) &closure->tramp[0]; + tramp[0] = 0x7c0802a6; /* mflr r0 */ + tramp[1] = 0x4800000d; /* bl 10 <trampoline_initial+0x10> */ + tramp[4] = 0x7d6802a6; /* mflr r11 */ + tramp[5] = 0x7c0803a6; /* mtlr r0 */ + tramp[6] = 0x800b0000; /* lwz r0,0(r11) */ + tramp[7] = 0x816b0004; /* lwz r11,4(r11) */ + tramp[8] = 0x7c0903a6; /* mtctr r0 */ + tramp[9] = 0x4e800420; /* bctr */ + *(void **) &tramp[2] = (void *) ffi_closure_SYSV; /* function */ + *(void **) &tramp[3] = codeloc; /* context */ + + /* Flush the icache. */ + flush_icache ((char *)tramp, (char *)codeloc, FFI_TRAMPOLINE_SIZE); +#endif + + closure->cif = cif; + closure->fun = fun; + closure->user_data = user_data; + + return FFI_OK; +} + +typedef union +{ + float f; + double d; +} ffi_dblfl; + +int ffi_closure_helper_SYSV (ffi_closure *, void *, unsigned long *, + ffi_dblfl *, unsigned long *); + +/* 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_closure *closure, 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; + long nf; /* number of floating registers already used */ + long ng; /* number of general registers already used */ + ffi_cif * cif; + double temp; + unsigned size; + + cif = closure->cif; + avalue = alloca (cif->nargs * sizeof (void *)); + size = cif->rtype->size; + + nf = 0; + ng = 0; + + /* 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 ((cif->rtype->type == FFI_TYPE_STRUCT + && !((cif->abi == FFI_SYSV) && (size <= 8))) +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + || (cif->rtype->type == FFI_TYPE_LONGDOUBLE + && cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT) +#endif + ) + { + 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) + { + switch (arg_types[i]->type) + { + case FFI_TYPE_SINT8: + case FFI_TYPE_UINT8: + /* there are 8 gpr registers used to pass values */ + if (ng < 8) + { + avalue[i] = (char *) pgr + 3; + ng++; + pgr++; + } + else + { + avalue[i] = (char *) pst + 3; + pst++; + } + break; + + case FFI_TYPE_SINT16: + case FFI_TYPE_UINT16: + /* there are 8 gpr registers used to pass values */ + if (ng < 8) + { + avalue[i] = (char *) pgr + 2; + ng++; + pgr++; + } + else + { + avalue[i] = (char *) pst + 2; + pst++; + } + break; + + case FFI_TYPE_SINT32: + case FFI_TYPE_UINT32: + case FFI_TYPE_POINTER: + soft_float_closure: + /* there are 8 gpr registers used to pass values */ + if (ng < 8) + { + avalue[i] = pgr; + ng++; + pgr++; + } + else + { + avalue[i] = pst; + pst++; + } + break; + + case FFI_TYPE_STRUCT: +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + do_struct: +#endif + /* Structs are passed by reference. The address will appear in a + gpr if it is one of the first 8 arguments. */ + if (ng < 8) + { + avalue[i] = (void *) *pgr; + ng++; + pgr++; + } + else + { + avalue[i] = (void *) *pst; + pst++; + } + break; + + case FFI_TYPE_SINT64: + case FFI_TYPE_UINT64: + soft_double_closure: + /* 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 < 7) + { + if (ng & 0x01) + { + /* 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 = 8; + } + break; + + case FFI_TYPE_FLOAT: + /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32. */ + if (cif->abi == FFI_LINUX_SOFT_FLOAT) + goto soft_float_closure; + /* unfortunately float values are stored as doubles + * in the ffi_closure_SYSV code (since we don't check + * the type in that routine). + */ + + /* there are 8 64bit floating point registers */ + + if (nf < 8) + { + temp = pfr->d; + pfr->f = (float) temp; + avalue[i] = pfr; + nf++; + pfr++; + } + else + { + /* 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... + */ + avalue[i] = pst; + pst += 1; + } + break; + + case FFI_TYPE_DOUBLE: + /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64. */ + if (cif->abi == FFI_LINUX_SOFT_FLOAT) + goto soft_double_closure; + /* On the outgoing stack all values are aligned to 8 */ + /* there are 8 64bit floating point registers */ + + if (nf < 8) + { + 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 (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT) + goto do_struct; + if (cif->abi == FFI_LINUX_SOFT_FLOAT) + { /* Test if for the whole long double, 4 gprs are available. + otherwise the stuff ends up on the stack. */ + if (ng < 5) + { + avalue[i] = pgr; + pgr += 4; + ng += 4; + } + else + { + avalue[i] = pst; + pst += 4; + ng = 8; + } + break; + } + if (nf < 7) + { + avalue[i] = pfr; + pfr += 2; + nf += 2; + } + else + { + if (((long) pst) & 4) + pst++; + avalue[i] = pst; + pst += 4; + nf = 8; + } + break; +#endif + + default: + FFI_ASSERT (0); + } + + i++; + } + + + (closure->fun) (cif, rvalue, avalue, closure->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 - 1 with the size of the struct. + So a one byte struct gets the return type 16. Return type 1 to 15 are + already used and we never have a struct with size zero. That is the reason + for the subtraction of 1. See the comment in ffitarget.h about ordering. + */ + if (cif->abi == FFI_SYSV && cif->rtype->type == FFI_TYPE_STRUCT + && size <= 8) + return (FFI_SYSV_TYPE_SMALL_STRUCT - 1) + size; +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + else if (cif->rtype->type == FFI_TYPE_LONGDOUBLE + && cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT) + return FFI_TYPE_STRUCT; +#endif + /* With FFI_LINUX_SOFT_FLOAT floats and doubles are handled like UINT32 + respectivley UINT64. */ + if (cif->abi == FFI_LINUX_SOFT_FLOAT) + { + switch (cif->rtype->type) + { + case FFI_TYPE_FLOAT: + return FFI_TYPE_UINT32; + break; + case FFI_TYPE_DOUBLE: + return FFI_TYPE_UINT64; + break; +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + case FFI_TYPE_LONGDOUBLE: + return FFI_TYPE_UINT128; + break; +#endif + default: + return cif->rtype->type; + } + } + else + { + return cif->rtype->type; + } +} + +int FFI_HIDDEN ffi_closure_helper_LINUX64 (ffi_closure *, void *, + unsigned long *, ffi_dblfl *); + +int FFI_HIDDEN +ffi_closure_helper_LINUX64 (ffi_closure *closure, void *rvalue, + unsigned long *pst, ffi_dblfl *pfr) +{ + /* rvalue is the pointer to space for return value in closure assembly */ + /* pst is the pointer to parameter save area + (r3-r10 are stored into its first 8 slots by ffi_closure_LINUX64) */ + /* pfr is the pointer to where f1-f13 are stored in ffi_closure_LINUX64 */ + + void **avalue; + ffi_type **arg_types; + long i, avn; + ffi_cif *cif; + ffi_dblfl *end_pfr = pfr + NUM_FPR_ARG_REGISTERS64; + + cif = closure->cif; + avalue = alloca (cif->nargs * sizeof (void *)); + + /* Copy the caller's structure return value address so that the closure + returns the data directly to the caller. */ + if (cif->rtype->type == FFI_TYPE_STRUCT) + { + rvalue = (void *) *pst; + pst++; + } + + i = 0; + avn = cif->nargs; + arg_types = cif->arg_types; + + /* Grab the addresses of the arguments from the stack frame. */ + while (i < avn) + { + switch (arg_types[i]->type) + { + case FFI_TYPE_SINT8: + case FFI_TYPE_UINT8: + avalue[i] = (char *) pst + 7; + pst++; + break; + + case FFI_TYPE_SINT16: + case FFI_TYPE_UINT16: + avalue[i] = (char *) pst + 6; + pst++; + break; + + case FFI_TYPE_SINT32: + case FFI_TYPE_UINT32: + avalue[i] = (char *) pst + 4; + pst++; + break; + + case FFI_TYPE_SINT64: + case FFI_TYPE_UINT64: + case FFI_TYPE_POINTER: + avalue[i] = pst; + pst++; + break; + + case FFI_TYPE_STRUCT: + /* Structures with size less than eight bytes are passed + left-padded. */ + if (arg_types[i]->size < 8) + avalue[i] = (char *) pst + 8 - arg_types[i]->size; + else + avalue[i] = pst; + pst += (arg_types[i]->size + 7) / 8; + break; + + case FFI_TYPE_FLOAT: + /* unfortunately float values are stored as doubles + * in the ffi_closure_LINUX64 code (since we don't check + * the type in that routine). + */ + + /* there are 13 64bit floating point registers */ + + if (pfr < end_pfr) + { + double temp = pfr->d; + pfr->f = (float) temp; + avalue[i] = pfr; + pfr++; + } + else + avalue[i] = pst; + pst++; + break; + + case FFI_TYPE_DOUBLE: + /* On the outgoing stack all values are aligned to 8 */ + /* there are 13 64bit floating point registers */ + + if (pfr < end_pfr) + { + avalue[i] = pfr; + pfr++; + } + else + avalue[i] = pst; + pst++; + break; + +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + case FFI_TYPE_LONGDOUBLE: + if (pfr + 1 < end_pfr) + { + avalue[i] = pfr; + pfr += 2; + } + else + { + if (pfr < end_pfr) + { + /* Passed partly in f13 and partly on the stack. + Move it all to the stack. */ + *pst = *(unsigned long *) pfr; + pfr++; + } + avalue[i] = pst; + } + pst += 2; + break; +#endif + + default: + FFI_ASSERT (0); + } + + i++; + } + + + (closure->fun) (cif, rvalue, avalue, closure->user_data); + + /* Tell ffi_closure_LINUX64 how to perform return type promotions. */ + return cif->rtype->type; +} diff --git a/src/powerpc/ffi.c.rej b/src/powerpc/ffi.c.rej new file mode 100644 index 0000000..021f6e3 --- /dev/null +++ b/src/powerpc/ffi.c.rej @@ -0,0 +1,12 @@ +--- src/powerpc/ffi.c ++++ src/powerpc/ffi.c +@@ -717,9 +734,6 @@ + } + } + } +-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE +- byref: +-#endif + intarg_count++; + flags |= FLAG_RETVAL_REFERENCE; + /* Fall through. */ diff --git a/src/powerpc/ffitarget.h b/src/powerpc/ffitarget.h index d17f731..820c482 100644 --- a/src/powerpc/ffitarget.h +++ b/src/powerpc/ffitarget.h @@ -60,18 +60,14 @@ typedef enum ffi_abi { FFI_LINUX64, FFI_LINUX, FFI_LINUX_SOFT_FLOAT, -# ifdef POWERPC64 +# if defined(POWERPC64) FFI_DEFAULT_ABI = FFI_LINUX64, -# else -# if (!defined(__NO_FPRS__) && (__LDBL_MANT_DIG__ == 106)) - FFI_DEFAULT_ABI = FFI_LINUX, -# else -# ifdef __NO_FPRS__ +# elif defined(__NO_FPRS__) FFI_DEFAULT_ABI = FFI_LINUX_SOFT_FLOAT, -# else +# elif (__LDBL_MANT_DIG__ == 106) + FFI_DEFAULT_ABI = FFI_LINUX, +# else FFI_DEFAULT_ABI = FFI_GCC_SYSV, -# endif -# endif # endif #endif diff --git a/src/powerpc/ppc_closure.S b/src/powerpc/ppc_closure.S index 56f7d1a..41fb885 100644 --- a/src/powerpc/ppc_closure.S +++ b/src/powerpc/ppc_closure.S @@ -122,22 +122,41 @@ ENTRY(ffi_closure_SYSV) blr # case FFI_TYPE_FLOAT +#ifndef __NO_FPRS__ lfs %f1,112+0(%r1) mtlr %r0 addi %r1,%r1,144 +#else + nop + nop + nop +#endif blr # case FFI_TYPE_DOUBLE +#ifndef __NO_FPRS__ lfd %f1,112+0(%r1) mtlr %r0 addi %r1,%r1,144 +#else + nop + nop + nop +#endif blr # case FFI_TYPE_LONGDOUBLE +#ifndef __NO_FPRS__ lfd %f1,112+0(%r1) lfd %f2,112+8(%r1) mtlr %r0 b .Lfinish +#else + nop + nop + nop + blr +#endif # case FFI_TYPE_UINT8 lbz %r3,112+3(%r1) diff --git a/src/powerpc/sysv.S b/src/powerpc/sysv.S index 9ad8d9f..675ed03 100644 --- a/src/powerpc/sysv.S +++ b/src/powerpc/sysv.S @@ -83,6 +83,7 @@ ENTRY(ffi_call_SYSV) nop 1: +#ifndef __NO_FPRS__ /* Load all the FP registers. */ bf- 6,2f lfd %f1,-16-(8*4)-(8*8)(%r28) @@ -94,6 +95,7 @@ ENTRY(ffi_call_SYSV) lfd %f6,-16-(8*4)-(3*8)(%r28) lfd %f7,-16-(8*4)-(2*8)(%r28) lfd %f8,-16-(8*4)-(1*8)(%r28) +#endif 2: /* Make the call. */ @@ -103,7 +105,9 @@ ENTRY(ffi_call_SYSV) mtcrf 0x01,%r31 /* cr7 */ bt- 31,L(small_struct_return_value) bt- 30,L(done_return_value) +#ifndef __NO_FPRS__ bt- 29,L(fp_return_value) +#endif stw %r3,0(%r30) bf+ 28,L(done_return_value) stw %r4,4(%r30) @@ -124,6 +128,7 @@ L(done_return_value): lwz %r1,0(%r1) blr +#ifndef __NO_FPRS__ L(fp_return_value): bf 28,L(float_return_value) stfd %f1,0(%r30) @@ -134,6 +139,7 @@ L(fp_return_value): L(float_return_value): stfs %f1,0(%r30) b L(done_return_value) +#endif L(small_struct_return_value): /* diff --git a/src/powerpc/sysv.S.orig b/src/powerpc/sysv.S.orig new file mode 100644 index 0000000..9ad8d9f --- /dev/null +++ b/src/powerpc/sysv.S.orig @@ -0,0 +1,214 @@ +/* ----------------------------------------------------------------------- + sysv.S - Copyright (c) 1998 Geoffrey Keating + Copyright (C) 2007 Free Software Foundation, Inc + + PowerPC Assembly glue. + + 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. + ----------------------------------------------------------------------- */ + +#define LIBFFI_ASM +#include <fficonfig.h> +#include <ffi.h> +#include <powerpc/asm.h> + +#ifndef __powerpc64__ + .globl ffi_prep_args_SYSV +ENTRY(ffi_call_SYSV) +.LFB1: + /* Save the old stack pointer as AP. */ + mr %r8,%r1 + +.LCFI0: + /* Allocate the stack space we need. */ + stwux %r1,%r1,%r4 + /* Save registers we use. */ + mflr %r9 + stw %r28,-16(%r8) +.LCFI1: + stw %r29,-12(%r8) +.LCFI2: + stw %r30, -8(%r8) +.LCFI3: + stw %r31, -4(%r8) +.LCFI4: + stw %r9, 4(%r8) +.LCFI5: + + /* Save arguments over call... */ + mr %r31,%r5 /* flags, */ + mr %r30,%r6 /* rvalue, */ + mr %r29,%r7 /* function address, */ + mr %r28,%r8 /* our AP. */ +.LCFI6: + + /* Call ffi_prep_args_SYSV. */ + mr %r4,%r1 + bl ffi_prep_args_SYSV@local + + /* Now do the call. */ + /* Set up cr1 with bits 4-7 of the flags. */ + mtcrf 0x40,%r31 + /* Get the address to call into CTR. */ + mtctr %r29 + /* Load all those argument registers. */ + lwz %r3,-16-(8*4)(%r28) + lwz %r4,-16-(7*4)(%r28) + lwz %r5,-16-(6*4)(%r28) + lwz %r6,-16-(5*4)(%r28) + bf- 5,1f + nop + lwz %r7,-16-(4*4)(%r28) + lwz %r8,-16-(3*4)(%r28) + lwz %r9,-16-(2*4)(%r28) + lwz %r10,-16-(1*4)(%r28) + nop +1: + + /* Load all the FP registers. */ + bf- 6,2f + lfd %f1,-16-(8*4)-(8*8)(%r28) + lfd %f2,-16-(8*4)-(7*8)(%r28) + lfd %f3,-16-(8*4)-(6*8)(%r28) + lfd %f4,-16-(8*4)-(5*8)(%r28) + nop + lfd %f5,-16-(8*4)-(4*8)(%r28) + lfd %f6,-16-(8*4)-(3*8)(%r28) + lfd %f7,-16-(8*4)-(2*8)(%r28) + lfd %f8,-16-(8*4)-(1*8)(%r28) +2: + + /* Make the call. */ + bctrl + + /* Now, deal with the return value. */ + mtcrf 0x01,%r31 /* cr7 */ + bt- 31,L(small_struct_return_value) + bt- 30,L(done_return_value) + bt- 29,L(fp_return_value) + stw %r3,0(%r30) + bf+ 28,L(done_return_value) + stw %r4,4(%r30) + mtcrf 0x02,%r31 /* cr6 */ + bf 27,L(done_return_value) + stw %r5,8(%r30) + stw %r6,12(%r30) + /* Fall through... */ + +L(done_return_value): + /* Restore the registers we used and return. */ + lwz %r9, 4(%r28) + lwz %r31, -4(%r28) + mtlr %r9 + lwz %r30, -8(%r28) + lwz %r29,-12(%r28) + lwz %r28,-16(%r28) + lwz %r1,0(%r1) + blr + +L(fp_return_value): + bf 28,L(float_return_value) + stfd %f1,0(%r30) + mtcrf 0x02,%r31 /* cr6 */ + bf 27,L(done_return_value) + stfd %f2,8(%r30) + b L(done_return_value) +L(float_return_value): + stfs %f1,0(%r30) + b L(done_return_value) + +L(small_struct_return_value): + /* + * The C code always allocates a properly-aligned 8-byte bounce + * buffer to make this assembly code very simple. Just write out + * r3 and r4 to the buffer to allow the C code to handle the rest. + */ + stw %r3, 0(%r30) + stw %r4, 4(%r30) + b L(done_return_value) + +.LFE1: +END(ffi_call_SYSV) + + .section ".eh_frame",EH_FRAME_FLAGS,@progbits +.Lframe1: + .4byte .LECIE1-.LSCIE1 /* Length of Common Information Entry */ +.LSCIE1: + .4byte 0x0 /* CIE Identifier Tag */ + .byte 0x1 /* CIE Version */ +#if defined _RELOCATABLE || defined __PIC__ + .ascii "zR\0" /* CIE Augmentation */ +#else + .ascii "\0" /* CIE Augmentation */ +#endif + .uleb128 0x1 /* CIE Code Alignment Factor */ + .sleb128 -4 /* CIE Data Alignment Factor */ + .byte 0x41 /* CIE RA Column */ +#if defined _RELOCATABLE || defined __PIC__ + .uleb128 0x1 /* Augmentation size */ + .byte 0x1b /* FDE Encoding (pcrel sdata4) */ +#endif + .byte 0xc /* DW_CFA_def_cfa */ + .uleb128 0x1 + .uleb128 0x0 + .align 2 +.LECIE1: +.LSFDE1: + .4byte .LEFDE1-.LASFDE1 /* FDE Length */ +.LASFDE1: + .4byte .LASFDE1-.Lframe1 /* FDE CIE offset */ +#if defined _RELOCATABLE || defined __PIC__ + .4byte .LFB1-. /* FDE initial location */ +#else + .4byte .LFB1 /* FDE initial location */ +#endif + .4byte .LFE1-.LFB1 /* FDE address range */ +#if defined _RELOCATABLE || defined __PIC__ + .uleb128 0x0 /* Augmentation size */ +#endif + .byte 0x4 /* DW_CFA_advance_loc4 */ + .4byte .LCFI0-.LFB1 + .byte 0xd /* DW_CFA_def_cfa_register */ + .uleb128 0x08 + .byte 0x4 /* DW_CFA_advance_loc4 */ + .4byte .LCFI5-.LCFI0 + .byte 0x11 /* DW_CFA_offset_extended_sf */ + .uleb128 0x41 + .sleb128 -1 + .byte 0x9f /* DW_CFA_offset, column 0x1f */ + .uleb128 0x1 + .byte 0x9e /* DW_CFA_offset, column 0x1e */ + .uleb128 0x2 + .byte 0x9d /* DW_CFA_offset, column 0x1d */ + .uleb128 0x3 + .byte 0x9c /* DW_CFA_offset, column 0x1c */ + .uleb128 0x4 + .byte 0x4 /* DW_CFA_advance_loc4 */ + .4byte .LCFI6-.LCFI5 + .byte 0xd /* DW_CFA_def_cfa_register */ + .uleb128 0x1c + .align 2 +.LEFDE1: +#endif + +#if defined __ELF__ && defined __linux__ + .section .note.GNU-stack,"",@progbits +#endif |