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