summaryrefslogtreecommitdiff
path: root/ext/ffi_c/libffi/src/metag/sysv.S
diff options
context:
space:
mode:
Diffstat (limited to 'ext/ffi_c/libffi/src/metag/sysv.S')
-rw-r--r--ext/ffi_c/libffi/src/metag/sysv.S311
1 files changed, 311 insertions, 0 deletions
diff --git a/ext/ffi_c/libffi/src/metag/sysv.S b/ext/ffi_c/libffi/src/metag/sysv.S
new file mode 100644
index 0000000..b4b2a3b
--- /dev/null
+++ b/ext/ffi_c/libffi/src/metag/sysv.S
@@ -0,0 +1,311 @@
+/* -----------------------------------------------------------------------
+ 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]