Commit 7ba30b19060dfefefeb91970a862e5b215606602

Richard Henderson 2014-12-19T11:38:17

s390: Inline and tidy ffi_prep_args As per discussion with Ulrich Weigand, document the restrictions on the code within ffi_call_int as we simultaneously prepare stack frames for ffi_call_SYSV and the target function.

diff --git a/src/s390/ffi.c b/src/s390/ffi.c
index 1189f7b..4035b6e 100644
--- a/src/s390/ffi.c
+++ b/src/s390/ffi.c
@@ -30,6 +30,7 @@
 
 #include <ffi.h>
 #include <ffi_common.h>
+#include <stdint.h>
 #include "internal.h"
 
 /*====================== End of Includes =============================*/
@@ -130,165 +131,6 @@ ffi_check_struct_type (ffi_type *arg)
 
 /*====================================================================*/
 /*                                                                    */
-/* Name     - ffi_prep_args.                                          */
-/*                                                                    */
-/* Function - Prepare parameters for call to function.                */
-/*                                                                    */
-/* ffi_prep_args is called by the assembly routine once stack space   */
-/* has been allocated for the function's arguments.                   */
-/*                                                                    */
-/*====================================================================*/
-
-static void
-ffi_prep_args (ffi_cif *cif, void *rvalue, void **p_argv,
-	       unsigned long *p_ov, struct call_frame *p_frame)
-{
-  unsigned char *p_struct = (unsigned char *)p_frame;
-  unsigned long *p_gpr = p_frame->gpr_args;
-  unsigned long long *p_fpr = p_frame->fpr_args;
-  int n_fpr = 0;
-  int n_gpr = 0;
-  int n_ov = 0;
-  ffi_type **ptr;
-  int i;
-
-  /* If we returning a structure then we set the first parameter register
-     to the address of where we are returning this structure.  */
-  if (cif->flags & FFI390_RET_IN_MEM)
-    p_gpr[n_gpr++] = (unsigned long) rvalue;
-
-  /* Now for the arguments.  */
-  for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++, p_argv++)
-    {
-      ffi_type *ty = *ptr;
-      void *arg = *p_argv;
-      int type = ty->type;
-
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-      /* 16-byte long double is passed like a struct.  */
-      if (type == FFI_TYPE_LONGDOUBLE)
-	type = FFI_TYPE_STRUCT;
-#endif
-
-      /* Check how a structure type is passed.  */
-      if (type == FFI_TYPE_STRUCT || type == FFI_TYPE_COMPLEX)
-	{
-	  if (type == FFI_TYPE_COMPLEX)
-	    type = FFI_TYPE_POINTER;
-	  else
-	    type = ffi_check_struct_type (ty);
-
-	  /* If we pass the struct via pointer, copy the data.  */
-	  if (type == FFI_TYPE_POINTER)
-	    {
-	      p_struct -= ROUND_SIZE (ty->size);
-	      memcpy (p_struct, (char *)arg, ty->size);
-	      arg = &p_struct;
-	    }
-	}
-
-      /* Now handle all primitive int/pointer/float data types.  */
-      switch (type)
-	{
-	  case FFI_TYPE_DOUBLE:
-	    if (n_fpr < MAX_FPRARGS)
-	      p_fpr[n_fpr++] = *(unsigned long long *) arg;
-	    else
-#ifdef __s390x__
-	      p_ov[n_ov++] = *(unsigned long *) arg;
-#else
-	      p_ov[n_ov++] = ((unsigned long *) arg)[0],
-	      p_ov[n_ov++] = ((unsigned long *) arg)[1];
-#endif
-	    break;
-
-	  case FFI_TYPE_FLOAT:
-	    if (n_fpr < MAX_FPRARGS)
-	      p_fpr[n_fpr++] = (unsigned long long)*(unsigned int *) arg << 32;
-	    else
-	      p_ov[n_ov++] = *(unsigned int *) arg;
-	    break;
-
-	  case FFI_TYPE_POINTER:
-	    if (n_gpr < MAX_GPRARGS)
-	      p_gpr[n_gpr++] = (unsigned long)*(unsigned char **) arg;
-	    else
-	      p_ov[n_ov++] = (unsigned long)*(unsigned char **) arg;
-	    break;
-
-	  case FFI_TYPE_UINT64:
-	  case FFI_TYPE_SINT64:
-#ifdef __s390x__
-	    if (n_gpr < MAX_GPRARGS)
-	      p_gpr[n_gpr++] = *(unsigned long *) arg;
-	    else
-	      p_ov[n_ov++] = *(unsigned long *) arg;
-#else
-	    if (n_gpr == MAX_GPRARGS-1)
-	      n_gpr = MAX_GPRARGS;
-	    if (n_gpr < MAX_GPRARGS)
-	      p_gpr[n_gpr++] = ((unsigned long *) arg)[0],
-	      p_gpr[n_gpr++] = ((unsigned long *) arg)[1];
-	    else
-	      p_ov[n_ov++] = ((unsigned long *) arg)[0],
-	      p_ov[n_ov++] = ((unsigned long *) arg)[1];
-#endif
-	    break;
-
-	  case FFI_TYPE_UINT32:
-	    if (n_gpr < MAX_GPRARGS)
-	      p_gpr[n_gpr++] = *(unsigned int *) arg;
-	    else
-	      p_ov[n_ov++] = *(unsigned int *) arg;
-	    break;
-
-	  case FFI_TYPE_INT:
-	  case FFI_TYPE_SINT32:
-	    if (n_gpr < MAX_GPRARGS)
-	      p_gpr[n_gpr++] = *(signed int *) arg;
-	    else
-	      p_ov[n_ov++] = *(signed int *) arg;
-	    break;
-
-	  case FFI_TYPE_UINT16:
-	    if (n_gpr < MAX_GPRARGS)
-	      p_gpr[n_gpr++] = *(unsigned short *) arg;
-	    else
-	      p_ov[n_ov++] = *(unsigned short *) arg;
-	    break;
-
-	  case FFI_TYPE_SINT16:
-	    if (n_gpr < MAX_GPRARGS)
-	      p_gpr[n_gpr++] = *(signed short *) arg;
-	    else
-	      p_ov[n_ov++] = *(signed short *) arg;
-	    break;
-
-	  case FFI_TYPE_UINT8:
-	    if (n_gpr < MAX_GPRARGS)
-	      p_gpr[n_gpr++] = *(unsigned char *) arg;
-	    else
-	      p_ov[n_ov++] = *(unsigned char *) arg;
-	    break;
-
-	  case FFI_TYPE_SINT8:
-	    if (n_gpr < MAX_GPRARGS)
-	      p_gpr[n_gpr++] = *(signed char *) arg;
-	    else
-	      p_ov[n_ov++] = *(signed char *) arg;
-	    break;
-
-	  default:
-	    FFI_ASSERT (0);
-	    break;
-        }
-    }
-}
-
-/*======================== End of Routine ============================*/
-
-/*====================================================================*/
-/*                                                                    */
 /* Name     - ffi_prep_cif_machdep.                                   */
 /*                                                                    */
 /* Function - Perform machine dependent CIF processing.               */
