diff options
Diffstat (limited to 'libffi')
-rw-r--r-- | libffi/ChangeLog | 20 | ||||
-rw-r--r-- | libffi/src/powerpc/ffi.c | 203 | ||||
-rw-r--r-- | libffi/src/powerpc/ffitarget.h | 16 | ||||
-rw-r--r-- | libffi/src/powerpc/ppc_closure.S | 14 | ||||
-rw-r--r-- | libffi/src/powerpc/sysv.S | 9 |
5 files changed, 222 insertions, 40 deletions
diff --git a/libffi/ChangeLog b/libffi/ChangeLog index 689bc452a7a..91609e3a79b 100644 --- a/libffi/ChangeLog +++ b/libffi/ChangeLog @@ -1,3 +1,23 @@ +2007-12-01 Andreas Tobler <a.tobler@schweiz.org> + + PR libffi/31937 + * src/powerpc/ffitarget.h: Introduce new ABI FFI_LINUX_SOFT_FLOAT. + Add local FFI_TYPE_UINT128 to handle soft-float long-double-128. + * src/powerpc/ffi.c: Distinguish between __NO_FPRS__ and not and + set the NUM_FPR_ARG_REGISTERS according to. + Add support for potential soft-float support under hard-float + architecture. + (ffi_prep_args_SYSV): Set NUM_FPR_ARG_REGISTERS to 0 in case of + FFI_LINUX_SOFT_FLOAT, handle float, doubles and long-doubles according + to the FFI_LINUX_SOFT_FLOAT ABI. + (ffi_prep_cif_machdep): Likewise. + (ffi_closure_helper_SYSV): Likewise. + * src/powerpc/ppc_closure.S: Make sure not to store float/double + on archs where __NO_FPRS__ is true. + Add FFI_TYPE_UINT128 support. + * src/powerpc/sysv.S: Add support for soft-float long-double-128. + Adjust copyright notice. + 2007-11-25 Andreas Tobler <a.tobler@schweiz.org> * src/closures.c: Move defintion of MAYBE_UNUSED from here to ... diff --git a/libffi/src/powerpc/ffi.c b/libffi/src/powerpc/ffi.c index eaa4c869be8..e6b869f8737 100644 --- a/libffi/src/powerpc/ffi.c +++ b/libffi/src/powerpc/ffi.c @@ -50,10 +50,13 @@ enum { }; /* About the SYSV ABI. */ -enum { - NUM_GPR_ARG_REGISTERS = 8, - NUM_FPR_ARG_REGISTERS = 8 -}; +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 @@ -116,7 +119,7 @@ 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; + int i, ii MAYBE_UNUSED; ffi_type **ptr; double double_tmp; union { @@ -134,6 +137,9 @@ 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; @@ -165,6 +171,9 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack) 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) { @@ -178,6 +187,9 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack) 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) @@ -199,38 +211,75 @@ 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) + if ((ecif->cif->abi != FFI_LINUX) + && (ecif->cif->abi != FFI_LINUX_SOFT_FLOAT)) goto do_struct; - double_tmp = (*p_argv.d)[0]; - - if (fparg_count >= NUM_FPR_ARG_REGISTERS - 1) + /* 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) { - if (intarg_count >= NUM_GPR_ARG_REGISTERS - && intarg_count % 2 != 0) + unsigned int int_tmp = (*p_argv.ui)[0]; + if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3) { - intarg_count++; + 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++; + } } - *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 + { + *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 { - *fpr_base.d++ = double_tmp; - double_tmp = (*p_argv.d)[1]; - *fpr_base.d++ = double_tmp; - } + double_tmp = (*p_argv.d)[0]; - fparg_count += 2; - FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); + 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) @@ -293,6 +342,8 @@ 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; putgpr: @@ -546,6 +597,9 @@ 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. @@ -582,14 +636,16 @@ ffi_prep_cif_machdep (ffi_cif *cif) For LINUX64: - integer values in gpr3; - Structures/Unions by reference; - - Single/double FP values in fpr1, long double in fpr1,fpr2. */ + - 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) + if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64 + && cif->abi != FFI_LINUX_SOFT_FLOAT) goto byref; - flags |= FLAG_RETURNS_128BITS; /* Fall through. */ #endif @@ -597,7 +653,9 @@ ffi_prep_cif_machdep (ffi_cif *cif) flags |= FLAG_RETURNS_64BITS; /* Fall through. */ case FFI_TYPE_FLOAT: - flags |= FLAG_RETURNS_FP; + /* 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: @@ -660,18 +718,36 @@ ffi_prep_cif_machdep (ffi_cif *cif) 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) + if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT) goto do_struct; - fparg_count++; + 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. */ @@ -683,6 +759,7 @@ ffi_prep_cif_machdep (ffi_cif *cif) 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 @@ -710,6 +787,7 @@ ffi_prep_cif_machdep (ffi_cif *cif) /* 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++; @@ -723,8 +801,13 @@ ffi_prep_cif_machdep (ffi_cif *cif) { #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE case FFI_TYPE_LONGDOUBLE: - fparg_count += 2; - intarg_count += 2; + if (cif->abi == FFI_LINUX_SOFT_FLOAT) + intarg_count += 4; + else + { + fparg_count += 2; + intarg_count += 2; + } break; #endif case FFI_TYPE_FLOAT: @@ -818,6 +901,7 @@ ffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue) 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 @@ -942,7 +1026,7 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue, && !((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 && cif->abi != FFI_LINUX_SOFT_FLOAT) #endif ) { @@ -995,6 +1079,7 @@ 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) { @@ -1030,6 +1115,7 @@ 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) @@ -1061,6 +1147,9 @@ 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). @@ -1089,6 +1178,9 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue, 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 */ @@ -1109,9 +1201,24 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue, #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE case FFI_TYPE_LONGDOUBLE: - if (cif->abi != FFI_LINUX) + 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; + } + break; + } if (nf < 7) { avalue[i] = pfr; @@ -1147,10 +1254,34 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue, return FFI_SYSV_TYPE_SMALL_STRUCT + size; #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE else if (cif->rtype->type == FFI_TYPE_LONGDOUBLE - && cif->abi != FFI_LINUX) + && cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT) return FFI_TYPE_STRUCT; #endif - return cif->rtype->type; + /* 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 *, diff --git a/libffi/src/powerpc/ffitarget.h b/libffi/src/powerpc/ffitarget.h index e7f62950371..e3fa30be6b5 100644 --- a/libffi/src/powerpc/ffitarget.h +++ b/libffi/src/powerpc/ffitarget.h @@ -1,5 +1,6 @@ /* -----------------------------------------------------------------*-C-*- ffitarget.h - Copyright (c) 1996-2003 Red Hat, Inc. + Copyright (C) 2007 Free Software Foundation, Inc Target configuration macros for PowerPC. Permission is hereby granted, free of charge, to any person obtaining @@ -44,13 +45,18 @@ typedef enum ffi_abi { FFI_GCC_SYSV, FFI_LINUX64, FFI_LINUX, + FFI_LINUX_SOFT_FLOAT, # ifdef POWERPC64 FFI_DEFAULT_ABI = FFI_LINUX64, # else -# if __LDBL_MANT_DIG__ == 106 +# if (!defined(__NO_FPRS__) && (__LDBL_MANT_DIG__ == 106)) FFI_DEFAULT_ABI = FFI_LINUX, # else +# ifdef __NO_FPRS__ + FFI_DEFAULT_ABI = FFI_LINUX_SOFT_FLOAT, +# else FFI_DEFAULT_ABI = FFI_GCC_SYSV, +# endif # endif # endif #endif @@ -83,8 +89,14 @@ typedef enum ffi_abi { #define FFI_CLOSURES 1 #define FFI_NATIVE_RAW_API 0 +/* For additional types like the below, take care about the order in + ppc_closures.S. They must follow after the FFI_TYPE_LAST. */ + +/* Needed for soft-float long-double-128 support. */ +#define FFI_TYPE_UINT128 (FFI_TYPE_LAST + 1) + /* Needed for FFI_SYSV small structure returns. */ -#define FFI_SYSV_TYPE_SMALL_STRUCT (FFI_TYPE_LAST) +#define FFI_SYSV_TYPE_SMALL_STRUCT (FFI_TYPE_LAST + 2) #if defined(POWERPC64) || defined(POWERPC_AIX) #define FFI_TRAMPOLINE_SIZE 24 diff --git a/libffi/src/powerpc/ppc_closure.S b/libffi/src/powerpc/ppc_closure.S index 356a0e32620..c9f5742f5aa 100644 --- a/libffi/src/powerpc/ppc_closure.S +++ b/libffi/src/powerpc/ppc_closure.S @@ -28,6 +28,7 @@ ENTRY(ffi_closure_SYSV) stw %r9, 40(%r1) stw %r10,44(%r1) +#ifndef __NO_FPRS__ # next save fpr 1 to fpr 8 (aligned to 8) stfd %f1, 48(%r1) stfd %f2, 56(%r1) @@ -37,6 +38,7 @@ ENTRY(ffi_closure_SYSV) stfd %f6, 88(%r1) stfd %f7, 96(%r1) stfd %f8, 104(%r1) +#endif # set up registers for the routine that actually does the work # get the context pointer from the trampoline @@ -171,6 +173,12 @@ ENTRY(ffi_closure_SYSV) addi %r1,%r1,144 blr +# case FFI_TYPE_UINT128 + lwz %r3,112+0(%r1) + lwz %r4,112+4(%r1) + lwz %r5,112+8(%r1) + bl .Luint128 + # The return types below are only used when the ABI type is FFI_SYSV. # case FFI_SYSV_TYPE_SMALL_STRUCT + 1. One byte struct. lbz %r3,112+0(%r1) @@ -230,6 +238,12 @@ ENTRY(ffi_closure_SYSV) addi %r1,%r1,144 blr +.Luint128: + lwz %r6,112+12(%r1) + mtlr %r0 + addi %r1,%r1,144 + blr + END(ffi_closure_SYSV) .section ".eh_frame",EH_FRAME_FLAGS,@progbits diff --git a/libffi/src/powerpc/sysv.S b/libffi/src/powerpc/sysv.S index 9682016d2fa..21367145eb9 100644 --- a/libffi/src/powerpc/sysv.S +++ b/libffi/src/powerpc/sysv.S @@ -1,5 +1,6 @@ /* ----------------------------------------------------------------------- - sysv.h - Copyright (c) 1998 Geoffrey Keating + sysv.S - Copyright (c) 1998 Geoffrey Keating + Copyright (C) 2007 Free Software Foundation, Inc PowerPC Assembly glue. @@ -98,13 +99,17 @@ ENTRY(ffi_call_SYSV) bctrl /* Now, deal with the return value. */ - mtcrf 0x01,%r31 + 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): |