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..a11c94e 100644 --- a/bridges/source/cpp_uno/gcc3_linux_arm/cpp2uno.cxx +++ b/bridges/source/cpp_uno/gcc3_linux_arm/cpp2uno.cxx @@ -68,7 +68,8 @@ namespace // pCallStack: ret, [return ptr], this, params char * pTopStack = (char *)(pCallStack + 0); char * pCppStack = pTopStack; - + int sc = 0, dc = 0; + char * pFloatArgs = (char *)(pCppStack - 64); // return typelib_TypeDescription * pReturnTypeDescr = 0; if (pReturnTypeRef) @@ -117,6 +118,7 @@ namespace typelib_TypeDescription * pParamTypeDescr = 0; TYPELIB_DANGER_GET( &pParamTypeDescr, rParam.pTypeRef ); + if (!rParam.bOut && bridges::cpp_uno::shared::isSimpleType( pParamTypeDescr )) { @@ -125,7 +127,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 +137,29 @@ namespace } #endif - pCppArgs[nPos] = pCppStack; - pUnoArgs[nPos] = pCppStack; +// For armhf we get the floating point arguments from a different area of the stack +#ifdef __ARM_PCS_VFP + if (pParamTypeDescr->eTypeClass == typelib_TypeClass_FLOAT) + { + pCppArgs[nPos] = pUnoArgs[nPos] = pFloatArgs; + pFloatArgs += sizeof(float); + } else + if (pParamTypeDescr->eTypeClass == typelib_TypeClass_DOUBLE) + { + if ((pFloatArgs - pTopStack) % 8) pFloatArgs+=sizeof(float); //align to 8 + pCppArgs[nPos] = pUnoArgs[nPos] = pFloatArgs; + pFloatArgs += sizeof(double); + } else +#endif + pCppArgs[nPos] = pUnoArgs[nPos] = pCppStack; + 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 +199,9 @@ namespace TYPELIB_DANGER_RELEASE( pParamTypeDescr ); } } +#ifdef __ARM_PCS_VFP + if (rParam.bOut || (pParamTypeDescr->eTypeClass != typelib_TypeClass_DOUBLE) && (pParamTypeDescr->eTypeClass != typelib_TypeClass_FLOAT)) +#endif pCppStack += sizeof(sal_Int32); // standard parameter length } @@ -223,8 +246,7 @@ namespace if (pParams[nIndex].bOut) // inout/out { // convert and assign - uno_destructData( pCppArgs[nIndex], pParamTypeDescr, - cpp_release ); + uno_destructData( pCppArgs[nIndex], pParamTypeDescr, cpp_release ); uno_copyAndConvertData( pCppArgs[nIndex], pUnoArgs[nIndex], pParamTypeDescr, pThis->getBridge()->getUno2Cpp() ); } @@ -314,6 +336,7 @@ namespace if (pTypeDescr->pMapMemberIndexToFunctionIndex[nMemberPos] == nFunctionIndex) { + // is GET method eRet = cpp2uno_call( pCppI, aMemberDescr.get(), 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..41df8a1 100644 --- a/bridges/source/cpp_uno/gcc3_linux_arm/uno2cpp.cxx +++ b/bridges/source/cpp_uno/gcc3_linux_arm/uno2cpp.cxx @@ -131,6 +131,21 @@ namespace arm return false; } +#ifdef __ARM_PCS_VFP + bool is_float_only_struct(const typelib_TypeDescription * type) + { + bool is_float_only = true; + const typelib_CompoundTypeDescription * p + = reinterpret_cast< const typelib_CompoundTypeDescription * >(type); + for (sal_Int32 i = 0; i < p->nMembers; ++i) + { + if (p->ppTypeRefs[i]->eTypeClass != typelib_TypeClass_FLOAT && + p->ppTypeRefs[i]->eTypeClass != typelib_TypeClass_DOUBLE) + return false; + } + return true; + } +#endif bool return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef ) { if (bridges::cpp_uno::shared::isSimpleType(pTypeRef)) @@ -143,6 +158,13 @@ namespace arm //A Composite Type not larger than 4 bytes is returned in r0 bool bRet = pTypeDescr->nSize > 4 || is_complex_struct(pTypeDescr); +#ifdef __ARM_PCS_VFP + // In the VFP ABI, structs with only float/double values that fit in + // 16 bytes are returned in registers + if( pTypeDescr->nSize <= 16 && is_float_only_struct(pTypeDescr)) + bRet = false; +#endif + TYPELIB_DANGER_RELEASE( pTypeDescr ); return bRet; } @@ -208,7 +230,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 +242,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 +261,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__ ( - "mov %0, r0\n\t" - "mov %1, r1\n\t" - : "=r" (r0), "=r" (r1) : ); + //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 %[r0], r0\n\t" + "mov %[r1], r1\n\t" + "fsts s0, %[s0]\n\t" + "fstd d0, %[d0]\n\t" + : [r0]"=r" (r0), [r1]"=r" (r1), [s0]"=m" (s0), [d0]"=m" (d0) + : [pmethod]"m" (pMethod), [pgpr]"m" (pGPR), [pfpr]"m" (pFPR) + : "r4", "r5"); MapReturn(r0, r1, pReturnType, (sal_uInt32*)pRegisterReturn); } @@ -290,12 +332,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 +404,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 +540,8 @@ static void cpp_call( pCppReturn, pReturnTypeRef, pStackStart, (pStack - pStackStart), - pGPR, nGPR); + pGPR, nGPR, + pFPR, nDR); // NO exception occurred... *ppUnoExc = 0;