@@ -469,8 +311,12 @@ ffi_call_int(ffi_cif *cif,
 {
   int ret_type = cif->flags;
   size_t rsize = 0, bytes = cif->bytes;
-  unsigned char *stack;
+  unsigned char *stack, *p_struct;
   struct call_frame *frame;
+  unsigned long *p_ov, *p_gpr;
+  unsigned long long *p_fpr;
+  int n_fpr, n_gpr, n_ov, i, n;
+  ffi_type **arg_types;
 
   FFI_ASSERT (cif->abi == FFI_SYSV);
 
@@ -499,22 +345,134 @@ ffi_call_int(ffi_cif *cif,
 	p_ov: bottom of the overflow area (growing upwards)
 	p_struct: top of the struct copy area (growing downwards)
 
-     All areas are kept aligned to twice the word size.  */
+     All areas are kept aligned to twice the word size.
+
+     Note that we're going to create the stack frame for both
+     ffi_call_SYSV _and_ the target function right here.  This
+     works because we don't make any function calls with more
+     than 5 arguments (indeed only memcpy and ffi_call_SYSV),
+     and thus we don't have any stacked outgoing parameters.  */
 
   stack = alloca (bytes + sizeof(struct call_frame) + rsize);
   frame = (struct call_frame *)(stack + bytes);
   if (rsize)
     rvalue = frame + 1;
 
-  /* Assuming that the current function has the standard call frame,
-     we can maintain the linked list like so.  */
-  frame->back_chain = __builtin_dwarf_cfa() - sizeof(struct call_frame);
-
-  /* Pass the outgoing stack frame in the r15 save slot.  */
-  frame->gpr_save[8] = (unsigned long)(stack - sizeof(struct call_frame));
+  /* Link the new frame back to the one from this function.  */
+  frame->back_chain = __builtin_frame_address (0);
 
   /* Fill in all of the argument stuff.  */
-  ffi_prep_args (cif, rvalue, avalue, (unsigned long *)stack, frame);
+  p_ov = (unsigned long *)stack;
+  p_struct = (unsigned char *)frame;
+  p_gpr = frame->gpr_args;
+  p_fpr = frame->fpr_args;
+  n_fpr = n_gpr = n_ov = 0;
+
+  /* If we returning a structure then we set the first parameter register
+     to the address of where we are returning this structure.  */
+  if (cif->flags & FFI390_RET_IN_MEM)
+    p_gpr[n_gpr++] = (uintptr_t) rvalue;
+
+  /* Now for the arguments.  */
+  arg_types = cif->arg_types;
+  for (i = 0, n = cif->nargs; i < n; ++i)
+    {
+      ffi_type *ty = arg_types[i];
+      void *arg = avalue[i];
+      int type = ty->type;
+      ffi_arg val;
+
+    restart:
+      switch (type)
+	{
+	case FFI_TYPE_SINT8:
+	  val = *(SINT8 *)arg;
+	  goto do_int;
+	case FFI_TYPE_UINT8:
+	  val = *(UINT8 *)arg;
+	  goto do_int;
+	case FFI_TYPE_SINT16:
+	  val = *(SINT16 *)arg;
+	  goto do_int;
+	case FFI_TYPE_UINT16:
+	  val = *(UINT16 *)arg;
+	  goto do_int;
+	case FFI_TYPE_INT:
+	case FFI_TYPE_SINT32:
+	  val = *(SINT32 *)arg;
+	  goto do_int;
+	case FFI_TYPE_UINT32:
+	  val = *(UINT32 *)arg;
+	  goto do_int;
+	case FFI_TYPE_POINTER:
+	  val = *(uintptr_t *)arg;
+	do_int:
+	  *(n_gpr < MAX_GPRARGS ? p_gpr + n_gpr++ : p_ov + n_ov++) = val;
+	  break;
+
+	case FFI_TYPE_UINT64:
+	case FFI_TYPE_SINT64:
+#ifdef __s390x__
+	  val = *(UINT64 *)arg;
+	  goto do_int;
+#else
+	  if (n_gpr == MAX_GPRARGS-1)
+	    n_gpr = MAX_GPRARGS;
+	  if (n_gpr < MAX_GPRARGS)
+	    p_gpr[n_gpr++] = ((UINT32 *) arg)[0],
+	    p_gpr[n_gpr++] = ((UINT32 *) arg)[1];
+	  else
+	    p_ov[n_ov++] = ((UINT32 *) arg)[0],
+	    p_ov[n_ov++] = ((UINT32 *) arg)[1];
+#endif
+	  break;
+
+	case FFI_TYPE_DOUBLE:
+	  if (n_fpr < MAX_FPRARGS)
+	    p_fpr[n_fpr++] = *(UINT64 *) arg;
+	  else
+	    {
+#ifdef __s390x__
+	      p_ov[n_ov++] = *(UINT64 *) arg;
+#else
+	      p_ov[n_ov++] = ((UINT32 *) arg)[0],
+	      p_ov[n_ov++] = ((UINT32 *) arg)[1];
+#endif
+	    }
+	  break;
+
+	case FFI_TYPE_FLOAT:
+	  val = *(UINT32 *)arg;
+	  if (n_fpr < MAX_FPRARGS)
+	    p_fpr[n_fpr++] = (UINT64)val << 32;
+	  else
+	    p_ov[n_ov++] = val;
+	  break;
+
+	case FFI_TYPE_STRUCT:
+          /* Check how a structure type is passed.  */
+	  type = ffi_check_struct_type (ty);
+	  /* Some structures are passed via a type they contain.  */
+	  if (type != FFI_TYPE_POINTER)
+	    goto restart;
+	  /* ... otherwise, passed by reference.  fallthru.  */
+
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+	case FFI_TYPE_LONGDOUBLE:
+	  /* 16-byte long double is passed via reference.  */
+#endif
+	case FFI_TYPE_COMPLEX:
+	  /* Complex types are passed via reference.  */
+	  p_struct -= ROUND_SIZE (ty->size);
+	  memcpy (p_struct, arg, ty->size);
+	  val = (uintptr_t)p_struct;
+	  goto do_int;
+
+	default:
+	  FFI_ASSERT (0);
+	  break;
+        }
+    }
 
   ffi_call_SYSV (frame, ret_type & FFI360_RET_MASK, rvalue, fn, closure);
 }
diff --git a/src/s390/sysv.S b/src/s390/sysv.S
index 1869269..c4b5006 100644
--- a/src/s390/sysv.S
+++ b/src/s390/sysv.S
@@ -54,26 +54,24 @@ ffi_call_SYSV:
 	.cfi_rel_offset r13, 52
 	.cfi_rel_offset r14, 56
 	.cfi_def_cfa_register r13
-	l	%r15,60(%r2)			# Set up outgoing stack
-#ifdef HAVE_AS_S390_ZARCH
-	larl	%r14,.Ltable
-#else
-	basr	%r14,0				# Set up base register
-.Lbase:
-#endif
+	st	%r2,0(%r15)			# Set up back chain
 	sla	%r3,3				# ret_type *= 8
 	lr	%r12,%r4			# Save ret_addr
 	lr	%r1,%r5				# Save fun
 	lr	%r0,%r6				# Install static chain
+
+	# Set return address, so that there is only one indirect jump.
 #ifdef HAVE_AS_S390_ZARCH
-	la	%r14,0(%r14,%r3)		# Set return address
+	larl	%r14,.Ltable
+	ar	%r14,%r3
 #else
-	la	%r14,.Ltable-.Lbase(%r14,%r3)	# Set return address
+	basr	%r14,0
+0:	la	%r14,.Ltable-0b(%r14,%r3)
 #endif
+
 	lm	%r2,%r6,8(%r13)			# Load arguments
 	ld	%f0,64(%r13)
 	ld	%f2,72(%r13)
-	st	%r13,0(%r15)			# Set up back chain
 	br	%r1				# ... and call function
 
 	.balign	8
@@ -210,7 +208,7 @@ ffi_call_SYSV:
 	.cfi_rel_offset r13, 104
 	.cfi_rel_offset r14, 112
 	.cfi_def_cfa_register r13
-	lg	%r15,120(%r2)			# Set up outgoing stack
+	stg	%r2,0(%r15)			# Set up back chain
 	larl	%r14,.Ltable			# Set up return address
 	slag	%r3,%r3,3			# ret_type *= 8
 	lgr	%r12,%r4			# Save ret_addr
@@ -222,7 +220,6 @@ ffi_call_SYSV:
 	ld	%f2,136(%r13)
 	ld	%f4,144(%r13)
 	ld	%f6,152(%r13)
-	stg	%r13,0(%r15)			# Set up back chain
 	br	%r1				# ... and call function
 
 	.balign	8