s390: Inline and tidy ffi_prep_args
authorRichard Henderson <rth@redhat.com>
Fri, 19 Dec 2014 16:38:17 +0000 (11:38 -0500)
committerRichard Henderson <rth@redhat.com>
Fri, 19 Dec 2014 16:38:17 +0000 (11:38 -0500)
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.

src/s390/ffi.c
src/s390/sysv.S

index 1189f7b..4035b6e 100644 (file)
@@ -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);
 }
index 1869269..c4b5006 100644 (file)
@@ -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