public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
From: Ian Molton <gcc_help-ian@mnementh.co.uk>
To: gcc-help@gcc.gnu.org
Subject: RFC: AVR interrupt handling issue
Date: Sat, 18 Dec 2021 16:00:34 +0000	[thread overview]
Message-ID: <f07660af-5827-e893-4f67-c0ebaef29238@mnementh.co.uk> (raw)

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" : : : <clobbered regs>);
}

__attribute__ ((naked))
void my_isr_epilogue (void)
{
	asm volatile("... un-whatever" : : : <clobbered regs>);
	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?

             reply	other threads:[~2021-12-18 16:00 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-18 16:00 Ian Molton [this message]
2021-12-18 16:31 ` Henri Cloetens
2021-12-18 17:47   ` Ian Molton
2021-12-18 18:07     ` Ian Molton
2021-12-20 21:06   ` gcc not generating dwarf prologue_end and epilogue_begin .loc directives (was: Re: RFC: AVR interrupt handling issue) Ian Molton

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=f07660af-5827-e893-4f67-c0ebaef29238@mnementh.co.uk \
    --to=gcc_help-ian@mnementh.co.uk \
    --cc=gcc-help@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).