#endif
+/* -- ARM64 ABI struct classification ------------------------------------- */
+
+#if LJ_TARGET_ARM64
+
+/* Classify a struct based on its fields. */
+static unsigned int ccall_classify_struct(CTState *cts, CType *ct)
+{
+ CTSize sz = ct->size;
+ unsigned int r = 0, n = 0, isu = (ct->info & CTF_UNION);
+ while (ct->sib && n <= 4) {
+ unsigned int m = 1;
+ CType *sct;
+ ct = ctype_get(cts, ct->sib);
+ if (ctype_isfield(ct->info)) {
+ sct = ctype_rawchild(cts, ct);
+ if (ctype_isarray(sct->info)) {
+ CType *cct = ctype_rawchild(cts, sct);
+ if (!cct->size) continue;
+ m = sct->size / cct->size;
+ sct = cct;
+ }
+ if (ctype_isfp(sct->info)) {
+ r |= sct->size;
+ if (!isu) n += m; else if (n < m) n = m;
+ } else if (ctype_iscomplex(sct->info)) {
+ r |= (sct->size >> 1);
+ if (!isu) n += 2*m; else if (n < 2*m) n = 2*m;
+ } else if (ctype_isstruct(sct->info)) {
+ goto substruct;
+ } else {
+ goto noth;
+ }
+ } else if (ctype_isbitfield(ct->info)) {
+ goto noth;
+ } else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) {
+ sct = ctype_rawchild(cts, ct);
+ substruct:
+ if (sct->size > 0) {
+ unsigned int s = ccall_classify_struct(cts, sct), sn;
+ if (s <= 1) goto noth;
+ r |= (s & 255);
+ sn = (s >> 8) * m;
+ if (!isu) n += sn; else if (n < sn) n = sn;
+ }
+ }
+ }
+ if ((r == 4 || r == 8) && n <= 4)
+ return r + (n << 8);
+noth: /* Not a homogeneous float/double aggregate. */
+ return (sz <= 16); /* Return structs of size <= 16 in GPRs. */
+}
+
+#endif
+
+/* -- MIPS64 ABI struct classification ---------------------------- */
+
+#if LJ_TARGET_MIPS64
+
+#define FTYPE_FLOAT 1
+#define FTYPE_DOUBLE 2
+
+/* Classify FP fields (max. 2) and their types. */
+static unsigned int ccall_classify_struct(CTState *cts, CType *ct, CType *ctf)
+{
+ int n = 0, ft = 0;
+ if ((ctf->info & CTF_VARARG) || (ct->info & CTF_UNION))
+ goto noth;
+ while (ct->sib) {
+ CType *sct;
+ ct = ctype_get(cts, ct->sib);
+ if (n == 2) {
+ goto noth;
+ } else if (ctype_isfield(ct->info)) {
+ sct = ctype_rawchild(cts, ct);
+ if (ctype_isfp(sct->info)) {
+ ft |= (sct->size == 4 ? FTYPE_FLOAT : FTYPE_DOUBLE) << 2*n;
+ n++;
+ } else {
+ goto noth;
+ }
+ } else if (ctype_isbitfield(ct->info) ||
+ ctype_isxattrib(ct->info, CTA_SUBTYPE)) {
+ goto noth;
+ }
+ }
+ if (n <= 2)
+ return ft;
+noth: /* Not a homogeneous float/double aggregate. */
+ return 0; /* Struct is in GPRs. */
+}
+
+static void ccall_copy_struct(CCallState *cc, CType *ctr, void *dp, void *sp,
+ int ft)
+{
+ if (LJ_ABI_SOFTFP ? ft :
+ ((ft & 3) == FTYPE_FLOAT || (ft >> 2) == FTYPE_FLOAT)) {
+ int i, ofs = 0;
+ for (i = 0; ft != 0; i++, ft >>= 2) {
+ if ((ft & 3) == FTYPE_FLOAT) {
+#if LJ_ABI_SOFTFP
+ /* The 2nd FP struct result is in CARG1 (gpr[2]) and not CRET2. */
+ memcpy((uint8_t *)dp + ofs,
+ (uint8_t *)&cc->gpr[2*i] + LJ_ENDIAN_SELECT(0, 4), 4);
+#else
+ *(float *)((uint8_t *)dp + ofs) = cc->fpr[i].f;
+#endif
+ ofs += 4;
+ } else {
+ ofs = (ofs + 7) & ~7; /* 64 bit alignment. */
+#if LJ_ABI_SOFTFP
+ *(intptr_t *)((uint8_t *)dp + ofs) = cc->gpr[2*i];
+#else
+ *(double *)((uint8_t *)dp + ofs) = cc->fpr[i].d;
+#endif
+ ofs += 8;
+ }
+ }
+ } else {
+#if !LJ_ABI_SOFTFP
+ if (ft) sp = (uint8_t *)&cc->fpr[0];
+#endif
+ memcpy(dp, sp, ctr->size);
+ }
+}
+
+#endif
+
/* -- Common C call handling ---------------------------------------------- */
- /* Infer the destination CTypeID for a vararg argument. */
+ /* Infer the destination CTypeID for a vararg argument.
+ ** Note: may reallocate cts->tab and invalidate CType pointers.
+ */
CTypeID lj_ccall_ctid_vararg(CTState *cts, cTValue *o)
{
if (tvisnumber(o)) {
fid = ctf->sib;
}
- if ((ct->info & CTF_VARARG)) {
+#if LJ_TARGET_ARM64 && LJ_ABI_WIN
++ if ((info & CTF_VARARG)) {
+ nsp -= maxgpr * CTSIZE_PTR; /* May end up with negative nsp. */
+ ngpr = maxgpr;
+ nfpr = CCALL_NARG_FPR;
+ }
+#endif
+
/* Walk through all passed arguments. */
for (o = L->base+1, narg = 1; o < top; o++, narg++) {
CTypeID did;
if (fid) { /* Get argument type from field. */
CType *ctf = ctype_get(cts, fid);
fid = ctf->sib;
- lua_assert(ctype_isfield(ctf->info));
+ lj_assertL(ctype_isfield(ctf->info), "field expected");
did = ctype_cid(ctf->info);
} else {
- if (!(ct->info & CTF_VARARG))
+ if (!(info & CTF_VARARG))
lj_err_caller(L, LJ_ERR_FFI_NUMARG); /* Too many arguments. */
did = lj_ccall_ctid_vararg(cts, o); /* Infer vararg type. */
isva = 1;
lj_vm_ffi_call(&cc);
if (cts->cb.slot != ~0u) { /* Blacklist function that called a callback. */
TValue tv;
- setlightudV(&tv, (void *)cc.func);
+ tv.u64 = ((uintptr_t)(void *)cc.func >> 2) | U64x(800000000, 00000000);
setboolV(lj_tab_set(L, cts->miscmap, &tv), 1);
}
- ct = (CType *)((intptr_t)ct+(intptr_t)cts->tab); /* May be reallocated. */
+ ct = ctype_get(cts, id); /* Table may have been reallocated. */
gcsteps += ccall_get_results(L, cts, ct, &cc, &ret);
#if LJ_TARGET_X86 && LJ_ABI_WIN
/* Automatically detect __stdcall and fix up C function declaration. */
crec_ct_tv(J, d, dp, lj_ir_kint(J, 0), &tv);
}
}
- /* Handle __gc metamethod. */
- fin = lj_ctype_meta(cts, id, MM_gc);
- if (fin)
- crec_finalizer(J, trcd, fin);
}
+ J->base[0] = trcd;
+ /* Handle __gc metamethod. */
+ fin = lj_ctype_meta(cts, id, MM_gc);
+ if (fin)
+ crec_finalizer(J, trcd, 0, fin);
}
- /* Record argument conversions. */
+ /* Record argument conversions.
+ ** Note: may reallocate cts->tab and invalidate CType pointers.
+ */
static TRef crec_call_args(jit_State *J, RecordFFData *rd,
CTState *cts, CType *ct)
{
TRef *arg0 = NULL, *arg1 = NULL;
#endif
int ngpr = 0;
- if (ctype_cconv(ct->info) == CTCC_THISCALL)
+ if (ctype_cconv(info) == CTCC_THISCALL)
ngpr = 1;
- else if (ctype_cconv(ct->info) == CTCC_FASTCALL)
+ else if (ctype_cconv(info) == CTCC_FASTCALL)
ngpr = 2;
+#elif LJ_TARGET_ARM64 && LJ_TARGET_OSX
+ int ngpr = CCALL_NARG_GPR;
#endif
/* Skip initial attributes. */
if (fid) { /* Get argument type from field. */
CType *ctf = ctype_get(cts, fid);
fid = ctf->sib;
- lua_assert(ctype_isfield(ctf->info));
+ lj_assertJ(ctype_isfield(ctf->info), "field expected");
did = ctype_cid(ctf->info);
} else {
- if (!(ct->info & CTF_VARARG))
+ if (!(info & CTF_VARARG))
lj_trace_err(J, LJ_TRERR_NYICALL); /* Too many arguments. */
+#if LJ_TARGET_ARM64 && LJ_TARGET_OSX
+ if (ngpr >= 0) {
+ ngpr = -1;
+ args[n++] = TREF_NIL; /* Marker for start of varargs. */
+ if (n >= CCI_NARGS_MAX)
+ lj_trace_err(J, LJ_TRERR_NYICALL);
+ }
+#endif
did = lj_ccall_ctid_vararg(cts, o); /* Infer vararg type. */
}
d = ctype_raw(cts, did);