This is the mail archive of the
libffi-discuss@sourceware.org
mailing list for the libffi project.
Re: [PATCH] Add variadic support
I think this looks fine.
Anthony, does the patch look okay to you?
Thanks,
Chung-Lin
On 2011/3/8 03:18 AM, David Gilbert wrote:
> Hi,
> Here is a rework of the previous patch, based on the previous discussion and
> suggestions from Chung-Lin.
>
> (This is relative libffi git rev cbb062cc35c518004f1ab45c847f8ec4f66069ad)
>
> Dave
>
> diff --git a/doc/libffi.texi b/doc/libffi.texi
> index 5cdd667..a7c629a 100644
> --- a/doc/libffi.texi
> +++ b/doc/libffi.texi
> @@ -133,8 +133,6 @@ This initializes @var{cif} according to the given
> parameters.
> you want. @ref{Multiple ABIs} for more information.
>
> @var{nargs} is the number of arguments that this function accepts.
> -@samp{libffi} does not yet handle varargs functions; see @ref{Missing
> -Features} for more information.
>
> @var{rtype} is a pointer to an @code{ffi_type} structure that
> describes the return type of the function. @xref{Types}.
> @@ -150,6 +148,28 @@ objects is incorrect; or @code{FFI_BAD_ABI} if
> the @var{abi} parameter
> is invalid.
> @end defun
>
> +If the function being called is variadic (varargs) then @code{ffi_prep_cif_var}
> +must be used instead of @code{ffi_prep_cif}.
> +
> +@findex ffi_prep_cif_var
> +@defun ffi_status ffi_prep_cif_var (ffi_cif *@var{cif}, ffi_abi
> @var{abi}, unsigned int @var{nfixedargs}, unsigned int
> @var{ntotalargs}, ffi_type *@var{rtype}, ffi_type **@var{argtypes})
> +This initializes @var{cif} according to the given parameters for
> +a call to a variadic function. In general it's operation is the
> +same as for @code{ffi_prep_cif} except that:
> +
> +@var{nfixedargs} is the number of fixed arguments, prior to any
> +variadic arguments. It must be greater than zero.
> +
> +@var{ntotalargs} the total number of arguments, including variadic
> +and fixed arguments.
> +
> +Note that, different cif's must be prepped for calls to the same
> +function when different numbers of arguments are passed.
> +
> +Also note that a call to @code{ffi_prep_cif_var} with
> @var{nfixedargs}=@var{nototalargs}
> +is NOT equivalent to a call to @code{ffi_prep_cif}.
> +
> +@end defun
>
> To call a function using an initialized @code{ffi_cif}, use the
> @code{ffi_call} function:
> @@ -572,9 +592,7 @@ support for these.
>
> @itemize @bullet
> @item
> -There is no support for calling varargs functions. This may work on
> -some platforms, depending on how the ABI is defined, but it is not
> -reliable.
> +Variadic closures.
>
> @item
> There is no support for bit fields in structures.
> @@ -591,6 +609,7 @@ The ``raw'' API is undocumented.
> @c anything else?
> @end itemize
>
> +Note that variadic support is very new and tested on a relatively
> small number of platforms.
>
> @node Index
> @unnumbered Index
> diff --git a/include/ffi.h.in b/include/ffi.h.in
> index 96b8fd3..f27f4f3 100644
> --- a/include/ffi.h.in
> +++ b/include/ffi.h.in
> @@ -203,6 +203,15 @@ typedef struct {
> #endif
> } ffi_cif;
>
> +/* Used internally, but overridden by some architectures */
> +ffi_status ffi_prep_cif_core(ffi_cif *cif,
> + ffi_abi abi,
> + unsigned int isvariadic,
> + unsigned int nfixedargs,
> + unsigned int ntotalargs,
> + ffi_type *rtype,
> + ffi_type **atypes);
> +
> /* ---- Definitions for the raw API -------------------------------------- */
>
> #ifndef FFI_SIZEOF_ARG
> @@ -380,6 +389,13 @@ ffi_status ffi_prep_cif(ffi_cif *cif,
> ffi_type *rtype,
> ffi_type **atypes);
>
> +ffi_status ffi_prep_cif_var(ffi_cif *cif,
> + ffi_abi abi,
> + unsigned int nfixedargs,
> + unsigned int ntotalargs,
> + ffi_type *rtype,
> + ffi_type **atypes);
> +
> void ffi_call(ffi_cif *cif,
> void (*fn)(void),
> void *rvalue,
> diff --git a/include/ffi_common.h b/include/ffi_common.h
> index d953762..919eec2 100644
> --- a/include/ffi_common.h
> +++ b/include/ffi_common.h
> @@ -75,6 +75,8 @@ void ffi_type_test(ffi_type *a, char *file, int line);
>
> /* Perform machine dependent cif processing */
> ffi_status ffi_prep_cif_machdep(ffi_cif *cif);
> +ffi_status ffi_prep_cif_machdep_var(ffi_cif *cif,
> + unsigned int nfixedargs, unsigned int ntotalargs);
>
> /* Extended cif, used in callback from assembly routine */
> typedef struct
> diff --git a/man/Makefile.am b/man/Makefile.am
> index 2519277..afcbfb6 100644
> --- a/man/Makefile.am
> +++ b/man/Makefile.am
> @@ -2,7 +2,7 @@
>
> AUTOMAKE_OPTIONS=foreign
>
> -EXTRA_DIST = ffi.3 ffi_call.3 ffi_prep_cif.3
> +EXTRA_DIST = ffi.3 ffi_call.3 ffi_prep_cif.3 ffi_prep_cif_var.3
>
> -man_MANS = ffi.3 ffi_call.3 ffi_prep_cif.3
> +man_MANS = ffi.3 ffi_call.3 ffi_prep_cif.3 ffi_prep_cif_var.3
>
> diff --git a/man/ffi.3 b/man/ffi.3
> index 18b5d5d..1f1d303 100644
> --- a/man/ffi.3
> +++ b/man/ffi.3
> @@ -16,6 +16,15 @@ libffi, -lffi
> .Fa "ffi_type **atypes"
> .Fc
> .Ft void
> +.Fo ffi_prep_cif_var
> +.Fa "ffi_cif *cif"
> +.Fa "ffi_abi abi"
> +.Fa "unsigned int nfixedargs"
> +.Fa "unsigned int ntotalargs"
> +.Fa "ffi_type *rtype"
> +.Fa "ffi_type **atypes"
> +.Fc
> +.Ft void
> .Fo ffi_call
> .Fa "ffi_cif *cif"
> .Fa "void (*fn)(void)"
> @@ -28,4 +37,5 @@ generate a call to another function at runtime
> without requiring knowledge of
> the called function's interface at compile time.
> .Sh SEE ALSO
> .Xr ffi_prep_cif 3 ,
> +.Xr ffi_prep_cif_var 3 ,
> .Xr ffi_call 3
> diff --git a/man/ffi_prep_cif.3 b/man/ffi_prep_cif.3
> index 9436b31..fe66452 100644
> --- a/man/ffi_prep_cif.3
> +++ b/man/ffi_prep_cif.3
> @@ -37,7 +37,9 @@ structs that describe the data type, size and
> alignment of each argument.
> points to an
> .Nm ffi_type
> that describes the data type, size and alignment of the
> -return value.
> +return value. Note that to call a variadic function
> +.Nm ffi_prep_cif_var
> +must be used instead.
> .Sh RETURN VALUES
> Upon successful completion,
> .Nm ffi_prep_cif
> @@ -63,4 +65,5 @@ defined in
> .
> .Sh SEE ALSO
> .Xr ffi 3 ,
> -.Xr ffi_call 3
> +.Xr ffi_call 3 ,
> +.Xr ffi_prep_cif_var 3
> diff --git a/man/ffi_prep_cif_var.3 b/man/ffi_prep_cif_var.3
> new file mode 100644
> index 0000000..1b39c03
> --- /dev/null
> +++ b/man/ffi_prep_cif_var.3
> @@ -0,0 +1,73 @@
> +.Dd January 25, 2011
> +.Dt ffi_prep_cif_var 3
> +.Sh NAME
> +.Nm ffi_prep_cif_var
> +.Nd Prepare a
> +.Nm ffi_cif
> +structure for use with
> +.Nm ffi_call
> +for variadic functions.
> +.Sh SYNOPSIS
> +.In ffi.h
> +.Ft ffi_status
> +.Fo ffi_prep_cif_var
> +.Fa "ffi_cif *cif"
> +.Fa "ffi_abi abi"
> +.Fa "unsigned int nfixedargs"
> +.Fa "unsigned int ntotalargs"
> +.Fa "ffi_type *rtype"
> +.Fa "ffi_type **atypes"
> +.Fc
> +.Sh DESCRIPTION
> +The
> +.Nm ffi_prep_cif_var
> +function prepares a
> +.Nm ffi_cif
> +structure for use with
> +.Nm ffi_call
> +for variadic functions.
> +.Fa abi
> +specifies a set of calling conventions to use.
> +.Fa atypes
> +is an array of
> +.Fa ntotalargs
> +pointers to
> +.Nm ffi_type
> +structs that describe the data type, size and alignment of each argument.
> +.Fa rtype
> +points to an
> +.Nm ffi_type
> +that describes the data type, size and alignment of the
> +return value.
> +.Fa nfixedargs
> +must contain the number of fixed (non-variadic) arguments.
> +Note that to call a non-variadic function
> +.Nm ffi_prep_cif
> +must be used.
> +.Sh RETURN VALUES
> +Upon successful completion,
> +.Nm ffi_prep_cif_var
> +returns
> +.Nm FFI_OK .
> +It will return
> +.Nm FFI_BAD_TYPEDEF
> +if
> +.Fa cif
> +is
> +.Nm NULL
> +or
> +.Fa atypes
> +or
> +.Fa rtype
> +is malformed. If
> +.Fa abi
> +does not refer to a valid ABI,
> +.Nm FFI_BAD_ABI
> +will be returned. Available ABIs are
> +defined in
> +.Nm <ffitarget.h>
> +.
> +.Sh SEE ALSO
> +.Xr ffi 3 ,
> +.Xr ffi_call 3 ,
> +.Xr ffi_prep_cif 3
> diff --git a/src/arm/ffi.c b/src/arm/ffi.c
> index 885a9cb..c226dbd 100644
> --- a/src/arm/ffi.c
> +++ b/src/arm/ffi.c
> @@ -189,6 +189,18 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
> return FFI_OK;
> }
>
> +/* Perform machine dependent cif processing for variadic calls */
> +ffi_status ffi_prep_cif_machdep_var(ffi_cif *cif,
> + unsigned int nfixedargs,
> + unsigned int ntotalargs)
> +{
> + /* VFP variadic calls actually use the SYSV ABI */
> + if (cif->abi == FFI_VFP)
> + cif->abi = FFI_SYSV;
> +
> + return ffi_prep_cif_machdep(cif);
> +}
> +
> /* Prototypes for assembly functions, in sysv.S */
> extern void ffi_call_SYSV (void (*fn)(void), extended_cif *,
> unsigned, unsigned, unsigned *);
> extern void ffi_call_VFP (void (*fn)(void), extended_cif *, unsigned,
> unsigned, unsigned *);
> diff --git a/src/arm/ffitarget.h b/src/arm/ffitarget.h
> index ce25b23..b9a0428 100644
> --- a/src/arm/ffitarget.h
> +++ b/src/arm/ffitarget.h
> @@ -55,6 +55,8 @@ typedef enum ffi_abi {
> #define FFI_TYPE_STRUCT_VFP_FLOAT (FFI_TYPE_LAST + 1)
> #define FFI_TYPE_STRUCT_VFP_DOUBLE (FFI_TYPE_LAST + 2)
>
> +#define FFI_TARGET_SPECIFIC_VARIADIC
> +
> /* ---- Definitions for closures ----------------------------------------- */
>
> #define FFI_CLOSURES 1
> @@ -62,4 +64,3 @@ typedef enum ffi_abi {
> #define FFI_NATIVE_RAW_API 0
>
> #endif
> -
> diff --git a/src/cris/ffi.c b/src/cris/ffi.c
> index f25d7b4..aaca5b1 100644
> --- a/src/cris/ffi.c
> +++ b/src/cris/ffi.c
> @@ -153,21 +153,24 @@ ffi_prep_args (char *stack, extended_cif * ecif)
> return (struct_count);
> }
>
> -ffi_status
> -ffi_prep_cif (ffi_cif * cif,
> - ffi_abi abi, unsigned int nargs,
> - ffi_type * rtype, ffi_type ** atypes)
> +ffi_status FFI_HIDDEN
> +ffi_prep_cif_core (ffi_cif * cif,
> + ffi_abi abi, unsigned int isvariadic,
> + unsigned int nfixedargs, unsigned int ntotalargs,
> + ffi_type * rtype, ffi_type ** atypes)
> {
> unsigned bytes = 0;
> unsigned int i;
> ffi_type **ptr;
>
> FFI_ASSERT (cif != NULL);
> + FFI_ASSERT((!isvariadic) || (nfixedargs >= 1));
> + FFI_ASSERT(nfixedargs <= ntotalargs);
> FFI_ASSERT (abi > FFI_FIRST_ABI && abi < FFI_LAST_ABI);
>
> cif->abi = abi;
> cif->arg_types = atypes;
> - cif->nargs = nargs;
> + cif->nargs = ntotalargs;
> cif->rtype = rtype;
>
> cif->flags = 0;
> diff --git a/src/prep_cif.c b/src/prep_cif.c
> index 8548cfd..0e2e116 100644
> --- a/src/prep_cif.c
> +++ b/src/prep_cif.c
> @@ -90,20 +90,33 @@ static ffi_status initialize_aggregate(ffi_type *arg)
> /* Perform machine independent ffi_cif preparation, then call
> machine dependent routine. */
>
> -ffi_status ffi_prep_cif(ffi_cif *cif, ffi_abi abi, unsigned int nargs,
> - ffi_type *rtype, ffi_type **atypes)
> +/* For non variadic functions isvariadic should be 0 and
> + nfixedargs==ntotalargs.
> +
> + For variadic calls, isvariadic should be 1 and nfixedargs
> + and ntotalargs set as appropriate. nfixedargs must always be >=1 */
> +
> +
> +ffi_status FFI_HIDDEN ffi_prep_cif_core(ffi_cif *cif, ffi_abi abi,
> + unsigned int isvariadic,
> + unsigned int nfixedargs,
> + unsigned int ntotalargs,
> + ffi_type *rtype, ffi_type **atypes)
> {
> unsigned bytes = 0;
> unsigned int i;
> ffi_type **ptr;
>
> FFI_ASSERT(cif != NULL);
> + FFI_ASSERT((!isvariadic) || (nfixedargs >= 1));
> + FFI_ASSERT(nfixedargs <= ntotalargs);
> +
> if (! (abi > FFI_FIRST_ABI && abi < FFI_LAST_ABI))
> return FFI_BAD_ABI;
>
> cif->abi = abi;
> cif->arg_types = atypes;
> - cif->nargs = nargs;
> + cif->nargs = ntotalargs;
> cif->rtype = rtype;
>
> cif->flags = 0;
> @@ -159,10 +172,31 @@ ffi_status ffi_prep_cif(ffi_cif *cif, ffi_abi
> abi, unsigned int nargs,
> cif->bytes = bytes;
>
> /* Perform machine dependent cif processing */
> +#ifdef FFI_TARGET_SPECIFIC_VARIADIC
> + if (isvariadic)
> + return ffi_prep_cif_machdep_var(cif, nfixedargs, ntotalargs);
> +#endif
> +
> return ffi_prep_cif_machdep(cif);
> }
> #endif /* not __CRIS__ */
>
> +ffi_status ffi_prep_cif(ffi_cif *cif, ffi_abi abi, unsigned int nargs,
> + ffi_type *rtype, ffi_type **atypes)
> +{
> + return ffi_prep_cif_core(cif, abi, 0, nargs, nargs, rtype, atypes);
> +}
> +
> +ffi_status ffi_prep_cif_var(ffi_cif *cif,
> + ffi_abi abi,
> + unsigned int nfixedargs,
> + unsigned int ntotalargs,
> + ffi_type *rtype,
> + ffi_type **atypes)
> +{
> + return ffi_prep_cif_core(cif, abi, 1, nfixedargs, ntotalargs, rtype, atypes);
> +}
> +
> #if FFI_CLOSURES
>
> ffi_status
> diff --git a/testsuite/libffi.call/cls_double_va.c
> b/testsuite/libffi.call/cls_double_va.c
> index 62bebbd..1a2706e 100644
> --- a/testsuite/libffi.call/cls_double_va.c
> +++ b/testsuite/libffi.call/cls_double_va.c
> @@ -6,7 +6,6 @@
>
> /* { dg-do run { xfail strongarm*-*-* xscale*-*-* } } */
> /* { dg-output "" { xfail avr32*-*-* } } */
> -/* { dg-skip-if "" arm*-*-* { "-mfloat-abi=hard" } { "" } } */
>
> #include "ffitest.h"
>
> @@ -36,7 +35,8 @@ int main (void)
> arg_types[1] = &ffi_type_double;
> arg_types[2] = NULL;
>
> - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_sint,
> + /* This printf call is variadic */
> + CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2, &ffi_type_sint,
> arg_types) == FFI_OK);
>
> args[0] = &format;
> @@ -48,6 +48,10 @@ int main (void)
> printf("res: %d\n", (int) res);
> // { dg-output "\nres: 4" }
>
> + /* The call to cls_double_va_fn is static, so have to use a normal prep_cif */
> + CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_sint,
> + arg_types) == FFI_OK);
> +
> CHECK(ffi_prep_closure_loc(pcl, &cif, cls_double_va_fn, NULL, code)
> == FFI_OK);
>
> res = ((int(*)(char*, double))(code))(format, doubleArg);
> diff --git a/testsuite/libffi.call/cls_longdouble_va.c
> b/testsuite/libffi.call/cls_longdouble_va.c
> index b33b2b7..95f9ff4 100644
> --- a/testsuite/libffi.call/cls_longdouble_va.c
> +++ b/testsuite/libffi.call/cls_longdouble_va.c
> @@ -6,7 +6,6 @@
>
> /* { dg-do run { xfail strongarm*-*-* xscale*-*-* } } */
> /* { dg-output "" { xfail avr32*-*-* x86_64-*-mingw* } } */
> -/* { dg-skip-if "" arm*-*-* { "-mfloat-abi=hard" } { "" } } */
>
> #include "ffitest.h"
>
> @@ -36,7 +35,8 @@ int main (void)
> arg_types[1] = &ffi_type_longdouble;
> arg_types[2] = NULL;
>
> - CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_sint,
> + /* This printf call is variadic */
> + CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2, &ffi_type_sint,
> arg_types) == FFI_OK);
>
> args[0] = &format;
> @@ -48,6 +48,10 @@ int main (void)
> printf("res: %d\n", (int) res);
> // { dg-output "\nres: 4" }
>
> + /* The call to cls_longdouble_va_fn is static, so have to use a
> normal prep_cif */
> + CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_sint,
> + arg_types) == FFI_OK);
> +
> CHECK(ffi_prep_closure_loc(pcl, &cif, cls_longdouble_va_fn, NULL,
> code) == FFI_OK);
>
> res = ((int(*)(char*, long double))(code))(format, ldArg);
> diff --git a/testsuite/libffi.call/float_va.c b/testsuite/libffi.call/float_va.c
> new file mode 100644
> index 0000000..4611969
> --- /dev/null
> +++ b/testsuite/libffi.call/float_va.c
> @@ -0,0 +1,107 @@
> +/* Area: fp and variadics
> + Purpose: check fp inputs and returns work on variadics, even
> the fixed params
> + Limitations: None
> + PR: none
> + Originator: <david.gilbert@linaro.org> 2011-01-25
> +
> + Intended to stress the difference in ABI on ARM vfp
> +*/
> +
> +/* { dg-do run } */
> +
> +#include <stdarg.h>
> +
> +#include "ffitest.h"
> +
> +/* prints out all the parameters, and returns the sum of them all.
> + * 'x' is the number of variadic parameters all of which are double
> in this test
> + */
> +double float_va_fn(unsigned int x, double y,...)
> +{
> + double total=0.0;
> + va_list ap;
> + unsigned int i;
> +
> + total+=(double)x;
> + total+=y;
> +
> + printf("%u: %.1lf :", x, y);
> +
> + va_start(ap, y);
> + for(i=0;i<x;i++)
> + {
> + double arg=va_arg(ap, double);
> + total+=arg;
> + printf(" %d:%.1lf ", i, arg);
> + }
> + va_end(ap);
> +
> + printf(" total: %.1lf\n", total);
> +
> + return total;
> +}
> +
> +int main (void)
> +{
> + ffi_cif cif;
> +
> + ffi_type *arg_types[5];
> + void *values[5];
> + double doubles[5];
> + unsigned int firstarg;
> + double resfp;
> +
> + /* First test, pass float_va_fn(0,2.0) - note there are no actual
> + * variadic parameters, but it's declared variadic so the ABI may be
> + * different. */
> + /* Call it statically and then via ffi */
> + resfp=float_va_fn(0,2.0);
> + // { dg-output "0: 2.0 : total: 2.0" }
> + printf("compiled: %.1lf\n", resfp);
> + // { dg-output "\ncompiled: 2.0" }
> +
> + arg_types[0] = &ffi_type_uint;
> + arg_types[1] = &ffi_type_double;
> + arg_types[2] = NULL;
> + CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 2, 2,
> + &ffi_type_double, arg_types) == FFI_OK);
> +
> + firstarg = 0;
> + doubles[0] = 2.0;
> + values[0] = &firstarg;
> + values[1] = &doubles[0];
> + ffi_call(&cif, FFI_FN(float_va_fn), &resfp, values);
> + // { dg-output "\n0: 2.0 : total: 2.0" }
> + printf("ffi: %.1lf\n", resfp);
> + // { dg-output "\nffi: 2.0" }
> +
> + /* Second test, float_va_fn(2,2.0,3.0,4.0), now with variadic params */
> + /* Call it statically and then via ffi */
> + resfp=float_va_fn(2,2.0,3.0,4.0);
> + // { dg-output "\n2: 2.0 : 0:3.0 1:4.0 total: 11.0" }
> + printf("compiled: %.1lf\n", resfp);
> + // { dg-output "\ncompiled: 11.0" }
> +
> + arg_types[0] = &ffi_type_uint;
> + arg_types[1] = &ffi_type_double;
> + arg_types[2] = &ffi_type_double;
> + arg_types[3] = &ffi_type_double;
> + arg_types[4] = NULL;
> + CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 2, 4,
> + &ffi_type_double, arg_types) == FFI_OK);
> +
> + firstarg = 2;
> + doubles[0] = 2.0;
> + doubles[1] = 3.0;
> + doubles[2] = 4.0;
> + values[0] = &firstarg;
> + values[1] = &doubles[0];
> + values[2] = &doubles[1];
> + values[3] = &doubles[2];
> + ffi_call(&cif, FFI_FN(float_va_fn), &resfp, values);
> + // { dg-output "\n2: 2.0 : 0:3.0 1:4.0 total: 11.0" }
> + printf("ffi: %.1lf\n", resfp);
> + // { dg-output "\nffi: 11.0" }
> +
> + exit(0);
> +}