- Notifications
You must be signed in to change notification settings - Fork 14.1k
Replace ad-hoc ABI "adjustments" with an AbiMap to CanonAbi #141569
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
f57ed46 c04e249 2d637f7 72ecde2 e0b07a8 0d8959f 87fa1ea 9d42e35 2ad64b4 2351a3e 307a18d 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,136 @@ | ||
| use std::fmt; | ||
| | ||
| #[cfg(feature = "nightly")] | ||
| use rustc_macros::HashStable_Generic; | ||
| | ||
| use crate::ExternAbi; | ||
| | ||
| /// Calling convention to determine codegen | ||
| /// | ||
| /// CanonAbi erases certain distinctions ExternAbi preserves, but remains target-dependent. | ||
| /// There are still both target-specific variants and aliasing variants, though much fewer. | ||
| /// The reason for this step is the frontend may wish to show an ExternAbi but implement that ABI | ||
| /// using a different ABI than the string per se, or describe irrelevant differences, e.g. | ||
| /// - extern "system" | ||
| /// - extern "cdecl" | ||
| /// - extern "C-unwind" | ||
| /// In that sense, this erases mere syntactic distinctions to create a canonical *directive*, | ||
| /// rather than picking the "actual" ABI. | ||
| #[derive(Copy, Clone, Debug)] | ||
| #[derive(PartialOrd, Ord, PartialEq, Eq, Hash)] | ||
| #[cfg_attr(feature = "nightly", derive(HashStable_Generic))] | ||
| pub enum CanonAbi { | ||
| // NOTE: the use of nested variants for some ABIs is for many targets they don't matter, | ||
| // and this pushes the complexity of their reasoning to target-specific code, | ||
| // allowing a `match` to easily exhaustively ignore these subcategories of variants. | ||
| // Otherwise it is very tempting to avoid matching exhaustively! | ||
| C, | ||
| Rust, | ||
| RustCold, | ||
| | ||
| /// ABIs relevant to 32-bit Arm targets | ||
| Arm(ArmCall), | ||
| /// ABI relevant to GPUs: the entry point for a GPU kernel | ||
| GpuKernel, | ||
| | ||
| /// ABIs relevant to bare-metal interrupt targets | ||
| // FIXME(workingjubilee): a particular reason for this nesting is we might not need these? | ||
| // interrupt ABIs should have the same properties: | ||
| // - uncallable by Rust calls, as LLVM rejects it in most cases | ||
| // - uses a preserve-all-registers *callee* convention | ||
| // - should always return `-> !` (effectively... it can't use normal `ret`) | ||
| // what differs between targets is | ||
| // - allowed arguments: x86 differs slightly, having 2-3 arguments which are handled magically | ||
| // - may need special prologues/epilogues for some interrupts, without affecting "call ABI" | ||
| Interrupt(InterruptKind), | ||
| | ||
| /// ABIs relevant to Windows or x86 targets | ||
| X86(X86Call), | ||
| } | ||
| 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. 😍 Member 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. Ah so that decision actually does look right to someone who is not inside my head? Great. | ||
| | ||
| impl fmt::Display for CanonAbi { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| self.to_erased_extern_abi().as_str().fmt(f) | ||
| } | ||
| } | ||
| | ||
| impl CanonAbi { | ||
| /// convert to the ExternAbi that *shares a string* with this CanonAbi | ||
| /// | ||
| /// A target-insensitive mapping of CanonAbi to ExternAbi, convenient for "forwarding" impls. | ||
| /// Importantly, the set of CanonAbi values is a logical *subset* of ExternAbi values, | ||
| /// so this is injective: if you take an ExternAbi to a CanonAbi and back, you have lost data. | ||
| const fn to_erased_extern_abi(self) -> ExternAbi { | ||
workingjubilee marked this conversation as resolved. Show resolved Hide resolved 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 this is now the only odd new translation function left here, I think? I'm wondering how we can eradicate this one as well. It's only used for printing. Where do we care about that? Is it only that Miri error message? Ideally that would have a way of recovering the original Member 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. My earlier versions of this had a Member 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. would be happy to pursue eliminating this in a followup if the PR otherwise looks good in its current state except for this function daring to exist (well, and needing a rebase-squash badly). that Miri error message would indeed be a relatively important part of any further changes and I think it would want a little more conversation around it. | ||
| match self { | ||
| CanonAbi::C => ExternAbi::C { unwind: false }, | ||
| CanonAbi::Rust => ExternAbi::Rust, | ||
| CanonAbi::RustCold => ExternAbi::RustCold, | ||
| CanonAbi::Arm(arm_call) => match arm_call { | ||
| ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false }, | ||
| ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall, | ||
| ArmCall::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry, | ||
| }, | ||
| CanonAbi::GpuKernel => ExternAbi::GpuKernel, | ||
| CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind { | ||
| InterruptKind::Avr => ExternAbi::AvrInterrupt, | ||
| InterruptKind::AvrNonBlocking => ExternAbi::AvrNonBlockingInterrupt, | ||
| InterruptKind::Msp430 => ExternAbi::Msp430Interrupt, | ||
| InterruptKind::RiscvMachine => ExternAbi::RiscvInterruptM, | ||
| InterruptKind::RiscvSupervisor => ExternAbi::RiscvInterruptS, | ||
| InterruptKind::X86 => ExternAbi::X86Interrupt, | ||
| }, | ||
| CanonAbi::X86(x86_call) => match x86_call { | ||
| X86Call::Fastcall => ExternAbi::Fastcall { unwind: false }, | ||
| X86Call::Stdcall => ExternAbi::Stdcall { unwind: false }, | ||
| X86Call::SysV64 => ExternAbi::SysV64 { unwind: false }, | ||
| X86Call::Thiscall => ExternAbi::Thiscall { unwind: false }, | ||
| X86Call::Vectorcall => ExternAbi::Vectorcall { unwind: false }, | ||
| X86Call::Win64 => ExternAbi::Win64 { unwind: false }, | ||
| }, | ||
| } | ||
| } | ||
| } | ||
| | ||
| /// Callee codegen for interrupts | ||
| /// | ||
| /// This is named differently from the "Call" enums because it is different: | ||
| /// these "ABI" differences are not relevant to callers, since there is "no caller". | ||
| /// These only affect callee codegen. making their categorization as distinct ABIs a bit peculiar. | ||
| #[derive(Copy, Clone, Debug)] | ||
| #[derive(PartialOrd, Ord, PartialEq, Eq, Hash)] | ||
| #[cfg_attr(feature = "nightly", derive(HashStable_Generic))] | ||
| pub enum InterruptKind { | ||
| Avr, | ||
| AvrNonBlocking, | ||
| Msp430, | ||
| RiscvMachine, | ||
| RiscvSupervisor, | ||
| X86, | ||
| } | ||
| | ||
| /// ABIs defined for x86-{32,64} | ||
| /// | ||
| /// One of SysV64 or Win64 may alias the C ABI, and arguably Win64 is cross-platform now? | ||
| #[derive(Clone, Copy, Debug)] | ||
| #[derive(PartialOrd, Ord, PartialEq, Eq, Hash)] | ||
| #[cfg_attr(feature = "nightly", derive(HashStable_Generic))] | ||
| pub enum X86Call { | ||
| /// "fastcall" has both GNU and Windows variants | ||
| Fastcall, | ||
| /// "stdcall" has both GNU and Windows variants | ||
| Stdcall, | ||
| SysV64, | ||
| Thiscall, | ||
| Vectorcall, | ||
| Win64, | ||
| } | ||
| | ||
| /// ABIs defined for 32-bit Arm | ||
| #[derive(Copy, Clone, Debug)] | ||
| #[derive(PartialOrd, Ord, PartialEq, Eq, Hash)] | ||
| #[cfg_attr(feature = "nightly", derive(HashStable_Generic))] | ||
| pub enum ArmCall { | ||
| Aapcs, | ||
| CCmseNonSecureCall, | ||
| CCmseNonSecureEntry, | ||
| } | ||
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.
The "to Windows" part here should disappear when #141435 becomes a hard error, right?
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.
Probably.