Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support interrupt calling conventions #1275

Closed
wants to merge 2 commits into from
Closed

Conversation

tari
Copy link
Contributor

@tari tari commented Sep 8, 2015

More targeted approach to scratching my itch for pure-Rust interrupt handling after #1201.

Rendered.

@nagisa
Copy link
Member

nagisa commented Sep 8, 2015

Do you have any implementation ideas?


clang allows for __attribute__((naked)) functions which do not generate code for any CC and thus allow one to satisfy any CC you want with inline assembly. It also looks more general in a sense that it is not tied to interrupt ABIs of select platforms currently supported.

Perhaps

extern "none" fn …
// or
#[naked]
fn …

would be better?

@tari
Copy link
Contributor Author

tari commented Sep 8, 2015

Naked functions were covered in #1201. Implementation details may differ by system, but I've written a patch for LLVM to add an x86 interrupt calling convention (it's a pretty small patch), and some systems already have equivalent functionality in LLVM (ARM supports it via an attribute, there's a MSP430 interrupt calling convention).

Naked functions are somewhat more flexible, but lack some of the efficiency of a true interrupt calling convention. Passing a parameter (x86 error code) requires going through the C ABI so there's a defined place to put the value, and if the compiler is aware that all registers are callee-saved unnecessary spills can be avoided.

@alexcrichton alexcrichton added the T-lang Relevant to the language team, which will review and decide on the RFC. label Sep 8, 2015
@alexcrichton
Copy link
Member

I've personally always been under the impression that these sorts of interrupt handlers are basically always written in assembly by hand. I'm not sure that there's necessarily a "one size fits all" convention which we can implement and simultaneously solve all use cases of interrupt handlers. Something like naked functions inline assembly seems more appropriate here to stay in a "pure rust" world to me at least.

@tari
Copy link
Contributor Author

tari commented Sep 8, 2015

Specially-annotated ISRs written in the host language entirely are pretty common in embedded C. I've never seen it on x86, but certainly more common embedded machines like ARM, MSP430 or AVR.

The calling convention of interrupts should be well-defined- it's an architectural thing. I'm not aware of any situations where the compiler would need particularly convoluted logic for any given system. That's not to say everything can be done in completely safe code then, but I feel some compiler support is necessary to bridge the gap between the slightly-unusual-but-well-defined architectural state and deliberately-unspecified Rust calling conventions.

I mentioned above that this also allows code generation that is uniquely efficient (especially regarding register usage and spilling), and I think that's a compelling benefit over other comparable languages which lack this support, particularly given the relatively small compiler impact such an addition should have.

@Ericson2314
Copy link
Contributor

1. I definitely prefer this to naked functions.

One caveat: do you think LLVM itself would take a patch for a single "portable" interrupt calling convention? http://llvm.org/docs/LangRef.html#calling-conventions already has calling conventions which impose different restrictions based on the target arch. The only downside I see to this is that it assumes/implies that each arch doesn't use multiple different interrupt calling conventions, but is that ever violated in practice?

@tari
Copy link
Contributor Author

tari commented Sep 9, 2015

I'm not aware of any particular exceptions. X86 is a little weird since some interrupt vectors ("exceptions") require a stack adjustment before return, which in my implementation of x86 interrupts is implied by the presence of the i32 parameter.

I expect required support in LLVM would be implemented on a per-arch basis, since some architectures already have support in a couple different ways. Getting some opinions from the LLVM side might be useful, though.

@Ericson2314
Copy link
Contributor

Yeah, I'd defer to the LLVM devs opinion on this one either way. Their concerns I'd imagine would apply equally well to Rust, and wrapping whichever route they'd prefer to do the other is probably not worth the effort.

@nikomatsakis
Copy link
Contributor

I echo the sentiment of preferring this to naked fns, since you can actually write rust code within these fns and it works more-or-less as expected (iiuc), just with a distinct calling convention. I feel like I am not that well-versed in this particular area so it's hard for me to say if this is really the proper solution, but I'd not be averse to adding this in an unstable and experimental way for the time being.

@Ericson2314
Copy link
Contributor

Speaking of unstable/experiemental, it would be nice to have a feature for using any calling convention LLVM supports. [Last I heard, we just pass the calling convention along, but whitelist it first.] This would of course aid this, but also aid any other calling convention experiment that pops up.

@nikomatsakis
Copy link
Contributor

What does LLVM's support in this area look like?

@nikomatsakis
Copy link
Contributor

That is, what calling conventions does it offer for this case (if any)? And how much work is this on the Rust side? (Typically, supporting a calling convention is a combination effort.)

@tari
Copy link
Contributor Author

tari commented Sep 11, 2015

