1/* -----------------------------------------------------------------------
2   n32.S - Copyright (c) 1996, 1998, 2005  Red Hat, Inc.
3
4   MIPS 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   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
18   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
21	ANY CLAIM, DAMAGES OR
22   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24   OTHER DEALINGS IN THE SOFTWARE.
25   ----------------------------------------------------------------------- */
26
27#define LIBFFI_ASM
28#include <fficonfig.h>
29#include <ffi.h>
30
31/* Only build this code if we are compiling for n32 */
32
33#if defined(FFI_MIPS_N32)
34
35#define callback a0
36#define bytes	 a2
37#define flags	 a3
38#define raddr    a4
39#define fn       a5
40
41#define SIZEOF_FRAME	( 8 * FFI_SIZEOF_ARG )
42
43	.abicalls
44	.text
45	.align	2
46	.globl	ffi_call_N32
47	.ent	ffi_call_N32
48ffi_call_N32:
49.LFB3:
50	.frame	$fp, SIZEOF_FRAME, ra
51	.mask	0xc0000000,-FFI_SIZEOF_ARG
52	.fmask	0x00000000,0
53
54	# Prologue
55	SUBU	$sp, SIZEOF_FRAME			# Frame size
56.LCFI0:
57	REG_S	$fp, SIZEOF_FRAME - 2*FFI_SIZEOF_ARG($sp)	# Save frame pointer
58	REG_S	ra, SIZEOF_FRAME - 1*FFI_SIZEOF_ARG($sp)	# Save return address
59.LCFI1:
60	move	$fp, $sp
61.LCFI3:
62	move	t9, callback	# callback function pointer
63	REG_S	bytes, 2*FFI_SIZEOF_ARG($fp) # bytes
64	REG_S	flags, 3*FFI_SIZEOF_ARG($fp) # flags
65	REG_S	raddr, 4*FFI_SIZEOF_ARG($fp) # raddr
66	REG_S	fn,    5*FFI_SIZEOF_ARG($fp) # fn
67
68	# Allocate at least 4 words in the argstack
69	move	v0, bytes
70	bge	bytes, 4 * FFI_SIZEOF_ARG, bigger
71	LI	v0, 4 * FFI_SIZEOF_ARG
72	b	sixteen
73
74	bigger:
75	ADDU	t4, v0, 2 * FFI_SIZEOF_ARG -1	# make sure it is aligned
76	and	v0, t4, -2 * FFI_SIZEOF_ARG		# to a proper boundry.
77
78sixteen:
79	SUBU	$sp, $sp, v0	# move the stack pointer to reflect the
80				# arg space
81
82	move	a0, $sp         # 4 * FFI_SIZEOF_ARG
83	ADDU	a3, $fp, 3 * FFI_SIZEOF_ARG
84
85	# Call ffi_prep_args
86	jal	t9
87
88	# Copy the stack pointer to t9
89	move	t9, $sp
90
91	# Fix the stack if there are more than 8 64bit slots worth
92	# of arguments.
93
94	# Load the number of bytes
95	REG_L	t6, 2*FFI_SIZEOF_ARG($fp)
96
97	# Is it bigger than 8 * FFI_SIZEOF_ARG?
98	daddiu	t8, t6, -(8 * FFI_SIZEOF_ARG)
99	bltz	t8, loadregs
100
101	ADDU	t9, t9, t8
102
103loadregs:
104
105	REG_L	t6, 3*FFI_SIZEOF_ARG($fp)  # load the flags word into t6.
106
107	and	t4, t6, ((1<<FFI_FLAG_BITS)-1)
108	bnez	t4, arg1_floatp
109	REG_L	a0, 0*FFI_SIZEOF_ARG(t9)
110	b	arg1_next
111arg1_floatp:
112	bne	t4, FFI_TYPE_FLOAT, arg1_doublep
113	l.s	$f12, 0*FFI_SIZEOF_ARG(t9)
114	b	arg1_next
115arg1_doublep:
116	l.d	$f12, 0*FFI_SIZEOF_ARG(t9)
117arg1_next:
118
119	SRL	t4, t6, 1*FFI_FLAG_BITS
120	and	t4, ((1<<FFI_FLAG_BITS)-1)
121	bnez	t4, arg2_floatp
122	REG_L	a1, 1*FFI_SIZEOF_ARG(t9)
123	b	arg2_next
124arg2_floatp:
125	bne	t4, FFI_TYPE_FLOAT, arg2_doublep
126	l.s	$f13, 1*FFI_SIZEOF_ARG(t9)
127	b	arg2_next
128arg2_doublep:
129	l.d	$f13, 1*FFI_SIZEOF_ARG(t9)
130arg2_next:
131
132	SRL	t4, t6, 2*FFI_FLAG_BITS
133	and	t4, ((1<<FFI_FLAG_BITS)-1)
134	bnez	t4, arg3_floatp
135	REG_L	a2, 2*FFI_SIZEOF_ARG(t9)
136	b	arg3_next
137arg3_floatp:
138	bne	t4, FFI_TYPE_FLOAT, arg3_doublep
139	l.s	$f14, 2*FFI_SIZEOF_ARG(t9)
140	b	arg3_next
141arg3_doublep:
142	l.d	$f14, 2*FFI_SIZEOF_ARG(t9)
143arg3_next:
144
145	SRL	t4, t6, 3*FFI_FLAG_BITS
146	and	t4, ((1<<FFI_FLAG_BITS)-1)
147	bnez	t4, arg4_floatp
148	REG_L	a3, 3*FFI_SIZEOF_ARG(t9)
149	b	arg4_next
150arg4_floatp:
151	bne	t4, FFI_TYPE_FLOAT, arg4_doublep
152	l.s	$f15, 3*FFI_SIZEOF_ARG(t9)
153	b	arg4_next
154arg4_doublep:
155	l.d	$f15, 3*FFI_SIZEOF_ARG(t9)
156arg4_next:
157
158	SRL	t4, t6, 4*FFI_FLAG_BITS
159	and	t4, ((1<<FFI_FLAG_BITS)-1)
160	bnez	t4, arg5_floatp
161	REG_L	a4, 4*FFI_SIZEOF_ARG(t9)
162	b	arg5_next
163arg5_floatp:
164	bne	t4, FFI_TYPE_FLOAT, arg5_doublep
165	l.s	$f16, 4*FFI_SIZEOF_ARG(t9)
166	b	arg5_next
167arg5_doublep:
168	l.d	$f16, 4*FFI_SIZEOF_ARG(t9)
169arg5_next:
170
171	SRL	t4, t6, 5*FFI_FLAG_BITS
172	and	t4, ((1<<FFI_FLAG_BITS)-1)
173	bnez	t4, arg6_floatp
174	REG_L	a5, 5*FFI_SIZEOF_ARG(t9)
175	b	arg6_next
176arg6_floatp:
177	bne	t4, FFI_TYPE_FLOAT, arg6_doublep
178	l.s	$f17, 5*FFI_SIZEOF_ARG(t9)
179	b	arg6_next
180arg6_doublep:
181	l.d	$f17, 5*FFI_SIZEOF_ARG(t9)
182arg6_next:
183
184	SRL	t4, t6, 6*FFI_FLAG_BITS
185	and	t4, ((1<<FFI_FLAG_BITS)-1)
186	bnez	t4, arg7_floatp
187	REG_L	a6, 6*FFI_SIZEOF_ARG(t9)
188	b	arg7_next
189arg7_floatp:
190	bne	t4, FFI_TYPE_FLOAT, arg7_doublep
191	l.s	$f18, 6*FFI_SIZEOF_ARG(t9)
192	b	arg7_next
193arg7_doublep:
194	l.d	$f18, 6*FFI_SIZEOF_ARG(t9)
195arg7_next:
196
197	SRL	t4, t6, 7*FFI_FLAG_BITS
198	and	t4, ((1<<FFI_FLAG_BITS)-1)
199	bnez	t4, arg8_floatp
200	REG_L	a7, 7*FFI_SIZEOF_ARG(t9)
201	b	arg8_next
202arg8_floatp:
203	bne	t4, FFI_TYPE_FLOAT, arg8_doublep
204 	l.s	$f19, 7*FFI_SIZEOF_ARG(t9)
205	b	arg8_next
206arg8_doublep:
207 	l.d	$f19, 7*FFI_SIZEOF_ARG(t9)
208arg8_next:
209
210callit:
211	# Load the function pointer
212	REG_L	t9, 5*FFI_SIZEOF_ARG($fp)
213
214	# If the return value pointer is NULL, assume no return value.
215	REG_L	t5, 4*FFI_SIZEOF_ARG($fp)
216	beqz	t5, noretval
217
218	# Shift the return type flag over
219	SRL	t6, 8*FFI_FLAG_BITS
220
221	bne     t6, FFI_TYPE_INT, retfloat
222	jal	t9
223	REG_L	t4, 4*FFI_SIZEOF_ARG($fp)
224	REG_S	v0, 0(t4)
225	b	epilogue
226
227retfloat:
228	bne     t6, FFI_TYPE_FLOAT, retdouble
229	jal	t9
230	REG_L	t4, 4*FFI_SIZEOF_ARG($fp)
231	s.s	$f0, 0(t4)
232	b	epilogue
233
234retdouble:
235	bne	t6, FFI_TYPE_DOUBLE, retstruct_d
236	jal	t9
237	REG_L	t4, 4*FFI_SIZEOF_ARG($fp)
238	s.d	$f0, 0(t4)
239	b	epilogue
240
241retstruct_d:
242	bne	t6, FFI_TYPE_STRUCT_D, retstruct_f
243	jal	t9
244	REG_L	t4, 4*FFI_SIZEOF_ARG($fp)
245	s.d	$f0, 0(t4)
246	b	epilogue
247
248retstruct_f:
249	bne	t6, FFI_TYPE_STRUCT_F, retstruct_d_d
250	jal	t9
251	REG_L	t4, 4*FFI_SIZEOF_ARG($fp)
252	s.s	$f0, 0(t4)
253	b	epilogue
254
255retstruct_d_d:
256	bne	t6, FFI_TYPE_STRUCT_DD, retstruct_f_f
257	jal	t9
258	REG_L	t4, 4*FFI_SIZEOF_ARG($fp)
259	s.d	$f0, 0(t4)
260	s.d	$f2, 8(t4)
261	b	epilogue
262
263retstruct_f_f:
264	bne	t6, FFI_TYPE_STRUCT_FF, retstruct_d_f
265	jal	t9
266	REG_L	t4, 4*FFI_SIZEOF_ARG($fp)
267	s.s	$f0, 0(t4)
268	s.s	$f2, 4(t4)
269	b	epilogue
270
271retstruct_d_f:
272	bne	t6, FFI_TYPE_STRUCT_DF, retstruct_f_d
273	jal	t9
274	REG_L	t4, 4*FFI_SIZEOF_ARG($fp)
275	s.d	$f0, 0(t4)
276	s.s	$f2, 8(t4)
277	b	epilogue
278
279retstruct_f_d:
280	bne	t6, FFI_TYPE_STRUCT_FD, retstruct_small
281	jal	t9
282	REG_L	t4, 4*FFI_SIZEOF_ARG($fp)
283	s.s	$f0, 0(t4)
284	s.d	$f2, 8(t4)
285	b	epilogue
286
287retstruct_small:
288	bne	t6, FFI_TYPE_STRUCT_SMALL, retstruct_small2
289	jal	t9
290	REG_L	t4, 4*FFI_SIZEOF_ARG($fp)
291	REG_S	v0, 0(t4)
292	b	epilogue
293
294retstruct_small2:
295	bne	t6, FFI_TYPE_STRUCT_SMALL2, retstruct
296	jal	t9
297	REG_L	t4, 4*FFI_SIZEOF_ARG($fp)
298	REG_S	v0, 0(t4)
299	REG_S	v1, 8(t4)
300	b	epilogue
301
302retstruct:
303noretval:
304	jal	t9
305
306	# Epilogue
307epilogue:
308	move	$sp, $fp
309	REG_L	$fp, SIZEOF_FRAME - 2*FFI_SIZEOF_ARG($sp) # Restore frame pointer
310	REG_L	ra, SIZEOF_FRAME - 1*FFI_SIZEOF_ARG($sp)  # Restore return address
311	ADDU	$sp, SIZEOF_FRAME		      # Fix stack pointer
312	j	ra
313
314.LFE3:
315	.end	ffi_call_N32
316
317/* ffi_closure_N32. Expects address of the passed-in ffi_closure in t0
318   ($12). Stores any arguments passed in registers onto the stack,
319   then calls ffi_closure_mips_inner_N32, which then decodes
320   them.
321
322	Stack layout:
323
324	20 - Start of parameters, original sp
325	19 - Called function a7 save
326	18 - Called function a6 save
327	17 - Called function a5 save
328	16 - Called function a4 save
329	15 - Called function a3 save
330	14 - Called function a2 save
331	13 - Called function a1 save
332	12 - Called function a0 save
333	11 - Called function f19
334	10 - Called function f18
335	 9 - Called function f17
336	 8 - Called function f16
337	 7 - Called function f15
338         6 - Called function f14
339         5 - Called function f13
340         4 - Called function f12
341	 3 - return value high (v1 or $f2)
342	 2 - return value low (v0 or $f0)
343	 1 - ra save
344	 0 - gp save our sp  points here
345	 */
346
347#define SIZEOF_FRAME2	(20 * FFI_SIZEOF_ARG)
348
349#define A7_OFF2		(19 * FFI_SIZEOF_ARG)
350#define A6_OFF2		(18 * FFI_SIZEOF_ARG)
351#define A5_OFF2		(17 * FFI_SIZEOF_ARG)
352#define A4_OFF2		(16 * FFI_SIZEOF_ARG)
353#define A3_OFF2		(15 * FFI_SIZEOF_ARG)
354#define A2_OFF2		(14 * FFI_SIZEOF_ARG)
355#define A1_OFF2		(13 * FFI_SIZEOF_ARG)
356#define A0_OFF2		(12 * FFI_SIZEOF_ARG)
357
358#define F19_OFF2	(11 * FFI_SIZEOF_ARG)
359#define F18_OFF2	(10 * FFI_SIZEOF_ARG)
360#define F17_OFF2	(9  * FFI_SIZEOF_ARG)
361#define F16_OFF2	(8  * FFI_SIZEOF_ARG)
362#define F15_OFF2	(7  * FFI_SIZEOF_ARG)
363#define F14_OFF2	(6  * FFI_SIZEOF_ARG)
364#define F13_OFF2	(5  * FFI_SIZEOF_ARG)
365#define F12_OFF2	(4  * FFI_SIZEOF_ARG)
366
367#define V1_OFF2		(3  * FFI_SIZEOF_ARG)
368#define V0_OFF2		(2  * FFI_SIZEOF_ARG)
369
370#define RA_OFF2		(1  * FFI_SIZEOF_ARG)
371#define GP_OFF2		(0  * FFI_SIZEOF_ARG)
372
373	.align	2
374	.globl	ffi_closure_N32
375	.ent	ffi_closure_N32
376ffi_closure_N32:
377.LFB2:
378	.frame	$sp, SIZEOF_FRAME2, ra
379	.mask	0x90000000,-(SIZEOF_FRAME2 - RA_OFF2)
380	.fmask	0x00000000,0
381	SUBU	$sp, SIZEOF_FRAME2
382.LCFI5:
383	.cpsetup t9, GP_OFF2, ffi_closure_N32
384	REG_S	ra, RA_OFF2($sp)	# Save return address
385.LCFI6:
386	# Store all possible argument registers. If there are more than
387	# fit in registers, then they were stored on the stack.
388	REG_S	a0, A0_OFF2($sp)
389	REG_S	a1, A1_OFF2($sp)
390	REG_S	a2, A2_OFF2($sp)
391	REG_S	a3, A3_OFF2($sp)
392	REG_S	a4, A4_OFF2($sp)
393	REG_S	a5, A5_OFF2($sp)
394	REG_S	a6, A6_OFF2($sp)
395	REG_S	a7, A7_OFF2($sp)
396
397	# Store all possible float/double registers.
398	s.d	$f12, F12_OFF2($sp)
399	s.d	$f13, F13_OFF2($sp)
400	s.d	$f14, F14_OFF2($sp)
401	s.d	$f15, F15_OFF2($sp)
402	s.d	$f16, F16_OFF2($sp)
403	s.d	$f17, F17_OFF2($sp)
404	s.d	$f18, F18_OFF2($sp)
405	s.d	$f19, F19_OFF2($sp)
406
407	# Call ffi_closure_mips_inner_N32 to do the real work.
408	LA	t9, ffi_closure_mips_inner_N32
409	move	a0, $12	 # Pointer to the ffi_closure
410	ADDU	a1, $sp, V0_OFF2
411	ADDU	a2, $sp, A0_OFF2
412	ADDU	a3, $sp, F12_OFF2
413	jalr	t9
414
415	# Return flags are in v0
416	bne     v0, FFI_TYPE_INT, cls_retfloat
417	REG_L	v0, V0_OFF2($sp)
418	b	cls_epilogue
419
420cls_retfloat:
421	bne     v0, FFI_TYPE_FLOAT, cls_retdouble
422	l.s	$f0, V0_OFF2($sp)
423	b	cls_epilogue
424
425cls_retdouble:
426	bne	v0, FFI_TYPE_DOUBLE, cls_retstruct_d
427	l.d	$f0, V0_OFF2($sp)
428	b	cls_epilogue
429
430cls_retstruct_d:
431	bne	v0, FFI_TYPE_STRUCT_D, cls_retstruct_f
432	l.d	$f0, V0_OFF2($sp)
433	b	cls_epilogue
434
435cls_retstruct_f:
436	bne	v0, FFI_TYPE_STRUCT_F, cls_retstruct_d_d
437	l.s	$f0, V0_OFF2($sp)
438	b	cls_epilogue
439
440cls_retstruct_d_d:
441	bne	v0, FFI_TYPE_STRUCT_DD, cls_retstruct_f_f
442	l.d	$f0, V0_OFF2($sp)
443	l.d	$f2, V1_OFF2($sp)
444	b	cls_epilogue
445
446cls_retstruct_f_f:
447	bne	v0, FFI_TYPE_STRUCT_FF, cls_retstruct_d_f
448	l.s	$f0, V0_OFF2($sp)
449	l.s	$f2, V1_OFF2($sp)
450	b	cls_epilogue
451
452cls_retstruct_d_f:
453	bne	v0, FFI_TYPE_STRUCT_DF, cls_retstruct_f_d
454	l.d	$f0, V0_OFF2($sp)
455	l.s	$f2, V1_OFF2($sp)
456	b	cls_epilogue
457
458cls_retstruct_f_d:
459	bne	v0, FFI_TYPE_STRUCT_FD, cls_retstruct_small2
460	l.s	$f0, V0_OFF2($sp)
461	l.d	$f2, V1_OFF2($sp)
462	b	cls_epilogue
463
464cls_retstruct_small2:
465	REG_L	v0, V0_OFF2($sp)
466	REG_L	v1, V1_OFF2($sp)
467
468	# Epilogue
469cls_epilogue:
470	REG_L	ra,  RA_OFF2($sp)	 # Restore return address
471	.cpreturn
472	ADDU	$sp, SIZEOF_FRAME2
473	j	ra
474.LFE2:
475	.end	ffi_closure_N32
476
477        .section        .eh_frame,"aw",@progbits
478.Lframe1:
479        .4byte  .LECIE1-.LSCIE1		# length
480.LSCIE1:
481        .4byte  0x0			# CIE
482        .byte   0x1			# Version 1
483        .ascii  "\000"			# Augmentation
484        .uleb128 0x1			# Code alignment 1
485        .sleb128 -4			# Data alignment -4
486        .byte   0x1f			# Return Address $31
487        .byte   0xc			# DW_CFA_def_cfa
488        .uleb128 0x1d			# in $sp
489        .uleb128 0x0			# offset 0
490        .align  EH_FRAME_ALIGN
491.LECIE1:
492
493.LSFDE1:
494        .4byte  .LEFDE1-.LASFDE1	# length.
495.LASFDE1:
496        .4byte  .LASFDE1-.Lframe1	# CIE_pointer.
497        FDE_ADDR_BYTES  .LFB3		# initial_location.
498        FDE_ADDR_BYTES  .LFE3-.LFB3	# address_range.
499        .byte   0x4			# DW_CFA_advance_loc4
500        .4byte  .LCFI0-.LFB3		# to .LCFI0
501        .byte   0xe			# DW_CFA_def_cfa_offset
502        .uleb128 SIZEOF_FRAME		# adjust stack.by SIZEOF_FRAME
503        .byte   0x4			# DW_CFA_advance_loc4
504        .4byte  .LCFI1-.LCFI0		# to .LCFI1
505        .byte   0x9e			# DW_CFA_offset of $fp
506        .uleb128 2*FFI_SIZEOF_ARG/4	#
507        .byte   0x9f			# DW_CFA_offset of ra
508        .uleb128 1*FFI_SIZEOF_ARG/4	#
509        .byte   0x4			# DW_CFA_advance_loc4
510        .4byte  .LCFI3-.LCFI1		# to .LCFI3
511        .byte   0xd			# DW_CFA_def_cfa_register
512        .uleb128 0x1e			# in $fp
513        .align  EH_FRAME_ALIGN
514.LEFDE1:
515.LSFDE3:
516	.4byte	.LEFDE3-.LASFDE3	# length
517.LASFDE3:
518	.4byte	.LASFDE3-.Lframe1	# CIE_pointer.
519	FDE_ADDR_BYTES	.LFB2		# initial_location.
520	FDE_ADDR_BYTES	.LFE2-.LFB2	# address_range.
521	.byte	0x4			# DW_CFA_advance_loc4
522	.4byte	.LCFI5-.LFB2		# to .LCFI5
523	.byte	0xe			# DW_CFA_def_cfa_offset
524	.uleb128 SIZEOF_FRAME2		# adjust stack.by SIZEOF_FRAME
525	.byte	0x4			# DW_CFA_advance_loc4
526	.4byte	.LCFI6-.LCFI5		# to .LCFI6
527	.byte	0x9c			# DW_CFA_offset of $gp ($28)
528	.uleb128 (SIZEOF_FRAME2 - GP_OFF2)/4
529	.byte	0x9f			# DW_CFA_offset of ra ($31)
530	.uleb128 (SIZEOF_FRAME2 - RA_OFF2)/4
531	.align	EH_FRAME_ALIGN
532.LEFDE3:
533
534#endif
535