-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Abi Descriptors #3910
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
base: master
Are you sure you want to change the base?
Abi Descriptors #3910
Changes from all commits
f4fc2b0
56b0158
3e1d8a2
902551b
a52c428
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,189 @@ | ||
| - Feature Name: `abi_descriptors` | ||
| - Start Date: `1/22/2026` | ||
| - RFC PR: [rust-lang/rfcs#3910](https://github.com/rust-lang/rfcs/pull/3910) | ||
| - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) | ||
|
|
||
| ## Summary | ||
| [summary]: #summary | ||
|
|
||
| Allows users to create custom **A**pplication **B**inary **I**nterfaces (ABIs) inside of Rust, allowing them to set the input registers, output registers, alignment requirements, etc. | ||
|
|
||
| ## Motivation | ||
| [motivation]: #motivation | ||
|
|
||
| Currently, the selection of ABIs available in rust is very suffecient. There is also a currently on-going implementation of `extern "custom"` functions (and possibly other items - see [#140829](https://github.com/rust-lang/rust/issues/140829)) | ||
|
|
||
| however, those are not general ABIs for all usages, this RFC refers more to an ABI that: | ||
| - can be used for regular, non-naked functions | ||
| - can be used for alignment, stacks, etc. | ||
|
|
||
| Additionally, some low-level projects need custom ABIs such as the `x86-interrupt` abi before it was added; however, there was no possible way to create a custom ABI. Resultingly, many of these projects link external libraries to be able to use ABIs foreign to Rust. This causes a lot of `unsafe` code in these projects - which is not ideal. | ||
|
|
||
| As an example, I (personally) was developing a project where a function was passed an argument via the `r12` register. However, due to the lack of a suitable ABI, I was forced to use external code to move the registers to their correct places; causing extra, unneccesary code. | ||
|
|
||
| In general, custom ABIs could lower the usage of external code fixing ABI requirements. it also gives the additional information that `abi_custom` does not (align as an example) | ||
|
|
||
| ## Guide-level explanation | ||
| [guide-level-explanation]: #guide-level-explanation | ||
|
|
||
| there are 2 possible ways this freature may be implemented. | ||
|
|
||
| ### Trait | ||
| Example for C ABI | ||
| ```rust | ||
| use core::abi::{Abi, Register}; // in core | ||
|
|
||
| struct CAbi; | ||
|
|
||
| unsafe impl Abi for CAbi { // unsafe impl | ||
|
Comment on lines
+29
to
+38
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. You simply cannot ergonomically describe an ABI in a single trait, because it is a matter of dire amounts of polymorphism. Speaking from the general view, even if we consider "how to handle an ABI" as a sort of stack machine-ish thing with multiple stacks for each type of register (so the machine mostly is reducing the program-argument stack while picking which machine-argument stack to push values into, essentially), a programmer-visible ABI is not required to translate to what we would consider a single mechanical implementation of a calling convention. It may select a different mechanical calling convention after considering the total function signature, which can have different stack or register usage rules. Often these rules are not easily described via simple constants, but instead must be modeled like executable code, e.g. some ABIs will insert "fake" arguments at various points, considering numerous small rules, to handle certain alignment constraints. This is not necessarily described entirely by stack alignment as a constraint, because an ABI may wish to consider "register alignment", which is not the same as stack alignment, and not the same as type alignment, but comes up quite often in actual CPU ISAs. |
||
| // might use slices instead. | ||
| const REGISTER_COUNT: u8 = 6; | ||
| const FLOAT_REGISTER_COUNT: u8 = 2; | ||
|
|
||
| const INPUT_REGISTERS: [Register; Self::REGISTER_COUNT] = [ | ||
| Register::Rdi, | ||
| // ... | ||
| ]; | ||
| const INPUT_FLOATS: [Register; Self::FLOAT_REGISTER_COUNT] = [ | ||
| Register::Xmm0, | ||
| // ... | ||
| ] | ||
|
|
||
| const STACK_ALIGN: usize = 16; | ||
|
|
||
| // more ABI fields here. | ||
| } | ||
|
Comment on lines
+38
to
+55
Contributor
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 just isn't enough to represent anything more than trivial ABIs. Different types may have different stack alignment, different float/int sizes may get passed in different registers, values may be split across registers, ADTs need to be figured out, etc...
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. Yeah I also thought that could be a problem. I'm not an expert on what an ABI really does, but there should be some possible way to allow for ABI defs in rust. I'm going to try and gather a bit more information and adjust the implementation
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. Ok i have thought of a better way to do this, im going to say it here before changing the RFC: We can have special Example:Cargo.toml [lib]
abi = true
# proc-macro = true
# this failslib.rs: extern crate abi;
use abi::{Alignment, AbiTable, BasicType}; // basic types are unsigned interger
#[abi(align_for /*, method specific options */)]
pub fn align_for(ty: BasicType) -> Alignment {
match ty {
// ...
BasicType::U8 => Alignment::One
}
}
#[abi(table, name="custom_abi")]
pub const CUSTOM_ABI: AbiTable = AbiTable::new()
.set_align_for(ty);Usage: use abi_crate::CUSTOM_ABI; // required import
extern "custom_abi" fn do_nothing() {}This offers more functionality, while keeping familiar API to users of Rust I want to state again that this may not be the best way, and that i am not an expert. 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 should not be writing a proposal for a full ABI definition framework which is permanently stable and cannot be changed unless you know what an ABI is and what it does. This doesn't mean that the idea doesn't have merit, but as someone who also does not fully understand ABIs, I at least am past the Dunning-Kruger curve enough to realise that defining an ABI using a simple trait does not seem sufficient in the slightest. Not even LLVM seems to even attempt to tackle this goal. What makes you think that Rust can and should? Just take a brief look at the closest similar proposal, to add crABI, and see if your proposal covers 1% of the requirements or answers 1% of the questions left open: rust-lang/compiler-team#631 |
||
| ``` | ||
| Usage: | ||
| ```rust | ||
| extern CAbi fn c_func(arg: u64, float: f32) { | ||
| // ... | ||
| } | ||
| ``` | ||
| Pros: | ||
| - Simple, easy to understand. | ||
| - extern is not a string - does not shadow builtin ABIs. | ||
| Cons: | ||
| - Lots of boilerplate. | ||
| - Arch-specific. | ||
| - Requires parser changes. | ||
|
|
||
| ### `#[abi(name)]` | ||
| This would be simmilar to `#[global_allocator]` | ||
| ``` | ||
| use core::abi::{CustomAbi, Register}; | ||
|
|
||
| #[unsafe(abi("bad-c"))] // unsafe attr | ||
| const C_ABI: CustomAbi = CustomAbi::new() | ||
| .set_inputs([Register::Rdi, ...]); | ||
| ``` | ||
| Usage: | ||
| ``` | ||
| extern "bad-c" fn c_func(arg: u64, float: f32) {} | ||
| ``` | ||
| Pros: | ||
| - Less boilerplate than traits. | ||
| - string names allow for more variety. | ||
| - better docs, due to the `CustomAbi` type. | ||
| Cons: | ||
| - String names may be confusing with Rust's builtins. | ||
| - Amiguouty if all field of `CustomAbi` must be set. | ||
| - Requires a new lang item. | ||
| ### Disscussion | ||
| This feature allows for better code maintainability. Instead of reading online documentation for an ABI, or worse, scouting undocumented code for the used ABI, the ABI lives directly in Rust, allowing for easier changing. | ||
|
|
||
| Additionally, from the core/std-side of things, we can define an ABI for general use - not stabilizing the `"Rust"`, though. | ||
|
|
||
| This feature fits nicely with [#140829](https://github.com/rust-lang/rust/issues/140829)), with a new macro. | ||
| ``` | ||
| use core::abi::call_with_abi; | ||
|
|
||
| #[unsafe(naked)] | ||
| extern "custom" unsafe fn ret() { | ||
| core::arch::naked_asm!( | ||
| "ret" | ||
| ) | ||
| } | ||
|
|
||
| fn foo() {} | ||
|
|
||
| // SomeAbi defined here... | ||
| unsafe { | ||
| call_with_abi(ret, "C"); // Calls with C abi | ||
| call_with_abi(ret, SomeAbi); // Calls with SomeAbi | ||
| // call_with_abi(foo, "C"); // ERROR: Only function with the `custom` abi can be called. | ||
| } | ||
| ``` | ||
|
|
||
| ## Reference-level explanation | ||
| [reference-level-explanation]: #reference-level-explanation | ||
|
|
||
| For the first implementation method, the defenition would likely look like this: | ||
| ``` | ||
| // #[rustc_deny_creation] // we may also deny creation, I'm not sure if there is already an attr for this. | ||
| pub unsafe trait CustomAbi { | ||
| // constants, no runtime-variables | ||
| } | ||
| ``` | ||
|
|
||
| this feature would require changing: | ||
| - the parser to allow non-strings after extern (first impl-method) | ||
| - the compiler to allow custom ABIs. (specifically anything that stores its ABI). | ||
|
|
||
| Also, creation of ABI that is invalid is considered undefined behaviour, hence the `unsafe` keyword. The exact defenition for an "Invalid ABI" is not clear, as certain ABIs may have different forms. Basic things to check include all alignment consts being powers of 2. (Note: We still check what can be checked) | ||
|
|
||
| Additionally, calling a "custom" function with an incorrect ABI is UB, and this applies to both the call and creation of the ABI. | ||
|
|
||
| ### Internal Implementation | ||
| During code generation, the compiler load the evaluated constants to ensure type-safety, and use the correct registers for the ABI. I'm not exactly sure if the order of the compiler's operations allows for this. | ||
|
|
||
| ## Drawbacks | ||
| [drawbacks]: #drawbacks | ||
|
|
||
| Custom ABIs are just unsafe. At the end of the day, users are creating these ABIs, and there is no way to check the validity of such ABI. | ||
|
|
||
| Additionally, these may be a little too arch-specific, and this increases the chance of users accedeintly causing UB. | ||
|
|
||
| There are likely other reasons. | ||
|
|
||
| ## Rationale and alternatives | ||
| [rationale-and-alternatives]: #rationale-and-alternatives | ||
|
|
||
| This design allows for the most customisability. | ||
|
|
||
| Other designs include the `"custom"` abi, and simply the `"C"` abi. But these only define a set ruleset, if a user neeeds specific functionallity, they must wait until we add that abi. | ||
|
|
||
| Not doing this wont have much of an impact - it may even be the better choice to avoid refactoring the compiler too much. | ||
|
|
||
| ## Prior art | ||
| [prior-art]: #prior-art | ||
|
|
||
| The `"custom"` abi is one solution to this same problem, it: | ||
|
|
||
| - allows for ABIs that are not supported | ||
| - allows users to specify their items are not in a supported ABI to prevent confusion. | ||
|
|
||
| However, it: | ||
|
|
||
| - does not support customisability. | ||
| - is builtin, and not accessible to users as a type. | ||
|
|
||
| This RFC isn't aiming to *replace* custom, but rather enhance it by adding what was missing. | ||
|
|
||
| Also, Other programming languages dont support custom ABIs, which is one reason why Rust should. | ||
| ## Unresolved questions | ||
| [unresolved-questions]: #unresolved-questions | ||
| > What parts of the design do you expect to resolve through the RFC process before this gets merged? | ||
| - How would the compiler implement this? | ||
| - Would we implement this via trait or global constant? | ||
| > What parts of the design do you expect to resolve through the implementation of this feature before stabilization? | ||
| - How do we ensure as much safety as we can? | ||
| - How can we use this feature in the standard library? | ||
|
|
||
| > What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? | ||
| N/A | ||
|
|
||
| ## Future possibilities | ||
| [future-possibilities]: #future-possibilities | ||
|
|
||
| We could possibly stabilize the previously mentioned ABI in the standard library for general use, not neccessarily for Rust, howevver, but having a general ABI wouldn't hurt | ||
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.
I kind of hinted at this in my other comment but IMO, any sort of proposal to express ABI in code is a non-starter. There is an immense amount of complexity (see all the LLVM tablegen files related to ABI) and little reason not to do things in assembly.
There are certainly problems with handwritten assembly that could be improved if you are interested, but I think this is better framed as “how can we make it easier to integrate handwritten assembly (like trampolines) with normal code” rather than trying to, effectively, get rustc to emit a very specific assembly without writing it.