| /* ----------------------------------------------------------------------- |
| sysv.S - Copyright (c) 2013 Imagination Technologies Ltd. |
| |
| Meta Foreign Function Interface |
| |
| 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 AUTHORS OR COPYRIGHT |
| HOLDERS 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 <fficonfig.h> |
| #include <ffi.h> |
| #ifdef HAVE_MACHINE_ASM_H |
| #include <machine/asm.h> |
| #else |
| #ifdef __USER_LABEL_PREFIX__ |
| #define CONCAT1(a, b) CONCAT2(a, b) |
| #define CONCAT2(a, b) a ## b |
| |
| /* Use the right prefix for global labels. */ |
| #define CNAME(x) CONCAT1 (__USER_LABEL_PREFIX__, x) |
| #else |
| #define CNAME(x) x |
| #endif |
| #define ENTRY(x) .globl CNAME(x); .type CNAME(x), %function; CNAME(x): |
| #endif |
| |
| #ifdef __ELF__ |
| #define LSYM(x) .x |
| #else |
| #define LSYM(x) x |
| #endif |
| |
| .macro call_reg x= |
| .text |
| .balign 4 |
| mov D1RtP, \x |
| swap D1RtP, PC |
| .endm |
| |
| ! Save register arguments |
| .macro SAVE_ARGS |
| .text |
| .balign 4 |
| setl [A0StP++], D0Ar6, D1Ar5 |
| setl [A0StP++], D0Ar4, D1Ar3 |
| setl [A0StP++], D0Ar2, D1Ar1 |
| .endm |
| |
| ! Save retrun, frame pointer and other regs |
| .macro SAVE_REGS regs= |
| .text |
| .balign 4 |
| setl [A0StP++], D0FrT, D1RtP |
| ! Needs to be a pair of regs |
| .ifnc "\regs","" |
| setl [A0StP++], \regs |
| .endif |
| .endm |
| |
| ! Declare a global function |
| .macro METAG_FUNC_START name |
| .text |
| .balign 4 |
| ENTRY(\name) |
| .endm |
| |
| ! Return registers from the stack. Reverse SAVE_REGS operation |
| .macro RET_REGS regs=, cond= |
| .ifnc "\regs", "" |
| getl \regs, [--A0StP] |
| .endif |
| getl D0FrT, D1RtP, [--A0StP] |
| .endm |
| |
| ! Return arguments |
| .macro RET_ARGS |
| getl D0Ar2, D1Ar1, [--A0StP] |
| getl D0Ar4, D1Ar3, [--A0StP] |
| getl D0Ar6, D1Ar5, [--A0StP] |
| .endm |
| |
| |
| ! D1Ar1: fn |
| ! D0Ar2: &ecif |
| ! D1Ar3: cif->bytes |
| ! D0Ar4: fig->flags |
| ! D1Ar5: ecif.rvalue |
| |
| ! This assumes we are using GNU as |
| METAG_FUNC_START ffi_call_SYSV |
| ! Save argument registers |
| |
| SAVE_ARGS |
| |
| ! new frame |
| mov D0FrT, A0FrP |
| add A0FrP, A0StP, #0 |
| |
| ! Preserve the old frame pointer |
| SAVE_REGS "D1.5, D0.5" |
| |
| ! Make room for new args. cifs->bytes is the total space for input |
| ! and return arguments |
| |
| add A0StP, A0StP, D1Ar3 |
| |
| ! Preserve cifs->bytes & fn |
| mov D0.5, D1Ar3 |
| mov D1.5, D1Ar1 |
| |
| ! Place all of the ffi_prep_args in position |
| mov D1Ar1, A0StP |
| |
| ! Call ffi_prep_args(stack, &ecif) |
| #ifdef __PIC__ |
| callr D1RtP, CNAME(ffi_prep_args@PLT) |
| #else |
| callr D1RtP, CNAME(ffi_prep_args) |
| #endif |
| |
| ! Restore fn pointer |
| |
| ! The foreign stack should look like this |
| ! XXXXX XXXXXX <--- stack pointer |
| ! FnArgN rvalue |
| ! FnArgN+2 FnArgN+1 |
| ! FnArgN+4 FnArgN+3 |
| ! .... |
| ! |
| |
| ! A0StP now points to the first (or return) argument + 4 |
| |
| ! Preserve cif->bytes |
| getl D0Ar2, D1Ar1, [--A0StP] |
| getl D0Ar4, D1Ar3, [--A0StP] |
| getl D0Ar6, D1Ar5, [--A0StP] |
| |
| ! Place A0StP to the first argument again |
| add A0StP, A0StP, #24 ! That's because we loaded 6 regs x 4 byte each |
| |
| ! A0FrP points to the initial stack without the reserved space for the |
| ! cifs->bytes, whilst A0StP points to the stack after the space allocation |
| |
| ! fn was the first argument of ffi_call_SYSV. |
| ! The stack at this point looks like this: |
| ! |
| ! A0StP(on entry to _SYSV) -> Arg6 Arg5 | low |
| ! Arg4 Arg3 | |
| ! Arg2 Arg1 | |
| ! A0FrP ----> D0FrtP D1RtP | |
| ! D1.5 D0.5 | |
| ! A0StP(bf prep_args) -> FnArgn FnArgn-1 | |
| ! FnArgn-2FnArgn-3 | |
| ! ................ | <= cifs->bytes |
| ! FnArg4 FnArg3 | |
| ! A0StP (prv_A0StP+cifs->bytes) FnArg2 FnArg1 | high |
| ! |
| ! fn was in Arg1 so it's located in in A0FrP+#-0xC |
| ! |
| |
| ! D0Re0 contains the size of arguments stored in registers |
| sub A0StP, A0StP, D0Re0 |
| |
| ! Arg1 is the function pointer for the foreign call. This has been |
| ! preserved in D1.5 |
| |
| ! Time to call (fn). Arguments should be like this: |
| ! Arg1-Arg6 are loaded to regs |
| ! The rest of the arguments are stored in stack pointed by A0StP |
| |
| call_reg D1.5 |
| |
| ! Reset stack. |
| |
| mov A0StP, A0FrP |
| |
| ! Load Arg1 with the pointer to storage for the return type |
| ! This was stored in Arg5 |
| |
| getd D1Ar1, [A0FrP+#-20] |
| |
| ! Load D0Ar2 with the return type code. This was stored in Arg4 (flags) |
| |
| getd D0Ar2, [A0FrP+#-16] |
| |
| ! We are ready to start processing the return value |
| ! D0Re0 (and D1Re0) hold the return value |
| |
| ! If the return value is NULL, assume no return value |
| cmp D1Ar1, #0 |
| beq LSYM(Lepilogue) |
| |
| ! return INT |
| cmp D0Ar2, #FFI_TYPE_INT |
| ! Sadly, there is no setd{cc} instruction so we need to workaround that |
| bne .INT64 |
| setd [D1Ar1], D0Re0 |
| b LSYM(Lepilogue) |
| |
| ! return INT64 |
| .INT64: |
| cmp D0Ar2, #FFI_TYPE_SINT64 |
| setleq [D1Ar1], D0Re0, D1Re0 |
| |
| ! return DOUBLE |
| cmp D0Ar2, #FFI_TYPE_DOUBLE |
| setl [D1AR1++], D0Re0, D1Re0 |
| |
| LSYM(Lepilogue): |
| ! At this point, the stack pointer points right after the argument |
| ! saved area. We need to restore 4 regs, therefore we need to move |
| ! 16 bytes ahead. |
| add A0StP, A0StP, #16 |
| RET_REGS "D1.5, D0.5" |
| RET_ARGS |
| getd D0Re0, [A0StP] |
| mov A0FrP, D0FrT |
| swap D1RtP, PC |
| |
| .ffi_call_SYSV_end: |
| .size CNAME(ffi_call_SYSV),.ffi_call_SYSV_end-CNAME(ffi_call_SYSV) |
| |
| |
| /* |
| (called by ffi_metag_trampoline) |
| void ffi_closure_SYSV (ffi_closure*) |
| |
| (called by ffi_closure_SYSV) |
| unsigned int FFI_HIDDEN |
| ffi_closure_SYSV_inner (closure,respp, args) |
| ffi_closure *closure; |
| void **respp; |
| void *args; |
| */ |
| |
| METAG_FUNC_START ffi_closure_SYSV |
| ! We assume that D1Ar1 holds the address of the |
| ! ffi_closure struct. We will use that to fetch the |
| ! arguments. The stack pointer points to an empty space |
| ! and it is ready to store more data. |
| |
| ! D1Ar1 is ready |
| ! Allocate stack space for return value |
| add A0StP, A0StP, #8 |
| ! Store it to D0Ar2 |
| sub D0Ar2, A0StP, #8 |
| |
| sub D1Ar3, A0FrP, #4 |
| |
| ! D1Ar3 contains the address of the original D1Ar1 argument |
| ! We need to subtract #4 later on |
| |
| ! Preverve D0Ar2 |
| mov D0.5, D0Ar2 |
| |
| #ifdef __PIC__ |
| callr D1RtP, CNAME(ffi_closure_SYSV_inner@PLT) |
| #else |
| callr D1RtP, CNAME(ffi_closure_SYSV_inner) |
| #endif |
| |
| ! Check the return value and store it to D0.5 |
| cmp D0Re0, #FFI_TYPE_INT |
| beq .Lretint |
| cmp D0Re0, #FFI_TYPE_DOUBLE |
| beq .Lretdouble |
| .Lclosure_epilogue: |
| sub A0StP, A0StP, #8 |
| RET_REGS "D1.5, D0.5" |
| RET_ARGS |
| swap D1RtP, PC |
| |
| .Lretint: |
| setd [D0.5], D0Re0 |
| b .Lclosure_epilogue |
| .Lretdouble: |
| setl [D0.5++], D0Re0, D1Re0 |
| b .Lclosure_epilogue |
| .ffi_closure_SYSV_end: |
| .size CNAME(ffi_closure_SYSV),.ffi_closure_SYSV_end-CNAME(ffi_closure_SYSV) |
| |
| |
| ENTRY(ffi_metag_trampoline) |
| SAVE_ARGS |
| ! New frame |
| mov A0FrP, A0StP |
| SAVE_REGS "D1.5, D0.5" |
| mov D0.5, PC |
| ! Load D1Ar1 the value of ffi_metag_trampoline |
| getd D1Ar1, [D0.5 + #8] |
| ! Jump to ffi_closure_SYSV |
| getd PC, [D0.5 + #12] |