Skip to content

Commit 6f6292e

Browse files
committed
Move versioned LLVM target creation to rustc_codegen_ssa
The OS version depends on the deployment target environment variables, the access of which we want to move to later in the compilation pipeline that has access to more information, for example `env_depinfo`.
1 parent 5384697 commit 6f6292e

File tree

12 files changed

+178
-138
lines changed

12 files changed

+178
-138
lines changed

compiler/rustc_codegen_cranelift/src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use std::sync::Arc;
3939
use cranelift_codegen::isa::TargetIsa;
4040
use cranelift_codegen::settings::{self, Configurable};
4141
use rustc_codegen_ssa::CodegenResults;
42+
use rustc_codegen_ssa::apple::versioned_llvm_target;
4243
use rustc_codegen_ssa::traits::CodegenBackend;
4344
use rustc_data_structures::profiling::SelfProfilerRef;
4445
use rustc_errors::ErrorGuaranteed;
@@ -259,7 +260,9 @@ impl CodegenBackend for CraneliftCodegenBackend {
259260
}
260261

261262
fn target_triple(sess: &Session) -> target_lexicon::Triple {
262-
match sess.target.llvm_target.parse() {
263+
// FIXME(madsmtm): Use `sess.target.llvm_target` once target-lexicon supports unversioned macOS.
264+
// See <https://github.com/bytecodealliance/target-lexicon/pull/113>
265+
match versioned_llvm_target(sess).parse() {
263266
Ok(triple) => triple,
264267
Err(err) => sess.dcx().fatal(format!("target not recognized: {}", err)),
265268
}

compiler/rustc_codegen_llvm/src/back/write.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use libc::{c_char, c_int, c_void, size_t};
88
use llvm::{
99
LLVMRustLLVMHasZlibCompressionForDebugSymbols, LLVMRustLLVMHasZstdCompressionForDebugSymbols,
1010
};
11+
use rustc_codegen_ssa::apple::versioned_llvm_target;
1112
use rustc_codegen_ssa::back::link::ensure_removed;
1213
use rustc_codegen_ssa::back::write::{
1314
BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig,
@@ -210,7 +211,7 @@ pub(crate) fn target_machine_factory(
210211
singlethread = false;
211212
}
212213

213-
let triple = SmallCStr::new(&sess.target.llvm_target);
214+
let triple = SmallCStr::new(&versioned_llvm_target(sess));
214215
let cpu = SmallCStr::new(llvm_util::target_cpu(sess));
215216
let features = CString::new(target_features.join(",")).unwrap();
216217
let abi = SmallCStr::new(&sess.target.llvm_abiname);

compiler/rustc_codegen_llvm/src/context.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::cell::{Cell, RefCell};
33
use std::ffi::{CStr, c_uint};
44
use std::str;
55

6+
use rustc_codegen_ssa::apple::versioned_llvm_target;
67
use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh};
78
use rustc_codegen_ssa::errors as ssa_errors;
89
use rustc_codegen_ssa::traits::*;
@@ -165,7 +166,7 @@ pub(crate) unsafe fn create_module<'ll>(
165166
llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr());
166167
}
167168

