summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bridges/source/cpp_uno/gcc3_linux_arm/armhelper.S18
-rw-r--r--bridges/source/cpp_uno/gcc3_linux_arm/cpp2uno.cxx34
-rw-r--r--bridges/source/cpp_uno/gcc3_linux_arm/share.hxx2
-rw-r--r--bridges/source/cpp_uno/gcc3_linux_arm/uno2cpp.cxx105
4 files changed, 142 insertions, 17 deletions
diff --git a/bridges/source/cpp_uno/gcc3_linux_arm/armhelper.S b/bridges/source/cpp_uno/gcc3_linux_arm/armhelper.S
index d5faf15eed6f..27148dc0278c 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 d347aa0ad266..07bdea17c2e4 100644
--- a/bridges/source/cpp_uno/gcc3_linux_arm/cpp2uno.cxx
+++ b/bridges/source/cpp_uno/gcc3_linux_arm/cpp2uno.cxx
@@ -69,6 +69,9 @@ namespace
char * pTopStack = (char *)(pCallStack + 0);
char * pCppStack = pTopStack;
+#ifdef __ARM_PCS_VFP
+ char * pFloatArgs = (char *)(pCppStack - 64);
+#endif
// return
typelib_TypeDescription * pReturnTypeDescr = 0;
if (pReturnTypeRef)
@@ -125,7 +128,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 +138,31 @@ namespace
}
#endif
- pCppArgs[nPos] = pCppStack;
- pUnoArgs[nPos] = pCppStack;
+// For armhf we get the floating point arguments from a different area of the stack
+// TODO: deal with functions with more than 8 floating point args that need to overflow
+// to the stack. Find such an UNO API to try on.
+#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 +202,13 @@ namespace
TYPELIB_DANGER_RELEASE( pParamTypeDescr );
}
}
+#ifdef __ARM_PCS_VFP
+ // use the stack for output parameters or non floating point values
+ 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 f7a85bad535d..da36c7592e79 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 fc8a9e2f4d2e..97eff5143da6 100644
--- a/bridges/source/cpp_uno/gcc3_linux_arm/uno2cpp.cxx
+++ b/bridges/source/cpp_uno/gcc3_linux_arm/uno2cpp.cxx
@@ -131,6 +131,20 @@ namespace arm
return false;
}
+#ifdef __ARM_PCS_VFP
+ bool is_float_only_struct(const typelib_TypeDescription * type)
+ {
+ 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 +157,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 +229,8 @@ void callVirtualMethod(
sal_uInt32 *pStack,
sal_uInt32 nStack,
sal_uInt32 *pGPR,
- sal_uInt32 nGPR) __attribute__((noinline));
+ sal_uInt32 nGPR,
+ double *pFPR) __attribute__((noinline));
void callVirtualMethod(
void * pThis,
@@ -218,7 +240,8 @@ void callVirtualMethod(
sal_uInt32 *pStack,
sal_uInt32 nStack,
sal_uInt32 *pGPR,
- sal_uInt32 nGPR)
+ sal_uInt32 nGPR,
+ double *pFPR)
{
// never called
if (! pThis)
@@ -240,19 +263,30 @@ void callVirtualMethod(
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;
- // 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"
+
+#ifdef __ARM_PCS_VFP
+ //Fill in VFP register arguments as double precision values
+ "ldr r4, %[pfpr]\n\t"
+ "vldmia r4, {d0-d7}\n\t"
+#endif
+ //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"
+ : [r0]"=r" (r0), [r1]"=r" (r1)
+ : [pmethod]"m" (pMethod), [pgpr]"m" (pGPR), [pfpr]"m" (pFPR)
+ : "r4", "r5");
MapReturn(r0, r1, pReturnType, (sal_uInt32*)pRegisterReturn);
}
@@ -290,11 +324,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<float *>( pSV ); \
+ if ((nSR % 2 == 1) && (nSR > 2*nDR)) {\
+ nDR++; \
+ }\
+ }\
+ else \
+ {\
+ *pDS++ = *reinterpret_cast<float *>( pSV );\
+ }
+#define INSERT_DOUBLE( pSV, nr, pGPR, pDS, pStart ) \
+ if ( nDR < arm::MAX_FPR_REGS ) { \
+ pFPR[nDR++] = *reinterpret_cast<double *>( pSV ); \
+ }\
+ else\
+ {\
+ if ( (pDS - pStart) % 2) \
+ { \
+ ++pDS; \
+ } \
+ *pDS++ = *reinterpret_cast<double *>( 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 ) \
@@ -325,6 +397,14 @@ static void cpp_call(
sal_uInt32 pGPR[arm::MAX_GPR_REGS];
sal_uInt32 nGPR = 0;
+ // storage and counters for single and double precision VFP registers
+ double pFPR[arm::MAX_FPR_REGS];
+#ifdef __ARM_PCS_VFP
+ sal_uInt32 nDR = 0;
+ float *pSPR = reinterpret_cast< float *>(&pFPR);
+ sal_uInt32 nSR = 0;
+#endif
+
// return
typelib_TypeDescription * pReturnTypeDescr = 0;
TYPELIB_DANGER_GET( &pReturnTypeDescr, pReturnTypeRef );
@@ -456,7 +536,8 @@ static void cpp_call(
pCppReturn, pReturnTypeRef,
pStackStart,
(pStack - pStackStart),
- pGPR, nGPR);
+ pGPR, nGPR,
+ pFPR);
// NO exception occurred...
*ppUnoExc = 0;