This is the mail archive of the libffi-discuss@sourceware.org mailing list for the libffi project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]