ARM: Add hard-float ABI support to the FFI (interpreter).

This commit is contained in:
Mike Pall 2012-07-30 19:00:52 +02:00
parent a373fddbd3
commit 4b0af611d0
5 changed files with 193 additions and 19 deletions

View File

@ -168,6 +168,8 @@
#elif LJ_TARGET_ARM #elif LJ_TARGET_ARM
/* -- ARM calling conventions --------------------------------------------- */ /* -- ARM calling conventions --------------------------------------------- */
#if LJ_ABI_SOFTFP
#define CCALL_HANDLE_STRUCTRET \ #define CCALL_HANDLE_STRUCTRET \
/* Return structs of size <= 4 in a GPR. */ \ /* Return structs of size <= 4 in a GPR. */ \
cc->retref = !(sz <= 4); \ cc->retref = !(sz <= 4); \
@ -186,13 +188,70 @@
#define CCALL_HANDLE_COMPLEXARG \ #define CCALL_HANDLE_COMPLEXARG \
/* Pass complex by value in 2 or 4 GPRs. */ /* Pass complex by value in 2 or 4 GPRs. */
/* ARM has a softfp ABI. */ #define CCALL_HANDLE_REGARG_FP1
#define CCALL_HANDLE_REGARG_FP2
#else
#define CCALL_HANDLE_STRUCTRET \
cc->retref = !ccall_classify_struct(cts, ctr, ct); \
if (cc->retref) cc->gpr[ngpr++] = (GPRArg)dp;
#define CCALL_HANDLE_STRUCTRET2 \
if (ccall_classify_struct(cts, ctr, ct) > 1) sp = (uint8_t *)&cc->fpr[0]; \
memcpy(dp, sp, ctr->size);
#define CCALL_HANDLE_COMPLEXRET \
if (!(ct->info & CTF_VARARG)) cc->retref = 0; /* Return complex in FPRs. */
#define CCALL_HANDLE_COMPLEXRET2 \
if (!(ct->info & CTF_VARARG)) memcpy(dp, &cc->fpr[0], ctr->size);
#define CCALL_HANDLE_STRUCTARG \
isfp = (ccall_classify_struct(cts, d, ct) > 1);
/* Pass all structs by value in registers and/or on the stack. */
#define CCALL_HANDLE_COMPLEXARG \
isfp = 1; /* Pass complex by value in FPRs or on stack. */
#define CCALL_HANDLE_REGARG_FP1 \
if (isfp && !(ct->info & CTF_VARARG)) { \
if ((d->info & CTF_ALIGN) > CTALIGN_PTR) { \
if (nfpr + (n >> 1) <= CCALL_NARG_FPR) { \
dp = &cc->fpr[nfpr]; \
nfpr += (n >> 1); \
goto done; \
} \
} else { \
if (sz > 1 && fprodd != nfpr) fprodd = 0; \
if (fprodd) { \
if (2*nfpr+n <= 2*CCALL_NARG_FPR+1) { \
dp = (void *)&cc->fpr[fprodd-1].f[1]; \
nfpr += (n >> 1); \
if ((n & 1)) fprodd = 0; else fprodd = nfpr-1; \
goto done; \
} \
} else { \
if (2*nfpr+n <= 2*CCALL_NARG_FPR) { \
dp = (void *)&cc->fpr[nfpr]; \
nfpr += (n >> 1); \
if ((n & 1)) fprodd = ++nfpr; else fprodd = 0; \
goto done; \
} \
} \
} \
fprodd = 0; /* No reordering after the first FP value is on stack. */ \
} else {
#define CCALL_HANDLE_REGARG_FP2 }
#endif
#define CCALL_HANDLE_REGARG \ #define CCALL_HANDLE_REGARG \
CCALL_HANDLE_REGARG_FP1 \
if ((d->info & CTF_ALIGN) > CTALIGN_PTR) { \ if ((d->info & CTF_ALIGN) > CTALIGN_PTR) { \
if (ngpr < maxgpr) \ if (ngpr < maxgpr) \
ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \ ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \
else \
nsp = (nsp + 1u) & ~1u; /* Align argument on stack. */ \
} \ } \
if (ngpr < maxgpr) { \ if (ngpr < maxgpr) { \
dp = &cc->gpr[ngpr]; \ dp = &cc->gpr[ngpr]; \
@ -204,7 +263,10 @@
ngpr += n; \ ngpr += n; \
} \ } \
goto done; \ goto done; \
} } CCALL_HANDLE_REGARG_FP2
#define CCALL_HANDLE_RET \
if ((ct->info & CTF_VARARG)) sp = (uint8_t *)&cc->gpr[0];
#elif LJ_TARGET_PPC #elif LJ_TARGET_PPC
/* -- PPC calling conventions --------------------------------------------- */ /* -- PPC calling conventions --------------------------------------------- */
@ -453,6 +515,49 @@ static void ccall_struct_ret(CCallState *cc, int *rcl, uint8_t *dp, CTSize sz)
} }
#endif #endif
/* -- ARM hard-float ABI struct classification ---------------------------- */
#if LJ_TARGET_ARM && !LJ_ABI_SOFTFP
/* Classify a struct based on its fields. */
static unsigned int ccall_classify_struct(CTState *cts, CType *ct, CType *ctf)
{
CTSize sz = ct->size;
unsigned int r = 0, n = 0, isu = (ct->info & CTF_UNION);
if ((ctf->info & CTF_VARARG)) goto noth;
while (ct->sib) {
ct = ctype_get(cts, ct->sib);
if (ctype_isfield(ct->info)) {
CType *sct = ctype_rawchild(cts, ct);
if (ctype_isfp(sct->info)) {
r |= sct->size;
if (!isu) n++; else if (n == 0) n = 1;
} else if (ctype_iscomplex(sct->info)) {
r |= (sct->size >> 1);
if (!isu) n += 2; else if (n < 2) n = 2;
} else {
goto noth;
}
} else if (ctype_isbitfield(ct->info)) {
goto noth;
} else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) {
CType *sct = ctype_child(cts, ct);
if (sct->size > 0) {
unsigned int s = ccall_classify_struct(cts, sct, ctf);
if (s <= 1) goto noth;
r |= (s & 255);
if (!isu) n += (s >> 8); else if (n < (s >>8)) n = (s >> 8);
}
}
}
if ((r == 4 || r == 8) && n <= 4)
return r + (n << 8);
noth: /* Not a homogeneous float/double aggregate. */
return (sz <= 4); /* Return structs of size <= 4 in a GPR. */
}
#endif
/* -- Common C call handling ---------------------------------------------- */ /* -- Common C call handling ---------------------------------------------- */
/* Infer the destination CTypeID for a vararg argument. */ /* Infer the destination CTypeID for a vararg argument. */
@ -494,6 +599,9 @@ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct,
MSize maxgpr, ngpr = 0, nsp = 0, narg; MSize maxgpr, ngpr = 0, nsp = 0, narg;
#if CCALL_NARG_FPR #if CCALL_NARG_FPR
MSize nfpr = 0; MSize nfpr = 0;
#if LJ_TARGET_ARM
MSize fprodd = 0;
#endif
#endif #endif
/* Clear unused regs to get some determinism in case of misdeclaration. */ /* Clear unused regs to get some determinism in case of misdeclaration. */

