alpha: Add support for complex types
authorRichard Henderson <rth@twiddle.net>
Sat, 18 Oct 2014 03:46:48 +0000 (20:46 -0700)
committerRichard Henderson <rth@twiddle.net>
Wed, 12 Nov 2014 08:31:19 +0000 (09:31 +0100)
src/alpha/ffi.c
src/alpha/ffitarget.h
src/alpha/internal.h
src/alpha/osf.S
testsuite/libffi.call/call.exp
testsuite/libffi.call/cls_complex_va_float.c

index 2f9e7e5..1e5187e 100644 (file)
@@ -1,8 +1,8 @@
 /* -----------------------------------------------------------------------
    ffi.c - Copyright (c) 2012  Anthony Green
-           Copyright (c) 1998, 2001, 2007, 2008  Red Hat, Inc.
-   
-   Alpha Foreign Function Interface 
+          Copyright (c) 1998, 2001, 2007, 2008  Red Hat, Inc.
+
+   Alpha Foreign Function Interface
 
    Permission is hereby granted, free of charge, to any person obtaining
    a copy of this software and associated documentation files (the
@@ -60,18 +60,61 @@ static inline void sts(void *ptr, UINT64 val)
   asm("sts %1,%0" : "=m"(*(UINT32 *)ptr) : "f"(val));
 }
 
-ffi_status
+ffi_status FFI_HIDDEN
 ffi_prep_cif_machdep(ffi_cif *cif)
 {
-  int flags;
+  size_t bytes = 0;
+  int flags, i, avn;
+  ffi_type *rtype, *itype;
+
+  if (cif->abi != FFI_OSF)
+    return FFI_BAD_ABI;
+
+  /* Compute the size of the argument area.  */
+  for (i = 0, avn = cif->nargs; i < avn; i++)
+    {
+      itype = cif->arg_types[i];
+      switch (itype->type)
+       {
+       case FFI_TYPE_INT:
+       case FFI_TYPE_SINT8:
+       case FFI_TYPE_UINT8:
+       case FFI_TYPE_SINT16:
+       case FFI_TYPE_UINT16:
+       case FFI_TYPE_SINT32:
+       case FFI_TYPE_UINT32:
+       case FFI_TYPE_SINT64:
+       case FFI_TYPE_UINT64:
+       case FFI_TYPE_POINTER:
+       case FFI_TYPE_FLOAT:
+       case FFI_TYPE_DOUBLE:
+       case FFI_TYPE_LONGDOUBLE:
+         /* All take one 8 byte slot.  */
+         bytes += 8;
+         break;
+
+       case FFI_TYPE_VOID:
+       case FFI_TYPE_STRUCT:
+         /* Passed by value in N slots.  */
+         bytes += ALIGN(itype->size, FFI_SIZEOF_ARG);
+         break;
+
+       case FFI_TYPE_COMPLEX:
+         /* _Complex long double passed by reference; others in 2 slots.  */
+         if (itype->elements[0]->type == FFI_TYPE_LONGDOUBLE)
+           bytes += 8;
+         else
+           bytes += 16;
+         break;
 
-  /* Adjust cif->bytes to represent a minimum 6 words for the temporary
-     register argument loading area.  */
-  if (cif->bytes < 6*FFI_SIZEOF_ARG)
-    cif->bytes = 6*FFI_SIZEOF_ARG;
+       default:
+         abort();
+       }
+    }
 
   /* Set the return type flag */
-  switch (cif->rtype->type)
+  rtype = cif->rtype;
+  switch (rtype->type)
     {
     case FFI_TYPE_VOID:
       flags = ALPHA_FLAGS(ALPHA_ST_VOID, ALPHA_LD_VOID);
@@ -109,22 +152,83 @@ ffi_prep_cif_machdep(ffi_cif *cif)
       /* Passed in memory, with a hidden pointer.  */
       flags = ALPHA_RET_IN_MEM;
       break;
+    case FFI_TYPE_COMPLEX:
+      itype = rtype->elements[0];
+      switch (itype->type)
+       {
+       case FFI_TYPE_FLOAT:
+         flags = ALPHA_FLAGS(ALPHA_ST_CPLXF, ALPHA_LD_CPLXF);
+         break;
+       case FFI_TYPE_DOUBLE:
+         flags = ALPHA_FLAGS(ALPHA_ST_CPLXD, ALPHA_LD_CPLXD);
+         break;
+       default:
+         if (rtype->size <= 8)
+           flags = ALPHA_FLAGS(ALPHA_ST_INT, ALPHA_LD_INT64);
+         else
+           flags = ALPHA_RET_IN_MEM;
+         break;
+       }
+      break;
     default:
       abort();
     }
   cif->flags = flags;
