about summary refs log tree commit diff
path: root/compiler/rustc_codegen_ssa/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_ssa/src')
-rw-r--r--compiler/rustc_codegen_ssa/src/back/apple.rs127
-rw-r--r--compiler/rustc_codegen_ssa/src/back/apple/tests.rs21
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs13
-rw-r--r--compiler/rustc_codegen_ssa/src/back/metadata.rs10
-rw-r--r--compiler/rustc_codegen_ssa/src/back/mod.rs21
5 files changed, 182 insertions, 10 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/apple.rs b/compiler/rustc_codegen_ssa/src/back/apple.rs
new file mode 100644
index 00000000000..939ce2e1a03
--- /dev/null
+++ b/compiler/rustc_codegen_ssa/src/back/apple.rs
@@ -0,0 +1,127 @@
+use std::env;
+use std::num::ParseIntError;
+
+use rustc_session::Session;
+use rustc_target::spec::Target;
+
+#[cfg(test)]
+mod tests;
+
+/// Deployment target or SDK version.
+///
+/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`.
+type OSVersion = (u16, u8, u8);
+
+/// Parse an OS version triple (SDK version or deployment target).
+fn parse_version(version: &str) -> Result<OSVersion, ParseIntError> {
+    if let Some((major, minor)) = version.split_once('.') {
+        let major = major.parse()?;
+        if let Some((minor, patch)) = minor.split_once('.') {
+            Ok((major, minor.parse()?, patch.parse()?))
+        } else {
+            Ok((major, minor.parse()?, 0))
+        }
+    } else {
+        Ok((version.parse()?, 0, 0))
+    }
+}
+
+/// Minimum operating system versions currently supported by `rustc`.
+fn os_minimum_deployment_target(os: &str) -> OSVersion {
+    // When bumping a version in here, remember to update the platform-support docs too.
+    //
+    // NOTE: The defaults may change in future `rustc` versions, so if you are looking for the
+    // default deployment target, prefer:
+    // ```
+    // $ rustc --print deployment-target
+    // ```
+    match os {
+        "macos" => (10, 12, 0),
+        "ios" => (10, 0, 0),
+        "tvos" => (10, 0, 0),
+        "watchos" => (5, 0, 0),
+        "visionos" => (1, 0, 0),
+        _ => unreachable!("tried to get deployment target for non-Apple platform"),
+    }
+}
+
+/// The deployment target for the given target.
+///
+/// This is similar to `os_minimum_deployment_target`, except that on certain targets it makes sense
+/// to raise the minimum OS version.
+///
+/// This matches what LLVM does, see in part:
+/// <https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/llvm/lib/TargetParser/Triple.cpp#L1900-L1932>
+fn minimum_deployment_target(target: &Target) -> OSVersion {
+    match (&*target.os, &*target.arch, &*target.abi) {
+        ("macos", "aarch64", _) => (11, 0, 0),
+        ("ios", "aarch64", "macabi") => (14, 0, 0),
+        ("ios", "aarch64", "sim") => (14, 0, 0),
+        ("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0),
+        // Mac Catalyst defaults to 13.1 in Clang.
+        ("ios", _, "macabi") => (13, 1, 0),
+        ("tvos", "aarch64", "sim") => (14, 0, 0),
+        ("watchos", "aarch64", "sim") => (7, 0, 0),
+        (os, _, _) => os_minimum_deployment_target(os),
+    }
+}
+
+/// Name of the environment variable used to fetch the deployment target on the given OS.
+fn deployment_target_env_var(os: &str) -> &'static str {
+    match os {
+        "macos" => "MACOSX_DEPLOYMENT_TARGET",
+        "ios" => "IPHONEOS_DEPLOYMENT_TARGET",
+        "watchos" => "WATCHOS_DEPLOYMENT_TARGET",
+        "tvos" => "TVOS_DEPLOYMENT_TARGET",
+        "visionos" => "XROS_DEPLOYMENT_TARGET",
+        _ => unreachable!("tried to get deployment target env var for non-Apple platform"),
+    }
+}
+
+/// Get the deployment target based on the standard environment variables, or fall back to the
+/// minimum version supported by `rustc`.
+pub fn deployment_target(sess: &Session) -> OSVersion {
+    let min = minimum_deployment_target(&sess.target);
+
+    if let Ok(deployment_target) = env::var(deployment_target_env_var(&sess.target.os)) {
+        match parse_version(&deployment_target) {
+            // It is common that the deployment target is set too low, e.g. on macOS Aarch64 to also
+            // target older x86_64, the user may set a lower deployment target than supported.
+            //
+            // To avoid such issues, we silently raise the deployment target here.
+            // FIXME: We want to show a warning when `version < os_min`.
+            Ok(version) => version.max(min),
+            // FIXME: Report erroneous environment variable to user.
+            Err(_) => min,
+        }
+    } else {
+        // If no deployment target variable is set, default to the minimum found above.
+        min
+    }
+}
+
+pub(super) fn add_version_to_llvm_target(
+    llvm_target: &str,
+    deployment_target: OSVersion,
+) -> String {
+    let mut components = llvm_target.split("-");
+    let arch = components.next().expect("apple target should have arch");
+    let vendor = components.next().expect("apple target should have vendor");
+    let os = components.next().expect("apple target should have os");
+    let environment = components.next();
+    assert_eq!(components.next(), None, "too many LLVM triple components");
+
+    let (major, minor, patch) = deployment_target;
+
+    assert!(
+        !os.contains(|c: char| c.is_ascii_digit()),
+        "LLVM target must not already be versioned"
+    );
+
+    if let Some(env) = environment {
+        // Insert version into OS, before environment
+        format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}-{env}")
+    } else {
+        format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}")
+    }
+}
diff --git a/compiler/rustc_codegen_ssa/src/back/apple/tests.rs b/compiler/rustc_codegen_ssa/src/back/apple/tests.rs
new file mode 100644
index 00000000000..7ccda5a8190
--- /dev/null
+++ b/compiler/rustc_codegen_ssa/src/back/apple/tests.rs
@@ -0,0 +1,21 @@
+use super::{add_version_to_llvm_target, parse_version};
+
+#[test]
+fn test_add_version_to_llvm_target() {
+    assert_eq!(
+        add_version_to_llvm_target("aarch64-apple-macosx", (10, 14, 1)),
+        "aarch64-apple-macosx10.14.1"
+    );
+    assert_eq!(
+        add_version_to_llvm_target("aarch64-apple-ios-simulator", (16, 1, 0)),
+        "aarch64-apple-ios16.1.0-simulator"
+    );
+}
+
+#[test]
+fn test_parse_version() {
+    assert_eq!(parse_version("10"), Ok((10, 0, 0)));
+    assert_eq!(parse_version("10.12"), Ok((10, 12, 0)));
+    assert_eq!(parse_version("10.12.6"), Ok((10, 12, 6)));
+    assert_eq!(parse_version("9999.99.99"), Ok((9999, 99, 99)));
+}
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 34dc599e4fd..b01a62b394b 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -40,7 +40,7 @@ use rustc_target::spec::crt_objects::CrtObjects;
 use rustc_target::spec::{
     Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFeatures,
     LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, SanitizerSet,
-    SplitDebuginfo, current_apple_deployment_target,
+    SplitDebuginfo,
 };
 use tempfile::Builder as TempFileBuilder;
 use tracing::{debug, info, warn};
