diff --git a/ChangeLog b/ChangeLog index 4b26025899a..94dc6317f72 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2003-04-18 Jakub Jelinek + + * configure.in (powerpc64*-*-linux*): Remove. + * configure: Rebuilt. + 2003-04-17 Phil Edwards * Makefile.tpl (GCC_STRAP_TARGETS): New variable containing all the diff --git a/configure b/configure index 8b10e661505..045ad147f73 100755 --- a/configure +++ b/configure @@ -1267,11 +1267,6 @@ case "${target}" in powerpc-*-eabi) noconfigdirs="$noconfigdirs ${libgcj}" ;; - powerpc64*-*-linux*) - noconfigdirs="$noconfigdirs target-newlib target-libgloss" - # not yet ported. - noconfigdirs="$noconfigdirs target-libffi" - ;; rs6000-*-lynxos*) noconfigdirs="$noconfigdirs target-newlib gprof ${libgcj}" ;; diff --git a/configure.in b/configure.in index b4e8099cbb0..f695982d796 100644 --- a/configure.in +++ b/configure.in @@ -607,11 +607,6 @@ case "${target}" in powerpc-*-eabi) noconfigdirs="$noconfigdirs ${libgcj}" ;; - powerpc64*-*-linux*) - noconfigdirs="$noconfigdirs target-newlib target-libgloss" - # not yet ported. - noconfigdirs="$noconfigdirs target-libffi" - ;; rs6000-*-lynxos*) noconfigdirs="$noconfigdirs target-newlib gprof ${libgcj}" ;; diff --git a/libffi/ChangeLog b/libffi/ChangeLog index 93b171acbaf..e07b07fead6 100644 --- a/libffi/ChangeLog +++ b/libffi/ChangeLog @@ -1,3 +1,42 @@ +2003-04-18 Jakub Jelinek + + * include/ffi.h.in (POWERPC64): Define if 64-bit. + (enum ffi_abi): Add FFI_LINUX64 on POWERPC. + Make it the default on POWERPC64. + (FFI_TRAMPOLINE_SIZE): Define to 24 on POWERPC64. + * configure.in: Change powerpc-*-linux* into powerpc*-*-linux*. + * configure: Rebuilt. + * src/powerpc/ffi.c (hidden): Define. + (ffi_prep_args_SYSV): Renamed from + ffi_prep_args. Cast pointers to unsigned long to shut up warnings. + (NUM_GPR_ARG_REGISTERS64, NUM_FPR_ARG_REGISTERS64, + ASM_NEEDS_REGISTERS64): New. + (ffi_prep_args64): New function. + (ffi_prep_cif_machdep): Handle FFI_LINUX64 ABI. + (ffi_call): Likewise. + (ffi_prep_closure): Likewise. + (flush_icache): Surround by #ifndef POWERPC64. + (ffi_dblfl): New union type. + (ffi_closure_helper_SYSV): Use it to avoid aliasing problems. + (ffi_closure_helper_LINUX64): New function. + * src/powerpc/ppc_closure.S: Surround whole file by #ifndef + __powerpc64__. + * src/powerpc/sysv.S: Likewise. + (ffi_call_SYSV): Rename ffi_prep_args to ffi_prep_args_SYSV. + * src/powerpc/linux64.S: New file. + * src/powerpc/linux64_closure.S: New file. + * Makefile.am (EXTRA_DIST): Add src/powerpc/linux64.S and + src/powerpc/linux64_closure.S. + (TARGET_SRC_POWERPC): Likewise. + + * src/ffitest.c (closure_test_fn, closure_test_fn1, closure_test_fn2, + closure_test_fn3): Fix result printing on big-endian 64-bit + machines. + (main): Print tst2_arg instead of uninitialized tst2_result. + + * src/ffitest.c (main): Hide what closure pointer really points to + from the compiler. + 2003-04-16 Richard Earnshaw * configure.in (arm-*-netbsdelf*): Add configuration. diff --git a/libffi/Makefile.am b/libffi/Makefile.am index 33c365c2985..82381b8f242 100644 --- a/libffi/Makefile.am +++ b/libffi/Makefile.am @@ -12,6 +12,7 @@ EXTRA_DIST = LICENSE ChangeLog.v1 src/mips/ffi.c src/mips/n32.S \ src/alpha/ffi.c src/alpha/osf.S \ src/m68k/ffi.c src/m68k/sysv.S \ src/powerpc/ffi.c src/powerpc/sysv.S \ + src/powerpc/linux64.S src/powerpc/linux64_closure.S \ src/powerpc/ppc_closure.S src/powerpc/asm.h \ src/powerpc/ffi_darwin.c \ src/powerpc/darwin.S src/powerpc/aix.S \ @@ -94,7 +95,7 @@ TARGET_SRC_SPARC = src/sparc/ffi.c src/sparc/v8.S src/sparc/v9.S TARGET_SRC_ALPHA = src/alpha/ffi.c src/alpha/osf.S TARGET_SRC_IA64 = src/ia64/ffi.c src/ia64/unix.S TARGET_SRC_M68K = src/m68k/ffi.c src/m68k/sysv.S -TARGET_SRC_POWERPC = src/powerpc/ffi.c src/powerpc/sysv.S src/powerpc/ppc_closure.S +TARGET_SRC_POWERPC = src/powerpc/ffi.c src/powerpc/sysv.S src/powerpc/ppc_closure.S src/powerpc/linux64.S src/powerpc/linux64_closure.S TARGET_SRC_POWERPC_AIX = src/powerpc/ffi_darwin.c src/powerpc/aix.S src/powerpc/aix_closure.S TARGET_SRC_POWERPC_DARWIN = src/powerpc/ffi_darwin.c src/powerpc/darwin.S src/powerpc/darwin_closure.S TARGET_SRC_ARM = src/arm/sysv.S src/arm/ffi.c diff --git a/libffi/Makefile.in b/libffi/Makefile.in index 6f89e0a0bd5..08d8c73ada4 100644 --- a/libffi/Makefile.in +++ b/libffi/Makefile.in @@ -95,6 +95,7 @@ EXTRA_DIST = LICENSE ChangeLog.v1 src/mips/ffi.c src/mips/n32.S \ src/alpha/ffi.c src/alpha/osf.S \ src/m68k/ffi.c src/m68k/sysv.S \ src/powerpc/ffi.c src/powerpc/sysv.S \ + src/powerpc/linux64.S src/powerpc/linux64_closure.S \ src/powerpc/ppc_closure.S src/powerpc/asm.h \ src/powerpc/ffi_darwin.c \ src/powerpc/darwin.S src/powerpc/aix.S \ @@ -173,7 +174,7 @@ TARGET_SRC_SPARC = src/sparc/ffi.c src/sparc/v8.S src/sparc/v9.S TARGET_SRC_ALPHA = src/alpha/ffi.c src/alpha/osf.S TARGET_SRC_IA64 = src/ia64/ffi.c src/ia64/unix.S TARGET_SRC_M68K = src/m68k/ffi.c src/m68k/sysv.S -TARGET_SRC_POWERPC = src/powerpc/ffi.c src/powerpc/sysv.S src/powerpc/ppc_closure.S +TARGET_SRC_POWERPC = src/powerpc/ffi.c src/powerpc/sysv.S src/powerpc/ppc_closure.S src/powerpc/linux64.S src/powerpc/linux64_closure.S TARGET_SRC_POWERPC_AIX = src/powerpc/ffi_darwin.c src/powerpc/aix.S src/powerpc/aix_closure.S TARGET_SRC_POWERPC_DARWIN = src/powerpc/ffi_darwin.c src/powerpc/darwin.S src/powerpc/darwin_closure.S TARGET_SRC_ARM = src/arm/sysv.S src/arm/ffi.c @@ -257,7 +258,8 @@ libffi_convenience_la_LIBADD = @POWERPC_TRUE@libffi_convenience_la_OBJECTS = src/debug.lo \ @POWERPC_TRUE@src/prep_cif.lo src/types.lo src/raw_api.lo \ @POWERPC_TRUE@src/java_raw_api.lo src/powerpc/ffi.lo \ -@POWERPC_TRUE@src/powerpc/sysv.lo src/powerpc/ppc_closure.lo +@POWERPC_TRUE@src/powerpc/sysv.lo src/powerpc/ppc_closure.lo \ +@POWERPC_TRUE@src/powerpc/linux64.lo src/powerpc/linux64_closure.lo @MIPS_LINUX_TRUE@libffi_convenience_la_OBJECTS = src/debug.lo \ @MIPS_LINUX_TRUE@src/prep_cif.lo src/types.lo src/raw_api.lo \ @MIPS_LINUX_TRUE@src/java_raw_api.lo src/mips/ffi.lo src/mips/o32.lo @@ -301,7 +303,8 @@ libffi_la_LIBADD = @POWERPC_TRUE@libffi_la_OBJECTS = src/debug.lo src/prep_cif.lo \ @POWERPC_TRUE@src/types.lo src/raw_api.lo src/java_raw_api.lo \ @POWERPC_TRUE@src/powerpc/ffi.lo src/powerpc/sysv.lo \ -@POWERPC_TRUE@src/powerpc/ppc_closure.lo +@POWERPC_TRUE@src/powerpc/ppc_closure.lo src/powerpc/linux64.lo \ +@POWERPC_TRUE@src/powerpc/linux64_closure.lo @MIPS_LINUX_TRUE@libffi_la_OBJECTS = src/debug.lo src/prep_cif.lo \ @MIPS_LINUX_TRUE@src/types.lo src/raw_api.lo src/java_raw_api.lo \ @MIPS_LINUX_TRUE@src/mips/ffi.lo src/mips/o32.lo diff --git a/libffi/configure b/libffi/configure index 42ff71cffc4..6197a44a8cd 100755 --- a/libffi/configure +++ b/libffi/configure @@ -2469,7 +2469,7 @@ ia64*-*-*) TARGET=IA64; TARGETDIR=ia64;; m68k-*-linux*) TARGET=M68K; TARGETDIR=m68k;; mips64*-*);; mips*-*-linux*) TARGET=MIPS_LINUX; TARGETDIR=mips;; -powerpc-*-linux* | powerpc-*-sysv*) TARGET=POWERPC; TARGETDIR=powerpc;; +powerpc*-*-linux* | powerpc-*-sysv*) TARGET=POWERPC; TARGETDIR=powerpc;; powerpc-*-beos*) TARGET=POWERPC; TARGETDIR=powerpc;; powerpc-*-darwin*) TARGET=POWERPC_DARWIN; TARGETDIR=powerpc;; powerpc-*-aix*) TARGET=POWERPC_AIX; TARGETDIR=powerpc;; diff --git a/libffi/configure.in b/libffi/configure.in index e6606f961a5..7bb01d68ca0 100644 --- a/libffi/configure.in +++ b/libffi/configure.in @@ -64,7 +64,7 @@ ia64*-*-*) TARGET=IA64; TARGETDIR=ia64;; m68k-*-linux*) TARGET=M68K; TARGETDIR=m68k;; mips64*-*);; mips*-*-linux*) TARGET=MIPS_LINUX; TARGETDIR=mips;; -powerpc-*-linux* | powerpc-*-sysv*) TARGET=POWERPC; TARGETDIR=powerpc;; +powerpc*-*-linux* | powerpc-*-sysv*) TARGET=POWERPC; TARGETDIR=powerpc;; powerpc-*-beos*) TARGET=POWERPC; TARGETDIR=powerpc;; powerpc-*-darwin*) TARGET=POWERPC_DARWIN; TARGETDIR=powerpc;; powerpc-*-aix*) TARGET=POWERPC_AIX; TARGETDIR=powerpc;; diff --git a/libffi/include/ffi.h.in b/libffi/include/ffi.h.in index fe91ecd5e2e..c51a809ea3f 100644 --- a/libffi/include/ffi.h.in +++ b/libffi/include/ffi.h.in @@ -158,6 +158,12 @@ extern "C" { #define SIZEOF_ARG SIZEOF_VOID_P #endif +#ifdef POWERPC +#if defined (__powerpc64__) +#define POWERPC64 +#endif +#endif + #ifdef SPARC #if defined(__arch64__) || defined(__sparcv9) #define SPARC64 @@ -250,7 +256,12 @@ typedef enum ffi_abi { #ifdef POWERPC FFI_SYSV, FFI_GCC_SYSV, + FFI_LINUX64, +# ifdef POWERPC64 + FFI_DEFAULT_ABI = FFI_LINUX64, +# else FFI_DEFAULT_ABI = FFI_GCC_SYSV, +# endif #endif #ifdef POWERPC_AIX @@ -435,7 +446,11 @@ struct ffi_ia64_trampoline_struct { #elif defined(POWERPC) #define FFI_CLOSURES 1 +#ifdef POWERPC64 +#define FFI_TRAMPOLINE_SIZE 24 +#else #define FFI_TRAMPOLINE_SIZE 40 +#endif #define FFI_NATIVE_RAW_API 0 #elif defined(POWERPC_DARWIN) diff --git a/libffi/src/ffitest.c b/libffi/src/ffitest.c index a05b746865d..223b49c6dd8 100644 --- a/libffi/src/ffitest.c +++ b/libffi/src/ffitest.c @@ -309,7 +309,7 @@ closure_test_fn(ffi_cif* cif,void* resp,void** args, void* userdata) (int)(*(int *)args[10]), (int)(*(float *)args[11]), (int)*(int *)args[12], (int)(*(int *)args[13]), (int)(*(int *)args[14]),*(int *)args[15], - (int)(long)userdata, *(int*)resp); + (int)(long)userdata, (int)*(ffi_arg *)resp); } typedef int (*closure_test_type)(unsigned long long, int, unsigned long long, @@ -339,7 +339,7 @@ static void closure_test_fn1(ffi_cif* cif,void* resp,void** args, (int)(*(int *)args[10]), (int)(*(float *)args[11]), (int)*(int *)args[12], (int)(*(int *)args[13]), (int)(*(int *)args[14]), *(int *)args[15], - (int)(long)userdata, *(int*)resp); + (int)(long)userdata, (int)*(ffi_arg *)resp); } typedef int (*closure_test_type1)(float, float, float, float, signed short, @@ -368,7 +368,7 @@ static void closure_test_fn2(ffi_cif* cif,void* resp,void** args, (int)(*(int *)args[10]), (int)(*(float *)args[11]), (int)*(int *)args[12], (int)(*(float *)args[13]), (int)(*(int *)args[14]), *(int *)args[15], (int)(long)userdata, - *(int*)resp); + (int)*(ffi_arg *)resp); } typedef int (*closure_test_type2)(double, double, double, double, signed short, @@ -397,7 +397,7 @@ static void closure_test_fn3(ffi_cif* cif,void* resp,void** args, (int)(*(float *)args[10]), (int)(*(float *)args[11]), (int)*(int *)args[12], (int)(*(float *)args[13]), (int)(*(float *)args[14]), *(int *)args[15], (int)(long)userdata, - *(int*)resp); + (int)*(ffi_arg *)resp); } typedef int (*closure_test_type3)(float, float, float, float, float, float, @@ -430,6 +430,7 @@ int main(/*@unused@*/ int argc, /*@unused@*/ char *argv[]) /* The closure must not be an automatic variable on platforms (Solaris) that forbid stack execution by default. */ static ffi_closure cl; + ffi_closure *pcl = &cl; #endif ffi_type * cl_arg_types[17]; @@ -841,8 +842,8 @@ int main(/*@unused@*/ int argc, /*@unused@*/ char *argv[]) ts2_arg.d1 = 5.55; ts2_arg.d2 = 6.66; - printf ("%g\n", ts2_result->d1); - printf ("%g\n", ts2_result->d2); + printf ("%g\n", ts2_arg.d1); + printf ("%g\n", ts2_arg.d2); ffi_call(&cif, FFI_FN(struct2), ts2_result, values); @@ -1161,6 +1162,13 @@ int main(/*@unused@*/ int argc, /*@unused@*/ char *argv[]) #endif /* X86_WIN32 */ # if FFI_CLOSURES +# if __GNUC__ >= 2 + /* Hide before the compiler that pcl is &cl, since on + some architectures it is not possible to call a data + object using direct function call. */ + asm ("" : "=g" (pcl) : "0" (pcl)); +# endif + /* A simple closure test */ { (void) puts("\nEnter FFI_CLOSURES\n"); @@ -1187,10 +1195,10 @@ int main(/*@unused@*/ int argc, /*@unused@*/ char *argv[]) CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 16, &ffi_type_sint, cl_arg_types) == FFI_OK); - CHECK(ffi_prep_closure(&cl, &cif, closure_test_fn, + CHECK(ffi_prep_closure(pcl, &cif, closure_test_fn, (void *) 3 /* userdata */) == FFI_OK); - CHECK((*((closure_test_type)(&cl))) + CHECK((*((closure_test_type)pcl)) (1LL, 2, 3LL, 4, 127, 429LL, 7, 8, 9.5, 10, 11, 12, 13, 19, 21, 1) == 680); } @@ -1219,10 +1227,10 @@ int main(/*@unused@*/ int argc, /*@unused@*/ char *argv[]) CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 16, &ffi_type_sint, cl_arg_types) == FFI_OK); - CHECK(ffi_prep_closure(&cl, &cif, closure_test_fn1, + CHECK(ffi_prep_closure(pcl, &cif, closure_test_fn1, (void *) 3 /* userdata */) == FFI_OK); - CHECK((*((closure_test_type1)(&cl))) + CHECK((*((closure_test_type1)pcl)) (1.1, 2.2, 3.3, 4.4, 127, 5.5, 6.6, 8, 9, 10, 11, 12.0, 13, 19, 21, 1) == 255); } @@ -1251,10 +1259,10 @@ int main(/*@unused@*/ int argc, /*@unused@*/ char *argv[]) CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 16, &ffi_type_sint, cl_arg_types) == FFI_OK); - CHECK(ffi_prep_closure(&cl, &cif, closure_test_fn2, + CHECK(ffi_prep_closure(pcl, &cif, closure_test_fn2, (void *) 3 /* userdata */) == FFI_OK); - CHECK((*((closure_test_type2)(&cl))) + CHECK((*((closure_test_type2)pcl)) (1, 2, 3, 4, 127, 5, 6, 8, 9, 10, 11, 12.0, 13, 19.0, 21, 1) == 255); @@ -1284,10 +1292,10 @@ int main(/*@unused@*/ int argc, /*@unused@*/ char *argv[]) CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 16, &ffi_type_sint, cl_arg_types) == FFI_OK); - CHECK(ffi_prep_closure(&cl, &cif, closure_test_fn3, + CHECK(ffi_prep_closure(pcl, &cif, closure_test_fn3, (void *) 3 /* userdata */) == FFI_OK); - CHECK((*((closure_test_type3)(&cl))) + CHECK((*((closure_test_type3)pcl)) (1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9, 10, 11.11, 12.0, 13, 19.19, 21.21, 1) == 135); } diff --git a/libffi/src/powerpc/ffi.c b/libffi/src/powerpc/ffi.c index ea1a14e8f65..6f0e2a561af 100644 --- a/libffi/src/powerpc/ffi.c +++ b/libffi/src/powerpc/ffi.c @@ -31,7 +31,15 @@ #include #include +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 1) +# define hidden __attribute__ ((visibility ("hidden"))) +#else +# define hidden +#endif + + extern void ffi_closure_SYSV(void); +extern void hidden ffi_closure_LINUX64(void); enum { /* The assembly depends on these exact flags. */ @@ -52,7 +60,7 @@ enum { }; enum { ASM_NEEDS_REGISTERS = 4 }; -/* ffi_prep_args is called by the assembly routine once stack space +/* ffi_prep_args_SYSV is called by the assembly routine once stack space has been allocated for the function's arguments. The stack layout we want looks like this: @@ -79,7 +87,7 @@ enum { ASM_NEEDS_REGISTERS = 4 }; */ /*@-exportheader@*/ -void ffi_prep_args(extended_cif *ecif, unsigned *const stack) +void ffi_prep_args_SYSV(extended_cif *ecif, unsigned *const stack) /*@=exportheader@*/ { const unsigned bytes = ecif->cif->bytes; @@ -124,7 +132,7 @@ void ffi_prep_args(extended_cif *ecif, unsigned *const stack) /* Deal with return values that are actually pass-by-reference. */ if (flags & FLAG_RETVAL_REFERENCE) { - *gpr_base++ = (unsigned)(char *)ecif->rvalue; + *gpr_base++ = (unsigned long)(char *)ecif->rvalue; intarg_count++; } @@ -210,7 +218,7 @@ void ffi_prep_args(extended_cif *ecif, unsigned *const stack) copy_space -= struct_copy_size; memcpy(copy_space, (char *)*p_argv, (*ptr)->size); - gprvalue = (unsigned)copy_space; + gprvalue = (unsigned long)copy_space; FFI_ASSERT(copy_space > (char *)next_arg); FFI_ASSERT(flags & FLAG_ARG_NEEDS_COPY); @@ -252,34 +260,229 @@ void ffi_prep_args(extended_cif *ecif, unsigned *const stack) FFI_ASSERT(flags & FLAG_4_GPR_ARGUMENTS || intarg_count <= 4); } +/* About the LINUX64 ABI. */ +enum { + NUM_GPR_ARG_REGISTERS64 = 8, + NUM_FPR_ARG_REGISTERS64 = 13 +}; +enum { ASM_NEEDS_REGISTERS64 = 4 }; + +/* ffi_prep_args64 is called by the assembly routine once stack space + has been allocated for the function's arguments. + + The stack layout we want looks like this: + + | Ret addr from ffi_call_LINUX64 8bytes | higher addresses + |--------------------------------------------| + | CR save area 8bytes | + |--------------------------------------------| + | Previous backchain pointer 8 | stack pointer here + |--------------------------------------------|<+ <<< on entry to + | Saved r28-r31 4*8 | | ffi_call_LINUX64 + |--------------------------------------------| | + | GPR registers r3-r10 8*8 | | + |--------------------------------------------| | + | FPR registers f1-f13 (optional) 13*8 | | + |--------------------------------------------| | + | Parameter save area | | + |--------------------------------------------| | + | TOC save area 8 | | + |--------------------------------------------| | stack | + | Linker doubleword 8 | | gorws | + |--------------------------------------------| | down V + | Compiler doubleword 8 | | + |--------------------------------------------| | lower addresses + | Space for callee's LR 8 | | + |--------------------------------------------| | + | CR save area 8 | | + |--------------------------------------------| | stack pointer here + | Current backchain pointer 8 |-/ during + |--------------------------------------------| <<< ffi_call_LINUX64 + + */ + +/*@-exportheader@*/ +void hidden ffi_prep_args64(extended_cif *ecif, unsigned long *const stack) +/*@=exportheader@*/ +{ + const unsigned long bytes = ecif->cif->bytes; + const unsigned long flags = ecif->cif->flags; + + /* 'stacktop' points at the previous backchain pointer. */ + unsigned long *const stacktop = stack + (bytes / sizeof(unsigned long)); + + /* 'next_arg' points at the space for gpr3, and grows upwards as + we use GPR registers, then continues at rest. */ + unsigned long *const gpr_base = stacktop - ASM_NEEDS_REGISTERS64 + - NUM_GPR_ARG_REGISTERS64; + unsigned long *const gpr_end = gpr_base + NUM_GPR_ARG_REGISTERS64; + unsigned long *const rest = stack + 6 + NUM_GPR_ARG_REGISTERS64; + unsigned long *next_arg = gpr_base; + + /* 'fpr_base' points at the space for fpr3, and grows upwards as + we use FPR registers. */ + double *fpr_base = (double *)gpr_base - NUM_FPR_ARG_REGISTERS64; + int fparg_count = 0; + + int i, words; + ffi_type **ptr; + double double_tmp; + void **p_argv; + unsigned long gprvalue; + + /* Check that everything starts aligned properly. */ + FFI_ASSERT(((unsigned long)(char *)stack & 0xF) == 0); + FFI_ASSERT(((unsigned long)(char *)stacktop & 0xF) == 0); + FFI_ASSERT((bytes & 0xF) == 0); + + /* Deal with return values that are actually pass-by-reference. */ + if (flags & FLAG_RETVAL_REFERENCE) + *next_arg++ = (unsigned long)(char *)ecif->rvalue; + + /* Now for the arguments. */ + p_argv = ecif->avalue; + for (ptr = ecif->cif->arg_types, i = ecif->cif->nargs; + i > 0; + i--, ptr++, p_argv++) + { + switch ((*ptr)->type) + { + case FFI_TYPE_FLOAT: + double_tmp = *(float *)*p_argv; + *(float *)next_arg = (float)double_tmp; + if (++next_arg == gpr_end) + next_arg = rest; + if (fparg_count < NUM_FPR_ARG_REGISTERS64) + *fpr_base++ = double_tmp; + fparg_count++; + FFI_ASSERT(flags & FLAG_FP_ARGUMENTS); + break; + + case FFI_TYPE_DOUBLE: + double_tmp = *(double *)*p_argv; + *(double *)next_arg = double_tmp; + if (++next_arg == gpr_end) + next_arg = rest; + if (fparg_count < NUM_FPR_ARG_REGISTERS64) + *fpr_base++ = double_tmp; + fparg_count++; + FFI_ASSERT(flags & FLAG_FP_ARGUMENTS); + break; + + case FFI_TYPE_STRUCT: +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + case FFI_TYPE_LONGDOUBLE: +#endif + words = ((*ptr)->size + 7) / 8; + if (next_arg >= gpr_base && next_arg + words > gpr_end) + { + unsigned int first = (char *) gpr_end - (char *) next_arg; + memcpy((char *) next_arg, (char *) *p_argv, first); + memcpy((char *) rest, (char *) *p_argv + first, + (*ptr)->size - first); + next_arg = rest + words * 8 - first; + } + else + { + /* Structures with 1, 2 and 4 byte sizes are passed left-padded + if they are in the first 8 arguments. */ + if (next_arg >= gpr_base + && (*ptr)->size < 8 + && ((*ptr)->size & ~((*ptr)->size - 1)) == (*ptr)->size) + memcpy((char *) next_arg + 8 - (*ptr)->size, + (char *) *p_argv, (*ptr)->size); + else + memcpy((char *) next_arg, (char *) *p_argv, (*ptr)->size); + next_arg += words; + if (next_arg == gpr_end) + next_arg = rest; + } + break; + + case FFI_TYPE_UINT8: + gprvalue = *(unsigned char *)*p_argv; + goto putgpr; + case FFI_TYPE_SINT8: + gprvalue = *(signed char *)*p_argv; + goto putgpr; + case FFI_TYPE_UINT16: + gprvalue = *(unsigned short *)*p_argv; + goto putgpr; + case FFI_TYPE_SINT16: + gprvalue = *(signed short *)*p_argv; + goto putgpr; + case FFI_TYPE_UINT32: + gprvalue = *(unsigned int *)*p_argv; + goto putgpr; + case FFI_TYPE_INT: + case FFI_TYPE_SINT32: + gprvalue = *(signed int *)*p_argv; + goto putgpr; + + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + case FFI_TYPE_POINTER: + gprvalue = *(unsigned long *)*p_argv; + putgpr: + *next_arg++ = gprvalue; + if (next_arg == gpr_end) + next_arg = rest; + break; + } + } + + FFI_ASSERT(flags & FLAG_4_GPR_ARGUMENTS + || (next_arg >= gpr_base && next_arg <= gpr_base + 4)); +} + + + /* Perform machine dependent cif processing */ ffi_status ffi_prep_cif_machdep(ffi_cif *cif) { - /* All this is for the SYSV ABI. */ + /* All this is for the SYSV and LINUX64 ABI. */ int i; ffi_type **ptr; unsigned bytes; int fparg_count = 0, intarg_count = 0; unsigned flags = 0; unsigned struct_copy_size = 0; - - /* All the machine-independent calculation of cif->bytes will be wrong. - Redo the calculation for SYSV. */ - /* Space for the frame pointer, callee's LR, and the asm's temp regs. */ - bytes = (2 + ASM_NEEDS_REGISTERS) * sizeof(int); + if (cif->abi != FFI_LINUX64) + { + /* All the machine-independent calculation of cif->bytes will be wrong. + Redo the calculation for SYSV. */ - /* Space for the GPR registers. */ - bytes += NUM_GPR_ARG_REGISTERS * sizeof(int); + /* Space for the frame pointer, callee's LR, and the asm's temp regs. */ + bytes = (2 + ASM_NEEDS_REGISTERS) * sizeof(int); - /* Return value handling. The rules are as follows: + /* Space for the GPR registers. */ + bytes += NUM_GPR_ARG_REGISTERS * sizeof(int); + } + else + { + /* 64-bit ABI. */ + + /* Space for backchain, CR, LR, cc/ld doubleword, TOC and the asm's temp + regs. */ + bytes = (6 + ASM_NEEDS_REGISTERS64) * sizeof(long); + + /* Space for the mandatory parm save area and general registers. */ + bytes += 2 * NUM_GPR_ARG_REGISTERS64 * sizeof(long); + } + + /* Return value handling. The rules for SYSV are as follows: - 32-bit (or less) integer values are returned in gpr3; - Structures of size <= 4 bytes also returned in gpr3; - 64-bit integer values and structures between 5 and 8 bytes are returned in gpr3 and gpr4; - Single/double FP values are returned in fpr1; - Larger structures and long double (if not equivalent to double) values - are allocated space and a pointer is passed as the first argument. */ + are allocated space and a pointer is passed as the first argument. + For LINUX64: + - integer values in gpr3; + - Structures/Unions and long double by reference; + - Single/double FP values in fpr1. */ switch (cif->rtype->type) { case FFI_TYPE_DOUBLE: @@ -295,7 +498,7 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif) break; case FFI_TYPE_STRUCT: - if (cif->abi != FFI_GCC_SYSV) + if (cif->abi != FFI_GCC_SYSV && cif->abi != FFI_LINUX64) if (cif->rtype->size <= 4) break; else if (cif->rtype->size <= 8) @@ -319,59 +522,86 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif) break; } - /* The first NUM_GPR_ARG_REGISTERS words of integer arguments, and the - first NUM_FPR_ARG_REGISTERS fp arguments, go in registers; the rest - goes on the stack. Structures and long doubles (if not equivalent - to double) are passed as a pointer to a copy of the structure. - Stuff on the stack needs to keep proper alignment. */ - for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) - { - switch ((*ptr)->type) - { - case FFI_TYPE_FLOAT: - fparg_count++; - /* floating singles are not 8-aligned on stack */ - break; + if (cif->abi != FFI_LINUX64) + /* The first NUM_GPR_ARG_REGISTERS words of integer arguments, and the + first NUM_FPR_ARG_REGISTERS fp arguments, go in registers; the rest + goes on the stack. Structures and long doubles (if not equivalent + to double) are passed as a pointer to a copy of the structure. + Stuff on the stack needs to keep proper alignment. */ + for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) + { + switch ((*ptr)->type) + { + case FFI_TYPE_FLOAT: + fparg_count++; + /* floating singles are not 8-aligned on stack */ + break; - case FFI_TYPE_DOUBLE: - fparg_count++; - /* If this FP arg is going on the stack, it must be - 8-byte-aligned. */ - if (fparg_count > NUM_FPR_ARG_REGISTERS - && intarg_count%2 != 0) - intarg_count++; - break; + case FFI_TYPE_DOUBLE: + fparg_count++; + /* If this FP arg is going on the stack, it must be + 8-byte-aligned. */ + if (fparg_count > NUM_FPR_ARG_REGISTERS + && intarg_count%2 != 0) + intarg_count++; + break; - case FFI_TYPE_UINT64: - case FFI_TYPE_SINT64: - /* 'long long' arguments are passed as two words, but - either both words must fit in registers or both go - on the stack. If they go on the stack, they must - be 8-byte-aligned. */ - if (intarg_count == NUM_GPR_ARG_REGISTERS-1 - || intarg_count >= NUM_GPR_ARG_REGISTERS && intarg_count%2 != 0) - intarg_count++; - intarg_count += 2; - break; + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + /* 'long long' arguments are passed as two words, but + either both words must fit in registers or both go + on the stack. If they go on the stack, they must + be 8-byte-aligned. */ + if (intarg_count == NUM_GPR_ARG_REGISTERS-1 + || (intarg_count >= NUM_GPR_ARG_REGISTERS + && intarg_count%2 != 0)) + intarg_count++; + intarg_count += 2; + break; - case FFI_TYPE_STRUCT: + case FFI_TYPE_STRUCT: #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE - case FFI_TYPE_LONGDOUBLE: + case FFI_TYPE_LONGDOUBLE: #endif - /* We must allocate space for a copy of these to enforce - pass-by-value. Pad the space up to a multiple of 16 - bytes (the maximum alignment required for anything under - the SYSV ABI). */ - struct_copy_size += ((*ptr)->size + 15) & ~0xF; - /* Fall through (allocate space for the pointer). */ + /* We must allocate space for a copy of these to enforce + pass-by-value. Pad the space up to a multiple of 16 + bytes (the maximum alignment required for anything under + the SYSV ABI). */ + struct_copy_size += ((*ptr)->size + 15) & ~0xF; + /* Fall through (allocate space for the pointer). */ - default: - /* Everything else is passed as a 4-byte word in a GPR, either - the object itself or a pointer to it. */ - intarg_count++; - break; - } - } + default: + /* Everything else is passed as a 4-byte word in a GPR, either + the object itself or a pointer to it. */ + intarg_count++; + break; + } + } + else + for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) + { + switch ((*ptr)->type) + { + case FFI_TYPE_FLOAT: + case FFI_TYPE_DOUBLE: + fparg_count++; + intarg_count++; + break; + + case FFI_TYPE_STRUCT: +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + case FFI_TYPE_LONGDOUBLE: +#endif + intarg_count += ((*ptr)->size + 7) & ~7; + break; + + default: + /* Everything else is passed as a 8-byte word in a GPR, either + the object itself or a pointer to it. */ + intarg_count++; + break; + } + } if (fparg_count != 0) flags |= FLAG_FP_ARGUMENTS; @@ -379,16 +609,29 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif) flags |= FLAG_4_GPR_ARGUMENTS; if (struct_copy_size != 0) flags |= FLAG_ARG_NEEDS_COPY; - - /* Space for the FPR registers, if needed. */ - if (fparg_count != 0) - bytes += NUM_FPR_ARG_REGISTERS * sizeof(double); - /* Stack space. */ - if (intarg_count > NUM_GPR_ARG_REGISTERS) - bytes += (intarg_count - NUM_GPR_ARG_REGISTERS) * sizeof(int); - if (fparg_count > NUM_FPR_ARG_REGISTERS) - bytes += (fparg_count - NUM_FPR_ARG_REGISTERS) * sizeof(double); + if (cif->abi != FFI_LINUX64) + { + /* Space for the FPR registers, if needed. */ + if (fparg_count != 0) + bytes += NUM_FPR_ARG_REGISTERS * sizeof(double); + + /* Stack space. */ + if (intarg_count > NUM_GPR_ARG_REGISTERS) + bytes += (intarg_count - NUM_GPR_ARG_REGISTERS) * sizeof(int); + if (fparg_count > NUM_FPR_ARG_REGISTERS) + bytes += (fparg_count - NUM_FPR_ARG_REGISTERS) * sizeof(double); + } + else + { + /* Space for the FPR registers, if needed. */ + if (fparg_count != 0) + bytes += NUM_FPR_ARG_REGISTERS64 * sizeof(double); + + /* Stack space. */ + if (intarg_count > NUM_GPR_ARG_REGISTERS64) + bytes += (intarg_count - NUM_GPR_ARG_REGISTERS64) * sizeof(long); + } /* The stack space allocated needs to be a multiple of 16 bytes. */ bytes = (bytes + 15) & ~0xF; @@ -408,6 +651,10 @@ extern void ffi_call_SYSV(/*@out@*/ extended_cif *, unsigned, unsigned, /*@out@*/ unsigned *, void (*fn)()); +extern void hidden ffi_call_LINUX64(/*@out@*/ extended_cif *, + unsigned long, unsigned long, + /*@out@*/ unsigned long *, + void (*fn)()); /*@=declundef@*/ /*@=exportheader@*/ @@ -437,6 +684,7 @@ void ffi_call(/*@dependent@*/ ffi_cif *cif, switch (cif->abi) { +#ifndef POWERPC64 case FFI_SYSV: case FFI_GCC_SYSV: /*@-usedef@*/ @@ -444,6 +692,14 @@ void ffi_call(/*@dependent@*/ ffi_cif *cif, cif->flags, ecif.rvalue, fn); /*@=usedef@*/ break; +#else + case FFI_LINUX64: + /*@-usedef@*/ + ffi_call_LINUX64(&ecif, -(long) cif->bytes, + cif->flags, ecif.rvalue, fn); + /*@=usedef@*/ + break; +#endif default: FFI_ASSERT(0); break; @@ -451,14 +707,38 @@ void ffi_call(/*@dependent@*/ ffi_cif *cif, } +#ifndef POWERPC64 static void flush_icache(char *, int); +#define MIN_CACHE_LINE_SIZE 8 + +static void flush_icache(char * addr1, int size) +{ + int i; + char * addr; + for (i = 0; i < size; i += MIN_CACHE_LINE_SIZE) { + addr = addr1 + i; + __asm__ volatile ("icbi 0,%0;" "dcbf 0,%0;" : : "r"(addr) : "memory"); + } + addr = addr1 + size - 1; + __asm__ volatile ("icbi 0,%0;" "dcbf 0,%0;" "sync;" "isync;" : : "r"(addr) : "memory"); +} +#endif + ffi_status ffi_prep_closure (ffi_closure* closure, ffi_cif* cif, void (*fun)(ffi_cif*, void*, void**, void*), void *user_data) { +#ifdef POWERPC64 + void **tramp = (void **) &closure->tramp[0]; + + FFI_ASSERT (cif->abi == FFI_LINUX64); + /* Copy function address and TOC from ffi_closure_LINUX64. */ + memcpy (tramp, (char *) ffi_closure_LINUX64, 16); + tramp[2] = (void *) closure; +#else unsigned int *tramp; FFI_ASSERT (cif->abi == FFI_GCC_SYSV); @@ -475,34 +755,25 @@ ffi_prep_closure (ffi_closure* closure, *(void **) &tramp[2] = (void *)ffi_closure_SYSV; /* function */ *(void **) &tramp[3] = (void *)closure; /* context */ + /* Flush the icache. */ + flush_icache(&closure->tramp[0],FFI_TRAMPOLINE_SIZE); +#endif + closure->cif = cif; closure->fun = fun; closure->user_data = user_data; - /* Flush the icache. */ - flush_icache(&closure->tramp[0],FFI_TRAMPOLINE_SIZE); - return FFI_OK; } - -#define MIN_CACHE_LINE_SIZE 8 - -static void flush_icache(char * addr1, int size) +typedef union { - int i; - char * addr; - for (i = 0; i < size; i += MIN_CACHE_LINE_SIZE) { - addr = addr1 + i; - __asm__ volatile ("icbi 0,%0;" "dcbf 0,%0;" : : "r"(addr) : "memory"); - } - addr = addr1 + size - 1; - __asm__ volatile ("icbi 0,%0;" "dcbf 0,%0;" "sync;" "isync;" : : "r"(addr) : "memory"); -} - + float f; + double d; +} ffi_dblfl; int ffi_closure_helper_SYSV (ffi_closure*, void*, unsigned long*, - unsigned long*, unsigned long*); + ffi_dblfl*, unsigned long*); /* Basically the trampoline invokes ffi_closure_SYSV, and on * entry, r11 holds the address of the closure. @@ -514,7 +785,7 @@ int ffi_closure_helper_SYSV (ffi_closure*, void*, unsigned long*, int ffi_closure_helper_SYSV (ffi_closure* closure, void * rvalue, - unsigned long * pgr, unsigned long * pfr, + unsigned long * pgr, ffi_dblfl * pfr, unsigned long * pst) { /* rvalue is the pointer to space for return value in closure assembly */ @@ -540,7 +811,7 @@ ffi_closure_helper_SYSV (ffi_closure* closure, void * rvalue, returns the data directly to the caller. */ if (cif->rtype->type == FFI_TYPE_STRUCT) { - rvalue = *pgr; + rvalue = (void *) *pgr; ng++; pgr++; } @@ -631,11 +902,11 @@ ffi_closure_helper_SYSV (ffi_closure* closure, void * rvalue, /* there are 8 64bit floating point registers */ if (nf < 8) { - temp = *(double*)pfr; - *(float*)pfr = (float)temp; + temp = pfr->d; + pfr->f = (float)temp; avalue[i] = pfr; nf++; - pfr+=2; + pfr++; } else { /* FIXME? here we are really changing the values * stored in the original calling routines outgoing @@ -655,7 +926,7 @@ ffi_closure_helper_SYSV (ffi_closure* closure, void * rvalue, if (nf < 8) { avalue[i] = pfr; nf++; - pfr+=2; + pfr++; } else { if (((long)pst) & 4) pst++; avalue[i] = pst; @@ -674,12 +945,148 @@ ffi_closure_helper_SYSV (ffi_closure* closure, void * rvalue, (closure->fun) (cif, rvalue, avalue, closure->user_data); - /* Tell ffi_closure_osf how to perform return type promotions. */ + /* Tell ffi_closure_SYSV how to perform return type promotions. */ return cif->rtype->type; } +int hidden ffi_closure_helper_LINUX64 (ffi_closure*, void*, unsigned long*, + ffi_dblfl*); + +int hidden +ffi_closure_helper_LINUX64 (ffi_closure* closure, void * rvalue, + unsigned long * pst, ffi_dblfl * pfr) +{ + /* rvalue is the pointer to space for return value in closure assembly */ + /* pst is the pointer to parameter save area + (r3-r10 are stored into its first 8 slots by ffi_closure_LINUX64) */ + /* pfr is the pointer to where f1-f13 are stored in ffi_closure_LINUX64 */ + + void ** avalue; + ffi_type ** arg_types; + long i, avn; + long nf; /* number of floating registers already used */ + long ng; /* number of general registers already used */ + ffi_cif * cif; + double temp; + + cif = closure->cif; + avalue = alloca(cif->nargs * sizeof(void *)); + + nf = 0; + ng = 0; + + /* Copy the caller's structure return value address so that the closure + returns the data directly to the caller. */ + if (cif->rtype->type == FFI_TYPE_STRUCT) + { + rvalue = (void *) *pst; + ng++; + pst++; + } + + i = 0; + avn = cif->nargs; + arg_types = cif->arg_types; + + /* Grab the addresses of the arguments from the stack frame. */ + while (i < avn) + { + switch (arg_types[i]->type) + { + case FFI_TYPE_SINT8: + case FFI_TYPE_UINT8: + avalue[i] = (char *) pst + 7; + ng++; + pst++; + break; + + case FFI_TYPE_SINT16: + case FFI_TYPE_UINT16: + avalue[i] = (char *) pst + 6; + ng++; + pst++; + break; + + case FFI_TYPE_SINT32: + case FFI_TYPE_UINT32: + avalue[i] = (char *) pst + 4; + ng++; + pst++; + break; + + case FFI_TYPE_SINT64: + case FFI_TYPE_UINT64: + case FFI_TYPE_POINTER: + avalue[i] = pst; + ng++; + pst++; + break; + + case FFI_TYPE_STRUCT: +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + case FFI_TYPE_LONGDOUBLE: +#endif + /* Structures with 1, 2 and 4 byte sizes are passed left-padded + if they are in the first 8 arguments. */ + if (ng < NUM_GPR_ARG_REGISTERS64 + && arg_types[i]->size < 8 + && ((arg_types[i]->size & ~(arg_types[i]->size - 1)) + == arg_types[i]->size)) + avalue[i] = (char *) pst + 8 - arg_types[i]->size; + else + avalue[i] = pst; + ng += (arg_types[i]->size + 7) / 8; + pst += (arg_types[i]->size + 7) / 8; + break; + + case FFI_TYPE_FLOAT: + /* unfortunately float values are stored as doubles + * in the ffi_closure_LINUX64 code (since we don't check + * the type in that routine). + */ + + /* there are 13 64bit floating point registers */ + + if (nf < NUM_FPR_ARG_REGISTERS64) { + temp = pfr->d; + pfr->f = (float)temp; + avalue[i] = pfr; + pfr++; + } else { + avalue[i] = pst; + } + nf++; + ng++; + pst++; + break; + + case FFI_TYPE_DOUBLE: + /* On the outgoing stack all values are aligned to 8 */ + /* there are 13 64bit floating point registers */ + + if (nf < NUM_FPR_ARG_REGISTERS64) { + avalue[i] = pfr; + pfr++; + } else { + avalue[i] = pst; + } + nf++; + ng++; + pst++; + break; + + default: + FFI_ASSERT(0); + } + + i++; + } + (closure->fun) (cif, rvalue, avalue, closure->user_data); + /* Tell ffi_closure_LINUX64 how to perform return type promotions. */ + return cif->rtype->type; +} diff --git a/libffi/src/powerpc/linux64.S b/libffi/src/powerpc/linux64.S new file mode 100644 index 00000000000..9619c539223 --- /dev/null +++ b/libffi/src/powerpc/linux64.S @@ -0,0 +1,185 @@ +/* ----------------------------------------------------------------------- + sysv.h - Copyright (c) 2003 Jakub Jelinek + + PowerPC64 Assembly glue. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + ----------------------------------------------------------------------- */ + +#define LIBFFI_ASM +#include + +#ifdef __powerpc64__ + .hidden ffi_call_LINUX64, .ffi_call_LINUX64 + .globl ffi_call_LINUX64, .ffi_call_LINUX64 + .section ".opd","aw" + .align 3 +ffi_call_LINUX64: + .quad .ffi_call_LINUX64,.TOC.@tocbase,0 + .size ffi_call_LINUX64,24 + .type .ffi_call_LINUX64,@function + .text +.ffi_call_LINUX64: +.LFB1: + mflr %r0 + std %r28, -32(%r1) + std %r29, -24(%r1) + std %r30, -16(%r1) + std %r31, -8(%r1) + std %r0, 16(%r1) + + mr %r28, %r1 /* our AP. */ + stdux %r1, %r1, %r4 +.LCFI0: + mr %r31, %r5 /* flags, */ + mr %r30, %r6 /* rvalue, */ + mr %r29, %r7 /* function address. */ + std %r2, 40(%r1) + + /* Call ffi_prep_args64. */ + mr %r4, %r1 + bl .ffi_prep_args64 + + ld %r0, 0(%r29) + ld %r2, 8(%r29) + ld %r11, 16(%r29) + + /* Now do the call. */ + /* Set up cr1 with bits 4-7 of the flags. */ + mtcrf 0x40, %r31 + + /* Get the address to call into CTR. */ + mtctr %r0 + /* Load all those argument registers. */ + ld %r3, -32-(8*8)(%r28) + ld %r4, -32-(7*8)(%r28) + ld %r5, -32-(6*8)(%r28) + ld %r6, -32-(5*8)(%r28) + bf- 5, 1f + ld %r7, -32-(4*4)(%r28) + ld %r8, -32-(3*4)(%r28) + ld %r9, -32-(2*4)(%r28) + ld %r10, -32-(1*4)(%r28) +1: + + /* Load all the FP registers. */ + bf- 6, 2f + lfd %f1, -32-(21*8)(%r28) + lfd %f2, -32-(20*8)(%r28) + lfd %f3, -32-(19*8)(%r28) + lfd %f4, -32-(18*8)(%r28) + lfd %f5, -32-(17*8)(%r28) + lfd %f6, -32-(16*8)(%r28) + lfd %f7, -32-(15*8)(%r28) + lfd %f8, -32-(14*8)(%r28) + lfd %f9, -32-(13*8)(%r28) + lfd %f10, -32-(12*8)(%r28) + lfd %f11, -32-(11*8)(%r28) + lfd %f12, -32-(10*8)(%r28) + lfd %f13, -32-(9*8)(%r28) +2: + /* FIXME: Shouldn't gcc use %r3-%r10 in this case + and not the parm save area? */ + std %r3, 48+(0*8)(%r1) + std %r4, 48+(1*8)(%r1) + std %r5, 48+(2*8)(%r1) + std %r6, 48+(3*8)(%r1) + std %r7, 48+(4*8)(%r1) + std %r8, 48+(5*8)(%r1) + std %r9, 48+(6*8)(%r1) + std %r10, 48+(7*8)(%r1) + /* end of FIXME. */ + + /* Make the call. */ + bctrl + + /* Now, deal with the return value. */ + mtcrf 0x01, %r31 + bt- 30, .Ldone_return_value + bt- 29, .Lfp_return_value + std %r3, 0(%r30) + /* Fall through... */ + +.Ldone_return_value: + /* Restore the registers we used and return. */ + ld %r2, 40(%r1) + mr %r1, %r28 + ld %r0, 16(%r28) + ld %r28, -32(%r1) + mtlr %r0 + ld %r29, -24(%r1) + ld %r30, -16(%r1) + ld %r31, -8(%r1) + blr + +.Lfp_return_value: + bf 28, .Lfloat_return_value + stfd %f1, 0(%r30) + b .Ldone_return_value +.Lfloat_return_value: + stfs %f1, 0(%r30) + b .Ldone_return_value +.LFE1: + .long 0 + .byte 0,12,0,1,128,4,0,0 + .size .ffi_call_LINUX64,.-.ffi_call_LINUX64 + + .section .eh_frame,"aw",@progbits +.Lframe1: + .4byte .LECIE1-.LSCIE1 # Length of Common Information Entry +.LSCIE1: + .4byte 0x0 # CIE Identifier Tag + .byte 0x1 # CIE Version + .ascii "zR\0" # CIE Augmentation + .uleb128 0x1 # CIE Code Alignment Factor + .sleb128 -8 # CIE Data Alignment Factor + .byte 0x41 # CIE RA Column + .uleb128 0x1 # Augmentation size + .byte 0x14 # FDE Encoding (pcrel udata8) + .byte 0xc # DW_CFA_def_cfa + .uleb128 0x1 + .uleb128 0x0 + .align 3 +.LECIE1: +.LSFDE1: + .4byte .LEFDE1-.LASFDE1 # FDE Length +.LASFDE1: + .4byte .LASFDE1-.Lframe1 # FDE CIE offset + .8byte .LFB1-. # FDE initial location + .8byte .LFE1-.LFB1 # FDE address range + .uleb128 0x0 # Augmentation size + .byte 0x2 # DW_CFA_advance_loc1 + .byte .LCFI0-.LFB1 + .byte 0xd # DW_CFA_def_cfa_register + .uleb128 0x1c + .byte 0x11 # DW_CFA_offset_extended_sf + .uleb128 0x41 + .sleb128 -2 + .byte 0x9f # DW_CFA_offset, column 0x1f + .uleb128 0x1 + .byte 0x9e # DW_CFA_offset, column 0x1e + .uleb128 0x2 + .byte 0x9d # DW_CFA_offset, column 0x1d + .uleb128 0x3 + .byte 0x9c # DW_CFA_offset, column 0x1c + .uleb128 0x4 + .align 3 +.LEFDE1: +#endif diff --git a/libffi/src/powerpc/linux64_closure.S b/libffi/src/powerpc/linux64_closure.S new file mode 100644 index 00000000000..d435e584fd5 --- /dev/null +++ b/libffi/src/powerpc/linux64_closure.S @@ -0,0 +1,210 @@ + .file "linux64_closure.S" + +#ifdef __powerpc64__ + .hidden ffi_closure_LINUX64, .ffi_closure_LINUX64 + .globl ffi_closure_LINUX64, .ffi_closure_LINUX64 + .section ".opd","aw" + .align 3 +ffi_closure_LINUX64: + .quad .ffi_closure_LINUX64,.TOC.@tocbase,0 + .size ffi_closure_LINUX64,24 + .type .ffi_closure_LINUX64,@function + .text +.ffi_closure_LINUX64: +.LFB1: + # save general regs into parm save area + std %r3, 48(%r1) + std %r4, 56(%r1) + std %r5, 64(%r1) + std %r6, 72(%r1) + mflr %r0 + + std %r7, 80(%r1) + std %r8, 88(%r1) + std %r9, 96(%r1) + std %r10, 104(%r1) + std %r0, 16(%r1) + + # mandatory 48 bytes special reg save area + 64 bytes parm save area + # + 8 bytes retval area + 13*8 bytes fpr save area + stdu %r1, -224(%r1) +.LCFI0: + + # next save fpr 1 to fpr 13 + stfd %f1, 120+(0*8)(%r1) + stfd %f2, 120+(1*8)(%r1) + stfd %f3, 120+(2*8)(%r1) + stfd %f4, 120+(3*8)(%r1) + stfd %f5, 120+(4*8)(%r1) + stfd %f6, 120+(5*8)(%r1) + stfd %f7, 120+(6*8)(%r1) + stfd %f8, 120+(7*8)(%r1) + stfd %f9, 120+(8*8)(%r1) + stfd %f10, 120+(9*8)(%r1) + stfd %f11, 120+(10*8)(%r1) + stfd %f12, 120+(11*8)(%r1) + stfd %f13, 120+(12*8)(%r1) + + # set up registers for the routine that actually does the work + # get the context pointer from the trampoline + mr %r3, %r11 + + # now load up the pointer to the result storage + addi %r4, %r1, 112 + + # now load up the pointer to the parameter save area + # in the previous frame + addi %r5, %r1, 224 + 48 + + # now load up the pointer to the saved fpr registers */ + addi %r6, %r1, 120 + + # make the call + bl .ffi_closure_helper_LINUX64 + + # now r3 contains the return type + # so use it to look up in a table + # so we know how to deal with each type + + # look up the proper starting point in table + # by using return type as offset + addi %r5, %r1, 112 # get pointer to results area + bl .Lget_ret_type0_addr # get pointer to .Lret_type0 into LR + mflr %r4 # move to r4 + sldi %r3, %r3, 4 # now multiply return type by 16 + add %r3, %r3, %r4 # add contents of table to table address + mtctr %r3 + bctr # jump to it + +# Each of the ret_typeX code fragments has to be exactly 16 bytes long +# (4 instructions). For cache effectiveness we align to a 16 byte boundary +# first. + .align 4 + + nop + nop + nop +.Lget_ret_type0_addr: + blrl + +.Lret_type0: +# case FFI_TYPE_VOID + b .Lfinish + nop + nop + nop +# case FFI_TYPE_INT + lwa %r3, 4(%r5) + b .Lfinish + nop + nop +# case FFI_TYPE_FLOAT + lfs %f1, 4(%r5) + b .Lfinish + nop + nop +# case FFI_TYPE_DOUBLE + lfd %f1, 0(%r5) + b .Lfinish + nop + nop +# case FFI_TYPE_LONGDOUBLE + lfd %f1, 0(%r5) + b .Lfinish + nop + nop +# case FFI_TYPE_UINT8 + lbz %r3, 7(%r5) + b .Lfinish + nop + nop +# case FFI_TYPE_SINT8 + lbz %r3, 7(%r5) + extsb %r3,%r3 + b .Lfinish + nop +# case FFI_TYPE_UINT16 + lhz %r3, 6(%r5) + b .Lfinish + nop + nop +# case FFI_TYPE_SINT16 + lha %r3, 6(%r5) + b .Lfinish + nop + nop +# case FFI_TYPE_UINT32 + lwz %r3, 4(%r5) + b .Lfinish + nop + nop +# case FFI_TYPE_SINT32 + lwa %r3, 4(%r5) + b .Lfinish + nop + nop +# case FFI_TYPE_UINT64 + ld %r3, 0(%r5) + b .Lfinish + nop + nop +# case FFI_TYPE_SINT64 + ld %r3, 0(%r5) + b .Lfinish + nop + nop +# case FFI_TYPE_STRUCT + b .Lfinish + nop + nop + nop +# case FFI_TYPE_POINTER + ld %r3, 0(%r5) + b .Lfinish + nop + nop +# esac +.Lfinish: + ld %r0, 224+16(%r1) + mtlr %r0 + addi %r1, %r1, 224 + blr +.LFE1: + .long 0 + .byte 0,12,0,1,128,0,0,0 + .size .ffi_closure_LINUX64,.-.ffi_closure_LINUX64 + + .section .eh_frame,"aw",@progbits +.Lframe1: + .4byte .LECIE1-.LSCIE1 # Length of Common Information Entry +.LSCIE1: + .4byte 0x0 # CIE Identifier Tag + .byte 0x1 # CIE Version + .ascii "zR\0" # CIE Augmentation + .uleb128 0x1 # CIE Code Alignment Factor + .sleb128 -8 # CIE Data Alignment Factor + .byte 0x41 # CIE RA Column + .uleb128 0x1 # Augmentation size + .byte 0x14 # FDE Encoding (pcrel udata8) + .byte 0xc # DW_CFA_def_cfa + .uleb128 0x1 + .uleb128 0x0 + .align 3 +.LECIE1: +.LSFDE1: + .4byte .LEFDE1-.LASFDE1 # FDE Length +.LASFDE1: + .4byte .LASFDE1-.Lframe1 # FDE CIE offset + .8byte .LFB1-. # FDE initial location + .8byte .LFE1-.LFB1 # FDE address range + .uleb128 0x0 # Augmentation size + .byte 0x2 # DW_CFA_advance_loc1 + .byte .LCFI0-.LFB1 + .byte 0xe # DW_CFA_def_cfa_offset + .uleb128 224 + .byte 0x11 # DW_CFA_offset_extended_sf + .uleb128 0x41 + .sleb128 -2 + .align 3 +.LEFDE1: +#endif diff --git a/libffi/src/powerpc/ppc_closure.S b/libffi/src/powerpc/ppc_closure.S index e402fb5cda3..4cfc8fda5ad 100644 --- a/libffi/src/powerpc/ppc_closure.S +++ b/libffi/src/powerpc/ppc_closure.S @@ -3,6 +3,8 @@ .file "ppc_closure.S" +#ifndef __powerpc64__ + ENTRY(ffi_closure_SYSV) .LFB1: stwu %r1,-144(%r1) @@ -227,3 +229,5 @@ __FRAME_BEGIN__: .byte 0x1 # uleb128 0x1 .align 2 .LEFDE1: + +#endif diff --git a/libffi/src/powerpc/sysv.S b/libffi/src/powerpc/sysv.S index 538ffa83bdb..c1e0d18461c 100644 --- a/libffi/src/powerpc/sysv.S +++ b/libffi/src/powerpc/sysv.S @@ -29,7 +29,8 @@ #include #include - .globl ffi_prep_args +#ifndef __powerpc64__ + .globl ffi_prep_args_SYSV ENTRY(ffi_call_SYSV) .LFB1: /* Save the old stack pointer as AP. */ @@ -58,9 +59,9 @@ ENTRY(ffi_call_SYSV) mr %r28,%r8 /* our AP. */ .LCFI6: - /* Call ffi_prep_args. */ + /* Call ffi_prep_args_SYSV. */ mr %r4,%r1 - bl JUMPTARGET(ffi_prep_args) + bl JUMPTARGET(ffi_prep_args_SYSV) /* Now do the call. */ /* Set up cr1 with bits 4-7 of the flags. */ @@ -171,3 +172,4 @@ __FRAME_BEGIN__: .byte 0x1c /* uleb128 0x1c */ .align 2 .LEFDE1: +#endif