This is the mail archive of the
libffi-discuss@sourceware.org
mailing list for the libffi project.
[PATCH 3/4] alpha: Add support for complex types
- From: Richard Henderson <rth at twiddle dot net>
- To: libffi-discuss at sourceware dot org
- Date: Wed, 29 Oct 2014 20:54:40 -0700
- Subject: [PATCH 3/4] alpha: Add support for complex types
- Authentication-results: sourceware.org; auth=none
- References: <1414641281-15172-1-git-send-email-rth at twiddle dot net>
---
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