aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMike Pall <mike>2011-01-29 13:04:16 +0100
committerMike Pall <mike>2011-01-29 13:04:16 +0100
commit6fd721ce7294c0ceba0d0d49a79e59801427ba8d (patch)
tree0c1066dd350e8d2f8f1a0ace0df0f02b0c4f0f59 /src
parent6fee0002b9ea12d4b7f745ee664d6ccb2411af6c (diff)
downloadluajit-6fd721ce7294c0ceba0d0d49a79e59801427ba8d.tar.gz
luajit-6fd721ce7294c0ceba0d0d49a79e59801427ba8d.tar.bz2
luajit-6fd721ce7294c0ceba0d0d49a79e59801427ba8d.zip
FFI: Implement POSIX/x64 struct-by-value calling conventions.
Diffstat (limited to 'src')
-rw-r--r--src/lj_ccall.c135
-rw-r--r--src/lj_ccall.h1
2 files changed, 127 insertions, 9 deletions
diff --git a/src/lj_ccall.c b/src/lj_ccall.c
index ece775a9..f2eceb6d 100644
--- a/src/lj_ccall.c
+++ b/src/lj_ccall.c
@@ -111,14 +111,19 @@
111/* -- POSIX/x64 calling conventions --------------------------------------- */ 111/* -- POSIX/x64 calling conventions --------------------------------------- */
112 112
113#define CCALL_HANDLE_STRUCTRET \ 113#define CCALL_HANDLE_STRUCTRET \
114 if (sz <= 16) { \ 114 int rcl[2]; rcl[0] = rcl[1] = 0; \
115 cc->retref = 0; \ 115 if (ccall_classify_struct(cts, ctr, rcl, 0)) { \
116 goto err_nyi; /* NYI: crazy x64 rules for small structs. */ \ 116 cc->retref = 1; /* Return struct by reference. */ \
117 } else { \
118 cc->retref = 1; /* Return all bigger structs by reference. */ \
119 cc->gpr[ngpr++] = (GPRArg)dp; \ 117 cc->gpr[ngpr++] = (GPRArg)dp; \
118 } else { \
119 cc->retref = 0; /* Return small structs in registers. */ \
120 } 120 }
121 121
122#define CCALL_HANDLE_STRUCTRET2 \
123 int rcl[2]; rcl[0] = rcl[1] = 0; \
124 ccall_classify_struct(cts, ctr, rcl, 0); \
125 ccall_struct_ret(cc, rcl, dp, ctr->size);
126
122#define CCALL_HANDLE_COMPLEXRET \ 127#define CCALL_HANDLE_COMPLEXRET \
123 /* Complex values are returned in one or two FPRs. */ \ 128 /* Complex values are returned in one or two FPRs. */ \
124 cc->retref = 0; 129 cc->retref = 0;
@@ -132,8 +137,12 @@
132 } 137 }
133 138
134#define CCALL_HANDLE_STRUCTARG \ 139#define CCALL_HANDLE_STRUCTARG \
135 if (sz <= 16) { \ 140 int rcl[2]; rcl[0] = rcl[1] = 0; \
136 goto err_nyi; /* NYI: crazy x64 rules for small structs. */ \ 141 if (!ccall_classify_struct(cts, d, rcl, 0)) { \
142 cc->nsp = nsp; cc->ngpr = ngpr; cc->nfpr = nfpr; \
143 if (ccall_struct_arg(cc, cts, d, rcl, o)) goto err_nyi; \
144 nsp = cc->nsp; ngpr = cc->ngpr; nfpr = cc->nfpr; \
145 continue; \
137 } /* Pass all other structs by value on stack. */ 146 } /* Pass all other structs by value on stack. */
138 147
139#define CCALL_HANDLE_COMPLEXARG \ 148#define CCALL_HANDLE_COMPLEXARG \
@@ -195,6 +204,113 @@
195#error "missing calling convention definitions for this architecture" 204#error "missing calling convention definitions for this architecture"
196#endif 205#endif
197 206
207#ifndef CCALL_HANDLE_STRUCTRET2
208#define CCALL_HANDLE_STRUCTRET2 \
209 memcpy(dp, sp, ctr->size); /* Copy struct return value from GPRs. */
210#endif
211
212/* -- x64 struct classification ------------------------------------------- */
213
214#if LJ_TARGET_X64 && !LJ_ABI_WIN
215
216/* Register classes for x64 struct classification. */
217#define CCALL_RCL_INT 1
218#define CCALL_RCL_SSE 2
219#define CCALL_RCL_MEM 4
220/* NYI: classify vectors. */
221
222static int ccall_classify_struct(CTState *cts, CType *ct, int *rcl, CTSize ofs);
223
224/* Classify a C type. */
225static void ccall_classify_ct(CTState *cts, CType *ct, int *rcl, CTSize ofs)
226{
227 if (ctype_isarray(ct->info)) {
228 CType *cct = ctype_rawchild(cts, ct);
229 CTSize eofs, esz = cct->size, asz = ct->size;
230 for (eofs = 0; eofs < asz; eofs += esz)
231 ccall_classify_ct(cts, cct, rcl, ofs+eofs);
232 } else if (ctype_isstruct(ct->info)) {
233 ccall_classify_struct(cts, ct, rcl, ofs);
234 } else {
235 int cl = ctype_isfp(ct->info) ? CCALL_RCL_SSE : CCALL_RCL_INT;
236 lua_assert(ctype_hassize(ct->info));
237 if ((ofs & (ct->size-1))) cl = CCALL_RCL_MEM; /* Unaligned. */
238 rcl[(ofs >= 8)] |= cl;
239 }
240}
241
242/* Recursively classify a struct based on its fields. */
243static int ccall_classify_struct(CTState *cts, CType *ct, int *rcl, CTSize ofs)
244{
245 if (ct->size > 16) return CCALL_RCL_MEM; /* Too big, gets memory class. */
246 while (ct->sib) {
247 CTSize fofs;
248 ct = ctype_get(cts, ct->sib);
249 fofs = ofs+ct->size;
250 if (ctype_isfield(ct->info))
251 ccall_classify_ct(cts, ctype_rawchild(cts, ct), rcl, fofs);
252 else if (ctype_isbitfield(ct->info))
253 rcl[(fofs >= 8)] |= CCALL_RCL_INT; /* NYI: unaligned bitfields? */
254 else if (ctype_isxattrib(ct->info, CTA_SUBTYPE))
255 ccall_classify_struct(cts, ctype_child(cts, ct), rcl, fofs);
256 }
257 return ((rcl[0]|rcl[1]) & CCALL_RCL_MEM); /* Memory class? */
258}
259
260/* Try to split up a small struct into registers. */
261static int ccall_struct_reg(CCallState *cc, GPRArg *dp, int *rcl)
262{
263 MSize ngpr = cc->ngpr, nfpr = cc->nfpr;
264 uint32_t i;
265 for (i = 0; i < 2; i++) {
266 lua_assert(!(rcl[i] & CCALL_RCL_MEM));
267 if ((rcl[i] & CCALL_RCL_INT)) { /* Integer class takes precedence. */
268 if (ngpr >= CCALL_NARG_GPR) return 1; /* Register overflow. */
269 cc->gpr[ngpr++] = dp[i];
270 } else if ((rcl[i] & CCALL_RCL_SSE)) {
271 if (nfpr >= CCALL_NARG_FPR) return 1; /* Register overflow. */
272 cc->fpr[nfpr++].l[0] = dp[i];
273 }
274 }
275 cc->ngpr = ngpr; cc->nfpr = nfpr;
276 return 0; /* Ok. */
277}
278
279/* Pass a small struct argument. */
280static int ccall_struct_arg(CCallState *cc, CTState *cts, CType *d, int *rcl,
281 TValue *o)
282{
283 GPRArg dp[2];
284 dp[0] = dp[1] = 0;
285 lj_cconv_ct_tv(cts, d, (uint8_t *)dp, o, 0); /* Convert to temp. struct. */
286 if (!ccall_struct_reg(cc, dp, rcl)) { /* Register overflow? Pass on stack. */
287 MSize nsp = cc->nsp, n = rcl[1] ? 2 : 1;
288 if (nsp + n > CCALL_MAXSTACK) return 1; /* Too many arguments. */
289 cc->nsp = nsp + n;
290 memcpy(&cc->stack[nsp], dp, n*CTSIZE_PTR);
291 }
292 return 0; /* Ok. */
293}
294
295/* Combine returned small struct. */
296static void ccall_struct_ret(CCallState *cc, int *rcl, uint8_t *dp, CTSize sz)
297{
298 GPRArg sp[2];
299 MSize ngpr = 0, nfpr = 0;
300 uint32_t i;
301 for (i = 0; i < 2; i++) {
302 if ((rcl[i] & CCALL_RCL_INT)) { /* Integer class takes precedence. */
303 sp[i] = cc->gpr[ngpr++];
304 } else if ((rcl[i] & CCALL_RCL_SSE)) {
305 sp[i] = cc->fpr[nfpr++].l[0];
306 }
307 }
308 memcpy(dp, sp, sz);
309}
310#endif
311
312/* -- Common C call handling ---------------------------------------------- */
313
198/* Infer the destination CTypeID for a vararg argument. */ 314/* Infer the destination CTypeID for a vararg argument. */
199static CTypeID ccall_ctid_vararg(CTState *cts, cTValue *o) 315static CTypeID ccall_ctid_vararg(CTState *cts, cTValue *o)
200{ 316{
@@ -207,6 +323,7 @@ static CTypeID ccall_ctid_vararg(CTState *cts, cTValue *o)
207 return lj_ctype_intern(cts, 323 return lj_ctype_intern(cts,
208 CTINFO(CT_PTR, CTALIGN_PTR|ctype_cid(s->info)), CTSIZE_PTR); 324 CTINFO(CT_PTR, CTALIGN_PTR|ctype_cid(s->info)), CTSIZE_PTR);
209 } else if (ctype_isstruct(s->info) || ctype_isfunc(s->info)) { 325 } else if (ctype_isstruct(s->info) || ctype_isfunc(s->info)) {
326 /* NYI: how to pass a struct by value in a vararg argument? */
210 return lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|id), CTSIZE_PTR); 327 return lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|id), CTSIZE_PTR);
211 } if (ctype_isfp(s->info) && s->size == sizeof(float)) { 328 } if (ctype_isfp(s->info) && s->size == sizeof(float)) {
212 return CTID_DOUBLE; 329 return CTID_DOUBLE;
@@ -335,7 +452,7 @@ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct,
335 MSize align = (1u << ctype_align(d->info-CTALIGN_PTR)) -1; 452 MSize align = (1u << ctype_align(d->info-CTALIGN_PTR)) -1;
336 nsp = (nsp + align) & ~align; /* Align argument on stack. */ 453 nsp = (nsp + align) & ~align; /* Align argument on stack. */
337 } 454 }
338 if (nsp + n >= CCALL_MAXSTACK) { /* Too many arguments. */ 455 if (nsp + n > CCALL_MAXSTACK) { /* Too many arguments. */
339 err_nyi: 456 err_nyi:
340 lj_err_caller(L, LJ_ERR_FFI_NYICALL); 457 lj_err_caller(L, LJ_ERR_FFI_NYICALL);
341 } 458 }
@@ -392,7 +509,7 @@ static int ccall_get_results(lua_State *L, CTState *cts, CType *ct,
392 /* Return cdata object which is already on top of stack. */ 509 /* Return cdata object which is already on top of stack. */
393 if (!cc->retref) { 510 if (!cc->retref) {
394 void *dp = cdataptr(cdataV(L->top-1)); /* Use preallocated object. */ 511 void *dp = cdataptr(cdataV(L->top-1)); /* Use preallocated object. */
395 memcpy(dp, sp, ctr->size); /* Copy struct return value from GPRs. */ 512 CCALL_HANDLE_STRUCTRET2
396 } 513 }
397 return 1; /* One GC step. */ 514 return 1; /* One GC step. */
398 } 515 }
diff --git a/src/lj_ccall.h b/src/lj_ccall.h
index 6f9c58e5..83e2403d 100644
--- a/src/lj_ccall.h
+++ b/src/lj_ccall.h
@@ -86,6 +86,7 @@ typedef struct CCallState {
86 uint8_t nsp; /* Number of stack slots. */ 86 uint8_t nsp; /* Number of stack slots. */
87 uint8_t retref; /* Return value by reference. */ 87 uint8_t retref; /* Return value by reference. */
88#if LJ_TARGET_X64 88#if LJ_TARGET_X64
89 uint8_t ngpr; /* Number of arguments in GPRs. */
89 uint8_t nfpr; /* Number of arguments in FPRs. */ 90 uint8_t nfpr; /* Number of arguments in FPRs. */
90#elif LJ_TARGET_X86 91#elif LJ_TARGET_X86
91 uint8_t resx87; /* Result on x87 stack: 1:float, 2:double. */ 92 uint8_t resx87; /* Result on x87 stack: 1:float, 2:double. */