1// exc-alloca-handler.S - OBSOLETE - ALLOCA cause exception assembly-level handler
2
3#if 0	/*  This handler is OBSOLETE - now part of window-vectors.S  */
4
5// Copyright (c) 2002-2010 Tensilica Inc.
6//
7// Permission is hereby granted, free of charge, to any person obtaining
8// a copy of this software and associated documentation files (the
9// "Software"), to deal in the Software without restriction, including
10// without limitation the rights to use, copy, modify, merge, publish,
11// distribute, sublicense, and/or sell copies of the Software, and to
12// permit persons to whom the Software is furnished to do so, subject to
13// the following conditions:
14//
15// The above copyright notice and this permission notice shall be included
16// in all copies or substantial portions of the Software.
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 NONINFRINGEMENT.
21// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26/*
27 * Code written to the windowed ABI must use the MOVSP instruction to modify
28 * the stack pointer (except for startup code, which doesn't have a caller).
29 * The compiler uses MOVSP to allocate very large or variable size stack frames.
30 * MOVSP guarantees that the caller frame's a0-a3 registers, stored below the
31 * stack pointer, are moved atomically with respect to interrupts and exceptions
32 * to satisfy windowed ABI requirements.  When user code executes the MOVSP
33 * instruction and the caller frame is on the stack rather than in the register
34 * file, the processor takes an ALLOCA exception.  The ALLOCA exception handler
35 * moves the caller frame's a0-a3 registers to follow the stack pointer.
36 * This file implements this ALLOCA exception handler.
37 *
38 * Code written in C can generate a MOVSP in four situations:
39 *
40 * 1. By calling "alloca":
41 *
42 *   void foo(int array_size) {
43 *     char * bar = alloca(array_size);
44 *     ...
45 *
46 * 2. By using variable sized arrays (a GNU C extension):
47 *
48 *   void foo(int array_size) {
49 *     char bar[array_size];
50 *     ...
51 *
52 * 3. By using nested C functions (also a GNU C extension):
53 *
54 *   void afunction(void) {
55 *     ...
56 *     int anotherfunction(void) {
57 *     }
58 *     ...
59 *
60 * 4. By using very large amounts of stack space in a single function. The exact
61 *    limit is 32,760 bytes (including 16-48 bytes of caller frame overhead).
62 *    Typically, users don't encounter this limit unless they have functions
63 *    that locally declare large arrays, for example:
64 *
65 *   void foo(void) {
66 *     int an_array[8192];		// 32,768 bytes
67 *     int another_array[100];		// 400 bytes
68 *     ...
69 *
70 *
71 * NOTE:  This handler only works when MOVSP's destination register is the stack
72 * pointer "a1" (synonym with "sp"), i.e. "MOVSP a1, <as>".  This is the only
73 * meaningful form of MOVSP in the windowed ABI, and the only form generated
74 * by the compiler and used in assembly.  The code below does not check the
75 * destination register, so other forms of MOVSP cause unexpected behaviour.
76 */
77
78#include <xtensa/coreasm.h>
79#include "xtos-internal.h"
80
81#define ERROR_CHECKING	1	// define as 0 to save a few bytes
82
83
84#if XCHAL_HAVE_EXCEPTIONS
85
86//Vector:
87//	addi	a1, a1, -ESF_TOTALSIZE	// allocate exception stack frame, etc.
88//	s32i	a2, a1, UEXC_a2
89//	s32i	a3, a1, UEXC_a3
90//	movi	a3, xtos_exc_handler_table
91//	rsr.exccause	a2
92//	addx4	a2, a2, a3
93//	l32i	a2, a2, 0
94//	s32i	a4, a1, UEXC_a4
95//	jx	a2		// jump to cause-specific handler
96
97	.global	_need_user_vector_	// pull-in real user vector (tiny LSP)
98
99	.text
100	.align	4
101	.global	_xtos_alloca_handler
102_xtos_alloca_handler:
103#if !XCHAL_HAVE_WINDOWED || defined(__XTENSA_CALL0_ABI__)
104	rfe_rfue
105#else /* we have windows w/o call0 abi */
106	//  HERE:  a2, a3, a4 have been saved to
107	//  exception stack frame allocated with a1 (sp).
108	//  a2 contains EXCCAUSE.
109	//  (12 cycles from vector to here, assuming cache hits, 5-stage pipe, etc)
110
111	/*
112	 *  Skip the MOVSP instruction so we don't execute it again on return:
113	 */
114
115	rsr.epc1	a3		// load instruction address (PC)
116	s32i	a5, a1, UEXC_a5		// save a5
117	addi	a2, a3, 3		// increment PC to skip MOVSP instruction
118#if XCHAL_HAVE_LOOPS
119	/*
120	 *  If the MOVSP instruction is the last instruction in the body of
121	 *  a zero-overhead loop that must be executed again, then decrement
122	 *  the loop count and resume execution at the head of the loop.
123	 */
124	rsr.lend	a4
125	rsr.lcount	a5
126	bne	a4, a2, 1f		// done unless next-PC matches LEND
127	beqz	a5, 1f			// if LCOUNT zero, not in loop
128	addi	a5, a5, -1		// z.o. loopback! decrement LCOUNT...
129	wsr.lcount	a5
130	rsr.lbeg	a2		// PC back to start of loop
131#endif /*XCHAL_HAVE_LOOPS*/
1321:	wsr.epc1	a2		// update return PC past MOVSP
133
134	/*
135	 *  Figure out what register MOVSP is moving from ('s' field, 2nd byte).
136	 *  If MOVSP is in an instruction RAM or ROM, we can only access it with
137	 *  32-bit loads.  So use shifts to read the byte from a 32-bit load.
138	 */
139
140	addi	a3, a3, 1		// advance to byte containing 's' field
141	extui	a2, a3, 0, 2		// get bits 0 and 1 of address of this byte
142	sub	a3, a3, a2		// put address on 32-bit boundary
143	l32i	a3, a3, 0		// get word containing byte (can't use l8ui on IRAM/IROM)
144	rsr.sar	a4			// save SAR
145	//  NOTE: possible addition here: verify destination register is indeed a1.
146# if XCHAL_HAVE_BE
147	ssa8b	a2
148	sll	a3, a3
149	extui	a3, a3, 28, 4		// extract source register number
150# else
151	ssa8l	a2
152	srl	a3, a3
153	extui	a3, a3, 0, 4		// extract source register number
154# endif
155	wsr.sar	a4			// restore SAR
156	//  (+?? cycles max above = ?? cycles, assuming cache hits, 5-stage pipe, no zoloops, etc)
157
158	movi	a4, .Ljmptable	        // jump table
159	mov	a5, a1			// save the exception stack frame ptr in a5
160	addi	a1, a1, ESF_TOTALSIZE	// restore a1 (in case of MOVSP a1,a1)
161
162# if XCHAL_HAVE_DENSITY
163	addx4	a4, a3, a4              // index by src reg number * 4
164#  define ALIGN	.align 4		// 4-byte jmptable entries
165#  define MOV	_mov.n
166#  define L32I	_l32i.n
167#  define DONE	_bnez.n a4, .Lmove_save_area	// a4 known non-zero
168# else
169	addx8	a4, a3, a4              // index by src reg number * 8
170#  define ALIGN	.align 8		// 8-byte jmptable entries
171#  define MOV	mov
172#  define L32I	l32i
173#  define DONE	j .Lmove_save_area
174# endif
175
176	jx	a4			// jump into the following table
177
178	ALIGN
179.Ljmptable:	MOV	a1, a0		; DONE	// MOVSP a1, a0
180	ALIGN				; DONE	// MOVSP a1, a1
181	ALIGN ; L32I	a1, a5, UEXC_a2	; DONE	// MOVSP a1, a2
182	ALIGN ; L32I	a1, a5, UEXC_a3	; DONE	// MOVSP a1, a3
183	ALIGN ; L32I	a1, a5, UEXC_a4	; DONE	// MOVSP a1, a4
184	ALIGN ; L32I	a1, a5, UEXC_a5	; DONE	// MOVSP a1, a5
185	ALIGN ; MOV	a1, a6		; DONE	// MOVSP a1, a6
186	ALIGN ; MOV	a1, a7		; DONE	// MOVSP a1, a7
187	ALIGN ; MOV	a1, a8		; DONE	// MOVSP a1, a8
188	ALIGN ; MOV	a1, a9		; DONE	// MOVSP a1, a9
189	ALIGN ; MOV	a1, a10		; DONE	// MOVSP a1, a10
190	ALIGN ; MOV	a1, a11		; DONE	// MOVSP a1, a11
191	ALIGN ; MOV	a1, a12		; DONE	// MOVSP a1, a12
192	ALIGN ; MOV	a1, a13		; DONE	// MOVSP a1, a13
193	ALIGN ; MOV	a1, a14		; DONE	// MOVSP a1, a14
194	ALIGN ; MOV	a1, a15			// MOVSP a1, a15
195
196.Lmove_save_area:
197	//  Okay.  a1 now contains the new SP value.
198
199# if ERROR_CHECKING
200	//  Verify it is sensible:
201	extui	a3, a1, 0, 2		// verify that new SP is 4-byte aligned
202	beqz	a3, 1f			// if so, skip fixup
203
204//	.global	_xtos_misaligned_movsp	// make label visible for debugging
205//_xtos_misaligned_movsp:
206#  if XCHAL_HAVE_DEBUG
207	break	1, 15			// break into debugger (if any)
208#  endif
209	sub	a1, a1, a3		// FORCE alignment of the new pointer (!)
2101:
211# endif
212
213# if XCHAL_HAVE_XEA2
214	addi	a2, a5, ESF_TOTALSIZE		// compute a2 = old SP
215# else /*XEA1:*/
216	addi	a2, a5, ESF_TOTALSIZE-16	// compute a2 = old SP's save area
217# endif
218	//  Does new SP (in a1) overlap with exception stack frame (in a5)?:
219	movi	a4, ESF_TOTALSIZE	// size of exception stack frame
220	sub	a3, a1, a5		// distance from ESF ptr to new SP
221	bgeu	a3, a4, 1f		// does new SP overlap ESF? branch if not
222	//  Move ESF down so it doesn't overlap with the new register save area:
223	//  (a1 = current ESF, a2 = new SP, a4 = ESF_TOTALSIZE)
224	sub	a5, a5, a4		// shift down ESF (by ESF size)
225	l32i	a3, a5, UEXC_a2+ESF_TOTALSIZE
226	l32i	a4, a5, UEXC_a3+ESF_TOTALSIZE
227	s32i	a3, a5, UEXC_a2
228	s32i	a4, a5, UEXC_a3
229	l32i	a3, a5, UEXC_a4+ESF_TOTALSIZE
230	l32i	a4, a5, UEXC_a5+ESF_TOTALSIZE
231	s32i	a3, a5, UEXC_a4
232	s32i	a4, a5, UEXC_a5
2331:
234
235	//  Move the register save area (from old SP to new SP):
236# if XCHAL_HAVE_XEA2
237	l32e	a3, a2, -16
238	l32e	a4, a2, -12
239	s32e	a3, a1, -16
240	s32e	a4, a1, -12
241	l32e	a3, a2, -8
242	l32e	a4, a2, -4
243	s32e	a3, a1, -8
244	s32e	a4, a1, -4
245# else /*XEA1:*/
246	addi	a1, a1, -16		// point to new save area
247	l32i	a3, a2, 0
248	l32i	a4, a2, 4
249	s32i	a3, a1, 0
250	s32i	a4, a1, 4
251	l32i	a3, a2, 8
252	l32i	a4, a2, 12
253	s32i	a3, a1, 8
254	s32i	a4, a1, 12
255	addi	a1, a1, 16		// back to correct new SP
256# endif /*XEA1*/
257	//  (+?? cycles max above = ?? cycles, assuming cache hits, 5-stage pipe, etc)
258
259	//  Restore a2, a3, a4, a5, and return:
260	l32i	a2, a5, UEXC_a2
261	l32i	a3, a5, UEXC_a3
262	l32i	a4, a5, UEXC_a4
263	l32i	a5, a5, UEXC_a5
264	rfe_rfue
265	//  (+?? cycles max above = ?? cycles, assuming cache hits, 5-stage pipe, etc)
266
267
268#endif /* !XCHAL_HAVE_WINDOWED || __XTENSA_CALL0_ABI */
269
270	.size	_xtos_alloca_handler, . - _xtos_alloca_handler
271
272#endif /* XCHAL_HAVE_EXCEPTIONS */
273
274#endif /* 0 */
275
276