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]

[PATCH 3/4] alpha: Add support for complex types


---
 src/alpha/ffi.c                              | 278 +++++++++++++++++++++------
 src/alpha/ffitarget.h                        |   3 +
 src/alpha/internal.h                         |   4 +
 src/alpha/osf.S                              |  18 ++
 testsuite/libffi.call/call.exp               |  10 +-
 testsuite/libffi.call/cls_complex_va_float.c |   6 +
 6 files changed, 255 insertions(+), 64 deletions(-)

diff --git a/src/alpha/ffi.c b/src/alpha/ffi.c
index 2f9e7e5..1e5187e 100644
--- a/src/alpha/ffi.c
+++ b/src/alpha/ffi.c
@@ -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.  */
diff --git a/src/alpha/ffitarget.h b/src/alpha/ffitarget.h
index af145bc..60f92fd 100644
--- a/src/alpha/ffitarget.h
+++ b/src/alpha/ffitarget.h
@@ -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
diff --git a/src/alpha/internal.h b/src/alpha/internal.h
index 664a2a6..44da192 100644
--- a/src/alpha/internal.h
+++ b/src/alpha/internal.h
@@ -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
diff --git a/src/alpha/osf.S b/src/alpha/osf.S
index fb9c595..4059f82 100644
--- a/src/alpha/osf.S
+++ b/src/alpha/osf.S
@@ -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
 
diff --git a/testsuite/libffi.call/call.exp b/testsuite/libffi.call/call.exp
index 5177f07..09f6965 100644
--- a/testsuite/libffi.call/call.exp
+++ b/testsuite/libffi.call/call.exp
@@ -24,16 +24,12 @@ set ctlist [lsearch -inline -all -glob [lsort [glob -nocomplain -- $srcdir/$subd
 
 run-many-tests $tlist ""
 
-if { ![istarget s390*] } {
-
+if { [istarget s390*] || [istarget alpha*] } {
+    run-many-tests $ctlist ""
+} else {
     foreach test $ctlist {
 	unsupported "$test"
     }
-
-} else {
-
-  run-many-tests $ctlist ""
-
 }
 
 dg-finish
diff --git a/testsuite/libffi.call/cls_complex_va_float.c b/testsuite/libffi.call/cls_complex_va_float.c
index 0b79979..2b17826 100644
--- a/testsuite/libffi.call/cls_complex_va_float.c
+++ b/testsuite/libffi.call/cls_complex_va_float.c
@@ -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"
-- 
1.9.3


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