diff options
| author | Mike Pall <mike> | 2026-05-25 01:15:29 +0200 |
|---|---|---|
| committer | Mike Pall <mike> | 2026-05-25 01:15:29 +0200 |
| commit | 5b2e51db2c5e445cb98e026fc1e290c14eca67c1 (patch) | |
| tree | 8eeb8aa6fcdf56e54145636e96255136e667beb2 | |
| parent | 18b087cd2cd4ddc4a79782bf155383a689d5093d (diff) | |
| download | luajit-5b2e51db2c5e445cb98e026fc1e290c14eca67c1.tar.gz luajit-5b2e51db2c5e445cb98e026fc1e290c14eca67c1.tar.bz2 luajit-5b2e51db2c5e445cb98e026fc1e290c14eca67c1.zip | |
FFI: Various ABI and calling convention fixes.
Thanks to Sergey Kaplun. #1455
| -rw-r--r-- | src/lj_ccall.c | 59 | ||||
| -rw-r--r-- | src/lj_cparse.c | 9 | ||||
| -rw-r--r-- | src/lj_ctype.h | 2 |
3 files changed, 57 insertions, 13 deletions
diff --git a/src/lj_ccall.c b/src/lj_ccall.c index f5379e960..2446ed132 100644 --- a/src/lj_ccall.c +++ b/src/lj_ccall.c | |||
| @@ -168,7 +168,9 @@ | |||
| 168 | if (ccall_struct_arg(cc, cts, d, rcl, o, narg)) goto err_nyi; \ | 168 | if (ccall_struct_arg(cc, cts, d, rcl, o, narg)) goto err_nyi; \ |
| 169 | nsp = cc->nsp; ngpr = cc->ngpr; nfpr = cc->nfpr; \ | 169 | nsp = cc->nsp; ngpr = cc->ngpr; nfpr = cc->nfpr; \ |
| 170 | continue; \ | 170 | continue; \ |
| 171 | } /* Pass all other structs by value on stack. */ | 171 | } else { /* Pass all other structs by value on stack. */ \ |
| 172 | onstack = 1; \ | ||
| 173 | } | ||
| 172 | 174 | ||
| 173 | #define CCALL_HANDLE_COMPLEXARG \ | 175 | #define CCALL_HANDLE_COMPLEXARG \ |
| 174 | isfp = 2; /* Pass complex in FPRs or on stack. Needs postprocessing. */ | 176 | isfp = 2; /* Pass complex in FPRs or on stack. Needs postprocessing. */ |
| @@ -183,7 +185,7 @@ | |||
| 183 | } \ | 185 | } \ |
| 184 | } else { /* Try to pass argument in GPRs. */ \ | 186 | } else { /* Try to pass argument in GPRs. */ \ |
| 185 | /* Note that reordering is explicitly allowed in the x64 ABI. */ \ | 187 | /* Note that reordering is explicitly allowed in the x64 ABI. */ \ |
| 186 | if (n <= 2 && ngpr + n <= maxgpr) { \ | 188 | if (!onstack && n <= 2 && ngpr + n <= maxgpr) { \ |
| 187 | dp = &cc->gpr[ngpr]; \ | 189 | dp = &cc->gpr[ngpr]; \ |
| 188 | ngpr += n; \ | 190 | ngpr += n; \ |
| 189 | goto done; \ | 191 | goto done; \ |
| @@ -350,7 +352,7 @@ | |||
| 350 | nfpr = CCALL_NARG_FPR; /* Prevent reordering. */ \ | 352 | nfpr = CCALL_NARG_FPR; /* Prevent reordering. */ \ |
| 351 | } \ | 353 | } \ |
| 352 | } else { /* Try to pass argument in GPRs. */ \ | 354 | } else { /* Try to pass argument in GPRs. */ \ |
| 353 | if (!LJ_TARGET_OSX && (d->info & CTF_ALIGN) > CTALIGN_PTR) \ | 355 | if (!LJ_TARGET_OSX && !rp && ccall_struct_align(cts, d) > CTALIGN_PTR) \ |
| 354 | ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \ | 356 | ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \ |
| 355 | if (ngpr + n <= maxgpr) { \ | 357 | if (ngpr + n <= maxgpr) { \ |
| 356 | dp = &cc->gpr[ngpr]; \ | 358 | dp = &cc->gpr[ngpr]; \ |
| @@ -661,7 +663,7 @@ static int ccall_classify_struct(CTState *cts, CType *ct, int *rcl, CTSize ofs) | |||
| 661 | fofs = ofs+ct->size; | 663 | fofs = ofs+ct->size; |
| 662 | if (ctype_isfield(ct->info)) | 664 | if (ctype_isfield(ct->info)) |
| 663 | ccall_classify_ct(cts, ctype_rawchild(cts, ct), rcl, fofs); | 665 | ccall_classify_ct(cts, ctype_rawchild(cts, ct), rcl, fofs); |
| 664 | else if (ctype_isbitfield(ct->info)) | 666 | else if (ctype_isbitfield(ct->info) && ctype_bitbsz(ct->info)) |
| 665 | rcl[(fofs >= 8)] |= CCALL_RCL_INT; /* NYI: unaligned bitfields? */ | 667 | rcl[(fofs >= 8)] |= CCALL_RCL_INT; /* NYI: unaligned bitfields? */ |
| 666 | else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) | 668 | else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) |
| 667 | ccall_classify_struct(cts, ctype_rawchild(cts, ct), rcl, fofs); | 669 | ccall_classify_struct(cts, ctype_rawchild(cts, ct), rcl, fofs); |
| @@ -700,8 +702,11 @@ static int ccall_struct_arg(CCallState *cc, CTState *cts, CType *d, int *rcl, | |||
| 700 | if (ccall_struct_reg(cc, cts, dp, rcl)) { | 702 | if (ccall_struct_reg(cc, cts, dp, rcl)) { |
| 701 | /* Register overflow? Pass on stack. */ | 703 | /* Register overflow? Pass on stack. */ |
| 702 | MSize nsp = cc->nsp, sz = rcl[1] ? 2*CTSIZE_PTR : CTSIZE_PTR; | 704 | MSize nsp = cc->nsp, sz = rcl[1] ? 2*CTSIZE_PTR : CTSIZE_PTR; |
| 705 | MSize align = (1u << ctype_align(d->info)) - 1; | ||
| 703 | if (nsp + sz > CCALL_SIZE_STACK) | 706 | if (nsp + sz > CCALL_SIZE_STACK) |
| 704 | return 1; /* Too many arguments. */ | 707 | return 1; /* Too many arguments. */ |
| 708 | if (CCALL_ALIGN_STACKARG && align > CTSIZE_PTR-1) | ||
| 709 | nsp = (nsp + align) & ~align; /* Align argument on stack. */ | ||
| 705 | cc->nsp = nsp + sz; | 710 | cc->nsp = nsp + sz; |
| 706 | memcpy((uint8_t *)cc->stack + nsp, dp, sz); | 711 | memcpy((uint8_t *)cc->stack + nsp, dp, sz); |
| 707 | } | 712 | } |
| @@ -776,6 +781,31 @@ noth: /* Not a homogeneous float/double aggregate. */ | |||
| 776 | 781 | ||
| 777 | #if LJ_TARGET_ARM64 | 782 | #if LJ_TARGET_ARM64 |
| 778 | 783 | ||
| 784 | #if !LJ_TARGET_OSX | ||
| 785 | /* Alignment of pass-by-value structs: 8 or 16. */ | ||
| 786 | static CTInfo ccall_struct_align_arm64(CTState *cts, CType *ct) | ||
| 787 | { | ||
| 788 | CTSize sz; | ||
| 789 | if (ct->sib) { | ||
| 790 | while (ct->sib) { | ||
| 791 | ct = ctype_get(cts, ct->sib); | ||
| 792 | if (ctype_isfield(ct->info)) { | ||
| 793 | if ((ct->info & CTF_ALIGN) > CTALIGN_PTR) return CTALIGN(4); | ||
| 794 | } else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) { | ||
| 795 | CType *sct = ctype_rawchild(cts, ct); | ||
| 796 | CTInfo info = lj_ctype_info(cts, ctype_typeid(cts, sct), &sz); | ||
| 797 | if ((info & CTF_ALIGN) > CTALIGN_PTR) return CTALIGN(4); | ||
| 798 | } | ||
| 799 | } | ||
| 800 | } else { | ||
| 801 | CTInfo info = lj_ctype_info(cts, ctype_typeid(cts, ct), &sz); | ||
| 802 | if ((info & CTF_ALIGN) > CTALIGN_PTR) return CTALIGN(4); | ||
| 803 | } | ||
| 804 | return CTALIGN_PTR; | ||
| 805 | } | ||
| 806 | #define ccall_struct_align(cts, ct) ccall_struct_align_arm64((cts), (ct)) | ||
| 807 | #endif | ||
| 808 | |||
| 779 | /* Classify a struct based on its fields. */ | 809 | /* Classify a struct based on its fields. */ |
| 780 | static unsigned int ccall_classify_struct(CTState *cts, CType *ct) | 810 | static unsigned int ccall_classify_struct(CTState *cts, CType *ct) |
| 781 | { | 811 | { |
| @@ -787,10 +817,10 @@ static unsigned int ccall_classify_struct(CTState *cts, CType *ct) | |||
| 787 | ct = ctype_get(cts, ct->sib); | 817 | ct = ctype_get(cts, ct->sib); |
| 788 | if (ctype_isfield(ct->info)) { | 818 | if (ctype_isfield(ct->info)) { |
| 789 | sct = ctype_rawchild(cts, ct); | 819 | sct = ctype_rawchild(cts, ct); |
| 790 | if (ctype_isarray(sct->info)) { | 820 | if (ctype_isarray(sct->info) && !sct->size) goto noth; |
| 821 | while (ctype_isarray(sct->info)) { | ||
| 791 | CType *cct = ctype_rawchild(cts, sct); | 822 | CType *cct = ctype_rawchild(cts, sct); |
| 792 | if (!cct->size) continue; | 823 | m *= sct->size / cct->size; |
| 793 | m = sct->size / cct->size; | ||
| 794 | sct = cct; | 824 | sct = cct; |
| 795 | } | 825 | } |
| 796 | if (ctype_isfp(sct->info)) { | 826 | if (ctype_isfp(sct->info)) { |
| @@ -804,7 +834,7 @@ static unsigned int ccall_classify_struct(CTState *cts, CType *ct) | |||
| 804 | } else { | 834 | } else { |
| 805 | goto noth; | 835 | goto noth; |
| 806 | } | 836 | } |
| 807 | } else if (ctype_isbitfield(ct->info)) { | 837 | } else if (ctype_isbitfield(ct->info) && ctype_bitbsz(ct->info)) { |
| 808 | goto noth; | 838 | goto noth; |
| 809 | } else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) { | 839 | } else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) { |
| 810 | sct = ctype_rawchild(cts, ct); | 840 | sct = ctype_rawchild(cts, ct); |
| @@ -899,6 +929,11 @@ static void ccall_copy_struct(CCallState *cc, CType *ctr, void *dp, void *sp, | |||
| 899 | 929 | ||
| 900 | #endif | 930 | #endif |
| 901 | 931 | ||
| 932 | #ifndef ccall_struct_align | ||
| 933 | /* Alignment of pass-by-value structs. */ | ||
| 934 | #define ccall_struct_align(cts, ct) ((ct)->info & CTF_ALIGN) | ||
| 935 | #endif | ||
| 936 | |||
| 902 | /* -- Common C call handling ---------------------------------------------- */ | 937 | /* -- Common C call handling ---------------------------------------------- */ |
| 903 | 938 | ||
| 904 | /* Infer the destination CTypeID for a vararg argument. | 939 | /* Infer the destination CTypeID for a vararg argument. |
| @@ -1013,6 +1048,9 @@ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct, | |||
| 1013 | CTSize sz; | 1048 | CTSize sz; |
| 1014 | MSize n, isfp = 0, isva = 0; | 1049 | MSize n, isfp = 0, isva = 0; |
| 1015 | void *dp, *rp = NULL; | 1050 | void *dp, *rp = NULL; |
| 1051 | #if LJ_TARGET_X64 && !LJ_ABI_WIN | ||
| 1052 | int onstack = 0; | ||
| 1053 | #endif | ||
| 1016 | 1054 | ||
| 1017 | if (fid) { /* Get argument type from field. */ | 1055 | if (fid) { /* Get argument type from field. */ |
| 1018 | CType *ctf = ctype_get(cts, fid); | 1056 | CType *ctf = ctype_get(cts, fid); |
| @@ -1051,7 +1089,10 @@ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct, | |||
| 1051 | 1089 | ||
| 1052 | /* Otherwise pass argument on stack. */ | 1090 | /* Otherwise pass argument on stack. */ |
| 1053 | if (CCALL_ALIGN_STACKARG) { /* Align argument on stack. */ | 1091 | if (CCALL_ALIGN_STACKARG) { /* Align argument on stack. */ |
| 1054 | MSize align = (1u << ctype_align(d->info)) - 1; | 1092 | MSize align = (1u << ctype_align(ccall_struct_align(cts, d))) - 1; |
| 1093 | #if LJ_TARGET_ARM64 && LJ_TARGET_OSX | ||
| 1094 | isva = 1; | ||
| 1095 | #endif | ||
| 1055 | if (rp || (CCALL_PACK_STACKARG && isva && align < CTSIZE_PTR-1)) | 1096 | if (rp || (CCALL_PACK_STACKARG && isva && align < CTSIZE_PTR-1)) |
| 1056 | align = CTSIZE_PTR-1; | 1097 | align = CTSIZE_PTR-1; |
| 1057 | nsp = (nsp + align) & ~align; | 1098 | nsp = (nsp + align) & ~align; |
diff --git a/src/lj_cparse.c b/src/lj_cparse.c index a1361a128..56e3e8491 100644 --- a/src/lj_cparse.c +++ b/src/lj_cparse.c | |||
| @@ -1309,14 +1309,16 @@ static void cp_struct_layout(CPState *cp, CTypeID sid, CTInfo sattr) | |||
| 1309 | align = ctype_align(attr); | 1309 | align = ctype_align(attr); |
| 1310 | if (cp->packstack[cp->curpack] < align) | 1310 | if (cp->packstack[cp->curpack] < align) |
| 1311 | align = cp->packstack[cp->curpack]; | 1311 | align = cp->packstack[cp->curpack]; |
| 1312 | if (align > maxalign) maxalign = align; | 1312 | bsz = ctype_bitcsz(ct->info); /* Bitfield size (temp.). */ |
| 1313 | if (align > maxalign && bsz) maxalign = align; | ||
| 1313 | amask = (8u << align) - 1; | 1314 | amask = (8u << align) - 1; |
| 1314 | 1315 | ||
| 1315 | bsz = ctype_bitcsz(ct->info); /* Bitfield size (temp.). */ | ||
| 1316 | if (bsz == CTBSZ_FIELD || !ctype_isfield(ct->info)) { | 1316 | if (bsz == CTBSZ_FIELD || !ctype_isfield(ct->info)) { |
| 1317 | bsz = csz; /* Regular fields or subtypes always fill the container. */ | 1317 | bsz = csz; /* Regular fields or subtypes always fill the container. */ |
| 1318 | bofs = (bofs + amask) & ~amask; /* Start new aligned field. */ | 1318 | bofs = (bofs + amask) & ~amask; /* Start new aligned field. */ |
| 1319 | ct->size = (bofs >> 3); /* Store field offset. */ | 1319 | ct->size = (bofs >> 3); /* Store field offset. */ |
| 1320 | if (ctype_isfield(ct->info)) | ||
| 1321 | ct->info = CTINFO(CT_FIELD, ctype_cid(ct->info)) + CTALIGN(align); | ||
| 1320 | } else { /* Bitfield. */ | 1322 | } else { /* Bitfield. */ |
| 1321 | if (bsz == 0 || (attr & CTFP_ALIGNED) || | 1323 | if (bsz == 0 || (attr & CTFP_ALIGNED) || |
| 1322 | (!((attr|sattr) & CTFP_PACKED) && (bofs & amask) + bsz > csz)) | 1324 | (!((attr|sattr) & CTFP_PACKED) && (bofs & amask) + bsz > csz)) |
| @@ -1324,7 +1326,8 @@ static void cp_struct_layout(CPState *cp, CTypeID sid, CTInfo sattr) | |||
| 1324 | 1326 | ||
| 1325 | /* Prefer regular field over bitfield. */ | 1327 | /* Prefer regular field over bitfield. */ |
| 1326 | if (bsz == csz && (bofs & amask) == 0) { | 1328 | if (bsz == csz && (bofs & amask) == 0) { |
| 1327 | ct->info = CTINFO(CT_FIELD, ctype_cid(ct->info)); | 1329 | ct->info = CTINFO(CT_FIELD, ctype_cid(ct->info)) + |
| 1330 | CTALIGN(lj_fls(sz)); | ||
| 1328 | ct->size = (bofs >> 3); /* Store field offset. */ | 1331 | ct->size = (bofs >> 3); /* Store field offset. */ |
| 1329 | } else { | 1332 | } else { |
| 1330 | if (csz > amask+1 && bsz <= amask+1) | 1333 | if (csz > amask+1 && bsz <= amask+1) |
diff --git a/src/lj_ctype.h b/src/lj_ctype.h index 912fe2152..43d507b14 100644 --- a/src/lj_ctype.h +++ b/src/lj_ctype.h | |||
| @@ -51,7 +51,7 @@ LJ_STATIC_ASSERT(((int)CT_STRUCT & (int)CT_ARRAY) == CT_STRUCT); | |||
| 51 | ** |FUNC ....VS.. cc cid | nargs | field | name? | name? | | 51 | ** |FUNC ....VS.. cc cid | nargs | field | name? | name? | |
| 52 | ** |TYPEDEF cid | | | name | name | | 52 | ** |TYPEDEF cid | | | name | name | |
| 53 | ** |ATTRIB attrnum cid | attr | sib? | type? | | | 53 | ** |ATTRIB attrnum cid | attr | sib? | type? | | |
| 54 | ** |FIELD cid | offset | field | | name? | | 54 | ** |FIELD A cid | offset | field | | name? | |
| 55 | ** |BITFIELD B.cvU csz bsz pos | offset | field | | name? | | 55 | ** |BITFIELD B.cvU csz bsz pos | offset | field | | name? | |
| 56 | ** |CONSTVAL c cid | value | const | name | name | | 56 | ** |CONSTVAL c cid | value | const | name | name | |
| 57 | ** |EXTERN cid | | sib? | name | name | | 57 | ** |EXTERN cid | | sib? | name | name | |
