Skip to content

Commit b822a19

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 b822a19

File tree

13 files changed

+205
-145
lines changed

13 files changed

+205
-145
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::base::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
@@ -13,6 +13,7 @@ use rustc_codegen_ssa::back::write::{
1313
BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig,
1414
TargetMachineFactoryFn,
1515
};
16+
use rustc_codegen_ssa::base::versioned_llvm_target;
1617
use rustc_codegen_ssa::traits::*;
1718
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
1819
use rustc_data_structures::profiling::SelfProfilerRef;
@@ -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-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::cell::{Cell, RefCell};
33
use std::ffi::{CStr, c_uint};
44
use std::str;
55

6-
use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh};
6+
use rustc_codegen_ssa::base::{versioned_llvm_target, wants_msvc_seh, wants_wasm_eh};
77
use rustc_codegen_ssa::errors as ssa_errors;
88
use rustc_codegen_ssa::traits::*;
99
use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN};
@@ -177,7 +177,7 @@ pub(crate) unsafe fn create_module<'ll>(
177177
llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr());
178178
}
179179

180-
let llvm_target = SmallCStr::new(&sess.target.llvm_target);
180+
let llvm_target = SmallCStr::new(&versioned_llvm_target(sess));
181181
unsafe {
182182
llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr());
183183
}
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+
pub 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(crate) 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

+8-6
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,18 @@ 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};
4747

48+
use super::apple;
4849
use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
4950
use super::command::Command;
5051
use super::linker::{self, Linker};
5152
use super::metadata::{MetadataPosition, create_wrapper_file};
5253
use super::rpath::{self, RPathConfig};
54+
use crate::base::versioned_llvm_target;
5355
use crate::{
5456
CodegenResults, CompiledModule, CrateInfo, NativeLib, common, errors,
5557
looks_like_rust_object_file,
@@ -2447,7 +2449,7 @@ fn add_order_independent_options(
24472449
if flavor == LinkerFlavor::Llbc {
24482450
cmd.link_args(&[
24492451
"--target",
2450-
sess.target.llvm_target.as_ref(),
2452+
&versioned_llvm_target(sess),
24512453
"--target-cpu",
24522454
&codegen_results.crate_info.target_cpu,
24532455
]);
@@ -3039,7 +3041,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
30393041
_ => bug!("invalid OS/ABI combination for Apple target: {target_os}, {target_abi}"),
30403042
};
30413043

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

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

31103112
// The presence of `-mmacosx-version-min` makes CC default to
31113113
// macOS, and it sets the deployment target.
3112-
let (major, minor, patch) = current_apple_deployment_target(&sess.target);
3114+
let (major, minor, patch) = apple::deployment_target(sess);
31133115
// Intentionally pass this as a single argument, Clang doesn't
31143116
// seem to like it otherwise.
31153117
cmd.cc_arg(&format!("-mmacosx-version-min={major}.{minor}.{patch}"));
@@ -3119,7 +3121,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
31193121
//
31203122
// We avoid `-m32`/`-m64`, as this is already encoded by `-arch`.
31213123
} else {
3122-
cmd.cc_args(&["-target", &sess.target.llvm_target]);
3124+
cmd.cc_args(&["-target", &versioned_llvm_target(sess)]);
31233125
}
31243126
}
31253127
}
@@ -3345,7 +3347,7 @@ fn add_lld_args(
33453347
// targeting a different linker flavor on macOS, and that's also always
33463348
// the case when targeting WASM.
33473349
if sess.target.linker_flavor != sess.host.linker_flavor {
3348-
cmd.cc_arg(format!("--target={}", sess.target.llvm_target));
3350+
cmd.cc_arg(format!("--target={}", versioned_llvm_target(sess)));
33493351
}
33503352
}
33513353
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod apple;
12
pub mod archive;
23
pub(crate) mod command;
34
pub mod link;

compiler/rustc_codegen_ssa/src/base.rs

+16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::borrow::Cow;
12
use std::cmp;
23
use std::collections::BTreeSet;
34
use std::time::{Duration, Instant};
@@ -33,6 +34,7 @@ use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
3334
use tracing::{debug, info};
3435

3536
use crate::assert_module_sources::CguReuse;
37+
use crate::back::apple;
3638
use crate::back::link::are_upstream_rust_objects_already_included;
3739
use crate::back::metadata::create_compressed_metadata_file;
3840
use crate::back::write::{
@@ -400,6 +402,20 @@ pub fn wants_msvc_seh(sess: &Session) -> bool {
400402
sess.target.is_like_msvc
401403
}
402404

405+
/// The target triple depends on the deployment target, and is required to
406+
/// enable features such as cross-language LTO, and for picking the right
407+
/// Mach-O commands.
408+
///
409+
/// Certain optimizations also depend on the deployment target.
410+
pub fn versioned_llvm_target(sess: &Session) -> Cow<'_, str> {
411+
if sess.target.is_like_osx {
412+
apple::add_version_to_llvm_target(&sess.target.llvm_target, apple::deployment_target(sess))
413+
.into()
414+
} else {
415+
Cow::Borrowed(&sess.target.llvm_target)
416+
}
417+
}
418+
403419
/// Returns `true` if this session's target requires the new exception
404420
/// handling LLVM IR instructions (catchpad / cleanuppad / ... instead
405421
/// of landingpad)

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)