View File

@ -51,12 +51,21 @@ typedef intptr_t GPRArg;
#elif LJ_TARGET_ARM #elif LJ_TARGET_ARM
#define CCALL_NARG_GPR 4 #define CCALL_NARG_GPR 4
#define CCALL_NARG_FPR 0
#define CCALL_NRET_GPR 2 /* For softfp double. */ #define CCALL_NRET_GPR 2 /* For softfp double. */
#if LJ_ABI_SOFTFP
#define CCALL_NARG_FPR 0
#define CCALL_NRET_FPR 0 #define CCALL_NRET_FPR 0
#else
#define CCALL_NARG_FPR 8
#define CCALL_NRET_FPR 4
#endif
#define CCALL_SPS_FREE 0 #define CCALL_SPS_FREE 0
typedef intptr_t GPRArg; typedef intptr_t GPRArg;
typedef union FPRArg {
double d;
float f[2];
} FPRArg;
#elif LJ_TARGET_PPC #elif LJ_TARGET_PPC
@ -122,7 +131,7 @@ LJ_STATIC_ASSERT(CCALL_NUM_FPR <= CCALL_MAX_FPR);
/* -- C call state -------------------------------------------------------- */ /* -- C call state -------------------------------------------------------- */
typedef struct CCallState { typedef LJ_ALIGN(8) struct CCallState {
void (*func)(void); /* Pointer to called function. */ void (*func)(void); /* Pointer to called function. */
uint32_t spadj; /* Stack pointer adjustment. */ uint32_t spadj; /* Stack pointer adjustment. */
uint8_t nsp; /* Number of stack slots. */ uint8_t nsp; /* Number of stack slots. */
@ -135,10 +144,10 @@ typedef struct CCallState {
#elif LJ_TARGET_PPC #elif LJ_TARGET_PPC
uint8_t nfpr; /* Number of arguments in FPRs. */ uint8_t nfpr; /* Number of arguments in FPRs. */
#endif #endif
#if CCALL_NUM_FPR
#if LJ_32 #if LJ_32
int32_t align1; int32_t align1;
#endif #endif
#if CCALL_NUM_FPR
FPRArg fpr[CCALL_NUM_FPR]; /* Arguments/results in FPRs. */ FPRArg fpr[CCALL_NUM_FPR]; /* Arguments/results in FPRs. */
#endif #endif
GPRArg gpr[CCALL_NUM_GPR]; /* Arguments/results in GPRs. */ GPRArg gpr[CCALL_NUM_GPR]; /* Arguments/results in GPRs. */

View File

@ -310,22 +310,53 @@ void lj_ccallback_mcode_free(CTState *cts)
#elif LJ_TARGET_ARM #elif LJ_TARGET_ARM
#if LJ_ABI_SOFTFP
#define CALLBACK_HANDLE_REGARG_FP1 UNUSED(isfp);
#define CALLBACK_HANDLE_REGARG_FP2
#else
#define CALLBACK_HANDLE_REGARG_FP1 \
if (isfp) { \
if (n == 1) { \
if (fprodd) { \
sp = &cts->cb.fpr[fprodd-1]; \
fprodd = 0; \
goto done; \
} else if (nfpr + 1 <= CCALL_NARG_FPR) { \
sp = &cts->cb.fpr[nfpr++]; \
fprodd = nfpr; \
goto done; \
} \
} else { \
if (nfpr + 1 <= CCALL_NARG_FPR) { \
sp = &cts->cb.fpr[nfpr++]; \
goto done; \
} \
} \
fprodd = 0; /* No reordering after the first FP value is on stack. */ \
} else {
#define CALLBACK_HANDLE_REGARG_FP2 }
#endif
#define CALLBACK_HANDLE_REGARG \ #define CALLBACK_HANDLE_REGARG \
UNUSED(isfp); \ CALLBACK_HANDLE_REGARG_FP1 \
if (n > 1) ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \ if (n > 1) ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \
if (ngpr + n <= maxgpr) { \ if (ngpr + n <= maxgpr) { \
sp = &cts->cb.gpr[ngpr]; \ sp = &cts->cb.gpr[ngpr]; \
ngpr += n; \ ngpr += n; \
goto done; \ goto done; \
} } CALLBACK_HANDLE_REGARG_FP2
#elif LJ_TARGET_PPC #elif LJ_TARGET_PPC
#define CALLBACK_HANDLE_REGARG \ #define CALLBACK_HANDLE_REGARG \
if (isfp) { \ if (isfp) { \
if (nfpr + 1 <= CCALL_NARG_FPR) { \ if (nfpr + 1 <= CCALL_NARG_FPR) { \
sp = &cts->cb.fpr[nfpr]; \ sp = &cts->cb.fpr[nfpr++]; \
nfpr += 1; \
cta = ctype_get(cts, CTID_DOUBLE); /* FPRs always hold doubles. */ \ cta = ctype_get(cts, CTID_DOUBLE); /* FPRs always hold doubles. */ \
goto done; \ goto done; \
} \ } \
@ -382,6 +413,9 @@ static void callback_conv_args(CTState *cts, lua_State *L)
MSize ngpr = 0, nsp = 0, maxgpr = CCALL_NARG_GPR; MSize ngpr = 0, nsp = 0, maxgpr = CCALL_NARG_GPR;
#if CCALL_NARG_FPR #if CCALL_NARG_FPR
MSize nfpr = 0; MSize nfpr = 0;
#if LJ_TARGET_ARM
MSize fprodd = 0;
#endif
#endif #endif
if (slot < cts->cb.sizeid && (id = cts->cb.cbid[slot]) != 0) { if (slot < cts->cb.sizeid && (id = cts->cb.cbid[slot]) != 0) {

View File

@ -155,7 +155,7 @@ typedef struct CType {
#define CCALL_MAX_GPR 8 #define CCALL_MAX_GPR 8
#define CCALL_MAX_FPR 8 #define CCALL_MAX_FPR 8
typedef LJ_ALIGN(8) union FPRCBArg { double d; float f; } FPRCBArg; typedef LJ_ALIGN(8) union FPRCBArg { double d; float f[2]; } FPRCBArg;
/* C callback state. Defined here, to avoid dragging in lj_ccall.h. */ /* C callback state. Defined here, to avoid dragging in lj_ccall.h. */

View File

@ -2414,8 +2414,19 @@ static void build_subroutines(BuildCtx *ctx)
|.type CTSTATE, CTState, PC |.type CTSTATE, CTState, PC
| ldr CTSTATE, GL:r12->ctype_state | ldr CTSTATE, GL:r12->ctype_state
| add DISPATCH, r12, #GG_G2DISP | add DISPATCH, r12, #GG_G2DISP
| strd CARG12, CTSTATE->cb.gpr[0] |.if FPU
| str r4, SAVE_R4
| add r4, sp, CFRAME_SPACE+4+8*8
| vstmdb r4!, {d8-d15}
|.endif
|.if HFABI
| add r12, CTSTATE, #offsetof(CTState, cb.fpr[8])
|.endif
| strd CARG34, CTSTATE->cb.gpr[2] | strd CARG34, CTSTATE->cb.gpr[2]
| strd CARG12, CTSTATE->cb.gpr[0]
|.if HFABI
| vstmdb r12!, {d0-d7}
|.endif
| ldr CARG4, [sp] | ldr CARG4, [sp]
| add CARG3, sp, #CFRAME_SIZE | add CARG3, sp, #CFRAME_SIZE
| mov CARG1, CTSTATE | mov CARG1, CTSTATE
@ -2448,6 +2459,9 @@ static void build_subroutines(BuildCtx *ctx)
| mov CARG2, RA | mov CARG2, RA
| bl extern lj_ccallback_leave // (CTState *cts, TValue *o) | bl extern lj_ccallback_leave // (CTState *cts, TValue *o)
| ldrd CARG12, CTSTATE->cb.gpr[0] | ldrd CARG12, CTSTATE->cb.gpr[0]
|.if HFABI
| vldr d0, CTSTATE->cb.fpr[0]
|.endif
| b ->vm_leave_unw | b ->vm_leave_unw
|.endif |.endif
| |
@ -2460,9 +2474,15 @@ static void build_subroutines(BuildCtx *ctx)
| ldr CARG1, CCSTATE:CARG1->spadj | ldr CARG1, CCSTATE:CARG1->spadj
| ldrb CARG2, CCSTATE->nsp | ldrb CARG2, CCSTATE->nsp
| add CARG3, CCSTATE, #offsetof(CCallState, stack) | add CARG3, CCSTATE, #offsetof(CCallState, stack)
|.if HFABI
| add RB, CCSTATE, #offsetof(CCallState, fpr[0])
|.endif
| mov r11, sp | mov r11, sp
| sub sp, sp, CARG1 // Readjust stack. | sub sp, sp, CARG1 // Readjust stack.
| subs CARG2, CARG2, #1 | subs CARG2, CARG2, #1
|.if HFABI
| vldm RB, {d0-d7}
|.endif
| ldr RB, CCSTATE->func | ldr RB, CCSTATE->func
| bmi >2 | bmi >2
|1: // Copy stack slots. |1: // Copy stack slots.
@ -2471,14 +2491,17 @@ static void build_subroutines(BuildCtx *ctx)
| subs CARG2, CARG2, #1 | subs CARG2, CARG2, #1
| bpl <1 | bpl <1
|2: |2:
| ldr CARG1, CCSTATE->gpr[0] | ldrd CARG12, CCSTATE->gpr[0]
| ldr CARG2, CCSTATE->gpr[1] | ldrd CARG34, CCSTATE->gpr[2]
| ldr CARG3, CCSTATE->gpr[2]
| ldr CARG4, CCSTATE->gpr[3]
| blx RB | blx RB
| mov sp, r11 | mov sp, r11
| str CRET1, CCSTATE->gpr[0] |.if HFABI
| str CRET2, CCSTATE->gpr[1] | add r12, CCSTATE, #offsetof(CCallState, fpr[4])
|.endif
| strd CRET1, CCSTATE->gpr[0]
|.if HFABI
| vstmdb r12!, {d0-d3}
|.endif
| pop {CCSTATE, r5, r11, pc} | pop {CCSTATE, r5, r11, pc}
|.endif |.endif
|// Note: vm_ffi_call must be the last function in this object file! |// Note: vm_ffi_call must be the last function in this object file!