LLVM and Interrupt Service Routines.

Hi,
Apparently, there is no explicit support for ISRs in the llvm framework. I could not find a matching attribute that can be used to mark a function as an ISR, which codegen and optimizer can use accordingly. ISRs aren’t called explicity from any function, so currently the optimizer deletes them. We are planning to introduce a new “interrupt” attribute (to be modeled similiar to “section” attribute) which one can use in different passes suitably.
How does that sound? Or do we have something available already?

  • Sanjiv

We've used the used attribute to ensure they are not deleted and had no problem.

Andrew

Hello Sanjiv,

Have you tried attribute((used))?

–Sam

Hi Sanjiv,

Assuming that that support for Ada in LLVM is complete, I would look to see if there is something that is done there. Ada provides two pragmas (Interrupt_Handler & Attach_Handler) which allow you to both statically and dynamically attach interrupts to procedures.

Alex Karahalios

Anton recently added support for __attribute__((naked)) in r76198 and
r76201. I'd imagine that the GCC "interrupt" and "isr" synonyms would
go in similarly. How each target handles the code generation
differences will be the interesting part.

deep

How each target handles the code generation
differences will be the interesting part.

Why not use a target specific calling convention?

An interrupt is like a normal function call, it only has a different
calling convention.

PIC16 is a slightly different animal. It is not only the entry and exit
part of the ISR that are important; also the ISR has to be placed at a
certain location in memory. In addition, since PIC16 does not have a
stack, we have to play few tricks to provide reentrancy for library and
intrinsic functions that are called from ISR as well because save and
restore is also very expensive on these processors. So I don't think
calling convention is a good idea for us.

A.

From: llvmdev-bounces@cs.uiuc.edu [mailto:llvmdev-bounces@cs.uiuc.edu]

On

Hi Alex,

Assuming that that support for Ada in LLVM is complete, I would look to see if there is something that is done there. Ada provides two pragmas (Interrupt_Handler & Attach_Handler) which allow you to both statically and dynamically attach interrupts to procedures.

the interrupt handler itself is a parameterless procedure that does not
return a value. As such, I guess calling conventions and so forth are
not very relevant for it :slight_smile: In any case llvm-gcc and gcc mainline seem
to output the interrupt handler as an ordinary function on x86-32-linux.

Ciao,

Duncan.

Duncan Sands <baldrick@free.fr> writes:

the interrupt handler itself is a parameterless procedure that does not
return a value. As such, I guess calling conventions and so forth are
not very relevant for it :slight_smile: In any case llvm-gcc and gcc mainline seem
to output the interrupt handler as an ordinary function on
x86-32-linux.

There can be significant differences between an interrupt handler and a
void f(void) function. An interrupt handler may have to:

- Save /all/ registers, including those that are normally caller saved.
- Use a special return instruction (RETI).
- Step over the "red zone" on the stack.
- Set up a safe stack frame before calling normal functions.

I usually write the first level interrupt handler in assembler, and then
call normal C function from there.

Wouldn't a custom calling convention work for all of these requirements?

-Chris

Chris Lattner <clattner@apple.com> writes:

Hi Jakub,

There can be significant differences between an interrupt handler and a
void f(void) function. An interrupt handler may have to:

- Save /all/ registers, including those that are normally caller saved.
- Use a special return instruction (RETI).
- Step over the "red zone" on the stack.
- Set up a safe stack frame before calling normal functions.

I usually write the first level interrupt handler in assembler, and then
call normal C function from there.

an Ada interrupt handler is registered using an Ada library routine.
The library might in fact route the interrupt to some assembler code
like you describe, which itself would then call the registered handler.
I don't know the details of how it is all made to work.

Ciao,

Duncan.

Both are possible.

I have seen systems where it is possible to compile a function as an interrupt handler, and at the same time the runtime library allows you to hook in normal functions like you describe.

Please read below:

> - Save /all/ registers, including those that are normally caller
> saved.
> - Use a special return instruction (RETI).
> - Step over the "red zone" on the stack.
> - Set up a safe stack frame before calling normal functions.
>
> I usually write the first level interrupt handler in assembler, and
> then
> call normal C function from there.

Wouldn't a custom calling convention work for all of these

requirements?

Custom calling convention can work for the above, but for PIC16 these
are not the only things to do...
As you know PIC16 does not have stack; so generating code for ISR and
all functions that it calls (including all stdlib and basic math
intrinsics used for mult/div/etc) requires special code generation
techniques. But we don't have this information until after llvm-ld has
merged all compilation units into one. Theoretically llvm-ld can also
correct the calling convention for the two classes of functions, but I'm
not sure about the practicality of it.

Regards
Ali

What happens with functions that are called both inside and outside ISR context? Do you have to codegen two copies of those?

Jakob Stoklund Olesen wrote:

As you know PIC16 does not have stack; so generating code for ISR and
all functions that it calls (including all stdlib and basic math
intrinsics used for mult/div/etc) requires special code generation
techniques. But we don't have this information until after llvm-ld has
merged all compilation units into one. Theoretically llvm-ld can also
correct the calling convention for the two classes of functions, but I'm
not sure about the practicality of it.
    
What happens with functions that are called both inside and outside ISR context? Do you have to codegen two copies of those?

Yes. That's precisely what we are trying to achieve in llvm-ld.
But the problems don't end there, as llvm-ld doesn't have any idea of libcalls (they're generated in llc) and they could also be called from both places.

- Sanjiv

If you have to generate two copies of the function with different entrypoints, the *front-end* should handle the duplication. This is just like C++ constructors.

One really old patch that apple guys experimented in the past was a "slow and fast call" attribute, which you could stick on function declarations. If you added it to a function, the frontend would generate an entry point with a standard calling convention as well as one with a faster in-register ABI. Direct calls would use the fast entry point, but if you took the address, you'd get the address of the normal one.

All of this was handled by the front-end, and works fine. I think the patch eventually got ripped out of the compiler for other reasons though.

-Chris

What happens with functions that are called both inside and outside
ISR context? Do you have to codegen two copies of those?

Yes we have to clone these functions.

If you have to generate two copies of the function with different
entrypoints, the *front-end* should handle the duplication. This is
just like C++ constructors.

One really old patch that apple guys experimented in the past was a
"slow and fast call" attribute, which you could stick on function
declarations. If you added it to a function, the frontend would
generate an entry point with a standard calling convention as well as
one with a faster in-register ABI. Direct calls would use the fast
entry point, but if you took the address, you'd get the address of the
normal one.

All of this was handled by the front-end, and works fine. I think the
patch eventually got ripped out of the compiler for other reasons
though.

I see 2 problems with this approach:
1 - Front end does not know which functions need to be cloned. The
cloned functions are the ones that are called both from ISR thread and
main thread; and this information is not available until all compilation
units are merged. We collect this information in a pass at the end of
llvm-ld.
2- Based on what you propose, for example the ISR thread would need to
make indirect call to cloned function; and main thread make direct call
to original function. Apart from the performance issue on PIC16 in
relation to indirect function calls, if the main thread already makes
indirect call to the function, then the same function would be called
from both threads, and the original problem is still there

A.