1 // exc-alloca-handler.S - ALLOCA cause exception assembly-level handler
2 // $Id: //depot/rel/Cottonwood/Xtensa/OS/xtos/exc-alloca-handler.S#3 $
4 // Copyright (c) 2002-2010 Tensilica Inc.
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:
14 // The above copyright notice and this permission notice shall be included
15 // in all copies or substantial portions of the Software.
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 NONINFRINGEMENT.
20 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 * Code written to the windowed ABI must use the MOVSP instruction to modify
27 * the stack pointer (except for startup code, which doesn't have a caller).
28 * The compiler uses MOVSP to allocate very large or variable size stack frames.
29 * MOVSP guarantees that the caller frame's a0-a3 registers, stored below the
30 * stack pointer, are moved atomically with respect to interrupts and exceptions
31 * to satisfy windowed ABI requirements. When user code executes the MOVSP
32 * instruction and the caller frame is on the stack rather than in the register
33 * file, the processor takes an ALLOCA exception. The ALLOCA exception handler
34 * moves the caller frame's a0-a3 registers to follow the stack pointer.
35 * This file implements this ALLOCA exception handler.
37 * Code written in C can generate a MOVSP in four situations:
39 * 1. By calling "alloca":
41 * void foo(int array_size) {
42 * char * bar = alloca(array_size);
45 * 2. By using variable sized arrays (a GNU C extension):
47 * void foo(int array_size) {
48 * char bar[array_size];
51 * 3. By using nested C functions (also a GNU C extension):
53 * void afunction(void) {
55 * int anotherfunction(void) {
59 * 4. By using very large amounts of stack space in a single function. The exact
60 * limit is 32,760 bytes (including 16-48 bytes of caller frame overhead).
61 * Typically, users don't encounter this limit unless they have functions
62 * that locally declare large arrays, for example:
65 * int an_array[8192]; // 32,768 bytes
66 * int another_array[100]; // 400 bytes
70 * NOTE: This handler only works when MOVSP's destination register is the stack
71 * pointer "a1" (synonym with "sp"), i.e. "MOVSP a1, <as>". This is the only
72 * meaningful form of MOVSP in the windowed ABI, and the only form generated
73 * by the compiler and used in assembly. The code below does not check the
74 * destination register, so other forms of MOVSP cause unexpected behaviour.
77 #include <xtensa/coreasm.h>
78 #include <xtensa/config/specreg.h>
79 #include "xtos-internal.h"
81 #define ERROR_CHECKING 1 // define as 0 to save a few bytes
84 #if XCHAL_HAVE_EXCEPTIONS
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
94 // s32i a4, a1, UEXC_a4
95 // jx a2 // jump to cause-specific handler
97 .global _need_user_vector_ // pull-in real user vector (tiny LSP)
101 .global _xtos_alloca_handler
102 _xtos_alloca_handler:
103 #if !XCHAL_HAVE_WINDOWED || defined(__XTENSA_CALL0_ABI__)
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)
112 * Skip the MOVSP instruction so we don't execute it again on return:
115 rsr a3, EPC_1 // load instruction address (PC)
116 s32i a5, a1, UEXC_a5 // save a5
117 addi a2, a3, 3 // increment PC to skip MOVSP instruction
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.
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...
130 rsr a2, LBEG // PC back to start of loop
131 #endif /*XCHAL_HAVE_LOOPS*/
132 1: wsr a2, EPC_1 // update return PC past MOVSP
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.
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 a4, SAR // save SAR
145 // NOTE: possible addition here: verify destination register is indeed a1.
149 extui a3, a3, 28, 4 // extract source register number
153 extui a3, a3, 0, 4 // extract source register number
155 wsr a4, SAR // restore SAR
156 // (+?? cycles max above = ?? cycles, assuming cache hits, 5-stage pipe, no zoloops, etc)
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)
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
166 # define L32I _l32i.n
167 # define DONE _bnez.n a4, .Lmove_save_area // a4 known non-zero
169 addx8 a4, a3, a4 // index by src reg number * 8
170 # define ALIGN .align 8 // 8-byte jmptable entries
173 # define DONE j .Lmove_save_area
176 jx a4 // jump into the following table
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
197 // Okay. a1 now contains the new SP value.
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
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)
209 sub a1, a1, a3 // FORCE alignment of the new pointer (!)
214 addi a2, a5, ESF_TOTALSIZE // compute a2 = old SP
216 addi a2, a5, ESF_TOTALSIZE-16 // compute a2 = old SP's save area
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
229 l32i a3, a5, UEXC_a4+ESF_TOTALSIZE
230 l32i a4, a5, UEXC_a5+ESF_TOTALSIZE
235 // Move the register save area (from old SP to new SP):
246 addi a1, a1, -16 // point to new save area
255 addi a1, a1, 16 // back to correct new SP
257 // (+?? cycles max above = ?? cycles, assuming cache hits, 5-stage pipe, etc)
259 // Restore a2, a3, a4, a5, and return:
265 // (+?? cycles max above = ?? cycles, assuming cache hits, 5-stage pipe, etc)
268 #endif /* !XCHAL_HAVE_WINDOWED || __XTENSA_CALL0_ABI */
270 .size _xtos_alloca_handler, . - _xtos_alloca_handler
272 #endif /* XCHAL_HAVE_EXCEPTIONS */