ARM bridge: VFP ABI (armhf) support
This commit is contained in:
@@ -8,6 +8,17 @@
|
||||
# define UNWIND
|
||||
#else
|
||||
# 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"
|
||||
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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 );
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user