diff options
author | Mike Pall <mike> | 2011-01-29 13:04:16 +0100 |
---|---|---|
committer | Mike Pall <mike> | 2011-01-29 13:04:16 +0100 |
commit | 6fd721ce7294c0ceba0d0d49a79e59801427ba8d (patch) | |
tree | 0c1066dd350e8d2f8f1a0ace0df0f02b0c4f0f59 /src | |
parent | 6fee0002b9ea12d4b7f745ee664d6ccb2411af6c (diff) | |
download | luajit-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.c | 135 | ||||
-rw-r--r-- | src/lj_ccall.h | 1 |
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 | |||
222 | static int ccall_classify_struct(CTState *cts, CType *ct, int *rcl, CTSize ofs); | ||
223 | |||
224 | /* Classify a C type. */ | ||
225 | static 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. */ | ||
243 | static 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. */ | ||
261 | static 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. */ | ||
280 | static 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. */ | ||
296 | static 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. */ |
199 | static CTypeID ccall_ctid_vararg(CTState *cts, cTValue *o) | 315 | static 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. */ |