From 865bbd6b69ce4c6ad0aa27c3c826850db8bd80c7 Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Wed, 17 Jan 2018 10:12:25 -0800 Subject: [PATCH] xtensa-asm2: Handle alloca/movsp exceptions Xtensa register windows have a special exception that happens when the stack pointer needs to be moved, but the caller function has already spilled its registers below it. I thought these were unexercised in Zephyr code, but they turn out to be thrown by the existing mem_pool tests when run in the 32-register qemu environment (but not on 64-register hardwre). Because the effect of the exception is to unspill the caller, there is no good way to handle this in a traditional handler. Instead put a 5-instruction stub in front of the user exception handler (i.e. incurring that cost on every trap and every L1 interrupt) to test before doing the normal entry. Works, but would be nicer to optimize this in the future so that only true alloca exceptions take that cost. Signed-off-by: Andy Ross --- arch/xtensa/core/xtensa-asm2-util.S | 24 ++++++++++++++++++++++++ arch/xtensa/include/xtensa-asm2-s.h | 7 +++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/arch/xtensa/core/xtensa-asm2-util.S b/arch/xtensa/core/xtensa-asm2-util.S index 2febda46803..f2c6750313f 100644 --- a/arch/xtensa/core/xtensa-asm2-util.S +++ b/arch/xtensa/core/xtensa-asm2-util.S @@ -265,6 +265,30 @@ DEF_EXCINT 6, _handle_excint, xtensa_int6_c DEF_EXCINT 7, _handle_excint, xtensa_int7_c #endif +/* The user exception vector is defined here, as we need to handle + * MOVSP exceptions in assembly (the result has to be to unspill the + * caller function of the code that took the exception, and that can't + * be done in C). A prototype exists which mucks with the stack frame + * from the C handler instead, but that would add a LARGE overhead to + * some alloca() calls (those whent he caller has been spilled) just + * to save these five cycles during other exceptions and L1 + * interrupts. Maybe revisit at some point, with better benchmarking. + * Note that _xt_alloca_exc is Xtensa-authored code which expects A0 + * to have been saved to EXCSAVE1, which is an unfortunate ABI given + * that Zephyr code otherwise does not use the EXCSAVE registers. + */ +.pushsection .UserExceptionVector.text, "ax" +.global _Level1RealVector +_Level1RealVector: + wsr.excsave1 a0 + rsr.exccause a0 + bnei a0, EXCCAUSE_ALLOCA, _not_alloca + j _xt_alloca_exc +_not_alloca: + rsr.excsave1 a0 + j _Level1Vector +.popsection + /* In theory you can have levels up to 15, but known hardware only uses 7. */ #if XCHAL_NMILEVEL > 7 #error More interrupts than expected. diff --git a/arch/xtensa/include/xtensa-asm2-s.h b/arch/xtensa/include/xtensa-asm2-s.h index 08881cbb2d1..6567c509320 100644 --- a/arch/xtensa/include/xtensa-asm2-s.h +++ b/arch/xtensa/include/xtensa-asm2-s.h @@ -322,11 +322,14 @@ _restore_\@: * Note that the linker sections for some levels get special names for * no particularly good reason. Only level 1 has any code generation * difference, because it is the legacy exception level that predates - * the EPS/EPC registers. + * the EPS/EPC registers. It also lives in the "iram0.text" segment + * (which is linked immediately after the vectors) so that an assembly + * stub can be loaded into the vector area instead and reach this code + * with a simple jump instruction. */ .macro DEF_EXCINT LVL, ENTRY_SYM, C_HANDLER_SYM .if \LVL == 1 -.pushsection .UserExceptionVector.text, "ax" +.pushsection .iram0.text, "ax" .elseif \LVL == XCHAL_DEBUGLEVEL .pushsection .DebugExceptionVector.text, "ax" .elseif \LVL == XCHAL_NMILEVEL