Uh oh!
There was an error while loading. Please reload this page.
calling convention for MSP430 interrupts - #38465
Conversation
rust-highfive
commented
Dec 19, 2016
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @alexcrichton (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
petrochenkov
commented
Dec 19, 2016
This should probably be feature gated as well - https://github.com/rust-lang/rust/blob/master/src/libsyntax/feature_gate.rs#L963 (+ the |
japaric
commented
Dec 19, 2016
I was wondering if we had feature gates for new ABIs. Thanks for the heads up, @petrochenkov. I'll add the gate in a bit. |
alexcrichton
commented
Dec 19, 2016
Yeah we'll definitely want to gate this, but otherwise seems ok to me. In that sense that name shouldn't matter too much for now. |
pftbest
commented
Dec 19, 2016
I have tested this PR, and it works fine. This rust code: #[no_mangle]#[link_section = "__interrupt_vector_10"]pubstaticTIM0_VECTOR:unsafeextern"msp430-interrupt"fn() = tim0;unsafeextern"msp430-interrupt"fntim0(){P1OUT.write(0x00);}Generates this assembly: It looks good to me. |
japaric
commented
Dec 19, 2016
@pftbest Excellent r? @alexcrichton Added the feature gate. Should I create a tracking issue for the stability of the new ABI? |
alexcrichton
commented
Dec 20, 2016
Looks like the travis failure is legitimate, and could you also add a test to ensure this ABI is gated? And yeah let's add a tracking issue. |
petrochenkov
commented
Dec 20, 2016
(ABI stability tests are centralized in |
japaric
commented
Dec 20, 2016
@alexcrichton Updated. Still testing locally. Thanks for the info, @petrochenkov! |
Okay, so if my memory doesn’t mislead me, we added |
Before even considering using this ABI as an argument for stabilizing naked functions, we should first verify that this particular ABI can actually be implemented using naked functions (I don't know the answer). If that's the case, this unstable ABI could be removed in favor of that (but ergonomics should be considered before reaching a decision). Personally, I'd prefer to land a sure and ergonomic (but unstable) way to write interrupt handlers for this architecture, this ABI, right now and then consider whether such ABI should be stabilized or removed/deprecated in favor of (stable) naked functions. |
eddyb
commented
Dec 20, 2016
TBH I would prefer an |
pftbest
commented
Dec 20, 2016
@nagisa unsafeextern"msp430-interrupt"fntim0(){not_inlined_foo();}I can write it using naked functions like this: #[naked]unsafeextern"C"fntim1(){asm!{" push r15 push r14 push r13 push r12 "}not_inlined_foo();asm!{" pop r12 pop r13 pop r14 pop r15 reti "}}It does not look pretty but at least it works the same. unsafeextern"msp430-interrupt"fntim0(){P1OUT.write(0x00);}This code I can't write as a naked function, because I don't know how many registers I need to save for this inlined Also we would like to have support for the |
bors
commented
Dec 21, 2016
☔ The latest upstream changes (presumably #38099) made this pull request unmergeable. Please resolve the merge conflicts. |
8f1bc9e to
2de9578Comparejaparic
commented
Dec 21, 2016
rebased @eddyb an "interrupt" ABI makes sense to me. I'd like to have at least one more interrupt-ish ABI (e.g. "avr-interrupt") before prototyping that; that way we can test in properly. @pftbest those are my concerns exactly: ergonomics and error-proneness. In theory, it might be possible to create a syntax extension that implements "msp430-interrupt" on top of #[naked] but I don't know if that could have all the features that a real ABI enables (cf. the __bic_SR_register_on_exit intrinsic). |
nagisa
commented
Dec 21, 2016
The C ABI for your platform should have somewhere a list of caller-saved (volatile) and callee-saved (non-volatile) registers. The saving and restoring as well as call overhead are unfortunate, though and this is the reason why an interrupt ABI is strictly better than naked functions. That being said, I would still rather we only had either one (naked fns) or the other (interrupt ABIs). The issue with 2nd thing is that LLVM obviously does not support your arbitrary hardware interrupt ABI, which is why |
pftbest
commented
Dec 21, 2016
I was talking about it in my examples. If the code contains only call to non inlined function like in the first example, it is enough to save only caller-saved registers (r12-r15). Some architectures do not have special ABI for interrupts, so they use naked functions. But for MSP430 it is available, and it would be unfortunate not being able to use it. |
pftbest
commented
Dec 21, 2016
Just to give a perspective on the overhead of using naked functions, I did a caculation. If the code is using proposed ABI it will have If the code is using naked functions it will get this numbers: You can say that if you care about this numbers you should be writing in assembly, but I think It would be great if rust could be at least as efficient as C in this regard. |
bors
commented
Dec 30, 2016
☔ The latest upstream changes (presumably #38697) made this pull request unmergeable. Please resolve the merge conflicts. |
2de9578 to
e8e5d8dComparebors
commented
Dec 31, 2016
☔ The latest upstream changes (presumably #38482) made this pull request unmergeable. Please resolve the merge conflicts. |
whitequark
commented
Jan 1, 2017
How is the C ABI (or any ABI) relevant here? An interrupt handler must save every register it uses, precisely because it is not invoked on a function boundary. |
@whitequark Since we’re talking trampolines here, you can avoid saving the non-volatile (aka. callee-saved) registers because no callee is allowed to modify them without also saving them beforehand and restoring register later, right? The Remember that I’m not arguing against interrupt ABIs. I’m arguing for having EITHER one or the other and not both. Honestly, though, I dislike naked functions and would rather have interrupt ABIs (having seen what mess |
whitequark
commented
Jan 1, 2017
Ah, so what you're suggesting is a naked function that only contains a single call instruction, which calls the actual interrupt handler. I see how this may work. Now, the $1M question: why not define the "interrupt" ABI to generate such a trampoline automatically? |
pftbest
commented
Jan 1, 2017
@nagisa What conflict do you see in having both of them? |
alexcrichton
commented
Jan 17, 2017
er nvmd, I'll check it for you, you've reviewed |
alexcrichton
commented
Jan 17, 2017
@japaric I believe this is ready to go after a rebase now. |
eddyb
commented
Jan 17, 2017
r=me w/ rebase |
04a88d6 to
f5c5bf6CompareThis calling convention is used to define interrup handlers on MSP430
microcontrollers. Usage looks like this:
``` rust
#[no_mangle]
#[link_section = "__interrupt_vector_10"]
pub static TIM0_VECTOR: unsafe extern "msp430-interrupt" fn() = tim0;
unsafe extern "msp430-interrupt" fn tim0() {
P1OUT.write(0x00);
}
```
which generates the following assembly:
``` asm
Disassembly of section __interrupt_vector_10:
0000fff2 <TIM0_VECTOR>:
fff2: 10 c0 interrupt service routine at 0xc010
Disassembly of section .text:
0000c010 <_ZN3msp4tim017h3193b957fd6a4fd4E>:
c010: c2 43 21 00 mov.b #0, &0x0021 ;r3 As==00
c014: 00 13 reti
...
```f5c5bf6 to
6296d52Comparejaparic
commented
Jan 19, 2017
@bors r=eddyb
|
bors
commented
Jan 19, 2017
📌 Commit 6296d52 has been approved by |
bors
commented
Jan 19, 2017
⌛ Testing commit 6296d52 with merge d7c4093... |
bors
commented
Jan 19, 2017
💔 Test failed - status-travis |
nikomatsakis
commented
Jan 19, 2017
This failed because you have no test for this feature gate (it's a new requirement). The very helpful error message by @est31 explains what you have to do to fix it =) |
japaric
commented
Jan 19, 2017
Thanks, @nikomatsakis. I had done that (the cfail test) for another PR but hadn't realize that it had to be done for this one too. @bors r=eddyb |
bors
commented
Jan 19, 2017
📌 Commit e928c75 has been approved by |
bors
commented
Jan 19, 2017
calling convention for MSP430 interrupts
This calling convention is used to define interrup handlers on MSP430 microcontrollers. Usage looks like this:
``` rust
#[no_mangle]
#[link_section = "__interrupt_vector_10"]
pub static TIM0_VECTOR: unsafe extern "msp430-interrupt" fn() = tim0;
unsafe extern "msp430-interrupt" fn tim0() {
P1OUT.write(0x00);
}
```
which generates the following assembly:
``` asm
Disassembly of section __interrupt_vector_10:
0000fff2 <TIM0_VECTOR>:
fff2: 10 c0 interrupt service routine at 0xc010
Disassembly of section .text:
0000c010 <_ZN3msp4tim017h3193b957fd6a4fd4E>:
c010: c2 43 21 00 mov.b #0, &0x0021 ;r3 As==00
c014: 00 13 reti
...
```bors
commented
Jan 19, 2017
☀️ Test successful - status-appveyor, status-travis |
…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.…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.
This calling convention is used to define interrup handlers on MSP430 microcontrollers. Usage looks like this:
which generates the following assembly: