From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mnementh.co.uk (mail.mnementh.co.uk [185.157.232.187]) by sourceware.org (Postfix) with ESMTPS id 112773858D28 for ; Sat, 18 Dec 2021 16:00:37 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 112773858D28 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=mnementh.co.uk Authentication-Results: sourceware.org; spf=none smtp.mailfrom=mnementh.co.uk Received: from 115.24.187.81.in-addr.arpa ([81.187.24.115] helo=[192.168.1.16]) by mnementh.co.uk with esmtpsa (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1myc8Q-0007N0-Om for gcc-help@gcc.gnu.org; Sat, 18 Dec 2021 16:00:34 +0000 Message-ID: Date: Sat, 18 Dec 2021 16:00:34 +0000 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.3.1 To: gcc-help@gcc.gnu.org Content-Language: en-GB From: Ian Molton Subject: RFC: AVR interrupt handling issue Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=1.2 required=5.0 tests=BAYES_00, KAM_COUK, KAM_DMARC_STATUS, KAM_LAZY_DOMAIN_SECURITY, KHOP_HELO_FCRDNS, SCC_5_SHORT_WORD_LINES, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=no autolearn_force=no version=3.4.4 X-Spam-Level: * X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-help@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-help mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 18 Dec 2021 16:00:39 -0000 Hi all, I posted about this yesterday on the binutils list, but in the light of day, I find myself re-thinking it; Right now, gcc will emit ISR prologues and epilogues such as this: __vector_35: push r1 ; push r0 ; in r0,__SREG__ ; , push r0 ; clr __zero_reg__ ; in r0,__RAMPZ__ ; , push r0 ; push r18 ; push r19 push r20 ; push r21 ; push r22 ; push r23 ; push r24 ; push r25 ; push r26 ; push r27 ; push r30 ; push r31 ; push r28 ; push r29 ; // External func call to provoke pro/epilogue // generation for this example... call foo_func(); ... pop r29 ; pop r28 ; pop r31 ; pop r30 ; pop r27 ; pop r26 ; pop r25 ; pop r24 ; pop r23 ; pop r22 ; pop r21 ; pop r20 ; pop r19 ; pop r18 ; pop r0 ; out __RAMPZ__,r0 ; , pop r0 ; out __SREG__,r0 ; , pop r0 ; pop r1 ; reti The problem I have is that I want to switch to a separate irq stack when I get an interrupt, which I cannot do in C, since all the prologue is executed before the function body. I *can* do it if I use an assembler stub that then calls my ISR, which switches stacks, and pushes the address of a custom epilogue onto the stack, before executing the ISR, but this obviously wastes a lot of cycles pushing the epilogue address, and by necessity, some of the registers that the existing ISR prologue will redundantly push again. likewise, being able to insert my own epilogue sequence would allow me to avoid an additional branch in the return path from the ISR. I can see two solutions to this problem: 1) Allow the compiler to omit certain registers from being saved in the ISR prologue 2) Allow the user to specify custom pro/epilogue functions. (-finstrument functions is similar, but not close enough) 1) would work, but would require careful futzing about with the linker to arrange the code in such a way that my prologue and epilogue are located immediately around the ISR code. implementation could look like -mno-save-isr-prologue="r0,r1,SREG,r26,r27" (or whatever regs the custom prologue might save prior to the ISR prologue) 2) would be ideal. The compiler would know which registers the custom prologue functions use, and would therefore be able to omit saving them from the ISR prologue (and conversely from the epilogue). Something like __attribute__ ((naked)) void my_isr_prologue (void) { asm volatile("... whatever" : : : ); } __attribute__ ((naked)) void my_isr_epilogue (void) { asm volatile("... un-whatever" : : : ); asm volatile("reti"); } __attribute__ ((__isr_prologue__(my_isr_prologue, my_isr_epilogue))) __attribute__ ((signal)) __vector_35(void) { ... do interrupt-y things ... } Thoguhts? I can see that I could implement option 1) thus: I specify -mno-gas-isr-prologues to force gcc to emit full pro/epi-logues in the assembler output. I can modify gcc/config/avr/avr.c at this point: (~line 1893) avr_regs_to_save (&set); if (no-save-isr-prologue) { // FIXME Remove registers my custom prologue saves from the set ... } if (cfun->machine->is_interrupt || cfun->machine->is_signal) { ... which just leaves me with a (trivial) script to write that can stuff my prologue / epilogue into the assembler, and re-assemble it into an object. The downside is that very simple ISRs which don't need many registers will be less efficient. But we're calling C here, and I would write such simple ISRs in assembler anyway. Option 2) would require more knowledge of gcc than I currently have. Other (doomed? cursed?) options: 3) It almost seems like this could be solved if a function with __attribute__ ((signal)) could (inline-) call another function with the same attribute. The first function would not need to save any call-used registers other than the ones it uses itself, and the called function would be able to avoid saving any call-used registers that were saved by its calling function. I suspect, however that that approach is probably doomed to failure, as inlining the second function (to avoid the overhead of calling it) would presumably also relocate its register save instructions right back to the first functions prologue, where it isn't wanted, as described above. 4) Use a naked function for the ISR and a script to process the assembler in order to generate the prologue and epilogue The GCC docs state that one should not write C code in a naked function. Presumably, as long as you add enough prologue to provide a C environment, this isn't a problem, but its explicitly disallowed, and would require me writing a script to parse the assembler output to generate the entire prologue/epilogue sequences. In a sense, this is the "purest" option, but I suspect properly determining the registers used in this way would be a challenge. Presumably this is why the __gcc_isr method of generating pro/epilogues is a task split between GCC and binutils? I am at a bit of a loss as to what information binutils is supposed to have in this case, that gcc does not - why *is* it done that way? I've poked at inline asm with gcc, and find that I can use clobbers to force a save of RAMPZ (etc.) from inline assembler within an ISR, so I don't really "get" what the __gcc_isr approach is buying... the ability to write inline asm without being explicit about clobbers? What for? Thoughts?