Existing LLVM support for interrupts, as I understand it:

  • On ARM the function attribute interrupt marks an ISR, causing the return sequence to be subs pc, lr, #n where n varies with the interrupt type (and the default type has n=4). I've never seen code that uses anything but the default, but if the attribute value is SWI or UNDEF n=0.
  • On MSP430 the MSP430_INTR calling convention marks an ISR, which affects availability of the hardware multiplier on some machines and emits an iret instruction on return.

The fact that ARM has these variants I was unaware of might imply we should support per-machine variants, such as arm_interrupt_fiq, generally reserving the <arch>_interrupt prefix for interrupt family calling conventions. This would probably be a more future-proof too, at the cost of a little additional complexity on our (Rust's) end. I think this warrants further research into all of the machines that are proposed to support.

As a point of reference for other implementations, the diffstat for my implementation of x86 interrupts in LLVM is about 150 lines, a large fraction of which is tests. X86 is also likely to be among, if not the most complex architecture to support.

@emberian
Copy link
Member

I want naked functions, not just this, because naked functions are useful for more than just ISRs. For example, naked functions are required if you want to write the entry point of an executable (which is mostly, but not entirely, inline assembly and then a jump to the "real" entry point - consider crt0). I want this for writing a pure-Rust userland on seL4, which I'm so close to doing besides a few assembly stubs that would be awesome to remove. They're also useful for implementing "custom" calling conventions that a JIT can call into without having to spill just to conform to the traditional calling conventions, which may be nice for certain parts of runtimes.

@dpc
Copy link

dpc commented Sep 14, 2015

Should this RFC touch the topic of dead code elimination removing functions that appear dead, but are actually called from non-trivial places (like external assembler)? I've reported it once before: rust-lang/rust#23577 . When doing some prototyping for titanos I've found that to be of much bigger problem than lack of naked functions, preventing me from getting some things done. I think most code that might need naked functions might be affected by that shortcomming as well.

If it's out of scope of that RFC, I just want to bring some attention to it, since it's at least related.

@emberian
Copy link
Member

@dpc just marking them pub should do it, unless you're compiling an executable (in which case my workaround is to build as a staticlib and then link with a stub main that calls a defined function)

@tari
Copy link
Contributor Author

tari commented Sep 14, 2015

DCE shouldn't eliminate functions provided they're either pub or referred to in code that's visible to the compiler, so yeah there shouldn't be anything special here to prevent them from being optimized out.

@dpc
Copy link

dpc commented Sep 15, 2015

@cmr What if something should not be exposed as pub? static lib seems like an OK workaround, but it's still a workaround.

@Ericson2314
Copy link
Contributor

@cmr I'd say while naked functions are strictly the most versatile, they are often not the cleanest solution. For the entry point, shouldn't top-level assembly work? For JIT, I'd argue that ad hoc / anonymous calling conventions (Watcom has something like this) are more appropriated. This is not to say we shouldn't have naked functions, just that we shouldn't use them to argue against this in the name of a strictly orthogonal features.

Then there is a matter of ease. I think it is less clear what naked functions should look like / how safe they can be. This was my own hunch, and what I gleaned from the previous discussions. Likewise, while one could argue that anonymous calling conventions are as clean/safe a solution for interrupt handlers as a new normal calling convention, implementing that seems like much more work / harder to get upstream to accept.

So just thinking about interrupt handlers, a new calling convention seems like the easiest thing to implement, and at least as clean/safe as anything else.


@dpc you can pass in symbols statically with inline asm. This solves the dead code problem, and allows one to use easily use mangled names as an extra benefit. See https://github.com/RustOS-Fork-Holding-Ground/RustOS/blob/master/src/arch/x86/cpu.rs#L68-L84 . If we had module-level asm (this reminds me we should still allow putting it inside functions for scoping purposes), I wouldn't any #[allow(dead_code)] whatsoever.

If it's done outside rust-land then it needs to be public (unless I am forgetting something very basic about linking).

@chrish42
Copy link

To add another data point regarding usage, almost all of the interrupt handlers I've written myself for embedded code have been in C. They were fast enough, and structuring the code to do as little work as possible in the interrupt handler was the most important thing for performance. For the cases where that wasn't enough, I would then rewrite in assembler.

On another angle, all the embedded C compilers I've used have had a (nonstandard) way to add an attribute to a function to mark it as a interrupt handler. So one way or another, I think there is a real need out there for this, and I think Rust would benefit (in comparison to C) from having a single, standard solution to this, supported in the language.

Also, for what it's worth, I think that naked functions, while useful, should be a separate RFC (and feature). From an ergonomic standpoint at least, I think people who set out to write an interrupt handler are going to look for something with "interrupt" in the name, and having them look for and use a much more general concept (with more potential for foot-guns too) to do this work will make things less user-friendly.

@nrc nrc self-assigned this Sep 17, 2015
@jackpot51
Copy link

rust-lang/rust#29189 attempts to address this in the following manner:

Description:
The naked attribute allows functions to be defined that do not have standard wrappers, like stack frames, local variables, or a function return.

This change exposes the LLVM Naked function attribute by using the #[naked] attribute on a Rust function.

Because it is unsafe to use, it is feature gated behind #![feature(naked)]. I have already verified that Rust builds correctly with this change.

Rationale:
The current rustc provides no mechanism for defining these functions correctly, which is why this change is required for operating systems development in Rust, such as the development of Redox.

This is highly important when designing operating systems, due to requirements for interrupt handlers and context switching, or other functions like green threads, virtual machines, and emulators. These require predictable assembly output, without any wrapping instructions. This has limited some features to only being possible in external assembly or C.

http://wiki.osdev.org/Interrupt_Service_Routines#Naked_Functions

Example:
A very simple example of use, inside an interrupt handler:

#![feature(naked)]

#[naked]
#[cfg(target_arch = "x86")]
fn interrupt_handler(){
    asm!("iretd");
}

Which will generate:

interrupt_handler:
   iretd

This is more correct than the current operation:

#[cfg(target_arch = "x86")]
fn interrupt_handler(){
    asm!("iretd");
}

Which will generate something similar to:

interrupt_handler:
   push ebp
   mov ebp, esp
   iretd
   ret

@jackpot51
Copy link

https://github.com/redox-os/redox is being switched over to using the new attribute, which will be up shortly at https://github.com/redox-os/redox/tree/naked

@arcnmx
Copy link

arcnmx commented Oct 20, 2015

Welp, my 1 to landing #[naked] behind a feature gate because it's the kind of feature that's worth using a patched compiler for when it's missing, but that does tend to be a pain... The only real change I'd suggest is requiring naked functions to be unsafe.

@jackpot51
Copy link

I have added tests here:
https://github.com/redox-os/rust/blob/a9fe6913d9d94202c8bd9bac8c8149351ee57273/src/test/codegen/naked-functions.rs

They are very complete tests and they pass with Rust master

@jackpot51
Copy link

@nrc You should reopen this PR as it still applies if the RFC were to be accepted.

Otherwise, I think I am done working with Rust

@ghost
Copy link

ghost commented Oct 21, 2015

👍

@nikomatsakis
Copy link
Contributor

@jackpot51

I was reading over your comments in more detail in this thread (I was about to board an airplane when I wrote my previous comment, and hence it was somewhat off the cuff) and I was hoping you could help elucidate something for me.

As you can see, these are llvm naked functions. That does not mean they have no ABI, just that they do not use local stack frames by default. They will use stack frames if absolutely required.

This means that naked is more of an optimization hint, which allows inline asm to have complete control of the stack.

This actually sounds rather different from what I've read about naked functions in the past. I did a quick search through the LLVM docs to see if it would add more detail but only found this:

naked: This attribute disables prologue / epilogue emission for the function. This can have very system-specific consequences.

Obviously this doesn't really give a lot of hints as to what the semantics are of (say) an alloca or a parameter or anything else. :) Is it as @nagisa suggests, that one inherits the ABI from the surrounding function? So can I thus think of this as "generate the assembly you would have generated, but just literally not emitting the prologue/epilogue"?

Typically in the prologue, some kind of frame pointer is established, whether it be through a dedicated register or just using the stack pointer, and excess arguments are accessed through this pointer. If we skip the prologue, what happens then when you try to access these arguments? My assumption here is that it is undefined and it might work or might not, depending. (Similarly, accessing any arguments on the old x86-32 ABIs is a bit risky.)

@nikomatsakis
Copy link
Contributor

@jackpot51

Ah, and one other question I wanted clarification on. You mentioned a number of places you had put this preliminary #[naked] attribute to work. In those cases:

  1. How many are "pure assembly" fns, versus functions that contain Rust code?
    • Do they use local variables? Parameters?
  2. In which of those cases would some this ABI-based approach be useful? Where would it not be sufficient?

@jackpot51
Copy link

@nikomatsakis take a look at Context::switch in https://github.com/redox-os/redox/blob/master/kernel/common/context.rs

You can see that this function uses local variables and struct parameters. It requires exact stack pushes and pops.

Local variables prefer registers with naked, and stack frames are never required (look at eliminate-frame-pointer in the rustc target definition).

Rust code works fine inside a naked function. All of my examples are unsafe functions with a mix of Rust and inline asm.

I will write more complex tests if required.

@mkpankov
Copy link

I'd like to also point out another data point of where explicitly naked functions are useful: ELF loader.

I'll describe only one possible implementation, but this implementation was indeed used by me in the past. I implemented the loader in C previously.

ELF loader's entry point itself may be passed parameters on stack (the so called auxiliary vector). If the loader is self-relocating, it may need these parameters right at the entry point to relocate itself.

I had the self-relocation code in ASM. If I would want to write it in Rust instead, it'd have to be naked function.

There is more to it - nakedness alone wouldn't suffice, and there still will be reasons to write such code in ASM, but it's a step towards that goal.

Then, there is another place where nakedness is also required: a function that does lazy resolution of symbols after the ELF is loaded. It also has to be naked, as per ARM ELF lazy resolution ABI there are parameters on stack and in registers (IIRC).

One might argue that all these cases should/might be added as separate calling conventions. Like, I don't know, #[elf_loader_entry] and #[elf_lazy_resolution_entry]. But that's actually not realistic: there are a lot of different cases. Layout of parameters is different in every case. In the end a trampoline that reads all the required stuff off stack and registers to real local variables is needed. Manual signature tuning is used: I pretend my function receives long foo and long bar and foo and bar get right values because I know the ABI. But putting every possible ABI into compiler is complicating things. What if I develop my own executable format?

All in all, #[naked] is unsafe, but universal. Calling convention attributes are safe but limited. I believe both options should be present.

* `x86`
* `x86_64`

Each of these architectures combines with a `_interrupt` suffix to form the full
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you expand on the motivation for having separate ABIs for every architecture? It seems like this is entirely determined by the hardware (i.e. it doesn't make sense to use the ARM interrupt CC on x86), so we could have extern "interrupt" fn ... and have its behaviour do the right thing for the target platform, like the C and System CCs do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This came up in discussion too; I like the idea of having a single default interrupt CC available, but there needs to be at least a little more flexibility. Some systems have multiple interrupt CCs, such as some (older) ARMs where there are 'fast' and 'slow' interrupts which differ in what resources the hardware will automatically preserve.

We could certainly add a "interrupt" CC with defined defaults for each architecture, but that alone is not always sufficiently expressive. Some of the architecture prefixes could be done away with, but at that point I think it's appropriate to have the architecture-prefixed versions available for all systems regardless of which ones have multiple variants.


I suppose some discussion of that (and possible future expansion within the interrupt families) is in order here.

@nikomatsakis
Copy link
Contributor

First, I have some questions about this RFC (the interrupt one):

In the prologue, it mentions some assembly code that redirects all interrupts to a central handler, which presumably switches on the interrupt number, and cites this as being less efficient. This does seem less efficient than directly dispatching to the correct code, it's true, but it's not clear to me why the small assembly hooks cannot redirect to distinct functions (interrupt1, interrupt2, etc), in which case the efficiency cost is largely removed, unless we are concerned about the call instruction itself. Is that true?

I was thinking about the relative generality of this and naked fns. It seems clear that naked fns would allow for one to write arbitrary "interrupt" logic. But, unless you write Rust code in the naked fns, you probably couldn't dispatch to a regular rust function without at least a call instruction (albeit a statically determined one). If you did write Rust code in the naked fn, perhaps we could convince LLVM to inline the body.


Second, on the topic of naked fns, we had some discussion in the @rust-lang/lang meeting yesterday. There was general feeling that it may be worth adding some support, since it seems to be a common feature across C compilers and also one that has its share of proponents, but we would want some restrictions:

  • the function being labeled as #[naked] ought to be declared unsafe, since it seems clear that omitting the prolog can have unsafe effects (does anyone think otherwise?);
  • the function being labeled as #[naked] ought to have a non-Rust ABI (e.g., extern "C"), because the Rust ABI is not defined and hence it is not possible to write assembly code that accesses parameters and the like in a truly correct way.

I think we were leaning towards the idea of preferring naked fns to this RFC, on the grounds that naked fns are more general. The interrupt ABIs are clearly nicer to use, but it's a bit frustrating that there would likely be an open-ended number of them required, and there are also use cases that don't seem to fit into this mold. In contrast, using a naked fn, it seems like one could easily write a cargo build preprocessor (or (unstable) syntax extension) that takes an input like the ones shown in this RFC and produces naked fns with the appropriate prolog/epilogs.

@arcnmx
Copy link

arcnmx commented Oct 23, 2015

In contrast, using a naked fn, it seems like one could easily write a cargo build preprocessor (or (unstable) syntax extension) that takes an input like the ones shown in this RFC and produces naked fns with the appropriate prolog/epilogs.

Yup, this is why it's a nice building block for whatever else you may need to do. I'm not quite against also including the more uncommon CCs like interrupt handlers (though I feel they'd belong better in a library if at all possible) but naked functions will always be useful regardless.

the function being labeled as #[naked] ought to be declared unsafe, since it seems clear that omitting the prolog can have unsafe effects (does anyone think otherwise?);

The main issue I have with this is that doesn't allow you to assert that you're maintaining the ABI/invariants/etc. and provide a safe implementation. Wrapping in safe functions would generate prologues and such. I'd prefer to take an approach like naming the attribute unsafe_naked, or maybe make it a lint that errors by default but can be bypassed with #[allow(unsafe_naked)]? Could also do funky things like disallow anything except a trailing function call in a non-unsafe naked function...

Also in general unsafe functions are just very granular, they don't allow you to isolate your unsafe code into unsafe blocks. I'm not completely against it, but having no escape hatch when you're sure of what you're doing seems a bit awkward.

the function being labeled as #[naked] ought to have a non-Rust ABI (e.g., extern "C"), because the Rust ABI is not defined and hence it is not possible to write assembly code that accesses parameters and the like in a truly correct way.

Sounds fair.

@tari
Copy link
Contributor Author

tari commented Oct 23, 2015

This does seem less efficient than directly dispatching to the correct code, it's true, but it's not clear to me why the small assembly hooks cannot redirect to distinct functions (interrupt1, interrupt2, etc), in which case the efficiency cost is largely removed, unless we are concerned about the call instruction itself. Is that true?

They could, but it may not be possible to do that type of single-dispatch in a system where interrupt vectors are not statically assigned.

We not only avoid a call instruction if the compiler is aware of the interrupt calling convention, but avoid spilling registers which end up unused (which is impossible to do with a preprocessing step). An ISR might perform a very simple operation like increment a counter, where the cost of unconditionally spilling all of the registers dwarfs the actual useful operation- if the compiler knows the ABI, it can only spill the register(s) which are used within the function. If the ISR is a non-leaf function it could even be unconcerned with registers which are callee-save, providing tangible benefits even to complex code.

@ticki
Copy link
Contributor

ticki commented Oct 30, 2015

What's the state of this?

@nikomatsakis
Copy link
Contributor

@arcnmx good point about unsafety. You convinced me it is not correct to require the fn to be declared unsafe: perhaps the rule should be that either the fn is declared unsafe or the body is an unsafe block. This way you can choose whether you are pushing the unsafety to your caller or whether you are exporting a safe interface. Kind of hacky but feels like a better direction. We could also go the unsafe_naked route in the attribute name I guess, though it seems somewhat less expressive. I am not terribly keen on "unsafe_" attributes, though we have a few -- I'd like to find a more elegant looking way of declaring that an attribute is unsafe to apply. :)

@nikomatsakis
Copy link
Contributor

We discussed this in the @rust-lang/lang meeting today. The feeling was that we ought to close this RFC for now and instead pursue an approach based on naked fns. This could mean re-opening #1201, though it might be nice to start out with a fresh RFC and fresh discussion as well. The RFC should be amended to address the concerns described previously, except for unsafety (see previous comment).

This is not to say that we are not amenable to interrupt calling conventions at some later point. They seem like a useful convenience feature that might also allow for more optimization in common cases (as @tari points out here). However, it seems like this and naked fns clearly overlap, and naked fns is the more general of the two, so it'd be better to start with naked fns and see how far we get.

Does this seem reasonable to everyone? If so, @tari, do you want to re-open/amend the naked fn RFC? If not, what are your concerns. Thanks!

@jackpot51
Copy link

@nikomatsakis I like it! Thanks!

@arcnmx
Copy link

arcnmx commented Nov 6, 2015

@nikomatsakis mm, requiring either an unsafe function or an unsafe block (or, in other words, requiring that the entirety of the function body is unsafe, whether implicitly or explicit) is the ideal case. It's unfortunate that it feels hacky, like a one-off rule for this particular attribute. unsafe_naked is about the same ugliness, really. Not sure that we have any better options though :/

I'd probably vote for landing it behind a feature gate as we work out the specifics around the unsafety and other lints/restrictions.

@nikomatsakis
Copy link
Contributor

@arcnmx

I'd probably vote for landing it behind a feature gate as we work out the specifics around the unsafety and other lints/restrictions.

Yes. I didn't say it explicitly, but I was assuming the same. This is standard procedure at this point anyway: all new features etc land behind feature gates until an explicit decision is made to "unfeature-gate" them by the appropriate subteam (and with core team approval).

@arcnmx
Copy link

arcnmx commented Nov 6, 2015

@nikomatsakis I just meant to bring attention to it as an interim step to reopen / tentatively land #29189 while we work out the safety details now that we're considering the naked RFC again. But I'm probably just being impatient :P

@Ericson2314
Copy link
Contributor

@nikomatsakis Did the discussion cover module-level inline assembly? It's just as general without the semantic ambiguities of naked functions.

@nikomatsakis
Copy link
Contributor

@Ericson2314 hmm, yes, I was in favor of this for a while, but it hasn't been discussed lately. To be honest I think I kind of forgot about it as an alternative. One concern I had is that you need a Rust name (and signature) for the labels you're going to be defining. Do you envision that users would create a extern "C" fn in order to have something they can call (with the definition supplied by module-level inline assembly)? Would that even work (I feel like on some platforms it might not)?

@Ericson2314
Copy link
Contributor

No worries, few mentioned it in this thread.

Based on https://github.com/RustOS-Fork-Holding-Ground/RustOS/blob/master/src/arch/x86/cpu.rs#L68-L84 I think the extern trick would work---the linker seems to handle references between the Rust and inline assembly in either direction.

I just realized that LLVM currently doesn't support extended inline assembly at the module level. That's a bummer. For example, note the "s" for symbol constraint in my linked source. Even better than extern, I'd love to be able to declare a function in the current module, and use such a constraint to put the properly mangled name in inline assembly for the definition.

I was going to try to see if that worked in C using namespaces and forward declarations, but it appears that Clang does not expose the "s" constraint. Yay.

@nikomatsakis
Copy link
Contributor

@arcnmx

I just meant to bring attention to it as an interim step to reopen / tentatively land #29189 while we work out the safety details now that we're considering the naked RFC again. But I'm probably just being impatient :P

I wouldn't necessarily be opposed to moving forward with SOME implementation, so long as there is an agreement to update the implementation following the outcome of the RFC discussion (we've done this before on other occasions). But it's usually nice to at least have the RFC open and pending.

@nikomatsakis
Copy link
Contributor

For now then I will close this in favor of a naked fn approach (#1201). Thanks all.

@tari
Copy link
Contributor Author

tari commented Jan 9, 2016

Datum: LLVM support for x86 interrupts landed in r256155, and should be present in releases beginning with 3.8.

frewsxcv added a commit to frewsxcv/rust that referenced this pull request Mar 2, 2017
…ention, r=nagisa

Add support for the x86-interrupt calling convention

This calling convention can be used for definining interrupt handlers on 32-bit and 64-bit x86 targets. The compiler then uses `iret` instead of `ret` for returning and ensures that all registers are restored to their
original values.

Usage:

```rust
extern "x86-interrupt" fn handler(stack_frame: &ExceptionStackFrame) {…}
```

for interrupts and exceptions without error code and

```rust
extern "x86-interrupt" fn handler_with_err_code(stack_frame: &ExceptionStackFrame,
                                                error_code: u64) {…}
```

for exceptions that push an error code (e.g., page faults or general protection faults). The programmer must ensure that the correct version is used for each interrupt.

For more details see the [LLVM PR][1] and the corresponding [proposal][2].

[1]: https://reviews.llvm.org/D15567
[2]: http://lists.llvm.org/pipermail/cfe-dev/2015-September/045171.html

It is also possible to implement interrupt handlers on x86 through [naked functions](https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md). In fact, almost all existing Rust OS projects for x86 use naked functions for this, including [Redox](https://github.com/redox-os/kernel/blob/b9793deb59c7650f0805dea96adb6b773ad99336/arch/x86_64/src/lib.rs#L109-L147), [IntermezzOS](https://github.com/intermezzOS/kernel/blob/f959cc18c78b1ba153f3ff7039d9ecc07f397628/interrupts/src/lib.rs#L28-L72), and [blog_os](https://github.com/phil-opp/blog_os/blob/844d739379ffdea6a7ede88365ec6e21a725bbf5/src/interrupts/mod.rs#L49-L64). So support for the `x86-interrupt` calling convention isn't absolutely needed.

However, it has a number of benefits to naked functions:

- **No inline assembly needed**: [Inline assembly](https://doc.rust-lang.org/book/inline-assembly.html) is highly unstable and dangerous. It's pretty easy to mess things up. Also, it uses an arcane syntax and requires that the programmer knows x86 assembly.
- **Higher performance**: A naked wrapper function always saves _all_ registers before calling the Rust function. This isn't needed for a compiler supported calling convention, since the compiler knows which registers are clobbered by the interrupt handler. Thus, only these registers need to be saved and restored.
- **Safer interfaces**: We can write a `set_handler` function that takes a `extern "x86-interrupt" fn(&ExceptionStackFrame)` and the compiler ensures that we always use the right function type for all handler functions. This isn't possible with the `#[naked]` attribute.
- **More convenient**: Instead of writing [tons of assembly boilerplate](https://github.com/redox-os/kernel/blob/b9793deb59c7650f0805dea96adb6b773ad99336/arch/x86_64/src/lib.rs#L109-L147) and desperately trying to improve things [through macros](https://github.com/phil-opp/blog_os/blob/844d739379ffdea6a7ede88365ec6e21a725bbf5/src/interrupts/mod.rs#L17-L92), we can just write [code like this](https://github.com/phil-opp/blog_os/blob/e6a61f9507a4c4fef6fb4e3474bc596391bc97d2/src/interrupts/mod.rs#L85-L89).
- **Naked functions are unreliable**: It is allowed to use Rust code inside a naked function, which sometimes works and sometimes not. For example, [calling a function](https://github.com/redox-os/kernel/blob/b9793deb59c7650f0805dea96adb6b773ad99336/arch/x86_64/src/lib.rs#L132) through Rust code seems to work fine without function prologue, but [code declaring a variable](https://is.gd/NQYXqE) silently adds a prologue even though the function is naked (look at the generated assembly, there is a `movl` instruction before the `nop`).

**Edit**: See the [tracking issue](rust-lang#40180) for an updated list of issues.

Unfortunately, the implementation of the `x86-interrupt` calling convention in LLVM has some issues that make it unsuitable for 64-bit kernels at the moment:

- LLVM always tries to backup the `xmm` registers on 64-bit platforms even if the target doesn't support SSE. This leads to invalid opcode exceptions whenever an interrupt handler is invoked. I submitted a fix to LLVM in [D29959](https://reviews.llvm.org/D29959). The fix is really small (<10 lines), so maybe we could backport it to [Rust's LLVM fork](https://github.com/rust-lang/llvm)?. **Edit**: The fix was merged to LLVM trunk in [rL295347](https://reviews.llvm.org/rL295347). Backported in rust-lang/llvm#63.

- On targets with SSE support, LLVM uses the `movaps` instruction for saving the `xmm` registers, which requires an alignment of 16. For handlers with error codes, however, the stack alignment is only 8, so a alignment exception occurs. This issue is tracked in [bug 26413](https://bugs.llvm.org/show_bug.cgi?id=26413). ~~Unfortunately, I don't know enough about LLVM to fix this.~~ **Edit**: Fix submitted in [D30049](https://reviews.llvm.org/D30049).

This PR adds experimental support for this calling convention under the `abi_x86_interrupt` feature gate. The implementation is very similar to rust-lang#38465 and was surprisingly simple :).

There is no accepted RFC for this change. In fact, the [RFC for interrupt calling convention](rust-lang/rfcs#1275) from 2015 was closed in favor of naked functions. However, the reactions to the recent [PR](rust-lang#38465) for a MSP430 interrupt calling convention were [in favor of experimental interrupt ABIs](rust-lang#38465 (comment)).

- [x] Add compile-fail tests for the feature gate.
- [x] Create tracking issue for the `abi_x86_interrupt` feature (and link it in code). **Edit**: Tracking issue: rust-lang#40180
- [x] Backport [rL295347](https://reviews.llvm.org/rL295347) to Rust's LLVM fork. **Edit**: Done in rust-lang/llvm#63

@tari @steveklabnik @jackpot51 @ticki @hawkw @thepowersgang, you might be interested in this.
frewsxcv added a commit to frewsxcv/rust that referenced this pull request Mar 2, 2017
…ention, r=nagisa

Add support for the x86-interrupt calling convention

This calling convention can be used for definining interrupt handlers on 32-bit and 64-bit x86 targets. The compiler then uses `iret` instead of `ret` for returning and ensures that all registers are restored to their
original values.

Usage:

```rust
extern "x86-interrupt" fn handler(stack_frame: &ExceptionStackFrame) {…}
```

for interrupts and exceptions without error code and

```rust
extern "x86-interrupt" fn handler_with_err_code(stack_frame: &ExceptionStackFrame,
                                                error_code: u64) {…}
```

for exceptions that push an error code (e.g., page faults or general protection faults). The programmer must ensure that the correct version is used for each interrupt.

For more details see the [LLVM PR][1] and the corresponding [proposal][2].

[1]: https://reviews.llvm.org/D15567
[2]: http://lists.llvm.org/pipermail/cfe-dev/2015-September/045171.html

It is also possible to implement interrupt handlers on x86 through [naked functions](https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md). In fact, almost all existing Rust OS projects for x86 use naked functions for this, including [Redox](https://github.com/redox-os/kernel/blob/b9793deb59c7650f0805dea96adb6b773ad99336/arch/x86_64/src/lib.rs#L109-L147), [IntermezzOS](https://github.com/intermezzOS/kernel/blob/f959cc18c78b1ba153f3ff7039d9ecc07f397628/interrupts/src/lib.rs#L28-L72), and [blog_os](https://github.com/phil-opp/blog_os/blob/844d739379ffdea6a7ede88365ec6e21a725bbf5/src/interrupts/mod.rs#L49-L64). So support for the `x86-interrupt` calling convention isn't absolutely needed.

However, it has a number of benefits to naked functions:

- **No inline assembly needed**: [Inline assembly](https://doc.rust-lang.org/book/inline-assembly.html) is highly unstable and dangerous. It's pretty easy to mess things up. Also, it uses an arcane syntax and requires that the programmer knows x86 assembly.
- **Higher performance**: A naked wrapper function always saves _all_ registers before calling the Rust function. This isn't needed for a compiler supported calling convention, since the compiler knows which registers are clobbered by the interrupt handler. Thus, only these registers need to be saved and restored.
- **Safer interfaces**: We can write a `set_handler` function that takes a `extern "x86-interrupt" fn(&ExceptionStackFrame)` and the compiler ensures that we always use the right function type for all handler functions. This isn't possible with the `#[naked]` attribute.
- **More convenient**: Instead of writing [tons of assembly boilerplate](https://github.com/redox-os/kernel/blob/b9793deb59c7650f0805dea96adb6b773ad99336/arch/x86_64/src/lib.rs#L109-L147) and desperately trying to improve things [through macros](https://github.com/phil-opp/blog_os/blob/844d739379ffdea6a7ede88365ec6e21a725bbf5/src/interrupts/mod.rs#L17-L92), we can just write [code like this](https://github.com/phil-opp/blog_os/blob/e6a61f9507a4c4fef6fb4e3474bc596391bc97d2/src/interrupts/mod.rs#L85-L89).
- **Naked functions are unreliable**: It is allowed to use Rust code inside a naked function, which sometimes works and sometimes not. For example, [calling a function](https://github.com/redox-os/kernel/blob/b9793deb59c7650f0805dea96adb6b773ad99336/arch/x86_64/src/lib.rs#L132) through Rust code seems to work fine without function prologue, but [code declaring a variable](https://is.gd/NQYXqE) silently adds a prologue even though the function is naked (look at the generated assembly, there is a `movl` instruction before the `nop`).

**Edit**: See the [tracking issue](rust-lang#40180) for an updated list of issues.

Unfortunately, the implementation of the `x86-interrupt` calling convention in LLVM has some issues that make it unsuitable for 64-bit kernels at the moment:

- LLVM always tries to backup the `xmm` registers on 64-bit platforms even if the target doesn't support SSE. This leads to invalid opcode exceptions whenever an interrupt handler is invoked. I submitted a fix to LLVM in [D29959](https://reviews.llvm.org/D29959). The fix is really small (<10 lines), so maybe we could backport it to [Rust's LLVM fork](https://github.com/rust-lang/llvm)?. **Edit**: The fix was merged to LLVM trunk in [rL295347](https://reviews.llvm.org/rL295347). Backported in rust-lang/llvm#63.

- On targets with SSE support, LLVM uses the `movaps` instruction for saving the `xmm` registers, which requires an alignment of 16. For handlers with error codes, however, the stack alignment is only 8, so a alignment exception occurs. This issue is tracked in [bug 26413](https://bugs.llvm.org/show_bug.cgi?id=26413). ~~Unfortunately, I don't know enough about LLVM to fix this.~~ **Edit**: Fix submitted in [D30049](https://reviews.llvm.org/D30049).

This PR adds experimental support for this calling convention under the `abi_x86_interrupt` feature gate. The implementation is very similar to rust-lang#38465 and was surprisingly simple :).

There is no accepted RFC for this change. In fact, the [RFC for interrupt calling convention](rust-lang/rfcs#1275) from 2015 was closed in favor of naked functions. However, the reactions to the recent [PR](rust-lang#38465) for a MSP430 interrupt calling convention were [in favor of experimental interrupt ABIs](rust-lang#38465 (comment)).

- [x] Add compile-fail tests for the feature gate.
- [x] Create tracking issue for the `abi_x86_interrupt` feature (and link it in code). **Edit**: Tracking issue: rust-lang#40180
- [x] Backport [rL295347](https://reviews.llvm.org/rL295347) to Rust's LLVM fork. **Edit**: Done in rust-lang/llvm#63

@tari @steveklabnik @jackpot51 @ticki @hawkw @thepowersgang, you might be interested in this.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet