diff --git a/bridges/source/cpp_uno/gcc3_linux_arm/armhelper.S b/bridges/source/cpp_uno/gcc3_linux_arm/armhelper.S index d5faf15..27148dc 100644 --- a/bridges/source/cpp_uno/gcc3_linux_arm/armhelper.S +++ b/bridges/source/cpp_uno/gcc3_linux_arm/armhelper.S @@ -10,6 +10,17 @@ # define UNWIND @ #endif +@ If the VFP ABI variant (armhf in Debian/Ubuntu) is used, an additional extra 64 bytes +@ are taken up on the stack (the equivalent of the 8 double precision VFP registers) + +#ifdef __ARM_PCS_VFP +# define PAD 80 +# define DISCARDED 84 +#else +# define PAD 16 +# define DISCARDED 20 +#endif + .file "armhelper.s" .text .align 4 @@ -19,9 +30,12 @@ privateSnippetExecutor: UNWIND .fnstart @ start of unwinder entry stmfd sp!, {r0-r3} @ follow other parameters on stack - UNWIND .pad #16 @ throw this data away on exception mov r0, ip @ r0 points to functionoffset/vtable mov r1, sp @ r1 points to this and params +#ifdef __ARM_PCS_VFP + vpush {d0-d7} @ floating point parameter on stack +#endif + UNWIND .pad #PAD @ throw this data away on exception @ (see cppuno.cxx:codeSnippet()) stmfd sp!, {r4,lr} @ save return address @ (r4 pushed to preserve stack alignment) @@ -30,7 +44,7 @@ privateSnippetExecutor: bl cpp_vtable_call(PLT) add sp, sp, #4 @ no need to restore r4 (we didn't touch it) - ldr pc, [sp], #20 @ return, discarding function arguments + ldr pc, [sp], #DISCARDED @ return, discarding function arguments UNWIND .fnend @ end of unwinder entry diff --git a/bridges/source/cpp_uno/gcc3_linux_arm/cpp2uno.cxx b/bridges/source/cpp_uno/gcc3_linux_arm/cpp2uno.cxx index d347aa0..dd0f2f7 100644 --- a/bridges/source/cpp_uno/gcc3_linux_arm/cpp2uno.cxx +++ b/bridges/source/cpp_uno/gcc3_linux_arm/cpp2uno.cxx @@ -69,6 +69,10 @@ namespace char * pTopStack = (char *)(pCallStack + 0); char * pCppStack = pTopStack; +#ifdef __ARM_PCS_VFP + int sc = 0, dc = 0; + char * pFloatArgs = (char *)(pCppStack - 64); +#endif // return typelib_TypeDescription * pReturnTypeDescr = 0; if (pReturnTypeRef) @@ -125,7 +129,9 @@ namespace { case typelib_TypeClass_HYPER: case typelib_TypeClass_UNSIGNED_HYPER: +#ifndef __ARM_PCS_VFP case typelib_TypeClass_DOUBLE: +#endif if ((pCppStack - pTopStack) % 8) pCppStack+=sizeof(sal_Int32); //align to 8 break; default: @@ -133,13 +139,37 @@ namespace } #endif +// For armhf we get the floating point arguments from a different area of the stack +#ifdef __ARM_PCS_VFP + switch (pParamTypeDescr->eTypeClass) + { + case typelib_TypeClass_FLOAT: + pCppArgs[nPos] = pFloatArgs; + pUnoArgs[nPos] = pFloatArgs; + pFloatArgs += sizeof(float); + break; + case typelib_TypeClass_DOUBLE: + if ((pFloatArgs - pTopStack) % 8) pFloatArgs+=sizeof(float); //align to 8 + pCppArgs[nPos] = pFloatArgs; + pUnoArgs[nPos] = pFloatArgs; + pFloatArgs += sizeof(double); + break; + default: + pCppArgs[nPos] = pCppStack; + pUnoArgs[nPos] = pCppStack; + } +#else pCppArgs[nPos] = pCppStack; pUnoArgs[nPos] = pCppStack; +#endif + switch (pParamTypeDescr->eTypeClass) { case typelib_TypeClass_HYPER: case typelib_TypeClass_UNSIGNED_HYPER: +#ifndef __ARM_PCS_VFP case typelib_TypeClass_DOUBLE: +#endif pCppStack += sizeof(sal_Int32); // extra long break; default: @@ -179,6 +209,12 @@ namespace TYPELIB_DANGER_RELEASE( pParamTypeDescr ); } } +#ifdef __ARM_PCS_VFP + // Output parameters and non-floating point parameters use the stack + if (rParam.bOut || + ((pParamTypeDescr->eTypeClass != typelib_TypeClass_DOUBLE) + && (pParamTypeDescr->eTypeClass != typelib_TypeClass_FLOAT))) +#endif pCppStack += sizeof(sal_Int32); // standard parameter length } diff --git a/bridges/source/cpp_uno/gcc3_linux_arm/share.hxx b/bridges/source/cpp_uno/gcc3_linux_arm/share.hxx index f7a85ba..da36c75 100644 --- a/bridges/source/cpp_uno/gcc3_linux_arm/share.hxx +++ b/bridges/source/cpp_uno/gcc3_linux_arm/share.hxx @@ -93,7 +93,7 @@ namespace CPPU_CURRENT_NAMESPACE namespace arm { - enum armlimits { MAX_GPR_REGS = 4 }; + enum armlimits { MAX_GPR_REGS = 4, MAX_FPR_REGS = 8 }; bool return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef ); } diff --git a/bridges/source/cpp_uno/gcc3_linux_arm/uno2cpp.cxx b/bridges/source/cpp_uno/gcc3_linux_arm/uno2cpp.cxx index fc8a9e2..ebd7128 100644 --- a/bridges/source/cpp_uno/gcc3_linux_arm/uno2cpp.cxx +++ b/bridges/source/cpp_uno/gcc3_linux_arm/uno2cpp.cxx @@ -208,7 +208,9 @@ void callVirtualMethod( sal_uInt32 *pStack, sal_uInt32 nStack, sal_uInt32 *pGPR, - sal_uInt32 nGPR) __attribute__((noinline)); + sal_uInt32 nGPR, + double *pFPR, + sal_uInt32 nFPR) __attribute__((noinline)); void callVirtualMethod( void * pThis, @@ -218,7 +220,9 @@ void callVirtualMethod( sal_uInt32 *pStack, sal_uInt32 nStack, sal_uInt32 *pGPR, - sal_uInt32 nGPR) + sal_uInt32 nGPR, + double *pFPR, + sal_uInt32 nFPR) { // never called if (! pThis) @@ -235,24 +239,40 @@ void callVirtualMethod( // Should not happen, but... if ( nGPR > arm::MAX_GPR_REGS ) nGPR = arm::MAX_GPR_REGS; + if ( nFPR > arm::MAX_FPR_REGS ) + nFPR = arm::MAX_FPR_REGS; sal_uInt32 pMethod = *((sal_uInt32*)pThis); pMethod += 4 * nVtableIndex; pMethod = *((sal_uInt32 *)pMethod); - typedef void (*FunctionCall )( sal_uInt32, sal_uInt32, sal_uInt32, sal_uInt32); - FunctionCall pFunc = (FunctionCall)pMethod; - - (*pFunc)(pGPR[0], pGPR[1], pGPR[2], pGPR[3]); - + //Return registers sal_uInt32 r0; sal_uInt32 r1; + float s0; + double d0; - // get return value __asm__ __volatile__ ( + //Fill in general purpose register arguments + "ldr r4, %[pgpr]\n\t" + "ldmia r4, {r0-r3}\n\t" + + //Fill in VFP register arguments as double precision values + "ldr r4, %[pfpr]\n\t" + "vldmia r4, {d0-d7}\n\t" + + //Make the call + "ldr r5, %[pmethod]\n\t" + "blx r5\n\t" + + //Fill in return values "mov %0, r0\n\t" "mov %1, r1\n\t" - : "=r" (r0), "=r" (r1) : ); + "fsts s0, %2\n\t" + "fstd d0, %3\n\t" + : "=r" (r0), "=r" (r1), "=m" (s0), "=m" (d0) + : [pmethod]"m" (pMethod), [pgpr]"m" (pGPR), [pfpr]"m" (pFPR) + : "r4", "r5"); MapReturn(r0, r1, pReturnType, (sal_uInt32*)pRegisterReturn); } @@ -290,12 +310,49 @@ void callVirtualMethod( INSERT_INT32( ((sal_uInt32*)pSV)+1, nr, pGPR, pDS ) #endif +#ifdef __ARM_PCS_VFP +// Since single and double arguments share the same register bank the filling of the +// registers is not always linear. Single values go to the first available single register, +// while doubles need to have an 8 byte alignment, so only go into double registers starting +// at every other single register. For ex a float, double, float sequence will fill registers +// s0, d1, and s1, actually corresponding to the linear order s0,s1, d1. +// +// These use the single/double register array and counters and ignore the pGPR argument +// nSR and nDR are the number of single and double precision registers that are no longer +// available +#define INSERT_FLOAT( pSV, nr, pGPR, pDS ) \ + if (nSR % 2 == 0) {\ + nSR = 2*nDR; \ + }\ + if ( nSR < arm::MAX_FPR_REGS*2 ) {\ + pSPR[nSR++] = *reinterpret_cast( pSV ); \ + if ((nSR % 2 == 1) && (nSR > 2*nDR)) {\ + nDR++; \ + }\ + }\ + else \ + {\ + *pDS++ = *reinterpret_cast( pSV );\ + } +#define INSERT_DOUBLE( pSV, nr, pGPR, pDS, pStart ) \ + if ( nDR < arm::MAX_FPR_REGS ) { \ + pFPR[nDR++] = *reinterpret_cast( pSV ); \ + }\ + else\ + {\ + if ( (pDS - pStart) % 2) \ + { \ + ++pDS; \ + } \ + *pDS++ = *reinterpret_cast( pSV );\ + } +#else #define INSERT_FLOAT( pSV, nr, pFPR, pDS ) \ INSERT_INT32( pSV, nr, pGPR, pDS ) #define INSERT_DOUBLE( pSV, nr, pFPR, pDS, pStart ) \ INSERT_INT64( pSV, nr, pGPR, pDS, pStart ) - +#endif #define INSERT_INT16( pSV, nr, pGPR, pDS ) \ if ( nr < arm::MAX_GPR_REGS ) \ pGPR[nr++] = *reinterpret_cast( pSV ); \ @@ -325,6 +382,11 @@ static void cpp_call( sal_uInt32 pGPR[arm::MAX_GPR_REGS]; sal_uInt32 nGPR = 0; + double pFPR[arm::MAX_FPR_REGS]; + float *pSPR = reinterpret_cast< float *>(&pFPR); + sal_uInt32 nSR = 0; + sal_uInt32 nDR = 0; + // return typelib_TypeDescription * pReturnTypeDescr = 0; TYPELIB_DANGER_GET( &pReturnTypeDescr, pReturnTypeRef ); @@ -456,7 +518,8 @@ static void cpp_call( pCppReturn, pReturnTypeRef, pStackStart, (pStack - pStackStart), - pGPR, nGPR); + pGPR, nGPR, + pFPR, nDR); // NO exception occurred... *ppUnoExc = 0;