Skip to content

Commit

Permalink
Merge branch 'move-llvm-target-versioning' into sdkroot
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Oct 9, 2024
2 parents f91a3b8 + 7750660 commit c70b075
Show file tree
Hide file tree
Showing 13 changed files with 196 additions and 98 deletions.
5 changes: 4 additions & 1 deletion compiler/rustc_codegen_cranelift/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use std::sync::Arc;
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::settings::{self, Configurable};
use rustc_codegen_ssa::CodegenResults;
use rustc_codegen_ssa::apple::versioned_llvm_target;
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_errors::ErrorGuaranteed;
Expand Down Expand Up @@ -259,7 +260,9 @@ impl CodegenBackend for CraneliftCodegenBackend {
}

fn target_triple(sess: &Session) -> target_lexicon::Triple {
match sess.target.llvm_target.parse() {
// FIXME(madsmtm): Use `sess.target.llvm_target` once target-lexicon supports unversioned macOS.
// See <https://github.com/bytecodealliance/target-lexicon/pull/113>
match versioned_llvm_target(sess).parse() {
Ok(triple) => triple,
Err(err) => sess.dcx().fatal(format!("target not recognized: {}", err)),
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_codegen_llvm/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use libc::{c_char, c_int, c_void, size_t};
use llvm::{
LLVMRustLLVMHasZlibCompressionForDebugSymbols, LLVMRustLLVMHasZstdCompressionForDebugSymbols,
};
use rustc_codegen_ssa::apple::versioned_llvm_target;
use rustc_codegen_ssa::back::link::ensure_removed;
use rustc_codegen_ssa::back::write::{
BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig,
Expand Down Expand Up @@ -210,7 +211,7 @@ pub(crate) fn target_machine_factory(
singlethread = false;
}

let triple = SmallCStr::new(&sess.target.llvm_target);
let triple = SmallCStr::new(&versioned_llvm_target(sess));
let cpu = SmallCStr::new(llvm_util::target_cpu(sess));
let features = CString::new(target_features.join(",")).unwrap();
let abi = SmallCStr::new(&sess.target.llvm_abiname);
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::cell::{Cell, RefCell};
use std::ffi::{CStr, c_uint};
use std::str;

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

let llvm_target = SmallCStr::new(&sess.target.llvm_target);
let llvm_target = SmallCStr::new(&versioned_llvm_target(sess));
unsafe {
llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr());
}
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_codegen_ssa/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ codegen_ssa_L4Bender_exporting_symbols_unimplemented = exporting symbols not imp
codegen_ssa_add_native_library = failed to add native library {$library_path}: {$error}
codegen_ssa_apple_deployment_target_invalid =
failed to parse deployment target specified in {$env_var}: {$error}
codegen_ssa_apple_deployment_target_too_low =
deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min}
codegen_ssa_apple_sdk_error_failed_reading =
failed reading `{$path}` while looking for SDK root: {$error}
Expand Down
94 changes: 93 additions & 1 deletion compiler/rustc_codegen_ssa/src/apple.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,104 @@
use std::borrow::Cow;
use std::env;
use std::fs;
use std::io::ErrorKind;
use std::fmt::{Display, from_fn};
use std::path::{Path, PathBuf};

use crate::errors::AppleSdkError;
use rustc_session::Session;
use rustc_target::spec::{
AppleOSVersion, apple_deployment_target_env_var, apple_minimum_deployment_target,
apple_os_minimum_deployment_target, apple_parse_version,
};

use crate::errors::{AppleDeploymentTarget, AppleSdkError};

#[cfg(test)]
mod tests;

pub fn pretty_version(version: AppleOSVersion) -> impl Display {
let (major, minor, patch) = version;
from_fn(move |f| {
write!(f, "{major}.{minor}")?;
if patch != 0 {
write!(f, ".{patch}")?;
}
Ok(())
})
}

/// 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) -> AppleOSVersion {
let os_min = apple_os_minimum_deployment_target(&sess.target.os);
let min = apple_minimum_deployment_target(&sess.target);
let env_var = apple_deployment_target_env_var(&sess.target.os);

if let Ok(deployment_target) = env::var(env_var) {
match apple_parse_version(&deployment_target) {
Ok(version) => {
// It is common that the deployment target is set a bit too low, for example on
// macOS Aarch64 to also target older x86_64. So we only want to warn when variable
// is lower than the minimum OS supported by rustc, not when the variable is lower
// than the minimum for a specific target.
if version < os_min {
sess.dcx().emit_warn(AppleDeploymentTarget::TooLow {
env_var,
version: pretty_version(version).to_string(),
os_min: pretty_version(os_min).to_string(),
});
}

// Raise the deployment target to the minimum supported.
version.max(min)
}
Err(error) => {
sess.dcx().emit_err(AppleDeploymentTarget::Invalid { env_var, error });
min
}
}
} else {
// If no deployment target variable is set, default to the minimum found above.
min
}
}

fn add_version_to_llvm_target(llvm_target: &str, deployment_target: AppleOSVersion) -> String {
let mut components = llvm_target.split("-");
let arch = components.next().expect("darwin target should have arch");
let vendor = components.next().expect("darwin target should have vendor");
let os = components.next().expect("darwin 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}")
}
}