-  
+
+  /* Include the hidden structure pointer in args requirement.  */
+  if (flags == ALPHA_RET_IN_MEM)
+    bytes += 8;
+  /* Minimum size is 6 slots, so that ffi_call_osf can pop them.  */
+  if (bytes < 6*8)
+    bytes = 6*8;
+  cif->bytes = bytes;
+
   return FFI_OK;
 }
 
+static unsigned long
+extend_basic_type(void *valp, int type, int argn)
+{
+  switch (type)
+    {
+    case FFI_TYPE_SINT8:
+      return *(SINT8 *)valp;
+    case FFI_TYPE_UINT8:
+      return *(UINT8 *)valp;
+    case FFI_TYPE_SINT16:
+      return *(SINT16 *)valp;
+    case FFI_TYPE_UINT16:
+      return *(UINT16 *)valp;
+
+    case FFI_TYPE_FLOAT:
+      if (argn < 6)
+       return lds(valp);
+      /* FALLTHRU */
+
+    case FFI_TYPE_INT:
+    case FFI_TYPE_SINT32:
+    case FFI_TYPE_UINT32:
+      /* Note that unsigned 32-bit quantities are sign extended.  */
+      return *(SINT32 *)valp;
+
+    case FFI_TYPE_SINT64:
+    case FFI_TYPE_UINT64:
+    case FFI_TYPE_POINTER:
+    case FFI_TYPE_DOUBLE:
+      return *(UINT64 *)valp;
+
+    default:
+      abort();
+    }
+}
 
 void
 ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
 {
-  unsigned long *stack, *argp;
-  long i, avn, flags = cif->flags;
+  unsigned long *argp;
+  long i, avn, argn, flags = cif->flags;
   ffi_type **arg_types;
-  
+
   /* If the return value is a struct and we don't have a return
      value address then we need to make one.  */
   if (rvalue == NULL && flags == ALPHA_RET_IN_MEM)
@@ -132,12 +236,12 @@ ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
 
   /* Allocate the space for the arguments, plus 4 words of temp
      space for ffi_call_osf.  */
-  argp = stack = alloca(cif->bytes + 4*FFI_SIZEOF_ARG);
+  argp = alloca(cif->bytes + 4*FFI_SIZEOF_ARG);
 
+  argn = 0;
   if (flags == ALPHA_RET_IN_MEM)
-    *argp++ = (unsigned long)rvalue;
+    argp[argn++] = (unsigned long)rvalue;
 
-  i = 0;
   avn = cif->nargs;
   arg_types = cif->arg_types;
 
@@ -145,67 +249,59 @@ ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
     {
       ffi_type *ty = arg_types[i];
       void *valp = avalue[i];
-      unsigned long val;
+      int type = ty->type;
       size_t size;
 
-      switch (ty->type)
+      switch (type)
        {
+       case FFI_TYPE_INT:
        case FFI_TYPE_SINT8:
-         val = *(SINT8 *)valp;
-         break;
-                 
        case FFI_TYPE_UINT8:
-         val = *(UINT8 *)valp;
-         break;
-                 
        case FFI_TYPE_SINT16:
-         val = *(SINT16 *)valp;
-         break;
-                 
        case FFI_TYPE_UINT16:
-         val = *(UINT16 *)valp;
-         break;
-                 
        case FFI_TYPE_SINT32:
        case FFI_TYPE_UINT32:
-         /* Note that unsigned 32-bit quantities are sign extended.  */
-         val = *(SINT32 *)valp;
-         break;
-
        case FFI_TYPE_SINT64:
        case FFI_TYPE_UINT64:
        case FFI_TYPE_POINTER:
+       case FFI_TYPE_FLOAT:
        case FFI_TYPE_DOUBLE:
-         val = *(UINT64 *)valp;
+         argp[argn] = extend_basic_type(valp, type, argn);
+         argn++;
          break;
 
        case FFI_TYPE_LONGDOUBLE:
+       by_reference:
          /* Note that 128-bit long double is passed by reference.  */
-         val = (unsigned long)valp;
-         break;
-
-       case FFI_TYPE_FLOAT:
-         if (argp - stack < 6)
-           val = lds(valp);
-         else
-           val = *(UINT32 *)valp;
+         argp[argn++] = (unsigned long)valp;
          break;
 
+       case FFI_TYPE_VOID:
        case FFI_TYPE_STRUCT:
          size = ty->size;
-         memcpy(argp, valp, size);
-         argp += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
-         continue;
+         memcpy(argp + argn, valp, size);
+         argn += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
+         break;
+
+       case FFI_TYPE_COMPLEX:
+         type = ty->elements[0]->type;
+         if (type == FFI_TYPE_LONGDOUBLE)
+           goto by_reference;
+
+         /* Most complex types passed as two separate arguments.  */
+         size = ty->elements[0]->size;
+         argp[argn] = extend_basic_type(valp, type, argn);
+         argp[argn + 1] = extend_basic_type(valp + size, type, argn + 1);
+         argn += 2;
+         break;
 
        default:
          abort();
        }
-
-      *argp++ = val;
     }
 
   flags = (flags >> ALPHA_ST_SHIFT) & 0xff;
-  ffi_call_osf(stack, cif->bytes, flags, rvalue, fn);
+  ffi_call_osf(argp, cif->bytes, flags, rvalue, fn);
 }
 
 
