diff options
Diffstat (limited to 'compiler/rustc_target')
23 files changed, 391 insertions, 115 deletions
diff --git a/compiler/rustc_target/src/abi/call/loongarch.rs b/compiler/rustc_target/src/abi/call/loongarch.rs index 247256f07..e649d58bb 100644 --- a/compiler/rustc_target/src/abi/call/loongarch.rs +++ b/compiler/rustc_target/src/abi/call/loongarch.rs @@ -83,6 +83,17 @@ where } FieldsShape::Union(_) => { if !arg_layout.is_zst() { + if arg_layout.is_transparent() { + let non_1zst_elem = arg_layout.non_1zst_field(cx).expect("not exactly one non-1-ZST field in non-ZST repr(transparent) union").1; + return should_use_fp_conv_helper( + cx, + &non_1zst_elem, + xlen, + flen, + field1_kind, + field2_kind, + ); + } return Err(CannotUseFpConv); } } diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 8fab13d5d..5efd171b9 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -2,6 +2,7 @@ use crate::abi::{self, Abi, Align, FieldsShape, Size}; use crate::abi::{HasDataLayout, TyAbiInterface, TyAndLayout}; use crate::spec::{self, HasTargetSpec}; use rustc_span::Symbol; +use std::fmt; use std::str::FromStr; mod aarch64; @@ -36,23 +37,52 @@ pub enum PassMode { Ignore, /// Pass the argument directly. /// - /// The argument has a layout abi of `Scalar`, `Vector` or in rare cases `Aggregate`. + /// 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, to either a single uniform or a - /// pair of registers. The bool indicates if a `Reg::i32()` dummy argument - /// is emitted before the real argument. - Cast(Box<CastTarget>, bool), + /// Pass the argument after casting it. See the `CastTarget` docs for details. The bool + /// 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 `extra_attrs` value, if any, is for the extra data (vtable or length) - /// which indicates that it refers to an unsized rvalue. - /// `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. - Indirect { attrs: ArgAttributes, extra_attrs: Option<ArgAttributes>, on_stack: bool }, + /// 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 (using the Rust type of this argument). `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 @@ -127,6 +157,24 @@ impl ArgAttributes { 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; + } + return true; + } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] @@ -211,6 +259,13 @@ impl Uniform { } } +/// 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], @@ -272,6 +327,14 @@ impl CastTarget { 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: rest_l, attrs: attrs_l } = self; + let CastTarget { prefix: prefix_r, rest: rest_r, attrs: attrs_r } = other; + prefix_l == prefix_r && rest_l == rest_r && attrs_l.eq_abi(attrs_r) + } } /// Return value from the `homogeneous_aggregate` test function. @@ -330,8 +393,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { /// only a single type (e.g., `(u32, u32)`). Such aggregates are often /// special-cased in ABIs. /// - /// Note: We generally ignore fields of zero-sized type when computing - /// this value (see #56877). + /// Note: We generally ignore 1-ZST fields when computing this value (see #56877). /// /// This is public so that it can be used in unit tests, but /// should generally only be relevant to the ABI details of @@ -389,12 +451,18 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { let mut total = start; for i in 0..layout.fields.count() { + let field = layout.field(cx, i); + if field.is_1zst() { + // No data here and no impact on layout, can be ignored. + // (We might be able to also ignore all aligned ZST but that's less clear.) + continue; + } + if !is_union && total != layout.fields.offset(i) { + // This field isn't just after the previous one we considered, abort. return Err(Heterogeneous); } - let field = layout.field(cx, i); - result = result.merge(field.homogeneous_aggregate(cx)?)?; // Keep track of the offset (without padding). @@ -458,13 +526,22 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { /// Information about how to pass an argument to, /// or return a value from, a function, under some ABI. -#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[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>, @@ -478,6 +555,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { scalar_attrs(&layout, b, a.size(cx).align_to(b.align(cx).abi)), ), Abi::Vector { .. } => PassMode::Direct(ArgAttributes::new()), + // The `Aggregate` ABI should always be adjusted later. Abi::Aggregate { .. } => PassMode::Direct(ArgAttributes::new()), }; ArgAbi { layout, mode } @@ -497,15 +575,15 @@ impl<'a, Ty> ArgAbi<'a, Ty> { attrs.pointee_size = layout.size; attrs.pointee_align = Some(layout.align.abi); - let extra_attrs = layout.is_unsized().then_some(ArgAttributes::new()); + let meta_attrs = layout.is_unsized().then_some(ArgAttributes::new()); - PassMode::Indirect { attrs, extra_attrs, on_stack: false } + PassMode::Indirect { attrs, meta_attrs, on_stack: false } } pub fn make_indirect(&mut self) { match self.mode { PassMode::Direct(_) | PassMode::Pair(_, _) => {} - PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: false } => return, + PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: false } => return, _ => panic!("Tried to make {:?} indirect", self.mode), } @@ -515,7 +593,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { pub fn make_indirect_byval(&mut self, byval_align: Option<Align>) { self.make_indirect(); match self.mode { - PassMode::Indirect { ref mut attrs, extra_attrs: _, ref mut on_stack } => { + 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 @@ -548,11 +626,11 @@ impl<'a, Ty> ArgAbi<'a, Ty> { } pub fn cast_to<T: Into<CastTarget>>(&mut self, target: T) { - self.mode = PassMode::Cast(Box::new(target.into()), false); + self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32: false }; } pub fn cast_to_and_pad_i32<T: Into<CastTarget>>(&mut self, target: T, pad_i32: bool) { - self.mode = PassMode::Cast(Box::new(target.into()), pad_i32); + self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32 }; } pub fn is_indirect(&self) -> bool { @@ -560,16 +638,24 @@ impl<'a, Ty> ArgAbi<'a, Ty> { } pub fn is_sized_indirect(&self) -> bool { - matches!(self.mode, PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ }) + matches!(self.mode, PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ }) } pub fn is_unsized_indirect(&self) -> bool { - matches!(self.mode, PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ }) + 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 { + // 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) + } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] @@ -579,10 +665,9 @@ pub enum Conv { C, Rust, - /// For things unlikely to be called, where smaller caller codegen is - /// preferred over raw speed. - /// Stronger than just `#[cold]` because `fn` pointers might be incompatible. - RustCold, + Cold, + PreserveMost, + PreserveAll, // Target-specific calling conventions. ArmAapcs, @@ -605,9 +690,7 @@ pub enum Conv { AvrInterrupt, AvrNonBlockingInterrupt, - RiscvInterrupt { - kind: RiscvInterruptKind, - }, + RiscvInterrupt { kind: RiscvInterruptKind }, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] @@ -630,7 +713,7 @@ impl RiscvInterruptKind { /// /// 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, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, Hash, HashStable_Generic)] pub struct FnAbi<'a, Ty> { /// The LLVM types of each argument. pub args: Box<[ArgAbi<'a, Ty>]>, @@ -651,6 +734,21 @@ pub struct FnAbi<'a, Ty> { 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() + } +} + /// Error produced by attempting to adjust a `FnAbi`, for a "foreign" ABI. #[derive(Copy, Clone, Debug, HashStable_Generic)] pub enum AdjustForForeignAbiError { diff --git a/compiler/rustc_target/src/abi/call/riscv.rs b/compiler/rustc_target/src/abi/call/riscv.rs index d90dce2a0..93a204563 100644 --- a/compiler/rustc_target/src/abi/call/riscv.rs +++ b/compiler/rustc_target/src/abi/call/riscv.rs @@ -89,6 +89,17 @@ where } FieldsShape::Union(_) => { if !arg_layout.is_zst() { + if arg_layout.is_transparent() { + let non_1zst_elem = arg_layout.non_1zst_field(cx).expect("not exactly one non-1-ZST field in non-ZST repr(transparent) union").1; + return should_use_fp_conv_helper( + cx, + &non_1zst_elem, + xlen, + flen, + field1_kind, + field2_kind, + ); + } return Err(CannotUseFpConv); } } diff --git a/compiler/rustc_target/src/abi/call/wasm.rs b/compiler/rustc_target/src/abi/call/wasm.rs index 0eb2309ec..796b752ff 100644 --- a/compiler/rustc_target/src/abi/call/wasm.rs +++ b/compiler/rustc_target/src/abi/call/wasm.rs @@ -61,6 +61,10 @@ where /// The purpose of this ABI is for matching the WebAssembly standard. This /// intentionally diverges from the C ABI and is specifically crafted to take /// advantage of LLVM's support of multiple returns in WebAssembly. +/// +/// This ABI is *bad*! It uses `PassMode::Direct` for `abi::Aggregate` types, which leaks LLVM +/// implementation details into the ABI. It's just hard to fix because ABIs are hard to change. +/// Also see <https://github.com/rust-lang/rust/issues/115666>. pub fn compute_wasm_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) { if !fn_abi.ret.is_ignore() { classify_ret(&mut fn_abi.ret); diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/abi/call/x86.rs index b738c3133..afa1b70ef 100644 --- a/compiler/rustc_target/src/abi/call/x86.rs +++ b/compiler/rustc_target/src/abi/call/x86.rs @@ -142,13 +142,13 @@ where for arg in fn_abi.args.iter_mut() { let attrs = match arg.mode { PassMode::Ignore - | PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => { + | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { continue; } PassMode::Direct(ref mut attrs) => attrs, PassMode::Pair(..) - | PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } - | PassMode::Cast(..) => { + | PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } + | PassMode::Cast { .. } => { unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode) } }; diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index dd435dbb0..74fe98920 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -3,6 +3,7 @@ pub use Primitive::*; use crate::json::{Json, ToJson}; +use std::fmt; use std::ops::Deref; use rustc_macros::HashStable_Generic; @@ -24,12 +25,22 @@ impl ToJson for Endian { /// to that obtained from `layout_of(ty)`, as we need to produce /// layouts for which Rust types do not exist, such as enum variants /// or synthetic fields of enums (i.e., discriminants) and fat pointers. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)] pub struct TyAndLayout<'a, Ty> { pub ty: Ty, pub layout: Layout<'a>, } +impl<'a, Ty: fmt::Display> fmt::Debug for TyAndLayout<'a, Ty> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Print the type in a readable way, not its debug representation. + f.debug_struct("TyAndLayout") + .field("ty", &format_args!("{}", self.ty)) + .field("layout", &self.layout) + .finish() + } +} + impl<'a, Ty> Deref for TyAndLayout<'a, Ty> { type Target = &'a LayoutS; fn deref(&self) -> &&'a LayoutS { @@ -55,6 +66,7 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug { fn is_never(this: TyAndLayout<'a, Self>) -> bool; fn is_tuple(this: TyAndLayout<'a, Self>) -> bool; fn is_unit(this: TyAndLayout<'a, Self>) -> bool; + fn is_transparent(this: TyAndLayout<'a, Self>) -> bool; } impl<'a, Ty> TyAndLayout<'a, Ty> { @@ -125,6 +137,13 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { Ty::is_unit(self) } + pub fn is_transparent<C>(self) -> bool + where + Ty: TyAbiInterface<'a, C>, + { + Ty::is_transparent(self) + } + pub fn offset_of_subfield<C>(self, cx: &C, indices: impl Iterator<Item = usize>) -> Size where Ty: TyAbiInterface<'a, C>, @@ -144,4 +163,25 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { offset } + + /// Finds the one field that is not a 1-ZST. + /// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields. + pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(usize, Self)> + where + Ty: TyAbiInterface<'a, C> + Copy, + { + let mut found = None; + for field_idx in 0..self.fields.count() { + let field = self.field(cx, field_idx); + if field.is_1zst() { + continue; + } + if found.is_some() { + // More than one non-1-ZST field. + return None; + } + found = Some((field_idx, field)); + } + found + } } diff --git a/compiler/rustc_target/src/json.rs b/compiler/rustc_target/src/json.rs index af455b643..c61351490 100644 --- a/compiler/rustc_target/src/json.rs +++ b/compiler/rustc_target/src/json.rs @@ -96,7 +96,9 @@ impl ToJson for crate::abi::call::Conv { let s = match self { Self::C => "C", Self::Rust => "Rust", - Self::RustCold => "RustCold", + Self::Cold => "Cold", + Self::PreserveMost => "PreserveMost", + Self::PreserveAll => "PreserveAll", Self::ArmAapcs => "ArmAapcs", Self::CCmseNonSecureCall => "CCmseNonSecureCall", Self::Msp430Intr => "Msp430Intr", diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index b52002b12..e838e1113 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -19,7 +19,7 @@ #![feature(step_trait)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![allow(internal_features)] use std::path::{Path, PathBuf}; diff --git a/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs index e2df7e0bd..b29ab14e7 100644 --- a/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs @@ -1,5 +1,5 @@ use super::apple_base::{opts, Arch}; -use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, Target, TargetOptions}; +use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, SanitizerSet, Target, TargetOptions}; pub fn target() -> Target { let llvm_target = "arm64-apple-ios14.0-macabi"; @@ -7,6 +7,7 @@ pub fn target() -> Target { let arch = Arch::Arm64_macabi; let mut base = opts("ios", arch); base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-target", llvm_target]); + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD; Target { llvm_target: llvm_target.into(), diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs index 550cdf6bd..a99cccd42 100644 --- a/compiler/rustc_target/src/spec/abi.rs +++ b/compiler/rustc_target/src/spec/abi.rs @@ -14,15 +14,33 @@ pub enum Abi { // hashing tests. These are used in many places, so giving them stable values reduces test // churn. The specific values are meaningless. Rust, - C { unwind: bool }, - Cdecl { unwind: bool }, - Stdcall { unwind: bool }, - Fastcall { unwind: bool }, - Vectorcall { unwind: bool }, - Thiscall { unwind: bool }, - Aapcs { unwind: bool }, - Win64 { unwind: bool }, - SysV64 { unwind: bool }, + C { + unwind: bool, + }, + Cdecl { + unwind: bool, + }, + Stdcall { + unwind: bool, + }, + Fastcall { + unwind: bool, + }, + Vectorcall { + unwind: bool, + }, + Thiscall { + unwind: bool, + }, + Aapcs { + unwind: bool, + }, + Win64 { + unwind: bool, + }, + SysV64 { + unwind: bool, + }, PtxKernel, Msp430Interrupt, X86Interrupt, @@ -32,11 +50,16 @@ pub enum Abi { AvrNonBlockingInterrupt, CCmseNonSecureCall, Wasm, - System { unwind: bool }, + System { + unwind: bool, + }, RustIntrinsic, RustCall, PlatformIntrinsic, Unadjusted, + /// For things unlikely to be called, where reducing register pressure in + /// `extern "Rust"` callers is worth paying extra cost in the callee. + /// Stronger than just `#[cold]` because `fn` pointers might be incompatible. RustCold, RiscvInterruptM, RiscvInterruptS, @@ -45,7 +68,7 @@ pub enum Abi { impl Abi { pub fn supports_varargs(self) -> bool { // * C and Cdecl obviously support varargs. - // * C can be based on SysV64 or Win64, so they must support varargs. + // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs. // * EfiApi is based on Win64 or C, so it also supports it. // // * Stdcall does not, because it would be impossible for the callee to clean @@ -56,6 +79,7 @@ impl Abi { match self { Self::C { .. } | Self::Cdecl { .. } + | Self::Aapcs { .. } | Self::Win64 { .. } | Self::SysV64 { .. } | Self::EfiApi => true, diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index 8a8d1ab95..7a666eea4 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -11,7 +11,6 @@ use Arch::*; #[allow(non_camel_case_types)] #[derive(Copy, Clone)] pub enum Arch { - Armv7, Armv7k, Armv7s, Arm64, @@ -29,7 +28,6 @@ pub enum Arch { impl Arch { pub fn target_name(self) -> &'static str { match self { - Armv7 => "armv7", Armv7k => "armv7k", Armv7s => "armv7s", Arm64 | Arm64_macabi | Arm64_sim => "arm64", @@ -43,7 +41,7 @@ impl Arch { pub fn target_arch(self) -> Cow<'static, str> { Cow::Borrowed(match self { - Armv7 | Armv7k | Armv7s => "arm", + Armv7k | Armv7s => "arm", Arm64 | Arm64_32 | Arm64_macabi | Arm64_sim => "aarch64", I386 | I686 => "x86", X86_64 | X86_64_sim | X86_64_macabi | X86_64h => "x86_64", @@ -52,7 +50,7 @@ impl Arch { fn target_abi(self) -> &'static str { match self { - Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64h => "", + Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64h => "", X86_64_macabi | Arm64_macabi => "macabi", // x86_64-apple-ios is a simulator target, even though it isn't // declared that way in the target like the other ones... @@ -62,18 +60,20 @@ impl Arch { fn target_cpu(self) -> &'static str { match self { - Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher Armv7k => "cortex-a8", - Armv7s => "cortex-a9", + Armv7s => "swift", // iOS 10 is only supported on iPhone 5 or higher. Arm64 => "apple-a7", Arm64_32 => "apple-s4", - I386 | I686 => "yonah", - X86_64 | X86_64_sim => "core2", + // Only macOS 10.12+ is supported, which means + // all x86_64/x86 CPUs must be running at least penryn + // https://github.com/llvm/llvm-project/blob/01f924d0e37a5deae51df0d77e10a15b63aa0c0f/clang/lib/Driver/ToolChains/Arch/X86.cpp#L79-L82 + I386 | I686 => "penryn", + X86_64 | X86_64_sim => "penryn", + X86_64_macabi => "penryn", // Note: `core-avx2` is slightly more advanced than `x86_64h`, see // comments (and disabled features) in `x86_64h_apple_darwin` for - // details. + // details. It is a higher baseline then `penryn` however. X86_64h => "core-avx2", - X86_64_macabi => "core2", Arm64_macabi => "apple-a12", Arm64_sim => "apple-a12", } @@ -115,21 +115,6 @@ fn pre_link_args(os: &'static str, arch: Arch, abi: &'static str) -> LinkArgs { } pub fn opts(os: &'static str, arch: Arch) -> TargetOptions { - // Static TLS is only available in macOS 10.7+. If you try to compile for 10.6 - // either the linker will complain if it is used or the binary will end up - // segfaulting at runtime when run on 10.6. Rust by default supports macOS - // 10.7+, but there is a standard environment variable, - // MACOSX_DEPLOYMENT_TARGET, which is used to signal targeting older - // versions of macOS. For example compiling on 10.10 with - // MACOSX_DEPLOYMENT_TARGET set to 10.6 will cause the linker to generate - // warnings about the usage of static TLS. - // - // Here we detect what version is being requested, defaulting to 10.7. Static - // TLS is flagged as enabled if it looks to be supported. The architecture - // only matters for default deployment target which is 11.0 for ARM64 and - // 10.7 for everything else. - let has_thread_local = os == "macos" && macos_deployment_target(Arch::X86_64) >= (10, 7); - let abi = arch.target_abi(); TargetOptions { @@ -145,12 +130,17 @@ pub fn opts(os: &'static str, arch: Arch) -> TargetOptions { pre_link_args: pre_link_args(os, arch, abi), families: cvs!["unix"], is_like_osx: true, - default_dwarf_version: 2, + // LLVM notes that macOS 10.11+ and iOS 9+ default + // to v4, so we do the same. + // https://github.com/llvm/llvm-project/blob/378778a0d10c2f8d5df8ceff81f95b6002984a4b/clang/lib/Driver/ToolChains/Darwin.cpp#L1203 + default_dwarf_version: 4, frame_pointer: FramePointer::Always, has_rpath: true, dll_suffix: ".dylib".into(), archive_format: "darwin".into(), - has_thread_local, + // Thread locals became available with iOS 8 and macOS 10.7, + // and both are far below our minimum. + has_thread_local: true, abi_return_struct_as_int: true, emit_debug_gdb_scripts: false, eh_frame_header: false, @@ -179,20 +169,52 @@ pub fn opts(os: &'static str, arch: Arch) -> TargetOptions { } } -pub fn deployment_target(target: &Target) -> Option<String> { +pub fn sdk_version(platform: u32) -> Option<(u32, u32)> { + // NOTE: These values are from an arbitrary point in time but shouldn't make it into the final + // binary since the final link command will have the current SDK version passed to it. + match platform { + object::macho::PLATFORM_MACOS => Some((13, 1)), + object::macho::PLATFORM_IOS + | object::macho::PLATFORM_IOSSIMULATOR + | object::macho::PLATFORM_TVOS + | object::macho::PLATFORM_TVOSSIMULATOR + | object::macho::PLATFORM_MACCATALYST => Some((16, 2)), + object::macho::PLATFORM_WATCHOS | object::macho::PLATFORM_WATCHOSSIMULATOR => Some((9, 1)), + _ => None, + } +} + +pub fn platform(target: &Target) -> Option<u32> { + Some(match (&*target.os, &*target.abi) { + ("macos", _) => object::macho::PLATFORM_MACOS, + ("ios", "macabi") => object::macho::PLATFORM_MACCATALYST, + ("ios", "sim") => object::macho::PLATFORM_IOSSIMULATOR, + ("ios", _) => object::macho::PLATFORM_IOS, + ("watchos", "sim") => object::macho::PLATFORM_WATCHOSSIMULATOR, + ("watchos", _) => object::macho::PLATFORM_WATCHOS, + ("tvos", "sim") => object::macho::PLATFORM_TVOSSIMULATOR, + ("tvos", _) => object::macho::PLATFORM_TVOS, + _ => return None, + }) +} + +pub fn deployment_target(target: &Target) -> Option<(u32, u32)> { let (major, minor) = match &*target.os { "macos" => { // This does not need to be specific. It just needs to handle x86 vs M1. let arch = if target.arch == "x86" || target.arch == "x86_64" { X86_64 } else { Arm64 }; macos_deployment_target(arch) } - "ios" => ios_deployment_target(), + "ios" => match &*target.abi { + "macabi" => mac_catalyst_deployment_target(), + _ => ios_deployment_target(), + }, "watchos" => watchos_deployment_target(), "tvos" => tvos_deployment_target(), _ => return None, }; - Some(format!("{major}.{minor}")) + Some((major, minor)) } fn from_set_deployment_target(var_name: &str) -> Option<(u32, u32)> { @@ -207,9 +229,7 @@ fn macos_default_deployment_target(arch: Arch) -> (u32, u32) { match arch { // Note: Arm64_sim is not included since macOS has no simulator. Arm64 | Arm64_macabi => (11, 0), - // x86_64h-apple-darwin only supports macOS 10.8 and later - X86_64h => (10, 8), - _ => (10, 7), + _ => (10, 12), } } @@ -260,8 +280,8 @@ fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]> // Otherwise if cross-compiling for a different OS/SDK, remove any part // of the linking environment that's wrong and reversed. match arch { - Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64_sim - | X86_64h | Arm64_sim => { + Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64_sim | X86_64h + | Arm64_sim => { cvs!["MACOSX_DEPLOYMENT_TARGET"] } X86_64_macabi | Arm64_macabi => cvs!["IPHONEOS_DEPLOYMENT_TARGET"], @@ -271,7 +291,12 @@ fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]> fn ios_deployment_target() -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. - from_set_deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((7, 0)) + from_set_deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((10, 0)) +} + +fn mac_catalyst_deployment_target() -> (u32, u32) { + // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. + from_set_deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((14, 0)) } pub fn ios_llvm_target(arch: Arch) -> String { @@ -297,7 +322,7 @@ pub fn ios_sim_llvm_target(arch: Arch) -> String { fn tvos_deployment_target() -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. - from_set_deployment_target("TVOS_DEPLOYMENT_TARGET").unwrap_or((7, 0)) + from_set_deployment_target("TVOS_DEPLOYMENT_TARGET").unwrap_or((10, 0)) } fn tvos_lld_platform_version() -> String { diff --git a/compiler/rustc_target/src/spec/armv7_apple_ios.rs b/compiler/rustc_target/src/spec/armv7_apple_ios.rs deleted file mode 100644 index 3259c8547..000000000 --- a/compiler/rustc_target/src/spec/armv7_apple_ios.rs +++ /dev/null @@ -1,21 +0,0 @@ -use super::apple_base::{ios_llvm_target, opts, Arch}; -use crate::spec::{Target, TargetOptions}; - -pub fn target() -> Target { - let arch = Arch::Armv7; - Target { - // Clang automatically chooses a more specific target based on - // IPHONEOS_DEPLOYMENT_TARGET. - // This is required for the target to pick the right - // MACH-O commands, so we do too. - llvm_target: ios_llvm_target(arch).into(), - pointer_width: 32, - data_layout: "e-m:o-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".into(), - arch: arch.target_arch(), - options: TargetOptions { - features: "+v7,+vfp3,+neon".into(), - max_atomic_width: Some(64), - ..opts("ios", arch) - }, - } -} diff --git a/compiler/rustc_target/src/spec/armv7s_apple_ios.rs b/compiler/rustc_target/src/spec/armv7s_apple_ios.rs index be4bc6758..be7f8542c 100644 --- a/compiler/rustc_target/src/spec/armv7s_apple_ios.rs +++ b/compiler/rustc_target/src/spec/armv7s_apple_ios.rs @@ -1,10 +1,10 @@ -use super::apple_base::{opts, Arch}; +use super::apple_base::{ios_llvm_target, opts, Arch}; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { let arch = Arch::Armv7s; Target { - llvm_target: "armv7s-apple-ios".into(), + llvm_target: ios_llvm_target(arch).into(), pointer_width: 32, data_layout: "e-m:o-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".into(), arch: arch.target_arch(), diff --git a/compiler/rustc_target/src/spec/hurd_base.rs b/compiler/rustc_target/src/spec/hurd_base.rs new file mode 100644 index 000000000..76f8223c0 --- /dev/null +++ b/compiler/rustc_target/src/spec/hurd_base.rs @@ -0,0 +1,15 @@ +use crate::spec::{cvs, RelroLevel, TargetOptions}; + +pub fn opts() -> TargetOptions { + TargetOptions { + os: "hurd".into(), + dynamic_linking: true, + families: cvs!["unix"], + has_rpath: true, + position_independent_executables: true, + relro_level: RelroLevel::Full, + has_thread_local: true, + crt_static_respected: true, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/hurd_gnu_base.rs b/compiler/rustc_target/src/spec/hurd_gnu_base.rs new file mode 100644 index 000000000..b9cf26d93 --- /dev/null +++ b/compiler/rustc_target/src/spec/hurd_gnu_base.rs @@ -0,0 +1,5 @@ +use crate::spec::TargetOptions; + +pub fn opts() -> TargetOptions { + TargetOptions { env: "gnu".into(), ..super::hurd_base::opts() } +} diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/i686_pc_windows_gnullvm.rs new file mode 100644 index 000000000..3154b512a --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_pc_windows_gnullvm.rs @@ -0,0 +1,26 @@ +use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, Target}; + +pub fn target() -> Target { + let mut base = super::windows_gnullvm_base::opts(); + base.cpu = "pentium4".into(); + base.max_atomic_width = Some(64); + base.frame_pointer = FramePointer::Always; // Required for backtraces + base.linker = Some("i686-w64-mingw32-clang".into()); + + // Mark all dynamic libraries and executables as compatible with the larger 4GiB address + // space available to x86 Windows binaries on x86_64. + base.add_pre_link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &["-m", "i386pe", "--large-address-aware"], + ); + + Target { + llvm_target: "i686-pc-windows-gnu".into(), + pointer_width: 32, + data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + i64:64-f80:32-n8:16:32-a:0:32-S32" + .into(), + arch: "x86".into(), + options: base, + } +} diff --git a/compiler/rustc_target/src/spec/i686_unknown_hurd_gnu.rs b/compiler/rustc_target/src/spec/i686_unknown_hurd_gnu.rs new file mode 100644 index 000000000..29f803601 --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_unknown_hurd_gnu.rs @@ -0,0 +1,19 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target}; + +pub fn target() -> Target { + let mut base = super::hurd_gnu_base::opts(); + base.cpu = "pentiumpro".into(); + base.max_atomic_width = Some(64); + base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]); + base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; + + Target { + llvm_target: "i686-unknown-hurd-gnu".into(), + pointer_width: 32, + data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + f64:32:64-f80:32-n8:16:32-S128" + .into(), + arch: "x86".into(), + options: base, + } +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 31b6961bb..1bcb1f353 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -61,6 +61,8 @@ mod aix_base; mod android_base; mod apple_base; pub use apple_base::deployment_target as current_apple_deployment_target; +pub use apple_base::platform as current_apple_platform; +pub use apple_base::sdk_version as current_apple_sdk_version; mod avr_gnu_base; pub use avr_gnu_base::ef_avr_arch; mod bpf_base; @@ -69,6 +71,8 @@ mod freebsd_base; mod fuchsia_base; mod haiku_base; mod hermit_base; +mod hurd_base; +mod hurd_gnu_base; mod illumos_base; mod l4re_base; mod linux_base; @@ -1365,6 +1369,8 @@ supported_targets! { ("i686-unknown-haiku", i686_unknown_haiku), ("x86_64-unknown-haiku", x86_64_unknown_haiku), + ("i686-unknown-hurd-gnu", i686_unknown_hurd_gnu), + ("aarch64-apple-darwin", aarch64_apple_darwin), ("x86_64-apple-darwin", x86_64_apple_darwin), ("x86_64h-apple-darwin", x86_64h_apple_darwin), @@ -1388,7 +1394,6 @@ supported_targets! { ("i386-apple-ios", i386_apple_ios), ("x86_64-apple-ios", x86_64_apple_ios), ("aarch64-apple-ios", aarch64_apple_ios), - ("armv7-apple-ios", armv7_apple_ios), ("armv7s-apple-ios", armv7s_apple_ios), ("x86_64-apple-ios-macabi", x86_64_apple_ios_macabi), ("aarch64-apple-ios-macabi", aarch64_apple_ios_macabi), @@ -1418,6 +1423,7 @@ supported_targets! { ("x86_64-uwp-windows-gnu", x86_64_uwp_windows_gnu), ("aarch64-pc-windows-gnullvm", aarch64_pc_windows_gnullvm), + ("i686-pc-windows-gnullvm", i686_pc_windows_gnullvm), ("x86_64-pc-windows-gnullvm", x86_64_pc_windows_gnullvm), ("aarch64-pc-windows-msvc", aarch64_pc_windows_msvc), @@ -2276,6 +2282,13 @@ impl Target { Abi::Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => abi, Abi::Fastcall { unwind } | Abi::Vectorcall { unwind } => Abi::C { unwind }, + // The Windows x64 calling convention we use for `extern "Rust"` + // <https://learn.microsoft.com/en-us/cpp/build/x64-software-conventions#register-volatility-and-preservation> + // expects the callee to save `xmm6` through `xmm15`, but `PreserveMost` + // (that we use by default for `extern "rust-cold"`) doesn't save any of those. + // So to avoid bloating callers, just use the Rust convention here. + Abi::RustCold if self.is_like_windows && self.arch == "x86_64" => Abi::Rust, + abi => abi, } } diff --git a/compiler/rustc_target/src/spec/riscv64_linux_android.rs b/compiler/rustc_target/src/spec/riscv64_linux_android.rs index af0d68554..91f5e562d 100644 --- a/compiler/rustc_target/src/spec/riscv64_linux_android.rs +++ b/compiler/rustc_target/src/spec/riscv64_linux_android.rs @@ -9,7 +9,7 @@ pub fn target() -> Target { options: TargetOptions { code_model: Some(CodeModel::Medium), cpu: "generic-rv64".into(), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+Zba,+Zbb,+Zbs".into(), llvm_abiname: "lp64d".into(), supported_sanitizers: SanitizerSet::ADDRESS, max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs index 8968d3c8f..a50a55ad7 100644 --- a/compiler/rustc_target/src/spec/uefi_msvc_base.rs +++ b/compiler/rustc_target/src/spec/uefi_msvc_base.rs @@ -46,6 +46,7 @@ pub fn opts() -> TargetOptions { stack_probes: StackProbeType::Call, singlethread: true, linker: Some("rust-lld".into()), + entry_name: "efi_main".into(), ..base } } diff --git a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs index e90bda9c9..e3f5d7321 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs @@ -5,7 +5,7 @@ use crate::spec::{StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let arch = Arch::X86_64; let mut base = opts("macos", arch); - base.max_atomic_width = Some(128); // core2 supports cmpxchg16b + base.max_atomic_width = Some(128); // penryn+ supports cmpxchg16b base.frame_pointer = FramePointer::Always; base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-m64"]); base.stack_probes = StackProbeType::X86; diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs index 50f359c35..fd1926f29 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs @@ -1,5 +1,5 @@ use super::apple_base::{opts, Arch}; -use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions}; +use crate::spec::{Cc, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let llvm_target = "x86_64-apple-ios14.0-macabi"; @@ -7,6 +7,7 @@ pub fn target() -> Target { let arch = Arch::X86_64_macabi; let mut base = opts("ios", arch); base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-target", llvm_target]); + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD; Target { llvm_target: llvm_target.into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs index 67664a747..41ba76806 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs @@ -5,13 +5,14 @@ // The win64 ABI is used. It differs from the sysv64 ABI, so we must use a windows target with // LLVM. "x86_64-unknown-windows" is used to get the minimal subset of windows-specific features. -use crate::spec::Target; +use crate::{abi::call::Conv, spec::Target}; pub fn target() -> Target { let mut base = super::uefi_msvc_base::opts(); base.cpu = "x86-64".into(); base.plt_by_default = false; base.max_atomic_width = Some(64); + base.entry_abi = Conv::X86_64Win64; // We disable MMX and SSE for now, even though UEFI allows using them. Problem is, you have to // enable these CPU features explicitly before their first use, otherwise their instructions |