x86: Honor alignment of arguments Darwin aligns long-double to 16, and thus all of the long double tests were failing due to not honoring that. We ought to be able to devise a test case for GCC using __attribute__((aligned)) that would have failed too.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
diff --git a/src/x86/ffi.c b/src/x86/ffi.c
index 4c96c6d..c4d740a 100644
--- a/src/x86/ffi.c
+++ b/src/x86/ffi.c
@@ -332,13 +332,28 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
else
{
size_t za = ALIGN (z, FFI_SIZEOF_ARG);
+ size_t align = FFI_SIZEOF_ARG;
+
+ /* Alignment rules for arguments are quite complex. Vectors and
+ structures with 16 byte alignment get it. Note that long double
+ on Darwin does have 16 byte alignment, and does not get this
+ alignment if passed directly; a structure with a long double
+ inside, however, would get 16 byte alignment. Since libffi does
+ not support vectors, we need non concern ourselves with other
+ cases. */
+ if (t == FFI_TYPE_STRUCT && ty->alignment >= 16)
+ align = 16;
+
if (dir < 0)
{
+ /* ??? These reverse argument ABIs are probably too old
+ to have cared about alignment. Someone should check. */
argp -= za;
memcpy (argp, valp, z);
}
else
{
+ argp = (char *)ALIGN (argp, align);
memcpy (argp, valp, z);
argp += za;
}
@@ -419,8 +434,9 @@ ffi_closure_inner (struct closure_frame *frame, char *stack)
arg_types = cif->arg_types;
for (i = 0; i < n; ++i)
{
- size_t z = arg_types[i]->size;
- int t = arg_types[i]->type;
+ ffi_type *ty = arg_types[i];
+ size_t z = ty->size;
+ int t = ty->type;
void *valp;
if (z <= FFI_SIZEOF_ARG && t != FFI_TYPE_STRUCT)
@@ -441,13 +457,22 @@ ffi_closure_inner (struct closure_frame *frame, char *stack)
else
{
size_t za = ALIGN (z, FFI_SIZEOF_ARG);
+ size_t align = FFI_SIZEOF_ARG;
+
+ /* See the comment in ffi_call_int. */
+ if (t == FFI_TYPE_STRUCT && ty->alignment >= 16)
+ align = 16;
+
if (dir < 0)
{
+ /* ??? These reverse argument ABIs are probably too old
+ to have cared about alignment. Someone should check. */
argp -= za;
valp = argp;
}
else
{
+ argp = (char *)ALIGN (argp, align);
valp = argp;
argp += za;
}