diff options
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. */ |
