| use std::{fmt, iter}; | |
| use rustc_abi::{ | |
| AddressSpace, Align, BackendRepr, CanonAbi, ExternAbi, FieldsShape, HasDataLayout, Primitive, | |
| Reg, RegKind, Scalar, Size, TyAbiInterface, TyAndLayout, Variants, | |
| }; | |
| use rustc_macros::HashStable_Generic; | |
| pub use crate::spec::AbiMap; | |
| use crate::spec::{Arch, HasTargetSpec, HasX86AbiOpt}; | |
| mod aarch64; | |
| mod amdgpu; | |
| mod arm; | |
| mod avr; | |
| mod bpf; | |
| mod csky; | |
| mod hexagon; | |
| mod loongarch; | |
| mod m68k; | |
| mod mips; | |
| mod mips64; | |
| mod msp430; | |
| mod nvptx64; | |
| mod powerpc; | |
| mod powerpc64; | |
| mod riscv; | |
| mod s390x; | |
| mod sparc; | |
| mod sparc64; | |
| mod wasm; | |
| mod x86; | |
| mod x86_64; | |
| mod x86_win32; | |
| mod x86_win64; | |
| mod xtensa; | |
| #[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] | |
| pub enum PassMode { | |
| /// Ignore the argument. | |
| /// | |
| /// The argument is a ZST. | |
| Ignore, | |
| /// Pass the argument directly. | |
| /// | |
| /// The argument has a layout abi of `Scalar` or `Vector`. | |
| /// Unfortunately due to past mistakes, in rare cases on wasm, it can also be `Aggregate`. | |
| /// This is bad since it leaks LLVM implementation details into the ABI. | |
| /// (Also see <https://github.com/rust-lang/rust/issues/115666>.) | |
| Direct(ArgAttributes), | |
| /// Pass a pair's elements directly in two arguments. | |
| /// | |
| /// The argument has a layout abi of `ScalarPair`. | |
| Pair(ArgAttributes, ArgAttributes), | |
| /// Pass the argument after casting it. See the `CastTarget` docs for details. | |
| /// | |
| /// `pad_i32` indicates if a `Reg::i32()` dummy argument is emitted before the real argument. | |
| Cast { pad_i32: bool, cast: Box<CastTarget> }, | |
| /// Pass the argument indirectly via a hidden pointer. | |
| /// | |
| /// The `meta_attrs` value, if any, is for the metadata (vtable or length) of an unsized | |
| /// argument. (This is the only mode that supports unsized arguments.) | |
| /// | |
| /// `on_stack` defines that the value should be passed at a fixed stack offset in accordance to | |
| /// the ABI rather than passed using a pointer. This corresponds to the `byval` LLVM argument | |
| /// attribute. The `byval` argument will use a byte array with the same size as the Rust type | |
| /// (which ensures that padding is preserved and that we do not rely on LLVM's struct layout), | |
| /// and will use the alignment specified in `attrs.pointee_align` (if `Some`) or the type's | |
| /// alignment (if `None`). This means that the alignment will not always | |
| /// match the Rust type's alignment; see documentation of `pass_by_stack_offset` for more info. | |
| /// | |
| /// `on_stack` cannot be true for unsized arguments, i.e., when `meta_attrs` is `Some`. | |
| Indirect { attrs: ArgAttributes, meta_attrs: Option<ArgAttributes>, on_stack: bool }, | |
| } | |
| impl PassMode { | |
| /// Checks if these two `PassMode` are equal enough to be considered "the same for all | |
| /// function call ABIs". However, the `Layout` can also impact ABI decisions, | |
| /// so that needs to be compared as well! | |
| pub fn eq_abi(&self, other: &Self) -> bool { | |
| match (self, other) { | |
| (PassMode::Ignore, PassMode::Ignore) => true, | |
| (PassMode::Direct(a1), PassMode::Direct(a2)) => a1.eq_abi(a2), | |
| (PassMode::Pair(a1, b1), PassMode::Pair(a2, b2)) => a1.eq_abi(a2) && b1.eq_abi(b2), | |
| ( | |
| PassMode::Cast { cast: c1, pad_i32: pad1 }, | |
| PassMode::Cast { cast: c2, pad_i32: pad2 }, | |
| ) => c1.eq_abi(c2) && pad1 == pad2, | |
| ( | |
| PassMode::Indirect { attrs: a1, meta_attrs: None, on_stack: s1 }, | |
| PassMode::Indirect { attrs: a2, meta_attrs: None, on_stack: s2 }, | |
| ) => a1.eq_abi(a2) && s1 == s2, | |
| ( | |
| PassMode::Indirect { attrs: a1, meta_attrs: Some(e1), on_stack: s1 }, | |
| PassMode::Indirect { attrs: a2, meta_attrs: Some(e2), on_stack: s2 }, | |
| ) => a1.eq_abi(a2) && e1.eq_abi(e2) && s1 == s2, | |
| _ => false, | |
| } | |
| } | |
| } | |
| // Hack to disable non_upper_case_globals only for the bitflags! and not for the rest | |
| // of this module | |
| pub use attr_impl::ArgAttribute; | |
| #[allow(non_upper_case_globals)] | |
| #[allow(unused)] | |
| mod attr_impl { | |
| use rustc_macros::HashStable_Generic; | |
| // The subset of llvm::Attribute needed for arguments, packed into a bitfield. | |
| #[derive(Clone, Copy, Default, Hash, PartialEq, Eq, HashStable_Generic)] | |
| pub struct ArgAttribute(u8); | |
| bitflags::bitflags! { | |
| impl ArgAttribute: u8 { | |
| const CapturesNone = 0b111; | |
| const CapturesAddress = 0b110; | |
| const CapturesReadOnly = 0b100; | |
| const NoAlias = 1 << 3; | |
| const NonNull = 1 << 4; | |
| const ReadOnly = 1 << 5; | |
| const InReg = 1 << 6; | |
| const NoUndef = 1 << 7; | |
| } | |
| } | |
| rustc_data_structures::external_bitflags_debug! { ArgAttribute } | |
| } | |
| /// Sometimes an ABI requires small integers to be extended to a full or partial register. This enum | |
| /// defines if this extension should be zero-extension or sign-extension when necessary. When it is | |
| /// not necessary to extend the argument, this enum is ignored. | |
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] | |
| pub enum ArgExtension { | |
| None, | |
| Zext, | |
| Sext, | |
| } | |
| /// A compact representation of LLVM attributes (at least those relevant for this module) | |
| /// that can be manipulated without interacting with LLVM's Attribute machinery. | |
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] | |
| pub struct ArgAttributes { | |
| pub regular: ArgAttribute, | |
| pub arg_ext: ArgExtension, | |
| /// The minimum size of the pointee, guaranteed to be valid for the duration of the whole call | |
| /// (corresponding to LLVM's dereferenceable_or_null attributes, i.e., it is okay for this to be | |
| /// set on a null pointer, but all non-null pointers must be dereferenceable). | |
| pub pointee_size: Size, | |
| /// The minimum alignment of the pointee, if any. | |
| pub pointee_align: Option<Align>, | |
| } | |
| impl ArgAttributes { | |
| pub fn new() -> Self { | |
| ArgAttributes { | |
| regular: ArgAttribute::default(), | |
| arg_ext: ArgExtension::None, | |
| pointee_size: Size::ZERO, | |
| pointee_align: None, | |
| } | |
| } | |
| pub fn ext(&mut self, ext: ArgExtension) -> &mut Self { | |
| assert!( | |
| self.arg_ext == ArgExtension::None || self.arg_ext == ext, | |
| "cannot set {:?} when {:?} is already set", | |
| ext, | |
| self.arg_ext | |
| ); | |
| self.arg_ext = ext; | |
| self | |
| } | |
| pub fn set(&mut self, attr: ArgAttribute) -> &mut Self { | |
| self.regular |= attr; | |
| self | |
| } | |
| pub fn contains(&self, attr: ArgAttribute) -> bool { | |
| self.regular.contains(attr) | |
| } | |
| /// Checks if these two `ArgAttributes` are equal enough to be considered "the same for all | |
| /// function call ABIs". | |
| pub fn eq_abi(&self, other: &Self) -> bool { | |
| // There's only one regular attribute that matters for the call ABI: InReg. | |
| // Everything else is things like noalias, dereferenceable, nonnull, ... | |
| // (This also applies to pointee_size, pointee_align.) | |
| if self.regular.contains(ArgAttribute::InReg) != other.regular.contains(ArgAttribute::InReg) | |
| { | |
| return false; | |
| } | |
| // We also compare the sign extension mode -- this could let the callee make assumptions | |
| // about bits that conceptually were not even passed. | |
| if self.arg_ext != other.arg_ext { | |
| return false; | |
| } | |
| true | |
| } | |
| } | |
| impl From<ArgAttribute> for ArgAttributes { | |
| fn from(value: ArgAttribute) -> Self { | |
| Self { | |
| regular: value, | |
| arg_ext: ArgExtension::None, | |
| pointee_size: Size::ZERO, | |
| pointee_align: None, | |
| } | |
| } | |
| } | |
| /// An argument passed entirely registers with the | |
| /// same kind (e.g., HFA / HVA on PPC64 and AArch64). | |
| #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)] | |
| pub struct Uniform { | |
| pub unit: Reg, | |
| /// The total size of the argument, which can be: | |
| /// * equal to `unit.size` (one scalar/vector), | |
| /// * a multiple of `unit.size` (an array of scalar/vectors), | |
| /// * if `unit.kind` is `Integer`, the last element can be shorter, i.e., `{ i64, i64, i32 }` | |
| /// for 64-bit integers with a total size of 20 bytes. When the argument is actually passed, | |
| /// this size will be rounded up to the nearest multiple of `unit.size`. | |
| pub total: Size, | |
| /// Indicate that the argument is consecutive, in the sense that either all values need to be | |
| /// passed in register, or all on the stack. If they are passed on the stack, there should be | |
| /// no additional padding between elements. | |
| pub is_consecutive: bool, | |
| } | |
| impl From<Reg> for Uniform { | |
| fn from(unit: Reg) -> Uniform { | |
| Uniform { unit, total: unit.size, is_consecutive: false } | |
| } | |
| } | |
| impl Uniform { | |
| pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align { | |
| self.unit.align(cx) | |
| } | |
| /// Pass using one or more values of the given type, without requiring them to be consecutive. | |
| /// That is, some values may be passed in register and some on the stack. | |
| pub fn new(unit: Reg, total: Size) -> Self { | |
| Uniform { unit, total, is_consecutive: false } | |
| } | |
| /// Pass using one or more consecutive values of the given type. Either all values will be | |
| /// passed in registers, or all on the stack. | |
| pub fn consecutive(unit: Reg, total: Size) -> Self { | |
| Uniform { unit, total, is_consecutive: true } | |
| } | |
| } | |
| /// Describes the type used for `PassMode::Cast`. | |
| /// | |
| /// Passing arguments in this mode works as follows: the registers in the `prefix` (the ones that | |
| /// are `Some`) get laid out one after the other (using `repr(C)` layout rules). Then the | |
| /// `rest.unit` register type gets repeated often enough to cover `rest.size`. This describes the | |
| /// actual type used for the call; the Rust type of the argument is then transmuted to this ABI type | |
| /// (and all data in the padding between the registers is dropped). | |
| #[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] | |
| pub struct CastTarget { | |
| pub prefix: [Option<Reg>; 8], | |
| /// The offset of `rest` from the start of the value. Currently only implemented for a `Reg` | |
| /// pair created by the `offset_pair` method. | |
| pub rest_offset: Option<Size>, | |
| pub rest: Uniform, | |
| pub attrs: ArgAttributes, | |
| } | |
| impl From<Reg> for CastTarget { | |
| fn from(unit: Reg) -> CastTarget { | |
| CastTarget::from(Uniform::from(unit)) | |
| } | |
| } | |
| impl From<Uniform> for CastTarget { | |
| fn from(uniform: Uniform) -> CastTarget { | |
| Self::prefixed([None; 8], uniform) | |
| } | |
| } | |
| impl CastTarget { | |
| pub fn prefixed(prefix: [Option<Reg>; 8], rest: Uniform) -> Self { | |
| Self { prefix, rest_offset: None, rest, attrs: ArgAttributes::new() } | |
| } | |
| pub fn offset_pair(a: Reg, offset_from_start: Size, b: Reg) -> Self { | |
| Self { | |
| prefix: [Some(a), None, None, None, None, None, None, None], | |
| rest_offset: Some(offset_from_start), | |
| rest: b.into(), | |
| attrs: ArgAttributes::new(), | |
| } | |
| } | |
| pub fn with_attrs(mut self, attrs: ArgAttributes) -> Self { | |
| self.attrs = attrs; | |
| self | |
| } | |
| pub fn pair(a: Reg, b: Reg) -> CastTarget { | |
| Self::prefixed([Some(a), None, None, None, None, None, None, None], Uniform::from(b)) | |
| } | |
| /// When you only access the range containing valid data, you can use this unaligned size; | |
| /// otherwise, use the safer `size` method. | |
| pub fn unaligned_size<C: HasDataLayout>(&self, _cx: &C) -> Size { | |
| // Prefix arguments are passed in specific designated registers | |
| let prefix_size = if let Some(offset_from_start) = self.rest_offset { | |
| offset_from_start | |
| } else { | |
| self.prefix | |
| .iter() | |
| .filter_map(|x| x.map(|reg| reg.size)) | |
| .fold(Size::ZERO, |acc, size| acc + size) | |
| }; | |
| // Remaining arguments are passed in chunks of the unit size | |
| let rest_size = | |
| self.rest.unit.size * self.rest.total.bytes().div_ceil(self.rest.unit.size.bytes()); | |
| prefix_size + rest_size | |
| } | |
| pub fn size<C: HasDataLayout>(&self, cx: &C) -> Size { | |
| self.unaligned_size(cx).align_to(self.align(cx)) | |
| } | |
| pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align { | |
| self.prefix | |
| .iter() | |
| .filter_map(|x| x.map(|reg| reg.align(cx))) | |
| .fold(cx.data_layout().aggregate_align.max(self.rest.align(cx)), |acc, align| { | |
| acc.max(align) | |
| }) | |
| } | |
| /// Checks if these two `CastTarget` are equal enough to be considered "the same for all | |
| /// function call ABIs". | |
| pub fn eq_abi(&self, other: &Self) -> bool { | |
| let CastTarget { | |
| prefix: prefix_l, | |
| rest_offset: rest_offset_l, | |
| rest: rest_l, | |
| attrs: attrs_l, | |
| } = self; | |
| let CastTarget { | |
| prefix: prefix_r, | |
| rest_offset: rest_offset_r, | |
| rest: rest_r, | |
| attrs: attrs_r, | |
| } = other; | |
| prefix_l == prefix_r | |
| && rest_offset_l == rest_offset_r | |
| && rest_l == rest_r | |
| && attrs_l.eq_abi(attrs_r) | |
| } | |
| } | |
| /// Information about how to pass an argument to, | |
| /// or return a value from, a function, under some ABI. | |
| #[derive(Clone, PartialEq, Eq, Hash, HashStable_Generic)] | |
| pub struct ArgAbi<'a, Ty> { | |
| pub layout: TyAndLayout<'a, Ty>, | |
| pub mode: PassMode, | |
| } | |
| // Needs to be a custom impl because of the bounds on the `TyAndLayout` debug impl. | |
| impl<'a, Ty: fmt::Display> fmt::Debug for ArgAbi<'a, Ty> { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| let ArgAbi { layout, mode } = self; | |
| f.debug_struct("ArgAbi").field("layout", layout).field("mode", mode).finish() | |
| } | |
| } | |
| impl<'a, Ty> ArgAbi<'a, Ty> { | |
| /// This defines the "default ABI" for that type, that is then later adjusted in `fn_abi_adjust_for_abi`. | |
| pub fn new( | |
| cx: &impl HasDataLayout, | |
| layout: TyAndLayout<'a, Ty>, | |
| scalar_attrs: impl Fn(Scalar, Size) -> ArgAttributes, | |
| ) -> Self { | |
| let mode = match layout.backend_repr { | |
| _ if layout.is_zst() => PassMode::Ignore, | |
| BackendRepr::Scalar(scalar) => PassMode::Direct(scalar_attrs(scalar, Size::ZERO)), | |
| BackendRepr::ScalarPair(a, b) => PassMode::Pair( | |
| scalar_attrs(a, Size::ZERO), | |
| scalar_attrs(b, a.size(cx).align_to(b.align(cx).abi)), | |
| ), | |
| BackendRepr::SimdVector { .. } => PassMode::Direct(ArgAttributes::new()), | |
| BackendRepr::Memory { .. } => Self::indirect_pass_mode(&layout), | |
| BackendRepr::SimdScalableVector { .. } => PassMode::Direct(ArgAttributes::new()), | |
| }; | |
| ArgAbi { layout, mode } | |
| } | |
| fn indirect_pass_mode(layout: &TyAndLayout<'a, Ty>) -> PassMode { | |
| let mut attrs = ArgAttributes::new(); | |
| // For non-immediate arguments the callee gets its own copy of | |
| // the value on the stack, so there are no aliases. The function | |
| // can capture the address of the argument, but not the provenance. | |
| attrs | |
| .set(ArgAttribute::NoAlias) | |
| .set(ArgAttribute::CapturesAddress) | |
| .set(ArgAttribute::NonNull) | |
| .set(ArgAttribute::NoUndef); | |
| attrs.pointee_size = layout.size; | |
| attrs.pointee_align = Some(layout.align.abi); | |
| let meta_attrs = layout.is_unsized().then_some(ArgAttributes::new()); | |
| PassMode::Indirect { attrs, meta_attrs, on_stack: false } | |
| } | |
| /// Pass this argument directly instead. Should NOT be used! | |
| /// Only exists because of past ABI mistakes that will take time to fix | |
| /// (see <https://github.com/rust-lang/rust/issues/115666>). | |
| #[track_caller] | |
| pub fn make_direct_deprecated(&mut self) { | |
| match self.mode { | |
| PassMode::Indirect { .. } => { | |
| self.mode = PassMode::Direct(ArgAttributes::new()); | |
| } | |
| PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) => {} // already direct | |
| _ => panic!("Tried to make {:?} direct", self.mode), | |
| } | |
| } | |
| /// Pass this argument indirectly, by passing a (thin or wide) pointer to the argument instead. | |
| /// This is valid for both sized and unsized arguments. | |
| #[track_caller] | |
| pub fn make_indirect(&mut self) { | |
| match self.mode { | |
| PassMode::Direct(_) | PassMode::Pair(_, _) => { | |
| self.mode = Self::indirect_pass_mode(&self.layout); | |
| } | |
| PassMode::Indirect { attrs: _, meta_attrs: _, on_stack: false } => { | |
| // already indirect | |
| } | |
| _ => panic!("Tried to make {:?} indirect", self.mode), | |
| } | |
| } | |
| /// Same as `make_indirect`, but for arguments that are ignored. Only needed for ABIs that pass | |
| /// ZSTs indirectly. | |
| #[track_caller] | |
| pub fn make_indirect_from_ignore(&mut self) { | |
| match self.mode { | |
| PassMode::Ignore => { | |
| self.mode = Self::indirect_pass_mode(&self.layout); | |
| } | |
| PassMode::Indirect { attrs: _, meta_attrs: _, on_stack: false } => { | |
| // already indirect | |
| } | |
| _ => panic!("Tried to make {:?} indirect (expected `PassMode::Ignore`)", self.mode), | |
| } | |
| } | |
| /// Pass this argument indirectly, by placing it at a fixed stack offset. | |
| /// This corresponds to the `byval` LLVM argument attribute. | |
| /// This is only valid for sized arguments. | |
| /// | |
| /// `byval_align` specifies the alignment of the `byval` stack slot, which does not need to | |
| /// correspond to the type's alignment. This will be `Some` if the target's ABI specifies that | |
| /// stack slots used for arguments passed by-value have specific alignment requirements which | |
| /// differ from the alignment used in other situations. | |
| /// | |
| /// If `None`, the type's alignment is used. | |
| /// | |
| /// If the resulting alignment differs from the type's alignment, | |
| /// the argument will be copied to an alloca with sufficient alignment, | |
| /// either in the caller (if the type's alignment is lower than the byval alignment) | |
| /// or in the callee (if the type's alignment is higher than the byval alignment), | |
| /// to ensure that Rust code never sees an underaligned pointer. | |
| pub fn pass_by_stack_offset(&mut self, byval_align: Option<Align>) { | |
| assert!(!self.layout.is_unsized(), "used byval ABI for unsized layout"); | |
| self.make_indirect(); | |
| match self.mode { | |
| PassMode::Indirect { ref mut attrs, meta_attrs: _, ref mut on_stack } => { | |
| *on_stack = true; | |
| // Some platforms, like 32-bit x86, change the alignment of the type when passing | |
| // `byval`. Account for that. | |
| if let Some(byval_align) = byval_align { | |
| // On all targets with byval align this is currently true, so let's assert it. | |
| debug_assert!(byval_align >= Align::from_bytes(4).unwrap()); | |
| attrs.pointee_align = Some(byval_align); | |
| } | |
| } | |
| _ => unreachable!(), | |
| } | |
| } | |
| pub fn extend_integer_width_to(&mut self, bits: u64) { | |
| // Only integers have signedness | |
| if let BackendRepr::Scalar(scalar) = self.layout.backend_repr | |
| && let Primitive::Int(i, signed) = scalar.primitive() | |
| && i.size().bits() < bits | |
| && let PassMode::Direct(ref mut attrs) = self.mode | |
| { | |
| if signed { | |
| attrs.ext(ArgExtension::Sext) | |
| } else { | |
| attrs.ext(ArgExtension::Zext) | |
| }; | |
| } | |
| } | |
| pub fn cast_to<T: Into<CastTarget>>(&mut self, target: T) { | |
| self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32: false }; | |
| } | |
| pub fn cast_to_with_attrs<T: Into<CastTarget>>(&mut self, target: T, attrs: ArgAttributes) { | |
| self.mode = | |
| PassMode::Cast { cast: Box::new(target.into().with_attrs(attrs)), pad_i32: false }; | |
| } | |
| pub fn cast_to_and_pad_i32<T: Into<CastTarget>>(&mut self, target: T, pad_i32: bool) { | |
| self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32 }; | |
| } | |
| pub fn is_indirect(&self) -> bool { | |
| matches!(self.mode, PassMode::Indirect { .. }) | |
| } | |
| pub fn is_sized_indirect(&self) -> bool { | |
| matches!(self.mode, PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ }) | |
| } | |
| pub fn is_unsized_indirect(&self) -> bool { | |
| matches!(self.mode, PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ }) | |
| } | |
| pub fn is_ignore(&self) -> bool { | |
| matches!(self.mode, PassMode::Ignore) | |
| } | |
| /// Checks if these two `ArgAbi` are equal enough to be considered "the same for all | |
| /// function call ABIs". | |
| pub fn eq_abi(&self, other: &Self) -> bool | |
| where | |
| Ty: PartialEq, | |
| { | |
| // Ideally we'd just compare the `mode`, but that is not enough -- for some modes LLVM will look | |
| // at the type. | |
| self.layout.eq_abi(&other.layout) && self.mode.eq_abi(&other.mode) && { | |
| // `fn_arg_sanity_check` accepts `PassMode::Direct` for some aggregates. | |
| // That elevates any type difference to an ABI difference since we just use the | |
| // full Rust type as the LLVM argument/return type. | |
| if matches!(self.mode, PassMode::Direct(..)) | |
| && matches!(self.layout.backend_repr, BackendRepr::Memory { .. }) | |
| { | |
| // For aggregates in `Direct` mode to be compatible, the types need to be equal. | |
| self.layout.ty == other.layout.ty | |
| } else { | |
| true | |
| } | |
| } | |
| } | |
| } | |
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] | |
| pub enum RiscvInterruptKind { | |
| Machine, | |
| Supervisor, | |
| } | |
| impl RiscvInterruptKind { | |
| pub fn as_str(&self) -> &'static str { | |
| match self { | |
| Self::Machine => "machine", | |
| Self::Supervisor => "supervisor", | |
| } | |
| } | |
| } | |
| /// Metadata describing how the arguments to a native function | |
| /// should be passed in order to respect the native ABI. | |
| /// | |
| /// The signature represented by this type may not match the MIR function signature. | |
| /// Certain attributes, like `#[track_caller]` can introduce additional arguments, which are present in [`FnAbi`], but not in `FnSig`. | |
| /// The std::offload module also adds an addition dyn_ptr argument to the GpuKernel ABI. | |
| /// While this difference is rarely relevant, it should still be kept in mind. | |
| /// | |
| /// I will do my best to describe this structure, but these | |
| /// comments are reverse-engineered and may be inaccurate. -NDM | |
| #[derive(Clone, PartialEq, Eq, Hash, HashStable_Generic)] | |
| pub struct FnAbi<'a, Ty> { | |
| /// The type, layout, and information about how each argument is passed. | |
| pub args: Box<[ArgAbi<'a, Ty>]>, | |
| /// The layout, type, and the way a value is returned from this function. | |
| pub ret: ArgAbi<'a, Ty>, | |
| /// Marks this function as variadic (accepting a variable number of arguments). | |
| pub c_variadic: bool, | |
| /// The count of non-variadic arguments. | |
| /// | |
| /// Should only be different from args.len() when c_variadic is true. | |
| /// This can be used to know whether an argument is variadic or not. | |
| pub fixed_count: u32, | |
| /// The calling convention of this function. | |
| pub conv: CanonAbi, | |
| /// Indicates if an unwind may happen across a call to this function. | |
| pub can_unwind: bool, | |
| } | |
| // Needs to be a custom impl because of the bounds on the `TyAndLayout` debug impl. | |
| impl<'a, Ty: fmt::Display> fmt::Debug for FnAbi<'a, Ty> { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| let FnAbi { args, ret, c_variadic, fixed_count, conv, can_unwind } = self; | |
| f.debug_struct("FnAbi") | |
| .field("args", args) | |
| .field("ret", ret) | |
| .field("c_variadic", c_variadic) | |
| .field("fixed_count", fixed_count) | |
| .field("conv", conv) | |
| .field("can_unwind", can_unwind) | |
| .finish() | |
| } | |
| } | |
| impl<'a, Ty> FnAbi<'a, Ty> { | |
| pub fn adjust_for_foreign_abi<C>(&mut self, cx: &C, abi: ExternAbi) | |
| where | |
| Ty: TyAbiInterface<'a, C> + Copy, | |
| C: HasDataLayout + HasTargetSpec + HasX86AbiOpt, | |
| { | |
| if abi == ExternAbi::X86Interrupt { | |
| if let Some(arg) = self.args.first_mut() { | |
| arg.pass_by_stack_offset(None); | |
| } | |
| return; | |
| } | |
| let spec = cx.target_spec(); | |
| match &spec.arch { | |
| Arch::X86 => { | |
| let (flavor, regparm) = match abi { | |
| ExternAbi::Fastcall { .. } | ExternAbi::Vectorcall { .. } => { | |
| (x86::Flavor::FastcallOrVectorcall, None) | |
| } | |
| ExternAbi::C { .. } | ExternAbi::Cdecl { .. } | ExternAbi::Stdcall { .. } => { | |
| (x86::Flavor::General, cx.x86_abi_opt().regparm) | |
| } | |
| _ => (x86::Flavor::General, None), | |
| }; | |
| let reg_struct_return = cx.x86_abi_opt().reg_struct_return; | |
| let opts = x86::X86Options { flavor, regparm, reg_struct_return }; | |
| if spec.is_like_msvc { | |
| x86_win32::compute_abi_info(cx, self, opts); | |
| } else { | |
| x86::compute_abi_info(cx, self, opts); | |
| } | |
| } | |
| Arch::X86_64 => match abi { | |
| ExternAbi::SysV64 { .. } => x86_64::compute_abi_info(cx, self), | |
| ExternAbi::Win64 { .. } | ExternAbi::Vectorcall { .. } => { | |
| x86_win64::compute_abi_info(cx, self) | |
| } | |
| _ => { | |
| if cx.target_spec().is_like_windows { | |
| x86_win64::compute_abi_info(cx, self) | |
| } else { | |
| x86_64::compute_abi_info(cx, self) | |
| } | |
| } | |
| }, | |
| Arch::AArch64 | Arch::Arm64EC => { | |
| let kind = if cx.target_spec().is_like_darwin { | |
| aarch64::AbiKind::DarwinPCS | |
| } else if cx.target_spec().is_like_windows { | |
| aarch64::AbiKind::Win64 | |
| } else { | |
| aarch64::AbiKind::AAPCS | |
| }; | |
| aarch64::compute_abi_info(cx, self, kind) | |
| } | |
| Arch::AmdGpu => amdgpu::compute_abi_info(cx, self), | |
| Arch::Arm => arm::compute_abi_info(cx, self), | |
| Arch::Avr => avr::compute_abi_info(cx, self), | |
| Arch::LoongArch32 | Arch::LoongArch64 => loongarch::compute_abi_info(cx, self), | |
| Arch::M68k => m68k::compute_abi_info(cx, self), | |
| Arch::CSky => csky::compute_abi_info(cx, self), | |
| Arch::Mips | Arch::Mips32r6 => mips::compute_abi_info(cx, self), | |
| Arch::Mips64 | Arch::Mips64r6 => mips64::compute_abi_info(cx, self), | |
| Arch::PowerPC => powerpc::compute_abi_info(cx, self), | |
| Arch::PowerPC64 => powerpc64::compute_abi_info(cx, self), | |
| Arch::S390x => s390x::compute_abi_info(cx, self), | |
| Arch::Msp430 => msp430::compute_abi_info(cx, self), | |
| Arch::Sparc => sparc::compute_abi_info(cx, self), | |
| Arch::Sparc64 => sparc64::compute_abi_info(cx, self), | |
| Arch::Nvptx64 => { | |
| if abi == ExternAbi::PtxKernel || abi == ExternAbi::GpuKernel { | |
| nvptx64::compute_ptx_kernel_abi_info(cx, self) | |
| } else { | |
| nvptx64::compute_abi_info(cx, self) | |
| } | |
| } | |
| Arch::Hexagon => hexagon::compute_abi_info(cx, self), | |
| Arch::Xtensa => xtensa::compute_abi_info(cx, self), | |
| Arch::RiscV32 | Arch::RiscV64 => riscv::compute_abi_info(cx, self), | |
| Arch::Wasm32 | Arch::Wasm64 => wasm::compute_abi_info(cx, self), | |
| Arch::Bpf => bpf::compute_abi_info(cx, self), | |
| arch @ (Arch::SpirV | Arch::Other(_)) => { | |
| panic!("no lowering implemented for {arch}") | |
| } | |
| } | |
| } | |
| pub fn adjust_for_rust_abi<C>(&mut self, cx: &C) | |
| where | |
| Ty: TyAbiInterface<'a, C> + Copy, | |
| C: HasDataLayout + HasTargetSpec, | |
| { | |
| let spec = cx.target_spec(); | |
| match &spec.arch { | |
| Arch::X86 => x86::compute_rust_abi_info(cx, self), | |
| Arch::RiscV32 | Arch::RiscV64 => riscv::compute_rust_abi_info(cx, self), | |
| Arch::LoongArch32 | Arch::LoongArch64 => loongarch::compute_rust_abi_info(cx, self), | |
| Arch::AArch64 => aarch64::compute_rust_abi_info(cx, self), | |
| Arch::Bpf => bpf::compute_rust_abi_info(self), | |
| _ => {} | |
| }; | |
| for (arg_idx, arg) in self | |
| .args | |
| .iter_mut() | |
| .enumerate() | |
| .map(|(idx, arg)| (Some(idx), arg)) | |
| .chain(iter::once((None, &mut self.ret))) | |
| { | |
| // If the logic above already picked a specific type to cast the argument to, leave that | |
| // in place. | |
| if matches!(arg.mode, PassMode::Ignore | PassMode::Cast { .. }) { | |
| continue; | |
| } | |
| if arg_idx.is_none() | |
| && arg.layout.size > Primitive::Pointer(AddressSpace::ZERO).size(cx) * 2 | |
| && !matches!(arg.layout.backend_repr, BackendRepr::SimdVector { .. }) | |
| { | |
| // Return values larger than 2 registers using a return area | |
| // pointer. LLVM and Cranelift disagree about how to return | |
| // values that don't fit in the registers designated for return | |
| // values. LLVM will force the entire return value to be passed | |
| // by return area pointer, while Cranelift will look at each IR level | |
| // return value independently and decide to pass it in a | |
| // register or not, which would result in the return value | |
| // being passed partially in registers and partially through a | |
| // return area pointer. For large IR-level values such as `i128`, | |
| // cranelift will even split up the value into smaller chunks. | |
| // | |
| // While Cranelift may need to be fixed as the LLVM behavior is | |
| // generally more correct with respect to the surface language, | |
| // forcing this behavior in rustc itself makes it easier for | |
| // other backends to conform to the Rust ABI and for the C ABI | |
| // rustc already handles this behavior anyway. | |
| // | |
| // In addition LLVM's decision to pass the return value in | |
| // registers or using a return area pointer depends on how | |
| // exactly the return type is lowered to an LLVM IR type. For | |
| // example `Option<u128>` can be lowered as `{ i128, i128 }` | |
| // in which case the x86_64 backend would use a return area | |
| // pointer, or it could be passed as `{ i32, i128 }` in which | |
| // case the x86_64 backend would pass it in registers by taking | |
| // advantage of an LLVM ABI extension that allows using 3 | |
| // registers for the x86_64 sysv call conv rather than the | |
| // officially specified 2 registers. | |
| // | |
| // FIXME: Technically we should look at the amount of available | |
| // return registers rather than guessing that there are 2 | |
| // registers for return values. In practice only a couple of | |
| // architectures have less than 2 return registers. None of | |
| // which supported by Cranelift. | |
| // | |
| // NOTE: This adjustment is only necessary for the Rust ABI as | |
| // for other ABI's the calling convention implementations in | |
| // rustc_target already ensure any return value which doesn't | |
| // fit in the available amount of return registers is passed in | |
| // the right way for the current target. | |
| // | |
| // The adjustment is not necessary nor desired for types with a vector | |
| // representation; those are handled below. | |
| arg.make_indirect(); | |
| continue; | |
| } | |
| match arg.layout.backend_repr { | |
| BackendRepr::Memory { .. } => { | |
| // Compute `Aggregate` ABI. | |
| let is_indirect_not_on_stack = | |
| matches!(arg.mode, PassMode::Indirect { on_stack: false, .. }); | |
| assert!(is_indirect_not_on_stack); | |
| let size = arg.layout.size; | |
| if arg.layout.is_sized() | |
| && size <= Primitive::Pointer(AddressSpace::ZERO).size(cx) | |
| { | |
| // We want to pass small aggregates as immediates, but using | |
| // an LLVM aggregate type for this leads to bad optimizations, | |
| // so we pick an appropriately sized integer type instead. | |
| let attr = if layout_is_noundef(arg.layout, cx) { | |
| ArgAttribute::NoUndef | |
| } else { | |
| ArgAttribute::default() | |
| }; | |
| arg.cast_to_with_attrs(Reg { kind: RegKind::Integer, size }, attr.into()); | |
| } | |
| } | |
| BackendRepr::SimdVector { .. } => { | |
| // This is a fun case! The gist of what this is doing is | |
| // that we want callers and callees to always agree on the | |
| // ABI of how they pass SIMD arguments. If we were to *not* | |
| // make these arguments indirect then they'd be immediates | |
| // in LLVM, which means that they'd used whatever the | |
| // appropriate ABI is for the callee and the caller. That | |
| // means, for example, if the caller doesn't have AVX | |
| // enabled but the callee does, then passing an AVX argument | |
| // across this boundary would cause corrupt data to show up. | |
| // | |
| // This problem is fixed by unconditionally passing SIMD | |
| // arguments through memory between callers and callees | |
| // which should get them all to agree on ABI regardless of | |
| // target feature sets. Some more information about this | |
| // issue can be found in #44367. | |
| // | |
| // We *could* do better in some cases, e.g. on x86_64 targets where SSE2 is | |
| // required. However, it turns out that that makes LLVM worse at optimizing this | |
| // code, so we pass things indirectly even there. See #139029 for more on that. | |
| if spec.simd_types_indirect { | |
| arg.make_indirect(); | |
| } | |
| } | |
| _ => {} | |
| } | |
| } | |
| } | |
| } | |
| /// Determines whether `layout` contains no uninit bytes (no padding, no unions), | |
| /// using only the computed layout. | |
| /// | |
| /// Conservative: returns `false` for anything it cannot prove fully initialized, | |
| /// including multi-variant enums and SIMD vectors. | |
| // FIXME: extend to multi-variant enums (per-variant padding analysis needed). | |
| fn layout_is_noundef<'a, Ty, C>(layout: TyAndLayout<'a, Ty>, cx: &C) -> bool | |
| where | |
| Ty: TyAbiInterface<'a, C> + Copy, | |
| C: HasDataLayout, | |
| { | |
| match layout.backend_repr { | |
| BackendRepr::Scalar(scalar) => !scalar.is_uninit_valid(), | |
| BackendRepr::ScalarPair(s1, s2) => { | |
| !s1.is_uninit_valid() | |
| && !s2.is_uninit_valid() | |
| // Ensure there is no padding. | |
| && s1.size(cx) + s2.size(cx) == layout.size | |
| } | |
| BackendRepr::Memory { .. } => match layout.fields { | |
| FieldsShape::Primitive | FieldsShape::Union(_) => false, | |
| // Array elements are at stride offsets with no inter-element gaps. | |
| FieldsShape::Array { stride: _, count } => { | |
| count == 0 || layout_is_noundef(layout.field(cx, 0), cx) | |
| } | |
| FieldsShape::Arbitrary { .. } => { | |
| // With `Variants::Multiple`, `layout.fields` only covers shared | |
| // bytes (niche/discriminant); per-variant data is absent, so | |
| // full coverage cannot be proven. | |
| matches!(layout.variants, Variants::Single { .. }) && fields_are_noundef(layout, cx) | |
| } | |
| }, | |
| BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => false, | |
| } | |
| } | |
| /// Returns `true` if the fields of `layout` contiguously cover bytes `0..layout.size` | |
| /// with no padding gaps and each field is recursively `layout_is_noundef`. | |
| fn fields_are_noundef<'a, Ty, C>(layout: TyAndLayout<'a, Ty>, cx: &C) -> bool | |
| where | |
| Ty: TyAbiInterface<'a, C> + Copy, | |
| C: HasDataLayout, | |
| { | |
| let mut cursor = Size::ZERO; | |
| for i in layout.fields.index_by_increasing_offset() { | |
| let field = layout.field(cx, i); | |
| if field.size == Size::ZERO { | |
| continue; | |
| } | |
| if layout.fields.offset(i) != cursor { | |
| return false; | |
| } | |
| if !layout_is_noundef(field, cx) { | |
| return false; | |
| } | |
| cursor += field.size; | |
| } | |
| cursor == layout.size | |
| } | |
| // Some types are used a lot. Make sure they don't unintentionally get bigger. | |
| #[cfg(target_pointer_width = "64")] | |
| mod size_asserts { | |
| use rustc_data_structures::static_assert_size; | |
| use super::*; | |
| // tidy-alphabetical-start | |
| static_assert_size!(ArgAbi<'_, usize>, 56); | |
| static_assert_size!(FnAbi<'_, usize>, 80); | |
| // tidy-alphabetical-end | |
| } |