Hi,
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
un-necessary regs.
For example
void __attribute__((signal)) __vector_20(void)
{
char a;
a++
return;
}
As it stands, the code generated by gcc, I assume, looks something like:
.foo
gccisr 0
add rX ...
gccisr 1
gccisr 2
Which would compile into something like: (in its "fuller form")
.foo
push r1
push r0
in r0, 0x3f ; 63
push r0
eor r1, r1
push rX
add rx ...
pop rX
pop r1
pop r0
Assuming "a" lives in static storage (so it won't be optimized away),
gcc would generate
__vector_20:
__gcc_isr 1
.L__stack_usage = 0 + __gcc_isr.n_pushed
lds r24,a
subi r24,lo8(-(1))
sts a,r24
__gcc_isr 2
reti
__gcc_isr 0,r24
Gas would scan this and recognize that SREG is touched, and emit:
00000000 <__vector_20>:
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:
https://sourceware.org/binutils/docs/as/AVR-Pseudo-Instructions.html
It seems that it even checks called functions, so its ideal for writing
interrupt handlers.
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
gcc:
__vector_20:
push r1
push r0
in r0,__SREG__
push r0
clr __zero_reg__
push r24
/* prologue: Signal */
.L__stack_usage = 4
...
/* epilogue start */
pop r24
pop r0
out __SREG__,r0
pop r0
pop r1
reti
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
-mno-gas-isr-prologues.
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
above.
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
there.
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.
.call_foo
<my ISR prologue to save SREG>
<switch stack>
rjmp foo()
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...
-Ian
Gcc attributes that influence function prologue and epilogue are:
signal, interrupt, naked, no_gccisr, OS_task, OS_main.
Johann