168-
let llvm_target = SmallCStr::new(&sess.target.llvm_target);
169+
let llvm_target = SmallCStr::new(&versioned_llvm_target(sess));
169170
unsafe {
170171
llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr());
171172
}
+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
use std::borrow::Cow;
2+
use std::env;
3+
use std::num::ParseIntError;
4+
5+
use rustc_middle::bug;
6+
use rustc_session::Session;
7+
8+
#[cfg(test)]
9+
mod tests;
10+
11+
/// Deployment target or SDK version.
12+
///
13+
/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`.
14+
pub type OsVersion = (u16, u8, u8);
15+
16+
/// Parse an OS version triple (SDK version or deployment target).
17+
fn parse_version(version: &str) -> Result<OsVersion, ParseIntError> {
18+
if let Some((major, minor)) = version.split_once('.') {
19+
let major = major.parse()?;
20+
if let Some((minor, patch)) = minor.split_once('.') {
21+
Ok((major, minor.parse()?, patch.parse()?))
22+
} else {
23+
Ok((major, minor.parse()?, 0))
24+
}
25+
} else {
26+
Ok((version.parse()?, 0, 0))
27+
}
28+
}
29+
30+
/// Get the deployment target based on the standard environment variables, or fall back to the
31+
/// minimum version supported by `rustc`.
32+
pub fn deployment_target(sess: &Session) -> OsVersion {
33+
let target = &sess.target;
34+
35+
// Minimum operating system versions currently supported by `rustc`.
36+
//
37+
// When bumping a version in here, remember to update the platform-support docs too.
38+
//
39+
// NOTE: If you are looking for `rustc`'s default deployment target, prefer
40+
// `rustc --print deployment-target`, as the default here may change in future `rustc` versions.
41+
let os_min = match &*target.os {
42+
"macos" => (10, 12, 0),
43+
"ios" => (10, 0, 0),
44+
"tvos" => (10, 0, 0),
45+
"watchos" => (5, 0, 0),
46+
"visionos" => (1, 0, 0),
47+
_ => bug!("tried to get deployment target for non-Apple platform"),
48+
};
49+
50+
// On certain targets it makes sense to raise the minimum OS version.
51+
//
52+
// This matches what LLVM does, see in part:
53+
// <https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/llvm/lib/TargetParser/Triple.cpp#L1900-L1932>
54+
let min = match (&*target.os, &*target.arch, &*target.abi) {
55+
("macos", "aarch64", _) => (11, 0, 0),
56+
("ios", "aarch64", "macabi" | "sim") => (14, 0, 0),
57+
("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0),
58+
// Mac Catalyst defaults to 13.1 in Clang.
59+
("ios", _, "macabi") => (13, 1, 0),
60+
("tvos", "aarch64", "sim") => (14, 0, 0),
61+
("watchos", "aarch64", "sim") => (7, 0, 0),
62+
_ => os_min,
63+
};
64+
65+
// Name of the environment variable used to fetch the deployment target on the given OS.
66+
let env_var = match &*target.os {
67+
"macos" => "MACOSX_DEPLOYMENT_TARGET",
68+
"ios" => "IPHONEOS_DEPLOYMENT_TARGET",
69+
"watchos" => "WATCHOS_DEPLOYMENT_TARGET",
70+
"tvos" => "TVOS_DEPLOYMENT_TARGET",
71+
"visionos" => "XROS_DEPLOYMENT_TARGET",
72+
_ => bug!("tried to get deployment target env var for non-Apple platform"),
73+
};
74+
75+
if let Ok(deployment_target) = env::var(env_var) {
76+
match parse_version(&deployment_target) {
77+
// It is common that the deployment target is set too low, e.g. on macOS Aarch64 to also
78+
// target older x86_64, the user may set a lower deployment target than supported.
79+
//
80+
// To avoid such issues, we silently raise the deployment target here.
81+
// FIXME: We want to show a warning when `version < os_min`.
82+
Ok(version) => version.max(min),
83+
// FIXME: Report erroneous environment variable to user.
84+
Err(_) => min,
85+
}
86+
} else {
87+
// If no deployment target variable is set, default to the minimum found above.
88+
min
89+
}
90+
}
91+
92+
fn add_version_to_llvm_target(llvm_target: &str, deployment_target: OsVersion) -> String {
93+
let mut components = llvm_target.split("-");
94+
let arch = components.next().expect("darwin target should have arch");
95+
let vendor = components.next().expect("darwin target should have vendor");
96+
let os = components.next().expect("darwin target should have os");
97+
let environment = components.next();
98+
assert_eq!(components.next(), None, "too many LLVM triple components");
99+
100+
let (major, minor, patch) = deployment_target;
101+
102+
assert!(
103+
!os.contains(|c: char| c.is_ascii_digit()),
104+
"LLVM target must not already be versioned"
105+
);
106+
107+
if let Some(env) = environment {
108+
// Insert version into OS, before environment
109+
format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}-{env}")
110+
} else {
111+
format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}")
112+
}
113+
}
114+
115+
/// The target triple depends on the deployment target, and is required to
116+
/// enable features such as cross-language LTO, and for picking the right
117+
/// Mach-O commands.
118+
///
119+
/// Certain optimizations also depend on the deployment target.
120+
pub fn versioned_llvm_target(sess: &Session) -> Cow<'static, str> {
121+
if sess.target.is_like_osx {
122+
add_version_to_llvm_target(&sess.target.llvm_target, deployment_target(sess)).into()
123+
} else {
124+
sess.target.llvm_target.clone()
125+
}
126+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use super::{add_version_to_llvm_target, parse_version};
2+
3+
#[test]
4+
fn test_parse_version() {
5+
assert_eq!(parse_version("10"), Ok((10, 0, 0)));
6+
assert_eq!(parse_version("10.12"), Ok((10, 12, 0)));
7+
assert_eq!(parse_version("10.12.6"), Ok((10, 12, 6)));
8+
assert_eq!(parse_version("9999.99.99"), Ok((9999, 99, 99)));
9+
}
10+
11+
#[test]
12+
fn test_add_version_to_llvm_target() {
13+
assert_eq!(
14+
add_version_to_llvm_target("aarch64-apple-macosx", (10, 14, 1)),
15+
"aarch64-apple-macosx10.14.1"
16+
);
17+
assert_eq!(
18+
add_version_to_llvm_target("aarch64-apple-ios-simulator", (16, 1, 0)),
19+
"aarch64-apple-ios16.1.0-simulator"
20+
);
21+
}

compiler/rustc_codegen_ssa/src/back/link.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use rustc_target::spec::crt_objects::CrtObjects;
4040
use rustc_target::spec::{
4141
Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFeatures,
4242
LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, SanitizerSet,
43-
SplitDebuginfo, current_apple_deployment_target,
43+
SplitDebuginfo,
4444
};
4545
use tempfile::Builder as TempFileBuilder;
4646
use tracing::{debug, info, warn};
@@ -50,6 +50,7 @@ use super::command::Command;
5050
use super::linker::{self, Linker};
5151
use super::metadata::{MetadataPosition, create_wrapper_file};
5252
use super::rpath::{self, RPathConfig};
53+
use crate::apple::{deployment_target, versioned_llvm_target};
5354
use crate::{
5455
CodegenResults, CompiledModule, CrateInfo, NativeLib, common, errors,
5556
looks_like_rust_object_file,
@@ -2445,7 +2446,7 @@ fn add_order_independent_options(
24452446
if flavor == LinkerFlavor::Llbc {
24462447
cmd.link_args(&[
24472448
"--target",
2448-
sess.target.llvm_target.as_ref(),
2449+
&versioned_llvm_target(sess),
24492450
"--target-cpu",
24502451
&codegen_results.crate_info.target_cpu,
24512452
]);
@@ -3037,7 +3038,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
30373038
_ => bug!("invalid OS/ABI combination for Apple target: {target_os}, {target_abi}"),
30383039
};
30393040

3040-
let (major, minor, patch) = current_apple_deployment_target(&sess.target);
3041+
let (major, minor, patch) = deployment_target(sess);
30413042
let min_version = format!("{major}.{minor}.{patch}");
30423043

30433044
// The SDK version is used at runtime when compiling with a newer SDK / version of Xcode:
@@ -3107,7 +3108,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
31073108

31083109
// The presence of `-mmacosx-version-min` makes CC default to
31093110
// macOS, and it sets the deployment target.
3110-
let (major, minor, patch) = current_apple_deployment_target(&sess.target);
3111+
let (major, minor, patch) = deployment_target(sess);
31113112
// Intentionally pass this as a single argument, Clang doesn't
31123113
// seem to like it otherwise.
31133114
cmd.cc_arg(&format!("-mmacosx-version-min={major}.{minor}.{patch}"));
@@ -3117,7 +3118,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
31173118
//
31183119
// We avoid `-m32`/`-m64`, as this is already encoded by `-arch`.
31193120
} else {
3120-
cmd.cc_args(&["-target", &sess.target.llvm_target]);
3121+
cmd.cc_args(&["-target", &versioned_llvm_target(sess)]);
31213122
}
31223123
}
31233124
}
@@ -3343,7 +3344,7 @@ fn add_lld_args(
33433344
// targeting a different linker flavor on macOS, and that's also always
33443345
// the case when targeting WASM.
33453346
if sess.target.linker_flavor != sess.host.linker_flavor {
3346-
cmd.cc_arg(format!("--target={}", sess.target.llvm_target));
3347+
cmd.cc_arg(format!("--target={}", versioned_llvm_target(sess)));
33473348
}
33483349
}
33493350
}

compiler/rustc_codegen_ssa/src/back/metadata.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
238238
file.set_macho_cpu_subtype(object::macho::CPU_SUBTYPE_ARM64E);
239239
}
240240

241-
file.set_macho_build_version(macho_object_build_version_for_target(&sess.target))
241+
file.set_macho_build_version(macho_object_build_version_for_target(sess))
242242
}
243243
if binary_format == BinaryFormat::Coff {
244244
// Disable the default mangler to avoid mangling the special "@feat.00" symbol name.
@@ -391,7 +391,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
391391
///
392392
/// Since Xcode 15, Apple's LD apparently requires object files to use this load command, so this
393393
/// returns the `MachOBuildVersion` for the target to do so.
394-
fn macho_object_build_version_for_target(target: &Target) -> object::write::MachOBuildVersion {
394+
fn macho_object_build_version_for_target(sess: &Session) -> object::write::MachOBuildVersion {
395395
/// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz"
396396
/// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200
397397
fn pack_version((major, minor, patch): (u16, u8, u8)) -> u32 {
@@ -400,8 +400,8 @@ fn macho_object_build_version_for_target(target: &Target) -> object::write::Mach
400400
}
401401

402402
let platform =
403-
rustc_target::spec::current_apple_platform(target).expect("unknown Apple target OS");
404-
let min_os = rustc_target::spec::current_apple_deployment_target(target);
403+
rustc_target::spec::current_apple_platform(&sess.target).expect("unknown Apple target OS");
404+
let min_os = crate::apple::deployment_target(sess);
405405

406406
let mut build_version = object::write::MachOBuildVersion::default();
407407
build_version.platform = platform;

compiler/rustc_codegen_ssa/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use rustc_session::cstore::{self, CrateSource};
4444
use rustc_session::utils::NativeLibKind;
4545
use rustc_span::symbol::Symbol;
4646

47+
pub mod apple;
4748
pub mod assert_module_sources;
4849
pub mod back;
4950
pub mod base;

compiler/rustc_driver_impl/src/lib.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -856,10 +856,8 @@ fn print_crate_info(
856856
}
857857
}
858858
DeploymentTarget => {
859-
use rustc_target::spec::current_apple_deployment_target;
860-
861859
if sess.target.is_like_osx {
862-
let (major, minor, patch) = current_apple_deployment_target(&sess.target);
860+
let (major, minor, patch) = rustc_codegen_ssa::apple::deployment_target(sess);
863861
let patch = if patch != 0 { format!(".{patch}") } else { String::new() };
864862
println_info!("deployment_target={major}.{minor}{patch}")
865863
} else {

0 commit comments

Comments
 (0)