@@ -50,6 +50,7 @@ use super::command::Command;
 use super::linker::{self, Linker};
 use super::metadata::{MetadataPosition, create_wrapper_file};
 use super::rpath::{self, RPathConfig};
+use super::{apple, versioned_llvm_target};
 use crate::{
     CodegenResults, CompiledModule, CrateInfo, NativeLib, common, errors,
     looks_like_rust_object_file,
@@ -2447,7 +2448,7 @@ fn add_order_independent_options(
     if flavor == LinkerFlavor::Llbc {
         cmd.link_args(&[
             "--target",
-            sess.target.llvm_target.as_ref(),
+            &versioned_llvm_target(sess),
             "--target-cpu",
             &codegen_results.crate_info.target_cpu,
         ]);
@@ -3039,7 +3040,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
             _ => bug!("invalid OS/ABI combination for Apple target: {target_os}, {target_abi}"),
         };
 
-        let (major, minor, patch) = current_apple_deployment_target(&sess.target);
+        let (major, minor, patch) = apple::deployment_target(sess);
         let min_version = format!("{major}.{minor}.{patch}");
 
         // The SDK version is used at runtime when compiling with a newer SDK / version of Xcode:
@@ -3109,7 +3110,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
 
             // The presence of `-mmacosx-version-min` makes CC default to
             // macOS, and it sets the deployment target.
-            let (major, minor, patch) = current_apple_deployment_target(&sess.target);
+            let (major, minor, patch) = apple::deployment_target(sess);
             // Intentionally pass this as a single argument, Clang doesn't
             // seem to like it otherwise.
             cmd.cc_arg(&format!("-mmacosx-version-min={major}.{minor}.{patch}"));
@@ -3119,7 +3120,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
             //
             // We avoid `-m32`/`-m64`, as this is already encoded by `-arch`.
         } else {
-            cmd.cc_args(&["-target", &sess.target.llvm_target]);
+            cmd.cc_args(&["-target", &versioned_llvm_target(sess)]);
         }
     }
 }
