The powerpc64 support opted to pass floating point values both in the fpr area and the parameter save area, necessary when the backend doesn't know if a function argument corresponds to the ellipsis arguments of a variadic function. This patch adds powerpc support for variadic functions, and changes the code to only pass fp in the ABI mandated area. ELFv2 needs this change since the parameter save area may not exist there.
This also fixes two faulty tests that used a non-variadic function cast to call a variadic function, and spuriously reasoned that this is somehow necessary for static functions.. The whitespace changes, and comment changes in the tests, are to make the gcc versions of these files mirror upstream libffi. * src/powerpc/ffitarget.h (FFI_TARGET_SPECIFIC_VARIADIC): Define. (FFI_EXTRA_CIF_FIELDS): Define. * src/powerpc/ffi.c (ffi_prep_args64): Save fprs as per the ABI, not to both fpr and param save area. (ffi_prep_cif_machdep_core): Renamed from ffi_prep_cif_machdep. Keep initial flags. Formatting. Remove dead FFI_LINUX_SOFT_FLOAT code. (ffi_prep_cif_machdep, ffi_prep_cif_machdep_var): New functions. (ffi_closure_helper_LINUX64): Pass floating point as per ABI, not to both fpr and parameter save areas. * libffi/testsuite/libffi.call/cls_double_va.c (main): Correct function cast and don't call ffi_prep_cif. * libffi/testsuite/libffi.call/cls_longdouble_va.c (main): Likewise. diff -urp gcc3/libffi/src/powerpc/ffitarget.h gcc4/libffi/src/powerpc/ffitarget.h --- gcc3/libffi/src/powerpc/ffitarget.h 2013-11-15 23:03:07.313959745 +1030 +++ gcc4/libffi/src/powerpc/ffitarget.h 2013-11-15 23:19:21.692053339 +1030 @@ -106,6 +106,10 @@ typedef enum ffi_abi { #define FFI_CLOSURES 1 #define FFI_NATIVE_RAW_API 0 +#if defined (POWERPC) || defined (POWERPC_FREEBSD) +# define FFI_TARGET_SPECIFIC_VARIADIC 1 +# define FFI_EXTRA_CIF_FIELDS unsigned nfixedargs +#endif /* For additional types like the below, take care about the order in ppc_closures.S. They must follow after the FFI_TYPE_LAST. */ diff -urp gcc3/libffi/src/powerpc/ffi.c gcc4/libffi/src/powerpc/ffi.c --- gcc3/libffi/src/powerpc/ffi.c 2013-11-15 23:06:57.313036827 +1030 +++ gcc4/libffi/src/powerpc/ffi.c 2013-11-15 23:47:24.402296569 +1030 @@ -443,9 +443,9 @@ ffi_prep_args64 (extended_cif *ecif, uns /* 'fpr_base' points at the space for fpr3, and grows upwards as we use FPR registers. */ valp fpr_base; - int fparg_count; + unsigned int fparg_count; - int i, words; + unsigned int i, words, nargs, nfixedargs; ffi_type **ptr; double double_tmp; union { @@ -482,30 +482,34 @@ ffi_prep_args64 (extended_cif *ecif, uns /* 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++) + nargs = ecif->cif->nargs; + nfixedargs = ecif->cif->nfixedargs; + for (ptr = ecif->cif->arg_types, i = 0; + i < nargs; + i++, ptr++, p_argv.v++) { switch ((*ptr)->type) { case FFI_TYPE_FLOAT: double_tmp = **p_argv.f; - *next_arg.f = (float) double_tmp; + if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) + *fpr_base.d++ = double_tmp; + else + *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 (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) + *fpr_base.d++ = double_tmp; + else + *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; @@ -513,18 +517,20 @@ ffi_prep_args64 (extended_cif *ecif, uns #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE case FFI_TYPE_LONGDOUBLE: double_tmp = (*p_argv.d)[0]; - *next_arg.d = double_tmp; + if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) + *fpr_base.d++ = double_tmp; + else + *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 (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) + *fpr_base.d++ = double_tmp; + else + *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); @@ -597,15 +603,14 @@ ffi_prep_args64 (extended_cif *ecif, uns /* Perform machine dependent cif processing */ -ffi_status -ffi_prep_cif_machdep (ffi_cif *cif) +static ffi_status +ffi_prep_cif_machdep_core (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 i, fparg_count = 0, intarg_count = 0; + unsigned flags = cif->flags; unsigned struct_copy_size = 0; unsigned type = cif->rtype->type; unsigned size = cif->rtype->size; @@ -650,19 +655,23 @@ ffi_prep_cif_machdep (ffi_cif *cif) - 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 (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; + if (type == FFI_TYPE_LONGDOUBLE) + type = FFI_TYPE_STRUCT; #endif - } + } switch (type) { @@ -823,13 +832,8 @@ ffi_prep_cif_machdep (ffi_cif *cif) { #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; - } + fparg_count += 2; + intarg_count += 2; break; #endif case FFI_TYPE_FLOAT: @@ -911,6 +915,22 @@ ffi_prep_cif_machdep (ffi_cif *cif) return FFI_OK; } +ffi_status +ffi_prep_cif_machdep (ffi_cif *cif) +{ + cif->nfixedargs = cif->nargs; + return ffi_prep_cif_machdep_core (cif); +} + +ffi_status +ffi_prep_cif_machdep_var (ffi_cif *cif, + unsigned int nfixedargs, + unsigned int ntotalargs MAYBE_UNUSED) +{ + cif->nfixedargs = nfixedargs; + return ffi_prep_cif_machdep_core (cif); +} + extern void ffi_call_SYSV(extended_cif *, unsigned, unsigned, unsigned *, void (*fn)(void)); extern void FFI_HIDDEN ffi_call_LINUX64(extended_cif *, unsigned long, @@ -1238,6 +1258,7 @@ ffi_closure_helper_SYSV (ffi_closure *cl } break; #endif + case FFI_TYPE_SINT16: case FFI_TYPE_UINT16: #ifndef __LITTLE_ENDIAN__ @@ -1255,6 +1276,7 @@ ffi_closure_helper_SYSV (ffi_closure *cl } break; #endif + case FFI_TYPE_SINT32: case FFI_TYPE_UINT32: case FFI_TYPE_POINTER: @@ -1358,7 +1380,7 @@ ffi_closure_helper_LINUX64 (ffi_closure void **avalue; ffi_type **arg_types; - long i, avn; + unsigned long i, avn, nfixedargs; ffi_cif *cif; ffi_dblfl *end_pfr = pfr + NUM_FPR_ARG_REGISTERS64; @@ -1375,6 +1397,7 @@ ffi_closure_helper_LINUX64 (ffi_closure i = 0; avn = cif->nargs; + nfixedargs = cif->nfixedargs; arg_types = cif->arg_types; /* Grab the addresses of the arguments from the stack frame. */ @@ -1389,6 +1412,7 @@ ffi_closure_helper_LINUX64 (ffi_closure pst++; break; #endif + case FFI_TYPE_SINT16: case FFI_TYPE_UINT16: #ifndef __LITTLE_ENDIAN__ @@ -1396,6 +1420,7 @@ ffi_closure_helper_LINUX64 (ffi_closure pst++; break; #endif + case FFI_TYPE_SINT32: case FFI_TYPE_UINT32: #ifndef __LITTLE_ENDIAN__ @@ -1403,6 +1428,7 @@ ffi_closure_helper_LINUX64 (ffi_closure pst++; break; #endif + case FFI_TYPE_SINT64: case FFI_TYPE_UINT64: case FFI_TYPE_POINTER: @@ -1430,7 +1456,7 @@ ffi_closure_helper_LINUX64 (ffi_closure /* there are 13 64bit floating point registers */ - if (pfr < end_pfr) + if (pfr < end_pfr && i < nfixedargs) { double temp = pfr->d; pfr->f = (float) temp; @@ -1446,7 +1472,7 @@ ffi_closure_helper_LINUX64 (ffi_closure /* On the outgoing stack all values are aligned to 8 */ /* there are 13 64bit floating point registers */ - if (pfr < end_pfr) + if (pfr < end_pfr && i < nfixedargs) { avalue[i] = pfr; pfr++; @@ -1458,14 +1484,14 @@ ffi_closure_helper_LINUX64 (ffi_closure #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE case FFI_TYPE_LONGDOUBLE: - if (pfr + 1 < end_pfr) + if (pfr + 1 < end_pfr && i + 1 < nfixedargs) { avalue[i] = pfr; pfr += 2; } else { - if (pfr < end_pfr) + if (pfr < end_pfr && i < nfixedargs) { /* Passed partly in f13 and partly on the stack. Move it all to the stack. */ diff -urp gcc3/libffi/testsuite/libffi.call/cls_double_va.c gcc4/libffi/testsuite/libffi.call/cls_double_va.c --- gcc3/libffi/testsuite/libffi.call/cls_double_va.c 2013-11-15 23:03:07.193964372 +1030 +++ gcc4/libffi/testsuite/libffi.call/cls_double_va.c 2013-11-15 23:22:51.383884118 +1030 @@ -38,26 +38,24 @@ int main (void) /* This printf call is variadic */ CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2, &ffi_type_sint, - arg_types) == FFI_OK); + arg_types) == FFI_OK); args[0] = &format; args[1] = &doubleArg; args[2] = NULL; ffi_call(&cif, FFI_FN(printf), &res, args); - // { dg-output "7.0" } + /* { dg-output "7.0" } */ printf("res: %d\n", (int) res); - // { dg-output "\nres: 4" } + /* { dg-output "\nres: 4" } */ - /* The call to cls_double_va_fn is static, so have to use a normal prep_cif */ - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_sint, arg_types) == FFI_OK); + CHECK(ffi_prep_closure_loc(pcl, &cif, cls_double_va_fn, NULL, + code) == FFI_OK); - CHECK(ffi_prep_closure_loc(pcl, &cif, cls_double_va_fn, NULL, code) == FFI_OK); - - res = ((int(*)(char*, double))(code))(format, doubleArg); - // { dg-output "\n7.0" } + res = ((int(*)(char*, ...))(code))(format, doubleArg); + /* { dg-output "\n7.0" } */ printf("res: %d\n", (int) res); - // { dg-output "\nres: 4" } + /* { dg-output "\nres: 4" } */ exit(0); } diff -urp gcc3/libffi/testsuite/libffi.call/cls_longdouble_va.c gcc4/libffi/testsuite/libffi.call/cls_longdouble_va.c --- gcc3/libffi/testsuite/libffi.call/cls_longdouble_va.c 2013-11-15 23:03:07.205963908 +1030 +++ gcc4/libffi/testsuite/libffi.call/cls_longdouble_va.c 2013-11-15 23:24:19.372455652 +1030 @@ -38,27 +38,24 @@ int main (void) /* This printf call is variadic */ CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2, &ffi_type_sint, - arg_types) == FFI_OK); + arg_types) == FFI_OK); args[0] = &format; args[1] = &ldArg; args[2] = NULL; ffi_call(&cif, FFI_FN(printf), &res, args); - // { dg-output "7.0" } + /* { dg-output "7.0" } */ printf("res: %d\n", (int) res); - // { dg-output "\nres: 4" } + /* { dg-output "\nres: 4" } */ - /* The call to cls_longdouble_va_fn is static, so have to use a normal prep_cif */ - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_sint, - arg_types) == FFI_OK); + CHECK(ffi_prep_closure_loc(pcl, &cif, cls_longdouble_va_fn, NULL, + code) == FFI_OK); - CHECK(ffi_prep_closure_loc(pcl, &cif, cls_longdouble_va_fn, NULL, code) == FFI_OK); - - res = ((int(*)(char*, long double))(code))(format, ldArg); - // { dg-output "\n7.0" } + res = ((int(*)(char*, ...))(code))(format, ldArg); + /* { dg-output "\n7.0" } */ printf("res: %d\n", (int) res); - // { dg-output "\nres: 4" } + /* { dg-output "\nres: 4" } */ exit(0); } -- Alan Modra Australia Development Lab, IBM