From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- libffi/src/powerpc/aix.S | 328 ++++++++ libffi/src/powerpc/aix_closure.S | 447 +++++++++++ libffi/src/powerpc/asm.h | 125 +++ libffi/src/powerpc/darwin.S | 383 +++++++++ libffi/src/powerpc/darwin_closure.S | 576 ++++++++++++++ libffi/src/powerpc/ffi.c | 1442 ++++++++++++++++++++++++++++++++++ libffi/src/powerpc/ffi_darwin.c | 1359 ++++++++++++++++++++++++++++++++ libffi/src/powerpc/ffitarget.h | 139 ++++ libffi/src/powerpc/linux64.S | 187 +++++ libffi/src/powerpc/linux64_closure.S | 236 ++++++ libffi/src/powerpc/ppc_closure.S | 327 ++++++++ libffi/src/powerpc/sysv.S | 219 ++++++ 12 files changed, 5768 insertions(+) create mode 100644 libffi/src/powerpc/aix.S create mode 100644 libffi/src/powerpc/aix_closure.S create mode 100644 libffi/src/powerpc/asm.h create mode 100644 libffi/src/powerpc/darwin.S create mode 100644 libffi/src/powerpc/darwin_closure.S create mode 100644 libffi/src/powerpc/ffi.c create mode 100644 libffi/src/powerpc/ffi_darwin.c create mode 100644 libffi/src/powerpc/ffitarget.h create mode 100644 libffi/src/powerpc/linux64.S create mode 100644 libffi/src/powerpc/linux64_closure.S create mode 100644 libffi/src/powerpc/ppc_closure.S create mode 100644 libffi/src/powerpc/sysv.S (limited to 'libffi/src/powerpc') diff --git a/libffi/src/powerpc/aix.S b/libffi/src/powerpc/aix.S new file mode 100644 index 000000000..213f2db39 --- /dev/null +++ b/libffi/src/powerpc/aix.S @@ -0,0 +1,328 @@ +/* ----------------------------------------------------------------------- + aix.S - Copyright (c) 2002, 2009 Free Software Foundation, Inc. + based on darwin.S by John Hornkvist + + 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 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. + ----------------------------------------------------------------------- */ + + .set r0,0 + .set r1,1 + .set r2,2 + .set r3,3 + .set r4,4 + .set r5,5 + .set r6,6 + .set r7,7 + .set r8,8 + .set r9,9 + .set r10,10 + .set r11,11 + .set r12,12 + .set r13,13 + .set r14,14 + .set r15,15 + .set r16,16 + .set r17,17 + .set r18,18 + .set r19,19 + .set r20,20 + .set r21,21 + .set r22,22 + .set r23,23 + .set r24,24 + .set r25,25 + .set r26,26 + .set r27,27 + .set r28,28 + .set r29,29 + .set r30,30 + .set r31,31 + .set f0,0 + .set f1,1 + .set f2,2 + .set f3,3 + .set f4,4 + .set f5,5 + .set f6,6 + .set f7,7 + .set f8,8 + .set f9,9 + .set f10,10 + .set f11,11 + .set f12,12 + .set f13,13 + .set f14,14 + .set f15,15 + .set f16,16 + .set f17,17 + .set f18,18 + .set f19,19 + .set f20,20 + .set f21,21 + + .extern .ffi_prep_args + +#define LIBFFI_ASM +#include +#include +#define JUMPTARGET(name) name +#define L(x) x + .file "aix.S" + .toc + + /* void ffi_call_AIX(extended_cif *ecif, unsigned long bytes, + * unsigned int flags, unsigned int *rvalue, + * void (*fn)(), + * void (*prep_args)(extended_cif*, unsigned *const)); + * r3=ecif, r4=bytes, r5=flags, r6=rvalue, r7=fn, r8=prep_args + */ + +.csect .text[PR] + .align 2 + .globl ffi_call_AIX + .globl .ffi_call_AIX +.csect ffi_call_AIX[DS] +ffi_call_AIX: +#ifdef __64BIT__ + .llong .ffi_call_AIX, TOC[tc0], 0 + .csect .text[PR] +.ffi_call_AIX: + /* Save registers we use. */ + mflr r0 + + std r28,-32(r1) + std r29,-24(r1) + std r30,-16(r1) + std r31, -8(r1) + + std r0, 16(r1) + mr r28, r1 /* our AP. */ + stdux r1, r1, r4 + + /* Save arguments over call... */ + mr r31, r5 /* flags, */ + mr r30, r6 /* rvalue, */ + mr r29, r7 /* function address. */ + std r2, 40(r1) + + /* Call ffi_prep_args. */ + mr r4, r1 + bl .ffi_prep_args + nop + + /* Now do the call. */ + ld r0, 0(r29) + ld r2, 8(r29) + ld r11, 16(r29) + /* Set up cr1 with bits 4-7 of the flags. */ + mtcrf 0x40, r31 + mtctr r0 + /* Load all those argument registers. */ + // We have set up a nice stack frame, just load it into registers. + ld r3, 40+(1*8)(r1) + ld r4, 40+(2*8)(r1) + ld r5, 40+(3*8)(r1) + ld r6, 40+(4*8)(r1) + nop + ld r7, 40+(5*8)(r1) + ld r8, 40+(6*8)(r1) + ld r9, 40+(7*8)(r1) + ld r10,40+(8*8)(r1) + +L1: + /* Load all the FP registers. */ + bf 6,L2 // 2f + 0x18 + lfd f1,-32-(13*8)(r28) + lfd f2,-32-(12*8)(r28) + lfd f3,-32-(11*8)(r28) + lfd f4,-32-(10*8)(r28) + nop + lfd f5,-32-(9*8)(r28) + lfd f6,-32-(8*8)(r28) + lfd f7,-32-(7*8)(r28) + lfd f8,-32-(6*8)(r28) + nop + lfd f9,-32-(5*8)(r28) + lfd f10,-32-(4*8)(r28) + lfd f11,-32-(3*8)(r28) + lfd f12,-32-(2*8)(r28) + nop + lfd f13,-32-(1*8)(r28) + +L2: + /* Make the call. */ + bctrl + ld r2, 40(r1) + + /* Now, deal with the return value. */ + mtcrf 0x01, r31 + + bt 30, L(done_return_value) + bt 29, L(fp_return_value) + std r3, 0(r30) + + /* Fall through... */ + +L(done_return_value): + /* Restore the registers we used and return. */ + mr r1, r28 + ld r0, 16(r28) + ld r28, -32(r1) + mtlr r0 + ld r29, -24(r1) + ld r30, -16(r1) + ld r31, -8(r1) + blr + +L(fp_return_value): + bf 28, L(float_return_value) + stfd f1, 0(r30) + bf 31, 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) + +#else /* ! __64BIT__ */ + + .long .ffi_call_AIX, TOC[tc0], 0 + .csect .text[PR] +.ffi_call_AIX: + /* Save registers we use. */ + mflr r0 + + stw r28,-16(r1) + stw r29,-12(r1) + stw r30, -8(r1) + stw r31, -4(r1) + + stw r0, 8(r1) + mr r28, r1 /* out AP. */ + stwux r1, r1, r4 + + /* Save arguments over call... */ + mr r31, r5 /* flags, */ + mr r30, r6 /* rvalue, */ + mr r29, r7 /* function address, */ + stw r2, 20(r1) + + /* Call ffi_prep_args. */ + mr r4, r1 + bl .ffi_prep_args + nop + + /* Now do the call. */ + lwz r0, 0(r29) + lwz r2, 4(r29) + lwz r11, 8(r29) + /* Set up cr1 with bits 4-7 of the flags. */ + mtcrf 0x40, r31 + mtctr r0 + /* Load all those argument registers. */ + // We have set up a nice stack frame, just load it into registers. + lwz r3, 20+(1*4)(r1) + lwz r4, 20+(2*4)(r1) + lwz r5, 20+(3*4)(r1) + lwz r6, 20+(4*4)(r1) + nop + lwz r7, 20+(5*4)(r1) + lwz r8, 20+(6*4)(r1) + lwz r9, 20+(7*4)(r1) + lwz r10,20+(8*4)(r1) + +L1: + /* Load all the FP registers. */ + bf 6,L2 // 2f + 0x18 + lfd f1,-16-(13*8)(r28) + lfd f2,-16-(12*8)(r28) + lfd f3,-16-(11*8)(r28) + lfd f4,-16-(10*8)(r28) + nop + lfd f5,-16-(9*8)(r28) + lfd f6,-16-(8*8)(r28) + lfd f7,-16-(7*8)(r28) + lfd f8,-16-(6*8)(r28) + nop + lfd f9,-16-(5*8)(r28) + lfd f10,-16-(4*8)(r28) + lfd f11,-16-(3*8)(r28) + lfd f12,-16-(2*8)(r28) + nop + lfd f13,-16-(1*8)(r28) + +L2: + /* Make the call. */ + bctrl + lwz r2, 20(r1) + + /* Now, deal with the return value. */ + mtcrf 0x01, r31 + + 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) + + /* Fall through... */ + +L(done_return_value): + /* Restore the registers we used and return. */ + mr r1, r28 + lwz r0, 8(r28) + lwz r28,-16(r1) + mtlr r0 + lwz r29,-12(r1) + lwz r30, -8(r1) + lwz r31, -4(r1) + blr + +L(fp_return_value): + bf 28, L(float_return_value) + stfd f1, 0(r30) + b L(done_return_value) +L(float_return_value): + stfs f1, 0(r30) + b L(done_return_value) +#endif + .long 0 + .byte 0,0,0,1,128,4,0,0 +//END(ffi_call_AIX) + +.csect .text[PR] + .align 2 + .globl ffi_call_DARWIN + .globl .ffi_call_DARWIN +.csect ffi_call_DARWIN[DS] +ffi_call_DARWIN: +#ifdef __64BIT__ + .llong .ffi_call_DARWIN, TOC[tc0], 0 +#else + .long .ffi_call_DARWIN, TOC[tc0], 0 +#endif + .csect .text[PR] +.ffi_call_DARWIN: + blr + .long 0 + .byte 0,0,0,0,0,0,0,0 +//END(ffi_call_DARWIN) diff --git a/libffi/src/powerpc/aix_closure.S b/libffi/src/powerpc/aix_closure.S new file mode 100644 index 000000000..aabd3c3c1 --- /dev/null +++ b/libffi/src/powerpc/aix_closure.S @@ -0,0 +1,447 @@ +/* ----------------------------------------------------------------------- + aix_closure.S - Copyright (c) 2002, 2003, 2009 Free Software Foundation, Inc. + based on darwin_closure.S + + 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 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. + ----------------------------------------------------------------------- */ + + .set r0,0 + .set r1,1 + .set r2,2 + .set r3,3 + .set r4,4 + .set r5,5 + .set r6,6 + .set r7,7 + .set r8,8 + .set r9,9 + .set r10,10 + .set r11,11 + .set r12,12 + .set r13,13 + .set r14,14 + .set r15,15 + .set r16,16 + .set r17,17 + .set r18,18 + .set r19,19 + .set r20,20 + .set r21,21 + .set r22,22 + .set r23,23 + .set r24,24 + .set r25,25 + .set r26,26 + .set r27,27 + .set r28,28 + .set r29,29 + .set r30,30 + .set r31,31 + .set f0,0 + .set f1,1 + .set f2,2 + .set f3,3 + .set f4,4 + .set f5,5 + .set f6,6 + .set f7,7 + .set f8,8 + .set f9,9 + .set f10,10 + .set f11,11 + .set f12,12 + .set f13,13 + .set f14,14 + .set f15,15 + .set f16,16 + .set f17,17 + .set f18,18 + .set f19,19 + .set f20,20 + .set f21,21 + + .extern .ffi_closure_helper_DARWIN + +#define LIBFFI_ASM +#define JUMPTARGET(name) name +#define L(x) x + .file "aix_closure.S" + .toc +LC..60: + .tc L..60[TC],L..60 + .csect .text[PR] + .align 2 + +.csect .text[PR] + .align 2 + .globl ffi_closure_ASM + .globl .ffi_closure_ASM +.csect ffi_closure_ASM[DS] +ffi_closure_ASM: +#ifdef __64BIT__ + .llong .ffi_closure_ASM, TOC[tc0], 0 + .csect .text[PR] +.ffi_closure_ASM: +/* we want to build up an area for the parameters passed */ +/* in registers (both floating point and integer) */ + + /* we store gpr 3 to gpr 10 (aligned to 4) + in the parents outgoing area */ + std r3, 48+(0*8)(r1) + std r4, 48+(1*8)(r1) + std r5, 48+(2*8)(r1) + std r6, 48+(3*8)(r1) + mflr r0 + + std r7, 48+(4*8)(r1) + std r8, 48+(5*8)(r1) + std r9, 48+(6*8)(r1) + std r10, 48+(7*8)(r1) + std r0, 16(r1) /* save the return address */ + + + /* 48 Bytes (Linkage Area) */ + /* 64 Bytes (params) */ + /* 16 Bytes (result) */ + /* 104 Bytes (13*8 from FPR) */ + /* 8 Bytes (alignment) */ + /* 240 Bytes */ + + stdu r1, -240(r1) /* skip over caller save area + keep stack aligned to 16 */ + + /* next save fpr 1 to fpr 13 (aligned to 8) */ + stfd f1, 128+(0*8)(r1) + stfd f2, 128+(1*8)(r1) + stfd f3, 128+(2*8)(r1) + stfd f4, 128+(3*8)(r1) + stfd f5, 128+(4*8)(r1) + stfd f6, 128+(5*8)(r1) + stfd f7, 128+(6*8)(r1) + stfd f8, 128+(7*8)(r1) + stfd f9, 128+(8*8)(r1) + stfd f10, 128+(9*8)(r1) + stfd f11, 128+(10*8)(r1) + stfd f12, 128+(11*8)(r1) + stfd f13, 128+(12*8)(r1) + + /* set up registers for the routine that actually does the work */ + /* get the context pointer from the trampoline */ + mr r3, r11 + + /* now load up the pointer to the result storage */ + addi r4, r1, 112 + + /* now load up the pointer to the saved gpr registers */ + addi r5, r1, 288 + + /* now load up the pointer to the saved fpr registers */ + addi r6, r1, 128 + + /* make the call */ + bl .ffi_closure_helper_DARWIN + nop + + /* now r3 contains the return type */ + /* so use it to look up in a table */ + /* so we know how to deal with each type */ + + /* look up the proper starting point in table */ + /* by using return type as offset */ + lhz r3, 10(r3) /* load type from return type */ + ld r4, LC..60(2) /* get address of jump table */ + sldi r3, r3, 4 /* now multiply return type by 16 */ + ld r0, 240+16(r1) /* load return address */ + add r3, r3, r4 /* add contents of table to table address */ + mtctr r3 + bctr /* jump to it */ + +/* Each fragment must be exactly 16 bytes long (4 instructions). + Align to 16 byte boundary for cache and dispatch efficiency. */ + .align 4 + +L..60: +/* case FFI_TYPE_VOID */ + mtlr r0 + addi r1, r1, 240 + blr + nop + +/* case FFI_TYPE_INT */ + lwa r3, 112+4(r1) + mtlr r0 + addi r1, r1, 240 + blr + +/* case FFI_TYPE_FLOAT */ + lfs f1, 112+0(r1) + mtlr r0 + addi r1, r1, 240 + blr + +/* case FFI_TYPE_DOUBLE */ + lfd f1, 112+0(r1) + mtlr r0 + addi r1, r1, 240 + blr + +/* case FFI_TYPE_LONGDOUBLE */ + lfd f1, 112+0(r1) + mtlr r0 + lfd f2, 112+8(r1) + b L..finish + +/* case FFI_TYPE_UINT8 */ + lbz r3, 112+7(r1) + mtlr r0 + addi r1, r1, 240 + blr + +/* case FFI_TYPE_SINT8 */ + lbz r3, 112+7(r1) + mtlr r0 + extsb r3, r3 + b L..finish + +/* case FFI_TYPE_UINT16 */ + lhz r3, 112+6(r1) + mtlr r0 +L..finish: + addi r1, r1, 240 + blr + +/* case FFI_TYPE_SINT16 */ + lha r3, 112+6(r1) + mtlr r0 + addi r1, r1, 240 + blr + +/* case FFI_TYPE_UINT32 */ + lwz r3, 112+4(r1) + mtlr r0 + addi r1, r1, 240 + blr + +/* case FFI_TYPE_SINT32 */ + lwa r3, 112+4(r1) + mtlr r0 + addi r1, r1, 240 + blr + +/* case FFI_TYPE_UINT64 */ + ld r3, 112+0(r1) + mtlr r0 + addi r1, r1, 240 + blr + +/* case FFI_TYPE_SINT64 */ + ld r3, 112+0(r1) + mtlr r0 + addi r1, r1, 240 + blr + +/* case FFI_TYPE_STRUCT */ + mtlr r0 + addi r1, r1, 240 + blr + nop + +/* case FFI_TYPE_POINTER */ + ld r3, 112+0(r1) + mtlr r0 + addi r1, r1, 240 + blr + +#else /* ! __64BIT__ */ + + .long .ffi_closure_ASM, TOC[tc0], 0 + .csect .text[PR] +.ffi_closure_ASM: +/* we want to build up an area for the parameters passed */ +/* in registers (both floating point and integer) */ + + /* we store gpr 3 to gpr 10 (aligned to 4) + in the parents outgoing area */ + stw r3, 24+(0*4)(r1) + stw r4, 24+(1*4)(r1) + stw r5, 24+(2*4)(r1) + stw r6, 24+(3*4)(r1) + mflr r0 + + stw r7, 24+(4*4)(r1) + stw r8, 24+(5*4)(r1) + stw r9, 24+(6*4)(r1) + stw r10, 24+(7*4)(r1) + stw r0, 8(r1) + + /* 24 Bytes (Linkage Area) */ + /* 32 Bytes (params) */ + /* 16 Bytes (result) */ + /* 104 Bytes (13*8 from FPR) */ + /* 176 Bytes */ + + stwu r1, -176(r1) /* skip over caller save area + keep stack aligned to 16 */ + + /* next save fpr 1 to fpr 13 (aligned to 8) */ + stfd f1, 72+(0*8)(r1) + stfd f2, 72+(1*8)(r1) + stfd f3, 72+(2*8)(r1) + stfd f4, 72+(3*8)(r1) + stfd f5, 72+(4*8)(r1) + stfd f6, 72+(5*8)(r1) + stfd f7, 72+(6*8)(r1) + stfd f8, 72+(7*8)(r1) + stfd f9, 72+(8*8)(r1) + stfd f10, 72+(9*8)(r1) + stfd f11, 72+(10*8)(r1) + stfd f12, 72+(11*8)(r1) + stfd f13, 72+(12*8)(r1) + + /* set up registers for the routine that actually does the work */ + /* get the context pointer from the trampoline */ + mr r3, r11 + + /* now load up the pointer to the result storage */ + addi r4, r1, 56 + + /* now load up the pointer to the saved gpr registers */ + addi r5, r1, 200 + + /* now load up the pointer to the saved fpr registers */ + addi r6, r1, 72 + + /* make the call */ + bl .ffi_closure_helper_DARWIN + nop + + /* now r3 contains the return type */ + /* so use it to look up in a table */ + /* so we know how to deal with each type */ + + /* look up the proper starting point in table */ + /* by using return type as offset */ + lhz r3, 6(r3) /* load type from return type */ + lwz r4, LC..60(2) /* get address of jump table */ + slwi r3, r3, 4 /* now multiply return type by 16 */ + lwz r0, 176+8(r1) /* load return address */ + add r3, r3, r4 /* add contents of table to table address */ + mtctr r3 + bctr /* jump to it */ + +/* Each fragment must be exactly 16 bytes long (4 instructions). + Align to 16 byte boundary for cache and dispatch efficiency. */ + .align 4 + +L..60: +/* case FFI_TYPE_VOID */ + mtlr r0 + addi r1, r1, 176 + blr + nop + +/* case FFI_TYPE_INT */ + lwz r3, 56+0(r1) + mtlr r0 + addi r1, r1, 176 + blr + +/* case FFI_TYPE_FLOAT */ + lfs f1, 56+0(r1) + mtlr r0 + addi r1, r1, 176 + blr + +/* case FFI_TYPE_DOUBLE */ + lfd f1, 56+0(r1) + mtlr r0 + addi r1, r1, 176 + blr + +/* case FFI_TYPE_LONGDOUBLE */ + lfd f1, 56+0(r1) + mtlr r0 + lfd f2, 56+8(r1) + b L..finish + +/* case FFI_TYPE_UINT8 */ + lbz r3, 56+3(r1) + mtlr r0 + addi r1, r1, 176 + blr + +/* case FFI_TYPE_SINT8 */ + lbz r3, 56+3(r1) + mtlr r0 + extsb r3, r3 + b L..finish + +/* case FFI_TYPE_UINT16 */ + lhz r3, 56+2(r1) + mtlr r0 + addi r1, r1, 176 + blr + +/* case FFI_TYPE_SINT16 */ + lha r3, 56+2(r1) + mtlr r0 + addi r1, r1, 176 + blr + +/* case FFI_TYPE_UINT32 */ + lwz r3, 56+0(r1) + mtlr r0 + addi r1, r1, 176 + blr + +/* case FFI_TYPE_SINT32 */ + lwz r3, 56+0(r1) + mtlr r0 + addi r1, r1, 176 + blr + +/* case FFI_TYPE_UINT64 */ + lwz r3, 56+0(r1) + mtlr r0 + lwz r4, 56+4(r1) + b L..finish + +/* case FFI_TYPE_SINT64 */ + lwz r3, 56+0(r1) + mtlr r0 + lwz r4, 56+4(r1) + b L..finish + +/* case FFI_TYPE_STRUCT */ + mtlr r0 + addi r1, r1, 176 + blr + nop + +/* case FFI_TYPE_POINTER */ + lwz r3, 56+0(r1) + mtlr r0 +L..finish: + addi r1, r1, 176 + blr +#endif +/* END(ffi_closure_ASM) */ diff --git a/libffi/src/powerpc/asm.h b/libffi/src/powerpc/asm.h new file mode 100644 index 000000000..e86e6b091 --- /dev/null +++ b/libffi/src/powerpc/asm.h @@ -0,0 +1,125 @@ +/* ----------------------------------------------------------------------- + asm.h - Copyright (c) 1998 Geoffrey Keating + + 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 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. + ----------------------------------------------------------------------- */ + +#define ASM_GLOBAL_DIRECTIVE .globl + + +#define C_SYMBOL_NAME(name) name +/* Macro for a label. */ +#ifdef __STDC__ +#define C_LABEL(name) name##: +#else +#define C_LABEL(name) name/**/: +#endif + +/* This seems to always be the case on PPC. */ +#define ALIGNARG(log2) log2 +/* For ELF we need the `.type' directive to make shared libs work right. */ +#define ASM_TYPE_DIRECTIVE(name,typearg) .type name,typearg; +#define ASM_SIZE_DIRECTIVE(name) .size name,.-name + +/* If compiled for profiling, call `_mcount' at the start of each function. */ +#ifdef PROF +/* The mcount code relies on a the return address being on the stack + to locate our caller and so it can restore it; so store one just + for its benefit. */ +#ifdef PIC +#define CALL_MCOUNT \ + .pushsection; \ + .section ".data"; \ + .align ALIGNARG(2); \ +0:.long 0; \ + .previous; \ + mflr %r0; \ + stw %r0,4(%r1); \ + bl _GLOBAL_OFFSET_TABLE_@local-4; \ + mflr %r11; \ + lwz %r0,0b@got(%r11); \ + bl JUMPTARGET(_mcount); +#else /* PIC */ +#define CALL_MCOUNT \ + .section ".data"; \ + .align ALIGNARG(2); \ +0:.long 0; \ + .previous; \ + mflr %r0; \ + lis %r11,0b@ha; \ + stw %r0,4(%r1); \ + addi %r0,%r11,0b@l; \ + bl JUMPTARGET(_mcount); +#endif /* PIC */ +#else /* PROF */ +#define CALL_MCOUNT /* Do nothing. */ +#endif /* PROF */ + +#define ENTRY(name) \ + ASM_GLOBAL_DIRECTIVE C_SYMBOL_NAME(name); \ + ASM_TYPE_DIRECTIVE (C_SYMBOL_NAME(name),@function) \ + .align ALIGNARG(2); \ + C_LABEL(name) \ + CALL_MCOUNT + +#define EALIGN_W_0 /* No words to insert. */ +#define EALIGN_W_1 nop +#define EALIGN_W_2 nop;nop +#define EALIGN_W_3 nop;nop;nop +#define EALIGN_W_4 EALIGN_W_3;nop +#define EALIGN_W_5 EALIGN_W_4;nop +#define EALIGN_W_6 EALIGN_W_5;nop +#define EALIGN_W_7 EALIGN_W_6;nop + +/* EALIGN is like ENTRY, but does alignment to 'words'*4 bytes + past a 2^align boundary. */ +#ifdef PROF +#define EALIGN(name, alignt, words) \ + ASM_GLOBAL_DIRECTIVE C_SYMBOL_NAME(name); \ + ASM_TYPE_DIRECTIVE (C_SYMBOL_NAME(name),@function) \ + .align ALIGNARG(2); \ + C_LABEL(name) \ + CALL_MCOUNT \ + b 0f; \ + .align ALIGNARG(alignt); \ + EALIGN_W_##words; \ + 0: +#else /* PROF */ +#define EALIGN(name, alignt, words) \ + ASM_GLOBAL_DIRECTIVE C_SYMBOL_NAME(name); \ + ASM_TYPE_DIRECTIVE (C_SYMBOL_NAME(name),@function) \ + .align ALIGNARG(alignt); \ + EALIGN_W_##words; \ + C_LABEL(name) +#endif + +#define END(name) \ + ASM_SIZE_DIRECTIVE(name) + +#ifdef PIC +#define JUMPTARGET(name) name##@plt +#else +#define JUMPTARGET(name) name +#endif + +/* Local labels stripped out by the linker. */ +#define L(x) .L##x diff --git a/libffi/src/powerpc/darwin.S b/libffi/src/powerpc/darwin.S new file mode 100644 index 000000000..3b425da78 --- /dev/null +++ b/libffi/src/powerpc/darwin.S @@ -0,0 +1,383 @@ +/* ----------------------------------------------------------------------- + darwin.S - Copyright (c) 2000 John Hornkvist + Copyright (c) 2004, 2010 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 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. + ----------------------------------------------------------------------- */ + +#define LIBFFI_ASM +#if defined(__ppc64__) +#define MODE_CHOICE(x, y) y +#else +#define MODE_CHOICE(x, y) x +#endif + +#define machine_choice MODE_CHOICE(ppc7400,ppc64) + +; Define some pseudo-opcodes for size-independent load & store of GPRs ... +#define lgu MODE_CHOICE(lwzu, ldu) +#define lg MODE_CHOICE(lwz,ld) +#define sg MODE_CHOICE(stw,std) +#define sgu MODE_CHOICE(stwu,stdu) +#define sgux MODE_CHOICE(stwux,stdux) + +; ... and the size of GPRs and their storage indicator. +#define GPR_BYTES MODE_CHOICE(4,8) +#define LOG2_GPR_BYTES MODE_CHOICE(2,3) /* log2(GPR_BYTES) */ +#define g_long MODE_CHOICE(long, quad) /* usage is ".g_long" */ + +; From the ABI doc: "Mac OS X ABI Function Call Guide" Version 2009-02-04. +#define LINKAGE_SIZE MODE_CHOICE(24,48) +#define PARAM_AREA MODE_CHOICE(32,64) +#define SAVED_LR_OFFSET MODE_CHOICE(8,16) /* save position for lr */ + +/* If there is any FP stuff we make space for all of the regs. */ +#define SAVED_FPR_COUNT 13 +#define FPR_SIZE 8 +#define RESULT_BYTES 16 + +/* This should be kept in step with the same value in ffi_darwin.c. */ +#define ASM_NEEDS_REGISTERS 4 +#define SAVE_REGS_SIZE (ASM_NEEDS_REGISTERS * GPR_BYTES) + +#include +#include + +#define JUMPTARGET(name) name +#define L(x) x + + .text + .align 2 + .globl _ffi_prep_args + + .align 2 + .globl _ffi_call_DARWIN + + /* We arrive here with: + r3 = ptr to extended cif. + r4 = -bytes. + r5 = cif flags. + r6 = ptr to return value. + r7 = fn pointer (user func). + r8 = fn pointer (ffi_prep_args). + r9 = ffi_type* for the ret val. */ + +_ffi_call_DARWIN: +Lstartcode: + mr r12,r8 /* We only need r12 until the call, + so it does not have to be saved. */ +LFB1: + /* Save the old stack pointer as AP. */ + mr r8,r1 +LCFI0: + + /* Save the retval type in parents frame. */ + sg r9,(LINKAGE_SIZE+6*GPR_BYTES)(r8) + + /* Allocate the stack space we need. */ + sgux r1,r1,r4 + + /* Save registers we use. */ + mflr r9 + sg r9,SAVED_LR_OFFSET(r8) + + sg r28,-(4 * GPR_BYTES)(r8) + sg r29,-(3 * GPR_BYTES)(r8) + sg r30,-(2 * GPR_BYTES)(r8) + sg r31,-( GPR_BYTES)(r8) + +#if !defined(POWERPC_DARWIN) + /* The TOC slot is reserved in the Darwin ABI and r2 is volatile. */ + sg r2,(5 * GPR_BYTES)(r1) +#endif + +LCFI1: + + /* Save arguments over call. */ + mr r31,r5 /* flags, */ + mr r30,r6 /* rvalue, */ + mr r29,r7 /* function address, */ + mr r28,r8 /* our AP. */ +LCFI2: + /* Call ffi_prep_args. r3 = extended cif, r4 = stack ptr copy. */ + mr r4,r1 + li r9,0 + + mtctr r12 /* r12 holds address of _ffi_prep_args. */ + bctrl + +#if !defined(POWERPC_DARWIN) + /* The TOC slot is reserved in the Darwin ABI and r2 is volatile. */ + lg r2,(5 * GPR_BYTES)(r1) +#endif + /* 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. + We have set up a nice stack frame, just load it into registers. */ + lg r3, (LINKAGE_SIZE )(r1) + lg r4, (LINKAGE_SIZE + GPR_BYTES)(r1) + lg r5, (LINKAGE_SIZE + 2 * GPR_BYTES)(r1) + lg r6, (LINKAGE_SIZE + 3 * GPR_BYTES)(r1) + nop + lg r7, (LINKAGE_SIZE + 4 * GPR_BYTES)(r1) + lg r8, (LINKAGE_SIZE + 5 * GPR_BYTES)(r1) + lg r9, (LINKAGE_SIZE + 6 * GPR_BYTES)(r1) + lg r10,(LINKAGE_SIZE + 7 * GPR_BYTES)(r1) + +L1: + /* ... Load all the FP registers. */ + bf 6,L2 /* No floats to load. */ + lfd f1, -SAVE_REGS_SIZE-(13*FPR_SIZE)(r28) + lfd f2, -SAVE_REGS_SIZE-(12*FPR_SIZE)(r28) + lfd f3, -SAVE_REGS_SIZE-(11*FPR_SIZE)(r28) + lfd f4, -SAVE_REGS_SIZE-(10*FPR_SIZE)(r28) + nop + lfd f5, -SAVE_REGS_SIZE-( 9*FPR_SIZE)(r28) + lfd f6, -SAVE_REGS_SIZE-( 8*FPR_SIZE)(r28) + lfd f7, -SAVE_REGS_SIZE-( 7*FPR_SIZE)(r28) + lfd f8, -SAVE_REGS_SIZE-( 6*FPR_SIZE)(r28) + nop + lfd f9, -SAVE_REGS_SIZE-( 5*FPR_SIZE)(r28) + lfd f10,-SAVE_REGS_SIZE-( 4*FPR_SIZE)(r28) + lfd f11,-SAVE_REGS_SIZE-( 3*FPR_SIZE)(r28) + lfd f12,-SAVE_REGS_SIZE-( 2*FPR_SIZE)(r28) + nop + lfd f13,-SAVE_REGS_SIZE-( 1*FPR_SIZE)(r28) + +L2: + mr r12,r29 /* Put the target address in r12 as specified. */ + mtctr r12 + nop + nop + + /* Make the call. */ + bctrl + + /* Now, deal with the return value. */ + + /* m64 structure returns can occupy the same set of registers as + would be used to pass such a structure as arg0 - so take care + not to step on any possibly hot regs. */ + + /* Get the flags.. */ + mtcrf 0x03,r31 ; we need c6 & cr7 now. + ; FLAG_RETURNS_NOTHING also covers struct ret-by-ref. + bt 30,L(done_return_value) ; FLAG_RETURNS_NOTHING + bf 27,L(scalar_return_value) ; not FLAG_RETURNS_STRUCT + + /* OK, so we have a struct. */ +#if defined(__ppc64__) + bt 31,L(maybe_return_128) ; FLAG_RETURNS_128BITS, special case + + /* OK, we have to map the return back to a mem struct. + We are about to trample the parents param area, so recover the + return type. r29 is free, since the call is done. */ + lg r29,(LINKAGE_SIZE + 6 * GPR_BYTES)(r28) + + sg r3, (LINKAGE_SIZE )(r28) + sg r4, (LINKAGE_SIZE + GPR_BYTES)(r28) + sg r5, (LINKAGE_SIZE + 2 * GPR_BYTES)(r28) + sg r6, (LINKAGE_SIZE + 3 * GPR_BYTES)(r28) + nop + sg r7, (LINKAGE_SIZE + 4 * GPR_BYTES)(r28) + sg r8, (LINKAGE_SIZE + 5 * GPR_BYTES)(r28) + sg r9, (LINKAGE_SIZE + 6 * GPR_BYTES)(r28) + sg r10,(LINKAGE_SIZE + 7 * GPR_BYTES)(r28) + /* OK, so do the block move - we trust that memcpy will not trample + the fprs... */ + mr r3,r30 ; dest + addi r4,r28,LINKAGE_SIZE ; source + /* The size is a size_t, should be long. */ + lg r5,0(r29) + /* Figure out small structs */ + cmpi 0,r5,4 + bgt L3 ; 1, 2 and 4 bytes have special rules. + cmpi 0,r5,3 + beq L3 ; not 3 + addi r4,r4,8 + subf r4,r5,r4 +L3: + bl _memcpy + + /* ... do we need the FP registers? - recover the flags.. */ + mtcrf 0x03,r31 ; we need c6 & cr7 now. + bf 29,L(done_return_value) /* No floats in the struct. */ + stfd f1, -SAVE_REGS_SIZE-(13*FPR_SIZE)(r28) + stfd f2, -SAVE_REGS_SIZE-(12*FPR_SIZE)(r28) + stfd f3, -SAVE_REGS_SIZE-(11*FPR_SIZE)(r28) + stfd f4, -SAVE_REGS_SIZE-(10*FPR_SIZE)(r28) + nop + stfd f5, -SAVE_REGS_SIZE-( 9*FPR_SIZE)(r28) + stfd f6, -SAVE_REGS_SIZE-( 8*FPR_SIZE)(r28) + stfd f7, -SAVE_REGS_SIZE-( 7*FPR_SIZE)(r28) + stfd f8, -SAVE_REGS_SIZE-( 6*FPR_SIZE)(r28) + nop + stfd f9, -SAVE_REGS_SIZE-( 5*FPR_SIZE)(r28) + stfd f10,-SAVE_REGS_SIZE-( 4*FPR_SIZE)(r28) + stfd f11,-SAVE_REGS_SIZE-( 3*FPR_SIZE)(r28) + stfd f12,-SAVE_REGS_SIZE-( 2*FPR_SIZE)(r28) + nop + stfd f13,-SAVE_REGS_SIZE-( 1*FPR_SIZE)(r28) + + mr r3,r29 ; ffi_type * + mr r4,r30 ; dest + addi r5,r28,-SAVE_REGS_SIZE-(13*FPR_SIZE) ; fprs + xor r6,r6,r6 + sg r6,(LINKAGE_SIZE + 7 * GPR_BYTES)(r28) + addi r6,r28,(LINKAGE_SIZE + 7 * GPR_BYTES) ; point to a zeroed counter. + bl _darwin64_struct_floats_to_mem + + b L(done_return_value) +#else + stw r3,0(r30) ; m32 the only struct return in reg is 4 bytes. +#endif + b L(done_return_value) + +L(fp_return_value): + /* Do we have long double to store? */ + bf 31,L(fd_return_value) ; FLAG_RETURNS_128BITS + stfd f1,0(r30) + stfd f2,FPR_SIZE(r30) + b L(done_return_value) + +L(fd_return_value): + /* Do we have double to store? */ + bf 28,L(float_return_value) + stfd f1,0(r30) + b L(done_return_value) + +L(float_return_value): + /* We only have a float to store. */ + stfs f1,0(r30) + b L(done_return_value) + +L(scalar_return_value): + bt 29,L(fp_return_value) ; FLAG_RETURNS_FP + ; ffi_arg is defined as unsigned long. + sg r3,0(r30) ; Save the reg. + bf 28,L(done_return_value) ; not FLAG_RETURNS_64BITS + +#if defined(__ppc64__) +L(maybe_return_128): + std r3,0(r30) + bf 31,L(done_return_value) ; not FLAG_RETURNS_128BITS + std r4,8(r30) +#else + stw r4,4(r30) +#endif + + /* Fall through. */ + /* We want this at the end to simplify eh epilog computation. */ + +L(done_return_value): + /* Restore the registers we used and return. */ + lg r29,SAVED_LR_OFFSET(r28) + ; epilog + lg r31,-(1 * GPR_BYTES)(r28) + mtlr r29 + lg r30,-(2 * GPR_BYTES)(r28) + lg r29,-(3 * GPR_BYTES)(r28) + lg r28,-(4 * GPR_BYTES)(r28) + lg r1,0(r1) + blr +LFE1: + .align 1 +/* END(_ffi_call_DARWIN) */ + +/* Provide a null definition of _ffi_call_AIX. */ + .text + .globl _ffi_call_AIX + .align 2 +_ffi_call_AIX: + blr +/* END(_ffi_call_AIX) */ + +/* EH stuff. */ + +#define EH_DATA_ALIGN_FACT MODE_CHOICE(0x7c,0x78) + + .static_data + .align LOG2_GPR_BYTES +LLFB0$non_lazy_ptr: + .g_long Lstartcode + + .section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support +EH_frame1: + .set L$set$0,LECIE1-LSCIE1 + .long L$set$0 ; Length of Common Information Entry +LSCIE1: + .long 0x0 ; CIE Identifier Tag + .byte 0x1 ; CIE Version + .ascii "zR\0" ; CIE Augmentation + .byte 0x1 ; uleb128 0x1; CIE Code Alignment Factor + .byte EH_DATA_ALIGN_FACT ; sleb128 -4; CIE Data Alignment Factor + .byte 0x41 ; CIE RA Column + .byte 0x1 ; uleb128 0x1; Augmentation size + .byte 0x90 ; FDE Encoding (indirect pcrel) + .byte 0xc ; DW_CFA_def_cfa + .byte 0x1 ; uleb128 0x1 + .byte 0x0 ; uleb128 0x0 + .align LOG2_GPR_BYTES +LECIE1: + + .globl _ffi_call_DARWIN.eh +_ffi_call_DARWIN.eh: +LSFDE1: + .set L$set$1,LEFDE1-LASFDE1 + .long L$set$1 ; FDE Length +LASFDE1: + .long LASFDE1-EH_frame1 ; FDE CIE offset + .g_long LLFB0$non_lazy_ptr-. ; FDE initial location + .set L$set$3,LFE1-Lstartcode + .g_long L$set$3 ; FDE address range + .byte 0x0 ; uleb128 0x0; Augmentation size + .byte 0x4 ; DW_CFA_advance_loc4 + .set L$set$4,LCFI0-Lstartcode + .long L$set$4 + .byte 0xd ; DW_CFA_def_cfa_register + .byte 0x08 ; uleb128 0x08 + .byte 0x4 ; DW_CFA_advance_loc4 + .set L$set$5,LCFI1-LCFI0 + .long L$set$5 + .byte 0x11 ; DW_CFA_offset_extended_sf + .byte 0x41 ; uleb128 0x41 + .byte 0x7e ; sleb128 -2 + .byte 0x9f ; DW_CFA_offset, column 0x1f + .byte 0x1 ; uleb128 0x1 + .byte 0x9e ; DW_CFA_offset, column 0x1e + .byte 0x2 ; uleb128 0x2 + .byte 0x9d ; DW_CFA_offset, column 0x1d + .byte 0x3 ; uleb128 0x3 + .byte 0x9c ; DW_CFA_offset, column 0x1c + .byte 0x4 ; uleb128 0x4 + .byte 0x4 ; DW_CFA_advance_loc4 + .set L$set$6,LCFI2-LCFI1 + .long L$set$6 + .byte 0xd ; DW_CFA_def_cfa_register + .byte 0x1c ; uleb128 0x1c + .align LOG2_GPR_BYTES +LEFDE1: + .align 1 + diff --git a/libffi/src/powerpc/darwin_closure.S b/libffi/src/powerpc/darwin_closure.S new file mode 100644 index 000000000..b43f9658f --- /dev/null +++ b/libffi/src/powerpc/darwin_closure.S @@ -0,0 +1,576 @@ +/* ----------------------------------------------------------------------- + darwin_closure.S - Copyright (c) 2002, 2003, 2004, 2010, + Free Software Foundation, Inc. + based on ppc_closure.S + + 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 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. + ----------------------------------------------------------------------- */ + +#define LIBFFI_ASM +#define L(x) x + +#if defined(__ppc64__) +#define MODE_CHOICE(x, y) y +#else +#define MODE_CHOICE(x, y) x +#endif + +#define machine_choice MODE_CHOICE(ppc7400,ppc64) + +; Define some pseudo-opcodes for size-independent load & store of GPRs ... +#define lgu MODE_CHOICE(lwzu, ldu) +#define lg MODE_CHOICE(lwz,ld) +#define sg MODE_CHOICE(stw,std) +#define sgu MODE_CHOICE(stwu,stdu) + +; ... and the size of GPRs and their storage indicator. +#define GPR_BYTES MODE_CHOICE(4,8) +#define LOG2_GPR_BYTES MODE_CHOICE(2,3) /* log2(GPR_BYTES) */ +#define g_long MODE_CHOICE(long, quad) /* usage is ".g_long" */ + +; From the ABI doc: "Mac OS X ABI Function Call Guide" Version 2009-02-04. +#define LINKAGE_SIZE MODE_CHOICE(24,48) +#define PARAM_AREA MODE_CHOICE(32,64) + +#define SAVED_CR_OFFSET MODE_CHOICE(4,8) /* save position for CR */ +#define SAVED_LR_OFFSET MODE_CHOICE(8,16) /* save position for lr */ + +/* WARNING: if ffi_type is changed... here be monsters. + Offsets of items within the result type. */ +#define FFI_TYPE_TYPE MODE_CHOICE(6,10) +#define FFI_TYPE_ELEM MODE_CHOICE(8,16) + +#define SAVED_FPR_COUNT 13 +#define FPR_SIZE 8 +/* biggest m64 struct ret is 8GPRS + 13FPRS = 168 bytes - rounded to 16bytes = 176. */ +#define RESULT_BYTES MODE_CHOICE(16,176) + +; The whole stack frame **MUST** be 16byte-aligned. +#define SAVE_SIZE (((LINKAGE_SIZE+PARAM_AREA+SAVED_FPR_COUNT*FPR_SIZE+RESULT_BYTES)+15) & -16LL) +#define PAD_SIZE (SAVE_SIZE-(LINKAGE_SIZE+PARAM_AREA+SAVED_FPR_COUNT*FPR_SIZE+RESULT_BYTES)) + +#define PARENT_PARM_BASE (SAVE_SIZE+LINKAGE_SIZE) +#define FP_SAVE_BASE (LINKAGE_SIZE+PARAM_AREA) + +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 +; We no longer need the pic symbol stub for Darwin >= 9. +#define BLCLS_HELP _ffi_closure_helper_DARWIN +#define STRUCT_RETVALUE_P _darwin64_struct_ret_by_value_p +#define PASS_STR_FLOATS _darwin64_pass_struct_floats +#undef WANT_STUB +#else +#define BLCLS_HELP L_ffi_closure_helper_DARWIN$stub +#define STRUCT_RETVALUE_P L_darwin64_struct_ret_by_value_p$stub +#define PASS_STR_FLOATS L_darwin64_pass_struct_floats$stub +#define WANT_STUB +#endif + +/* m32/m64 + + The stack layout looks like this: + + | Additional params... | | Higher address + ~ ~ ~ + | Parameters (at least 8*4/8=32/64) | | NUM_GPR_ARG_REGISTERS + |--------------------------------------------| | + | TOC=R2 (AIX) Reserved (Darwin) 4/8 | | + |--------------------------------------------| | + | Reserved 2*4/8 | | + |--------------------------------------------| | + | Space for callee`s LR 4/8 | | + |--------------------------------------------| | + | Saved CR [low word for m64] 4/8 | | + |--------------------------------------------| | + | Current backchain pointer 4/8 |-/ Parent`s frame. + |--------------------------------------------| <+ <<< on entry to + | Result Bytes 16/176 | | + |--------------------------------------------| | + ~ padding to 16-byte alignment ~ ~ + |--------------------------------------------| | + | NUM_FPR_ARG_REGISTERS slots | | + | here fp13 .. fp1 13*8 | | + |--------------------------------------------| | + | R3..R10 8*4/8=32/64 | | NUM_GPR_ARG_REGISTERS + |--------------------------------------------| | + | TOC=R2 (AIX) Reserved (Darwin) 4/8 | | + |--------------------------------------------| | stack | + | Reserved [compiler,binder] 2*4/8 | | grows | + |--------------------------------------------| | down V + | Space for callees LR 4/8 | | + |--------------------------------------------| | lower addresses + | Saved CR [low word for m64] 4/8 | | + |--------------------------------------------| | stack pointer here + | Current backchain pointer 4/8 |-/ during + |--------------------------------------------| <<< call. + +*/ + + .file "darwin_closure.S" + + .machine machine_choice + + .text + .globl _ffi_closure_ASM + .align LOG2_GPR_BYTES +_ffi_closure_ASM: +LFB1: +Lstartcode: + mflr r0 /* extract return address */ + sg r0,SAVED_LR_OFFSET(r1) /* save the return address */ +LCFI0: + sgu r1,-SAVE_SIZE(r1) /* skip over caller save area + keep stack aligned to 16. */ +LCFI1: + /* We want to build up an area for the parameters passed + in registers. (both floating point and integer) */ + + /* Put gpr 3 to gpr 10 in the parents outgoing area... + ... the remainder of any params that overflowed the regs will + follow here. */ + sg r3, (PARENT_PARM_BASE )(r1) + sg r4, (PARENT_PARM_BASE + GPR_BYTES )(r1) + sg r5, (PARENT_PARM_BASE + GPR_BYTES * 2)(r1) + sg r6, (PARENT_PARM_BASE + GPR_BYTES * 3)(r1) + sg r7, (PARENT_PARM_BASE + GPR_BYTES * 4)(r1) + sg r8, (PARENT_PARM_BASE + GPR_BYTES * 5)(r1) + sg r9, (PARENT_PARM_BASE + GPR_BYTES * 6)(r1) + sg r10,(PARENT_PARM_BASE + GPR_BYTES * 7)(r1) + + /* We save fpr 1 to fpr 14 in our own save frame. */ + stfd f1, (FP_SAVE_BASE )(r1) + stfd f2, (FP_SAVE_BASE + FPR_SIZE )(r1) + stfd f3, (FP_SAVE_BASE + FPR_SIZE * 2 )(r1) + stfd f4, (FP_SAVE_BASE + FPR_SIZE * 3 )(r1) + stfd f5, (FP_SAVE_BASE + FPR_SIZE * 4 )(r1) + stfd f6, (FP_SAVE_BASE + FPR_SIZE * 5 )(r1) + stfd f7, (FP_SAVE_BASE + FPR_SIZE * 6 )(r1) + stfd f8, (FP_SAVE_BASE + FPR_SIZE * 7 )(r1) + stfd f9, (FP_SAVE_BASE + FPR_SIZE * 8 )(r1) + stfd f10,(FP_SAVE_BASE + FPR_SIZE * 9 )(r1) + stfd f11,(FP_SAVE_BASE + FPR_SIZE * 10)(r1) + stfd f12,(FP_SAVE_BASE + FPR_SIZE * 11)(r1) + stfd f13,(FP_SAVE_BASE + FPR_SIZE * 12)(r1) + + /* Set up registers for the routine that actually does the work + get the context pointer from the trampoline. */ + mr r3,r11 + + /* Now load up the pointer to the result storage. */ + addi r4,r1,(SAVE_SIZE-RESULT_BYTES) + + /* Now load up the pointer to the saved gpr registers. */ + addi r5,r1,PARENT_PARM_BASE + + /* Now load up the pointer to the saved fpr registers. */ + addi r6,r1,FP_SAVE_BASE + + /* Make the call. */ + bl BLCLS_HELP + + /* r3 contains the rtype pointer... save it since we will need + it later. */ + sg r3,LINKAGE_SIZE(r1) ; ffi_type * result_type + lg r0,0(r3) ; size => r0 + lhz r3,FFI_TYPE_TYPE(r3) ; type => r3 + + /* The helper will have intercepted struture returns and inserted + the caller`s destination address for structs returned by ref. */ + + /* r3 contains the return type so use it to look up in a table + so we know how to deal with each type. */ + + addi r5,r1,(SAVE_SIZE-RESULT_BYTES) /* Otherwise, our return is here. */ + bl Lget_ret_type0_addr /* Get pointer to Lret_type0 into LR. */ + mflr r4 /* Move to r4. */ + slwi r3,r3,4 /* Now multiply return type by 16. */ + add r3,r3,r4 /* Add contents of table to table address. */ + mtctr r3 + bctr /* Jump to it. */ +LFE1: +/* Each of the ret_typeX code fragments has to be exactly 16 bytes long + (4 instructions). For cache effectiveness we align to a 16 byte boundary + first. */ + + .align 4 + + nop + nop + nop +Lget_ret_type0_addr: + blrl + +/* case FFI_TYPE_VOID */ +Lret_type0: + b Lfinish + nop + nop + nop + +/* case FFI_TYPE_INT */ +Lret_type1: + lg r3,0(r5) + b Lfinish + nop + nop + +/* case FFI_TYPE_FLOAT */ +Lret_type2: + lfs f1,0(r5) + b Lfinish + nop + nop + +/* case FFI_TYPE_DOUBLE */ +Lret_type3: + lfd f1,0(r5) + b Lfinish + nop + nop + +/* case FFI_TYPE_LONGDOUBLE */ +Lret_type4: + lfd f1,0(r5) + lfd f2,8(r5) + b Lfinish + nop + +/* case FFI_TYPE_UINT8 */ +Lret_type5: +#if defined(__ppc64__) + lbz r3,7(r5) +#else + lbz r3,3(r5) +#endif + b Lfinish + nop + nop + +/* case FFI_TYPE_SINT8 */ +Lret_type6: +#if defined(__ppc64__) + lbz r3,7(r5) +#else + lbz r3,3(r5) +#endif + extsb r3,r3 + b Lfinish + nop + +/* case FFI_TYPE_UINT16 */ +Lret_type7: +#if defined(__ppc64__) + lhz r3,6(r5) +#else + lhz r3,2(r5) +#endif + b Lfinish + nop + nop + +/* case FFI_TYPE_SINT16 */ +Lret_type8: +#if defined(__ppc64__) + lha r3,6(r5) +#else + lha r3,2(r5) +#endif + b Lfinish + nop + nop + +/* case FFI_TYPE_UINT32 */ +Lret_type9: +#if defined(__ppc64__) + lwz r3,4(r5) +#else + lwz r3,0(r5) +#endif + b Lfinish + nop + nop + +/* case FFI_TYPE_SINT32 */ +Lret_type10: +#if defined(__ppc64__) + lwz r3,4(r5) +#else + lwz r3,0(r5) +#endif + b Lfinish + nop + nop + +/* case FFI_TYPE_UINT64 */ +Lret_type11: +#if defined(__ppc64__) + lg r3,0(r5) + b Lfinish + nop +#else + lwz r3,0(r5) + lwz r4,4(r5) + b Lfinish +#endif + nop + +/* case FFI_TYPE_SINT64 */ +Lret_type12: +#if defined(__ppc64__) + lg r3,0(r5) + b Lfinish + nop +#else + lwz r3,0(r5) + lwz r4,4(r5) + b Lfinish +#endif + nop + +/* case FFI_TYPE_STRUCT */ +Lret_type13: +#if defined(__ppc64__) + lg r3,0(r5) ; we need at least this... + cmpi 0,r0,4 + bgt Lstructend ; not a special small case + b Lsmallstruct ; see if we need more. +#else + cmpi 0,r0,4 + bgt Lfinish ; not by value + lg r3,0(r5) + b Lfinish +#endif +/* case FFI_TYPE_POINTER */ +Lret_type14: + lg r3,0(r5) + b Lfinish + nop + nop + +#if defined(__ppc64__) +Lsmallstruct: + beq Lfour ; continuation of Lret13. + cmpi 0,r0,3 + beq Lfinish ; don`t adjust this - can`t be any floats here... + srdi r3,r3,48 + cmpi 0,r0,2 + beq Lfinish ; .. or here .. + srdi r3,r3,8 + b Lfinish ; .. or here. + +Lfour: + lg r6,LINKAGE_SIZE(r1) ; get the result type + lg r6,FFI_TYPE_ELEM(r6) ; elements array pointer + lg r6,0(r6) ; first element + lhz r0,FFI_TYPE_TYPE(r6) ; OK go the type + cmpi 0,r0,2 ; FFI_TYPE_FLOAT + bne Lfourint + lfs f1,0(r5) ; just one float in the struct. + b Lfinish + +Lfourint: + srdi r3,r3,32 ; four bytes. + b Lfinish + +Lstructend: + lg r3,LINKAGE_SIZE(r1) ; get the result type + bl STRUCT_RETVALUE_P + cmpi 0,r3,0 + beq Lfinish ; nope. + /* Recover a pointer to the results. */ + addi r11,r1,(SAVE_SIZE-RESULT_BYTES) + lg r3,0(r11) ; we need at least this... + lg r4,8(r11) + cmpi 0,r0,16 + beq Lfinish ; special case 16 bytes we don't consider floats. + + /* OK, frustratingly, the process of saving the struct to mem might have + messed with the FPRs, so we have to re-load them :(. + We`ll use our FPRs space again - calling: + void darwin64_pass_struct_floats (ffi_type *s, char *src, + unsigned *nfpr, double **fprs) + We`ll temporarily pinch the first two slots of the param area for local + vars used by the routine. */ + xor r6,r6,r6 + addi r5,r1,PARENT_PARM_BASE ; some space + sg r6,0(r5) ; *nfpr zeroed. + addi r6,r5,8 ; **fprs + addi r3,r1,FP_SAVE_BASE ; pointer to FPRs space + sg r3,0(r6) + mr r4,r11 ; the struct is here... + lg r3,LINKAGE_SIZE(r1) ; ffi_type * result_type. + bl PASS_STR_FLOATS ; get struct floats into FPR save space. + /* See if we used any floats */ + lwz r0,(SAVE_SIZE-RESULT_BYTES)(r1) + cmpi 0,r0,0 + beq Lstructints ; nope. + /* OK load `em up... */ + lfd f1, (FP_SAVE_BASE )(r1) + lfd f2, (FP_SAVE_BASE + FPR_SIZE )(r1) + lfd f3, (FP_SAVE_BASE + FPR_SIZE * 2 )(r1) + lfd f4, (FP_SAVE_BASE + FPR_SIZE * 3 )(r1) + lfd f5, (FP_SAVE_BASE + FPR_SIZE * 4 )(r1) + lfd f6, (FP_SAVE_BASE + FPR_SIZE * 5 )(r1) + lfd f7, (FP_SAVE_BASE + FPR_SIZE * 6 )(r1) + lfd f8, (FP_SAVE_BASE + FPR_SIZE * 7 )(r1) + lfd f9, (FP_SAVE_BASE + FPR_SIZE * 8 )(r1) + lfd f10,(FP_SAVE_BASE + FPR_SIZE * 9 )(r1) + lfd f11,(FP_SAVE_BASE + FPR_SIZE * 10)(r1) + lfd f12,(FP_SAVE_BASE + FPR_SIZE * 11)(r1) + lfd f13,(FP_SAVE_BASE + FPR_SIZE * 12)(r1) + + /* point back at our saved struct. */ +Lstructints: + addi r11,r1,(SAVE_SIZE-RESULT_BYTES) + lg r3,0(r11) ; we end up picking the + lg r4,8(r11) ; first two again. + lg r5,16(r11) + lg r6,24(r11) + lg r7,32(r11) + lg r8,40(r11) + lg r9,48(r11) + lg r10,56(r11) +#endif + +/* case done */ +Lfinish: + addi r1,r1,SAVE_SIZE /* Restore stack pointer. */ + lg r0,SAVED_LR_OFFSET(r1) /* Get return address. */ + mtlr r0 /* Reset link register. */ + blr +Lendcode: + .align 1 + +/* END(ffi_closure_ASM) */ + +/* EH frame stuff. */ +#define EH_DATA_ALIGN_FACT MODE_CHOICE(0x7c,0x78) +/* 176, 400 */ +#define EH_FRAME_OFFSETA MODE_CHOICE(176,0x90) +#define EH_FRAME_OFFSETB MODE_CHOICE(1,3) + + .static_data + .align LOG2_GPR_BYTES +LLFB1$non_lazy_ptr: + .g_long Lstartcode + + .section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support +EH_frame1: + .set L$set$0,LECIE1-LSCIE1 + .long L$set$0 ; Length of Common Information Entry +LSCIE1: + .long 0x0 ; CIE Identifier Tag + .byte 0x1 ; CIE Version + .ascii "zR\0" ; CIE Augmentation + .byte 0x1 ; uleb128 0x1; CIE Code Alignment Factor + .byte EH_DATA_ALIGN_FACT ; sleb128 -4; CIE Data Alignment Factor + .byte 0x41 ; CIE RA Column + .byte 0x1 ; uleb128 0x1; Augmentation size + .byte 0x90 ; FDE Encoding (indirect pcrel) + .byte 0xc ; DW_CFA_def_cfa + .byte 0x1 ; uleb128 0x1 + .byte 0x0 ; uleb128 0x0 + .align LOG2_GPR_BYTES +LECIE1: + .globl _ffi_closure_ASM.eh +_ffi_closure_ASM.eh: +LSFDE1: + .set L$set$1,LEFDE1-LASFDE1 + .long L$set$1 ; FDE Length + +LASFDE1: + .long LASFDE1-EH_frame1 ; FDE CIE offset + .g_long LLFB1$non_lazy_ptr-. ; FDE initial location + .set L$set$3,LFE1-Lstartcode + .g_long L$set$3 ; FDE address range + .byte 0x0 ; uleb128 0x0; Augmentation size + .byte 0x4 ; DW_CFA_advance_loc4 + .set L$set$3,LCFI1-LCFI0 + .long L$set$3 + .byte 0xe ; DW_CFA_def_cfa_offset + .byte EH_FRAME_OFFSETA,EH_FRAME_OFFSETB ; uleb128 176,1/190,3 + .byte 0x4 ; DW_CFA_advance_loc4 + .set L$set$4,LCFI0-Lstartcode + .long L$set$4 + .byte 0x11 ; DW_CFA_offset_extended_sf + .byte 0x41 ; uleb128 0x41 + .byte 0x7e ; sleb128 -2 + .align LOG2_GPR_BYTES +LEFDE1: + .align 1 + +#ifdef WANT_STUB + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_ffi_closure_helper_DARWIN$stub: + .indirect_symbol _ffi_closure_helper_DARWIN + mflr r0 + bcl 20,31,"L00000000001$spb" +"L00000000001$spb": + mflr r11 + addis r11,r11,ha16(L_ffi_closure_helper_DARWIN$lazy_ptr-"L00000000001$spb") + mtlr r0 + lwzu r12,lo16(L_ffi_closure_helper_DARWIN$lazy_ptr-"L00000000001$spb")(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_ffi_closure_helper_DARWIN$lazy_ptr: + .indirect_symbol _ffi_closure_helper_DARWIN + .g_long dyld_stub_binding_helper + +#if defined(__ppc64__) + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_darwin64_struct_ret_by_value_p$stub: + .indirect_symbol _darwin64_struct_ret_by_value_p + mflr r0 + bcl 20,31,"L00000000002$spb" +"L00000000002$spb": + mflr r11 + addis r11,r11,ha16(L_darwin64_struct_ret_by_value_p$lazy_ptr-"L00000000002$spb") + mtlr r0 + lwzu r12,lo16(L_darwin64_struct_ret_by_value_p$lazy_ptr-"L00000000002$spb")(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_darwin64_struct_ret_by_value_p$lazy_ptr: + .indirect_symbol _darwin64_struct_ret_by_value_p + .g_long dyld_stub_binding_helper + + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_darwin64_pass_struct_floats$stub: + .indirect_symbol _darwin64_pass_struct_floats + mflr r0 + bcl 20,31,"L00000000003$spb" +"L00000000003$spb": + mflr r11 + addis r11,r11,ha16(L_darwin64_pass_struct_floats$lazy_ptr-"L00000000003$spb") + mtlr r0 + lwzu r12,lo16(L_darwin64_pass_struct_floats$lazy_ptr-"L00000000003$spb")(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_darwin64_pass_struct_floats$lazy_ptr: + .indirect_symbol _darwin64_pass_struct_floats + .g_long dyld_stub_binding_helper +# endif +#endif diff --git a/libffi/src/powerpc/ffi.c b/libffi/src/powerpc/ffi.c new file mode 100644 index 000000000..75784a96d --- /dev/null +++ b/libffi/src/powerpc/ffi.c @@ -0,0 +1,1442 @@ +/* ----------------------------------------------------------------------- + ffi.c - Copyright (c) 1998 Geoffrey Keating + Copyright (C) 2007, 2008 Free Software Foundation, Inc + Copyright (C) 2008 Red Hat, Inc + + 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 +#include + +#include +#include + + +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_SYSV_SMST_R4 = 1 << (31-26), /* use r4 for FFI_SYSV 8 byte + structs. */ + FLAG_SYSV_SMST_R3 = 1 << (31-25), /* use r3 for FFI_SYSV 4 byte + structs. */ + /* Bits (31-24) through (31-19) store shift value for SMST */ + + 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: + if (cif->abi == FFI_SYSV) + { + /* 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. */ + + /* Treat structs with size <= 8 bytes. */ + if (size <= 8) + { + flags |= FLAG_RETURNS_SMST; + /* These structs are returned in r3. We pack the type and the + precalculated shift value (needed in the sysv.S) into flags. + The same applies for the structs returned in r3/r4. */ + if (size <= 4) + { + flags |= FLAG_SYSV_SMST_R3; + flags |= 8 * (4 - size) << 8; + break; + } + /* These structs are returned in r3 and r4. See above. */ + if (size <= 8) + { + flags |= FLAG_SYSV_SMST_R3 | FLAG_SYSV_SMST_R4; + flags |= 8 * (8 - size) << 8; + break; + } + } + } +#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) +{ + extended_cif ecif; + + ecif.cif = cif; + ecif.avalue = avalue; + + /* If the return value is a struct and we don't have a return */ + /* value address then we need to make one */ + + if ((rvalue == NULL) && (cif->rtype->type == FFI_TYPE_STRUCT)) + { + ecif.rvalue = alloca(cif->rtype->size); + } + else + ecif.rvalue = rvalue; + + + 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; + } +} + + +#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]; + + FFI_ASSERT (cif->abi == FFI_LINUX64); + /* Copy function address and TOC from ffi_closure_LINUX64. */ + memcpy (tramp, (char *) ffi_closure_LINUX64, 16); + tramp[2] = codeloc; +#else + unsigned int *tramp; + + FFI_ASSERT (cif->abi == FFI_GCC_SYSV || cif->abi == FFI_SYSV); + + tramp = (unsigned int *) &closure->tramp[0]; + tramp[0] = 0x7c0802a6; /* mflr r0 */ + tramp[1] = 0x4800000d; /* bl 10 */ + 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/libffi/src/powerpc/ffi_darwin.c b/libffi/src/powerpc/ffi_darwin.c new file mode 100644 index 000000000..ee03dab69 --- /dev/null +++ b/libffi/src/powerpc/ffi_darwin.c @@ -0,0 +1,1359 @@ +/* ----------------------------------------------------------------------- + ffi_darwin.c + + Copyright (C) 1998 Geoffrey Keating + Copyright (C) 2001 John Hornkvist + Copyright (C) 2002, 2006, 2007, 2009, 2010 Free Software Foundation, Inc. + + FFI support for Darwin and AIX. + + 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 +#include + +#include + +extern void ffi_closure_ASM (void); + +enum { + /* The assembly depends on these exact flags. + For Darwin64 (when FLAG_RETURNS_STRUCT is set): + FLAG_RETURNS_FP indicates that the structure embeds FP data. + FLAG_RETURNS_128BITS signals a special struct size that is not + expanded for float content. */ + FLAG_RETURNS_128BITS = 1 << (31-31), /* These go in cr7 */ + FLAG_RETURNS_NOTHING = 1 << (31-30), + FLAG_RETURNS_FP = 1 << (31-29), + FLAG_RETURNS_64BITS = 1 << (31-28), + + FLAG_RETURNS_STRUCT = 1 << (31-27), /* This goes in 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 DARWIN ABI. */ +enum { + NUM_GPR_ARG_REGISTERS = 8, + NUM_FPR_ARG_REGISTERS = 13, + LINKAGE_AREA_GPRS = 6 +}; + +enum { ASM_NEEDS_REGISTERS = 4 }; /* r28-r31 */ + +/* ffi_prep_args is called by the assembly routine once stack space + has been allocated for the function's arguments. + + m32/m64 + + The stack layout we want looks like this: + + | Return address from ffi_call_DARWIN | higher addresses + |--------------------------------------------| + | Previous backchain pointer 4/8 | stack pointer here + |--------------------------------------------|<+ <<< on entry to + | ASM_NEEDS_REGISTERS=r28-r31 4*(4/8) | | ffi_call_DARWIN + |--------------------------------------------| | + | When we have any FP activity... the | | + | FPRs occupy NUM_FPR_ARG_REGISTERS slots | | + | here fp13 .. fp1 from high to low addr. | | + ~ ~ ~ + | Parameters (at least 8*4/8=32/64) | | NUM_GPR_ARG_REGISTERS + |--------------------------------------------| | + | TOC=R2 (AIX) Reserved (Darwin) 4/8 | | + |--------------------------------------------| | stack | + | Reserved 2*4/8 | | grows | + |--------------------------------------------| | down V + | Space for callee's LR 4/8 | | + |--------------------------------------------| | lower addresses + | Saved CR [low word for m64] 4/8 | | + |--------------------------------------------| | stack pointer here + | Current backchain pointer 4/8 |-/ during + |--------------------------------------------| <<< ffi_call_DARWIN + + */ + +#if defined(POWERPC_DARWIN64) +static void +darwin64_pass_struct_by_value + (ffi_type *, char *, unsigned, unsigned *, double **, unsigned long **); +#endif + +/* This depends on GPR_SIZE = sizeof (unsigned long) */ + +void +ffi_prep_args (extended_cif *ecif, unsigned long *const stack) +{ + const unsigned bytes = ecif->cif->bytes; + const unsigned flags = ecif->cif->flags; + const unsigned nargs = ecif->cif->nargs; +#if !defined(POWERPC_DARWIN64) + const ffi_abi abi = ecif->cif->abi; +#endif + + /* 'stacktop' points at the previous backchain pointer. */ + unsigned long *const stacktop = stack + (bytes / sizeof(unsigned long)); + + /* 'fpr_base' points at the space for fpr1, and grows upwards as + we use FPR registers. */ + double *fpr_base = (double *) (stacktop - ASM_NEEDS_REGISTERS) - NUM_FPR_ARG_REGISTERS; + int gp_count = 0, fparg_count = 0; + + /* 'next_arg' grows up as we put parameters in it. */ + unsigned long *next_arg = stack + LINKAGE_AREA_GPRS; /* 6 reserved positions. */ + + int i; + double double_tmp; + void **p_argv = ecif->avalue; + unsigned long gprvalue; + ffi_type** ptr = ecif->cif->arg_types; +#if !defined(POWERPC_DARWIN64) + char *dest_cpy; +#endif + unsigned size_al = 0; + + /* Check that everything starts aligned properly. */ + FFI_ASSERT(((unsigned) (char *) stack & 0xF) == 0); + FFI_ASSERT(((unsigned) (char *) stacktop & 0xF) == 0); + FFI_ASSERT((bytes & 0xF) == 0); + + /* Deal with return values that are actually pass-by-reference. + Rule: + Return values are referenced by r3, so r4 is the first parameter. */ + + if (flags & FLAG_RETVAL_REFERENCE) + *next_arg++ = (unsigned long) (char *) ecif->rvalue; + + /* Now for the arguments. */ + for (i = nargs; i > 0; i--, ptr++, p_argv++) + { + switch ((*ptr)->type) + { + /* If a floating-point parameter appears before all of the general- + purpose registers are filled, the corresponding GPRs that match + the size of the floating-point parameter are skipped. */ + case FFI_TYPE_FLOAT: + double_tmp = *(float *) *p_argv; + if (fparg_count < NUM_FPR_ARG_REGISTERS) + *fpr_base++ = double_tmp; +#if defined(POWERPC_DARWIN) + *(float *)next_arg = *(float *) *p_argv; +#else + *(double *)next_arg = double_tmp; +#endif + next_arg++; + gp_count++; + fparg_count++; + FFI_ASSERT(flags & FLAG_FP_ARGUMENTS); + break; + + case FFI_TYPE_DOUBLE: + double_tmp = *(double *) *p_argv; + if (fparg_count < NUM_FPR_ARG_REGISTERS) + *fpr_base++ = double_tmp; + *(double *)next_arg = double_tmp; +#ifdef POWERPC64 + next_arg++; + gp_count++; +#else + next_arg += 2; + gp_count += 2; +#endif + fparg_count++; + FFI_ASSERT(flags & FLAG_FP_ARGUMENTS); + break; + +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + + case FFI_TYPE_LONGDOUBLE: +# if defined(POWERPC64) && !defined(POWERPC_DARWIN64) + /* ??? This will exceed the regs count when the value starts at fp13 + and it will not put the extra bit on the stack. */ + if (fparg_count < NUM_FPR_ARG_REGISTERS) + *(long double *) fpr_base++ = *(long double *) *p_argv; + else + *(long double *) next_arg = *(long double *) *p_argv; + next_arg += 2; + fparg_count += 2; +# else + double_tmp = ((double *) *p_argv)[0]; + if (fparg_count < NUM_FPR_ARG_REGISTERS) + *fpr_base++ = double_tmp; + *(double *) next_arg = double_tmp; +# if defined(POWERPC_DARWIN64) + next_arg++; + gp_count++; +# else + next_arg += 2; + gp_count += 2; +# endif + fparg_count++; + double_tmp = ((double *) *p_argv)[1]; + if (fparg_count < NUM_FPR_ARG_REGISTERS) + *fpr_base++ = double_tmp; + *(double *) next_arg = double_tmp; +# if defined(POWERPC_DARWIN64) + next_arg++; + gp_count++; +# else + next_arg += 2; + gp_count += 2; +# endif + fparg_count++; +# endif + FFI_ASSERT(flags & FLAG_FP_ARGUMENTS); + break; +#endif + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: +#ifdef POWERPC64 + gprvalue = *(long long *) *p_argv; + goto putgpr; +#else + *(long long *) next_arg = *(long long *) *p_argv; + next_arg += 2; + gp_count += 2; +#endif + break; + case FFI_TYPE_POINTER: + gprvalue = *(unsigned long *) *p_argv; + goto putgpr; + case FFI_TYPE_UINT8: + gprvalue = *(unsigned char *) *p_argv; + goto putgpr; + case FFI_TYPE_SINT8: + gprvalue = *(signed char *) *p_argv; + goto putgpr; + case FFI_TYPE_UINT16: + gprvalue = *(unsigned short *) *p_argv; + goto putgpr; + case FFI_TYPE_SINT16: + gprvalue = *(signed short *) *p_argv; + goto putgpr; + + case FFI_TYPE_STRUCT: + size_al = (*ptr)->size; +#if defined(POWERPC_DARWIN64) + next_arg = (unsigned long *)ALIGN((char *)next_arg, (*ptr)->alignment); + darwin64_pass_struct_by_value (*ptr, (char *) *p_argv, + (unsigned) size_al, + (unsigned int *) &fparg_count, + &fpr_base, &next_arg); +#else + dest_cpy = (char *) next_arg; + + /* If the first member of the struct is a double, then include enough + padding in the struct size to align it to double-word. */ + if ((*ptr)->elements[0]->type == FFI_TYPE_DOUBLE) + size_al = ALIGN((*ptr)->size, 8); + +# if defined(POWERPC64) + FFI_ASSERT (abi != FFI_DARWIN); + memcpy ((char *) dest_cpy, (char *) *p_argv, size_al); + next_arg += (size_al + 7) / 8; +# else + /* Structures that match the basic modes (QI 1 byte, HI 2 bytes, + SI 4 bytes) are aligned as if they were those modes. + Structures with 3 byte in size are padded upwards. */ + if (size_al < 3 && abi == FFI_DARWIN) + dest_cpy += 4 - size_al; + + memcpy((char *) dest_cpy, (char *) *p_argv, size_al); + next_arg += (size_al + 3) / 4; +# endif +#endif + break; + + case FFI_TYPE_INT: + case FFI_TYPE_SINT32: + gprvalue = *(signed int *) *p_argv; + goto putgpr; + + case FFI_TYPE_UINT32: + gprvalue = *(unsigned int *) *p_argv; + putgpr: + *next_arg++ = gprvalue; + gp_count++; + break; + default: + break; + } + } + + /* Check that we didn't overrun the stack... */ + //FFI_ASSERT(gpr_base <= stacktop - ASM_NEEDS_REGISTERS); + //FFI_ASSERT((unsigned *)fpr_base + // <= stacktop - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS); + //FFI_ASSERT(flags & FLAG_4_GPR_ARGUMENTS || intarg_count <= 4); +} + +#if defined(POWERPC_DARWIN64) + +/* See if we can put some of the struct into fprs. + This should not be called for structures of size 16 bytes, since these are not + broken out this way. */ +static void +darwin64_scan_struct_for_floats (ffi_type *s, unsigned *nfpr) +{ + int i; + + FFI_ASSERT (s->type == FFI_TYPE_STRUCT) + + for (i = 0; s->elements[i] != NULL; i++) + { + ffi_type *p = s->elements[i]; + switch (p->type) + { + case FFI_TYPE_STRUCT: + darwin64_scan_struct_for_floats (p, nfpr); + break; + case FFI_TYPE_LONGDOUBLE: + (*nfpr) += 2; + break; + case FFI_TYPE_DOUBLE: + case FFI_TYPE_FLOAT: + (*nfpr) += 1; + break; + default: + break; + } + } +} + +static int +darwin64_struct_size_exceeds_gprs_p (ffi_type *s, char *src, unsigned *nfpr) +{ + unsigned struct_offset=0, i; + + for (i = 0; s->elements[i] != NULL; i++) + { + char *item_base; + ffi_type *p = s->elements[i]; + /* Find the start of this item (0 for the first one). */ + if (i > 0) + struct_offset = ALIGN(struct_offset, p->alignment); + + item_base = src + struct_offset; + + switch (p->type) + { + case FFI_TYPE_STRUCT: + if (darwin64_struct_size_exceeds_gprs_p (p, item_base, nfpr)) + return 1; + break; + case FFI_TYPE_LONGDOUBLE: + if (*nfpr >= NUM_FPR_ARG_REGISTERS) + return 1; + (*nfpr) += 1; + item_base += 8; + /* FALL THROUGH */ + case FFI_TYPE_DOUBLE: + if (*nfpr >= NUM_FPR_ARG_REGISTERS) + return 1; + (*nfpr) += 1; + break; + case FFI_TYPE_FLOAT: + if (*nfpr >= NUM_FPR_ARG_REGISTERS) + return 1; + (*nfpr) += 1; + break; + default: + /* If we try and place any item, that is non-float, once we've + exceeded the 8 GPR mark, then we can't fit the struct. */ + if ((unsigned long)item_base >= 8*8) + return 1; + break; + } + /* now count the size of what we just used. */ + struct_offset += p->size; + } + return 0; +} + +/* Can this struct be returned by value? */ +int +darwin64_struct_ret_by_value_p (ffi_type *s) +{ + unsigned nfp = 0; + + FFI_ASSERT (s && s->type == FFI_TYPE_STRUCT); + + /* The largest structure we can return is 8long + 13 doubles. */ + if (s->size > 168) + return 0; + + /* We can't pass more than 13 floats. */ + darwin64_scan_struct_for_floats (s, &nfp); + if (nfp > 13) + return 0; + + /* If there are not too many floats, and the struct is + small enough to accommodate in the GPRs, then it must be OK. */ + if (s->size <= 64) + return 1; + + /* Well, we have to look harder. */ + nfp = 0; + if (darwin64_struct_size_exceeds_gprs_p (s, NULL, &nfp)) + return 0; + + return 1; +} + +void +darwin64_pass_struct_floats (ffi_type *s, char *src, + unsigned *nfpr, double **fprs) +{ + int i; + double *fpr_base = *fprs; + unsigned struct_offset = 0; + + /* We don't assume anything about the alignment of the source. */ + for (i = 0; s->elements[i] != NULL; i++) + { + char *item_base; + ffi_type *p = s->elements[i]; + /* Find the start of this item (0 for the first one). */ + if (i > 0) + struct_offset = ALIGN(struct_offset, p->alignment); + item_base = src + struct_offset; + + switch (p->type) + { + case FFI_TYPE_STRUCT: + darwin64_pass_struct_floats (p, item_base, nfpr, + &fpr_base); + break; + case FFI_TYPE_LONGDOUBLE: + if (*nfpr < NUM_FPR_ARG_REGISTERS) + *fpr_base++ = *(double *)item_base; + (*nfpr) += 1; + item_base += 8; + /* FALL THROUGH */ + case FFI_TYPE_DOUBLE: + if (*nfpr < NUM_FPR_ARG_REGISTERS) + *fpr_base++ = *(double *)item_base; + (*nfpr) += 1; + break; + case FFI_TYPE_FLOAT: + if (*nfpr < NUM_FPR_ARG_REGISTERS) + *fpr_base++ = (double) *(float *)item_base; + (*nfpr) += 1; + break; + default: + break; + } + /* now count the size of what we just used. */ + struct_offset += p->size; + } + /* Update the scores. */ + *fprs = fpr_base; +} + +/* Darwin64 special rules. + Break out a struct into params and float registers. */ +static void +darwin64_pass_struct_by_value (ffi_type *s, char *src, unsigned size, + unsigned *nfpr, double **fprs, unsigned long **arg) +{ + unsigned long *next_arg = *arg; + char *dest_cpy = (char *)next_arg; + + FFI_ASSERT (s->type == FFI_TYPE_STRUCT) + + if (!size) + return; + + /* First... special cases. */ + if (size < 3 + || (size == 4 + && s->elements[0] + && s->elements[0]->type != FFI_TYPE_FLOAT)) + { + /* Must be at least one GPR, padding is unspecified in value, + let's make it zero. */ + *next_arg = 0UL; + dest_cpy += 8 - size; + memcpy ((char *) dest_cpy, src, size); + next_arg++; + } + else if (size == 16) + { + memcpy ((char *) dest_cpy, src, size); + next_arg += 2; + } + else + { + /* now the general case, we consider embedded floats. */ + memcpy ((char *) dest_cpy, src, size); + darwin64_pass_struct_floats (s, src, nfpr, fprs); + next_arg += (size+7)/8; + } + + *arg = next_arg; +} + +double * +darwin64_struct_floats_to_mem (ffi_type *s, char *dest, double *fprs, unsigned *nf) +{ + int i; + unsigned struct_offset = 0; + + /* We don't assume anything about the alignment of the source. */ + for (i = 0; s->elements[i] != NULL; i++) + { + char *item_base; + ffi_type *p = s->elements[i]; + /* Find the start of this item (0 for the first one). */ + if (i > 0) + struct_offset = ALIGN(struct_offset, p->alignment); + item_base = dest + struct_offset; + + switch (p->type) + { + case FFI_TYPE_STRUCT: + fprs = darwin64_struct_floats_to_mem (p, item_base, fprs, nf); + break; + case FFI_TYPE_LONGDOUBLE: + if (*nf < NUM_FPR_ARG_REGISTERS) + { + *(double *)item_base = *fprs++ ; + (*nf) += 1; + } + item_base += 8; + /* FALL THROUGH */ + case FFI_TYPE_DOUBLE: + if (*nf < NUM_FPR_ARG_REGISTERS) + { + *(double *)item_base = *fprs++ ; + (*nf) += 1; + } + break; + case FFI_TYPE_FLOAT: + if (*nf < NUM_FPR_ARG_REGISTERS) + { + *(float *)item_base = (float) *fprs++ ; + (*nf) += 1; + } + break; + default: + break; + } + /* now count the size of what we just used. */ + struct_offset += p->size; + } + return fprs; +} + +#endif + +/* Adjust the size of S to be correct for Darwin. + On Darwin m32, the first field of a structure has natural alignment. + On Darwin m64, all fields have natural alignment. */ + +static void +darwin_adjust_aggregate_sizes (ffi_type *s) +{ + int i; + + if (s->type != FFI_TYPE_STRUCT) + return; + + s->size = 0; + for (i = 0; s->elements[i] != NULL; i++) + { + ffi_type *p; + int align; + + p = s->elements[i]; + if (p->type == FFI_TYPE_STRUCT) + darwin_adjust_aggregate_sizes (p); +#if defined(POWERPC_DARWIN64) + /* Natural alignment for all items. */ + align = p->alignment; +#else + /* Natrual alignment for the first item... */ + if (i == 0) + align = p->alignment; + else if (p->alignment == 16 || p->alignment < 4) + /* .. subsequent items with vector or align < 4 have natural align. */ + align = p->alignment; + else + /* .. or align is 4. */ + align = 4; +#endif + /* Pad, if necessary, before adding the current item. */ + s->size = ALIGN(s->size, align) + p->size; + } + + s->size = ALIGN(s->size, s->alignment); + + /* This should not be necessary on m64, but harmless. */ + if (s->elements[0]->type == FFI_TYPE_UINT64 + || s->elements[0]->type == FFI_TYPE_SINT64 + || s->elements[0]->type == FFI_TYPE_DOUBLE + || s->elements[0]->alignment == 8) + s->alignment = s->alignment > 8 ? s->alignment : 8; + /* Do not add additional tail padding. */ +} + +/* Adjust the size of S to be correct for AIX. + Word-align double unless it is the first member of a structure. */ + +static void +aix_adjust_aggregate_sizes (ffi_type *s) +{ + int i; + + if (s->type != FFI_TYPE_STRUCT) + return; + + s->size = 0; + for (i = 0; s->elements[i] != NULL; i++) + { + ffi_type *p; + int align; + + p = s->elements[i]; + aix_adjust_aggregate_sizes (p); + align = p->alignment; + if (i != 0 && p->type == FFI_TYPE_DOUBLE) + align = 4; + s->size = ALIGN(s->size, align) + p->size; + } + + s->size = ALIGN(s->size, s->alignment); + + if (s->elements[0]->type == FFI_TYPE_UINT64 + || s->elements[0]->type == FFI_TYPE_SINT64 + || s->elements[0]->type == FFI_TYPE_DOUBLE + || s->elements[0]->alignment == 8) + s->alignment = s->alignment > 8 ? s->alignment : 8; + /* Do not add additional tail padding. */ +} + +/* Perform machine dependent cif processing. */ +ffi_status +ffi_prep_cif_machdep (ffi_cif *cif) +{ + /* All this is for the DARWIN ABI. */ + unsigned i; + ffi_type **ptr; + unsigned bytes; + unsigned fparg_count = 0, intarg_count = 0; + unsigned flags = 0; + unsigned size_al = 0; + + /* All the machine-independent calculation of cif->bytes will be wrong. + All the calculation of structure sizes will also be wrong. + Redo the calculation for DARWIN. */ + + if (cif->abi == FFI_DARWIN) + { + darwin_adjust_aggregate_sizes (cif->rtype); + for (i = 0; i < cif->nargs; i++) + darwin_adjust_aggregate_sizes (cif->arg_types[i]); + } + + if (cif->abi == FFI_AIX) + { + aix_adjust_aggregate_sizes (cif->rtype); + for (i = 0; i < cif->nargs; i++) + aix_adjust_aggregate_sizes (cif->arg_types[i]); + } + + /* Space for the frame pointer, callee's LR, CR, etc, and for + the asm's temp regs. */ + + bytes = (LINKAGE_AREA_GPRS + ASM_NEEDS_REGISTERS) * sizeof(unsigned long); + + /* Return value handling. + The rules m32 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; + - Long double FP (if not equivalent to double) values are returned in + fpr1 and fpr2; + m64: + - 64-bit or smaller integral values are returned in GPR3 + - Single/double FP values are returned in fpr1; + - Long double FP values are returned in fpr1 and fpr2; + m64 Structures: + - If the structure could be accommodated in registers were it to be the + first argument to a routine, then it is returned in those registers. + m32/m64 structures otherwise: + - Larger structures values are allocated space and a pointer is passed + as the first argument. */ + switch (cif->rtype->type) + { + +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + case FFI_TYPE_LONGDOUBLE: + flags |= FLAG_RETURNS_128BITS; + flags |= FLAG_RETURNS_FP; + break; +#endif + + case FFI_TYPE_DOUBLE: + flags |= FLAG_RETURNS_64BITS; + /* Fall through. */ + case FFI_TYPE_FLOAT: + flags |= FLAG_RETURNS_FP; + break; + + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: +#ifdef POWERPC64 + case FFI_TYPE_POINTER: +#endif + flags |= FLAG_RETURNS_64BITS; + break; + + case FFI_TYPE_STRUCT: +#if defined(POWERPC_DARWIN64) + { + /* Can we fit the struct into regs? */ + if (darwin64_struct_ret_by_value_p (cif->rtype)) + { + unsigned nfpr = 0; + flags |= FLAG_RETURNS_STRUCT; + if (cif->rtype->size != 16) + darwin64_scan_struct_for_floats (cif->rtype, &nfpr) ; + else + flags |= FLAG_RETURNS_128BITS; + /* Will be 0 for 16byte struct. */ + if (nfpr) + flags |= FLAG_RETURNS_FP; + } + else /* By ref. */ + { + flags |= FLAG_RETVAL_REFERENCE; + flags |= FLAG_RETURNS_NOTHING; + intarg_count++; + } + } +#elif defined(DARWIN_PPC) + if (cif->rtype->size <= 4) + flags |= FLAG_RETURNS_STRUCT; + else /* else by reference. */ + { + flags |= FLAG_RETVAL_REFERENCE; + flags |= FLAG_RETURNS_NOTHING; + intarg_count++; + } +#else /* assume we pass by ref. */ + flags |= FLAG_RETVAL_REFERENCE; + flags |= FLAG_RETURNS_NOTHING; + intarg_count++; +#endif + break; + case FFI_TYPE_VOID: + flags |= FLAG_RETURNS_NOTHING; + break; + + default: + /* Returns 32-bit integer, or similar. Nothing to do here. */ + break; + } + + /* The first NUM_GPR_ARG_REGISTERS words of integer arguments, and the + first NUM_FPR_ARG_REGISTERS fp arguments, go in registers; the rest + goes on the stack. + ??? Structures are passed as a pointer to a copy of the structure. + Stuff on the stack needs to keep proper alignment. + For m64 the count is effectively of half-GPRs. */ + for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) + { + unsigned align_words; + switch ((*ptr)->type) + { + case FFI_TYPE_FLOAT: + case FFI_TYPE_DOUBLE: + fparg_count++; +#if !defined(POWERPC_DARWIN64) + /* If this FP arg is going on the stack, it must be + 8-byte-aligned. */ + if (fparg_count > NUM_FPR_ARG_REGISTERS + && (intarg_count & 0x01) != 0) + intarg_count++; +#endif + break; + +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + case FFI_TYPE_LONGDOUBLE: + fparg_count += 2; + /* If this FP arg is going on the stack, it must be + 16-byte-aligned. */ + if (fparg_count >= NUM_FPR_ARG_REGISTERS) +#if defined (POWERPC64) + intarg_count = ALIGN(intarg_count, 2); +#else + intarg_count = ALIGN(intarg_count, 4); +#endif + break; +#endif + + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: +#if defined(POWERPC64) + intarg_count++; +#else + /* '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. */ + if (intarg_count == NUM_GPR_ARG_REGISTERS-1 + || (intarg_count >= NUM_GPR_ARG_REGISTERS + && (intarg_count & 0x01) != 0)) + intarg_count++; + intarg_count += 2; +#endif + break; + + case FFI_TYPE_STRUCT: + size_al = (*ptr)->size; +#if defined(POWERPC_DARWIN64) + align_words = (*ptr)->alignment >> 3; + if (align_words) + intarg_count = ALIGN(intarg_count, align_words); + /* Base size of the struct. */ + intarg_count += (size_al + 7) / 8; + /* If 16 bytes then don't worry about floats. */ + if (size_al != 16) + /* Scan through for floats to be placed in regs. */ + darwin64_scan_struct_for_floats (*ptr, &fparg_count) ; +#else + align_words = (*ptr)->alignment >> 2; + if (align_words) + intarg_count = ALIGN(intarg_count, align_words); + /* If the first member of the struct is a double, then align + the struct to double-word. + if ((*ptr)->elements[0]->type == FFI_TYPE_DOUBLE) + size_al = ALIGN((*ptr)->size, 8); */ +# ifdef POWERPC64 + intarg_count += (size_al + 7) / 8; +# else + intarg_count += (size_al + 3) / 4; +# endif +#endif + break; + + default: + /* Everything else is passed as a 4-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 defined(POWERPC_DARWIN64) + /* Space to image the FPR registers, if needed - which includes when they might be + used in a struct return. */ + if (fparg_count != 0 + || ((flags & FLAG_RETURNS_STRUCT) + && (flags & FLAG_RETURNS_FP))) + bytes += NUM_FPR_ARG_REGISTERS * sizeof(double); +#else + /* Space for the FPR registers, if needed. */ + if (fparg_count != 0) + bytes += NUM_FPR_ARG_REGISTERS * sizeof(double); +#endif + + /* Stack space. */ +#ifdef POWERPC64 + if ((intarg_count + fparg_count) > NUM_GPR_ARG_REGISTERS) + bytes += (intarg_count + fparg_count) * sizeof(long); +#else + if ((intarg_count + 2 * fparg_count) > NUM_GPR_ARG_REGISTERS) + bytes += (intarg_count + 2 * fparg_count) * sizeof(long); +#endif + else + bytes += NUM_GPR_ARG_REGISTERS * sizeof(long); + + /* The stack space allocated needs to be a multiple of 16 bytes. */ + bytes = ALIGN(bytes, 16) ; + + cif->flags = flags; + cif->bytes = bytes; + + return FFI_OK; +} + +extern void ffi_call_AIX(extended_cif *, long, unsigned, unsigned *, + void (*fn)(void), void (*fn2)(void)); + +extern void ffi_call_DARWIN(extended_cif *, long, unsigned, unsigned *, + void (*fn)(void), void (*fn2)(void), ffi_type*); + +void +ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) +{ + extended_cif ecif; + + ecif.cif = cif; + ecif.avalue = avalue; + + /* If the return value is a struct and we don't have a return + value address then we need to make one. */ + + if ((rvalue == NULL) && + (cif->rtype->type == FFI_TYPE_STRUCT)) + { + ecif.rvalue = alloca (cif->rtype->size); + } + else + ecif.rvalue = rvalue; + + switch (cif->abi) + { + case FFI_AIX: + ffi_call_AIX(&ecif, -(long)cif->bytes, cif->flags, ecif.rvalue, fn, + FFI_FN(ffi_prep_args)); + break; + case FFI_DARWIN: + ffi_call_DARWIN(&ecif, -(long)cif->bytes, cif->flags, ecif.rvalue, fn, + FFI_FN(ffi_prep_args), cif->rtype); + break; + default: + FFI_ASSERT(0); + break; + } +} + +static void flush_icache(char *); +static void flush_range(char *, int); + +/* The layout of a function descriptor. A C function pointer really + points to one of these. */ + +typedef struct aix_fd_struct { + void *code_pointer; + void *toc; +} aix_fd; + +/* here I'd like to add the stack frame layout we use in darwin_closure.S + and aix_closure.S + + m32/m64 + + The stack layout looks like this: + + | Additional params... | | Higher address + ~ ~ ~ + | Parameters (at least 8*4/8=32/64) | | NUM_GPR_ARG_REGISTERS + |--------------------------------------------| | + | TOC=R2 (AIX) Reserved (Darwin) 4/8 | | + |--------------------------------------------| | + | Reserved 2*4/8 | | + |--------------------------------------------| | + | Space for callee's LR 4/8 | | + |--------------------------------------------| | + | Saved CR [low word for m64] 4/8 | | + |--------------------------------------------| | + | Current backchain pointer 4/8 |-/ Parent's frame. + |--------------------------------------------| <+ <<< on entry to ffi_closure_ASM + | Result Bytes 16 | | + |--------------------------------------------| | + ~ padding to 16-byte alignment ~ ~ + |--------------------------------------------| | + | NUM_FPR_ARG_REGISTERS slots | | + | here fp13 .. fp1 13*8 | | + |--------------------------------------------| | + | R3..R10 8*4/8=32/64 | | NUM_GPR_ARG_REGISTERS + |--------------------------------------------| | + | TOC=R2 (AIX) Reserved (Darwin) 4/8 | | + |--------------------------------------------| | stack | + | Reserved [compiler,binder] 2*4/8 | | grows | + |--------------------------------------------| | down V + | Space for callee's LR 4/8 | | + |--------------------------------------------| | lower addresses + | Saved CR [low word for m64] 4/8 | | + |--------------------------------------------| | stack pointer here + | Current backchain pointer 4/8 |-/ during + |--------------------------------------------| <<< ffi_closure_ASM. + +*/ + +ffi_status +ffi_prep_closure_loc (ffi_closure* closure, + ffi_cif* cif, + void (*fun)(ffi_cif*, void*, void**, void*), + void *user_data, + void *codeloc) +{ + unsigned int *tramp; + struct ffi_aix_trampoline_struct *tramp_aix; + aix_fd *fd; + + switch (cif->abi) + { + case FFI_DARWIN: + + FFI_ASSERT (cif->abi == FFI_DARWIN); + + tramp = (unsigned int *) &closure->tramp[0]; +#if defined(POWERPC_DARWIN64) + tramp[0] = 0x7c0802a6; /* mflr r0 */ + tramp[1] = 0x429f0015; /* bcl- 20,4*cr7+so, +0x18 (L1) */ + /* We put the addresses here. */ + tramp[6] = 0x7d6802a6; /*L1: mflr r11 */ + tramp[7] = 0xe98b0000; /* ld r12,0(r11) function address */ + tramp[8] = 0x7c0803a6; /* mtlr r0 */ + tramp[9] = 0x7d8903a6; /* mtctr r12 */ + tramp[10] = 0xe96b0008; /* lwz r11,8(r11) static chain */ + tramp[11] = 0x4e800420; /* bctr */ + + *((unsigned long *)&tramp[2]) = (unsigned long) ffi_closure_ASM; /* function */ + *((unsigned long *)&tramp[4]) = (unsigned long) codeloc; /* context */ +#else + tramp[0] = 0x7c0802a6; /* mflr r0 */ + tramp[1] = 0x429f000d; /* bcl- 20,4*cr7+so,0x10 */ + tramp[4] = 0x7d6802a6; /* mflr r11 */ + tramp[5] = 0x818b0000; /* lwz r12,0(r11) function address */ + tramp[6] = 0x7c0803a6; /* mtlr r0 */ + tramp[7] = 0x7d8903a6; /* mtctr r12 */ + tramp[8] = 0x816b0004; /* lwz r11,4(r11) static chain */ + tramp[9] = 0x4e800420; /* bctr */ + tramp[2] = (unsigned long) ffi_closure_ASM; /* function */ + tramp[3] = (unsigned long) codeloc; /* context */ +#endif + closure->cif = cif; + closure->fun = fun; + closure->user_data = user_data; + + /* Flush the icache. Only necessary on Darwin. */ + flush_range(codeloc, FFI_TRAMPOLINE_SIZE); + + break; + + case FFI_AIX: + + tramp_aix = (struct ffi_aix_trampoline_struct *) (closure->tramp); + fd = (aix_fd *)(void *)ffi_closure_ASM; + + FFI_ASSERT (cif->abi == FFI_AIX); + + tramp_aix->code_pointer = fd->code_pointer; + tramp_aix->toc = fd->toc; + tramp_aix->static_chain = codeloc; + closure->cif = cif; + closure->fun = fun; + closure->user_data = user_data; + + default: + + FFI_ASSERT(0); + break; + } + return FFI_OK; +} + +static void +flush_icache(char *addr) +{ +#ifndef _AIX + __asm__ volatile ( + "dcbf 0,%0\n" + "\tsync\n" + "\ticbi 0,%0\n" + "\tsync\n" + "\tisync" + : : "r"(addr) : "memory"); +#endif +} + +static void +flush_range(char * addr1, int size) +{ +#define MIN_LINE_SIZE 32 + int i; + for (i = 0; i < size; i += MIN_LINE_SIZE) + flush_icache(addr1+i); + flush_icache(addr1+size-1); +} + +typedef union +{ + float f; + double d; +} ffi_dblfl; + +ffi_type * +ffi_closure_helper_DARWIN (ffi_closure *, void *, + unsigned long *, ffi_dblfl *); + +/* Basically the trampoline invokes ffi_closure_ASM, 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_ASM invokes the + following helper function to do most of the work. */ + +ffi_type * +ffi_closure_helper_DARWIN (ffi_closure *closure, void *rvalue, + unsigned long *pgr, ffi_dblfl *pfr) +{ + /* 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_ASM + pfr is the pointer to where f1-f13 are stored in ffi_closure_ASM. */ + + typedef double ldbits[2]; + + union ldu + { + ldbits lb; + long double ld; + }; + + void ** avalue; + ffi_type ** arg_types; + long i, avn; + ffi_cif * cif; + ffi_dblfl * end_pfr = pfr + NUM_FPR_ARG_REGISTERS; + unsigned size_al; +#if defined(POWERPC_DARWIN64) + unsigned fpsused = 0; +#endif + + cif = closure->cif; + avalue = alloca (cif->nargs * sizeof(void *)); + + if (cif->rtype->type == FFI_TYPE_STRUCT) + { +#if defined(POWERPC_DARWIN64) + if (!darwin64_struct_ret_by_value_p (cif->rtype)) + { + /* Won't fit into the regs - return by ref. */ + rvalue = (void *) *pgr; + pgr++; + } +#elif defined(DARWIN_PPC) + if (cif->rtype->size > 4) + { + rvalue = (void *) *pgr; + pgr++; + } +#else /* assume we return by ref. */ + rvalue = (void *) *pgr; + pgr++; +#endif + } + + 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: +#if defined(POWERPC64) + avalue[i] = (char *) pgr + 7; +#else + avalue[i] = (char *) pgr + 3; +#endif + pgr++; + break; + + case FFI_TYPE_SINT16: + case FFI_TYPE_UINT16: +#if defined(POWERPC64) + avalue[i] = (char *) pgr + 6; +#else + avalue[i] = (char *) pgr + 2; +#endif + pgr++; + break; + + case FFI_TYPE_SINT32: + case FFI_TYPE_UINT32: +#if defined(POWERPC64) + avalue[i] = (char *) pgr + 4; +#else + case FFI_TYPE_POINTER: + avalue[i] = pgr; +#endif + pgr++; + break; + + case FFI_TYPE_STRUCT: + size_al = arg_types[i]->size; +#if defined(POWERPC_DARWIN64) + pgr = (unsigned long *)ALIGN((char *)pgr, arg_types[i]->alignment); + if (size_al < 3 || size_al == 4) + { + avalue[i] = ((char *)pgr)+8-size_al; + if (arg_types[i]->elements[0]->type == FFI_TYPE_FLOAT + && fpsused < NUM_FPR_ARG_REGISTERS) + { + *(float *)pgr = (float) *(double *)pfr; + pfr++; + fpsused++; + } + } + else + { + if (size_al != 16) + pfr = (ffi_dblfl *) + darwin64_struct_floats_to_mem (arg_types[i], (char *)pgr, + (double *)pfr, &fpsused); + avalue[i] = pgr; + } + pgr += (size_al + 7) / 8; +#else + /* If the first member of the struct is a double, then align + the struct to double-word. */ + if (arg_types[i]->elements[0]->type == FFI_TYPE_DOUBLE) + size_al = ALIGN(arg_types[i]->size, 8); +# if defined(POWERPC64) + FFI_ASSERT (cif->abi != FFI_DARWIN) + avalue[i] = pgr; + pgr += (size_al + 7) / 8; +# else + /* Structures that match the basic modes (QI 1 byte, HI 2 bytes, + SI 4 bytes) are aligned as if they were those modes. */ + if (size_al < 3 && cif->abi == FFI_DARWIN) + avalue[i] = (char*) pgr + 4 - size_al; + else + avalue[i] = pgr; + pgr += (size_al + 3) / 4; +# endif +#endif + break; + + case FFI_TYPE_SINT64: + case FFI_TYPE_UINT64: +#if defined(POWERPC64) + case FFI_TYPE_POINTER: + avalue[i] = pgr; + pgr++; + break; +#else + /* Long long ints are passed in two gpr's. */ + avalue[i] = pgr; + pgr += 2; + break; +#endif + + case FFI_TYPE_FLOAT: + /* A float value consumes a GPR. + 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] = pgr; + } + pgr++; + break; + + case FFI_TYPE_DOUBLE: + /* A double value consumes two GPRs. + There are 13 64bit floating point registers. */ + if (pfr < end_pfr) + { + avalue[i] = pfr; + pfr++; + } + else + { + avalue[i] = pgr; + } +#ifdef POWERPC64 + pgr++; +#else + pgr += 2; +#endif + break; + +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + + case FFI_TYPE_LONGDOUBLE: +#ifdef POWERPC64 + if (pfr + 1 < end_pfr) + { + avalue[i] = pfr; + pfr += 2; + } + else + { + if (pfr < end_pfr) + { + *pgr = *(unsigned long *) pfr; + pfr++; + } + avalue[i] = pgr; + } + pgr += 2; +#else /* POWERPC64 */ + /* A long double value consumes four GPRs and two FPRs. + There are 13 64bit floating point registers. */ + if (pfr + 1 < end_pfr) + { + avalue[i] = pfr; + pfr += 2; + } + /* Here we have the situation where one part of the long double + is stored in fpr13 and the other part is already on the stack. + We use a union to pass the long double to avalue[i]. */ + else if (pfr + 1 == end_pfr) + { + union ldu temp_ld; + memcpy (&temp_ld.lb[0], pfr, sizeof(ldbits)); + memcpy (&temp_ld.lb[1], pgr + 2, sizeof(ldbits)); + avalue[i] = &temp_ld.ld; + pfr++; + } + else + { + avalue[i] = pgr; + } + pgr += 4; +#endif /* POWERPC64 */ + break; +#endif + default: + FFI_ASSERT(0); + } + i++; + } + + (closure->fun) (cif, rvalue, avalue, closure->user_data); + + /* Tell ffi_closure_ASM to perform return type promotions. */ + return cif->rtype; +} diff --git a/libffi/src/powerpc/ffitarget.h b/libffi/src/powerpc/ffitarget.h new file mode 100644 index 000000000..d17f7312d --- /dev/null +++ b/libffi/src/powerpc/ffitarget.h @@ -0,0 +1,139 @@ +/* -----------------------------------------------------------------*-C-*- + ffitarget.h - Copyright (c) 1996-2003 Red Hat, Inc. + Copyright (C) 2007, 2008, 2010 Free Software Foundation, Inc + Target configuration macros for PowerPC. + + 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. + + ----------------------------------------------------------------------- */ + +#ifndef LIBFFI_TARGET_H +#define LIBFFI_TARGET_H + +/* ---- System specific configurations ----------------------------------- */ + +#if defined (POWERPC) && defined (__powerpc64__) /* linux64 */ +#ifndef POWERPC64 +#define POWERPC64 +#endif +#elif defined (POWERPC_DARWIN) && defined (__ppc64__) /* Darwin64 */ +#ifndef POWERPC64 +#define POWERPC64 +#endif +#ifndef POWERPC_DARWIN64 +#define POWERPC_DARWIN64 +#endif +#elif defined (POWERPC_AIX) && defined (__64BIT__) /* AIX64 */ +#ifndef POWERPC64 +#define POWERPC64 +#endif +#endif + +#ifndef LIBFFI_ASM +typedef unsigned long ffi_arg; +typedef signed long ffi_sarg; + +typedef enum ffi_abi { + FFI_FIRST_ABI = 0, + +#ifdef POWERPC + FFI_SYSV, + FFI_GCC_SYSV, + FFI_LINUX64, + FFI_LINUX, + FFI_LINUX_SOFT_FLOAT, +# ifdef POWERPC64 + FFI_DEFAULT_ABI = FFI_LINUX64, +# else +# 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 + +#ifdef POWERPC_AIX + FFI_AIX, + FFI_DARWIN, + FFI_DEFAULT_ABI = FFI_AIX, +#endif + +#ifdef POWERPC_DARWIN + FFI_AIX, + FFI_DARWIN, + FFI_DEFAULT_ABI = FFI_DARWIN, +#endif + +#ifdef POWERPC_FREEBSD + FFI_SYSV, + FFI_GCC_SYSV, + FFI_LINUX64, + FFI_LINUX, + FFI_LINUX_SOFT_FLOAT, + FFI_DEFAULT_ABI = FFI_SYSV, +#endif + + FFI_LAST_ABI +} ffi_abi; +#endif + +/* ---- Definitions for closures ----------------------------------------- */ + +#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. + We use two flag bits, (FLAG_SYSV_SMST_R3, FLAG_SYSV_SMST_R4) which are + defined in ffi.c, to determine the exact return type and its size. */ +#define FFI_SYSV_TYPE_SMALL_STRUCT (FFI_TYPE_LAST + 2) + +#if defined(POWERPC64) || defined(POWERPC_AIX) +# if defined(POWERPC_DARWIN64) +# define FFI_TRAMPOLINE_SIZE 48 +# else +# define FFI_TRAMPOLINE_SIZE 24 +# endif +#else /* POWERPC || POWERPC_AIX */ +# define FFI_TRAMPOLINE_SIZE 40 +#endif + +#ifndef LIBFFI_ASM +#if defined(POWERPC_DARWIN) || defined(POWERPC_AIX) +struct ffi_aix_trampoline_struct { + void * code_pointer; /* Pointer to ffi_closure_ASM */ + void * toc; /* TOC */ + void * static_chain; /* Pointer to closure */ +}; +#endif +#endif + +#endif diff --git a/libffi/src/powerpc/linux64.S b/libffi/src/powerpc/linux64.S new file mode 100644 index 000000000..57b56cbb2 --- /dev/null +++ b/libffi/src/powerpc/linux64.S @@ -0,0 +1,187 @@ +/* ----------------------------------------------------------------------- + sysv.h - Copyright (c) 2003 Jakub Jelinek + Copyright (c) 2008 Red Hat, Inc. + + PowerPC64 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 +#include + +#ifdef __powerpc64__ + .hidden ffi_call_LINUX64, .ffi_call_LINUX64 + .globl ffi_call_LINUX64, .ffi_call_LINUX64 + .section ".opd","aw" + .align 3 +ffi_call_LINUX64: + .quad .ffi_call_LINUX64,.TOC.@tocbase,0 + .size ffi_call_LINUX64,24 + .type .ffi_call_LINUX64,@function + .text +.ffi_call_LINUX64: +.LFB1: + mflr %r0 + std %r28, -32(%r1) + std %r29, -24(%r1) + std %r30, -16(%r1) + std %r31, -8(%r1) + std %r0, 16(%r1) + + mr %r28, %r1 /* our AP. */ +.LCFI0: + stdux %r1, %r1, %r4 + mr %r31, %r5 /* flags, */ + mr %r30, %r6 /* rvalue, */ + mr %r29, %r7 /* function address. */ + std %r2, 40(%r1) + + /* Call ffi_prep_args64. */ + mr %r4, %r1 + bl .ffi_prep_args64 + + ld %r0, 0(%r29) + ld %r2, 8(%r29) + ld %r11, 16(%r29) + + /* 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 %r0 + /* Load all those argument registers. */ + ld %r3, -32-(8*8)(%r28) + ld %r4, -32-(7*8)(%r28) + ld %r5, -32-(6*8)(%r28) + ld %r6, -32-(5*8)(%r28) + bf- 5, 1f + ld %r7, -32-(4*8)(%r28) + ld %r8, -32-(3*8)(%r28) + ld %r9, -32-(2*8)(%r28) + ld %r10, -32-(1*8)(%r28) +1: + + /* Load all the FP registers. */ + bf- 6, 2f + lfd %f1, -32-(21*8)(%r28) + lfd %f2, -32-(20*8)(%r28) + lfd %f3, -32-(19*8)(%r28) + lfd %f4, -32-(18*8)(%r28) + lfd %f5, -32-(17*8)(%r28) + lfd %f6, -32-(16*8)(%r28) + lfd %f7, -32-(15*8)(%r28) + lfd %f8, -32-(14*8)(%r28) + lfd %f9, -32-(13*8)(%r28) + lfd %f10, -32-(12*8)(%r28) + lfd %f11, -32-(11*8)(%r28) + lfd %f12, -32-(10*8)(%r28) + lfd %f13, -32-(9*8)(%r28) +2: + + /* Make the call. */ + bctrl + + /* This must follow the call immediately, the unwinder + uses this to find out if r2 has been saved or not. */ + ld %r2, 40(%r1) + + /* Now, deal with the return value. */ + mtcrf 0x01, %r31 + bt- 30, .Ldone_return_value + bt- 29, .Lfp_return_value + std %r3, 0(%r30) + /* Fall through... */ + +.Ldone_return_value: + /* Restore the registers we used and return. */ + mr %r1, %r28 + ld %r0, 16(%r28) + ld %r28, -32(%r1) + mtlr %r0 + ld %r29, -24(%r1) + ld %r30, -16(%r1) + ld %r31, -8(%r1) + blr + +.Lfp_return_value: + bf 28, .Lfloat_return_value + stfd %f1, 0(%r30) + mtcrf 0x02, %r31 /* cr6 */ + bf 27, .Ldone_return_value + stfd %f2, 8(%r30) + b .Ldone_return_value +.Lfloat_return_value: + stfs %f1, 0(%r30) + b .Ldone_return_value +.LFE1: + .long 0 + .byte 0,12,0,1,128,4,0,0 + .size .ffi_call_LINUX64,.-.ffi_call_LINUX64 + + .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 + .ascii "zR\0" # CIE Augmentation + .uleb128 0x1 # CIE Code Alignment Factor + .sleb128 -8 # CIE Data Alignment Factor + .byte 0x41 # CIE RA Column + .uleb128 0x1 # Augmentation size + .byte 0x14 # FDE Encoding (pcrel udata8) + .byte 0xc # DW_CFA_def_cfa + .uleb128 0x1 + .uleb128 0x0 + .align 3 +.LECIE1: +.LSFDE1: + .4byte .LEFDE1-.LASFDE1 # FDE Length +.LASFDE1: + .4byte .LASFDE1-.Lframe1 # FDE CIE offset + .8byte .LFB1-. # FDE initial location + .8byte .LFE1-.LFB1 # FDE address range + .uleb128 0x0 # Augmentation size + .byte 0x2 # DW_CFA_advance_loc1 + .byte .LCFI0-.LFB1 + .byte 0xd # DW_CFA_def_cfa_register + .uleb128 0x1c + .byte 0x11 # DW_CFA_offset_extended_sf + .uleb128 0x41 + .sleb128 -2 + .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 + .align 3 +.LEFDE1: +#endif + +#if defined __ELF__ && defined __linux__ + .section .note.GNU-stack,"",@progbits +#endif diff --git a/libffi/src/powerpc/linux64_closure.S b/libffi/src/powerpc/linux64_closure.S new file mode 100644 index 000000000..f7aa2c98e --- /dev/null +++ b/libffi/src/powerpc/linux64_closure.S @@ -0,0 +1,236 @@ +/* ----------------------------------------------------------------------- + sysv.h - Copyright (c) 2003 Jakub Jelinek + Copyright (c) 2008 Red Hat, Inc. + + PowerPC64 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 +#include + + .file "linux64_closure.S" + +#ifdef __powerpc64__ + FFI_HIDDEN (ffi_closure_LINUX64) + FFI_HIDDEN (.ffi_closure_LINUX64) + .globl ffi_closure_LINUX64, .ffi_closure_LINUX64 + .section ".opd","aw" + .align 3 +ffi_closure_LINUX64: + .quad .ffi_closure_LINUX64,.TOC.@tocbase,0 + .size ffi_closure_LINUX64,24 + .type .ffi_closure_LINUX64,@function + .text +.ffi_closure_LINUX64: +.LFB1: + # save general regs into parm save area + std %r3, 48(%r1) + std %r4, 56(%r1) + std %r5, 64(%r1) + std %r6, 72(%r1) + mflr %r0 + + std %r7, 80(%r1) + std %r8, 88(%r1) + std %r9, 96(%r1) + std %r10, 104(%r1) + std %r0, 16(%r1) + + # mandatory 48 bytes special reg save area + 64 bytes parm save area + # + 16 bytes retval area + 13*8 bytes fpr save area + round to 16 + stdu %r1, -240(%r1) +.LCFI0: + + # next save fpr 1 to fpr 13 + stfd %f1, 128+(0*8)(%r1) + stfd %f2, 128+(1*8)(%r1) + stfd %f3, 128+(2*8)(%r1) + stfd %f4, 128+(3*8)(%r1) + stfd %f5, 128+(4*8)(%r1) + stfd %f6, 128+(5*8)(%r1) + stfd %f7, 128+(6*8)(%r1) + stfd %f8, 128+(7*8)(%r1) + stfd %f9, 128+(8*8)(%r1) + stfd %f10, 128+(9*8)(%r1) + stfd %f11, 128+(10*8)(%r1) + stfd %f12, 128+(11*8)(%r1) + stfd %f13, 128+(12*8)(%r1) + + # set up registers for the routine that actually does the work + # get the context pointer from the trampoline + mr %r3, %r11 + + # now load up the pointer to the result storage + addi %r4, %r1, 112 + + # now load up the pointer to the parameter save area + # in the previous frame + addi %r5, %r1, 240 + 48 + + # now load up the pointer to the saved fpr registers */ + addi %r6, %r1, 128 + + # make the call + bl .ffi_closure_helper_LINUX64 +.Lret: + + # now r3 contains the return type + # so use it to look up in a table + # so we know how to deal with each type + + # look up the proper starting point in table + # by using return type as offset + mflr %r4 # move address of .Lret to r4 + sldi %r3, %r3, 4 # now multiply return type by 16 + addi %r4, %r4, .Lret_type0 - .Lret + ld %r0, 240+16(%r1) + add %r3, %r3, %r4 # add contents of table to table address + mtctr %r3 + bctr # jump to it + +# Each of the ret_typeX code fragments has to be exactly 16 bytes long +# (4 instructions). For cache effectiveness we align to a 16 byte boundary +# first. + .align 4 + +.Lret_type0: +# case FFI_TYPE_VOID + mtlr %r0 + addi %r1, %r1, 240 + blr + nop +# case FFI_TYPE_INT + lwa %r3, 112+4(%r1) + mtlr %r0 + addi %r1, %r1, 240 + blr +# case FFI_TYPE_FLOAT + lfs %f1, 112+0(%r1) + mtlr %r0 + addi %r1, %r1, 240 + blr +# case FFI_TYPE_DOUBLE + lfd %f1, 112+0(%r1) + mtlr %r0 + addi %r1, %r1, 240 + blr +# case FFI_TYPE_LONGDOUBLE + lfd %f1, 112+0(%r1) + mtlr %r0 + lfd %f2, 112+8(%r1) + b .Lfinish +# case FFI_TYPE_UINT8 + lbz %r3, 112+7(%r1) + mtlr %r0 + addi %r1, %r1, 240 + blr +# case FFI_TYPE_SINT8 + lbz %r3, 112+7(%r1) + extsb %r3,%r3 + mtlr %r0 + b .Lfinish +# case FFI_TYPE_UINT16 + lhz %r3, 112+6(%r1) + mtlr %r0 +.Lfinish: + addi %r1, %r1, 240 + blr +# case FFI_TYPE_SINT16 + lha %r3, 112+6(%r1) + mtlr %r0 + addi %r1, %r1, 240 + blr +# case FFI_TYPE_UINT32 + lwz %r3, 112+4(%r1) + mtlr %r0 + addi %r1, %r1, 240 + blr +# case FFI_TYPE_SINT32 + lwa %r3, 112+4(%r1) + mtlr %r0 + addi %r1, %r1, 240 + blr +# case FFI_TYPE_UINT64 + ld %r3, 112+0(%r1) + mtlr %r0 + addi %r1, %r1, 240 + blr +# case FFI_TYPE_SINT64 + ld %r3, 112+0(%r1) + mtlr %r0 + addi %r1, %r1, 240 + blr +# case FFI_TYPE_STRUCT + mtlr %r0 + addi %r1, %r1, 240 + blr + nop +# case FFI_TYPE_POINTER + ld %r3, 112+0(%r1) + mtlr %r0 + addi %r1, %r1, 240 + blr +# esac +.LFE1: + .long 0 + .byte 0,12,0,1,128,0,0,0 + .size .ffi_closure_LINUX64,.-.ffi_closure_LINUX64 + + .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 + .ascii "zR\0" # CIE Augmentation + .uleb128 0x1 # CIE Code Alignment Factor + .sleb128 -8 # CIE Data Alignment Factor + .byte 0x41 # CIE RA Column + .uleb128 0x1 # Augmentation size + .byte 0x14 # FDE Encoding (pcrel udata8) + .byte 0xc # DW_CFA_def_cfa + .uleb128 0x1 + .uleb128 0x0 + .align 3 +.LECIE1: +.LSFDE1: + .4byte .LEFDE1-.LASFDE1 # FDE Length +.LASFDE1: + .4byte .LASFDE1-.Lframe1 # FDE CIE offset + .8byte .LFB1-. # FDE initial location + .8byte .LFE1-.LFB1 # FDE address range + .uleb128 0x0 # Augmentation size + .byte 0x2 # DW_CFA_advance_loc1 + .byte .LCFI0-.LFB1 + .byte 0xe # DW_CFA_def_cfa_offset + .uleb128 240 + .byte 0x11 # DW_CFA_offset_extended_sf + .uleb128 0x41 + .sleb128 -2 + .align 3 +.LEFDE1: +#endif + +#if defined __ELF__ && defined __linux__ + .section .note.GNU-stack,"",@progbits +#endif diff --git a/libffi/src/powerpc/ppc_closure.S b/libffi/src/powerpc/ppc_closure.S new file mode 100644 index 000000000..56f7d1af2 --- /dev/null +++ b/libffi/src/powerpc/ppc_closure.S @@ -0,0 +1,327 @@ +/* ----------------------------------------------------------------------- + sysv.h - Copyright (c) 2003 Jakub Jelinek + Copyright (c) 2008 Red Hat, 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 +#include +#include + + .file "ppc_closure.S" + +#ifndef __powerpc64__ + +ENTRY(ffi_closure_SYSV) +.LFB1: + stwu %r1,-144(%r1) +.LCFI0: + mflr %r0 +.LCFI1: + stw %r0,148(%r1) + +# we want to build up an areas for the parameters passed +# in registers (both floating point and integer) + + # so first save gpr 3 to gpr 10 (aligned to 4) + stw %r3, 16(%r1) + stw %r4, 20(%r1) + stw %r5, 24(%r1) + stw %r6, 28(%r1) + stw %r7, 32(%r1) + stw %r8, 36(%r1) + 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) + stfd %f3, 64(%r1) + stfd %f4, 72(%r1) + stfd %f5, 80(%r1) + 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 + mr %r3,%r11 + + # now load up the pointer to the result storage + addi %r4,%r1,112 + + # now load up the pointer to the saved gpr registers + addi %r5,%r1,16 + + # now load up the pointer to the saved fpr registers */ + addi %r6,%r1,48 + + # now load up the pointer to the outgoing parameter + # stack in the previous frame + # i.e. the previous frame pointer + 8 + addi %r7,%r1,152 + + # make the call + bl ffi_closure_helper_SYSV@local +.Lret: + # now r3 contains the return type + # so use it to look up in a table + # so we know how to deal with each type + + # look up the proper starting point in table + # by using return type as offset + + mflr %r4 # move address of .Lret to r4 + slwi %r3,%r3,4 # now multiply return type by 16 + addi %r4, %r4, .Lret_type0 - .Lret + lwz %r0,148(%r1) + add %r3,%r3,%r4 # add contents of table to table address + mtctr %r3 + bctr # jump to it +.LFE1: + +# Each of the ret_typeX code fragments has to be exactly 16 bytes long +# (4 instructions). For cache effectiveness we align to a 16 byte boundary +# first. + .align 4 +# case FFI_TYPE_VOID +.Lret_type0: + mtlr %r0 + addi %r1,%r1,144 + blr + nop + +# case FFI_TYPE_INT + lwz %r3,112+0(%r1) + mtlr %r0 +.Lfinish: + addi %r1,%r1,144 + blr + +# case FFI_TYPE_FLOAT + lfs %f1,112+0(%r1) + mtlr %r0 + addi %r1,%r1,144 + blr + +# case FFI_TYPE_DOUBLE + lfd %f1,112+0(%r1) + mtlr %r0 + addi %r1,%r1,144 + blr + +# case FFI_TYPE_LONGDOUBLE + lfd %f1,112+0(%r1) + lfd %f2,112+8(%r1) + mtlr %r0 + b .Lfinish + +# case FFI_TYPE_UINT8 + lbz %r3,112+3(%r1) + mtlr %r0 + addi %r1,%r1,144 + blr + +# case FFI_TYPE_SINT8 + lbz %r3,112+3(%r1) + extsb %r3,%r3 + mtlr %r0 + b .Lfinish + +# case FFI_TYPE_UINT16 + lhz %r3,112+2(%r1) + mtlr %r0 + addi %r1,%r1,144 + blr + +# case FFI_TYPE_SINT16 + lha %r3,112+2(%r1) + mtlr %r0 + addi %r1,%r1,144 + blr + +# case FFI_TYPE_UINT32 + lwz %r3,112+0(%r1) + mtlr %r0 + addi %r1,%r1,144 + blr + +# case FFI_TYPE_SINT32 + lwz %r3,112+0(%r1) + mtlr %r0 + addi %r1,%r1,144 + blr + +# case FFI_TYPE_UINT64 + lwz %r3,112+0(%r1) + lwz %r4,112+4(%r1) + mtlr %r0 + b .Lfinish + +# case FFI_TYPE_SINT64 + lwz %r3,112+0(%r1) + lwz %r4,112+4(%r1) + mtlr %r0 + b .Lfinish + +# case FFI_TYPE_STRUCT + mtlr %r0 + addi %r1,%r1,144 + blr + nop + +# case FFI_TYPE_POINTER + lwz %r3,112+0(%r1) + mtlr %r0 + 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) + mtlr %r0 + addi %r1,%r1,144 + blr + +# case FFI_SYSV_TYPE_SMALL_STRUCT + 2. Two byte struct. + lhz %r3,112+0(%r1) + mtlr %r0 + addi %r1,%r1,144 + blr + +# case FFI_SYSV_TYPE_SMALL_STRUCT + 3. Three byte struct. + lwz %r3,112+0(%r1) + srwi %r3,%r3,8 + mtlr %r0 + b .Lfinish + +# case FFI_SYSV_TYPE_SMALL_STRUCT + 4. Four byte struct. + lwz %r3,112+0(%r1) + mtlr %r0 + addi %r1,%r1,144 + blr + +# case FFI_SYSV_TYPE_SMALL_STRUCT + 5. Five byte struct. + lwz %r3,112+0(%r1) + lwz %r4,112+4(%r1) + li %r5,24 + b .Lstruct567 + +# case FFI_SYSV_TYPE_SMALL_STRUCT + 6. Six byte struct. + lwz %r3,112+0(%r1) + lwz %r4,112+4(%r1) + li %r5,16 + b .Lstruct567 + +# case FFI_SYSV_TYPE_SMALL_STRUCT + 7. Seven byte struct. + lwz %r3,112+0(%r1) + lwz %r4,112+4(%r1) + li %r5,8 + b .Lstruct567 + +# case FFI_SYSV_TYPE_SMALL_STRUCT + 8. Eight byte struct. + lwz %r3,112+0(%r1) + lwz %r4,112+4(%r1) + mtlr %r0 + b .Lfinish + +.Lstruct567: + subfic %r6,%r5,32 + srw %r4,%r4,%r5 + slw %r6,%r3,%r6 + srw %r3,%r3,%r5 + or %r4,%r6,%r4 + mtlr %r0 + 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 +.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 0xe # DW_CFA_def_cfa_offset + .uleb128 144 + .byte 0x4 # DW_CFA_advance_loc4 + .4byte .LCFI1-.LCFI0 + .byte 0x11 # DW_CFA_offset_extended_sf + .uleb128 0x41 + .sleb128 -1 + .align 2 +.LEFDE1: + +#endif + +#if defined __ELF__ && defined __linux__ + .section .note.GNU-stack,"",@progbits +#endif diff --git a/libffi/src/powerpc/sysv.S b/libffi/src/powerpc/sysv.S new file mode 100644 index 000000000..96ea22b0b --- /dev/null +++ b/libffi/src/powerpc/sysv.S @@ -0,0 +1,219 @@ +/* ----------------------------------------------------------------------- + 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 +#include +#include + +#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): + extrwi %r6,%r31,2,19 /* number of bytes padding = shift/8 */ + mtcrf 0x02,%r31 /* copy flags to cr[24:27] (cr6) */ + extrwi %r5,%r31,5,19 /* r5 <- number of bits of padding */ + subfic %r6,%r6,4 /* r6 <- number of useful bytes in r3 */ + bf- 25,L(done_return_value) /* struct in r3 ? if not, done. */ +/* smst_one_register: */ + slw %r3,%r3,%r5 /* Left-justify value in r3 */ + mtxer %r6 /* move byte count to XER ... */ + stswx %r3,0,%r30 /* ... and store that many bytes */ + bf+ 26,L(done_return_value) /* struct in r3:r4 ? */ + add %r6,%r6,%r30 /* adjust pointer */ + stswi %r4,%r6,4 /* store last four bytes */ + 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 -- cgit v1.2.3