[patch, ARM] VFP hard-float calling convention support

Chung-Lin Tang cltang@codesourcery.com
Thu Oct 14 03:45:00 GMT 2010


Anthony Green wrote:
> Chung-Lin Tang<cltang@codesourcery.com>  writes:
>
>> Hi,
>> this patch implements VFP hard-float calling conventions, as a new ABI
>> for ARM. Asides from VFP calling convention bits, some additional
>> optimizations have been added to the current softfp assembly code
>> paths too.
>
> Chung-Lin,
>
>    Sorry for the long delay.  I think this patch is fine.  Do you have
>    commit rights to GCC?  If so, please commit yourself.  Otherwise, I'll
>    push it into libffi git tree and sync with GCC in a week or so.
>
> Thank you!
>
> Anthony Green
> 650 352-3402

Hi Anthony,
thanks for the review. I have commit rights, but I think it would be 
better to sync with the git tree.

Thanks!
Chung-Lin




>
>
>>
>> The FFI_DEFAULT_ABI value is now defined depending on a new GCC
>> built-in preprocessor symbol __ARM_PCS_VFP, which was only recently
>> added into GCC trunk; this seems to be the only reliable way of
>> detecting the FP calling convention from within the source.
>>
>> Variadic function tests (cls_double_va.c and cls_longdouble_va.c in
>> testsuite/libffi.call) has been disabled for VFP hard-float; the rules
>> of AAPCS states that variadic functions switch back to the base
>> (softfp) ABI, a condition on function type that seems not currently
>> detectable within libffi.
>>
>> Testsuite patches have been added to support a dg-skip-if option
>> (adapted from the GCC testsuite), to turn off the above two mentioned
>> tests based on compiler options (skip on -mfloat-abi=hard). Patch was
>> regression tested under both -mfloat-abi=hard and softfp with no
>> unexpected failures.
>>
>> Comments on the patch are of course welcome.
>> If approved, please help to commit to git head and GCC trunk.
>>
>> Thanks,
>> Chung-Lin
>>
>>
>> 2010-08-16  Chung-Lin Tang<cltang@codesourcery.com>
>>
>>     * src/arm/ffi.c (ffi_prep_args): Add VFP register argument handling
>>     code, new parameter, and return value. Update comments.
>>     (ffi_prep_cif_machdep): Add case for VFP struct return values. Add
>>     call to layout_vfp_args().
>>     (ffi_call_SYSV): Update declaration.
>>     (ffi_call_VFP): New declaration.
>>     (ffi_call): Add VFP struct return conditions. Call ffi_call_VFP()
>>     when ABI is FFI_VFP.
>>     (ffi_closure_VFP): New declaration.
>>     (ffi_closure_SYSV_inner): Add new vfp_args parameter, update call to
>>     ffi_prep_incoming_args_SYSV().
>>     (ffi_prep_incoming_args_SYSV): Update parameters. Add VFP argument
>>     case handling.
>>     (ffi_prep_closure_loc): Pass ffi_closure_VFP to trampoline
>>     construction under VFP hard-float.
>>     (rec_vfp_type_p): New function.
>>     (vfp_type_p): Same.
>>     (place_vfp_arg): Same.
>>     (layout_vfp_args): Same.
>>     * src/arm/ffitarget.h (ffi_abi): Add FFI_VFP. Define FFI_DEFAULT_ABI
>>     based on __ARM_PCS_VFP.
>>     (FFI_EXTRA_CIF_FIELDS): Define for adding VFP hard-float specific
>>     fields.
>>     (FFI_TYPE_STRUCT_VFP_FLOAT): Define internally used type code.
>>     (FFI_TYPE_STRUCT_VFP_DOUBLE): Same.
>>     * src/arm/sysv.S (ffi_call_SYSV): Change call of ffi_prep_args() to
>>     direct call. Move function pointer load upwards.
>>     (ffi_call_VFP): New function.
>>     (ffi_closure_VFP): Same.
>>
>>     * testsuite/lib/libffi-dg.exp (check-flags): New function.
>>     (dg-skip-if): New function.
>>     * testsuite/libffi.call/cls_double_va.c: Skip if target is arm*-*-*
>>     and compiler options include -mfloat-abi=hard.
>>     * testsuite/libffi.call/cls_longdouble_va.c: Same.
>>
>> diff --git a/src/arm/ffi.c b/src/arm/ffi.c
>> index f6a6475..9a0a53c 100644
>> --- a/src/arm/ffi.c
>> +++ b/src/arm/ffi.c
>> @@ -29,12 +29,20 @@
>>
>>   #include<stdlib.h>
>>
>> -/* ffi_prep_args is called by the assembly routine once stack space
>> -   has been allocated for the function's arguments */
>> +/* Forward declares. */
>> +static int vfp_type_p (ffi_type *);
>> +static void layout_vfp_args (ffi_cif *);
>>
>> -void ffi_prep_args(char *stack, extended_cif *ecif)
>> +/* ffi_prep_args is called by the assembly routine once stack space
>> +   has been allocated for the function's arguments
>> +
>> +   The vfp_space parameter is the load area for VFP regs, the return
>> +   value is cif->vfp_used (word bitset of VFP regs used for passing
>> +   arguments). These are only used for the VFP hard-float ABI.
>> +*/
>> +int ffi_prep_args(char *stack, extended_cif *ecif, float *vfp_space)
>>   {
>> -  register unsigned int i;
>> +  register unsigned int i, vi = 0;
>>     register void **p_argv;
>>     register char *argp;
>>     register ffi_type **p_arg;
>> @@ -54,6 +62,21 @@ void ffi_prep_args(char *stack, extended_cif *ecif)
>>       {
>>         size_t z;
>>
>> +      /* Allocated in VFP registers. */
>> +      if (ecif->cif->abi == FFI_VFP
>> +	&&  vi<  ecif->cif->vfp_nargs&&  vfp_type_p (*p_arg))
>> +	{
>> +	  float* vfp_slot = vfp_space + ecif->cif->vfp_args[vi++];
>> +	  if ((*p_arg)->type == FFI_TYPE_FLOAT)
>> +	    *((float*)vfp_slot) = *((float*)*p_argv);
>> +	  else if ((*p_arg)->type == FFI_TYPE_DOUBLE)
>> +	    *((double*)vfp_slot) = *((double*)*p_argv);
>> +	  else
>> +	    memcpy(vfp_slot, *p_argv, (*p_arg)->size);
>> +	  p_argv++;
>> +	  continue;
>> +	}
>> +
>>         /* Align if necessary */
>>         if (((*p_arg)->alignment - 1)&  (unsigned) argp) {
>>   	argp = (char *) ALIGN(argp, (*p_arg)->alignment);
>> @@ -103,13 +126,15 @@ void ffi_prep_args(char *stack, extended_cif *ecif)
>>   	  p_argv++;
>>   	  argp += z;
>>       }
>> -
>> -  return;
>> +
>> +  /* Indicate the VFP registers used. */
>> +  return ecif->cif->vfp_used;
>>   }
>>
>>   /* Perform machine dependent cif processing */
>>   ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
>>   {
>> +  int type_code;
>>     /* Round the stack up to a multiple of 8 bytes.  This isn't needed
>>        everywhere, but it is on some platforms, and it doesn't harm anything
>>        when it isn't needed.  */
>> @@ -130,7 +155,14 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
>>         break;
>>
>>       case FFI_TYPE_STRUCT:
>> -      if (cif->rtype->size<= 4)
>> +      if (cif->abi == FFI_VFP
>> +	&&  (type_code = vfp_type_p (cif->rtype)) != 0)
>> +	{
>> +	  /* A Composite Type passed in VFP registers, either
>> +	     FFI_TYPE_STRUCT_VFP_FLOAT or FFI_TYPE_STRUCT_VFP_DOUBLE. */
>> +	  cif->flags = (unsigned) type_code;
>> +	}
>> +      else if (cif->rtype->size<= 4)
>>   	/* A Composite Type not larger than 4 bytes is returned in r0.  */
>>   	cif->flags = (unsigned)FFI_TYPE_INT;
>>         else
>> @@ -145,11 +177,18 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
>>         break;
>>       }
>>
>> +  /* Map out the register placements of VFP register args.
>> +     The VFP hard-float calling conventions are slightly more sophisticated than
>> +     the base calling conventions, so we do it here instead of in ffi_prep_args(). */
>> +  if (cif->abi == FFI_VFP)
>> +    layout_vfp_args (cif);
>> +
>>     return FFI_OK;
>>   }
>>
>> -extern void ffi_call_SYSV(void (*)(char *, extended_cif *), extended_cif *,
>> -			  unsigned, unsigned, unsigned *, void (*fn)(void));
>> +/* 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 *);
>>
>>   void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
>>   {
>> @@ -157,6 +196,8 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
>>
>>     int small_struct = (cif->flags == FFI_TYPE_INT
>>   		&&  cif->rtype->type == FFI_TYPE_STRUCT);
>> +  int vfp_struct = (cif->flags == FFI_TYPE_STRUCT_VFP_FLOAT
>> +		    || cif->flags == FFI_TYPE_STRUCT_VFP_DOUBLE);
>>
>>     ecif.cif = cif;
>>     ecif.avalue = avalue;
>> @@ -173,38 +214,51 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
>>       }
>>     else if (small_struct)
>>       ecif.rvalue =&temp;
>> +  else if (vfp_struct)
>> +    {
>> +      /* Largest case is double x 4. */
>> +      ecif.rvalue = alloca(32);
>> +    }
>>     else
>>       ecif.rvalue = rvalue;
>>
>>     switch (cif->abi)
>>       {
>>       case FFI_SYSV:
>> -      ffi_call_SYSV(ffi_prep_args,&ecif, cif->bytes, cif->flags, ecif.rvalue,
>> -		    fn);
>> +      ffi_call_SYSV (fn,&ecif, cif->bytes, cif->flags, ecif.rvalue);
>> +      break;
>>
>> +    case FFI_VFP:
>> +      ffi_call_VFP (fn,&ecif, cif->bytes, cif->flags, ecif.rvalue);
>>         break;
>> +
>>       default:
>>         FFI_ASSERT(0);
>>         break;
>>       }
>>     if (small_struct)
>>       memcpy (rvalue,&temp, cif->rtype->size);
>> +  else if (vfp_struct)
>> +    memcpy (rvalue, ecif.rvalue, cif->rtype->size);
>>   }
>>
>>   /** private members **/
>>
>>   static void ffi_prep_incoming_args_SYSV (char *stack, void **ret,
>> -					 void** args, ffi_cif* cif);
>> +					 void** args, ffi_cif* cif, float *vfp_stack);
>>
>>   void ffi_closure_SYSV (ffi_closure *);
>>
>> +void ffi_closure_VFP (ffi_closure *);
>> +
>>   /* This function is jumped to by the trampoline */
>>
>>   unsigned int
>> -ffi_closure_SYSV_inner (closure, respp, args)
>> +ffi_closure_SYSV_inner (closure, respp, args, vfp_args)
>>        ffi_closure *closure;
>>        void **respp;
>>        void *args;
>> +     void *vfp_args;
>>   {
>>     // our various things...
>>     ffi_cif       *cif;
>> @@ -219,7 +273,7 @@ ffi_closure_SYSV_inner (closure, respp, args)
>>      * a structure, it will re-set RESP to point to the
>>      * structure return address.  */
>>
>> -  ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif);
>> +  ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif, vfp_args);
>>
>>     (closure->fun) (cif, *respp, arg_area, closure->user_data);
>>
>> @@ -229,10 +283,12 @@ ffi_closure_SYSV_inner (closure, respp, args)
>>   /*@-exportheader@*/
>>   static void
>>   ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
>> -			    void **avalue, ffi_cif *cif)
>> +			    void **avalue, ffi_cif *cif,
>> +			    /* Used only under VFP hard-float ABI. */
>> +			    float *vfp_stack)
>>   /*@=exportheader@*/
>>   {
>> -  register unsigned int i;
>> +  register unsigned int i, vi = 0;
>>     register void **p_argv;
>>     register char *argp;
>>     register ffi_type **p_arg;
>> @@ -249,8 +305,16 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
>>     for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++)
>>       {
>>         size_t z;
>> -
>> -      size_t alignment = (*p_arg)->alignment;
>> +      size_t alignment;
>> +
>> +      if (cif->abi == FFI_VFP
>> +	&&  vi<  cif->vfp_nargs&&  vfp_type_p (*p_arg))
>> +	{
>> +	  *p_argv++ = (void*)(vfp_stack + cif->vfp_args[vi++]);
>> +	  continue;
>> +	}
>> +
>> +      alignment = (*p_arg)->alignment;
>>         if (alignment<  4)
>>   	alignment = 4;
>>         /* Align if necessary */
>> @@ -295,10 +359,17 @@ ffi_prep_closure_loc (ffi_closure* closure,
>>   		      void *user_data,
>>   		      void *codeloc)
>>   {
>> -  FFI_ASSERT (cif->abi == FFI_SYSV);
>> +  void (*closure_func)(ffi_closure*) = NULL;
>>
>> +  if (cif->abi == FFI_SYSV)
>> +    closure_func =&ffi_closure_SYSV;
>> +  else if (cif->abi == FFI_VFP)
>> +    closure_func =&ffi_closure_VFP;
>> +  else
>> +    FFI_ASSERT (0);
>> +
>>     FFI_INIT_TRAMPOLINE (&closure->tramp[0], \
>> -		&ffi_closure_SYSV,  \
>> +		       closure_func,  \
>>   		       codeloc);
>>
>>     closure->cif  = cif;
>> @@ -307,3 +378,123 @@ ffi_prep_closure_loc (ffi_closure* closure,
>>
>>     return FFI_OK;
>>   }
>> +
>> +/* Below are routines for VFP hard-float support. */
>> +
>> +static int rec_vfp_type_p (ffi_type *t, int *elt, int *elnum)
>> +{
>> +  switch (t->type)
>> +    {
>> +    case FFI_TYPE_FLOAT:
>> +    case FFI_TYPE_DOUBLE:
>> +      *elt = (int) t->type;
>> +      *elnum = 1;
>> +      return 1;
>> +
>> +    case FFI_TYPE_STRUCT_VFP_FLOAT:
>> +      *elt = FFI_TYPE_FLOAT;
>> +      *elnum = t->size / sizeof (float);
>> +      return 1;
>> +
>> +    case FFI_TYPE_STRUCT_VFP_DOUBLE:
>> +      *elt = FFI_TYPE_DOUBLE;
>> +      *elnum = t->size / sizeof (double);
>> +      return 1;
>> +
>> +    case FFI_TYPE_STRUCT:;
>> +      {
>> +	int base_elt = 0, total_elnum = 0;
>> +	ffi_type **el = t->elements;
>> +	while (*el)
>> +	  {
>> +	    int el_elt = 0, el_elnum = 0;
>> +	    if (! rec_vfp_type_p (*el,&el_elt,&el_elnum)
>> +		|| (base_elt&&  base_elt != el_elt)
>> +		|| total_elnum + el_elnum>  4)
>> +	      return 0;
>> +	    base_elt = el_elt;
>> +	    total_elnum += el_elnum;
>> +	    el++;
>> +	  }
>> +	*elnum = total_elnum;
>> +	*elt = base_elt;
>> +	return 1;
>> +      }
>> +    default: ;
>> +    }
>> +  return 0;
>> +}
>> +
>> +static int vfp_type_p (ffi_type *t)
>> +{
>> +  int elt, elnum;
>> +  if (rec_vfp_type_p (t,&elt,&elnum))
>> +    {
>> +      if (t->type == FFI_TYPE_STRUCT)
>> +	{
>> +	  if (elnum == 1)
>> +	    t->type = elt;
>> +	  else
>> +	    t->type = (elt == FFI_TYPE_FLOAT
>> +		       ? FFI_TYPE_STRUCT_VFP_FLOAT
>> +		       : FFI_TYPE_STRUCT_VFP_DOUBLE);
>> +	}
>> +      return (int) t->type;
>> +    }
>> +  return 0;
>> +}
>> +
>> +static void place_vfp_arg (ffi_cif *cif, ffi_type *t)
>> +{
>> +  int reg = cif->vfp_reg_free;
>> +  int nregs = t->size / sizeof (float);
>> +  int align = ((t->type == FFI_TYPE_STRUCT_VFP_FLOAT
>> +		|| t->type == FFI_TYPE_FLOAT) ? 1 : 2);
>> +  /* Align register number. */
>> +  if ((reg&  1)&&  align == 2)
>> +    reg++;
>> +  while (reg + nregs<= 16)
>> +    {
>> +      int s, new_used = 0;
>> +      for (s = reg; s<  reg + nregs; s++)
>> +	{
>> +	  new_used |= (1<<  s);
>> +	  if (cif->vfp_used&  (1<<  s))
>> +	    {
>> +	      reg += align;
>> +	      goto next_reg;
>> +	    }
>> +	}
>> +      /* Found regs to allocate. */
>> +      cif->vfp_used |= new_used;
>> +      cif->vfp_args[cif->vfp_nargs++] = reg;
>> +
>> +      /* Update vfp_reg_free. */
>> +      if (cif->vfp_used&  (1<<  cif->vfp_reg_free))
>> +	{
>> +	  reg += nregs;
>> +	  while (cif->vfp_used&  (1<<  reg))
>> +	    reg += 1;
>> +	  cif->vfp_reg_free = reg;
>> +	}
>> +      return;
>> +    next_reg: ;
>> +    }
>> +}
>> +
>> +static void layout_vfp_args (ffi_cif *cif)
>> +{
>> +  int i;
>> +  /* Init VFP fields */
>> +  cif->vfp_used = 0;
>> +  cif->vfp_nargs = 0;
>> +  cif->vfp_reg_free = 0;
>> +  memset (cif->vfp_args, -1, 16); /* Init to -1. */
>> +
>> +  for (i = 0; i<  cif->nargs; i++)
>> +    {
>> +      ffi_type *t = cif->arg_types[i];
>> +      if (vfp_type_p (t))
>> +	place_vfp_arg (cif, t);
>> +    }
>> +}
>> diff --git a/src/arm/ffitarget.h b/src/arm/ffitarget.h
>> index eede79f..aceb10b 100644
>> --- a/src/arm/ffitarget.h
>> +++ b/src/arm/ffitarget.h
>> @@ -34,11 +34,25 @@ typedef signed long            ffi_sarg;
>>   typedef enum ffi_abi {
>>     FFI_FIRST_ABI = 0,
>>     FFI_SYSV,
>> +  FFI_VFP,
>>     FFI_LAST_ABI,
>> -  FFI_DEFAULT_ABI = FFI_SYSV
>> +#ifdef __ARM_PCS_VFP
>> +  FFI_DEFAULT_ABI = FFI_VFP,
>> +#else
>> +  FFI_DEFAULT_ABI = FFI_SYSV,
>> +#endif
>>   } ffi_abi;
>>   #endif
>>
>> +#define FFI_EXTRA_CIF_FIELDS			\
>> +  int vfp_used;					\
>> +  short vfp_reg_free, vfp_nargs;		\
>> +  signed char vfp_args[16]			\
>> +
>> +/* Internally used. */
>> +#define FFI_TYPE_STRUCT_VFP_FLOAT  (FFI_TYPE_LAST + 1)
>> +#define FFI_TYPE_STRUCT_VFP_DOUBLE (FFI_TYPE_LAST + 2)
>> +
>>   /* ---- Definitions for closures ----------------------------------------- */
>>
>>   #define FFI_CLOSURES 1
>> diff --git a/src/arm/sysv.S b/src/arm/sysv.S
>> index 9064318..7bce727 100644
>> --- a/src/arm/sysv.S
>> +++ b/src/arm/sysv.S
>> @@ -142,12 +142,11 @@ _L__\name:
>>   .endm
>>
>>
>> -	@ r0:   ffi_prep_args
>> +	@ r0:   fn
>>   	@ r1:&ecif
>>   	@ r2:   cif->bytes
>>   	@ r3:   fig->flags
>>   	@ sp+0: ecif.rvalue
>> -	@ sp+4: fn
>>
>>   	@ This assumes we are using gas.
>>   ARM_FUNC_START ffi_call_SYSV
>> @@ -162,24 +161,23 @@ ARM_FUNC_START ffi_call_SYSV
>>   	sub	sp, fp, r2
>>
>>   	@ Place all of the ffi_prep_args in position
>> -	mov	ip, r0
>>   	mov	r0, sp
>>   	@     r1 already set
>>
>>   	@ Call ffi_prep_args(stack,&ecif)
>> -	call_reg(ip)
>> +	bl	ffi_prep_args
>>
>>   	@ move first 4 parameters in registers
>>   	ldmia	sp, {r0-r3}
>>
>>   	@ and adjust stack
>> -	ldr	ip, [fp, #8]
>> -        cmp	ip, #16
>> -	movhs	ip, #16
>> -        add	sp, sp, ip
>> +	sub	lr, fp, sp	@ cif->bytes == fp - sp
>> +	ldr	ip, [fp]	@ load fn() in advance
>> +	cmp	lr, #16
>> +	movhs	lr, #16
>> +	add	sp, sp, lr
>>
>>   	@ call (fn) (...)
>> -	ldr	ip, [fp, #28]
>>   	call_reg(ip)
>>   	
>>   	@ Remove the space we pushed for the args
>> @@ -230,6 +228,101 @@ LSYM(Lepilogue):
>>   	UNWIND .fnend
>>           .size    CNAME(ffi_call_SYSV),.ffi_call_SYSV_end-CNAME(ffi_call_SYSV)
>>
>> +
>> +	@ r0:   fn
>> +	@ r1:&ecif
>> +	@ r2:   cif->bytes
>> +	@ r3:   fig->flags
>> +	@ sp+0: ecif.rvalue
>> +
>> +ARM_FUNC_START ffi_call_VFP
>> +	@ Save registers
>> +        stmfd	sp!, {r0-r3, fp, lr}
>> +	UNWIND .save	{r0-r3, fp, lr}
>> +	mov	fp, sp
>> +	UNWIND .setfp	fp, sp
>> +
>> +	@ Make room for all of the new args.
>> +	sub	sp, sp, r2
>> +
>> +	@ Make room for loading VFP args
>> +	sub	sp, sp, #64
>> +
>> +	@ Place all of the ffi_prep_args in position
>> +	mov	r0, sp
>> +	@     r1 already set
>> +	sub	r2, fp, #64   @ VFP scratch space
>> +
>> +	@ Call ffi_prep_args(stack,&ecif, vfp_space)
>> +	bl	ffi_prep_args
>> +
>> +	@ Load VFP register args if needed
>> +	cmp	r0, #0
>> +	beq	LSYM(Lbase_args)
>> +
>> +	@ Load only d0 if possible
>> +	cmp	r0, #3
>> +	sub	ip, fp, #64
>> +	flddle	d0, [ip]
>> +	fldmiadgt	ip, {d0-d7}
>> +
>> +LSYM(Lbase_args):
>> +	@ move first 4 parameters in registers
>> +	ldmia	sp, {r0-r3}
>> +
>> +	@ and adjust stack
>> +	sub	lr, ip, sp	@ cif->bytes == (fp - 64) - sp
>> +	ldr	ip, [fp]	@ load fn() in advance
>> +        cmp	lr, #16
>> +	movhs	lr, #16
>> +        add	sp, sp, lr
>> +
>> +	@ call (fn) (...)
>> +	call_reg(ip)
>> +
>> +	@ Remove the space we pushed for the args
>> +	mov	sp, fp
>> +
>> +	@ Load r2 with the pointer to storage for
>> +	@ the return value
>> +	ldr	r2, [sp, #24]
>> +
>> +	@ Load r3 with the return type code
>> +	ldr	r3, [sp, #12]
>> +
>> +	@ If the return value pointer is NULL,
>> +	@ assume no return value.
>> +	cmp	r2, #0
>> +	beq	LSYM(Lepilogue_vfp)
>> +	
>> +	cmp	r3, #FFI_TYPE_INT
>> +	streq	r0, [r2]
>> +	beq	LSYM(Lepilogue_vfp)
>> +
>> +	cmp	r3, #FFI_TYPE_SINT64
>> +	stmeqia	r2, {r0, r1}
>> +	beq	LSYM(Lepilogue_vfp)
>> +
>> +	cmp	r3, #FFI_TYPE_FLOAT
>> +	fstseq	s0, [r2]
>> +	beq	LSYM(Lepilogue_vfp)
>> +	
>> +	cmp	r3, #FFI_TYPE_DOUBLE
>> +	fstdeq	d0, [r2]
>> +	beq	LSYM(Lepilogue_vfp)
>> +
>> +	cmp	r3, #FFI_TYPE_STRUCT_VFP_FLOAT
>> +	cmpne	r3, #FFI_TYPE_STRUCT_VFP_DOUBLE
>> +	fstmiadeq	r2, {d0-d3}
>> +
>> +LSYM(Lepilogue_vfp):
>> +	RETLDM	"r0-r3,fp"
>> +
>> +.ffi_call_VFP_end:
>> +	UNWIND .fnend
>> +        .size    CNAME(ffi_call_VFP),.ffi_call_VFP_end-CNAME(ffi_call_VFP)
>> +	
>> +	
>>   /*
>>   	unsigned int FFI_HIDDEN
>>   	ffi_closure_SYSV_inner (closure, respp, args)
>> @@ -302,6 +395,68 @@ ARM_FUNC_START ffi_closure_SYSV
>>   	UNWIND .fnend
>>           .size    CNAME(ffi_closure_SYSV),.ffi_closure_SYSV_end-CNAME(ffi_closure_SYSV)
>>
>> +
>> +ARM_FUNC_START ffi_closure_VFP
>> +	fstmfdd	sp!, {d0-d7}
>> +	@ r0-r3, then d0-d7
>> +	UNWIND .pad #80
>> +	add	ip, sp, #80
>> +	stmfd	sp!, {ip, lr}
>> +	UNWIND .save	{r0, lr}
>> +	add	r2, sp, #72
>> +	add	r3, sp, #8
>> +	.pad #72
>> +	sub	sp, sp, #72
>> +	str	sp, [sp, #64]
>> +	add	r1, sp, #64
>> +	bl	ffi_closure_SYSV_inner
>> +
>> +	cmp	r0, #FFI_TYPE_INT
>> +	beq	.Lretint_vfp
>> +
>> +	cmp	r0, #FFI_TYPE_FLOAT
>> +	beq	.Lretfloat_vfp
>> +
>> +	cmp	r0, #FFI_TYPE_DOUBLE
>> +	cmpne	r0, #FFI_TYPE_LONGDOUBLE
>> +	beq	.Lretdouble_vfp
>> +
>> +	cmp	r0, #FFI_TYPE_SINT64
>> +	beq	.Lretlonglong_vfp
>> +
>> +	cmp	r0, #FFI_TYPE_STRUCT_VFP_FLOAT
>> +	beq	.Lretfloat_struct_vfp
>> +
>> +	cmp	r0, #FFI_TYPE_STRUCT_VFP_DOUBLE
>> +	beq	.Lretdouble_struct_vfp
>> +	
>> +.Lclosure_epilogue_vfp:
>> +	add	sp, sp, #72
>> +	ldmfd	sp, {sp, pc}
>> +
>> +.Lretfloat_vfp:
>> +	flds	s0, [sp]
>> +	b	.Lclosure_epilogue_vfp
>> +.Lretdouble_vfp:
>> +	fldd	d0, [sp]
>> +	b	.Lclosure_epilogue_vfp
>> +.Lretint_vfp:
>> +	ldr	r0, [sp]
>> +	b	.Lclosure_epilogue_vfp
>> +.Lretlonglong_vfp:
>> +	ldmia	sp, {r0, r1}
>> +	b	.Lclosure_epilogue_vfp
>> +.Lretfloat_struct_vfp:
>> +	fldmiad	sp, {d0-d1}
>> +	b	.Lclosure_epilogue_vfp
>> +.Lretdouble_struct_vfp:
>> +	fldmiad	sp, {d0-d3}
>> +	b	.Lclosure_epilogue_vfp
>> +
>> +.ffi_closure_VFP_end:
>> +	UNWIND .fnend
>> +        .size    CNAME(ffi_closure_VFP),.ffi_closure_VFP_end-CNAME(ffi_closure_VFP)
>> +
>>   #if defined __ELF__&&  defined __linux__
>>   	.section	.note.GNU-stack,"",%progbits
>>   #endif
>> diff --git a/testsuite/lib/libffi-dg.exp b/testsuite/lib/libffi-dg.exp
>> index 838a306..ca5f751 100644
>> --- a/testsuite/lib/libffi-dg.exp
>> +++ b/testsuite/lib/libffi-dg.exp
>> @@ -266,6 +266,56 @@ proc dg-xfail-if { args } {
>>       }
>>   }
>>
>> +proc check-flags { args } {
>> +
>> +    # The args are within another list; pull them out.
>> +    set args [lindex $args 0]
>> +
>> +    # The next two arguments are optional.  If they were not specified,
>> +    # use the defaults.
>> +    if { [llength $args] == 2 } {
>> +	lappend $args [list "*"]
>> +    }
>> +    if { [llength $args] == 3 } {
>> +	lappend $args [list ""]
>> +    }
>> +
>> +    # If the option strings are the defaults, or the same as the
>> +    # defaults, there is no need to call check_conditional_xfail to
>> +    # compare them to the actual options.
>> +    if { [string compare [lindex $args 2] "*"] == 0
>> +	&&  [string compare [lindex $args 3] "" ] == 0 } {
>> +	set result 1
>> +    } else {
>> +	# The target list might be an effective-target keyword, so replace
>> +	# the original list with "*-*-*", since we already know it matches.
>> +	set result [check_conditional_xfail [lreplace $args 1 1 "*-*-*"]]
>> +    }
>> +
>> +    return $result
>> +}
>> +
>> +proc dg-skip-if { args } {
>> +    # Verify the number of arguments.  The last two are optional.
>> +    set args [lreplace $args 0 0]
>> +    if { [llength $args]<  2 || [llength $args]>  4 } {
>> +        error "dg-skip-if 2: need 2, 3, or 4 arguments"
>> +    }
>> +
>> +    # Don't bother if we're already skipping the test.
>> +    upvar dg-do-what dg-do-what
>> +    if { [lindex ${dg-do-what} 1] == "N" } {
>> +      return
>> +    }
>> +
>> +    set selector [list target [lindex $args 1]]
>> +    if { [dg-process-target $selector] == "S" } {
>> +        if [check-flags $args] {
>> +            upvar dg-do-what dg-do-what
>> +            set dg-do-what [list [lindex ${dg-do-what} 0] "N" "P"]
>> +        }
>> +    }
>> +}
>>
>>   # We need to make sure that additional_files and additional_sources
>>   # are both cleared out after every test.  It is not enough to clear
>> diff --git a/testsuite/libffi.call/cls_double_va.c b/testsuite/libffi.call/cls_double_va.c
>> index 0695874..62bebbd 100644
>> --- a/testsuite/libffi.call/cls_double_va.c
>> +++ b/testsuite/libffi.call/cls_double_va.c
>> @@ -6,6 +6,8 @@
>>
>>   /* { dg-do run { xfail strongarm*-*-* xscale*-*-* } } */
>>   /* { dg-output "" { xfail avr32*-*-* } } */
>> +/* { dg-skip-if "" arm*-*-* { "-mfloat-abi=hard" } { "" } } */
>> +
>>   #include "ffitest.h"
>>
>>   static void
>> diff --git a/testsuite/libffi.call/cls_longdouble_va.c b/testsuite/libffi.call/cls_longdouble_va.c
>> index 38564cb..b33b2b7 100644
>> --- a/testsuite/libffi.call/cls_longdouble_va.c
>> +++ b/testsuite/libffi.call/cls_longdouble_va.c
>> @@ -6,6 +6,8 @@
>>
>>   /* { dg-do run { xfail strongarm*-*-* xscale*-*-* } } */
>>   /* { dg-output "" { xfail avr32*-*-* x86_64-*-mingw* } } */
>> +/* { dg-skip-if "" arm*-*-* { "-mfloat-abi=hard" } { "" } } */
>> +
>>   #include "ffitest.h"
>>
>>   static void



More information about the Libffi-discuss mailing list