ARM bridge: VFP ABI (armhf) support
This commit is contained in:
@@ -8,6 +8,17 @@
|
|||||||
# define UNWIND
|
# define UNWIND
|
||||||
#else
|
#else
|
||||||
# define UNWIND @
|
# 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
|
#endif
|
||||||
|
|
||||||
.file "armhelper.s"
|
.file "armhelper.s"
|
||||||
@@ -19,9 +30,12 @@ privateSnippetExecutor:
|
|||||||
UNWIND .fnstart @ start of unwinder entry
|
UNWIND .fnstart @ start of unwinder entry
|
||||||
|
|
||||||
stmfd sp!, {r0-r3} @ follow other parameters on stack
|
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 r0, ip @ r0 points to functionoffset/vtable
|
||||||
mov r1, sp @ r1 points to this and params
|
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())
|
@ (see cppuno.cxx:codeSnippet())
|
||||||
stmfd sp!, {r4,lr} @ save return address
|
stmfd sp!, {r4,lr} @ save return address
|
||||||
@ (r4 pushed to preserve stack alignment)
|
@ (r4 pushed to preserve stack alignment)
|
||||||
@@ -30,7 +44,7 @@ privateSnippetExecutor:
|
|||||||
bl cpp_vtable_call(PLT)
|
bl cpp_vtable_call(PLT)
|
||||||
|
|
||||||
add sp, sp, #4 @ no need to restore r4 (we didn't touch it)
|
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
|
UNWIND .fnend @ end of unwinder entry
|
||||||
|
|
||||||
|
@@ -69,6 +69,9 @@ namespace
|
|||||||
char * pTopStack = (char *)(pCallStack + 0);
|
char * pTopStack = (char *)(pCallStack + 0);
|
||||||
char * pCppStack = pTopStack;
|
char * pCppStack = pTopStack;
|
||||||
|
|
||||||
|
#ifdef __ARM_PCS_VFP
|
||||||
|
char * pFloatArgs = (char *)(pCppStack - 64);
|
||||||
|
#endif
|
||||||
// return
|
// return
|
||||||
typelib_TypeDescription * pReturnTypeDescr = 0;
|
typelib_TypeDescription * pReturnTypeDescr = 0;
|
||||||
if (pReturnTypeRef)
|
if (pReturnTypeRef)
|
||||||
@@ -125,7 +128,9 @@ namespace
|
|||||||
{
|
{
|
||||||
case typelib_TypeClass_HYPER:
|
case typelib_TypeClass_HYPER:
|
||||||
case typelib_TypeClass_UNSIGNED_HYPER:
|
case typelib_TypeClass_UNSIGNED_HYPER:
|
||||||
|
#ifndef __ARM_PCS_VFP
|
||||||
case typelib_TypeClass_DOUBLE:
|
case typelib_TypeClass_DOUBLE:
|
||||||
|
#endif
|
||||||
if ((pCppStack - pTopStack) % 8) pCppStack+=sizeof(sal_Int32); //align to 8
|
if ((pCppStack - pTopStack) % 8) pCppStack+=sizeof(sal_Int32); //align to 8
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -133,13 +138,31 @@ namespace
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pCppArgs[nPos] = pCppStack;
|
// For armhf we get the floating point arguments from a different area of the stack
|
||||||
pUnoArgs[nPos] = pCppStack;
|
// 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)
|
switch (pParamTypeDescr->eTypeClass)
|
||||||
{
|
{
|
||||||
case typelib_TypeClass_HYPER:
|
case typelib_TypeClass_HYPER:
|
||||||
case typelib_TypeClass_UNSIGNED_HYPER:
|
case typelib_TypeClass_UNSIGNED_HYPER:
|
||||||
|
#ifndef __ARM_PCS_VFP
|
||||||
case typelib_TypeClass_DOUBLE:
|
case typelib_TypeClass_DOUBLE:
|
||||||
|
#endif
|
||||||
pCppStack += sizeof(sal_Int32); // extra long
|
pCppStack += sizeof(sal_Int32); // extra long
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -179,6 +202,13 @@ namespace
|
|||||||
TYPELIB_DANGER_RELEASE( pParamTypeDescr );
|
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
|
pCppStack += sizeof(sal_Int32); // standard parameter length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -93,7 +93,7 @@ namespace CPPU_CURRENT_NAMESPACE
|
|||||||
|
|
||||||
namespace arm
|
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 );
|
bool return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -131,6 +131,20 @@ namespace arm
|
|||||||
return false;
|
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 )
|
bool return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef )
|
||||||
{
|
{
|
||||||
if (bridges::cpp_uno::shared::isSimpleType(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
|
//A Composite Type not larger than 4 bytes is returned in r0
|
||||||
bool bRet = pTypeDescr->nSize > 4 || is_complex_struct(pTypeDescr);
|
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 );
|
TYPELIB_DANGER_RELEASE( pTypeDescr );
|
||||||
return bRet;
|
return bRet;
|
||||||
}
|
}
|
||||||
@@ -208,7 +229,8 @@ void callVirtualMethod(
|
|||||||
sal_uInt32 *pStack,
|
sal_uInt32 *pStack,
|
||||||
sal_uInt32 nStack,
|
sal_uInt32 nStack,
|
||||||
sal_uInt32 *pGPR,
|
sal_uInt32 *pGPR,
|
||||||
sal_uInt32 nGPR) __attribute__((noinline));
|
sal_uInt32 nGPR,
|
||||||
|
double *pFPR) __attribute__((noinline));
|
||||||
|
|
||||||
void callVirtualMethod(
|
void callVirtualMethod(
|
||||||
void * pThis,
|
void * pThis,
|
||||||
@@ -218,7 +240,8 @@ void callVirtualMethod(
|
|||||||
sal_uInt32 *pStack,
|
sal_uInt32 *pStack,
|
||||||
sal_uInt32 nStack,
|
sal_uInt32 nStack,
|
||||||
sal_uInt32 *pGPR,
|
sal_uInt32 *pGPR,
|
||||||
sal_uInt32 nGPR)
|
sal_uInt32 nGPR,
|
||||||
|
double *pFPR)
|
||||||
{
|
{
|
||||||
// never called
|
// never called
|
||||||
if (! pThis)
|
if (! pThis)
|
||||||
@@ -240,19 +263,30 @@ void callVirtualMethod(
|
|||||||
pMethod += 4 * nVtableIndex;
|
pMethod += 4 * nVtableIndex;
|
||||||
pMethod = *((sal_uInt32 *)pMethod);
|
pMethod = *((sal_uInt32 *)pMethod);
|
||||||
|
|
||||||
typedef void (*FunctionCall )( sal_uInt32, sal_uInt32, sal_uInt32, sal_uInt32);
|
//Return registers
|
||||||
FunctionCall pFunc = (FunctionCall)pMethod;
|
|
||||||
|
|
||||||
(*pFunc)(pGPR[0], pGPR[1], pGPR[2], pGPR[3]);
|
|
||||||
|
|
||||||
sal_uInt32 r0;
|
sal_uInt32 r0;
|
||||||
sal_uInt32 r1;
|
sal_uInt32 r1;
|
||||||
|
|
||||||
// get return value
|
|
||||||
__asm__ __volatile__ (
|
__asm__ __volatile__ (
|
||||||
"mov %0, r0\n\t"
|
//Fill in general purpose register arguments
|
||||||
"mov %1, r1\n\t"
|
"ldr r4, %[pgpr]\n\t"
|
||||||
: "=r" (r0), "=r" (r1) : );
|
"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);
|
MapReturn(r0, r1, pReturnType, (sal_uInt32*)pRegisterReturn);
|
||||||
}
|
}
|
||||||
@@ -290,11 +324,49 @@ void callVirtualMethod(
|
|||||||
INSERT_INT32( ((sal_uInt32*)pSV)+1, nr, pGPR, pDS )
|
INSERT_INT32( ((sal_uInt32*)pSV)+1, nr, pGPR, pDS )
|
||||||
#endif
|
#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 ) \
|
#define INSERT_FLOAT( pSV, nr, pFPR, pDS ) \
|
||||||
INSERT_INT32( pSV, nr, pGPR, pDS )
|
INSERT_INT32( pSV, nr, pGPR, pDS )
|
||||||
|
|
||||||
#define INSERT_DOUBLE( pSV, nr, pFPR, pDS, pStart ) \
|
#define INSERT_DOUBLE( pSV, nr, pFPR, pDS, pStart ) \
|
||||||
INSERT_INT64( pSV, nr, pGPR, pDS, pStart )
|
INSERT_INT64( pSV, nr, pGPR, pDS, pStart )
|
||||||
|
#endif
|
||||||
|
|
||||||
#define INSERT_INT16( pSV, nr, pGPR, pDS ) \
|
#define INSERT_INT16( pSV, nr, pGPR, pDS ) \
|
||||||
if ( nr < arm::MAX_GPR_REGS ) \
|
if ( nr < arm::MAX_GPR_REGS ) \
|
||||||
@@ -325,6 +397,14 @@ static void cpp_call(
|
|||||||
sal_uInt32 pGPR[arm::MAX_GPR_REGS];
|
sal_uInt32 pGPR[arm::MAX_GPR_REGS];
|
||||||
sal_uInt32 nGPR = 0;
|
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
|
// return
|
||||||
typelib_TypeDescription * pReturnTypeDescr = 0;
|
typelib_TypeDescription * pReturnTypeDescr = 0;
|
||||||
TYPELIB_DANGER_GET( &pReturnTypeDescr, pReturnTypeRef );
|
TYPELIB_DANGER_GET( &pReturnTypeDescr, pReturnTypeRef );
|
||||||
@@ -456,7 +536,8 @@ static void cpp_call(
|
|||||||
pCppReturn, pReturnTypeRef,
|
pCppReturn, pReturnTypeRef,
|
||||||
pStackStart,
|
pStackStart,
|
||||||
(pStack - pStackStart),
|
(pStack - pStackStart),
|
||||||
pGPR, nGPR);
|
pGPR, nGPR,
|
||||||
|
pFPR);
|
||||||
|
|
||||||
// NO exception occurred...
|
// NO exception occurred...
|
||||||
*ppUnoExc = 0;
|
*ppUnoExc = 0;
|
||||||
|
Reference in New Issue
Block a user