1/* -----------------------------------------------------------------------
2   sysv.S - Copyright (c) 2013 Imagination Technologies Ltd.
3
4   Meta Foreign Function Interface
5
6   Permission is hereby granted, free of charge, to any person obtaining
7   a copy of this software and associated documentation files (the
8   ``Software''), to deal in the Software without restriction, including
9   without limitation the rights to use, copy, modify, merge, publish,
10   distribute, sublicense, and/or sell copies of the Software, and to
11   permit persons to whom the Software is furnished to do so, subject to
12   the following conditions:
13
14   The above copyright notice and this permission notice shall be included
15   in all copies or substantial portions of the Software.
16
17
18   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
19   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21   NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25   DEALINGS IN THE SOFTWARE.
26   ----------------------------------------------------------------------- */
27
28#define LIBFFI_ASM
29#include <fficonfig.h>
30#include <ffi.h>
31#ifdef HAVE_MACHINE_ASM_H
32#include <machine/asm.h>
33#else
34#ifdef __USER_LABEL_PREFIX__
35#define CONCAT1(a, b) CONCAT2(a, b)
36#define CONCAT2(a, b) a ## b
37
38/* Use the right prefix for global labels. */
39#define CNAME(x) CONCAT1 (__USER_LABEL_PREFIX__, x)
40#else
41#define CNAME(x) x
42#endif
43#define ENTRY(x) .globl CNAME(x); .type CNAME(x), %function; CNAME(x):
44#endif
45
46#ifdef __ELF__
47#define LSYM(x) .x
48#else
49#define LSYM(x) x
50#endif
51
52.macro call_reg x=
53	.text
54	.balign 4
55	mov D1RtP, \x
56	swap D1RtP, PC
57.endm
58
59! Save register arguments
60.macro SAVE_ARGS
61	.text
62	.balign 4
63	setl	[A0StP++], D0Ar6, D1Ar5
64	setl	[A0StP++], D0Ar4, D1Ar3
65	setl	[A0StP++], D0Ar2, D1Ar1
66.endm
67
68! Save retrun, frame pointer and other regs
69.macro SAVE_REGS regs=
70	.text
71	.balign 4
72	setl	[A0StP++], D0FrT, D1RtP
73	! Needs to be a pair of regs
74	.ifnc "\regs",""
75	setl	[A0StP++], \regs
76	.endif
77.endm
78
79! Declare a global function
80.macro METAG_FUNC_START name
81	.text
82	.balign 4
83	ENTRY(\name)
84.endm
85
86! Return registers from the stack. Reverse SAVE_REGS operation
87.macro RET_REGS regs=, cond=
88	.ifnc "\regs", ""
89	getl	\regs, [--A0StP]
90	.endif
91	getl	D0FrT, D1RtP, [--A0StP]
92.endm
93
94! Return arguments
95.macro RET_ARGS
96	getl	D0Ar2, D1Ar1, [--A0StP]
97	getl	D0Ar4, D1Ar3, [--A0StP]
98	getl	D0Ar6, D1Ar5, [--A0StP]
99.endm
100
101
102	! D1Ar1:	fn
103	! D0Ar2:	&ecif
104	! D1Ar3:	cif->bytes
105	! D0Ar4:	fig->flags
106	! D1Ar5:	ecif.rvalue
107
108	! This assumes we are using GNU as
109METAG_FUNC_START ffi_call_SYSV
110	! Save argument registers
111
112	SAVE_ARGS
113
114	! new frame
115	mov	D0FrT, A0FrP
116	add     A0FrP, A0StP, #0
117
118	! Preserve the old frame pointer
119	SAVE_REGS "D1.5, D0.5"
120
121	! Make room for new args. cifs->bytes is the total space for input
122	! and return arguments
123
124	add	A0StP, A0StP, D1Ar3
125
126	! Preserve cifs->bytes & fn
127	mov	D0.5, D1Ar3
128	mov	D1.5, D1Ar1
129
130	! Place all of the ffi_prep_args in position
131	mov	D1Ar1, A0StP
132
133	! Call ffi_prep_args(stack, &ecif)
134#ifdef __PIC__
135	callr  D1RtP, CNAME(ffi_prep_args@PLT)
136#else
137	callr  D1RtP, CNAME(ffi_prep_args)
138#endif
139
140	! Restore fn pointer
141
142	! The foreign stack should look like this
143	! XXXXX XXXXXX <--- stack pointer
144	! FnArgN rvalue
145	! FnArgN+2 FnArgN+1
146	! FnArgN+4 FnArgN+3
147	! ....
148	!
149
150	! A0StP now points to the first (or return) argument + 4
151
152	! Preserve cif->bytes
153	getl	D0Ar2, D1Ar1, [--A0StP]
154	getl	D0Ar4, D1Ar3, [--A0StP]
155	getl	D0Ar6, D1Ar5, [--A0StP]
156
157	! Place A0StP to the first argument again
158	add	A0StP, A0StP, #24 ! That's because we loaded 6 regs x 4 byte each
159
160	! A0FrP points to the initial stack without the reserved space for the
161	! cifs->bytes, whilst A0StP points to the stack after the space allocation
162
163	! fn was the first argument of ffi_call_SYSV.
164	! The stack at this point looks like this:
165	!
166	! A0StP(on entry to _SYSV) ->	Arg6	Arg5     | low
167	!				Arg4	Arg3     |
168	! 				Arg2	Arg1     |
169	! A0FrP ---->			D0FrtP	D1RtP    |
170	!				D1.5	D0.5	 |
171	! A0StP(bf prep_args) ->	FnArgn	FnArgn-1 |
172	!				FnArgn-2FnArgn-3 |
173	!				................ | <= cifs->bytes
174	!				FnArg4  FnArg3	 |
175	! A0StP (prv_A0StP+cifs->bytes) FnArg2  FnArg1   | high
176	!
177	! fn was in Arg1 so it's located in in A0FrP+#-0xC
178	!
179
180	! D0Re0 contains the size of arguments stored in registers
181	sub	A0StP, A0StP, D0Re0
182
183	! Arg1 is the function pointer for the foreign call. This has been
184	! preserved in D1.5
185
186	! Time to call (fn). Arguments should be like this:
187	! Arg1-Arg6 are loaded to regs
188	! The rest of the arguments are stored in stack pointed by A0StP
189
190	call_reg D1.5
191
192	! Reset stack.
193
194	mov	A0StP, A0FrP
195
196	! Load Arg1 with the pointer to storage for the return type
197	! This was stored in Arg5
198
199	getd	D1Ar1, [A0FrP+#-20]
200
201	! Load D0Ar2 with the return type code. This was stored in Arg4 (flags)
202
203	getd	D0Ar2, [A0FrP+#-16]
204
205	! We are ready to start processing the return value
206	! D0Re0 (and D1Re0) hold the return value
207
208	! If the return value is NULL, assume no return value
209	cmp	D1Ar1, #0
210	beq	LSYM(Lepilogue)
211
212	! return INT
213	cmp		D0Ar2, #FFI_TYPE_INT
214	! Sadly, there is no setd{cc} instruction so we need to workaround that
215	bne	.INT64
216	setd	[D1Ar1], D0Re0
217	b	LSYM(Lepilogue)
218
219	! return INT64
220.INT64:
221	cmp	D0Ar2, #FFI_TYPE_SINT64
222	setleq	[D1Ar1], D0Re0, D1Re0
223
224	! return DOUBLE
225	cmp	D0Ar2, #FFI_TYPE_DOUBLE
226	setl	[D1AR1++], D0Re0, D1Re0
227
228LSYM(Lepilogue):
229	! At this point, the stack pointer points right after the argument
230	! saved area. We need to restore 4 regs, therefore we need to move
231	! 16 bytes ahead.
232	add     A0StP, A0StP, #16
233	RET_REGS "D1.5, D0.5"
234	RET_ARGS
235	getd	D0Re0, [A0StP]
236	mov     A0FrP, D0FrT
237	swap	D1RtP, PC
238
239.ffi_call_SYSV_end:
240       .size   CNAME(ffi_call_SYSV),.ffi_call_SYSV_end-CNAME(ffi_call_SYSV)
241
242
243/*
244	(called by ffi_metag_trampoline)
245	void ffi_closure_SYSV (ffi_closure*)
246
247	(called by ffi_closure_SYSV)
248	unsigned int FFI_HIDDEN
249	ffi_closure_SYSV_inner (closure,respp, args)
250		ffi_closure *closure;
251		void **respp;
252		void *args;
253*/
254
255METAG_FUNC_START ffi_closure_SYSV
256	! We assume that D1Ar1 holds the address of the
257	! ffi_closure struct. We will use that to fetch the
258	! arguments. The stack pointer points to an empty space
259	! and it is ready to store more data.
260
261	! D1Ar1 is ready
262	! Allocate stack space for return value
263	add A0StP, A0StP, #8
264	! Store it to D0Ar2
265	sub D0Ar2, A0StP, #8
266
267	sub D1Ar3, A0FrP, #4
268
269	! D1Ar3 contains the address of the original D1Ar1 argument
270	! We need to subtract #4 later on
271
272	! Preverve D0Ar2
273	mov D0.5, D0Ar2
274
275#ifdef __PIC__
276	callr D1RtP, CNAME(ffi_closure_SYSV_inner@PLT)
277#else
278	callr D1RtP, CNAME(ffi_closure_SYSV_inner)
279#endif
280
281	! Check the return value and store it to D0.5
282	cmp D0Re0, #FFI_TYPE_INT
283	beq .Lretint
284	cmp D0Re0, #FFI_TYPE_DOUBLE
285	beq .Lretdouble
286.Lclosure_epilogue:
287	sub A0StP, A0StP, #8
288	RET_REGS "D1.5, D0.5"
289	RET_ARGS
290	swap	D1RtP, PC
291
292.Lretint:
293	setd [D0.5], D0Re0
294	b .Lclosure_epilogue
295.Lretdouble:
296	setl [D0.5++], D0Re0, D1Re0
297	b .Lclosure_epilogue
298.ffi_closure_SYSV_end:
299.size CNAME(ffi_closure_SYSV),.ffi_closure_SYSV_end-CNAME(ffi_closure_SYSV)
300
301
302ENTRY(ffi_metag_trampoline)
303	SAVE_ARGS
304	! New frame
305	mov A0FrP, A0StP
306	SAVE_REGS "D1.5, D0.5"
307	mov D0.5, PC
308	! Load D1Ar1 the value of ffi_metag_trampoline
309	getd D1Ar1, [D0.5 + #8]
310	! Jump to ffi_closure_SYSV
311	getd PC, [D0.5 + #12]
312