summaryrefslogtreecommitdiff
path: root/src/lj_ccall.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lj_ccall.c')
-rw-r--r--src/lj_ccall.c349
1 files changed, 349 insertions, 0 deletions
diff --git a/src/lj_ccall.c b/src/lj_ccall.c
new file mode 100644
index 00000000..5e91d962
--- /dev/null
+++ b/src/lj_ccall.c
@@ -0,0 +1,349 @@
1/*
2** FFI C call handling.
3** Copyright (C) 2005-2010 Mike Pall. See Copyright Notice in luajit.h
4*/
5
6#include "lj_obj.h"
7
8#if LJ_HASFFI
9
10#include "lj_err.h"
11#include "lj_str.h"
12#include "lj_ctype.h"
13#include "lj_cconv.h"
14#include "lj_cdata.h"
15#include "lj_ccall.h"
16
17/* Target-specific handling of register arguments. */
18#if LJ_TARGET_X86
19
20#define CCALL_HANDLE_REGARG \
21 if (!isfp) { /* Only non-FP values may be passed in registers. */ \
22 if (n > 1) { /* Anything > 32 bit is passed on the stack. */ \
23 ngpr = maxgpr; /* Prevent reordering. */ \
24 } else if (ngpr + 1 <= maxgpr) { \
25 dp = &cc->gpr[ngpr]; \
26 ngpr += n; \
27 goto done; \
28 } \
29 }
30
31#elif LJ_TARGET_X64 && LJ_ABI_WIN
32
33/* Windows/x64 argument registers are strictly positional (use ngpr). */
34#define CCALL_HANDLE_REGARG \
35 if (isfp) { \
36 if (ngpr < 4) { dp = &cc->fpr[ngpr++]; nfpr = ngpr; goto done; } \
37 } else { \
38 if (ngpr < 4) { dp = &cc->gpr[ngpr++]; goto done; } \
39 }
40
41#elif LJ_TARGET_X64
42
43#define CCALL_HANDLE_REGARG \
44 if (isfp) { /* Try to pass argument in FPRs. */ \
45 if (nfpr + isfp <= CCALL_NARG_FPR) { \
46 dp = &cc->fpr[nfpr]; \
47 nfpr += isfp; \
48 goto done; \
49 } \
50 } else { /* Try to pass argument in GPRs. */ \
51 /* Note that reordering is explicitly allowed in the x64 ABI. */ \
52 if (n <= 2 && ngpr + n <= maxgpr) { \
53 dp = &cc->gpr[ngpr]; \
54 ngpr += n; \
55 goto done; \
56 } \
57 }
58
59#elif LJ_TARGET_PPCSPE
60
61/* PPC/SPE has a softfp ABI. */
62#define CCALL_HANDLE_REGARG \
63 if (n > 1) { /* Doesn't fit in a single GPR? */ \
64 lua_assert(n == 2 || n == 4); /* int64_t, double or complex (float). */ \
65 if (n == 2) \
66 ngpr = (ngpr + 1u) & ~1u; /* Only align 64 bit value to regpair. */ \
67 else if (ngpr + n > maxgpr) \
68 ngpr = maxgpr; /* Prevent reordering. */ \
69 } \
70 if (ngpr + n <= maxgpr) { \
71 dp = &cc->gpr[ngpr]; \
72 ngpr += n; \
73 goto done; \
74 }
75
76#else
77#error "missing definition for handling of register arguments"
78#endif
79
80/* Infer the destination CTypeID for a vararg argument. */
81static CTypeID ccall_ctid_vararg(CTState *cts, cTValue *o)
82{
83 if (tvisnum(o)) {
84 return CTID_DOUBLE;
85 } else if (tviscdata(o)) {
86 CTypeID id = cdataV(o)->typeid;
87 CType *s = ctype_get(cts, id);
88 if (ctype_isrefarray(s->info)) {
89 return lj_ctype_intern(cts,
90 CTINFO(CT_PTR, CTALIGN_PTR|ctype_cid(s->info)), CTSIZE_PTR);
91 } else if (ctype_isstruct(s->info) || ctype_isfunc(s->info)) {
92 return lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|id), CTSIZE_PTR);
93 } if (ctype_isfp(s->info) && s->size == sizeof(float)) {
94 return CTID_DOUBLE;
95 } else {
96 return id;
97 }
98 } else if (tvisstr(o)) {
99 return CTID_P_CCHAR;
100 } else if (tvisbool(o)) {
101 return CTID_BOOL;
102 } else {
103 return CTID_P_VOID;
104 }
105}
106
107/* Setup arguments for C call. */
108static void ccall_set_args(lua_State *L, CTState *cts, CType *ct,
109 CCallState *cc)
110{
111 TValue *o, *top = L->top;
112 CTypeID fid;
113 CType *ctr;
114 MSize maxgpr, ngpr = 0, nsp = 0;
115#if CCALL_NARG_FPR
116 MSize nfpr = 0;
117#endif
118
119 /* Clear unused regs to get some determinism in case of misdeclaration. */
120 memset(cc->gpr, 0, sizeof(cc->gpr));
121#if CCALL_NUM_FPR
122 memset(cc->fpr, 0, sizeof(cc->fpr));
123#endif
124
125#if LJ_TARGET_X86
126 /* x86 has several different calling conventions. */
127 cc->resx87 = 0;
128 switch ((ct->info >> CTSHIFT_CCONV) & CTMASK_CCONV) {
129 case CTCC_FASTCALL: maxgpr = 2; break;
130 case CTCC_THISCALL: maxgpr = 1; break;
131 default: maxgpr = 0; break;
132 }
133#else
134 maxgpr = CCALL_NARG_GPR;
135#endif
136
137 /* Perform required setup for some result types. */
138 ctr = ctype_rawchild(cts, ct);
139 if (ctype_isvector(ctr->info)) {
140 if (!(CCALL_VECTOR_REG && (ctr->size == 8 || ctr->size == 16)))
141 goto err_nyi;
142 } else if (ctype_iscomplex(ctr->info) || ctype_isstruct(ctr->info)) {
143 /* Preallocate cdata object and anchor it after arguments. */
144 CTSize sz = ctr->size;
145 GCcdata *cd = lj_cdata_new(cts, ctype_cid(ct->info), sz);
146 setcdataV(L, L->top++, cd);
147 if (ctype_iscomplex(ctr->info)) {
148 cc->retref = (sz == 2*sizeof(float)) ? CCALL_COMPLEXF_RETREF :
149 CCALL_COMPLEX_RETREF;
150 } else {
151#if CCALL_STRUCT_RETREF
152 cc->retref = 1; /* Return all structs by reference. */
153#elif LJ_TARGET_X64
154#if LJ_ABI_WIN
155 /* Return structs of size 1, 2, 4 or 8 in a GPR. */
156 cc->retref = !(sz == 1 || sz == 2 || sz == 4 || sz == 8);
157#else
158 if (sz <= 16) goto err_nyi; /* NYI: crazy x64 rules for structs. */
159 cc->retref = 1; /* Return all bigger structs by reference. */
160#endif
161#else
162#error "missing definition for handling of struct return values"
163#endif
164 }
165 /* Pass reference to returned aggregate in first argument. */
166 if (cc->retref) {
167 if (ngpr < maxgpr)
168 cc->gpr[ngpr++] = (GPRArg)cdataptr(cd);
169 else
170 cc->stack[nsp++] = (GPRArg)cdataptr(cd);
171 }
172#if LJ_TARGET_X86
173 } else if (ctype_isfp(ctr->info)) {
174 cc->resx87 = ctr->size == sizeof(float) ? 1 : 2;
175#endif
176 }
177
178 /* Walk through all passed arguments. */
179 for (fid = ct->sib, o = L->base+1; o < top; o++) {
180 CTypeID did;
181 CType *d;
182 CTSize sz;
183 MSize n, isfp = 0, isva = 0;
184 void *dp, *rp = NULL;
185
186 if (fid) { /* Get argument type from field. */
187 CType *ctf = ctype_get(cts, fid);
188 fid = ctf->sib;
189 lua_assert(ctype_isfield(ctf->info));
190 did = ctype_cid(ctf->info);
191 } else {
192 if (!(ct->info & CTF_VARARG))
193 lj_err_caller(L, LJ_ERR_FFI_NUMARG); /* Too many arguments. */
194 did = ccall_ctid_vararg(cts, o); /* Infer vararg type. */
195 isva = 1;
196 }
197 d = ctype_raw(cts, did);
198 sz = d->size;
199
200 /* Find out how (by value/ref) and where (GPR/FPR) to pass an argument. */
201 if (ctype_isnum(d->info)) {
202 if (sz > 8) goto err_nyi;
203 if ((d->info & CTF_FP)) {
204 isfp = 1;
205 } else if (sz < CTSIZE_PTR) {
206 d = ctype_get(cts, CTID_INT_PSZ);
207 }
208 } else if (ctype_isvector(d->info)) {
209 if (CCALL_VECTOR_REG && (sz == 8 || sz == 16))
210 isfp = 1;
211 else
212 goto err_nyi;
213 } else if (ctype_iscomplex(d->info)) {
214#if CCALL_COMPLEX_ARGREF
215 rp = cdataptr(lj_cdata_new(cts, did, sz));
216 sz = CTSIZE_PTR;
217#else
218 isfp = 2;
219#endif
220 } else if (ctype_isstruct(d->info)) {
221 int sref = CCALL_STRUCT_ARGREF;
222#if LJ_TARGET_X86
223 ngpr = maxgpr; /* Pass all structs by value on the stack. */
224#elif LJ_TARGET_X64
225#if LJ_ABI_WIN
226 /* Pass structs of size 1, 2, 4 or 8 in a GPR by value. */
227 sref = !(sz == 1 || sz == 2 || sz == 4 || sz == 8);
228#else
229 if (sz <= 16) goto err_nyi; /* NYI: crazy x64 rules for structs. */
230 /* Pass all bigger structs by value on the stack. */
231#endif
232#endif
233 if (sref) { /* Pass struct by reference. */
234 rp = cdataptr(lj_cdata_new(cts, did, sz));
235 sz = CTSIZE_PTR; /* Pass all other structs by reference. */
236 }
237 } else {
238 sz = CTSIZE_PTR;
239 }
240 sz = (sz + CTSIZE_PTR-1) & ~(CTSIZE_PTR-1);
241 n = sz / CTSIZE_PTR; /* Number of GPRs or stack slots needed. */
242
243 CCALL_HANDLE_REGARG /* Handle register arguments. */
244
245 /* Otherwise pass argument on stack. */
246 if (CCALL_ALIGN_STACKARG && !rp && (d->info & CTF_ALIGN) > CTALIGN_PTR) {
247 MSize align = (1u << ctype_align(d->info-CTALIGN_PTR)) -1;
248 nsp = (nsp + align) & ~align; /* Align argument on stack. */
249 }
250 if (nsp + n >= CCALL_MAXSTACK) { /* Too many arguments. */
251 err_nyi:
252 lj_err_caller(L, LJ_ERR_FFI_NYICALL);
253 }
254 dp = &cc->stack[nsp];
255 nsp += n;
256 isva = 0;
257
258 done:
259 if (rp) { /* Pass by reference. */
260 *(void **)dp = rp;
261 dp = rp;
262 }
263 lj_cconv_ct_tv(cts, d, (uint8_t *)dp, o, 0);
264#if LJ_TARGET_X64 && LJ_ABI_WIN
265 if (isva) { /* Windows/x64 mirrors varargs in both register sets. */
266 if (nfpr == ngpr)
267 cc->gpr[ngpr-1] = cc->fpr[ngpr-1].l[0];
268 else
269 cc->fpr[ngpr-1].l[0] = cc->gpr[ngpr-1];
270 }
271#endif
272 }
273 if (fid) lj_err_caller(L, LJ_ERR_FFI_NUMARG); /* Too few arguments. */
274
275#if LJ_TARGET_X64
276 cc->nfpr = nfpr; /* Required for vararg functions. */
277#endif
278 cc->nsp = nsp;
279 cc->spadj = (CCALL_SPS_FREE + CCALL_SPS_EXTRA)*CTSIZE_PTR;
280 if (nsp > CCALL_SPS_FREE)
281 cc->spadj += (((nsp-CCALL_SPS_FREE)*CTSIZE_PTR + 15u) & ~15u);
282}
283
284/* Get results from C call. */
285static int ccall_get_results(lua_State *L, CTState *cts, CType *ct,
286 CCallState *cc)
287{
288 CType *ctr = ctype_rawchild(cts, ct);
289 void *sp = &cc->gpr[0];
290 if (ctype_isvoid(ctr->info))
291 return 0; /* Zero results. */
292 if (ctype_isstruct(ctr->info)) {
293 if (!CCALL_STRUCT_RETREF && !cc->retref) {
294 void *dp = cdataptr(cdataV(L->top-1)); /* Use preallocated object. */
295 memcpy(dp, sp, ctr->size); /* Copy struct return value from GPRs. */
296 }
297 return 1; /* Return cdata object which is already on top of stack. */
298 }
299 if (ctype_iscomplex(ctr->info)) {
300#if !CCALL_COMPLEX_RETREF || !CCALL_COMPLEXF_RETREF
301 void *dp = cdataptr(cdataV(L->top-1)); /* Use preallocated object. */
302#if CCALL_COMPLEX_RETREF && !CCALL_COMPLEXF_RETREF
303 if (ctr->size == 2*sizeof(float))
304 memcpy(dp, sp, ctr->size); /* Copy complex float from GPRs. */
305#elif CCALL_NUM_FPR
306 /* Copy non-contiguous re/im part from FPRs to cdata object. */
307 if (ctr->size == 2*sizeof(float)) {
308 ((float *)dp)[0] = cc->fpr[0].f[0];
309 ((float *)dp)[1] = cc->fpr[1].f[0];
310 } else {
311 ((double *)dp)[0] = cc->fpr[0].d[0];
312 ((double *)dp)[1] = cc->fpr[1].d[0];
313 }
314#else
315 memcpy(dp, sp, ctr->size); /* Copy complex from GPRs. */
316#endif
317#endif
318 return 1; /* Return cdata object which is already on top of stack. */
319 }
320#if CCALL_NUM_FPR
321 if (ctype_isfp(ctr->info) || ctype_isvector(ctr->info))
322 sp = &cc->fpr[0];
323#endif
324 /* No reference types end up here, so there's no need for the CTypeID. */
325 lua_assert(!(ctype_isrefarray(ctr->info) || ctype_isstruct(ctr->info)));
326 lj_cconv_tv_ct(cts, ctr, 0, L->top-1, (uint8_t *)sp);
327 return 1; /* One result. */
328}
329
330/* Call C function. */
331int lj_ccall_func(lua_State *L, GCcdata *cd)
332{
333 CTState *cts = ctype_cts(L);
334 CType *ct = ctype_raw(cts, cd->typeid);
335 CTSize sz = ct->size;
336 void *p = cdataptr(cd);
337 if (ctype_isptr(ct->info))
338 ct = ctype_rawchild(cts, ct);
339 if (ctype_isfunc(ct->info)) {
340 CCallState cc;
341 cc.func = (void (*)(void))cdata_getptr(p, sz);
342 ccall_set_args(L, cts, ct, &cc);
343 lj_vm_ffi_call(&cc);
344 return ccall_get_results(L, cts, ct, &cc);
345 }
346 return -1; /* Not a function. */
347}
348
349#endif