@@ -3345,7 +3346,7 @@ fn add_lld_args(
         // targeting a different linker flavor on macOS, and that's also always
         // the case when targeting WASM.
         if sess.target.linker_flavor != sess.host.linker_flavor {
-            cmd.cc_arg(format!("--target={}", sess.target.llvm_target));
+            cmd.cc_arg(format!("--target={}", versioned_llvm_target(sess)));
         }
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index 4c25c85569b..1b11fe661db 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -22,6 +22,8 @@ use rustc_span::sym;
 use rustc_target::abi::Endian;
 use rustc_target::spec::{RelocModel, Target, ef_avr_arch};
 
+use super::apple;
+
 /// The default metadata loader. This is used by cg_llvm and cg_clif.
 ///
 /// # Metadata location
@@ -238,7 +240,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
             file.set_macho_cpu_subtype(object::macho::CPU_SUBTYPE_ARM64E);
         }
 
-        file.set_macho_build_version(macho_object_build_version_for_target(&sess.target))
+        file.set_macho_build_version(macho_object_build_version_for_target(sess))
     }
     if binary_format == BinaryFormat::Coff {
         // Disable the default mangler to avoid mangling the special "@feat.00" symbol name.
@@ -392,7 +394,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
 ///
 /// Since Xcode 15, Apple's LD apparently requires object files to use this load command, so this
 /// returns the `MachOBuildVersion` for the target to do so.
-fn macho_object_build_version_for_target(target: &Target) -> object::write::MachOBuildVersion {
+fn macho_object_build_version_for_target(sess: &Session) -> object::write::MachOBuildVersion {
     /// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz"
     /// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200
     fn pack_version((major, minor, patch): (u16, u8, u8)) -> u32 {
@@ -401,8 +403,8 @@ fn macho_object_build_version_for_target(target: &Target) -> object::write::Mach
     }
 
     let platform =
-        rustc_target::spec::current_apple_platform(target).expect("unknown Apple target OS");
-    let min_os = rustc_target::spec::current_apple_deployment_target(target);
+        rustc_target::spec::current_apple_platform(&sess.target).expect("unknown Apple target OS");
+    let min_os = apple::deployment_target(sess);
 
     let mut build_version = object::write::MachOBuildVersion::default();
     build_version.platform = platform;
diff --git a/compiler/rustc_codegen_ssa/src/back/mod.rs b/compiler/rustc_codegen_ssa/src/back/mod.rs
index 2b3a2e3a369..64b5d4569ec 100644
--- a/compiler/rustc_codegen_ssa/src/back/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/back/mod.rs
@@ -1,3 +1,8 @@
+use std::borrow::Cow;
+
+use rustc_session::Session;
+
+pub mod apple;
 pub mod archive;
 pub(crate) mod command;
 pub mod link;
@@ -7,3 +12,19 @@ pub mod metadata;
 pub(crate) mod rpath;
 pub mod symbol_export;
 pub mod write;
+
+/// The target triple depends on the deployment target, and is required to
+/// enable features such as cross-language LTO, and for picking the right
+/// Mach-O commands.
+///
+/// Certain optimizations also depend on the deployment target.
+pub fn versioned_llvm_target(sess: &Session) -> Cow<'_, str> {
+    if sess.target.is_like_osx {
+        apple::add_version_to_llvm_target(&sess.target.llvm_target, apple::deployment_target(sess))
+            .into()
+    } else {
+        // FIXME(madsmtm): Certain other targets also include a version,
+        // we might want to move that here as well.
+        Cow::Borrowed(&sess.target.llvm_target)
+    }
+}