@@ -243,7 +339,6 @@ ffi_prep_closure_loc (ffi_closure* closure,
   return FFI_OK;
 }
 
-
 long FFI_HIDDEN
 ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
 {
@@ -266,15 +361,18 @@ ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
     }
 
   arg_types = cif->arg_types;
-  
+
   /* Grab the addresses of the arguments from the stack frame.  */
   for (i = 0, avn = cif->nargs; i < avn; i++)
     {
-      size_t size = arg_types[i]->size;
+      ffi_type *ty = arg_types[i];
+      int type = ty->type;
       void *valp = &argp[argn];
+      size_t size;
 
-      switch (arg_types[i]->type)
+      switch (type)
        {
+       case FFI_TYPE_INT:
        case FFI_TYPE_SINT8:
        case FFI_TYPE_UINT8:
        case FFI_TYPE_SINT16:
@@ -284,7 +382,13 @@ ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
        case FFI_TYPE_SINT64:
        case FFI_TYPE_UINT64:
        case FFI_TYPE_POINTER:
+         argn += 1;
+         break;
+
+       case FFI_TYPE_VOID:
        case FFI_TYPE_STRUCT:
+         size = ty->size;
+         argn += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
          break;
 
        case FFI_TYPE_FLOAT:
@@ -295,17 +399,78 @@ ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
              valp = &argp[argn - 6];
              sts(valp, argp[argn - 6]);
            }
+         argn += 1;
          break;
 
        case FFI_TYPE_DOUBLE:
          if (argn < 6)
            valp = &argp[argn - 6];
+         argn += 1;
          break;
 
        case FFI_TYPE_LONGDOUBLE:
+       by_reference:
          /* 128-bit long double is passed by reference.  */
-         valp = (long double *) argp[argn];
-         size = sizeof (long double *);
+         valp = (void *)argp[argn];
+         argn += 1;
+         break;
+
+       case FFI_TYPE_COMPLEX:
+         type = ty->elements[0]->type;
+         switch (type)
+           {
+           case FFI_TYPE_SINT64:
+           case FFI_TYPE_UINT64:
+             /* Passed as separate arguments, but they wind up sequential.  */
+             break;
+
+           case FFI_TYPE_INT:
+           case FFI_TYPE_SINT8:
+           case FFI_TYPE_UINT8:
+           case FFI_TYPE_SINT16:
+           case FFI_TYPE_UINT16:
+           case FFI_TYPE_SINT32:
+           case FFI_TYPE_UINT32:
+             /* Passed as separate arguments.  Disjoint, but there's room
+                enough in one slot to hold the pair.  */
+             size = ty->elements[0]->size;
+             memcpy(valp + size, valp + 8, size);
+             break;
+
+           case FFI_TYPE_FLOAT:
+             /* Passed as separate arguments.  Disjoint, and each piece
+                may need conversion back to float.  */
+             if (argn < 6)
+               {
+                 valp = &argp[argn - 6];
+                 sts(valp, argp[argn - 6]);
+               }
+             if (argn + 1 < 6)
+               sts(valp + 4, argp[argn + 1 - 6]);
+             else
+               *(UINT32 *)(valp + 4) = argp[argn + 1];
+             break;
+
+           case FFI_TYPE_DOUBLE:
+             /* Passed as separate arguments.  Only disjoint if one part
+                is in fp regs and the other is on the stack.  */
+             if (argn < 5)
+               valp = &argp[argn - 6];
+             else if (argn == 5)
+               {
+                 valp = alloca(16);
+                 ((UINT64 *)valp)[0] = argp[5 - 6];
+                 ((UINT64 *)valp)[1] = argp[6];
+               }
+             break;
+
+           case FFI_TYPE_LONGDOUBLE:
+             goto by_reference;
+
+           default:
+             abort();
+           }
+         argn += 2;
          break;
 
        default:
@@ -313,7 +478,6 @@ ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
        }
 
       avalue[i] = valp;
