>From 95d80e11f6d14da32c9e117321658c27155e313a Mon Sep 17 00:00:00 2001 From: Kyle Moffett Date: Tue, 16 Aug 2011 14:46:50 -0400 Subject: [PATCH] PowerPC: Debug and fix soft-floating-point support There were a wide range of bugs in this code, including long-double register alignment issues, assignments to global constants (which were actually stored as non-constant integers). This passes the testsuite on soft-floating-point PowerPC, and it builds and passes the testsuite on PowerPC e500 systems which cannot even assemble the regular floating-point instruction set. Signed-off-by: Kyle Moffett --- src/powerpc/ffi.c | 533 ++++++++++++++++++++++++--------------------- src/powerpc/ffitarget.h | 14 +- src/powerpc/ppc_closure.S | 19 ++ src/powerpc/sysv.S | 6 + 4 files changed, 310 insertions(+), 262 deletions(-) diff --git a/src/powerpc/ffi.c b/src/powerpc/ffi.c index fb2a39f..e5ec1c5 100644 --- a/src/libffi/src/powerpc/ffi.c +++ b/src/libffi/src/powerpc/ffi.c @@ -39,7 +39,9 @@ /* 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 */ @@ -50,21 +52,20 @@ /* Bits (31-24) through (31-19) store shift value for SMST */ 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. @@ -113,10 +114,12 @@ 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. */ @@ -125,9 +128,8 @@ /* '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; @@ -143,15 +145,16 @@ 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. */ @@ -174,12 +177,29 @@ 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) { + double double_tmp; +#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) { @@ -195,8 +215,6 @@ 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) @@ -218,43 +236,6 @@ #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) @@ -280,13 +261,40 @@ 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) @@ -319,9 +327,6 @@ 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); @@ -349,7 +354,6 @@ case FFI_TYPE_UINT32: case FFI_TYPE_SINT32: case FFI_TYPE_POINTER: - soft_float_prep: gprvalue = **p_argv.ui; @@ -366,8 +370,10 @@ /* 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); } @@ -604,9 +610,6 @@ 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. @@ -646,25 +649,38 @@ - 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; @@ -699,9 +715,6 @@ } } } -#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE - byref: -#endif intarg_count++; flags |= FLAG_RETVAL_REFERENCE; /* Fall through. */ @@ -722,39 +735,36 @@ 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. */ @@ -763,10 +773,21 @@ && 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 @@ -783,9 +804,6 @@ 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 @@ -793,12 +811,20 @@ 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 @@ -827,16 +853,29 @@ 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) @@ -844,21 +883,27 @@ 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) @@ -905,9 +950,11 @@ 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; @@ -1011,32 +1058,38 @@ 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++; @@ -1047,10 +1100,109 @@ 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) + { + double 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 */ @@ -1086,7 +1238,6 @@ 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) { @@ -1102,9 +1253,6 @@ 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) @@ -1122,7 +1270,6 @@ 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) @@ -1154,99 +1301,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); + FFI_ASSERT (0); } i++; @@ -1263,39 +1319,9 @@ 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/ffitarget.h b/src/powerpc/ffitarget.h index d17f731..820c482 100644 --- a/src/libffi/src/powerpc/ffitarget.h +++ b/src/libffi/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/libffi/src/powerpc/ppc_closure.S +++ b/src/libffi/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 96ea22b..5ee3a19 100644 --- a/src/libffi/src/powerpc/sysv.S +++ b/src/libffi/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): extrwi %r6,%r31,2,19 /* number of bytes padding = shift/8 */ -- 1.7.2.5