/// 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<'static, str> {
if sess.target.is_like_osx {
add_version_to_llvm_target(&sess.target.llvm_target, deployment_target(sess)).into()
} else {
sess.target.llvm_target.clone()
}
}

// TOCTOU is not _really_ an issue with our use of `try_exists` in here, we mostly use it for
// diagnostics, and these directories are global state that the user may change anytime anyhow.
fn try_exists(path: &Path) -> Result<bool, AppleSdkError> {
Expand Down
14 changes: 13 additions & 1 deletion compiler/rustc_codegen_ssa/src/apple/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,19 @@ use std::io;
use std::path::PathBuf;
use std::process::Command;

use super::find_sdk_root;
use super::{add_version_to_llvm_target, find_sdk_root};

#[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"
);
}

fn find_sdk_root_xcrun(sdk_name: &str) -> io::Result<PathBuf> {
let output = Command::new("xcrun")
Expand Down
14 changes: 7 additions & 7 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -50,7 +50,7 @@ use super::command::Command;
use super::linker::{self, Linker};
use super::metadata::{MetadataPosition, create_wrapper_file};
use super::rpath::{self, RPathConfig};
use crate::apple::find_sdk_root;
use crate::apple::{deployment_target, find_sdk_root, versioned_llvm_target};
use crate::{
CodegenResults, CompiledModule, CrateInfo, NativeLib, common, errors,
looks_like_rust_object_file,
Expand Down Expand Up @@ -2446,7 +2446,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,
]);
Expand Down Expand Up @@ -3038,7 +3038,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) = 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:
Expand Down Expand Up @@ -3108,7 +3108,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) = 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}"));
Expand All @@ -3118,7 +3118,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)]);
}
}
}
Expand Down Expand Up @@ -3305,7 +3305,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)));
}
}
}
8 changes: 4 additions & 4 deletions compiler/rustc_codegen_ssa/src/back/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,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.
Expand Down Expand Up @@ -391,7 +391,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 {
Expand All @@ -400,8 +400,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 = crate::apple::deployment_target(sess);

let mut build_version = object::write::MachOBuildVersion::default();
build_version.platform = platform;
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_codegen_ssa/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::borrow::Cow;
use std::io::Error;
use std::num::ParseIntError;
use std::path::{Path, PathBuf};
use std::process::ExitStatus;

Expand Down Expand Up @@ -532,6 +533,14 @@ pub enum ExtractBundledLibsError<'a> {
ExtractSection { rlib: &'a Path, error: Box<dyn std::error::Error> },
}

#[derive(Diagnostic)]
pub(crate) enum AppleDeploymentTarget {
#[diag(codegen_ssa_apple_deployment_target_invalid)]
Invalid { env_var: &'static str, error: ParseIntError },
#[diag(codegen_ssa_apple_deployment_target_too_low)]
TooLow { env_var: &'static str, version: String, os_min: String },
}

#[derive(Diagnostic, Debug)]
pub(crate) enum AppleSdkError {
#[diag(codegen_ssa_apple_sdk_error_failed_reading)]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(debug_closure_helpers)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(let_chains)]
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -855,12 +855,10 @@ fn print_crate_info(
}
}
DeploymentTarget => {
use rustc_target::spec::current_apple_deployment_target;
use rustc_codegen_ssa::apple::{deployment_target, pretty_version};

if sess.target.is_like_osx {
let (major, minor, patch) = current_apple_deployment_target(&sess.target);
let patch = if patch != 0 { format!(".{patch}") } else { String::new() };
println_info!("deployment_target={major}.{minor}{patch}")
println_info!("deployment_target={}", pretty_version(deployment_target(sess)))
} else {
#[allow(rustc::diagnostic_outside_of_impl)]
sess.dcx().fatal("only Apple targets currently support deployment version info")
Expand Down
Loading

0 comments on commit c70b075

Please sign in to comment.