From: Richard Henderson Date: Sat, 18 Oct 2014 03:46:48 +0000 (-0700) Subject: alpha: Add support for complex types X-Git-Tag: upstream/3.3~371^2~1^2~20 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f41bec3b576aa5ff8915b1188446c2dc086dfe64;p=platform%2Fupstream%2Flibffi.git alpha: Add support for complex types --- 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 676b323..82e9fe8 100644 --- a/testsuite/libffi.call/call.exp +++ b/testsuite/libffi.call/call.exp @@ -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*] } { 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"