diff options
Diffstat (limited to 'libffi/src/sparc/ffi.c')
-rw-r--r-- | libffi/src/sparc/ffi.c | 66 |
1 files changed, 55 insertions, 11 deletions
diff --git a/libffi/src/sparc/ffi.c b/libffi/src/sparc/ffi.c index 1d01f59ec23..1ac5d464e8b 100644 --- a/libffi/src/sparc/ffi.c +++ b/libffi/src/sparc/ffi.c @@ -1,5 +1,6 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (c) 1996, 2003, 2004, 2007, 2008 Red Hat, Inc. + ffi.c - Copyright (c) 2011 Anthony Green + Copyright (c) 1996, 2003-2004, 2007-2008 Red Hat, Inc. SPARC Foreign Function Interface @@ -406,8 +407,50 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) /* We don't yet support calling 32bit code from 64bit */ FFI_ASSERT(0); #else - ffi_call_v8(ffi_prep_args_v8, &ecif, cif->bytes, - cif->flags, rvalue, fn); + if (rvalue && (cif->rtype->type == FFI_TYPE_STRUCT +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + || cif->flags == FFI_TYPE_LONGDOUBLE +#endif + )) + { + /* For v8, we need an "unimp" with size of returning struct */ + /* behind "call", so we alloc some executable space for it. */ + /* l7 is used, we need to make sure v8.S doesn't use %l7. */ + unsigned int *call_struct = NULL; + ffi_closure_alloc(32, &call_struct); + if (call_struct) + { + unsigned long f = (unsigned long)fn; + call_struct[0] = 0xae10001f; /* mov %i7, %l7 */ + call_struct[1] = 0xbe10000f; /* mov %o7, %i7 */ + call_struct[2] = 0x03000000 | f >> 10; /* sethi %hi(fn), %g1 */ + call_struct[3] = 0x9fc06000 | (f & 0x3ff); /* jmp %g1+%lo(fn), %o7 */ + call_struct[4] = 0x01000000; /* nop */ + if (cif->rtype->size < 0x7f) + call_struct[5] = cif->rtype->size; /* unimp */ + else + call_struct[5] = 0x01000000; /* nop */ + call_struct[6] = 0x81c7e008; /* ret */ + call_struct[7] = 0xbe100017; /* mov %l7, %i7 */ + asm volatile ("iflush %0; iflush %0+8; iflush %0+16; iflush %0+24" : : + "r" (call_struct) : "memory"); + /* SPARC v8 requires 5 instructions for flush to be visible */ + asm volatile ("nop; nop; nop; nop; nop"); + ffi_call_v8(ffi_prep_args_v8, &ecif, cif->bytes, + cif->flags, rvalue, call_struct); + ffi_closure_free(call_struct); + } + else + { + ffi_call_v8(ffi_prep_args_v8, &ecif, cif->bytes, + cif->flags, rvalue, fn); + } + } + else + { + ffi_call_v8(ffi_prep_args_v8, &ecif, cif->bytes, + cif->flags, rvalue, fn); + } #endif break; case FFI_V9: @@ -425,7 +468,6 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) FFI_ASSERT(0); break; } - } @@ -447,7 +489,8 @@ ffi_prep_closure_loc (ffi_closure* closure, #ifdef SPARC64 /* Trampoline address is equal to the closure address. We take advantage of that to reduce the trampoline size by 8 bytes. */ - FFI_ASSERT (cif->abi == FFI_V9); + if (cif->abi != FFI_V9) + return FFI_BAD_ABI; fn = (unsigned long) ffi_closure_v9; tramp[0] = 0x83414000; /* rd %pc, %g1 */ tramp[1] = 0xca586010; /* ldx [%g1+16], %g5 */ @@ -456,7 +499,8 @@ ffi_prep_closure_loc (ffi_closure* closure, *((unsigned long *) &tramp[4]) = fn; #else unsigned long ctx = (unsigned long) codeloc; - FFI_ASSERT (cif->abi == FFI_V8); + if (cif->abi != FFI_V8) + return FFI_BAD_ABI; fn = (unsigned long) ffi_closure_v8; tramp[0] = 0x03000000 | fn >> 10; /* sethi %hi(fn), %g1 */ tramp[1] = 0x05000000 | ctx >> 10; /* sethi %hi(ctx), %g2 */ @@ -468,13 +512,13 @@ ffi_prep_closure_loc (ffi_closure* closure, closure->fun = fun; closure->user_data = user_data; - /* Flush the Icache. FIXME: alignment isn't certain, assume 8 bytes */ + /* Flush the Icache. closure is 8 bytes aligned. */ #ifdef SPARC64 - asm volatile ("flush %0" : : "r" (closure) : "memory"); - asm volatile ("flush %0" : : "r" (((char *) closure) + 8) : "memory"); + asm volatile ("flush %0; flush %0+8" : : "r" (closure) : "memory"); #else - asm volatile ("iflush %0" : : "r" (closure) : "memory"); - asm volatile ("iflush %0" : : "r" (((char *) closure) + 8) : "memory"); + asm volatile ("iflush %0; iflush %0+8" : : "r" (closure) : "memory"); + /* SPARC v8 requires 5 instructions for flush to be visible */ + asm volatile ("nop; nop; nop; nop; nop"); #endif return FFI_OK; |