[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
ISR Function prologues on AVR with Binutils and LD
Georg Johann Lay
ISR Function prologues on AVR with Binutils and LD
Thu, 3 Nov 2022 12:29:42 +0100
Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.2.2
I've dug about in the sourcecode of gcc and gas, in order to understand
how it works.
in particular, in gas
avr_patch_gccisr_frag (fragS *fr, int reg)
There is behaviour, specific to AVR that allows binutils to analyse the
compiler output for interrupt routines, and causes it to generate
special prologue/epilogues that preserve the CPU state.
My problem is that these appear to have been designed with a particular
approach/model for interrupt handling in mind, and are proving to be
frustrating now that I wish to write my own kernel for AVR.
Hi, the rationale behind this is to optimize ISR prologues and epilogues
as of https://gcc.gnu.org/PR20296.
Because it's not feasible in gcc alone, gas is analyzing most of the
instructions and is emitting appropriate prologues and epilogues.
In particular, I want to call schedule() following an interrupt handler
(and prior to returning to a thread) that causes schedule() to be
required, which would in turn require saving the entire current thread
state, but if I don't call it, then I want to avoid pushing a bunch of
void __attribute__((signal)) __vector_20(void)
As it stands, the code generated by gcc, I assume, looks something like:
add rX ...
Which would compile into something like: (in its "fuller form")
in r0, 0x3f ; 63
eor r1, r1
add rx ...
Assuming "a" lives in static storage (so it won't be optimized away),
gcc would generate
.L__stack_usage = 0 + __gcc_isr.n_pushed
Gas would scan this and recognize that SREG is touched, and emit:
0: 8f 93 push r24
2: 8f b7 in r24, 0x3f ; SREG
4: 8f 93 push r24
6: 80 91 60 00 lds r24, 0x0060 ; 0x800060 <a>
a: 8f 5f subi r24, 0xFF ; 255
c: 80 93 60 00 sts 0x0060, r24 ; 0x800060 <a>
10: 8f 91 pop r24
12: 8f bf out 0x3f, r24 ; SREG
14: 8f 91 pop r24
16: 18 95 reti
The documentation for the pseudo-instruction __gcc_isr is here:
It seems that it even checks called functions, so its ideal for writing
It checks *whether* the ISR is calling functions (be they tail-calls,
ordinary calls, transparent), and if so, the compiler won't emit
__gcc_isr at all but resort to the "old" prologue; here asm generated by
/* prologue: Signal */
.L__stack_usage = 4
/* epilogue start */
so is saves, restores, inits zero_reg (R1) even though that's not
needed, and tmp_reg (R0) is not clobbered, either.
However, I want to use a separate stack for IRQs, and this prevents me
doing so, AFAICT.
If __gcc_isr is what's getting in your way, then you can switch back to
old ISR prologues by attribute "no_gccisr" for individual ISRs. If you
want to disable __gcc_isr for the whole compilation unit, use option
I could have my interrupt call an assembler stub beforehand to switch
SP, but to do so, I must save a couple of registers to the stack. I
don't want to save them *again* in the prologue, as its wasteful and on
a hot path.
I could write code to switch stack within foo(), but this won't work,
since the gccisr prologue will come before it, which is not right either.
I can see no other gcc / binutils option that would allow me to achieve
this trick - I had hoped that I could use some sort of function
prologue, but I can't see how, and it would suffer the same problems as
Sounds like what you want is attribute "naked" for the ISR, so that gcc
won't generate prologues or epilogues at all. According to the GCC
documentation, only inline asm is safe in naked functions; but what you
are going to do (manipulating stack) cannot be done from C/C++, anyways.
I can see that I can trivially modify binutils so that it *NEVER* emits
the code to save SREG, only the registers to push. This gets me half-way
What I need then, is a way to prepend my ISR "prologue" to foo()
In a simplistic way, I could call an assembler shim, eg.
<my ISR prologue to save SREG>
which would clearly work, but the rjmp could be avoided if I can place
It' not clear what you are trying to do. If, for example, an ISR should
handle the scheduling, then you would write it in plain asm: Restore
stack and manipulate return address so that a final RETI would jump to
the point of execution of the respective task.
the code of .call_foo immediately prior to the code of foo(). I assume
the linker can be instructed to do this, but I'm at a bit of a loss as
to how right now.
It seems like being able to disable the "extra prologue" and only have
gas fix up the stack, would be a useful option.
Gas does not have the necessary information. One could assume that each
mentioned register would require PUSH / POP, but that's basically what
already happens, albeit the code is emit by gcc.
Documentation is sparse in this area, and if gcc has a more generic way
of doing this or if I'm barking up the wrong tree, please do mention...
Gcc attributes that influence function prologue and epilogue are:
signal, interrupt, naked, no_gccisr, OS_task, OS_main.
|[Prev in Thread]
||[Next in Thread]|
- ISR Function prologues on AVR with Binutils and LD,
Georg Johann Lay <=