Tarantool development patches archive
 help / color / mirror / Atom feed
From: Sergey Bronnikov via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: Sergey Kaplun <skaplun@tarantool.org>,
	Evgeniy Temirgaleev <e.temirgaleev@tarantool.org>
Cc: tarantool-patches@dev.tarantool.org
Subject: Re: [Tarantool-patches] [PATCH luajit 4/5] FFI: Various ABI and calling convention fixes.
Date: Mon, 1 Jun 2026 16:02:10 +0300	[thread overview]
Message-ID: <8e8cab09-9dfd-4cbd-bd55-a74cc491955b@tarantool.org> (raw)
In-Reply-To: <20260530160409.4043089-5-skaplun@tarantool.org>

[-- Attachment #1: Type: text/plain, Size: 55011 bytes --]

Hi, Sergey,

thanks for the patch! See my comments below.

It looks like you tried to test the patch as thoroughly as possible.

Could you tell me how you compiled your test list?

Is there a matrix of possible types and structures you used? How do you 
assess the completeness of testing?

Sergey

On 5/30/26 19:04, Sergey Kaplun wrote:
> From: Mike Pall <mike>
>
> Thanks to Sergey Kaplun.
>
> (cherry picked from commit 5b2e51db2c5e445cb98e026fc1e290c14eca67c1)
>
> This patch fixes several issues at once:
>
> 1) On x64, the structure pass-by-value on the stack for the small
>     argument size lacks the alignment check. This patch fixes that.
>
> 2 ) According to the AAPCS64, the alignment of the argument to be passed
>      by value is determined by its natural alignment [1] instead of the
>      alignment of the type (see B.6 [2]). Not applicable to OSX. For
>      fixing this, we need to store the field alignment for `CT_FIELD`
>      ctype since field alignment determines natural alignment.
>
>      On OSX the "packed" stack rules [3] applied to non-variadic
>      functions too. This patch fixes that. Unfortunately, it breaks the
>      calling convention for native types to be passed by stack for
>      non-variadic functions. This leads to the failure of tests added in
>      the previous commit. This will be fixed in the next commit.
>
> 3) The x64 ABI allows reordering of arguments. The structures that
>     should be passed on the stack due to lack of the corresponding check
>     may be placed in registers anyway. This patch fixes that by adding
>     the corresponding flag in `ccall_set_args()`.
>
> 4) Also, this patch fixes the zero-sized bitfield behaviour to be intact
>     with GCC (after 12.1 [4]) and Clang. Be aware that the alignment of
>     zero-sized fields is not applied to each field of the structure.
>
> 5) It fixes handling for multidimensional HFA structures on arm64.
>     Structures with zero-sized arrays are considered non-HFA.
>
> Also, this commit adds tests for vector arguments and empty structures
> to be sure that their behaviour still valid after changes.
>
> [1]:https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#510composite-types
> [2]:https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#682parameter-passing-rules
> [3]:https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-arguments-to-functions-correctly
> [4]:https://gcc.gnu.org/gcc-12/changes.html
>
> Sergey Kaplun:
> * added the description and the test for the problem
>
> Part of tarantool/tarantool#12480
> ---
>   src/lj_ccall.c                                |  59 +-
>   src/lj_cparse.c                               |   9 +-
>   src/lj_ctype.h                                |   2 +-
>   .../ffi-call-empty-struct.test.lua            |  47 ++
>   test/tarantool-tests/ffi-ccall/CMakeLists.txt |   4 +
>   test/tarantool-tests/ffi-ccall/libfficcall.c  | 587 ++++++++++++++++++
>   .../ffi-vector-arguments.test.lua             |  62 ++
>   .../lj-1455-arm64-ffi-ccall-hfa.test.lua      |  82 +++
>   .../lj-1455-bitfield0-a16.test.lua            |  27 +
>   .../lj-1455-ffi-conventions.test.lua          | 441 +++++++++++++
>   10 files changed, 1307 insertions(+), 13 deletions(-)
>   create mode 100644 test/tarantool-tests/ffi-call-empty-struct.test.lua
>   create mode 100644 test/tarantool-tests/ffi-vector-arguments.test.lua
>   create mode 100644 test/tarantool-tests/lj-1455-arm64-ffi-ccall-hfa.test.lua
>   create mode 100644 test/tarantool-tests/lj-1455-bitfield0-a16.test.lua
>   create mode 100644 test/tarantool-tests/lj-1455-ffi-conventions.test.lua
>
> diff --git a/src/lj_ccall.c b/src/lj_ccall.c
> index 104c9d34..1beccc10 100644
> --- a/src/lj_ccall.c
> +++ b/src/lj_ccall.c
> @@ -168,7 +168,9 @@
>       if (ccall_struct_arg(cc, cts, d, rcl, o, narg)) goto err_nyi; \
>       nsp = cc->nsp; ngpr = cc->ngpr; nfpr = cc->nfpr; \
>       continue; \
> -  }  /* Pass all other structs by value on stack. */
> +  } else {  /* Pass all other structs by value on stack. */ \
> +    onstack = 1; \
> +  }
>   
>   #define CCALL_HANDLE_COMPLEXARG \
>     isfp = 2;  /* Pass complex in FPRs or on stack. Needs postprocessing. */
> @@ -183,7 +185,7 @@
>       } \
>     } else {  /* Try to pass argument in GPRs. */ \
>       /* Note that reordering is explicitly allowed in the x64 ABI. */ \
> -    if (n <= 2 && ngpr + n <= maxgpr) { \
> +    if (!onstack && n <= 2 && ngpr + n <= maxgpr) { \
>         dp = &cc->gpr[ngpr]; \
>         ngpr += n; \
>         goto done; \
> @@ -350,7 +352,7 @@
>         nfpr = CCALL_NARG_FPR;  /* Prevent reordering. */ \
>       } \
>     } else {  /* Try to pass argument in GPRs. */ \
> -    if (!LJ_TARGET_OSX && (d->info & CTF_ALIGN) > CTALIGN_PTR) \
> +    if (!LJ_TARGET_OSX && !rp && ccall_struct_align(cts, d) > CTALIGN_PTR) \
>         ngpr = (ngpr + 1u) & ~1u;  /* Align to regpair. */ \
>       if (ngpr + n <= maxgpr) { \
>         dp = &cc->gpr[ngpr]; \
> @@ -661,7 +663,7 @@ static int ccall_classify_struct(CTState *cts, CType *ct, int *rcl, CTSize ofs)
>       fofs = ofs+ct->size;
>       if (ctype_isfield(ct->info))
>         ccall_classify_ct(cts, ctype_rawchild(cts, ct), rcl, fofs);
> -    else if (ctype_isbitfield(ct->info))
> +    else if (ctype_isbitfield(ct->info) && ctype_bitbsz(ct->info))
>         rcl[(fofs >= 8)] |= CCALL_RCL_INT;  /* NYI: unaligned bitfields? */
>       else if (ctype_isxattrib(ct->info, CTA_SUBTYPE))
>         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,
>     if (ccall_struct_reg(cc, cts, dp, rcl)) {
>       /* Register overflow? Pass on stack. */
>       MSize nsp = cc->nsp, sz = rcl[1] ? 2*CTSIZE_PTR : CTSIZE_PTR;
> +    MSize align = (1u << ctype_align(d->info)) - 1;
>       if (nsp + sz > CCALL_SIZE_STACK)
>         return 1;  /* Too many arguments. */
> +    if (CCALL_ALIGN_STACKARG && align > CTSIZE_PTR-1)
> +      nsp = (nsp + align) & ~align;  /* Align argument on stack. */
>       cc->nsp = nsp + sz;
>       memcpy((uint8_t *)cc->stack + nsp, dp, sz);
>     }
> @@ -776,6 +781,31 @@ noth:  /* Not a homogeneous float/double aggregate. */
>   
>   #if LJ_TARGET_ARM64
>   
> +#if !LJ_TARGET_OSX
> +/* Alignment of pass-by-value structs: 8 or 16. */
> +static CTInfo ccall_struct_align_arm64(CTState *cts, CType *ct)
> +{
> +  CTSize sz;
> +  if (ct->sib) {
> +    while (ct->sib) {
> +      ct = ctype_get(cts, ct->sib);
> +      if (ctype_isfield(ct->info)) {
> +	if ((ct->info & CTF_ALIGN) > CTALIGN_PTR) return CTALIGN(4);
> +      } else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) {
> +	CType *sct = ctype_rawchild(cts, ct);
> +	CTInfo info = lj_ctype_info(cts, ctype_typeid(cts, sct), &sz);
> +	if ((info & CTF_ALIGN) > CTALIGN_PTR) return CTALIGN(4);
> +      }
> +    }
> +  } else  {
> +    CTInfo info = lj_ctype_info(cts, ctype_typeid(cts, ct), &sz);
> +    if ((info & CTF_ALIGN) > CTALIGN_PTR) return CTALIGN(4);
> +  }
> +  return CTALIGN_PTR;
> +}
> +#define ccall_struct_align(cts, ct)	ccall_struct_align_arm64((cts), (ct))
> +#endif
> +
>   /* Classify a struct based on its fields. */
>   static unsigned int ccall_classify_struct(CTState *cts, CType *ct)
>   {
> @@ -787,10 +817,10 @@ static unsigned int ccall_classify_struct(CTState *cts, CType *ct)
>       ct = ctype_get(cts, ct->sib);
>       if (ctype_isfield(ct->info)) {
>         sct = ctype_rawchild(cts, ct);
> -      if (ctype_isarray(sct->info)) {
> +      if (ctype_isarray(sct->info) && !sct->size) goto noth;
> +      while (ctype_isarray(sct->info)) {
>   	CType *cct = ctype_rawchild(cts, sct);
> -	if (!cct->size) continue;
> -	m = sct->size / cct->size;
> +	m *= sct->size / cct->size;
>   	sct = cct;
>         }
>         if (ctype_isfp(sct->info)) {
> @@ -804,7 +834,7 @@ static unsigned int ccall_classify_struct(CTState *cts, CType *ct)
>         } else {
>   	goto noth;
>         }
> -    } else if (ctype_isbitfield(ct->info)) {
> +    } else if (ctype_isbitfield(ct->info) && ctype_bitbsz(ct->info)) {
>         goto noth;
>       } else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) {
>         sct = ctype_rawchild(cts, ct);
> @@ -898,6 +928,11 @@ void ccall_copy_struct(CCallState *cc, CType *ctr, void *dp, void *sp, int ft)
>   
>   #endif
>   
> +#ifndef ccall_struct_align
> +/* Alignment of pass-by-value structs. */
> +#define ccall_struct_align(cts, ct)	((ct)->info & CTF_ALIGN)
> +#endif
> +
>   /* -- Common C call handling ---------------------------------------------- */
>   
>   /* Infer the destination CTypeID for a vararg argument.
> @@ -1004,6 +1039,9 @@ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct,
>       CTSize sz;
>       MSize n, isfp = 0, isva = 0;
>       void *dp, *rp = NULL;
> +#if LJ_TARGET_X64 && !LJ_ABI_WIN
> +    int onstack = 0;
> +#endif
>   
>       if (fid) {  /* Get argument type from field. */
>         CType *ctf = ctype_get(cts, fid);
> @@ -1042,7 +1080,10 @@ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct,
>   
>       /* Otherwise pass argument on stack. */
>       if (CCALL_ALIGN_STACKARG) {  /* Align argument on stack. */
> -      MSize align = (1u << ctype_align(d->info)) - 1;
> +      MSize align = (1u << ctype_align(ccall_struct_align(cts, d))) - 1;
> +#if LJ_TARGET_ARM64 && LJ_TARGET_OSX
> +      isva = 1;
> +#endif
>         if (rp || (CCALL_PACK_STACKARG && isva && align < CTSIZE_PTR-1))
>   	align = CTSIZE_PTR-1;
>         nsp = (nsp + align) & ~align;
> diff --git a/src/lj_cparse.c b/src/lj_cparse.c
> index 9f3b032a..ff23b44b 100644
> --- a/src/lj_cparse.c
> +++ b/src/lj_cparse.c
> @@ -1320,14 +1320,16 @@ static void cp_struct_layout(CPState *cp, CTypeID sid, CTInfo sattr)
>   	align = ctype_align(attr);
>         if (cp->packstack[cp->curpack] < align)
>   	align = cp->packstack[cp->curpack];
> -      if (align > maxalign) maxalign = align;
> +      bsz = ctype_bitcsz(ct->info);  /* Bitfield size (temp.). */
> +      if (align > maxalign && bsz) maxalign = align;
>         amask = (8u << align) - 1;
>   
> -      bsz = ctype_bitcsz(ct->info);  /* Bitfield size (temp.). */
>         if (bsz == CTBSZ_FIELD || !ctype_isfield(ct->info)) {
>   	bsz = csz;  /* Regular fields or subtypes always fill the container. */
>   	bofs = (bofs + amask) & ~amask;  /* Start new aligned field. */
>   	ct->size = (bofs >> 3);  /* Store field offset. */
> +	if (ctype_isfield(ct->info))
> +	  ct->info = CTINFO(CT_FIELD, ctype_cid(ct->info)) + CTALIGN(align);
>         } else {  /* Bitfield. */
>   	if (bsz == 0 || (attr & CTFP_ALIGNED) ||
>   	    (!((attr|sattr) & CTFP_PACKED) && (bofs & amask) + bsz > csz))
> @@ -1335,7 +1337,8 @@ static void cp_struct_layout(CPState *cp, CTypeID sid, CTInfo sattr)
>   
>   	/* Prefer regular field over bitfield. */
>   	if (bsz == csz && (bofs & amask) == 0) {
> -	  ct->info = CTINFO(CT_FIELD, ctype_cid(ct->info));
> +	  ct->info = CTINFO(CT_FIELD, ctype_cid(ct->info)) +
> +		     CTALIGN(lj_fls(sz));
>   	  ct->size = (bofs >> 3);  /* Store field offset. */
>   	} else {
>   	  ct->info = CTINFO(CT_BITFIELD,
> diff --git a/src/lj_ctype.h b/src/lj_ctype.h
> index 2d393eb9..bee3f72a 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);
>   ** |FUNC      ....VS.. cc   cid | nargs  | field | name? | name? |
>   ** |TYPEDEF                 cid |        |       | name  | name  |
>   ** |ATTRIB        attrnum   cid | attr   | sib?  | type? |       |
> -** |FIELD                   cid | offset | field |       | name? |
> +** |FIELD               A   cid | offset | field |       | name? |
>   ** |BITFIELD  B.cvU csz bsz pos | offset | field |       | name? |
>   ** |CONSTVAL    c           cid | value  | const | name  | name  |
>   ** |EXTERN                  cid |        | sib?  | name  | name  |
> diff --git a/test/tarantool-tests/ffi-call-empty-struct.test.lua b/test/tarantool-tests/ffi-call-empty-struct.test.lua
> new file mode 100644
> index 00000000..cb7d3ea2
> --- /dev/null
> +++ b/test/tarantool-tests/ffi-call-empty-struct.test.lua
> @@ -0,0 +1,47 @@
> +local ffi = require('ffi')
> +local tap = require('tap')
> +
> +-- The test file to check FFI correctness for the empty structs.
> +local test = tap.test('ffi-call-empty-struct')
> +
> +test:plan(6)
> +
> +local ffi_ccall = ffi.load('libfficcall')
> +
> +ffi.cdef[[
> +  struct empty {};
> +  struct super_empty {int arg[0];};
> +  struct sort_of_empty {struct super_empty;};
> +
> +  struct empty empty_ret(void);
> +  struct super_empty super_empty_ret(void);
> +  struct sort_of_empty sort_of_empty_ret(void);
> +
> +  int super_empty_arg(struct super_empty e, int a);
> +  int sort_of_empty_arg(struct sort_of_empty e, int a);
> +  int empty_arg(struct empty e, int a);
> +]]
> +
> +local MAGIC = 42LL
> +
> +local empty_t = ffi.typeof('struct empty')
> +local super_empty_t = ffi.typeof('struct super_empty')
> +local sort_of_empty_t = ffi.typeof('struct sort_of_empty')
> +
> +test:is(ffi.typeof(ffi_ccall.empty_ret()), empty_t, 'correct empty ret type')
> +test:is(ffi.typeof(ffi_ccall.super_empty_ret()), super_empty_t,
> +        'correct super_empty ret type')
> +test:is(ffi.typeof(ffi_ccall.sort_of_empty_ret()), sort_of_empty_t,
> +        'correct sort_of_empty ret type')
> +
> +local empty_o = empty_t()
> +local super_empty_o = super_empty_t()
> +local sort_of_empty_o = sort_of_empty_t()
> +
> +test:is(ffi_ccall.empty_arg(empty_o, MAGIC), MAGIC, 'correct empty arg handle')
> +test:is(ffi_ccall.super_empty_arg(super_empty_o, MAGIC), MAGIC,
> +        'correct super_empty arg handle')
> +test:is(ffi_ccall.sort_of_empty_arg(sort_of_empty_o, MAGIC), MAGIC,
> +        'correct sort_of_empty arg handle')
> +
> +test:done(true)
> diff --git a/test/tarantool-tests/ffi-ccall/CMakeLists.txt b/test/tarantool-tests/ffi-ccall/CMakeLists.txt
> index 1d004591..dfd58bd2 100644
> --- a/test/tarantool-tests/ffi-ccall/CMakeLists.txt
> +++ b/test/tarantool-tests/ffi-ccall/CMakeLists.txt
> @@ -1,8 +1,12 @@
>   list(APPEND tests
> +  ffi-call-empty-struct.test.lua
> +  ffi-vector-arguments.test.lua
>     ffi-ccall-arm64-fp-convention.test.lua
>     lj-205-arm64-osx-ffi-enum-arg.test.lua
>     lj-205-arm64-osx-ffi-small-arg.test.lua
>     lj-1357-arm64-struct-array-pass-by-val.test.lua
> +  lj-1455-arm64-ffi-ccall-hfa.test.lua
> +  lj-1455-ffi-conventions.test.lua
>   )

it is sorted not alphabetically:

--- a/test/tarantool-tests/ffi-ccall/CMakeLists.txt
+++ b/test/tarantool-tests/ffi-ccall/CMakeLists.txt
@@ -1,12 +1,12 @@
  list(APPEND tests
    ffi-call-empty-struct.test.lua
-  ffi-vector-arguments.test.lua
    ffi-ccall-arm64-fp-convention.test.lua
-  lj-205-arm64-osx-ffi-enum-arg.test.lua
-  lj-205-arm64-osx-ffi-small-arg.test.lua
+  ffi-vector-arguments.test.lua
    lj-1357-arm64-struct-array-pass-by-val.test.lua
    lj-1455-arm64-ffi-ccall-hfa.test.lua
    lj-1455-ffi-conventions.test.lua
+  lj-205-arm64-osx-ffi-enum-arg.test.lua
+  lj-205-arm64-osx-ffi-small-arg.test.lua
  )


>   
>   BuildTestCLib(libfficcall libfficcall.c "${tests}")
> diff --git a/test/tarantool-tests/ffi-ccall/libfficcall.c b/test/tarantool-tests/ffi-ccall/libfficcall.c
> index ecb21752..60d30f3c 100644
> --- a/test/tarantool-tests/ffi-ccall/libfficcall.c
> +++ b/test/tarantool-tests/ffi-ccall/libfficcall.c
> @@ -1,4 +1,13 @@
>   #include <stdint.h>
> +#include <stdarg.h>
> +
> +#define lengthof(a) (sizeof(a) / sizeof((a)[0]))
> +
> +#define UNUSED(x) ((void)(x))
> +
> +#if defined(__clang__)
> +#undef __GNUC__
> +#endif
>   
>   struct sz12_t {
>   	float f1;
> @@ -85,7 +94,585 @@ typedef struct hfa_float2 {
>   	float v[2];
>   } hfa_float2;
>   
> +typedef struct hfa_float22 {
> +	float v[2][2];
> +} hfa_float22;
> +
> +typedef struct non_hfa_float222 {
> +	float v[2][2][2];
> +} non_hfa_float222;
> +
> +typedef struct hfa_double2 {
> +	double v[2];
> +} hfa_double2;
> +
> +typedef struct hfa_double2_a16 {
> +	__attribute__((__aligned__(16))) double v[2];
> +} hfa_double2_a16;
> +
> +typedef struct hfa_double2_a32 {
> +	__attribute__((__aligned__(32))) double v[4];
> +} hfa_double2_a32;
> +
>   float hfa_float2_sum(hfa_float2 h)
>   {
>   	return h.v[0] + h.v[1];
>   }
> +
> +float hfa_float22_sum(hfa_float22 h)
> +{
> +	return h.v[0][0] + h.v[0][1] + h.v[1][0] + h.v[1][1];
> +}
> +
> +float non_hfa_float222_sum(non_hfa_float222 h)
> +{
> +	return h.v[0][0][0] + h.v[0][0][1] + h.v[0][1][0] + h.v[0][1][1] +
> +	       h.v[1][0][0] + h.v[1][0][1] + h.v[1][1][0] + h.v[1][1][1];
> +}
> +
> +/*
> + * Incorrect GCC behaviour.
> + * See:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125023.
> + */
> +#if !defined(__GNUC__)
> +typedef struct hfa_float_hole {
> +	float x;
> +	float hole[0][2][2];
> +	float y;
> +} hfa_float_hole;
> +
> +float hfa_float_hole_sum(hfa_float_hole h)
> +{
> +	return h.x + h.y;
> +}
> +#endif /* GCC */
> +
> +double hfa_double2_sum(hfa_double2 h)
> +{
> +	return h.v[0] + h.v[1];
> +}
> +
> +double hfa_double2_a16_sum(hfa_double2_a16 h)
> +{
> +	return h.v[0] + h.v[1];
> +}
> +
> +double hfa_double2_a32_sum(hfa_double2_a32 h)
> +{
> +	return h.v[0] + h.v[1] + h.v[2] + h.v[3];
> +}
> +
> +/*
> + * Enable only for GCC >= 12.0.0
> + * See the first paragraph about the 0 bitfield:
> + *https://gcc.gnu.org/gcc-12/changes.html.
> + */
> +#if !defined(__GNUC__) || __GNUC__ >= 12
> +typedef struct hfa_0bitfield {
> +	float x;
> +	int : 0;
> +	float y;
> +	float z;
> +} hfa_0bitfield;
> +
> +float hfa_0bitfield_sum(hfa_0bitfield h)
> +{
> +	return h.x + h.y + h.z;
> +}
> +#endif /* GNUC >= 12.0.0 */
> +
> +/****************************************************************/
> +/*                     Empty structures.                        */
> +/****************************************************************/
> +
> +struct empty {};
> +
> +struct super_empty {
> +	int arr[0];
> +};
> +
> +struct sort_of_empty {
> +	struct super_empty e;
> +};
> +
> +struct empty empty_ret(void)
> +{
> +	struct empty e;
> +	return e;
> +}
> +
> +struct super_empty super_empty_ret(void)
> +{
> +	struct super_empty e;
> +	return e;
> +}
> +
> +struct sort_of_empty sort_of_empty_ret(void)
> +{
> +	struct sort_of_empty e;
> +	return e;
> +}
> +
> +int empty_arg(struct empty e, int a)
> +{
> +	return a;
> +}
> +
> +int super_empty_arg(struct super_empty e, int a)
> +{
> +	return a;
> +}
> +
> +int sort_of_empty_arg(struct sort_of_empty e, int a)
> +{
> +	return a;
> +}
> +
> +/****************************************************************/
> +/*                 Vector passing.                              */
> +/****************************************************************/
> +
> +/* Test direct vector passing. */
> +typedef float vfloatx2 __attribute__ ((__vector_size__ (8)));
> +typedef float vfloatx4 __attribute__ ((__vector_size__ (16)));
> +
> +/* Return the given value without change. */
> +vfloatx2  vfloatx2_call(vfloatx2 x) { return x; }
> +vfloatx4  vfloatx4_call(vfloatx4 x) { return x; }
> +
> +typedef int int32x4_t __attribute__((__vector_size__ (4 * 4)));
> +
> +int32x4_t test_hva_varg(int n, ...)
> +{
> +	va_list vl;
> +	va_start(vl, n);
> +	int32x4_t a = va_arg(vl, int32x4_t);
> +	int32x4_t b = va_arg(vl, int32x4_t);
> +	va_end(vl);
> +	int32x4_t res = a + b;
> +	return res;
> +}
> +
> +/****************************************************************/
> +/*                Various argument types.                       */
> +/****************************************************************/
> +
> +/* Testing alignment with aggregates. */
> +
> +/*
> + * HFA, aggregates with size <= 16 bytes and aggregates with
> + * size > 16 bytes.
> + */
> +typedef struct hfa_floatx4_a16 {
> +	float v[4];
> +} __attribute__((aligned(16))) hfa_floatx4_a16;
> +
> +float test_2_align_hfa(int i, hfa_floatx4_a16 s1, hfa_floatx4_a16 s2)
> +{
> +	UNUSED(i);
> +	const float *v1 = s1.v;
> +	const float *v2 = s2.v;
> +	return v1[0] + v1[1] + v1[2] + v1[3] + v2[0] + v2[1] + v2[2] + v2[3];
> +}
> +
> +/* Testing 16-byte aggregate. */
> +typedef struct intx4_a16 {
> +	int v[4];
> +} __attribute__((aligned(16))) intx4_a16;
> +
> +int test_2_intx4_a16(int i, intx4_a16 s1, intx4_a16 s2)
> +{
> +	const int *v1 = s1.v;
> +	const int *v2 = s2.v;
> +	return i + v1[0] + v1[1] + v1[2] + v1[3] +
> +		   v2[0] + v2[1] + v2[2] + v2[3];
> +}
> +
> +/* Testing large aggregate. */
> +typedef struct large_agg_a16 {
> +	int v[18];
> +} __attribute__((aligned(16))) large_agg_a16;
> +
> +int test_2_large_agg_a16(int x, large_agg_a16 s1, large_agg_a16 s2)
> +{
> +	const int *v1 = s1.v;
> +	const int *v2 = s2.v;
> +	int sum = x;
> +	for (int i = 0; i < lengthof(s1.v); i++) {
> +		sum += v1[i] + v2[i];
> +	}
> +	return sum;
> +}
> +
> +typedef struct intx3_0bitfield {
> +	int x;
> +	int : 0;
> +	int y;
> +	int z;
> +} intx3_0bitfield;
> +
> +int test_2_intx3_0bitfield_reg(int i, intx3_0bitfield s1, intx3_0bitfield s2)
> +{
> +	return i + s1.x + s1.y + s1.z + s2.x + s2.y + s2.z;
> +}
> +
> +int test_2_intx3_0bitfield_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +				 int i7, int i8, int i9, intx3_0bitfield s1,
> +				 intx3_0bitfield s2)
> +{
> +	return i + s1.x + s1.y + s1.z + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 +
> +	       s2.x + s2.y + s2.z;
> +}
> +
> +typedef struct intx3_0bitfield_a16 {
> +	int x;
> +	int : 0 __attribute__((aligned(16)));
> +	int y;
> +	int z;
> +} intx3_0bitfield_a16;
> +
> +int test_2_intx3_0bitfield_a16_reg(int i, intx3_0bitfield_a16 s1,
> +				   intx3_0bitfield_a16 s2)
> +{
> +	return i + s1.x + s1.y + s1.z + s2.x + s2.y + s2.z;
> +}
> +
> +int test_2_intx3_0bitfield_a16_stack(int i, int i2, int i3, int i4, int i5,
> +				     int i6, int i7, int i8, int i9,
> +				     intx3_0bitfield_a16 s1,
> +				     intx3_0bitfield_a16 s2)
> +{
> +	return i + s1.x + s1.y + s1.z + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 +
> +	       s2.x + s2.y + s2.z;
> +}
> +
> +typedef struct intx3_full_bitfield_a16 {
> +	int x;
> +	int y: 32 __attribute__((aligned(16)));
> +	int z;
> +} intx3_full_bitfield_a16;
> +
> +int test_2_intx3_full_bitfield_a16_reg(int i, intx3_full_bitfield_a16 s1,
> +				       intx3_full_bitfield_a16 s2)
> +{
> +	return i + s1.x + s1.y + s1.z + s2.x + s2.y + s2.z;
> +}
> +
> +int test_2_intx3_full_bitfield_a16_stack(int i, int i2, int i3, int i4, int i5,
> +					 int i6, int i7, int i8, int i9,
> +					 intx3_full_bitfield_a16 s1,
> +					 intx3_full_bitfield_a16 s2)
> +{
> +	return i + s1.x + s1.y + s1.z + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 +
> +	       s2.x + s2.y + s2.z;
> +}
> +
> +typedef struct intx3_half_bitfield {
> +	int x : 16;
> +	int y : 16;
> +	int z;
> +} intx3_half_bitfield;
> +
> +int test_2_intx3_half_bitfield_reg(int i, intx3_half_bitfield s1,
> +				   intx3_half_bitfield s2)
> +{
> +	return i + s1.x + s1.y + s1.z + s2.x + s2.y + s2.z;
> +}
> +
> +int test_2_intx3_half_bitfield_stack(int i, int i2, int i3, int i4, int i5,
> +				     int i6, int i7, int i8, int i9,
> +				     intx3_half_bitfield s1,
> +				     intx3_half_bitfield s2)
> +{
> +	return i + s1.x + s1.y + s1.z + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 +
> +	       s2.x + s2.y + s2.z;
> +}
> +
> +typedef struct intx3_half_bitfield_a16 {
> +	int x : 16;
> +	int y : 16 __attribute__((aligned(16)));
> +	int z;
> +} intx3_half_bitfield_a16;
> +
> +int test_2_intx3_half_bitfield_a16_reg(int i, intx3_half_bitfield_a16 s1,
> +				       intx3_half_bitfield_a16 s2)
> +{
> +	return i + s1.x + s1.y + s1.z + s2.x + s2.y + s2.z;
> +}
> +
> +int test_2_intx3_half_bitfield_a16_stack(int i, int i2, int i3, int i4, int i5,
> +					 int i6, int i7, int i8, int i9,
> +					 intx3_half_bitfield_a16 s1,
> +					 intx3_half_bitfield_a16 s2)
> +{
> +	return i + s1.x + s1.y + s1.z + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 +
> +	       s2.x + s2.y + s2.z;
> +}
> +
> +/* Increased natural alignment. */
> +typedef struct la16l {
> +	long long x __attribute__((aligned(16)));
> +	long long y;
> +} la16l;
> +
> +int test_2_la16l_reg(int i, la16l s1, la16l s2)
> +{
> +	return s1.x + s2.x + i + s1.y + s2.y;
> +}
> +
> +int test_2_la16l_stack(int i, int i2, int i3, int i4, int i5, int i6, int i7,
> +		       int i8, int i9, la16l s1, la16l s2)
> +{
> +	return s1.x + s2.x + i + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + s1.y +
> +	       s2.y;
> +}
> +
> +/****************************************************************/
> +/*                Transparent structures.                       */
> +/****************************************************************/
> +
> +typedef struct a16_tsp {
> +	struct {
> +		long long x;
> +		long long y;
> +	} __attribute__((aligned(16)));
> +} a16_tsp;
> +
> +int test_2_a16_tsp_reg(int i, a16_tsp s1, a16_tsp s2)
> +{
> +	return s1.x + s2.x + i + s1.y + s2.y;
> +}
> +
> +int test_2_a16_tsp_stack(int i, int i2, int i3, int i4, int i5, int i6, int i7,
> +			 int i8, int i9, a16_tsp s1, a16_tsp s2)
> +{
> +	return s1.x + s2.x + i + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + s1.y +
> +	       s2.y;
> +}
> +
> +typedef struct f_a16_tsp {
> +	struct {
> +		long long x __attribute__((aligned(16)));
> +		long long y;
> +	};
> +} f_a16_tsp;
> +
> +int test_2_f_a16_tsp_reg(int i, f_a16_tsp s1, f_a16_tsp s2)
> +{
> +	return s1.x + s2.x + i + s1.y + s2.y;
> +}
> +
> +int test_2_f_a16_tsp_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +			   int i7, int i8, int i9, f_a16_tsp s1, f_a16_tsp s2)
> +{
> +	return s1.x + s2.x + i + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + s1.y +
> +	       s2.y;
> +}
> +
> +/*
> + * Test passing structs with size < 8, < 16 and > 16
> + * with alignment of 16 and without.
> + *
> + * Structs with size <= 8 bytes, without alignment attribute
> + * passed as i64 regardless of the align attribute.
> + */
> +typedef struct is_no_align {
> +	int i;
> +	short s;
> +} is_no_align;
> +
> +int test_2_is_no_align_reg(int i, is_no_align s1, is_no_align s2)
> +{
> +	return s1.i + s2.i + i + s1.s + s2.s;
> +}
> +
> +int test_2_is_no_align_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +			     int i7, int i8, int i9, is_no_align s1,
> +			     is_no_align s2)
> +{
> +	return s1.i + s2.i + i + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + s1.s +
> +	       s2.s;
> +}
> +
> +/* Structs with size <= 8 bytes, with alignment attribute. */
> +typedef struct is_a16 {
> +	int i;
> +	short s;
> +} __attribute__((aligned(16))) is_a16;
> +
> +int test_2_is_a16_reg(int i, is_a16 s1, is_a16 s2)
> +{
> +	return s1.i + s2.i + i + s1.s + s2.s;
> +}
> +
> +int test_2_is_a16_stack(int i, int i2, int i3, int i4, int i5, int i6, int i7,
> +			int i8, int i9, is_a16 s1, is_a16 s2)
> +{
> +	return s1.i + s2.i + i + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + s1.s +
> +	       s2.s;
> +}
> +
> +/* Structs with size <= 16 bytes, without alignment attribute. */
> +typedef struct isis_no_align {
> +	int i;
> +	short s;
> +	int i2;
> +	short s2;
> +} isis_no_align;
> +
> +int test_2_isis_no_align_reg(int i, isis_no_align s1, isis_no_align s2)
> +{
> +	return s1.i + s2.i + s1.i2 + s2.i2 + i + s1.s + s2.s + s1.s2 + s2.s2;
> +}
> +
> +int test_2_isis_no_align_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +			       int i7, int i8, int i9, isis_no_align s1,
> +			       isis_no_align s2)
> +{
> +	return s1.i + s2.i + s1.i2 + s2.i2 + i + i2 + i3 + i4 + i5 + i6 + i7 +
> +	       i8 + i9 + s1.s + s2.s + s1.s2 + s2.s2;
> +}
> +
> +/* Structs with size <= 16 bytes, with alignment attribute. */
> +typedef struct isis_a16 {
> +	int i;
> +	short s;
> +	int i2;
> +	short s2;
> +} __attribute__((aligned(16))) isis_a16;
> +
> +int test_2_isis_a16_reg(int i, isis_a16 s1, isis_a16 s2)
> +{
> +	return s1.i + s2.i + s1.i2 + s2.i2 + i + s1.s + s2.s + s1.s2 + s2.s2;
> +}
> +
> +int test_2_isis_a16_stack(int i, int i2, int i3, int i4, int i5, int i6, int i7,
> +			  int i8, int i9, isis_a16 s1, isis_a16 s2)
> +{
> +	return s1.i + s2.i + s1.i2 + s2.i2 + i + i2 + i3 + i4 + i5 + i6 + i7 +
> +	       i8 + i9 + s1.s + s2.s + s1.s2 + s2.s2;
> +}
> +
> +/* structs with size > 16 bytes, without alignment attribute. */
> +typedef struct isisis {
> +	int i;
> +	short s;
> +	int i2;
> +	short s2;
> +	int i3;
> +	short s3;
> +} isisis_no_align;
> +
> +int test_2_isisis_no_align_reg(int i, isisis_no_align s1, isisis_no_align s2)
> +{
> +	return s1.i + s2.i + s1.i2 + s2.i2 + s1.i3 + s2.i3 + i + s1.s + s2.s +
> +	       s1.s2 + s2.s2 + s1.s3 + s2.s3;
> +}
> +
> +int test_2_isisis_no_align_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +				 int i7, int i8, int i9, isisis_no_align s1,
> +				 isisis_no_align s2)
> +{
> +	return s1.i + s2.i + s1.i2 + s2.i2 + s1.i3 + s2.i3 + i + i2 + i3 + i4 +
> +	       i5 + i6 + i7 + i8 + i9 + s1.s + s2.s + s1.s2 + s2.s2 + s1.s3 +
> +	       s2.s3;
> +}
> +
> +/* Structs with size > 16 bytes, with alignment attribute. */
> +typedef struct isisis_a16 {
> +	int i;
> +	short s;
> +	int i2;
> +	short s2;
> +	int i3;
> +	short s3;
> +} __attribute__((aligned(16))) isisis_a16;
> +
> +int test_2_isisis_a16_reg(int i, isisis_a16 s1, isisis_a16 s2)
> +{
> +	return s1.i + s2.i + s1.i2 + s2.i2 + s1.i3 + s2.i3 + i + s1.s + s2.s +
> +	       s1.s2 + s2.s2 + s1.s3 + s2.s3;
> +}
> +
> +int test_2_isisis_a16_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +			    int i7, int i8, int i9, isisis_a16 s1,
> +			    isisis_a16 s2)
> +{
> +	return s1.i + s2.i + s1.i2 + s2.i2 + s1.i3 + s2.i3 + i + i2 + i3 + i4 +
> +	       i5 + i6 + i7 + i8 + i9 + s1.s + s2.s + s1.s2 + s2.s2 + s1.s3 +
> +	       s2.s3;
> +}
> +
> +/* We should not split struct argument between regs and stack. */
> +int test_2_isis_no_align_split(int i, int i2, int i3, int i4, int i5, int i6,
> +			       int i7, isis_no_align s1, isis_no_align s2)
> +{
> +	return s1.i + s2.i + s1.i2 + s2.i2 + i + i2 + i3 + i4 + i5 + i6 + i7 +
> +	       s1.s + s2.s + s1.s2 + s2.s2;
> +}
> +
> +int test_2_isis_a16_split(int i, int i2, int i3, int i4, int i5, int i6, int i7,
> +			  isis_a16 s1, isis_a16 s2)
> +{
> +	return s1.i + s2.i + s1.i2 + s2.i2 + i + i2 + i3 + i4 + i5 + i6 + i7 +
> +	       s1.s + s2.s + s1.s2 + s2.s2;
> +}
> +
> +/****************************************************************/
> +/*                Packed structures.                            */
> +/****************************************************************/
> +
> +typedef struct ill_packed {
> +	int x;
> +	long long y;
> +} __attribute__((packed)) ill_packed;
> +
> +typedef struct ii {
> +	int x;
> +	int y;
> +} ii;
> +
> +/* Passing structs with unaligned fields, not in registers. */
> +int test_2_ill_packed(int i, ill_packed s1, ill_packed s2)
> +{
> +	return s1.x + s2.x + i + s1.y + s2.y;
> +}
> +
> +int test_2_ill_packed_reord(int i, ill_packed s1, ill_packed s2, int i2, ii s3)
> +{
> +	return s1.x + s2.x + i + s1.y + s2.y + i2 + s3.x + s3.y;
> +}
> +
> +int test_2_ill_packed_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +			    int i7, int i8, int i9, ill_packed s1,
> +			    ill_packed s2)
> +{
> +	return s1.x + s2.x + i + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + s1.y +
> +	       s2.y;
> +}
> +
> +/* Packed structure, overaligned, same as above. */
> +typedef struct ill_packed_a16 {
> +	int x;
> +	long long y;
> +} __attribute__((packed, aligned(16))) ill_packed_a16;
> +
> +/* Passing structs with unaligned fields not in registers. */
> +int test_2_ill_packed_a16(int i, ill_packed_a16 s1, ill_packed_a16 s2)
> +{
> +	return s1.x + s2.x + i + s1.y + s2.y;
> +}
> +
> +int test_2_ill_packed_a16_reord(int i, ill_packed_a16 s1, ill_packed_a16 s2,
> +				int i2, ii s3)
> +{
> +	return s1.x + s2.x + i + s1.y + s2.y + i2 + s3.x + s3.y;
> +}
> +
> +int test_2_ill_packed_a16_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +				int i7, int i8, int i9, ill_packed_a16 s1,
> +				ill_packed_a16 s2)
> +{
> +	return s1.x + s2.x + i + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + s1.y +
> +	       s2.y;
> +}
> diff --git a/test/tarantool-tests/ffi-vector-arguments.test.lua b/test/tarantool-tests/ffi-vector-arguments.test.lua
> new file mode 100644
> index 00000000..330ec991
> --- /dev/null
> +++ b/test/tarantool-tests/ffi-vector-arguments.test.lua
> @@ -0,0 +1,62 @@
> +local ffi = require('ffi')
> +local tap = require('tap')
> +
> +-- The test file to check FFI vector passing correctness.
> +local test = tap.test('ffi-vector-arguments'):skipcond({
> +  ['NYI for non x64 arches'] = jit.arch ~= 'x64',
s/non x64/non-x64/
> +})
> +
> +local SIZING
> +-- Only those are implemented.
> +if jit.arch == 'x64' then
> +  SIZING = {2, 4,}
s/,}/}/
> +else
> +  SIZING = {}
> +end
> +
> +test:plan(#SIZING + 1)
> +
> +local ffi_ccall = ffi.load('libfficcall')
> +
> +ffi.cdef[[
> +  typedef float vfloatx2  __attribute__ ((__vector_size__ (8)));
> +  typedef float vfloatx4  __attribute__ ((__vector_size__ (16)));
> +
> +  vfloatx2  vfloatx2_call(vfloatx2 x);
> +  vfloatx4  vfloatx4_call(vfloatx4 x);
> +
> +  typedef int int32x4_t __attribute__((__vector_size__ (4 * 4)));
> +  int32x4_t test_hva_varg(int n, ...);
> +]]
> +
> +local function test_self_ret_vector(subtest, nelem)
> +subtest:plan(1)
> +  local typestr = 'vfloatx' .. nelem
> +  local f = ffi_ccall[typestr .. '_call']
> +  local arg = {}
> +  for i = 1, nelem do
> +    arg[i - 1] = i + 0LL
> +  end
> +  local res = f(arg)
> +  local table_res = {}
> +  for i = 0, nelem - 1 do
> +    table_res[i] = res[i]
> +  end
> +subtest:is_deeply(table_res, arg,
> +                    'correct result for ' .. nelem .. '-sized vec')
> +end
> +
> +for i = 1, #SIZING do
> +test:test('vec-' .. SIZING[i], test_self_ret_vector, SIZING[i])
> +end
> +
> +local hva_arg_vec = ffi.new('int32x4_t', {0LL, 1LL, 2LL, 3LL})
> +local hva_res = ffi_ccall.test_hva_varg(0LL, hva_arg_vec, hva_arg_vec)
> +local hva_res_tab = {}
> +local hva_expected = {[0] = 0, 2, 4, 6}
> +for i = 0, 3 do
> +  hva_res_tab[i] = hva_res[i]
> +end
> +test:is_deeply(hva_res_tab, hva_expected, 'correct hva with the int type')
> +
> +test:done(true)
> diff --git a/test/tarantool-tests/lj-1455-arm64-ffi-ccall-hfa.test.lua b/test/tarantool-tests/lj-1455-arm64-ffi-ccall-hfa.test.lua
> new file mode 100644
> index 00000000..8552a5f0
> --- /dev/null
> +++ b/test/tarantool-tests/lj-1455-arm64-ffi-ccall-hfa.test.lua
> @@ -0,0 +1,82 @@
> +local ffi = require('ffi')
> +local tap = require('tap')
> +
> +-- The test file to test various FFI C call conventions for HFA
> +-- aggregates.
> +-- See also:https://github.com/LuaJIT/LuaJIT/issues/1455.
> +local test = tap.test('lj-1455-arm64-ffi-ccall-hfa')
> +
> +test:plan(7)
> +
> +local ffi_ccall = ffi.load('libfficcall')
> +
> +ffi.cdef[[
> +  typedef struct hfa_float22 {
> +    float v[2][2];
> +  } hfa_float22;
> +
> +
> +  typedef struct non_hfa_float222 {
> +    float v[2][2][2];
> +  } non_hfa_float222;
> +
> +  typedef struct hfa_float_hole {
> +    float x;
> +    float hole[0][2][2];
> +    float y;
> +  } hfa_float_hole;
> +
> +  typedef struct hfa_double2 {
> +    double v[2];
> +  } hfa_double2;
> +
> +  typedef struct hfa_double2_a16 {
> +    __attribute__((__aligned__(16))) double v[2];
> +  } hfa_double2_a16;
> +
> +  typedef struct hfa_double2_a32 {
> +    __attribute__((__aligned__(32))) double v[4];
> +  } hfa_double2_a32;
> +
> +  float hfa_float22_sum(hfa_float22 h);
> +  double hfa_double2_sum(hfa_double2 h);
> +  double hfa_double2_a16_sum(hfa_double2_a16 h);
> +  double hfa_double2_a32_sum(hfa_double2_a32 h);
> +
> +  typedef struct hfa_0bitfield {
> +    float x;
> +    int : 0;
> +    float y;
> +    float z;
> +  } hfa_0bitfield;
> +
> +  float hfa_0bitfield_sum(hfa_0bitfield h);
> +
> +  float non_hfa_float222_sum(non_hfa_float222 h);
> +
> +  float hfa_float_hole_sum(hfa_float_hole h);
> +]]
> +
> +test:is(ffi_ccall.hfa_float22_sum({{{1, 2}, {3, 4}}}), 10,
> +        'HFA 2 dimensional correct')
> +test:is(ffi_ccall.non_hfa_float222_sum({{{{1, 2}, {3, 4}},{{5, 6}, {7, 8}}}}),
> +        36, 'non HFA array correct')
> +local supported, func = pcall(function()
> +  return ffi_ccall.hfa_float_hole_sum
> +end)
> +if supported then
> +test:is(func({x = 1, y = 2}), 3, 'HFA float hole correct')
> +else
> +test:skip('HFA float hole -- Unsupported by C compiler')
> +end
> +test:is(ffi_ccall.hfa_double2_sum({{1, 2}}), 3, 'HFA double correct')
> +test:is(ffi_ccall.hfa_double2_a16_sum({{1, 2}}), 3, 'align 16 correct')
> +test:is(ffi_ccall.hfa_double2_a32_sum({{1, 2, 3, 4}}), 10, 'align 32 correct')
> +supported, func = pcall(function() return ffi_ccall.hfa_0bitfield_sum end)
> +if supported then
> +test:is(func({x = 1, y = 2, z = 3}), 6, 'HFA 0 bitfield correct')
> +else
> +test:skip('HFA 0 bitfield -- Unsupported by C compiler')
> +end
> +
> +test:done(true)
> diff --git a/test/tarantool-tests/lj-1455-bitfield0-a16.test.lua b/test/tarantool-tests/lj-1455-bitfield0-a16.test.lua
> new file mode 100644
> index 00000000..6f8e9aac
> --- /dev/null
> +++ b/test/tarantool-tests/lj-1455-bitfield0-a16.test.lua
> @@ -0,0 +1,27 @@
> +local ffi = require('ffi')
> +local tap = require('tap')
> +
> +-- The test file demonstrates incorrect FFI attributes for the
> +-- structure with a zero-sized bitfield.
> +-- See also:https://github.com/LuaJIT/LuaJIT/issues/1455.
> +local test = tap.test('lj-1455-ffi-conventions')
> +
> +test:plan(3)
> +
> +ffi.cdef[[
> +  typedef struct {
> +          int x;
> +          int : 0 __attribute__((aligned(16)));
> +          int y;
> +          int z;
> +  } intx3_0bitfield_a16;
> +]]
> +
> +test:is(ffi.sizeof(ffi.new('intx3_0bitfield_a16')), 24,
> +        'correct size of struct with 0 bitfield')
> +test:is(ffi.offsetof('intx3_0bitfield_a16', 'y'), 16,
> +        'correct offset of field after 0 bitfield')
> +test:is(ffi.alignof('intx3_0bitfield_a16'), 4,
> +        'correct total align of struct with 0 bitfield')
> +
> +test:done(true)
> diff --git a/test/tarantool-tests/lj-1455-ffi-conventions.test.lua b/test/tarantool-tests/lj-1455-ffi-conventions.test.lua
> new file mode 100644
> index 00000000..e7a45736
> --- /dev/null
> +++ b/test/tarantool-tests/lj-1455-ffi-conventions.test.lua
> @@ -0,0 +1,441 @@
> +local ffi = require('ffi')
> +local tap = require('tap')
> +
> +-- The test file to test various FFI C call conventions.
> +-- See also:https://github.com/LuaJIT/LuaJIT/issues/1455.
> +local test = tap.test('lj-1455-ffi-conventions')
> +
> +test:plan(39)
> +
> +local ffi_ccall = ffi.load('libfficcall')
> +
> +ffi.cdef[[
> +  typedef struct hfa_floatx4_a16 {
> +          float v[4];
> +  } __attribute__((aligned(16))) hfa_floatx4_a16;
> +
> +  float test_2_align_hfa(int i, hfa_floatx4_a16 s1, hfa_floatx4_a16 s2);
> +
> +  typedef struct intx4_a16 {
> +          int v[4];
> +  } __attribute__((aligned(16))) intx4_a16;
> +
> +  int test_2_intx4_a16(int i, intx4_a16 s1, intx4_a16 s2);
> +
> +  typedef struct large_agg_a16 {
> +          int v[18];
> +  } __attribute__((aligned(16))) large_agg_a16;
> +
> +  int test_2_large_agg_a16(int x, large_agg_a16 s1, large_agg_a16 s2);
> +
> +  typedef struct intx3_0bitfield {
> +          int x;
> +          int : 0;
> +          int y;
> +          int z;
> +  } intx3_0bitfield;
> +
> +  int test_2_intx3_0bitfield_reg(int i, intx3_0bitfield s1, intx3_0bitfield s2);
> +  int test_2_intx3_0bitfield_stack(int i, int i2, int i3, int i4, int i5,
> +                                   int i6, int i7, int i8, int i9,
> +                                   intx3_0bitfield s1, intx3_0bitfield s2);
> +
> +  typedef struct intx3_0bitfield_a16 {
> +          int x;
> +          int : 0 __attribute__((aligned(16)));
> +          int y;
> +          int z;
> +  } intx3_0bitfield_a16;
> +
> +  int test_2_intx3_0bitfield_a16_reg(int i, intx3_0bitfield_a16 s1,
> +                                     intx3_0bitfield_a16 s2);
> +  int test_2_intx3_0bitfield_a16_stack(int i, int i2, int i3, int i4, int i5,
> +                                       int i6, int i7, int i8, int i9,
> +                                       intx3_0bitfield_a16 s1,
> +                                       intx3_0bitfield_a16 s2);
> +
> +  typedef struct intx3_full_bitfield_a16 {
> +          int x;
> +          int y: 32 __attribute__((aligned(16)));
> +          int z;
> +  } intx3_full_bitfield_a16;
> +
> +  int test_2_intx3_full_bitfield_a16_reg(int i, intx3_full_bitfield_a16 s1,
> +                                         intx3_full_bitfield_a16 s2);
> +  int test_2_intx3_full_bitfield_a16_stack(int i, int i2, int i3, int i4,
> +                                           int i5, int i6, int i7, int i8,
> +                                           int i9, intx3_full_bitfield_a16 s1,
> +                                           intx3_full_bitfield_a16 s2);
> +
> +  typedef struct intx3_half_bitfield {
> +          int x : 16;
> +          int y : 16;
> +          int z;
> +  } intx3_half_bitfield;
> +
> +  int test_2_intx3_half_bitfield_reg(int i, intx3_half_bitfield s1,
> +                                     intx3_half_bitfield s2);
> +  int test_2_intx3_half_bitfield_stack(int i, int i2, int i3, int i4, int i5,
> +                                       int i6, int i7, int i8, int i9,
> +                                       intx3_half_bitfield s1,
> +                                       intx3_half_bitfield s2);
> +
> +  typedef struct intx3_half_bitfield_a16 {
> +          int x : 16;
> +          int y : 16 __attribute__((aligned(16)));
> +          int z;
> +  } intx3_half_bitfield_a16;
> +
> +  int test_2_intx3_half_bitfield_a16_reg(int i, intx3_half_bitfield_a16 s1,
> +                                         intx3_half_bitfield_a16 s2);
> +  int test_2_intx3_half_bitfield_a16_stack(int i, int i2, int i3, int i4,
> +                                           int i5, int i6, int i7, int i8,
> +                                           int i9, intx3_half_bitfield_a16 s1,
> +                                           intx3_half_bitfield_a16 s2);
> +
> +  typedef struct la16l {
> +          long long x __attribute__((aligned(16)));
> +          long long y;
> +  } la16l;
> +
> +  int test_2_la16l_reg(int i, la16l s1, la16l s2);
> +  int test_2_la16l_stack(int i, int i2, int i3, int i4, int i5, int i6, int i7,
> +                         int i8, int i9, la16l s1, la16l s2);
> +
> +  typedef struct a16_tsp {
> +          struct {
> +                  long long x;
> +                  long long y;
> +          } __attribute__((aligned(16)));
> +  } a16_tsp;
> +
> +  int test_2_a16_tsp_reg(int i, a16_tsp s1, a16_tsp s2);
> +  int test_2_a16_tsp_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +                           int i7, int i8, int i9, a16_tsp s1, a16_tsp s2);
> +
> +  typedef struct f_a16_tsp {
> +          struct {
> +                  long long x __attribute__((aligned(16)));
> +                  long long y;
> +          };
> +  } f_a16_tsp;
> +
> +  int test_2_f_a16_tsp_reg(int i, f_a16_tsp s1, f_a16_tsp s2);
> +  int test_2_f_a16_tsp_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +                             int i7, int i8, int i9, f_a16_tsp s1,
> +                             f_a16_tsp s2);
> +
> +  typedef struct is_no_align {
> +          int i;
> +          short s;
> +  } is_no_align;
> +
> +  int test_2_is_no_align_reg(int i, is_no_align s1, is_no_align s2);
> +  int test_2_is_no_align_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +                               int i7, int i8, int i9, is_no_align s1,
> +                               is_no_align s2);
> +
> +  typedef struct is_a16 {
> +          int i;
> +          short s;
> +  } __attribute__((aligned(16))) is_a16;
> +
> +  int test_2_is_a16_reg(int i, is_a16 s1, is_a16 s2);
> +  int test_2_is_a16_stack(int i, int i2, int i3, int i4, int i5, int i6, int i7,
> +                          int i8, int i9, is_a16 s1, is_a16 s2);
> +
> +  typedef struct isis_no_align {
> +          int i;
> +          short s;
> +          int i2;
> +          short s2;
> +  } isis_no_align;
> +
> +  int test_2_isis_no_align_reg(int i, isis_no_align s1, isis_no_align s2);
> +  int test_2_isis_no_align_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +                                 int i7, int i8, int i9, isis_no_align s1,
> +                                 isis_no_align s2);
> +
> +  typedef struct isis_a16
> +  {
> +          int i;
> +          short s;
> +          int i2;
> +          short s2;
> +  } __attribute__((aligned(16))) isis_a16;
> +
> +  int test_2_isis_a16_reg(int i, isis_a16 s1, isis_a16 s2);
> +  int test_2_isis_a16_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +                            int i7, int i8, int i9, isis_a16 s1, isis_a16 s2);
> +
> +  typedef struct isisis
> +  {
> +          int i;
> +          short s;
> +          int i2;
> +          short s2;
> +          int i3;
> +          short s3;
> +  } isisis_no_align;
> +
> +  int test_2_isisis_no_align_reg(int i, isisis_no_align s1, isisis_no_align s2);
> +  int test_2_isisis_no_align_stack(int i, int i2, int i3, int i4, int i5,
> +                                   int i6, int i7, int i8, int i9,
> +                                   isisis_no_align s1, isisis_no_align s2);
> +
> +  typedef struct isisis_a16
> +  {
> +          int i;
> +          short s;
> +          int i2;
> +          short s2;
> +          int i3;
> +          short s3;
> +  } __attribute__((aligned(16))) isisis_a16;
> +
> +  int test_2_isisis_a16_reg(int i, isisis_a16 s1, isisis_a16 s2);
> +  int test_2_isisis_a16_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +                              int i7, int i8, int i9, isisis_a16 s1,
> +                              isisis_a16 s2);
> +
> +  int test_2_isis_no_align_split(int i, int i2, int i3, int i4, int i5, int i6,
> +                                 int i7, isis_no_align s1, isis_no_align s2);
> +  int test_2_isis_a16_split(int i, int i2, int i3, int i4, int i5, int i6,
> +                            int i7, isis_a16 s1, isis_a16 s2);
> +
> +  typedef struct ill_packed {
> +          int x;
> +          long long y;
> +  } __attribute__((packed)) ill_packed;
> +
> +  typedef struct ii {
> +          int x;
> +          int y;
> +  } ii;
> +
> +  int test_2_ill_packed(int i, ill_packed s1, ill_packed s2);
> +  int test_2_ill_packed_reord(int i, ill_packed s1, ill_packed s2, int i2,
> +                              ii s3);
> +  int test_2_ill_packed_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +                              int i7, int i8, int i9, ill_packed s1,
> +                              ill_packed s2);
> +
> +  typedef struct ill_packed_a16 {
> +          int x;
> +          long long y;
> +  } __attribute__((packed, aligned(16))) ill_packed_a16;
> +
> +  int test_2_ill_packed_a16(int i, ill_packed_a16 s1, ill_packed_a16 s2);
> +  int test_2_ill_packed_a16_reord(int i, ill_packed_a16 s1, ill_packed_a16 s2,
> +                                  int i2, ii s3);
> +  int test_2_ill_packed_a16_stack(int i, int i2, int i3, int i4, int i5, int i6,
> +                                  int i7, int i8, int i9, ill_packed_a16 s1,
> +                                  ill_packed_a16 s2);
> +]]
> +
> +test:is(ffi_ccall.test_2_align_hfa(0LL,
> +    {{0, 1, 2, 3}}, {{4, 5, 6, 7}}),
> +  28, 'correct align hfa')
> +
> +test:is(ffi_ccall.test_2_intx4_a16(0LL,
> +    {{0LL, 1LL, 2LL, 3LL}}, {{4LL, 5LL, 6LL, 7LL}}),
> +  28, 'correct align hva')
> +
> +local LARGE_HVA_SZ = 18
> +local large_agg_sum = 0LL
> +local large_agg1 = {}
> +local large_agg2 = {}
> +for i = 0, LARGE_HVA_SZ - 1 do
> +  large_agg1[i] = i + 0LL
> +  large_agg2[i] = LARGE_HVA_SZ + i + 0LL
> +  large_agg_sum = large_agg_sum + large_agg1[i] + large_agg2[i]
> +end
> +
> +test:is(ffi_ccall.test_2_large_agg_a16(0LL, {large_agg1}, {large_agg2}),
> +        large_agg_sum, 'correct large align agg')
> +
> +test:is(ffi_ccall.test_2_intx3_0bitfield_reg(0LL,
> +    {x = 1LL, y = 2LL, z = 3LL}, {x = 4LL, y = 5LL, z = 6LL}),
> +  21, 'correct intx3 0 bitfield reg')
> +
> +test:is(ffi_ccall.test_2_intx3_0bitfield_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {x = 10LL, y = 11LL, z = 12LL},
> +    {x = 13LL, y = 14LL, z = 15LL}),
> +  120, 'correct intx3 0 bitfield stack')
> +
> +test:is(ffi_ccall.test_2_intx3_0bitfield_a16_reg(0LL,
> +    {x = 1LL, y = 2LL, z = 3LL},
> +    {x = 4LL, y = 5LL, z = 6LL}),
> +  21, 'correct intx3 0 bitfield align 16 reg')
> +
> +test:is(ffi_ccall.test_2_intx3_0bitfield_a16_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {x = 10LL, y = 11LL, z = 12LL},
> +    {x = 13LL, y = 14LL, z = 15LL}),
> +  120, 'correct intx3 0 bitfield align 16 stack')
> +
> +test:is(ffi_ccall.test_2_intx3_full_bitfield_a16_reg(
> +    0LL,
> +    {x = 1LL, y = 2LL, z = 3LL},
> +    {x = 4LL, y = 5LL, z = 6LL}),
> +  21, 'correct intx3 0 full bitfield align 16 reg')
> +
> +test:is(ffi_ccall.test_2_intx3_full_bitfield_a16_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {x = 10LL, y = 11LL, z = 12LL},
> +    {x = 13LL, y = 14LL, z = 15LL}),
> +  120, 'correct intx3 full bitfield align 16 stack')
> +
> +test:is(ffi_ccall.test_2_intx3_half_bitfield_reg(0LL,
> +    {x = 1LL, y = 2LL, z = 3LL},
> +    {x = 4LL, y = 5LL, z = 6LL}),
> +  21, 'correct intx3 0 half bitfield reg')
> +
> +test:is(ffi_ccall.test_2_intx3_half_bitfield_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {x = 10LL, y = 11LL, z = 12LL},
> +    {x = 13LL, y = 14LL, z = 15LL}),
> +  120, 'correct intx3 half bitfield stack')
> +
> +test:is(ffi_ccall.test_2_intx3_half_bitfield_a16_reg(0LL,
> +    {x = 1LL, y = 2LL, z = 3LL},
> +    {x = 4LL, y = 5LL, z = 6LL}),
> +  21, 'correct intx3 0 half bitfield align 16 reg')
> +
> +test:is(ffi_ccall.test_2_intx3_half_bitfield_a16_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {x = 10LL, y = 11LL, z = 12LL},
> +    {x = 13LL, y = 14LL, z = 15LL}),
> +  120, 'correct intx3 half bitfield align 16 stack')
> +
> +test:is(ffi_ccall.test_2_la16l_reg(0LL, {x = 1LL, y = 2LL}, {x = 3LL, y = 4LL}),
> +        10, 'correct la16l reg')
> +
> +test:is(ffi_ccall.test_2_la16l_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {x = 10LL, y = 11LL}, {x = 12LL, y = 13LL}),
> +  91, 'correct la16l stack')
> +
> +test:is(ffi_ccall.test_2_a16_tsp_reg(0LL,
> +    {x = 1LL, y = 2LL}, {x = 3LL, y = 4LL}),
> +  10, 'correct tsp reg')
> +
> +test:is(ffi_ccall.test_2_a16_tsp_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {x = 10LL, y = 11LL}, {x = 12LL, y = 13LL}),
> +  91, 'correct tsp stack')
> +
> +test:is(ffi_ccall.test_2_f_a16_tsp_reg(0LL,
> +    {x = 1LL, y = 2LL}, {x = 3LL, y = 4LL}),
> +  10, 'correct tsp aligned field reg')
> +
> +test:is(ffi_ccall.test_2_f_a16_tsp_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {x = 10LL, y = 11LL}, {x = 12LL, y = 13LL}),
> +  91, 'correct tsp aligned field stack')
> +
> +test:is(ffi_ccall.test_2_is_no_align_reg(0LL,
> +    {i = 1LL, s = 2LL}, {i = 3LL, s = 4LL}),
> +  10, 'correct is no align reg')
> +
> +test:is(ffi_ccall.test_2_is_no_align_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {i = 10LL, s = 11LL}, {i = 12LL, s = 13LL}),
> +  91, 'correct is no align stack')
> +
> +test:is(ffi_ccall.test_2_is_a16_reg(0LL,
> +    {i = 1LL, s = 2LL}, {i = 3LL, s = 4LL}),
> +  10, 'correct is align 16 reg')
> +
> +test:is(ffi_ccall.test_2_is_a16_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {i = 10LL, s = 11LL}, {i = 12LL, s = 13LL}),
> +  91, 'correct is align 16 stack')
> +
> +test:is(ffi_ccall.test_2_isis_no_align_reg(0LL,
> +    {i = 1LL, s = 2LL, i2 = 3LL, s2 = 4LL},
> +    {i = 5LL, s = 6LL, i2 = 7LL, s2 = 8LL}),
> +  36, 'correct isis no align reg')
> +
> +test:is(ffi_ccall.test_2_isis_no_align_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {i = 10LL, s = 11LL, i2 = 12LL, s2 = 13LL},
> +    {i = 14LL, s = 15LL, i2 = 16LL, s2 = 17LL}),
> +  153, 'correct isis no align stack')
> +
> +test:is(ffi_ccall.test_2_isis_a16_reg(0LL,
> +    {i = 1LL, s = 2LL, i2 = 3LL, s2 = 4LL},
> +    {i = 5LL, s = 6LL, i2 = 7LL, s2 = 8LL}),
> +  36, 'correct isis align 16 reg')
> +
> +test:is(ffi_ccall.test_2_isis_a16_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {i = 10LL, s = 11LL, i2 = 12LL, s2 = 13LL},
> +    {i = 14LL, s = 15LL, i2 = 16LL, s2 = 17LL}),
> +  153, 'correct isis align 16 stack')
> +
> +test:is(ffi_ccall.test_2_isisis_no_align_reg(0LL,
> +    {i = 1LL, s = 2LL, i2 = 3LL, s2 = 4LL, i3 = 5LL, s3 = 6LL},
> +    {i = 7LL, s = 8LL, i2 = 9LL, s2 = 10LL}),
> +  55, 'correct isisis no align reg')
> +
> +test:is(ffi_ccall.test_2_isisis_no_align_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {i = 10LL, s = 11LL, i2 = 12LL, s2 = 13LL, i3 = 14LL, s3 = 15LL},
> +    {i = 16LL, s = 17LL, i2 = 18LL, s2 = 19LL, i3 = 20LL, s3 = 21LL}),
> +  231, 'correct isisis no align stack')
> +
> +test:is(ffi_ccall.test_2_isisis_a16_reg(0LL,
> +    {i = 1LL, s = 2LL, i2 = 3LL, s2 = 4LL, i3 = 5LL, s3 = 6LL},
> +    {i = 7LL, s = 8LL, i2 = 9LL, s2 = 10LL}),
> +  55, 'correct isisis align 16 reg')
> +
> +test:is(ffi_ccall.test_2_isisis_a16_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {i = 10LL, s = 11LL, i2 = 12LL, s2 = 13LL, i3 = 14LL, s3 = 15LL},
> +    {i = 16LL, s = 17LL, i2 = 18LL, s2 = 19LL, i3 = 20LL, s3 = 21LL}),
> +  231, 'correct isisis align 16 stack')
> +
> +
> +test:is(ffi_ccall.test_2_isis_no_align_split(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL,
> +    {i = 8LL, s = 9LL, i2 = 10LL, s2 = 11LL},
> +    {i = 12LL, s = 13LL, i2 = 14LL, s2 = 15LL}),
> +  120, 'correct isis no align split')
> +
> +test:is(ffi_ccall.test_2_isis_a16_split(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL,
> +    {i = 8LL, s = 9LL, i2 = 10LL, s2 = 11LL},
> +    {i = 12LL, s = 13LL, i2 = 14LL, s2 = 15LL}),
> +  120, 'correct isis a16 split')
> +
> +test:is(ffi_ccall.test_2_ill_packed(0LL,
> +    {x = 1LL, y = 2LL}, {x = 3LL, y = 4LL}),
> +  10, 'correct ill packed')
> +
> +test:is(ffi_ccall.test_2_ill_packed_reord(0LL,
> +    {x = 1LL, y = 2LL}, {x = 3LL, y = 4LL},
> +    5LL, {x = 6LL, y = 7LL}),
> +  28, 'correct ill packed reord')
> +
> +test:is(ffi_ccall.test_2_ill_packed_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {x = 10LL, y = 11LL}, {x = 12LL, y = 13LL}),
> +  91, 'correct ill packed stack')
> +
> +test:is(ffi_ccall.test_2_ill_packed_a16(0LL,
> +    {x = 1LL, y = 2LL}, {x = 3LL, y = 4LL}),
> +  10, 'correct ill packed a16')
> +
> +test:is(ffi_ccall.test_2_ill_packed_a16_reord(0LL,
> +    {x = 1LL, y = 2LL}, {x = 3LL, y = 4LL},
> +    5LL, {x = 6LL, y = 7LL}),
> +  28, 'correct ill packed a16 reord')
> +
> +test:is(ffi_ccall.test_2_ill_packed_a16_stack(
> +    1LL, 2LL, 3LL, 4LL, 5LL, 6LL, 7LL, 8LL, 9LL,
> +    {x = 10LL, y = 11LL}, {x = 12LL, y = 13LL}),
> +  91, 'correct ill packed a16 stack')
> +
> +test:done(true)

