1/*
2 * %CopyrightBegin%
3
4 *
5 * Copyright Ericsson AB 2001-2016. All Rights Reserved.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *     http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * %CopyrightEnd%
20 */
21
22#define ASM
23#include "hipe_sparc_asm.h"
24#include "hipe_literals.h"
25#include "hipe_mode_switch.h"
26
27	.section ".text"
28	.align	4
29
30/*
31 * Enter Erlang from C.
32 * Switch to a new register window.
33 * Create a new frame on the C stack.
34 * Save C return address in the frame.
35 * Retrieve the process pointer from the C argument registers.
36 */
37#define ENTER_FROM_C		\
38	save	%sp, -112, %sp;	\
39	st	%i7, [%sp+96]
40
41/*
42 * Return to the calling C function.
43 * The return value is in %o0.
44 *
45 * .flush_exit saves NSP and other cached P state.
46 * .suspend_exit also saves RA.
47 */
48.suspend_exit:
49	/* save RA, so we can be resumed */
50	st	RA, [P+P_NRA]
51.flush_exit:
52	/* restore C return address (hoisted to avoid stall) */
53	ld	[%sp+96], %i7
54	/* flush cached P state */
55	SAVE_CACHED_STATE
56	/* restore callee-save registers, drop frame, return */
57	jmp	%i7+8		/* ret */
58	restore	%g0, %o0, %o0	/* kills P, moves our %o0 to caller's %o0 */
59
60/*
61 * int hipe_sparc_call_to_native(Process *p);
62 * Emulated code recursively calls native code.
63 */
64	.global	hipe_sparc_call_to_native
65	.type	hipe_sparc_call_to_native, #function
66	.proc	04		/* ??? */
67hipe_sparc_call_to_native:
68	ENTER_FROM_C
69	/* prepare to call the target */
70	ld	[P+P_NCALLEE], TEMP_ARG0
71	/* get argument registers */
72	LOAD_ARG_REGS
73	/* cache some P state in registers */
74	RESTORE_CACHED_STATE
75/* FALLTHROUGH
76 *
77 * We export this return address so that hipe_mode_switch() can discover
78 * when native code tailcalls emulated code.
79 * Note: this is SPARC, so the value in the return address register
80 * is the address of the call/jmpl instruction itself.
81 */
82	.global nbif_return
83nbif_return:
84	/* call the target */
85	jmpl	TEMP_ARG0, RA
86	nop
87/* FALLTHROUGH
88 *
89 * This is where native code returns to emulated code.
90 */
91	st	%o0, [P+P_ARG0]		/* save retval */
92	ba	.flush_exit
93	mov	HIPE_MODE_SWITCH_RES_RETURN, %o0
94
95/*
96 * int hipe_sparc_return_to_native(Process *p);
97 * Emulated code returns to its native code caller.
98 */
99	.global	hipe_sparc_return_to_native
100	.type	hipe_sparc_return_to_native, #function
101	.proc	04		/* ??? */
102hipe_sparc_return_to_native:
103	ENTER_FROM_C
104	/* restore return address */
105	ld	[P+P_NRA], RA
106	/* cache some P state in registers */
107	RESTORE_CACHED_STATE
108	/*
109	 * Return using the current return address.
110	 * The parameters were popped at the original native-to-emulated
111	 * call (hipe_call_from_native_is_recursive), so a plain ret suffices.
112	 */
113	jmp	RA+8
114	ld	[P+P_ARG0], %o0		/* delay slot: get return value */
115
116/*
117 * int hipe_sparc_tailcall_to_native(Process *);
118 * Emulated code tailcalls native code.
119 */
120	.global	hipe_sparc_tailcall_to_native
121	.type	hipe_sparc_tailcall_to_native, #function
122	.proc	04		/* ??? */
123hipe_sparc_tailcall_to_native:
124	ENTER_FROM_C
125	/* prepare to call the target */
126	ld	[P+P_NCALLEE], TEMP_ARG0
127	/* get argument registers */
128	LOAD_ARG_REGS
129	/* cache some P state in registers */
130	RESTORE_CACHED_STATE
131	/* call the target */
132	jmp	TEMP_ARG0
133	ld	[P+P_NRA], RA	/* delay slot: restore return address */
134
135/*
136 * int hipe_sparc_throw_to_native(Process *p);
137 * Emulated code throws an exception to its native code caller.
138 */
139	.align	4
140	.global	hipe_sparc_throw_to_native
141	.type	hipe_sparc_throw_to_native, #function
142	.proc	04		/* ??? */
143hipe_sparc_throw_to_native:
144	ENTER_FROM_C
145	/* prepare to invoke handler */
146	ld	[P+P_NCALLEE], TEMP_ARG0	/* set by hipe_find_handler() */
147	/* cache some P state in registers */
148	RESTORE_CACHED_STATE
149	/* invoke the handler */
150	jmp	TEMP_ARG0
151	nop
152
153/*
154 * Native code calls emulated code via a stub
155 * which should look as follows:
156 *
157 * stub for f/N:
158 *	sethi %hi(f's export entry address), TEMP_ARG0
159 *	mov RA, TEMP_RA		! because the call below clobbers RA (%o7)
160 *	or TEMP_ARG0, %lo(f's export entry address), TEMP_ARG0
161 *	call nbif_callemu	! clobbers RA!
162 *	mov N, TEMP_ARG1	! delay slot: TEMP_ARG1 := ARITY
163 *
164 * XXX. Different stubs for different number of register parameters?
165 */
166	.global nbif_callemu
167nbif_callemu:
168	st	TEMP_ARG0, [P+P_CALLEE_EXP]
169	st	TEMP_ARG1, [P+P_ARITY]
170	st	TEMP_RA, [P+P_NRA]
171	STORE_ARG_REGS
172	ba	.flush_exit
173	mov	HIPE_MODE_SWITCH_RES_CALL_EXPORTED, %o0
174
175/*
176 * nbif_apply
177 */
178	.global	nbif_apply
179nbif_apply:
180	STORE_ARG_REGS
181	ba	.suspend_exit
182	mov	HIPE_MODE_SWITCH_RES_APPLY, %o0
183
184/*
185 * Native code calls an emulated-mode closure via a stub defined below.
186 *
187 * The closure is appended as the last actual parameter, and parameters
188 * beyond the first few passed in registers are pushed onto the stack in
189 * left-to-right order.
190 * Hence, the location of the closure parameter only depends on the number
191 * of parameters in registers, not the total number of parameters.
192 */
193#if NR_ARG_REGS >= 6
194	.global	nbif_ccallemu6
195nbif_ccallemu6:
196	st	ARG5, [P+P_ARG5]
197#if NR_ARG_REGS > 6
198	mov	ARG6, ARG5
199#else
200	ld	[NSP+0], ARG5
201#endif
202	/*FALLTHROUGH*/
203#endif
204
205#if NR_ARG_REGS >= 5
206	.global	nbif_ccallemu5
207nbif_ccallemu5:
208	st	ARG4, [P+P_ARG4]
209#if NR_ARG_REGS > 5
210	mov	ARG5, ARG4
211#else
212	ld	[NSP+0], ARG4
213#endif
214	/*FALLTHROUGH*/
215#endif
216
217#if NR_ARG_REGS >= 4
218	.global	nbif_ccallemu4
219nbif_ccallemu4:
220	st	ARG3, [P+P_ARG3]
221#if NR_ARG_REGS > 4
222	mov	ARG4, ARG3
223#else
224	ld	[NSP+0], ARG3
225#endif
226	/*FALLTHROUGH*/
227#endif
228
229#if NR_ARG_REGS >= 3
230	.global	nbif_ccallemu3
231nbif_ccallemu3:
232	st	ARG2, [P+P_ARG2]
233#if NR_ARG_REGS > 3
234	mov	ARG3, ARG2
235#else
236	ld	[NSP+0], ARG2
237#endif
238	/*FALLTHROUGH*/
239#endif
240
241#if NR_ARG_REGS >= 2
242	.global	nbif_ccallemu2
243nbif_ccallemu2:
244	st	ARG1, [P+P_ARG1]
245#if NR_ARG_REGS > 2
246	mov	ARG2, ARG1
247#else
248	ld	[NSP+0], ARG1
249#endif
250	/*FALLTHROUGH*/
251#endif
252
253#if NR_ARG_REGS >= 1
254	.global	nbif_ccallemu1
255nbif_ccallemu1:
256	st	ARG0, [P+P_ARG0]
257#if NR_ARG_REGS > 1
258	mov	ARG1, ARG0
259#else
260	ld	[NSP+0], ARG0
261#endif
262	/*FALLTHROUGH*/
263#endif
264
265	.global	nbif_ccallemu0
266nbif_ccallemu0:
267	/* We use %o1 not ARG0 here because ARG0 is not
268	   defined when NR_ARG_REGS == 0. */
269#if NR_ARG_REGS == 0
270	ld	[NSP+0], %o1		/* get the closure */
271#endif
272	st	%o1, [P+P_CLOSURE]	/* save the closure */
273	ba	.suspend_exit
274	mov	HIPE_MODE_SWITCH_RES_CALL_CLOSURE, %o0
275
276/*
277 * This is where native code suspends.
278 */
279	.global nbif_suspend_0
280nbif_suspend_0:
281	ba	.suspend_exit
282	mov	HIPE_MODE_SWITCH_RES_SUSPEND, %o0
283
284/*
285 * Suspend from a receive (waiting for a message)
286 */
287	.global nbif_suspend_msg
288nbif_suspend_msg:
289	ba	.suspend_exit
290	mov	HIPE_MODE_SWITCH_RES_WAIT, %o0
291
292/*
293 * Suspend from a receive with a timeout (waiting for a message)
294 *	if (!(p->flags & F_TIMO)) { suspend }
295 *	else { return 0; }
296 */
297	.global nbif_suspend_msg_timeout
298nbif_suspend_msg_timeout:
299	ld	[P+P_FLAGS], %o1
300	/* this relies on F_TIMO (1<<2) fitting in a simm13 */
301	andcc	%o1, F_TIMO, %g0
302	bz,a	.suspend_exit
303	mov	HIPE_MODE_SWITCH_RES_WAIT_TIMEOUT, %o0	/* delay slot */
304	/* timeout has occurred */
305	jmp	RA+8
306	mov	0, %o0
307
308/*
309 * This is the default exception handler for native code.
310 */
311	.global	nbif_fail
312nbif_fail:
313	ba	.flush_exit
314	mov	HIPE_MODE_SWITCH_RES_THROW, %o0
315
316	.global	nbif_0_gc_after_bif
317	.global	nbif_1_gc_after_bif
318	.global	nbif_2_gc_after_bif
319	.global	nbif_3_gc_after_bif
320	.global nbif_4_gc_after_bif
321nbif_0_gc_after_bif:
322	ba	.gc_after_bif
323	mov	0, %o1		/* delay slot */
324nbif_1_gc_after_bif:
325	ba	.gc_after_bif
326	mov	1, %o1		/* delay slot */
327nbif_2_gc_after_bif:
328	ba	.gc_after_bif
329	mov	2, %o1		/* delay slot */
330nbif_3_gc_after_bif:
331	ba	.gc_after_bif
332	mov	3, %o1		/* delay slot */
333nbif_4_gc_after_bif:
334	mov	4, %o1
335	/*FALLTHROUGH*/
336.gc_after_bif:
337	st	%o1, [P+P_NARITY]
338	st	TEMP_RA, [P+P_NRA]
339	st	NSP, [P+P_NSP]
340	mov	RA, TEMP_RA
341	mov	0, %o3			/* Pass 0 in arity */
342	mov	0, %o2			/* Pass NULL in regs */
343	mov	%o0, %o1
344	call	erts_gc_after_bif_call
345	mov	P, %o0			/* delay slot */
346	mov	TEMP_RA, RA
347	ld	[P+P_NRA], TEMP_RA
348	jmp	RA+8
349	st	%g0, [P+P_NARITY]	/* delay slot */
350
351/*
352 * We end up here when a BIF called from native signals an
353 * exceptional condition.
354 * HP has not been read from P.
355 * NSP has not been saved in P.
356 * TEMP_LR contains a copy of LR
357 */
358	.global	nbif_0_simple_exception
359nbif_0_simple_exception:
360	ba	.nbif_simple_exception
361	mov	0, %o1		/* delay slot */
362	.global	nbif_1_simple_exception
363nbif_1_simple_exception:
364	ba	.nbif_simple_exception
365	mov	1, %o1		/* delay slot */
366	.global	nbif_2_simple_exception
367nbif_2_simple_exception:
368	ba	.nbif_simple_exception
369	mov	2, %o1		/* delay slot */
370	.global	nbif_3_simple_exception
371nbif_3_simple_exception:
372	ba	.nbif_simple_exception
373	mov	3, %o1		/* delay slot */
374	.global	nbif_4_simple_exception
375nbif_4_simple_exception:
376	mov	4, %o1
377	/*FALLTHROUGH*/
378.nbif_simple_exception:
379	ld	[P+P_FREASON], %o0
380	cmp	%o0, FREASON_TRAP
381	beq	.handle_trap
382	nop
383	/*
384	 * Find and invoke catch handler (it must exist).
385	 * HP has not been read from P.
386	 * NSP has not been saved in P.
387	 * TEMP_RA should contain the current call's return address.
388	 * %o1 should contain the current call's arity.
389	 */
390	st	NSP, [P+P_NSP]
391	st	TEMP_RA, [P+P_NRA]
392	st	%o1, [P+P_NARITY]
393	/* find and prepare to invoke the handler */
394	call	hipe_handle_exception	/* Note: hipe_handle_exception() conses */
395	mov	P, %o0			/* delay slot */
396	/* prepare to invoke the handler */
397	ld	[P+P_NCALLEE], %o0	/* set by hipe_find_handler() */
398	RESTORE_CACHED_STATE
399	/* now invoke the handler */
400	jmp	%o0
401	nop
402
403	/*
404	 * A BIF failed with freason TRAP:
405	 * - the BIF's arity is in %o1
406	 * - the native RA was saved in TEMP_RA before the BIF call
407	 * - HP has not been read from P
408	 * - NSP has not been saved in P
409	 */
410.handle_trap:
411	mov	HIPE_MODE_SWITCH_RES_TRAP, %o0
412.bif_exit:
413	/* restore C return address (hoisted to avoid stall) */
414	ld	[%sp+96], %i7
415	st	NSP, [P+P_NSP]
416	st	%o1, [P+P_NARITY]
417	st	TEMP_RA, [P+P_NRA]
418	jmp	%i7+8
419	restore	%g0, %o0, %o0
420
421/*
422 * nbif_stack_trap_ra: trap return address for maintaining
423 * the gray/white stack boundary
424 */
425	.global	nbif_stack_trap_ra
426nbif_stack_trap_ra:			/* a return address, not a function */
427	nop                             /* ditto */
428	nop				/* ditto */
429	/* This only handles a single return value.
430	   If we have more, we need to save them in the PCB. */
431	mov	%o0, TEMP_ARG0		/* save retval */
432	st	NSP, [P+P_NSP]
433	call hipe_handle_stack_trap	/* must not cons */
434	mov	P, %o0			/* delay slot */
435	mov	%o0, RA			/* original RA */
436	jmp	RA+8			/* resume at original RA */
437	mov	TEMP_ARG0, %o0		/* delay slot: restore retval */
438
439/*
440 * hipe_sparc_inc_stack
441 * Caller saved its RA in TEMP_RA (== TEMP1) before calling us.
442 */
443	.global	hipe_sparc_inc_stack
444hipe_sparc_inc_stack:
445	STORE_ARG_REGS
446	mov	RA, TEMP_ARG0
447	st	NSP, [P+P_NSP]
448	/* hipe_inc_nstack reads and writes NSP and NSP_LIMIT,
449	   but does not access LR/RA, HP, or FCALLS. */
450	call	hipe_inc_nstack
451	mov	P, %o0		/* delay slot */
452	LOAD_ARG_REGS
453	/* this relies on LOAD_ARG_REGS not clobbering TEMP_ARG0 */
454	jmp	TEMP_ARG0+8
455	ld	[P+P_NSP], NSP	/* delay slot */
456
457#if defined(__linux__) && defined(__ELF__)
458.section .note.GNU-stack,"",%progbits
459#endif
460