Repository navigation
RFC: extern "Rust_manyregs" ABI #4018
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| - Feature Name: manyregs_abi | ||
| - Start Date: 18-10-2026 | ||
| - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) | ||
| - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) | ||
|
|
||
| ## Summary | ||
| [summary]: #summary | ||
|
|
||
| Add an ABI (provisional name: "manyregs") that passes arguments in registers as much | ||
| as possible. | ||
|
|
||
| ## Motivation | ||
| [motivation]: #motivation | ||
|
|
||
| Most of the time, passing arguments in registers is significantly faster than passing | ||
| them by stack. | ||
|
|
||
| At the moment, most ABIs only allow a limited number of registers to be used for | ||
| passing or returning arguments - especially for vector code. | ||
|
|
||
| The idea of the `extern "Rust_manyregs"` ABI is to allow for as many arguments as | ||
| allowed by the architecture to be passed directly in registers. This would be | ||
| especially relevant in specialized SIMD DSP kernels, but such function calls could | ||
| provide subtle speedups in other locations too. | ||
|
|
||
| ## Guide-level explanation | ||
| [guide-level-explanation]: #guide-level-explanation | ||
|
|
||
| An `extern "Rust_manyregs"` function uses as many registers as possible for argument | ||
| passing. This can be faster than the standard Rust ABI in some cases. | ||
|
|
||
| Unwinding through a "manyregs" function causes termination. | ||
|
|
||
| ## Reference-level explanation | ||
| [reference-level-explanation]: #reference-level-explanation | ||
|
|
||
| The specifics of `Rust_manyregs` are architecture dependent and somewhat dependant on | ||
| what LLVM would allow. It is an internal ABI, and thus no explicit guarantees are made | ||
| (especially across compiler versions). | ||
|
|
||
| As this ABI passes vectors by registers, it is subject to the same correctness checks | ||
| as the extern "C" ABI - in particular, it is an error to pass an argument that would | ||
| be passed by register if a target feature were present if the feature is not present. | ||
|
|
||
| ## Drawbacks | ||
| [drawbacks]: #drawbacks | ||
|
|
||
| It is one more ABI. It would need some amount of LLVM support. | ||
|
|
||
| If the ABI actually uses "as many registers as possible", some ISA extensions | ||
| (i.e. avx512 on x86) can extend the number of registers available. Thus, | ||
| whether avx512 is enabled on the function can change the ABI, which can lead to | ||
| UB. | ||
|
Comment on lines
+50
to
+53
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a huge thing. rustc currently allows building one crate with
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't even need multiple crates, I comment on this below in the RFC, but I have three possible solutions in mind (in order of personal preference):
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO it seems better to have the ABI depend on which ABI-changing target features you have in the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO it would be much better to start with the low hanging fruit, which is what can be done without language work. If we can use the registers available in the target's baseline then that already gets us quite a lot:
(All of those probably have a few reserved GPRs) That can be done to the default
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we can do pass-by-value of ymm/zmm registers on x86 without breaking backwards compatibility (see https://github.com/rust-lang/rust/blob/b6eeb93c992027a03e1fd120b6652546396b142c/compiler/rustc_target/src/callconv/mod.rs#L799) - we could do that with a new opt-in ABI. I don't believe I'm also happy to just expose more ABIs currently existing in LLVM as a simpler initial approach, if we think that would be better.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
So, yes, but I'm not looking at only the described change. There are a few separate aspects of the Zulip discussion and what's in this RFC (from a few different people):
The status quo as of 1.99 https://rust.godbolt.org/z/Exo36YYKd:
This RFC is proposing new language features that are going to be rather difficult to specify and only help if you opt in. I think it's more useful to work on the first three items than the last two since they can apply to the default Rust ABI, meaning all users benefit, and it doesn't need any language changes.
It doesn't implement our ideal ABI at this time, but the description at https://llvm.org/docs/LangRef.html#calling-conventions sounds like its goal fits our needs. If the LLVM maintainers are open to adjusting it to pass and return with more registers, then that's a lot less effort than adding a new ABI. E.g. I don't see why
While I do think it would be nice to have the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's changing with Rust 1.100: rust-lang/rust#161583
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is the main difficulty of the RFC and will need language-level work (as in, user-visible error messages). The RFC is IMO quite incomplete without a proposed solution of this issue, as the tradeoffs change significantly depending on the solution that is picked. This question is too central to delegate it to the future as an "unresolved question". |
||
|
|
||
|
|
||
| ## Rationale and alternatives | ||
| [rationale-and-alternatives]: #rationale-and-alternatives | ||
|
|
||
| See the "motivation" section. | ||
|
|
||
| The naming of the ABI string could definitely be improved. | ||
|
|
||
| I propose disallowing unwinding through such functions because they are usually | ||
| performance-critical and do not unwind, but we could allow unwinding or we could | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you provide exact technical motivation/examples to warrant this limitation. My own handwavey intuition is that there is going to be little performance impact to functions that would not otherwise unwind anyway and setting up a handler to forcibly abort here can have a negative impact (which we deal with on
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't actually have a strong opinion here - I had a handwavy feeling that the possibility of unwinding of such functions could produce worse asm, but I don't have any evidence. |
||
| add a `-unwind` variant. | ||
|
|
||
| We could have the Rust ABI behave this way. I believe this would break compatibility | ||
| in cases in which one passes by value register-like arguments to functions that don't | ||
| have the required target features. | ||
|
|
||
|
|
||
| ## Prior art | ||
| [prior-art]: #prior-art | ||
|
|
||
| On x86, `x86_regcallcc` passes up to 16 integer / vector arguments via registers. | ||
|
|
||
| The proposed `rust_preserve_none_cc` ABI (https://github.com/rust-lang/rust/issues/151401) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would this CC be implemented differently in the backend compared to
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know if a new ABI in this direction could be implemented without adding a new ABI to LLVM - I suspect not.
On x86, the closest equivalent is |
||
| is somewhat similar in spirit. | ||
|
|
||
| ## Unresolved questions | ||
| [unresolved-questions]: #unresolved-questions | ||
|
|
||
| - What should we do with ISAs that extend the set of available vector registers? | ||
| - Have a separate ABI string for those | ||
| - Limit the register count to the minimum guaranteed available | ||
| - Limit the register count to the minimum that the arguments guarantee is | ||
| available (i.e. 32 if a _mm512 argument is present) | ||
| - How should the ABI behave in terms of callee-saved / caller-saved registers? | ||
| - All registers are caller-saved | ||
| - Some number of registers are callee-saved unless they are used as arguments | ||
| - ??? | ||
|
|
||
| ## Future possibilities | ||
| [future-possibilities]: #future-possibilities | ||
|
|
||
| Does it make sense to have the "Rust" ABI behave in a similar way? | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comes out of nowhere and is only explained later, making the RFC hard to read.
View changes since the review