PowerPC64 ELFv1 fp arg fixes The ELFv1 ABI says: "Single precision floating point values are mapped to the second word in a single doubleword" and also "Floating point registers f1 through f13 are used consecutively to pass up to 13 floating point values, one member aggregates passed by value containing a floating point value, and to pass complex floating point values". libffi wasn't expecting float args in the second word, and wasn't passing one member aggregates in fp registers. This patch fixes those problems, making use of the existing ELFv2 homogeneous aggregate support since a one element fp struct is a special case of an homogeneous aggregate. I've also set a flag when returning pointers that might be used one day. This is just a tidy since the ppc64 assembly support code currently doesn't test FLAG_RETURNS_64BITS for integer types.. * src/powerpc/ffi_linux64.c (discover_homogeneous_aggregate): Compile for ELFv1 too, handling single element aggregates. (ffi_prep_cif_linux64_core): Call discover_homogeneous_aggregate for ELFv1. Set FLAG_RETURNS_64BITS for FFI_TYPE_POINTER return. (ffi_prep_args64): Call discover_homogeneous_aggregate for ELFv1, and handle single element structs containing float or double as if the element wasn't wrapped in a struct. Store floats in second word of doubleword slot when big-endian. (ffi_closure_helper_LINUX64): Similarly.
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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
diff --git a/src/powerpc/ffi_linux64.c b/src/powerpc/ffi_linux64.c
index b481c60..93a31f9 100644
--- a/src/powerpc/ffi_linux64.c
+++ b/src/powerpc/ffi_linux64.c
@@ -62,7 +62,6 @@ ffi_prep_types_linux64 (ffi_abi abi)
#endif
-#if _CALL_ELF == 2
static unsigned int
discover_homogeneous_aggregate (const ffi_type *t, unsigned int *elnum)
{
@@ -86,8 +85,13 @@ discover_homogeneous_aggregate (const ffi_type *t, unsigned int *elnum)
return 0;
base_elt = el_elt;
total_elnum += el_elnum;
+#if _CALL_ELF == 2
if (total_elnum > 8)
return 0;
+#else
+ if (total_elnum > 1)
+ return 0;
+#endif
el++;
}
*elnum = total_elnum;
@@ -98,7 +102,6 @@ discover_homogeneous_aggregate (const ffi_type *t, unsigned int *elnum)
return 0;
}
}
-#endif
/* Perform machine dependent cif processing */
@@ -109,9 +112,7 @@ ffi_prep_cif_linux64_core (ffi_cif *cif)
unsigned bytes;
unsigned i, fparg_count = 0, intarg_count = 0;
unsigned flags = cif->flags;
-#if _CALL_ELF == 2
unsigned int elt, elnum;
-#endif
#if FFI_TYPE_LONGDOUBLE == FFI_TYPE_DOUBLE
/* If compiled without long double support.. */
@@ -157,6 +158,7 @@ ffi_prep_cif_linux64_core (ffi_cif *cif)
/* Fall through. */
case FFI_TYPE_UINT64:
case FFI_TYPE_SINT64:
+ case FFI_TYPE_POINTER:
flags |= FLAG_RETURNS_64BITS;
break;
@@ -222,7 +224,6 @@ ffi_prep_cif_linux64_core (ffi_cif *cif)
intarg_count = FFI_ALIGN (intarg_count, align);
}
intarg_count += ((*ptr)->size + 7) / 8;
-#if _CALL_ELF == 2
elt = discover_homogeneous_aggregate (*ptr, &elnum);
if (elt)
{
@@ -231,7 +232,6 @@ ffi_prep_cif_linux64_core (ffi_cif *cif)
flags |= FLAG_ARG_NEEDS_PSAVE;
}
else
-#endif
{
if (intarg_count > NUM_GPR_ARG_REGISTERS64)
flags |= FLAG_ARG_NEEDS_PSAVE;
@@ -449,9 +449,7 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
i < nargs;
i++, ptr++, p_argv.v++)
{
-#if _CALL_ELF == 2
unsigned int elt, elnum;
-#endif
switch ((*ptr)->type)
{
@@ -494,6 +492,7 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
/* Fall through. */
#endif
case FFI_TYPE_DOUBLE:
+ do_double:
double_tmp = **p_argv.d;
if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
{
@@ -512,17 +511,30 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
break;
case FFI_TYPE_FLOAT:
+ do_float:
double_tmp = **p_argv.f;
if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
{
*fpr_base.d++ = double_tmp;
#if _CALL_ELF != 2
if ((flags & FLAG_COMPAT) != 0)
- *next_arg.f = (float) double_tmp;
+ {
+# ifndef __LITTLE_ENDIAN__
+ next_arg.f[1] = (float) double_tmp;
+# else
+ next_arg.f[0] = (float) double_tmp;
+# endif
+ }
#endif
}
else
- *next_arg.f = (float) double_tmp;
+ {
+# ifndef __LITTLE_ENDIAN__
+ next_arg.f[1] = (float) double_tmp;
+# else
+ next_arg.f[0] = (float) double_tmp;
+# endif
+ }
if (++next_arg.ul == gpr_end.ul)
next_arg.ul = rest.ul;
fparg_count++;
@@ -538,10 +550,10 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
if (align > 1)
next_arg.p = FFI_ALIGN (next_arg.p, align);
}
-#if _CALL_ELF == 2
elt = discover_homogeneous_aggregate (*ptr, &elnum);
if (elt)
{
+#if _CALL_ELF == 2
union {
void *v;
float *f;
@@ -583,9 +595,14 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
fparg_count++;
}
while (--elnum != 0);
+#else
+ if (elt == FFI_TYPE_FLOAT)
+ goto do_float;
+ else
+ goto do_double;
+#endif
}
else
-#endif
{
words = ((*ptr)->size + 7) / 8;
if (next_arg.ul >= gpr_base.ul && next_arg.ul + words > gpr_end.ul)
@@ -796,12 +813,10 @@ ffi_closure_helper_LINUX64 (ffi_cif *cif,
if (align > 1)
pst = (unsigned long *) FFI_ALIGN ((size_t) pst, align);
}
- elt = 0;
-#if _CALL_ELF == 2
elt = discover_homogeneous_aggregate (arg_types[i], &elnum);
-#endif
if (elt)
{
+#if _CALL_ELF == 2
union {
void *v;
unsigned long *ul;
@@ -853,6 +868,12 @@ ffi_closure_helper_LINUX64 (ffi_cif *cif,
}
while (--elnum != 0);
}
+#else
+ if (elt == FFI_TYPE_FLOAT)
+ goto do_float;
+ else
+ goto do_double;
+#endif
}
else
{
@@ -894,6 +915,7 @@ ffi_closure_helper_LINUX64 (ffi_cif *cif,
/* Fall through. */
#endif
case FFI_TYPE_DOUBLE:
+ do_double:
/* On the outgoing stack all values are aligned to 8 */
/* there are 13 64bit floating point registers */
@@ -908,6 +930,7 @@ ffi_closure_helper_LINUX64 (ffi_cif *cif,
break;
case FFI_TYPE_FLOAT:
+ do_float:
if (pfr < end_pfr && i < nfixedargs)
{
/* Float values are stored as doubles in the
@@ -917,7 +940,13 @@ ffi_closure_helper_LINUX64 (ffi_cif *cif,
pfr++;
}
else
- avalue[i] = pst;
+ {
+#ifndef __LITTLE_ENDIAN__
+ avalue[i] = (char *) pst + 4;
+#else
+ avalue[i] = pst;
+#endif
+ }
pst++;
break;