Skip to content

Commit e123315

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 20c909f commit e123315

File tree

12 files changed

+208
-144
lines changed

12 files changed

+208
-144
lines changed

compiler/rustc_codegen_cranelift/src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use std::sync::Arc;
4040
use cranelift_codegen::isa::TargetIsa;
4141
use cranelift_codegen::settings::{self, Configurable};
4242
use rustc_codegen_ssa::CodegenResults;
43+
use rustc_codegen_ssa::back::versioned_llvm_target;
4344
use rustc_codegen_ssa::traits::CodegenBackend;
4445
use rustc_data_structures::profiling::SelfProfilerRef;
4546
use rustc_errors::ErrorGuaranteed;
@@ -260,7 +261,9 @@ impl CodegenBackend for CraneliftCodegenBackend {
260261
}
261262

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

compiler/rustc_codegen_llvm/src/back/write.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use llvm::{
99
LLVMRustLLVMHasZlibCompressionForDebugSymbols, LLVMRustLLVMHasZstdCompressionForDebugSymbols,
1010
};
1111
use rustc_codegen_ssa::back::link::ensure_removed;
12+
use rustc_codegen_ssa::back::versioned_llvm_target;
1213
use rustc_codegen_ssa::back::write::{
1314
BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig,
1415
TargetMachineFactoryFn,
@@ -211,7 +212,7 @@ pub(crate) fn target_machine_factory(
211212
singlethread = false;
212213
}
213214

214-
let triple = SmallCStr::new(&sess.target.llvm_target);
215+
let triple = SmallCStr::new(&versioned_llvm_target(sess));
215216
let cpu = SmallCStr::new(llvm_util::target_cpu(sess));
216217
let features = CString::new(target_features.join(",")).unwrap();
217218
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::back::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::*;
@@ -177,7 +178,7 @@ pub(crate) unsafe fn create_module<'ll>(
177178
llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr());
178179
}
179180

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

3042-
let (major, minor, patch) = current_apple_deployment_target(&sess.target);
3043+
let (major, minor, patch) = apple::deployment_target(sess);
30433044
let min_version = format!("{major}.{minor}.{patch}");
30443045

30453046
// 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
31093110

31103111
// The presence of `-mmacosx-version-min` makes CC default to
31113112
// macOS, and it sets the deployment target.
3112-
let (major, minor, patch) = current_apple_deployment_target(&sess.target);
3113+
let (major, minor, patch) = apple::deployment_target(sess);
31133114
// Intentionally pass this as a single argument, Clang doesn't
31143115
// seem to like it otherwise.
31153116
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
31193120
//
31203121
// We avoid `-m32`/`-m64`, as this is already encoded by `-arch`.
31213122
} else {
3122-
cmd.cc_args(&["-target", &sess.target.llvm_target]);
3123+
cmd.cc_args(&["-target", &versioned_llvm_target(sess)]);
31233124
}
31243125
}
31253126
}
@@ -3345,7 +3346,7 @@ fn add_lld_args(
33453346
// targeting a different linker flavor on macOS, and that's also always
33463347
// the case when targeting WASM.
33473348
if sess.target.linker_flavor != sess.host.linker_flavor {
3348-
cmd.cc_arg(format!("--target={}", sess.target.llvm_target));
3349+
cmd.cc_arg(format!("--target={}", versioned_llvm_target(sess)));
33493350
}
33503351
}
33513352
}

compiler/rustc_codegen_ssa/src/back/metadata.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use rustc_span::sym;
2222
use rustc_target::abi::Endian;
2323
use rustc_target::spec::{RelocModel, Target, ef_avr_arch};
2424

25+
use super::apple;
26+
2527
/// The default metadata loader. This is used by cg_llvm and cg_clif.
2628
///
2729
/// # Metadata location
@@ -238,7 +240,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
238240
file.set_macho_cpu_subtype(object::macho::CPU_SUBTYPE_ARM64E);
239241
}
240242

241-
file.set_macho_build_version(macho_object_build_version_for_target(&sess.target))
243+
file.set_macho_build_version(macho_object_build_version_for_target(sess))
242244
}
243245
if binary_format == BinaryFormat::Coff {
244246
// 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
392394
///
393395
/// Since Xcode 15, Apple's LD apparently requires object files to use this load command, so this
394396
/// returns the `MachOBuildVersion` for the target to do so.
395-
fn macho_object_build_version_for_target(target: &Target) -> object::write::MachOBuildVersion {
397+
fn macho_object_build_version_for_target(sess: &Session) -> object::write::MachOBuildVersion {
396398
/// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz"
397399
/// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200
398400
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
401403
}
402404

403405
let platform =
404-
rustc_target::spec::current_apple_platform(target).expect("unknown Apple target OS");
405-
let min_os = rustc_target::spec::current_apple_deployment_target(target);
406+
rustc_target::spec::current_apple_platform(&sess.target).expect("unknown Apple target OS");
407+
let min_os = apple::deployment_target(sess);
406408

407409
let mut build_version = object::write::MachOBuildVersion::default();
408410
build_version.platform = platform;

compiler/rustc_codegen_ssa/src/back/mod.rs

+21
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
use std::borrow::Cow;
2+
3+
use rustc_session::Session;
4+
5+
pub mod apple;
16
pub mod archive;
27
pub(crate) mod command;
38
pub mod link;
@@ -7,3 +12,19 @@ pub mod metadata;
712
pub(crate) mod rpath;
813
pub mod symbol_export;
914
pub mod write;
15+
16+
/// The target triple depends on the deployment target, and is required to
17+
/// enable features such as cross-language LTO, and for picking the right
18+
/// Mach-O commands.
19+
///
20+
/// Certain optimizations also depend on the deployment target.
21+
pub fn versioned_llvm_target(sess: &Session) -> Cow<'_, str> {
22+
if sess.target.is_like_osx {
23+
apple::add_version_to_llvm_target(&sess.target.llvm_target, apple::deployment_target(sess))
24+
.into()
25+
} else {
26+
// FIXME(madsmtm): Certain other targets also include a version,
27+
// we might want to move that here as well.
28+
Cow::Borrowed(&sess.target.llvm_target)
29+
}
30+
}

compiler/rustc_driver_impl/src/lib.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use std::time::{Instant, SystemTime};
3333
use std::{env, str};
3434

3535
use rustc_ast as ast;
36+
use rustc_codegen_ssa::back::apple;
3637
use rustc_codegen_ssa::traits::CodegenBackend;
3738
use rustc_codegen_ssa::{CodegenErrors, CodegenResults};
3839
use rustc_data_structures::profiling::{
@@ -855,10 +856,8 @@ fn print_crate_info(
855856
}
856857
}
857858
DeploymentTarget => {
858-
use rustc_target::spec::current_apple_deployment_target;
859-
860859
if sess.target.is_like_osx {
861-
let (major, minor, patch) = current_apple_deployment_target(&sess.target);
860+
let (major, minor, patch) = apple::deployment_target(sess);
862861
let patch = if patch != 0 { format!(".{patch}") } else { String::new() };
863862
println_info!("deployment_target={major}.{minor}{patch}")
864863
} else {

0 commit comments

Comments
 (0)