Commit 3c1608613ab3c2184222b98c5482cddedd6b559b

David Schneider 2013-03-26T19:24:47

extend ffi_prepare_args for FFI_VFP (hard-float ABI), fixing an issue with passing VFP arguments in VFP registers and the stack, while at the same time not using all core registers.

diff --git a/src/arm/ffi.c b/src/arm/ffi.c
index d3dba9c..cce24d8 100644
--- a/src/arm/ffi.c
+++ b/src/arm/ffi.c
@@ -153,6 +153,76 @@ int ffi_prep_args_VFP(char *stack, extended_cif *ecif, float *vfp_space)
 {
   // make sure we are using FFI_VFP
   FFI_ASSERT(ecif->cif->abi == FFI_VFP);
+
+  register unsigned int i, vi = 0;
+  register void **p_argv;
+  register char *argp, *regp, *eo_regp;
+  register ffi_type **p_arg;
+  char stack_used = 0;
+  char done_with_regs = 0;
+  char is_vfp_type;
+
+  /* the first 4 words on the stack are used for values passed in core
+   * registers. */
+  regp = stack;
+  eo_regp = argp = regp + 16;
+  
+
+  /* if the function returns an FFI_TYPE_STRUCT in memory, that address is
+   * passed in r0 to the function */
+  if ( ecif->cif->flags == FFI_TYPE_STRUCT ) {
+    *(void **) regp = ecif->rvalue;
+    regp += 4;
+  }
+
+  p_argv = ecif->avalue;
+
+  for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types;
+       (i != 0);
+       i--, p_arg++, p_argv++)
+    {
+      is_vfp_type = vfp_type_p (*p_arg);
+
+      /* Allocated in VFP registers. */
+      if(vi < ecif->cif->vfp_nargs && is_vfp_type)
+        {
+          char *vfp_slot = (char *)(vfp_space + ecif->cif->vfp_args[vi++]);
+          ffi_put_arg(p_arg, p_argv, vfp_slot);
+          continue;
+        }
+      /* Try allocating in core registers. */
+      else if (!done_with_regs && !is_vfp_type)
+        {
+          char *tregp = ffi_align(p_arg, regp);
+          size_t size = (*p_arg)->size; 
+          size = (size < 4)? 4 : size; // pad
+          /* Check if there is space left in the aligned register area to place
+           * the argument */
+          if(tregp + size <= eo_regp)
+            {
+              regp = tregp + ffi_put_arg(p_arg, p_argv, tregp);
+              done_with_regs = (regp == argp);
+              // ensure we did not write into the stack area
+              FFI_ASSERT(regp <= argp);
+              continue;
+            }
+          /* In case there are no arguments in the stack area yet, 
+          the argument is passed in the remaining core registers and on the
+          stack. */
+          else if (!stack_used) 
+            {
+              stack_used = 1;
+              done_with_regs = 1;
+              argp = tregp + ffi_put_arg(p_arg, p_argv, tregp);
+              FFI_ASSERT(eo_regp < argp);
+              continue;
+            }
+        }
+      /* Base case, arguments are passed on the stack */
+      stack_used = 1;
+      argp = ffi_align(p_arg, argp);
+      argp += ffi_put_arg(p_arg, p_argv, argp);
+    }
   /* Indicate the VFP registers used. */
   return ecif->cif->vfp_used;
 }