-      argn += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
     }
 
   /* Invoke the closure.  */
index af145bc..60f92fd 100644 (file)
@@ -44,6 +44,9 @@ typedef enum ffi_abi {
 } ffi_abi;
 #endif
 
+#define FFI_TARGET_SPECIFIC_STACK_SPACE_ALLOCATION
+#define FFI_TARGET_HAS_COMPLEX_TYPE
+
 /* ---- Definitions for closures ----------------------------------------- */
 
 #define FFI_CLOSURES 1
index 664a2a6..44da192 100644 (file)
@@ -2,6 +2,8 @@
 #define ALPHA_ST_INT   1
 #define ALPHA_ST_FLOAT 2
 #define ALPHA_ST_DOUBLE        3
+#define ALPHA_ST_CPLXF 4
+#define ALPHA_ST_CPLXD 5
 
 #define ALPHA_LD_VOID  0
 #define ALPHA_LD_INT64 1
@@ -12,6 +14,8 @@
 #define ALPHA_LD_SINT8 6
 #define ALPHA_LD_FLOAT 7
 #define ALPHA_LD_DOUBLE        8
+#define ALPHA_LD_CPLXF 9
+#define ALPHA_LD_CPLXD 10
 
 #define ALPHA_ST_SHIFT         0
 #define ALPHA_LD_SHIFT         8
index fb9c595..4059f82 100644 (file)
@@ -117,6 +117,14 @@ E ALPHA_ST_FLOAT
 E ALPHA_ST_DOUBLE
        stt     $f0, 0($2)
        ret
+E ALPHA_ST_CPLXF
+       sts     $f0, 0($2)
+       sts     $f1, 4($2)
+       ret
+E ALPHA_ST_CPLXD
+       stt     $f0, 0($2)
+       stt     $f1, 8($2)
+       ret
 
        cfi_endproc
        .end    ffi_call_osf
@@ -228,6 +236,16 @@ E ALPHA_LD_DOUBLE
        ldt     $f0, 16($sp)
        epilogue
 
+E ALPHA_LD_CPLXF
+       lds     $f0, 16($sp)
+       lds     $f1, 20($sp)
+       epilogue
+
+E ALPHA_LD_CPLXD
+       ldt     $f0, 16($sp)
+       ldt     $f1, 24($sp)
+       epilogue
+
        cfi_endproc
        .end    ffi_closure_osf
 
index 676b323..82e9fe8 100644 (file)
@@ -27,6 +27,7 @@ run-many-tests $tlist ""
 # ??? We really should preprocess ffi.h and grep
 # for FFI_TARGET_HAS_COMPLEX_TYPE.
 if { [istarget aarch64*]
+     || [istarget alpha*]
      || [istarget i?86*]
      || [istarget s390*]
      || [istarget x86_64*] } {
index 0b79979..2b17826 100644 (file)
@@ -6,5 +6,11 @@
 
 /* { dg-do run } */
 
+/* Alpha splits _Complex into two arguments.  It's illegal to pass
+   float through varargs, so _Complex float goes badly.  In sort of
+   gets passed as _Complex double, but the compiler doesn't agree
+   with itself on this issue.  */
+/* { dg-do run { xfail alpha*-*-* } } */
+
 #include "complex_defs_float.inc"
 #include "cls_complex_va.inc"