[-- Attachment #2: Type: text/html, Size: 55312 bytes --]

  reply	other threads:[~2026-06-01 13:02 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-30 16:04 [Tarantool-patches] [PATCH luajit 0/5] Various FFI ABI calling conventions fixes Sergey Kaplun via Tarantool-patches
2026-05-30 16:04 ` [Tarantool-patches] [PATCH luajit 1/5] FFI: Unify stack setup for C calls in interpreter Sergey Kaplun via Tarantool-patches
2026-05-30 16:04 ` [Tarantool-patches] [PATCH luajit 2/5] FFI/ARM64/OSX: Handle non-standard OSX C calling conventions Sergey Kaplun via Tarantool-patches
2026-06-01 11:40   ` Sergey Bronnikov via Tarantool-patches
2026-05-30 16:04 ` [Tarantool-patches] [PATCH luajit 3/5] ARM64: Fix pass-by-value struct " Sergey Kaplun via Tarantool-patches
2026-06-01 12:27   ` Sergey Bronnikov via Tarantool-patches
2026-05-30 16:04 ` [Tarantool-patches] [PATCH luajit 4/5] FFI: Various ABI and calling convention fixes Sergey Kaplun via Tarantool-patches
2026-06-01 13:02   ` Sergey Bronnikov via Tarantool-patches [this message]
2026-05-30 16:04 ` [Tarantool-patches] [PATCH luajit 5/5] FFI/MacOS: Fix calling convention for enums Sergey Kaplun via Tarantool-patches
2026-06-01 13:07   ` Sergey Bronnikov via Tarantool-patches

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8e8cab09-9dfd-4cbd-bd55-a74cc491955b@tarantool.org \
    --to=tarantool-patches@dev.tarantool.org \
    --cc=e.temirgaleev@tarantool.org \
    --cc=sergeyb@tarantool.org \
    --cc=skaplun@tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH luajit 4/5] FFI: Various ABI and calling convention fixes.' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox