ia64: fix variadic function closures with FP arguments

libffi test framework already flagged failures as:

```
    FAIL: libffi.call/cls_double_va.c -W -Wall -Wno-psabi -O0 output pattern test, is 7.0
    res: 4
    0.0
    res: 4
    ? should match 7.0
    ?es: 4
    ?.0
    res: 4
```

Failure happens here at

```c
    // testsuite/libffi.call/cls_double_va.c
    ...
    char*  format    = "%.1f\n";
    double doubleArg = 7;
    ...
    CHECK(ffi_prep_closure_loc(pcl, &cif, cls_double_va_fn, NULL,
                               code) == FFI_OK);
    res = ((int(*)(char*, ...))(code))(format, doubleArg);
```

libffi expects 'doubleArg' to be located in 'f9' (second FP argument) but
gcc placed it to 'r33' (second GR).

ia64 software [1] manual described argument passing ABI in
"8.5.2 Register Parameters" as:

"""
    If an actual parameter is known to correspond to a floating-point
    formal parameter, the following rules apply:
    a) The actual parameter is passed in the next available floating-point
       parameter register, if one is available. Floating-point parameter
       registers are allocated as needed from the range f8-f15, starting
       with f8.
    b) If all available floating-point parameter registers have been used,
       the actual parameter is passed in the appropriate general register(s).
       (This case can occur only as a result of homogeneous floating-point
       aggregates, described below.)

    If a floating-point actual parameter is known to correspond to
    a variable-argument specification in  the formal parameter list,
    the following rule applies:
    c) The actual parameter is passed in the appropriate general
       register(s).

    If the compiler cannot determine, at the point of call,
    whether the corresponding formal parameter is a varargs parameter,
    it must generate code that satisfies both of the above conditions.
    (The compiler’s determination may be based on prototype declarations,
    language standard assumptions,  analysis, or other user options or
    information.)
"""

We have [c] case here and gcc uses only GR for parameter passing.

The change binds known variadic arguments ro GRs instead of FPs as those
are always expected to be initialized for all variadic call types.

This fixes all 10 failures on ia64-unknown-linux-gnu:

```
                 === libffi Summary ===
    -# of expected passes            1945
    -# of unexpected failures        10
    +
    +# of expected passes            1955
```

[1]: https://www.intel.com/content/dam/www/public/us/en/documents/guides/itanium-software-runtime-architecture-guide.pdf

Signed-off-by: Sergei Trofimovich <slyfox@gentoo.org>
This commit is contained in:
Sergei Trofimovich
2018-02-11 11:29:39 +00:00
parent 9429968b82
commit 11de69ddb7
2 changed files with 26 additions and 7 deletions

View File

@@ -220,8 +220,8 @@ hfa_element_type (ffi_type *type, int nested)
/* Perform machine dependent cif processing. */
ffi_status
ffi_prep_cif_machdep(ffi_cif *cif)
static ffi_status
ffi_prep_cif_machdep_core(ffi_cif *cif)
{
int flags;
@@ -271,6 +271,22 @@ ffi_prep_cif_machdep(ffi_cif *cif)
return FFI_OK;
}
ffi_status
ffi_prep_cif_machdep(ffi_cif *cif)
{
cif->nfixedargs = cif->nargs;
return ffi_prep_cif_machdep_core(cif);
}
ffi_status
ffi_prep_cif_machdep_var(ffi_cif *cif,
unsigned int nfixedargs,
unsigned int ntotalargs MAYBE_UNUSED)
{
cif->nfixedargs = nfixedargs;
return ffi_prep_cif_machdep_core(cif);
}
extern int ffi_call_unix (struct ia64_args *, PTR64, void (*)(void), UINT64);
void
@@ -454,10 +470,11 @@ ffi_closure_unix_inner (ffi_closure *closure, struct ia64_args *stack,
ffi_cif *cif;
void **avalue;
ffi_type **p_arg;
long i, avn, gpcount, fpcount;
long i, avn, gpcount, fpcount, nfixedargs;
cif = closure->cif;
avn = cif->nargs;
nfixedargs = cif->nfixedargs;
avalue = alloca (avn * sizeof (void *));
/* If the structure return value is passed in memory get that location
@@ -468,6 +485,7 @@ ffi_closure_unix_inner (ffi_closure *closure, struct ia64_args *stack,
gpcount = fpcount = 0;
for (i = 0, p_arg = cif->arg_types; i < avn; i++, p_arg++)
{
int named = i < nfixedargs;
switch ((*p_arg)->type)
{
case FFI_TYPE_SINT8:
@@ -491,7 +509,7 @@ ffi_closure_unix_inner (ffi_closure *closure, struct ia64_args *stack,
break;
case FFI_TYPE_FLOAT:
if (gpcount < 8 && fpcount < 8)
if (named && gpcount < 8 && fpcount < 8)
{
fpreg *addr = &stack->fp_regs[fpcount++];
float result;
@@ -505,7 +523,7 @@ ffi_closure_unix_inner (ffi_closure *closure, struct ia64_args *stack,
break;
case FFI_TYPE_DOUBLE:
if (gpcount < 8 && fpcount < 8)
if (named && gpcount < 8 && fpcount < 8)
{
fpreg *addr = &stack->fp_regs[fpcount++];
double result;
@@ -521,7 +539,7 @@ ffi_closure_unix_inner (ffi_closure *closure, struct ia64_args *stack,
case FFI_TYPE_LONGDOUBLE:
if (gpcount & 1)
gpcount++;
if (LDBL_MANT_DIG == 64 && gpcount < 8 && fpcount < 8)
if (LDBL_MANT_DIG == 64 && named && gpcount < 8 && fpcount < 8)
{
fpreg *addr = &stack->fp_regs[fpcount++];
__float80 result;

View File

@@ -50,6 +50,7 @@ typedef enum ffi_abi {
#define FFI_TRAMPOLINE_SIZE 24 /* Really the following struct, which */
/* can be interpreted as a C function */
/* descriptor: */
#define FFI_TARGET_SPECIFIC_VARIADIC 1
#define FFI_EXTRA_CIF_FIELDS unsigned nfixedargs
#endif