1/* -----------------------------------------------------------------------
2   o32.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,
18   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20   NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24   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 o32 */
32
33#if defined(FFI_MIPS_O32)
34
35#define callback a0
36#define bytes	 a2
37#define flags	 a3
38
39#define SIZEOF_FRAME	(4 * FFI_SIZEOF_ARG + 2 * FFI_SIZEOF_ARG)
40#define A3_OFF		(SIZEOF_FRAME + 3 * FFI_SIZEOF_ARG)
41#define FP_OFF		(SIZEOF_FRAME - 2 * FFI_SIZEOF_ARG)
42#define RA_OFF		(SIZEOF_FRAME - 1 * FFI_SIZEOF_ARG)
43
44	.abicalls
45	.text
46	.align	2
47	.globl	ffi_call_O32
48	.ent	ffi_call_O32
49ffi_call_O32:
50$LFB0:
51	# Prologue
52	SUBU	$sp, SIZEOF_FRAME	# Frame size
53$LCFI00:
54	REG_S	$fp, FP_OFF($sp)	# Save frame pointer
55$LCFI01:
56	REG_S	ra, RA_OFF($sp)		# Save return address
57$LCFI02:
58	move	$fp, $sp
59
60$LCFI03:
61	move	t9, callback		# callback function pointer
62	REG_S	flags, A3_OFF($fp)	# flags
63
64	# Allocate at least 4 words in the argstack
65	LI	v0, 4 * FFI_SIZEOF_ARG
66	blt	bytes, v0, sixteen
67
68	ADDU	v0, bytes, 7	# make sure it is aligned
69	and	v0, -8		# to an 8 byte boundry
70
71sixteen:
72	SUBU	$sp, v0		# move the stack pointer to reflect the
73				# arg space
74
75	ADDU	a0, $sp, 4 * FFI_SIZEOF_ARG
76
77	jalr	t9
78
79	REG_L	t0, A3_OFF($fp)		# load the flags word
80	SRL	t2, t0, 4		# shift our arg info
81	and     t0, ((1<<4)-1)          # mask out the return type
82
83	ADDU	$sp, 4 * FFI_SIZEOF_ARG		# adjust $sp to new args
84
85#ifndef __mips_soft_float
86	bnez	t0, pass_d			# make it quick for int
87#endif
88	REG_L	a0, 0*FFI_SIZEOF_ARG($sp)	# just go ahead and load the
89	REG_L	a1, 1*FFI_SIZEOF_ARG($sp)	# four regs.
90	REG_L	a2, 2*FFI_SIZEOF_ARG($sp)
91	REG_L	a3, 3*FFI_SIZEOF_ARG($sp)
92	b	call_it
93
94#ifndef __mips_soft_float
95pass_d:
96	bne	t0, FFI_ARGS_D, pass_f
97	l.d	$f12, 0*FFI_SIZEOF_ARG($sp)	# load $fp regs from args
98	REG_L	a2,   2*FFI_SIZEOF_ARG($sp)	# passing a double
99	REG_L	a3,   3*FFI_SIZEOF_ARG($sp)
100	b	call_it
101
102pass_f:
103	bne	t0, FFI_ARGS_F, pass_d_d
104	l.s	$f12, 0*FFI_SIZEOF_ARG($sp)	# load $fp regs from args
105	REG_L	a1,   1*FFI_SIZEOF_ARG($sp)	# passing a float
106	REG_L	a2,   2*FFI_SIZEOF_ARG($sp)
107	REG_L	a3,   3*FFI_SIZEOF_ARG($sp)
108	b	call_it
109
110pass_d_d:
111	bne	t0, FFI_ARGS_DD, pass_f_f
112	l.d	$f12, 0*FFI_SIZEOF_ARG($sp)	# load $fp regs from args
113	l.d	$f14, 2*FFI_SIZEOF_ARG($sp)	# passing two doubles
114	b	call_it
115
116pass_f_f:
117	bne	t0, FFI_ARGS_FF, pass_d_f
118	l.s	$f12, 0*FFI_SIZEOF_ARG($sp)	# load $fp regs from args
119	l.s	$f14, 1*FFI_SIZEOF_ARG($sp)	# passing two floats
120	REG_L	a2,   2*FFI_SIZEOF_ARG($sp)
121	REG_L	a3,   3*FFI_SIZEOF_ARG($sp)
122	b	call_it
123
124pass_d_f:
125	bne	t0, FFI_ARGS_DF, pass_f_d
126	l.d	$f12, 0*FFI_SIZEOF_ARG($sp)	# load $fp regs from args
127	l.s	$f14, 2*FFI_SIZEOF_ARG($sp)	# passing double and float
128	REG_L	a3,   3*FFI_SIZEOF_ARG($sp)
129	b	call_it
130
131pass_f_d:
132 # assume that the only other combination must be float then double
133 #	bne	t0, FFI_ARGS_F_D, call_it
134	l.s	$f12, 0*FFI_SIZEOF_ARG($sp)	# load $fp regs from args
135	l.d	$f14, 2*FFI_SIZEOF_ARG($sp)	# passing double and float
136#endif
137
138call_it:
139	# Load the static chain pointer
140	REG_L	t7, SIZEOF_FRAME + 6*FFI_SIZEOF_ARG($fp)
141
142	# Load the function pointer
143	REG_L	t9, SIZEOF_FRAME + 5*FFI_SIZEOF_ARG($fp)
144
145	# If the return value pointer is NULL, assume no return value.
146	REG_L	t1, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
147	beqz	t1, noretval
148
149	bne     t2, FFI_TYPE_INT, retlonglong
150	jalr	t9
151	REG_L	t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
152	REG_S	v0, 0(t0)
153	b	epilogue
154
155retlonglong:
156	# Really any 64-bit int, signed or not.
157	bne	t2, FFI_TYPE_UINT64, retfloat
158	jalr	t9
159	REG_L	t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
160	REG_S	v1, 4(t0)
161	REG_S	v0, 0(t0)
162	b	epilogue
163
164retfloat:
165	bne     t2, FFI_TYPE_FLOAT, retdouble
166	jalr	t9
167	REG_L	t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
168#ifndef __mips_soft_float
169	s.s	$f0, 0(t0)
170#else
171	REG_S	v0, 0(t0)
172#endif
173	b	epilogue
174
175retdouble:
176	bne	t2, FFI_TYPE_DOUBLE, noretval
177	jalr	t9
178	REG_L	t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
179#ifndef __mips_soft_float
180	s.d	$f0, 0(t0)
181#else
182	REG_S	v1, 4(t0)
183	REG_S	v0, 0(t0)
184#endif
185	b	epilogue
186
187noretval:
188	jalr	t9
189
190	# Epilogue
191epilogue:
192	move	$sp, $fp
193	REG_L	$fp, FP_OFF($sp)	# Restore frame pointer
194	REG_L	ra, RA_OFF($sp)		# Restore return address
195	ADDU	$sp, SIZEOF_FRAME	# Fix stack pointer
196	j	ra
197
198$LFE0:
199	.end	ffi_call_O32
200
201
202/* ffi_closure_O32. Expects address of the passed-in ffi_closure
203	in t4 ($12). Stores any arguments passed in registers onto the
204	stack, then calls ffi_closure_mips_inner_O32, which
205	then decodes them.
206
207	Stack layout:
208
209	 3 - a3 save
210	 2 - a2 save
211	 1 - a1 save
212	 0 - a0 save, original sp
213	-1 - ra save
214	-2 - fp save
215	-3 - $16 (s0) save
216	-4 - cprestore
217	-5 - return value high (v1)
218	-6 - return value low (v0)
219	-7 - f14 (le high, be low)
220	-8 - f14 (le low, be high)
221	-9 - f12 (le high, be low)
222       -10 - f12 (le low, be high)
223       -11 - Called function a5 save
224       -12 - Called function a4 save
225       -13 - Called function a3 save
226       -14 - Called function a2 save
227       -15 - Called function a1 save
228       -16 - Called function a0 save, our sp and fp point here
229	 */
230
231#define SIZEOF_FRAME2	(16 * FFI_SIZEOF_ARG)
232#define A3_OFF2		(SIZEOF_FRAME2 + 3 * FFI_SIZEOF_ARG)
233#define A2_OFF2		(SIZEOF_FRAME2 + 2 * FFI_SIZEOF_ARG)
234#define A1_OFF2		(SIZEOF_FRAME2 + 1 * FFI_SIZEOF_ARG)
235#define A0_OFF2		(SIZEOF_FRAME2 + 0 * FFI_SIZEOF_ARG)
236#define RA_OFF2		(SIZEOF_FRAME2 - 1 * FFI_SIZEOF_ARG)
237#define FP_OFF2		(SIZEOF_FRAME2 - 2 * FFI_SIZEOF_ARG)
238#define S0_OFF2		(SIZEOF_FRAME2 - 3 * FFI_SIZEOF_ARG)
239#define GP_OFF2		(SIZEOF_FRAME2 - 4 * FFI_SIZEOF_ARG)
240#define V1_OFF2		(SIZEOF_FRAME2 - 5 * FFI_SIZEOF_ARG)
241#define V0_OFF2		(SIZEOF_FRAME2 - 6 * FFI_SIZEOF_ARG)
242#define FA_1_1_OFF2	(SIZEOF_FRAME2 - 7 * FFI_SIZEOF_ARG)
243#define FA_1_0_OFF2	(SIZEOF_FRAME2 - 8 * FFI_SIZEOF_ARG)
244#define FA_0_1_OFF2	(SIZEOF_FRAME2 - 9 * FFI_SIZEOF_ARG)
245#define FA_0_0_OFF2	(SIZEOF_FRAME2 - 10 * FFI_SIZEOF_ARG)
246#define CALLED_A5_OFF2  (SIZEOF_FRAME2 - 11 * FFI_SIZEOF_ARG)
247#define CALLED_A4_OFF2  (SIZEOF_FRAME2 - 12 * FFI_SIZEOF_ARG)
248
249	.text
250
251	.align	2
252	.globl	ffi_go_closure_O32
253	.ent	ffi_go_closure_O32
254ffi_go_closure_O32:
255$LFB1:
256	# Prologue
257	.frame	$fp, SIZEOF_FRAME2, ra
258	.set	noreorder
259	.cpload	t9
260	.set	reorder
261	SUBU	$sp, SIZEOF_FRAME2
262	.cprestore GP_OFF2
263$LCFI10:
264
265	REG_S	$16, S0_OFF2($sp)	 # Save s0
266	REG_S	$fp, FP_OFF2($sp)	 # Save frame pointer
267	REG_S	ra, RA_OFF2($sp)	 # Save return address
268$LCFI11:
269
270	move	$fp, $sp
271$LCFI12:
272
273	REG_S	a0, A0_OFF2($fp)
274	REG_S	a1, A1_OFF2($fp)
275	REG_S	a2, A2_OFF2($fp)
276	REG_S	a3, A3_OFF2($fp)
277
278	# Load ABI enum to s0
279	REG_L	$16, 4($15)	# cif
280	REG_L	$16, 0($16)	# abi is first member.
281
282	li	$13, 1		# FFI_O32
283	bne	$16, $13, 1f	# Skip fp save if FFI_O32_SOFT_FLOAT
284
285	# Store all possible float/double registers.
286	s.d	$f12, FA_0_0_OFF2($fp)
287	s.d	$f14, FA_1_0_OFF2($fp)
2881:
289	# prepare arguments for ffi_closure_mips_inner_O32
290	REG_L	a0, 4($15)	 # cif
291	REG_L	a1, 8($15)	 # fun
292	move	a2, $15		 # user_data = go closure
293	addu	a3, $fp, V0_OFF2 # rvalue
294
295	addu	t9, $fp, A0_OFF2 # ar
296	REG_S   t9, CALLED_A4_OFF2($fp)
297
298	addu	t9, $fp, FA_0_0_OFF2 #fpr
299	REG_S   t9, CALLED_A5_OFF2($fp)
300
301	b $do_closure
302
303$LFE1:
304	.end ffi_go_closure_O32
305
306	.align	2
307	.globl	ffi_closure_O32
308	.ent	ffi_closure_O32
309ffi_closure_O32:
310$LFB2:
311	# Prologue
312	.frame	$fp, SIZEOF_FRAME2, ra
313	.set	noreorder
314	.cpload	t9
315	.set	reorder
316	SUBU	$sp, SIZEOF_FRAME2
317	.cprestore GP_OFF2
318$LCFI20:
319	REG_S	$16, S0_OFF2($sp)	 # Save s0
320	REG_S	$fp, FP_OFF2($sp)	 # Save frame pointer
321	REG_S	ra, RA_OFF2($sp)	 # Save return address
322$LCFI21:
323	move	$fp, $sp
324
325$LCFI22:
326	# Store all possible argument registers. If there are more than
327	# four arguments, then they are stored above where we put a3.
328	REG_S	a0, A0_OFF2($fp)
329	REG_S	a1, A1_OFF2($fp)
330	REG_S	a2, A2_OFF2($fp)
331	REG_S	a3, A3_OFF2($fp)
332
333	# Load ABI enum to s0
334	REG_L	$16, 20($12)	# cif pointer follows tramp.
335	REG_L	$16, 0($16)	# abi is first member.
336
337	li	$13, 1		# FFI_O32
338	bne	$16, $13, 1f	# Skip fp save if FFI_O32_SOFT_FLOAT
339
340#ifndef __mips_soft_float
341	# Store all possible float/double registers.
342	s.d	$f12, FA_0_0_OFF2($fp)
343	s.d	$f14, FA_1_0_OFF2($fp)
344#endif
3451:
346	# prepare arguments for ffi_closure_mips_inner_O32
347	REG_L	a0, 20($12)	 # cif pointer follows tramp.
348	REG_L	a1, 24($12)	 # fun
349	REG_L	a2, 28($12)	 # user_data
350	addu	a3, $fp, V0_OFF2 # rvalue
351
352	addu	t9, $fp, A0_OFF2 # ar
353	REG_S   t9, CALLED_A4_OFF2($fp)
354
355	addu	t9, $fp, FA_0_0_OFF2 #fpr
356	REG_S   t9, CALLED_A5_OFF2($fp)
357
358$do_closure:
359	la	t9, ffi_closure_mips_inner_O32
360	# Call ffi_closure_mips_inner_O32 to do the work.
361	jalr	t9
362
363	# Load the return value into the appropriate register.
364	move	$8, $2
365	li	$9, FFI_TYPE_VOID
366	beq	$8, $9, closure_done
367
368	li	$13, 1		# FFI_O32
369	bne	$16, $13, 1f	# Skip fp restore if FFI_O32_SOFT_FLOAT
370
371#ifndef __mips_soft_float
372	li	$9, FFI_TYPE_FLOAT
373	l.s	$f0, V0_OFF2($fp)
374	beq	$8, $9, closure_done
375
376	li	$9, FFI_TYPE_DOUBLE
377	l.d	$f0, V0_OFF2($fp)
378	beq	$8, $9, closure_done
379#endif
3801:
381	REG_L	$3, V1_OFF2($fp)
382	REG_L	$2, V0_OFF2($fp)
383
384closure_done:
385	# Epilogue
386	move	$sp, $fp
387	REG_L	$16, S0_OFF2($sp)	 # Restore s0
388	REG_L	$fp, FP_OFF2($sp)	 # Restore frame pointer
389	REG_L	ra,  RA_OFF2($sp)	 # Restore return address
390	ADDU	$sp, SIZEOF_FRAME2
391	j	ra
392$LFE2:
393	.end	ffi_closure_O32
394
395/* DWARF-2 unwind info. */
396
397	.section	.eh_frame,"a",@progbits
398$Lframe0:
399	.4byte	$LECIE0-$LSCIE0	 # Length of Common Information Entry
400$LSCIE0:
401	.4byte	0x0	 # CIE Identifier Tag
402	.byte	0x1	 # CIE Version
403	.ascii "zR\0"	 # CIE Augmentation
404	.uleb128 0x1	 # CIE Code Alignment Factor
405	.sleb128 4	 # CIE Data Alignment Factor
406	.byte	0x1f	 # CIE RA Column
407	.uleb128 0x1	 # Augmentation size
408	.byte	0x00	 # FDE Encoding (absptr)
409	.byte	0xc	 # DW_CFA_def_cfa
410	.uleb128 0x1d
411	.uleb128 0x0
412	.align	2
413$LECIE0:
414
415$LSFDE0:
416	.4byte	$LEFDE0-$LASFDE0	 # FDE Length
417$LASFDE0:
418	.4byte	$LASFDE0-$Lframe0	 # FDE CIE offset
419	.4byte	$LFB0	 # FDE initial location
420	.4byte	$LFE0-$LFB0	 # FDE address range
421	.uleb128 0x0	 # Augmentation size
422	.byte	0x4	 # DW_CFA_advance_loc4
423	.4byte	$LCFI00-$LFB0
424	.byte	0xe	 # DW_CFA_def_cfa_offset
425	.uleb128 0x18
426	.byte	0x4	 # DW_CFA_advance_loc4
427	.4byte	$LCFI01-$LCFI00
428	.byte	0x11	 # DW_CFA_offset_extended_sf
429	.uleb128 0x1e	 # $fp
430	.sleb128 -2	 # SIZEOF_FRAME2 - 2*FFI_SIZEOF_ARG($sp)
431	.byte	0x11	 # DW_CFA_offset_extended_sf
432	.uleb128 0x1f	 # $ra
433	.sleb128 -1	 # SIZEOF_FRAME2 - 1*FFI_SIZEOF_ARG($sp)
434	.byte	0x4	 # DW_CFA_advance_loc4
435	.4byte	$LCFI02-$LCFI01
436	.byte	0xc	 # DW_CFA_def_cfa
437	.uleb128 0x1e
438	.uleb128 0x18
439	.align	2
440$LEFDE0:
441
442$LSFDE1:
443	.4byte	$LEFDE1-$LASFDE1	 # FDE Length
444$LASFDE1:
445	.4byte	$LASFDE1-$Lframe0	 # FDE CIE offset
446	.4byte	$LFB1	 # FDE initial location
447	.4byte	$LFE1-$LFB1	 # FDE address range
448	.uleb128 0x0	 # Augmentation size
449	.byte	0x4	 # DW_CFA_advance_loc4
450	.4byte	$LCFI10-$LFB1
451	.byte	0xe	 # DW_CFA_def_cfa_offset
452	.uleb128 SIZEOF_FRAME2
453	.byte	0x4	 # DW_CFA_advance_loc4
454	.4byte	$LCFI11-$LCFI10
455	.byte	0x11	 # DW_CFA_offset_extended_sf
456	.uleb128 0x10	 # $16
457	.sleb128 -3	 # SIZEOF_FRAME2 - 3*FFI_SIZEOF_ARG($sp)
458	.byte	0x11	 # DW_CFA_offset_extended_sf
459	.uleb128 0x1e	 # $fp
460	.sleb128 -2	 # SIZEOF_FRAME2 - 2*FFI_SIZEOF_ARG($sp)
461	.byte	0x11	 # DW_CFA_offset_extended_sf
462	.uleb128 0x1f	 # $ra
463	.sleb128 -1	 # SIZEOF_FRAME2 - 1*FFI_SIZEOF_ARG($sp)
464	.byte	0x4	 # DW_CFA_advance_loc4
465	.4byte	$LCFI12-$LCFI11
466	.byte	0xc	 # DW_CFA_def_cfa
467	.uleb128 0x1e
468	.uleb128 SIZEOF_FRAME2
469	.align	2
470$LEFDE1:
471
472$LSFDE2:
473	.4byte	$LEFDE2-$LASFDE2	 # FDE Length
474$LASFDE2:
475	.4byte	$LASFDE2-$Lframe0	 # FDE CIE offset
476	.4byte	$LFB2	 # FDE initial location
477	.4byte	$LFE2-$LFB2	 # FDE address range
478	.uleb128 0x0	 # Augmentation size
479	.byte	0x4	 # DW_CFA_advance_loc4
480	.4byte	$LCFI20-$LFB2
481	.byte	0xe	 # DW_CFA_def_cfa_offset
482	.uleb128 SIZEOF_FRAME2
483	.byte	0x4	 # DW_CFA_advance_loc4
484	.4byte	$LCFI21-$LCFI20
485	.byte	0x11	 # DW_CFA_offset_extended_sf
486	.uleb128 0x10	 # $16
487	.sleb128 -3	 # SIZEOF_FRAME2 - 3*FFI_SIZEOF_ARG($sp)
488	.byte	0x11	 # DW_CFA_offset_extended_sf
489	.uleb128 0x1e	 # $fp
490	.sleb128 -2	 # SIZEOF_FRAME2 - 2*FFI_SIZEOF_ARG($sp)
491	.byte	0x11	 # DW_CFA_offset_extended_sf
492	.uleb128 0x1f	 # $ra
493	.sleb128 -1	 # SIZEOF_FRAME2 - 1*FFI_SIZEOF_ARG($sp)
494	.byte	0x4	 # DW_CFA_advance_loc4
495	.4byte	$LCFI22-$LCFI21
496	.byte	0xc	 # DW_CFA_def_cfa
497	.uleb128 0x1e
498	.uleb128 SIZEOF_FRAME2
499	.align	2
500$LEFDE2:
501
502#endif
503