From f1a579a860a3329cf06c828015780a663a345320 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Aug 2026 21:47:00 +1000 Subject: [PATCH 1/7] [161903] Slightly extend `tests/run-make/target-specs/rmake.rs` The `require-explicit-cpu.json` case currently prints a "default target CPU" line; test for this. (It will change in the next commit.) --- tests/run-make/target-specs/rmake.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/run-make/target-specs/rmake.rs b/tests/run-make/target-specs/rmake.rs index 6c88f3164e9e4..39a153262d11c 100644 --- a/tests/run-make/target-specs/rmake.rs +++ b/tests/run-make/target-specs/rmake.rs @@ -95,5 +95,10 @@ fn main() { .crate_type("lib") .arg("-Ctarget-cpu=generic") .run(); - rustc().arg("-Zunstable-options").target("require-explicit-cpu").print("target-cpus").run(); + rustc() + .arg("-Zunstable-options") + .target("require-explicit-cpu") + .print("target-cpus") + .run() + .assert_stdout_contains("default target CPU"); } From a35d96dcc7895f894a242b5d4eb7c374ae7bd0f0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Aug 2026 21:30:09 +1000 Subject: [PATCH 2/7] [161903] Adjust when "This is the default target CPU..." message is printed Specifically, don't print it when `need_explicit_cpu` is set, because it doesn't really make sense in that context. Right now among builtin targets this only affects the `amdgcn-amd-amdhsa` target, but it will also be relevant for the `avr2` target in the next commit. It also affects the `require-explicit-cpu.json` case in `tests/run-make/target-specs/rmake.rs`. --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 5 +++-- tests/run-make/target-specs/rmake.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 82ddcca3e1530..156aa87cff0b0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -519,10 +519,11 @@ fn print_target_cpus(sess: &Session, tm: &llvm::TargetMachine, out: &mut String) cpu_name: &'a str, remark: String, } - // Compare CPU against current target to label the default. + // Compare CPU against current target to label the default. Do not print it if + // `need_explicit_cpu` is set, because in that case the concept of default makes less sense. let target_cpu = handle_native(&sess.target.cpu); let make_remark = |cpu_name| { - if cpu_name == target_cpu { + if cpu_name == target_cpu && !sess.target.need_explicit_cpu { // FIXME(#132514): This prints the LLVM target string, which can be // different from the Rust target string. Is that intended? let target = &sess.target.llvm_target; diff --git a/tests/run-make/target-specs/rmake.rs b/tests/run-make/target-specs/rmake.rs index 39a153262d11c..4deb7c9bfcc93 100644 --- a/tests/run-make/target-specs/rmake.rs +++ b/tests/run-make/target-specs/rmake.rs @@ -100,5 +100,5 @@ fn main() { .target("require-explicit-cpu") .print("target-cpus") .run() - .assert_stdout_contains("default target CPU"); + .assert_stdout_not_contains("default target CPU"); } From 1b3ee677e43157c926444432944be7a41623b2b8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Aug 2026 16:07:47 +1000 Subject: [PATCH 3/7] [161903] Explicitly set the `cpu` field for `avr-none` Currently rustc uses LLVM's `TargetMachine::getMCSubtargetInfo` method to access an `MCSubtargetInfo` to do feature testing. The next commit will change the feature testing to instead use an alternative pathway, LLVM's `Target::createMCSubtargetInfo` method. The two pathways have some slight differences. One difference relates to the `avr-none` target. Currently its `cpu` field isn't set so it gets the default "generic" value, which is not a valid AVR CPU name. This was hidden by the fact that the current LLVM pathway goes through the `getCPU` function in `AVRTargetMachine.cpp`, which rewrites "generic" as "avr2". But the alternative LLVM pathway doesn't rewrite "generic". Without an adjustment, we would get some behavioural differences with the alternative pathway, such as "unrecognized processor" errors and empty base feature sets. Therefore, this commit sets `cpu` to "avr2", a more obviously correct choice, and what the current LLVM pathway is effectively doing behind the scenes. You might think this would change the code generated by default, but `avr-none` has `need_explicit_cpu` set to true, so that's not the case, because a missing `-Ctarget-cpu` will trigger a fatal error before codegen. But `cpu` can still reach non-codegen paths (e.g. feature/cfg computation in session setup, and `--print`) so we need a valid backend name. A consequence of this is that `--print target-spec-json` will emit `cpu: "avr2"`. Another consequence is that the `requires_consistent_cpu` check will compare a crate built without `-Ctarget-cpu` (non-codegen only) against "avr2" instead of "generic". The commit also modifies two tests. In both cases, the test passes in this commit with or without the explicit `cpu` field. But in the next commit (using the alternative pathway) both tests would fail without the explicit `cpu` field: - `tests/ui/abi/avr-sram.rs` would fail with ``` 'generic' is not a recognized processor for this target (ignoring processor) 'generic' is not a recognized processor for this target (ignoring processor) warning: target feature `sram` must be enabled to ensure that the ABI of the current target can be implemented correctly ``` - `tests/run-make/print-cfg/rmake.rs` would fail because all features would be missing. Finally, the field docs for `TargetOptions` are tweaked to clarify the interplay between `cpu` and `need_explicit_cpu`. --- compiler/rustc_target/src/spec/mod.rs | 9 +++++---- .../rustc_target/src/spec/targets/avr_none.rs | 1 + tests/run-make/print-cfg/rmake.rs | 15 ++++++++++++++- tests/ui/abi/avr-sram.rs | 17 +++++++++++++++-- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index a1c8fd304cd94..f0efba6c8aaac 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2260,11 +2260,12 @@ pub struct TargetOptions { /// Extra arguments to pass to the external assembler (when used) pub asm_args: StaticCow<[StaticCow]>, - /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults - /// to "generic". + /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Must be a name the backend + /// accepts. Defaults to "generic" (which some backends won't accept). pub cpu: StaticCow, - /// Whether a cpu needs to be explicitly set. - /// Set to true if there is no default cpu. Defaults to false. + /// Whether a cpu needs to be explicitly set via `-Ctarget-cpu` for codegen to run. (Even if + /// true, `cpu` is still consulted on non-codegen paths such as cfg/feature computation.) + /// Defaults to false. pub need_explicit_cpu: bool, /// Whether `-Ctarget-cpu` is treated as a target modifier. If this is set /// all crates that are linked together must have been compiled with the diff --git a/compiler/rustc_target/src/spec/targets/avr_none.rs b/compiler/rustc_target/src/spec/targets/avr_none.rs index 0dcd2428fc703..d4b0bf64206c7 100644 --- a/compiler/rustc_target/src/spec/targets/avr_none.rs +++ b/compiler/rustc_target/src/spec/targets/avr_none.rs @@ -14,6 +14,7 @@ pub(crate) fn target() -> Target { pointer_width: 16, options: TargetOptions { c_int_width: 16, + cpu: "avr2".into(), exe_suffix: ".elf".into(), linker: Some("avr-gcc".into()), eh_frame_header: false, diff --git a/tests/run-make/print-cfg/rmake.rs b/tests/run-make/print-cfg/rmake.rs index d5de89c0de151..62b28ef84909d 100644 --- a/tests/run-make/print-cfg/rmake.rs +++ b/tests/run-make/print-cfg/rmake.rs @@ -14,7 +14,7 @@ use std::collections::HashSet; use std::iter::FromIterator; use std::path::PathBuf; -use run_make_support::{rfs, rustc}; +use run_make_support::{llvm_components_contain, rfs, rustc}; struct PrintCfg { target: &'static str, @@ -73,6 +73,19 @@ fn main() { includes: &["target_has_threads"], disallow: &[], }); + // AVR is experimental, so don't assume it's supported. + if llvm_components_contain("avr") { + check(PrintCfg { + target: "avr-none", + args: &[], + includes: &[ + "target_feature=\"addsubiw\"", + "target_feature=\"ijmpcall\"", + "target_feature=\"lpm\"", + ], + disallow: &[], + }); + } } fn check(PrintCfg { target, args, includes, disallow }: PrintCfg) { diff --git a/tests/ui/abi/avr-sram.rs b/tests/ui/abi/avr-sram.rs index 0266f7d6b22ca..7b8ec5ee8fa0c 100644 --- a/tests/ui/abi/avr-sram.rs +++ b/tests/ui/abi/avr-sram.rs @@ -1,12 +1,25 @@ -//@ revisions: has_sram no_sram disable_sram -//@ build-pass +//@ revisions: has_sram no_sram disable_sram default_cpu +// +//@[has_sram] build-pass //@[has_sram] compile-flags: --target avr-none -C target-cpu=atmega328p //@[has_sram] needs-llvm-components: avr +// +//@[no_sram] build-pass //@[no_sram] compile-flags: --target avr-none -C target-cpu=attiny11 //@[no_sram] needs-llvm-components: avr +// +//@[disable_sram] build-pass //@[disable_sram] compile-flags: --target avr-none -C target-cpu=atmega328p -C target-feature=-sram //@[disable_sram] needs-llvm-components: avr +// +// Note: this revision relies on `need_explicit_cpu` only being enforced at codegen, which is why +// it uses `check-pass` instead of `build-pass`. +//@[default_cpu] check-pass +//@[default_cpu] compile-flags: --target avr-none +//@[default_cpu] needs-llvm-components: avr +// //@ ignore-backends: gcc +// //[no_sram,disable_sram]~? WARN target feature `sram` must be enabled //[disable_sram]~? WARN target feature `sram` cannot be disabled with `-Ctarget-feature` From 96079cd6b60bd3eb0d8a8bfe978514f5bb1ed1e8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Aug 2026 09:15:34 +1000 Subject: [PATCH 4/7] [161903] Fix initialization cycle in `target_config` `llvm::target_config` creates `target_machine` by calling `create_informational_target_machine`, which calls `target_machine_factory`, which uses `internal_target_features`. But this is just before `internal_target_features` is initialized! So we should move `internal_target_features` initialization before `target_machine`, right? But `internal_target_features` initialization involves a closure that inspects `target_machine`. There is a cyclic dependency. There is enough function nesting here that it's hard to spot. In practice this cycle doesn't cause problems because the closure doesn't inspect the parts of `target_machine` that depend on `internal_target_features`. But it demonstrates how startup initialization is all tangled up, and it's blocking some cleanups I am doing in #161432 relating to the dangerous uses of `Session` before it's fully initialized. Therefore, this commit changes the first part: instead of creating and `OwnedTargetMachine` we create an `OwnedMCSubtargetInfo`. This is a smaller type that has the feature information we need but doesn't depend on `internal_target_features`. Under the covers we are now using LLVM's `Target::createMCSubtargetInfo` instead of `TargetMachine::getMCSubtargetInfo` so that we avoid having to create a `TargetMachine` at this early stage. This eliminates the cycle. (`TargetMachine` can still be created later on, once we're past this fraught initialization.) There are some slight differences between these two approaches, and the preceding commits fixed up some issues there. Some details about this commit: - The new `OwnedMCSubtargetInfo` is similar to the existing `OwnedTargetMachine`. - `create_informational_target_machine` no longer needs a `for_cfg` parameter, because the one site where `for_cfg` was true has been removed. - `LLVMRustCreateMCSubtargetInfo` mostly replicates part of `LLVMRustCreateTargetMachine` - `LLVMRustMCSubtargetInfoHasFeature` partly replicates `LLVMRustHasFeature`. - `LLVMRustHasFeature` is no longer needed. - The error message for `custom-target-invalid-llvm-target.rs` changed. --- compiler/rustc_codegen_llvm/src/back/mod.rs | 1 + .../src/back/owned_mc_subtarget_info.rs | 51 +++++++++++++++++++ .../src/back/owned_target_machine.rs | 2 +- compiler/rustc_codegen_llvm/src/back/write.rs | 12 ++--- compiler/rustc_codegen_llvm/src/context.rs | 2 +- .../rustc_codegen_llvm/src/diagnostics.rs | 5 ++ compiler/rustc_codegen_llvm/src/lib.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 15 +++++- compiler/rustc_codegen_llvm/src/llvm_util.rs | 21 +++++--- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 32 ++++++++---- .../custom-target-invalid-llvm-target.rs | 2 +- .../custom-target-invalid-llvm-target.stderr | 2 +- 12 files changed, 117 insertions(+), 30 deletions(-) create mode 100644 compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs diff --git a/compiler/rustc_codegen_llvm/src/back/mod.rs b/compiler/rustc_codegen_llvm/src/back/mod.rs index 6cb89f80ab89a..de6007c17bfff 100644 --- a/compiler/rustc_codegen_llvm/src/back/mod.rs +++ b/compiler/rustc_codegen_llvm/src/back/mod.rs @@ -1,5 +1,6 @@ pub(crate) mod archive; pub(crate) mod lto; +pub(crate) mod owned_mc_subtarget_info; pub(crate) mod owned_target_machine; mod profiling; pub(crate) mod write; diff --git a/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs b/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs new file mode 100644 index 0000000000000..6bc346b5acc17 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs @@ -0,0 +1,51 @@ +use std::ffi::CStr; +use std::marker::PhantomData; +use std::ptr::NonNull; + +use rustc_data_structures::small_c_str::SmallCStr; + +use crate::diagnostics::LlvmError; +use crate::llvm; + +/// Responsible for safely creating and disposing llvm::MCSubtargetInfo via ffi functions. +/// Not cloneable as there is no clone function for llvm::MCSubtargetInfo. +pub(crate) struct OwnedMCSubtargetInfo { + info_unique: NonNull, + phantom: PhantomData, +} + +impl OwnedMCSubtargetInfo { + pub(crate) fn new( + triple: &CStr, + cpu: &CStr, + features: &CStr, + ) -> Result> { + // SAFETY: llvm::LLVMRustCreateMCSubtargetInfo copies pointed-to data. + let info_ptr = unsafe { + llvm::LLVMRustCreateMCSubtargetInfo(triple.as_ptr(), cpu.as_ptr(), features.as_ptr()) + }; + + NonNull::new(info_ptr) + .map(|info_unique| Self { info_unique, phantom: PhantomData }) + .ok_or_else(|| LlvmError::CreateMCSubtargetInfo { triple: SmallCStr::from(triple) }) + } + + pub(crate) fn has_feature(&self, feature: &CStr) -> bool { + // SAFETY: `new` ensures we have a valid pointer created by + // `llvm::LLVMRustCreateMCSubtargetInfo`. + unsafe { + llvm::LLVMRustMCSubtargetInfoHasFeature(self.info_unique.as_ref(), feature.as_ptr()) + } + } +} + +impl Drop for OwnedMCSubtargetInfo { + fn drop(&mut self) { + // SAFETY: `new` ensures we have a valid pointer created by + // `llvm::LLVMRustCreateMCSubtargetInfo` and `OwnedMCSubtargetInfo` is not copyable so + // there is no double free or use after free. + unsafe { + llvm::LLVMRustDisposeMCSubtargetInfo(self.info_unique); + } + } +} diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 350d4ce9ee331..20fcbf8b48466 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -41,7 +41,7 @@ impl OwnedTargetMachine { use_wasm_eh: bool, large_data_threshold: u64, ) -> Result> { - // SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data + // SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed-to data. let tm_ptr = unsafe { llvm::LLVMRustCreateTargetMachine( triple.as_ptr(), diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index b8952ffc6bf81..62c1729720009 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -100,17 +100,12 @@ fn write_output_file<'ll>( result.into_result().unwrap_or_else(|()| llvm_err(dcx, LlvmError::WriteOutput { path: output })) } -/// If `for_cfg` is `true` then we are creating this machine for the purpose of populating -/// [`rustc_codegen_ssa::TargetConfig`] based on what LLVM actually enables in this configuration. -/// `-Ctarget-feature` should be ignored in that case since it is already processed separately. -pub(crate) fn create_informational_target_machine( - sess: &Session, - for_cfg: bool, -) -> OwnedTargetMachine { +pub(crate) fn create_informational_target_machine(sess: &Session) -> OwnedTargetMachine { let config = TargetMachineFactoryConfig { split_dwarf_file: None, output_obj_file: None }; // Can't use query system here quite yet because this function is invoked before the query // system/tcx is set up. - let features = llvm_util::global_llvm_features(sess, for_cfg); + let features = llvm_util::global_llvm_features(sess, false); + target_machine_factory(sess, config::OptLevel::No, &features)(sess.dcx(), config) } @@ -212,7 +207,6 @@ pub(crate) fn target_machine_factory( let code_model = to_llvm_code_model(sess.code_model()); - // This is used to set cfg_has_threads, so all logic must be in this method. let singlethread = sess.target.singlethread(&sess.internal_target_features); let triple = SmallCStr::new(&versioned_llvm_target(sess)); diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 853c4bfc9ca3f..f913bda10052e 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -228,7 +228,7 @@ pub(crate) unsafe fn create_module<'ll>( // Ensure the data-layout values hardcoded remain the defaults. { - let tm = crate::back::write::create_informational_target_machine(sess, false); + let tm = crate::back::write::create_informational_target_machine(sess); unsafe { llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm.raw()); } diff --git a/compiler/rustc_codegen_llvm/src/diagnostics.rs b/compiler/rustc_codegen_llvm/src/diagnostics.rs index fb43b36fe39b9..d20c4fc48a7df 100644 --- a/compiler/rustc_codegen_llvm/src/diagnostics.rs +++ b/compiler/rustc_codegen_llvm/src/diagnostics.rs @@ -119,6 +119,8 @@ pub(crate) enum LlvmError<'a> { WriteOutput { path: &'a Path }, #[diag("could not create LLVM TargetMachine for triple: {$triple}")] CreateTargetMachine { triple: SmallCStr }, + #[diag("could not create LLVM MCSubtargetInfo for triple: {$triple}")] + CreateMCSubtargetInfo { triple: SmallCStr }, #[diag("failed to run LLVM passes")] RunLlvmPasses, #[diag("failed to write LLVM IR to {$path}")] @@ -145,6 +147,9 @@ impl Diagnostic<'_, G> for WithLlvmError<'_> { CreateTargetMachine { .. } => { msg!("could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}") } + CreateMCSubtargetInfo { .. } => { + msg!("could not create LLVM MCSubtargetInfo for triple: {$triple}: {$llvm_err}") + } RunLlvmPasses => msg!("failed to run LLVM passes: {$llvm_err}"), WriteIr { .. } => msg!("failed to write LLVM IR to {$path}: {$llvm_err}"), PrepareThinLtoContext => { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index a8a8edf98bd86..629a33950144d 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -495,7 +495,7 @@ impl ModuleLlvm { ModuleLlvm { llmod_raw, llcx, - tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess, false)), + tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)), } } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 684bba7a717db..f72a4c54c1425 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -720,6 +720,7 @@ unsafe extern "C" { pub type TargetMachine; } unsafe extern "C" { + pub(crate) type MCSubtargetInfo; pub(crate) type Twine; pub(crate) type DiagnosticInfo; pub(crate) type SMDiagnostic; @@ -2364,7 +2365,6 @@ unsafe extern "C" { pub(crate) fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString); pub(crate) fn LLVMRustWriteValueToString(value_ref: &Value, s: &RustString); - pub(crate) fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool; pub(crate) fn LLVMRustTargetHasMnemonic(T: &TargetMachine, s: *const c_char) -> bool; pub(crate) fn LLVMRustPrintTargetCPUs(TM: &TargetMachine, OutStr: &RustString); @@ -2406,6 +2406,19 @@ unsafe extern "C" { LargeDataThreshold: u64, ) -> *mut TargetMachine; + pub(crate) fn LLVMRustCreateMCSubtargetInfo( + TripleStr: *const c_char, + CPU: *const c_char, + Features: *const c_char, + ) -> *mut MCSubtargetInfo; + + pub(crate) fn LLVMRustMCSubtargetInfoHasFeature( + MCInfo: &MCSubtargetInfo, + Feature: *const c_char, + ) -> bool; + + pub(crate) fn LLVMRustDisposeMCSubtargetInfo(MCInfo: ptr::NonNull); + pub(crate) fn LLVMRustAddLibraryInfo<'a>( T: &TargetMachine, PM: &PassManager<'a>, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 156aa87cff0b0..4acabcff22d18 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -6,6 +6,7 @@ use std::sync::Once; use std::{ptr, slice, str}; use libc::c_int; +use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::base::wants_wasm_eh; use rustc_codegen_ssa::target_features::internal_target_features; use rustc_codegen_ssa::{TargetConfig, target_features}; @@ -20,7 +21,8 @@ use rustc_target::spec::{ }; use smallvec::{SmallVec, smallvec}; -use crate::back::write::create_informational_target_machine; +use crate::back::owned_mc_subtarget_info::OwnedMCSubtargetInfo; +use crate::back::write::{create_informational_target_machine, llvm_err}; use crate::{diagnostics, llvm}; static INIT: Once = Once::new(); @@ -340,7 +342,14 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option TargetConfig { - let target_machine = create_informational_target_machine(sess, true); + require_inited(); + let target_features = global_llvm_features(sess, true); + + let triple = SmallCStr::new(&versioned_llvm_target(sess)); + let cpu = SmallCStr::new(target_cpu(sess)); + let features = CString::new(target_features.join(",")).unwrap(); + let mc_subtarget_info = OwnedMCSubtargetInfo::new(&triple, &cpu, &features) + .unwrap_or_else(|err| llvm_err(sess.dcx(), err)); let internal_target_features = internal_target_features( sess, @@ -357,10 +366,10 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig { // All the LLVM features this expands to must be enabled. for llvm_feature in feat { let cstr = SmallCStr::new(llvm_feature); - // `LLVMRustHasFeature` is moderately expensive. On targets with many + // `has_feature` is moderately expensive. On targets with many // features (e.g. x86) these calls take a non-trivial fraction of runtime // when compiling very small programs. - if !unsafe { llvm::LLVMRustHasFeature(target_machine.raw(), cstr.as_ptr()) } { + if !mc_subtarget_info.has_feature(&cstr) { return false; } } @@ -501,7 +510,7 @@ fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> { pub(crate) fn print(req: &PrintRequest, out: &mut String, sess: &Session) { require_inited(); - let tm = create_informational_target_machine(sess, false); + let tm = create_informational_target_machine(sess); match req.kind { PrintKind::TargetCPUs => print_target_cpus(sess, tm.raw(), out), PrintKind::TargetFeatures => print_target_features(sess, tm.raw(), out), @@ -799,7 +808,7 @@ pub(crate) fn tune_cpu(sess: &Session) -> Option<&str> { pub(crate) fn target_has_mnemonic(sess: &Session, mnemonic: &str) -> bool { require_inited(); - let tm = create_informational_target_machine(sess, false); + let tm = create_informational_target_machine(sess); let cstr = SmallCStr::new(mnemonic); unsafe { llvm::LLVMRustTargetHasMnemonic(tm.raw(), cstr.as_ptr()) } } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index e77d63d91703b..076f40b83a205 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -67,8 +67,10 @@ using namespace llvm; static codegen::RegisterCodeGenFlags CGF; typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef; +typedef struct LLVMOpaqueMCSubtargetInfo *LLVMMCSubtargetInfoRef; DEFINE_STDCXX_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef) +DEFINE_STDCXX_CONVERSION_FUNCTIONS(MCSubtargetInfo, LLVMMCSubtargetInfoRef) extern "C" void LLVMRustTimeTraceProfilerInitialize() { timeTraceProfilerInitialize( @@ -89,15 +91,27 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char *FileName) { timeTraceProfilerCleanup(); } -extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM, - const char *Feature) { - TargetMachine *Target = unwrap(TM); -#if LLVM_VERSION_GE(23, 0) - const MCSubtargetInfo &MCInfo = Target->getMCSubtargetInfo(); -#else - const MCSubtargetInfo &MCInfo = *Target->getMCSubtargetInfo(); -#endif - return MCInfo.checkFeatures(std::string("+") + Feature); +extern "C" LLVMMCSubtargetInfoRef +LLVMRustCreateMCSubtargetInfo(const char *TripleStr, const char *CPU, + const char *Features) { + std::string Error; + auto Trip = Triple(Triple::normalize(TripleStr)); + const llvm::Target *TheTarget = TargetRegistry::lookupTarget(Trip, Error); + if (TheTarget == nullptr) { + LLVMRustSetLastError(Error.c_str()); + return nullptr; + } + + return wrap(TheTarget->createMCSubtargetInfo(Trip, CPU, Features)); +} + +extern "C" bool LLVMRustMCSubtargetInfoHasFeature(LLVMMCSubtargetInfoRef MCInfo, + const char *Feature) { + return unwrap(MCInfo)->checkFeatures(std::string("+") + Feature); +} + +extern "C" void LLVMRustDisposeMCSubtargetInfo(LLVMMCSubtargetInfoRef MCInfo) { + delete unwrap(MCInfo); } /// Check whether the target has a specific assembly mnemonic like `ret` or diff --git a/tests/ui/codegen/custom-target-invalid-llvm-target.rs b/tests/ui/codegen/custom-target-invalid-llvm-target.rs index 72c80cd7af4f1..d90b56c5d13c0 100644 --- a/tests/ui/codegen/custom-target-invalid-llvm-target.rs +++ b/tests/ui/codegen/custom-target-invalid-llvm-target.rs @@ -7,4 +7,4 @@ fn main() {} -//~? ERROR failed to parse target machine config to target machine +//~? ERROR could not create LLVM MCSubtargetInfo for triple: not-a-real-target diff --git a/tests/ui/codegen/custom-target-invalid-llvm-target.stderr b/tests/ui/codegen/custom-target-invalid-llvm-target.stderr index d5ac437a2b646..e2e844fa1bce7 100644 --- a/tests/ui/codegen/custom-target-invalid-llvm-target.stderr +++ b/tests/ui/codegen/custom-target-invalid-llvm-target.stderr @@ -1,2 +1,2 @@ -error: failed to parse target machine config to target machine: could not create LLVM TargetMachine for triple: not-a-real-target +error: could not create LLVM MCSubtargetInfo for triple: not-a-real-target: No available targets are compatible with triple "not-a-real-target" From ef564ebdac3203f8c4ed776cd6e6af8349820e35 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 28 Aug 2026 13:45:54 +1000 Subject: [PATCH 5/7] Split `build_session` Session creation is currently awkward: we build a mostly-initialized session, then use it to initialize a codegen backend, and then use the codegen backend to finish initializing the session. And it's not just awkward: within the Cranelift backend's `init` method `sess.lto()` is called, which consults `sess.thin_lto_supported`, *before* that field has been properly set! In practice it had no effect but it's worth fixing up. This commit cleans up this mess. It introduces `EarlySession`, which contains just four `Session` fields, the ones that are needed for codegen backend initialization. It is now a field within `Session`, and `Session` derefs to `EarlySession` to avoid changing a zillion `sess.target`/`sess.opts`/etc. occurrences. `EarlySession` is passed to `init`, which returns a `CodegenBackendInit` that contains the backend-specific information needed to build a `Session`. (It replaces the `replaced_intrinsics`, `fallback_intrinsics`, and `thin_lto_supported` methods.) The `Session` can then be built in a single step. No more `Session`/`CodegenBackend` initialization intermingling. A few functions that previously took a `Session` now take something else, e.g. a `Target`. Some `Session` methods are now `EarlySession` methods. And a new `early_lto` method is used for Cranelift's LTO check. --- compiler/rustc_codegen_cranelift/src/lib.rs | 29 +- compiler/rustc_codegen_gcc/src/attributes.rs | 2 +- compiler/rustc_codegen_gcc/src/base.rs | 2 +- compiler/rustc_codegen_gcc/src/context.rs | 4 +- compiler/rustc_codegen_gcc/src/gcc_util.rs | 18 +- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 2 +- compiler/rustc_codegen_gcc/src/lib.rs | 24 +- compiler/rustc_codegen_llvm/src/attributes.rs | 2 +- compiler/rustc_codegen_llvm/src/back/write.rs | 2 +- compiler/rustc_codegen_llvm/src/context.rs | 4 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 +- compiler/rustc_codegen_llvm/src/lib.rs | 97 +++---- compiler/rustc_codegen_llvm/src/llvm_util.rs | 46 +-- compiler/rustc_codegen_ssa/src/back/mod.rs | 4 +- compiler/rustc_codegen_ssa/src/base.rs | 15 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 14 +- compiler/rustc_codegen_ssa/src/mir/mod.rs | 2 +- .../rustc_codegen_ssa/src/target_features.rs | 36 +-- .../rustc_codegen_ssa/src/traits/backend.rs | 25 +- compiler/rustc_interface/src/interface.rs | 48 ++-- compiler/rustc_interface/src/tests.rs | 11 +- compiler/rustc_interface/src/util.rs | 4 +- compiler/rustc_session/src/session.rs | 263 +++++++++++------- src/tools/clippy/clippy_utils/src/source.rs | 2 +- src/tools/miri/src/bin/miri.rs | 6 +- 26 files changed, 357 insertions(+), 311 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 71fce9e28f120..742f08835b7bf 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -43,7 +43,7 @@ use rustc_data_structures::unord::UnordSet; use rustc_log::tracing::info; use rustc_middle::dep_graph::WorkProductMap; use rustc_session::config::{NATIVE_CPU, OutputFilenames}; -use rustc_session::{IncrCompSession, Session}; +use rustc_session::{CodegenBackendInit, EarlySession, IncrCompSession, Session}; use rustc_span::{Symbol, sym}; use rustc_target::spec::{Arch, CfgAbi, Env, Os}; @@ -126,13 +126,14 @@ impl CodegenBackend for CraneliftCodegenBackend { "cranelift" } - fn init(&self, sess: &Session) { - use rustc_session::config::{InstrumentCoverage, Lto}; - match sess.lto() { - Lto::No | Lto::ThinLocal => {} - Lto::Thin | Lto::Fat => { - sess.dcx().fatal("LTO is not supported by rustc_codegen_cranelift"); + fn init(&self, sess: &EarlySession) -> CodegenBackendInit { + use rustc_session::config::{InstrumentCoverage, LtoCli}; + + match (sess.target.requires_lto, sess.early_lto()) { + (true, _) | (false, LtoCli::Yes | LtoCli::Fat | LtoCli::NoParam | LtoCli::Thin) => { + sess.dcx().fatal("LTO is not supported by rustc_codegen_cranelift") } + (false, LtoCli::Unspecified | LtoCli::No) => {} } if sess.opts.cg.instrument_coverage() != InstrumentCoverage::No { @@ -148,13 +149,15 @@ impl CodegenBackend for CraneliftCodegenBackend { if config.jit_mode && !sess.opts.output_types.should_codegen() { sess.dcx().fatal("JIT mode doesn't work with `cargo check`"); } - } - fn thin_lto_supported(&self) -> bool { - false + CodegenBackendInit { + replaced_intrinsics: vec![], + fallback_intrinsics: vec![sym::type_id_eq], + thin_lto_supported: false, + } } - fn target_config(&self, sess: &Session) -> TargetConfig { + fn target_config(&self, sess: &EarlySession) -> TargetConfig { // FIXME return the actually used target features. this is necessary for #[cfg(target_feature)] let target_features = match sess.target.arch { Arch::X86_64 if sess.target.os != Os::None => { @@ -240,10 +243,6 @@ impl CodegenBackend for CraneliftCodegenBackend { .unwrap() .join(sess, incr_comp_session, crate_info) } - - fn fallback_intrinsics(&self) -> Vec { - vec![sym::type_id_eq] - } } /// Determine if the Cranelift ir verifier should run. diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index ce1877b308e94..321e150a1568d 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -124,7 +124,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>( .target_features .iter() .map(|features| features.name.as_str()) - .flat_map(|feat| to_gcc_features(cx.tcx.sess, feat).into_iter()) + .flat_map(|feat| to_gcc_features(&cx.tcx.sess.target, feat).into_iter()) .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match *x { InstructionSetAttr::ArmA32 => "-thumb-mode", // FIXME(antoyo): support removing feature. InstructionSetAttr::ArmT32 => "thumb-mode", diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 7a25fc46fd3fc..74031d0f1628b 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -162,7 +162,7 @@ pub fn compile_codegen_unit( add_pic_option(&context, tcx.sess.relocation_model()); - let target_cpu = gcc_util::target_cpu(tcx.sess); + let target_cpu = gcc_util::target_cpu(&tcx.sess); if target_cpu != "generic" { context.add_command_line_option(format!("-march={}", target_cpu)); } diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 19fbe37c27b9e..86f1ae1cb5060 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -451,7 +451,7 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } let tcx = self.tcx; let func = match tcx.lang_items().eh_personality() { - Some(def_id) if !wants_msvc_seh(self.sess()) => { + Some(def_id) if !wants_msvc_seh(&self.sess().target) => { let instance = ty::Instance::expect_resolve( tcx, self.typing_env(), @@ -466,7 +466,7 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.declare_fn(symbol_name, fn_abi) } _ => { - let name = if wants_msvc_seh(self.sess()) { + let name = if wants_msvc_seh(&self.sess().target) { "__CxxFrameHandler3" } else { "rust_eh_personality" diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index a95b4da28eb63..24f552fed32c6 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -2,18 +2,18 @@ use gccjit::Context; use rustc_codegen_ssa::target_features; use rustc_data_structures::smallvec::{SmallVec, smallvec}; -use rustc_session::Session; +use rustc_session::EarlySession; use rustc_session::config::NATIVE_CPU; -use rustc_target::spec::Arch; +use rustc_target::spec::{Arch, Target}; -fn gcc_features_by_flags(sess: &Session, features: &mut Vec) { +fn gcc_features_by_flags(sess: &EarlySession, features: &mut Vec) { target_features::retpoline_features_by_flags(sess, features); // FIXME: LLVM also sets +reserve-x18 here under some conditions. } /// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, /// `--target` and similar). -pub(crate) fn global_gcc_features(sess: &Session) -> Vec { +pub(crate) fn global_gcc_features(sess: &EarlySession) -> Vec { // Features that come earlier are overridden by conflicting features later in the string. // Typically we'll want more explicit settings to override the implicit ones, so: // @@ -40,9 +40,9 @@ pub(crate) fn global_gcc_features(sess: &Session) -> Vec { // features also work on the command line instead of having two // different names when the GCC name and the Rust name differ. features.extend( - to_gcc_features(sess, feature) + to_gcc_features(&sess.target, feature) .iter() - .flat_map(|feat| to_gcc_features(sess, feat).into_iter()) + .flat_map(|feat| to_gcc_features(&sess.target, feat).into_iter()) .map(|feature| if !enable { format!("-{}", feature) } else { feature.to_string() }), ); }; @@ -59,9 +59,9 @@ pub(crate) fn global_gcc_features(sess: &Session) -> Vec { } // To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html -pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> { +pub fn to_gcc_features<'a>(target: &Target, s: &'a str) -> SmallVec<[&'a str; 2]> { // cSpell:disable - match (&sess.target.arch, s) { + match (&target.arch, s) { // FIXME: seems like x87 does not exist? (&Arch::X86 | &Arch::X86_64, "x87") => smallvec![], (&Arch::X86 | &Arch::X86_64, "sse4.2") => smallvec!["sse4.2", "crc32"], @@ -130,7 +130,7 @@ fn handle_native(name: &str) -> &str { unimplemented!(); } -pub fn target_cpu(sess: &Session) -> &str { +pub fn target_cpu(sess: &EarlySession) -> &str { match sess.opts.cg.target_cpu { Some(ref name) => handle_native(name), None => handle_native(sess.target.cpu.as_ref()), diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index b3b1bea68e0b4..0121df077f26a 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -1351,7 +1351,7 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( // we can never unwind. OperandValue::Immediate(bx.const_bool(false)).store(bx, dest); } else { - if wants_msvc_seh(bx.sess()) { + if wants_msvc_seh(&bx.sess().target) { unimplemented!(); } #[cfg(feature = "master")] diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index cbc7db8e9e23f..8d8dc58b18b7c 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -95,7 +95,7 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::config::{OptLevel, OutputFilenames}; -use rustc_session::{IncrCompSession, Session}; +use rustc_session::{CodegenBackendInit, EarlySession, IncrCompSession, Session}; use rustc_span::{Symbol, sym}; use rustc_target::spec::{Arch, RelocModel}; use tempfile::TempDir; @@ -195,8 +195,8 @@ impl CodegenBackend for GccCodegenBackend { "gcc" } - fn init(&self, sess: &Session) { - fn file_path(sysroot_path: &Path, sess: &Session) -> PathBuf { + fn init(&self, sess: &EarlySession) -> CodegenBackendInit { + fn file_path(sysroot_path: &Path, sess: &EarlySession) -> PathBuf { let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot_path, &sess.host.llvm_target); sysroot_path @@ -274,10 +274,12 @@ impl CodegenBackend for GccCodegenBackend { .supports_128bit_integers .store(check_context.get_last_error() == Ok(None), Ordering::SeqCst); } - } - fn thin_lto_supported(&self) -> bool { - false + CodegenBackendInit { + replaced_intrinsics: vec![], + fallback_intrinsics: vec![sym::type_id_eq], + thin_lto_supported: false, + } } fn provide(&self, providers: &mut Providers) { @@ -307,13 +309,9 @@ impl CodegenBackend for GccCodegenBackend { .join(sess, incr_comp_session, crate_info) } - fn target_config(&self, sess: &Session) -> TargetConfig { + fn target_config(&self, sess: &EarlySession) -> TargetConfig { target_config(sess, &self.target_info) } - - fn fallback_intrinsics(&self) -> Vec { - vec![sym::type_id_eq] - } } fn new_context<'gcc, 'tcx>(tcx: TyCtxt<'tcx>) -> Context<'gcc> { @@ -531,10 +529,10 @@ fn to_gcc_opt_level(optlevel: Option) -> OptimizationLevel { } /// Returns the features that should be set in `cfg(target_feature)`. -fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig { +fn target_config(sess: &EarlySession, target_info: &LockedTargetInfo) -> TargetConfig { let internal_target_features = internal_target_features( sess, - |feature| to_gcc_features(sess, feature), + |feature| to_gcc_features(&sess.target, feature), |feature| { // FIXME: we disable Neon for now since we don't support the LLVM intrinsics for it. if feature == "neon" { diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 9415c2ecb9d10..ccb434e2d706f 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -649,7 +649,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( let function_features = function_features .iter() // Convert to LLVMFeatures and filter out unavailable ones - .flat_map(|feat| llvm_util::to_llvm_features(sess, feat)) + .flat_map(|feat| llvm_util::to_llvm_features(&sess.target, feat)) // Convert LLVMFeatures & dependencies to +s .flat_map(|feat| feat.into_iter().map(|f| format!("+{f}"))) .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 62c1729720009..45247e12c1da9 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -249,7 +249,7 @@ pub(crate) fn target_machine_factory( } }; - let use_wasm_eh = wants_wasm_eh(sess); + let use_wasm_eh = wants_wasm_eh(&sess.target); let large_data_threshold = sess.opts.unstable_opts.large_data_threshold.unwrap_or(0); diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index f913bda10052e..fba1a1b746a29 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -984,9 +984,9 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { return llpersonality; } - let name = if wants_msvc_seh(self.sess()) { + let name = if wants_msvc_seh(&self.sess().target) { Some("__CxxFrameHandler3") - } else if wants_wasm_eh(self.sess()) { + } else if wants_wasm_eh(&self.sess().target) { // LLVM specifically tests for the name of the personality function // There is no need for this function to exist anywhere, it will // not be called. However, its name has to be "__gxx_wasm_personality_v0" diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 36709e4ff954f..147ae6cf012b4 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1366,9 +1366,9 @@ fn catch_unwind_intrinsic<'ll, 'tcx>( // Return 0 unconditionally from the intrinsic call; // we can never unwind. bx.const_bool(false) - } else if wants_msvc_seh(bx.sess()) { + } else if wants_msvc_seh(&bx.sess().target) { codegen_msvc_try(bx, try_func, data, catch_func) - } else if wants_wasm_eh(bx.sess()) { + } else if wants_wasm_eh(&bx.sess().target) { codegen_wasm_try(bx, try_func, data, catch_func) } else { codegen_gnu_try(bx, try_func, data, catch_func) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 629a33950144d..810f281217809 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -39,7 +39,7 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; -use rustc_session::{IncrCompSession, Session}; +use rustc_session::{CodegenBackendInit, EarlySession, IncrCompSession, Session}; use rustc_span::{Symbol, sym}; use rustc_target::spec::{RelocModel, TlsModel}; @@ -219,7 +219,7 @@ impl CodegenBackend for LlvmCodegenBackend { "llvm" } - fn init(&self, sess: &Session) { + fn init(&self, sess: &EarlySession) -> CodegenBackendInit { llvm_util::init(sess); // Make sure llvm is inited // autodiff is based on Enzyme, a library which we might not have available, when it was @@ -243,6 +243,52 @@ impl CodegenBackend for LlvmCodegenBackend { enable_autodiff_settings(&sess.opts.unstable_opts.autodiff); } } + + // Intrinsics whose fallback body will not be used by the LLVM backend. + let replaced_intrinsics = { + #[rustfmt::skip] + let mut will_not_use_fallback = vec![ + // These are mapped to LLVM intrinsics instead. + sym::unchecked_funnel_shl, + sym::unchecked_funnel_shr, + sym::carrying_mul_add, + sym::integer_max, + sym::integer_min, + + // Fallback via libm, but the LLVM intrinsic is used instead. + sym::sinf16, sym::sinf32, sym::sinf64, + sym::cosf16, sym::cosf32, sym::cosf64, + sym::powf16, sym::powf32, sym::powf64, + sym::expf16, sym::expf32, sym::expf64, + sym::exp2f16, sym::exp2f32, sym::exp2f64, + sym::logf16, sym::logf32, sym::logf64, + sym::log10f16, sym::log10f32, sym::log10f64, + sym::log2f16, sym::log2f32, sym::log2f64, + + // Fallback via f32 or f64, but the LLVM intrinsic is used instead. + sym::floorf16, sym::ceilf16, sym::truncf16, + sym::round_ties_even_f16, sym::roundf16, + sym::sqrtf16, sym::powif16, + sym::fmaf16, + + sym::copysignf16, sym::copysignf32, sym::copysignf64, sym::copysignf128, + ]; + + if llvm_util::get_version() >= (22, 0, 0) { + will_not_use_fallback.push(sym::carryless_mul); + } + + will_not_use_fallback + }; + + // `type_id_eq` is a safe choice since *all* backends use the fallback body for that. When + // adding more intrinsics, keep in mind that the distributed standard library is compiled + // with the LLVM backend but might later be included in a project built with cranelift or + // GCC. Adding an intrinsic here can therefore mean the fallback body is used with + // cranelift/GCC even if they have dedicated implementations. + let fallback_intrinsics = vec![sym::type_id_eq]; + + CodegenBackendInit { replaced_intrinsics, fallback_intrinsics, thin_lto_supported: true } } fn provide(&self, providers: &mut Providers) { @@ -321,55 +367,10 @@ impl CodegenBackend for LlvmCodegenBackend { llvm_util::target_has_mnemonic(sess, mnemonic) } - fn target_config(&self, sess: &Session) -> TargetConfig { + fn target_config(&self, sess: &EarlySession) -> TargetConfig { target_config(sess) } - /// Intrinsics whose fallback body will not be used by the LLVM backend. - fn replaced_intrinsics(&self) -> Vec { - #[rustfmt::skip] - let mut will_not_use_fallback = vec![ - // These are mapped to LLVM intrinsics instead. - sym::unchecked_funnel_shl, - sym::unchecked_funnel_shr, - sym::carrying_mul_add, - sym::integer_max, - sym::integer_min, - - // Fallback via libm, but the LLVM intrinsic is used instead. - sym::sinf16, sym::sinf32, sym::sinf64, - sym::cosf16, sym::cosf32, sym::cosf64, - sym::powf16, sym::powf32, sym::powf64, - sym::expf16, sym::expf32, sym::expf64, - sym::exp2f16, sym::exp2f32, sym::exp2f64, - sym::logf16, sym::logf32, sym::logf64, - sym::log10f16, sym::log10f32, sym::log10f64, - sym::log2f16, sym::log2f32, sym::log2f64, - - // Fallback via f32 or f64, but the LLVM intrinsic is used instead. - sym::floorf16, sym::ceilf16, sym::truncf16, - sym::round_ties_even_f16, sym::roundf16, - sym::sqrtf16, sym::powif16, - sym::fmaf16, - - sym::copysignf16, sym::copysignf32, sym::copysignf64, sym::copysignf128, - ]; - - if llvm_util::get_version() >= (22, 0, 0) { - will_not_use_fallback.push(sym::carryless_mul); - } - - will_not_use_fallback - } - - fn fallback_intrinsics(&self) -> Vec { - // `type_id_eq` is a safe choice since *all* backends use the fallback body for that. - // When adding more intrinsics, keep in mind that the distributed standard library - // is compiled with the LLVM backend but might later be included in a project built - // with cranelift or GCC. - vec![sym::type_id_eq] - } - fn target_cpu(&self, sess: &Session) -> String { crate::llvm_util::target_cpu(sess).to_string() } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 4acabcff22d18..544fe609e24a3 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -14,10 +14,10 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::small_c_str::SmallCStr; use rustc_fs_util::path_to_c_string; use rustc_middle::bug; -use rustc_session::Session; use rustc_session::config::{NATIVE_CPU, PrintKind, PrintRequest}; +use rustc_session::{EarlySession, Session}; use rustc_target::spec::{ - Arch, CfgAbi, Env, MergeFunctions, Os, PanicStrategy, SmallDataThresholdSupport, + Arch, CfgAbi, Env, MergeFunctions, Os, PanicStrategy, SmallDataThresholdSupport, Target, }; use smallvec::{SmallVec, smallvec}; @@ -27,7 +27,7 @@ use crate::{diagnostics, llvm}; static INIT: Once = Once::new(); -pub(crate) fn init(sess: &Session) { +pub(crate) fn init(sess: &EarlySession) { unsafe { // Before we touch LLVM, make sure that multithreading is enabled. if !llvm::LLVMIsMultithreaded().is_true() { @@ -45,7 +45,7 @@ fn require_inited() { } } -unsafe fn configure_llvm(sess: &Session) { +unsafe fn configure_llvm(sess: &EarlySession) { let n_args = sess.opts.cg.llvm_args.len() + sess.target.llvm_args.len(); let mut llvm_c_strs = Vec::with_capacity(n_args + 1); let mut llvm_args = Vec::with_capacity(n_args + 1); @@ -129,7 +129,7 @@ unsafe fn configure_llvm(sess: &Session) { } } - if wants_wasm_eh(sess) { + if wants_wasm_eh(&sess.target) { add("-wasm-enable-eh", false); } @@ -238,9 +238,9 @@ impl<'a> IntoIterator for LLVMFeature<'a> { /// `llvm-project` submodule in Though note that /// Rust can also be build with an external precompiled version of LLVM which might lead to failures /// if the oldest tested / supported LLVM version doesn't yet support the relevant intrinsics. -pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option> { +pub(crate) fn to_llvm_features<'a>(target: &Target, s: &'a str) -> Option> { let (major, _, _) = get_version(); - match sess.target.arch { + match target.arch { Arch::AArch64 | Arch::Arm64EC => { match s { "rcpc2" => Some(LLVMFeature::new("rcpc-immo")), @@ -341,7 +341,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option TargetConfig { +pub(crate) fn target_config(sess: &EarlySession) -> TargetConfig { require_inited(); let target_features = global_llvm_features(sess, true); @@ -354,7 +354,7 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig { let internal_target_features = internal_target_features( sess, |feature| { - to_llvm_features(sess, feature) + to_llvm_features(&sess.target, feature) .map(|f| SmallVec::<[&str; 2]>::from_iter(f.into_iter())) .unwrap_or_default() }, @@ -362,7 +362,7 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig { // This closure determines whether the target CPU has the feature according to LLVM. We // do *not* consider the `-Ctarget-feature`s here, as that will be handled later in // `internal_target_features`. - if let Some(feat) = to_llvm_features(sess, feature) { + if let Some(feat) = to_llvm_features(&sess.target, feature) { // All the LLVM features this expands to must be enabled. for llvm_feature in feat { let cstr = SmallCStr::new(llvm_feature); @@ -388,17 +388,17 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig { has_reliable_f128_math: true, }; - update_target_reliable_float_cfg(sess, &mut cfg); + update_target_reliable_float_cfg(&sess.target, &mut cfg); cfg } /// Determine whether or not experimental float types are reliable based on known bugs. -fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { - let target_arch = &sess.target.arch; - let target_os = &sess.target.options.os; - let target_env = &sess.target.options.env; - let target_abi = &sess.target.options.cfg_abi; - let target_pointer_width = sess.target.pointer_width; +fn update_target_reliable_float_cfg(target: &Target, cfg: &mut TargetConfig) { + let target_arch = &target.arch; + let target_os = &target.options.os; + let target_env = &target.options.env; + let target_abi = &target.options.cfg_abi; + let target_pointer_width = target.pointer_width; let version = get_version(); let (major, _, _) = version; @@ -586,7 +586,7 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine, out: &mut Str } // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these // strings. - let llvm_feature = to_llvm_features(sess, *feature)?.llvm_feature_name; + let llvm_feature = to_llvm_features(&sess.target, *feature)?.llvm_feature_name; let desc = match llvm_target_features.binary_search_by_key(&llvm_feature, |(f, _d)| f).ok() { Some(index) => { @@ -658,14 +658,14 @@ fn handle_native(cpu_name: &str) -> &str { } } -pub(crate) fn target_cpu(sess: &Session) -> &str { +pub(crate) fn target_cpu(sess: &EarlySession) -> &str { let cpu_name = sess.opts.cg.target_cpu.as_deref().unwrap_or_else(|| &sess.target.cpu); handle_native(cpu_name) } /// The target features for compiler flags other than `-Ctarget-features`. -fn llvm_features_by_flags(sess: &Session, features: &mut Vec) { - if wants_wasm_eh(sess) && sess.panic_strategy() == PanicStrategy::Unwind { +fn llvm_features_by_flags(sess: &EarlySession, features: &mut Vec) { + if wants_wasm_eh(&sess.target) && sess.panic_strategy() == PanicStrategy::Unwind { features.push("+exception-handling".into()); } @@ -689,7 +689,7 @@ fn llvm_features_by_flags(sess: &Session, features: &mut Vec) { /// If `for_cfg` is `true` then we are assembling the feature list for the purpose of populating /// [`rustc_codegen_ssa::TargetConfig`] based on what LLVM actually enables in this configuration. /// `-Ctarget-feature` should be ignored in that case since it is already processed separately. -pub(crate) fn global_llvm_features(sess: &Session, for_cfg: bool) -> Vec { +pub(crate) fn global_llvm_features(sess: &EarlySession, for_cfg: bool) -> Vec { // Features that come earlier are overridden by conflicting features later in the string. // Typically we'll want more explicit settings to override the implicit ones, so: // @@ -748,7 +748,7 @@ pub(crate) fn global_llvm_features(sess: &Session, for_cfg: bool) -> Vec // passing requests down to LLVM. This means that all in-language // features also work on the command line instead of having two // different names when the LLVM name and the Rust name differ. - let Some(llvm_feature) = to_llvm_features(sess, feature) else { return }; + let Some(llvm_feature) = to_llvm_features(&sess.target, feature) else { return }; features.extend( std::iter::once(format!("{}{}", enable_disable, llvm_feature.llvm_feature_name)).chain( diff --git a/compiler/rustc_codegen_ssa/src/back/mod.rs b/compiler/rustc_codegen_ssa/src/back/mod.rs index 68db2d0cbf0bb..5d7c318b0d307 100644 --- a/compiler/rustc_codegen_ssa/src/back/mod.rs +++ b/compiler/rustc_codegen_ssa/src/back/mod.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use rustc_session::Session; +use rustc_session::EarlySession; pub mod apple; pub mod archive; @@ -22,7 +22,7 @@ pub use symbol_export::{exported_non_generic_symbols_helper, reachable_non_gener /// Mach-O commands. /// /// Certain optimizations also depend on the deployment target. -pub fn versioned_llvm_target(sess: &Session) -> Cow<'_, str> { +pub fn versioned_llvm_target(sess: &EarlySession) -> Cow<'_, str> { if sess.target.is_like_darwin { apple::add_version_to_llvm_target(&sess.target.llvm_target, sess.apple_deployment_target()) .into() diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 9eb4fd510fd7f..65612e9fce074 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -29,12 +29,11 @@ use rustc_middle::query::Providers; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, PatternKind, Ty, TyCtxt, UintTy, Unnormalized}; use rustc_middle::{bug, span_bug}; -use rustc_session::Session; use rustc_session::config::{self, EntryFnType}; use rustc_span::{DUMMY_SP, Symbol}; use rustc_structures::CrateType; use rustc_symbol_mangling::mangle_internal_symbol; -use rustc_target::spec::{Arch, Os}; +use rustc_target::spec::{Arch, Os, Target as TargetSpec}; use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::{debug, info}; @@ -373,8 +372,8 @@ pub(crate) fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // Returns `true` if this session's target will use native wasm // exceptions. This means that the VM does the unwinding for // us -pub fn wants_wasm_eh(sess: &Session) -> bool { - sess.target.is_like_wasm +pub fn wants_wasm_eh(target: &TargetSpec) -> bool { + target.is_like_wasm } /// Returns `true` if this session's target will use SEH-based unwinding. @@ -382,15 +381,15 @@ pub fn wants_wasm_eh(sess: &Session) -> bool { /// This is only true for MSVC targets, and even then the 64-bit MSVC target /// currently uses SEH-ish unwinding with DWARF info tables to the side (same as /// 64-bit MinGW) instead of "full SEH". -pub fn wants_msvc_seh(sess: &Session) -> bool { - sess.target.is_like_msvc +pub fn wants_msvc_seh(target: &TargetSpec) -> bool { + target.is_like_msvc } /// Returns `true` if this session's target requires the new exception /// handling LLVM IR instructions (catchpad / cleanuppad / ... instead /// of landingpad) -pub(crate) fn wants_new_eh_instructions(sess: &Session) -> bool { - wants_wasm_eh(sess) || wants_msvc_seh(sess) +pub(crate) fn wants_new_eh_instructions(target: &TargetSpec) -> bool { + wants_wasm_eh(target) || wants_msvc_seh(target) } pub(crate) fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index aae300d2f9ed5..99ba7b4360cae 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -504,7 +504,7 @@ fn check_result( } if let Some(features) = check_tied_features( - tcx.sess, + &tcx.sess.target, &codegen_fn_attrs .target_features .iter() diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index afd9a88784c2f..b44a460afcbe2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -98,7 +98,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { } if is_cleanupret { // Cross-funclet jump - need a trampoline - assert!(base::wants_new_eh_instructions(fx.cx.tcx().sess)); + assert!(base::wants_new_eh_instructions(&fx.cx.tcx().sess.target)); debug!("llbb_with_cleanup: creating cleanup trampoline for {:?}", target); let name = &format!("{:?}_cleanup_trampoline_{:?}", self.bb, target); let trampoline_llbb = Bx::append_block(fx.cx, fx.llfn, name); @@ -228,12 +228,12 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { mir::UnwindAction::Continue => None, mir::UnwindAction::Unreachable => None, mir::UnwindAction::Terminate(reason) => { - if fx.mir[self.bb].is_cleanup && base::wants_wasm_eh(fx.cx.tcx().sess) { + if fx.mir[self.bb].is_cleanup && base::wants_wasm_eh(&fx.cx.tcx().sess.target) { // For wasm, we need to generate a nested `cleanuppad within %outer_pad` // to catch exceptions during cleanup and call `panic_in_cleanup`. Some(fx.terminate_block(reason, Some(self.bb))) } else if fx.mir[self.bb].is_cleanup - && base::wants_new_eh_instructions(fx.cx.tcx().sess) + && base::wants_new_eh_instructions(&fx.cx.tcx().sess.target) { // MSVC SEH will abort automatically if an exception tries to // propagate out from cleanup. @@ -2177,7 +2177,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // FIXME(eddyb) rename this to `eh_pad_for_uncached`. fn landing_pad_for_uncached(&mut self, bb: mir::BasicBlock) -> Bx::BasicBlock { let llbb = self.llbb(bb); - if base::wants_new_eh_instructions(self.cx.sess()) { + if base::wants_new_eh_instructions(&self.cx.sess().target) { let cleanup_bb = Bx::append_block(self.cx, self.llfn, &format!("funclet_{bb:?}")); let mut cleanup_bx = Bx::build(self.cx, cleanup_bb); let funclet = cleanup_bx.cleanup_pad(None, &[]); @@ -2221,7 +2221,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // what outer catch_pad it is contained in. debug_assert!( outer_catchpad_bb.is_some() - == (base::wants_wasm_eh(self.cx.tcx().sess) + == (base::wants_wasm_eh(&self.cx.tcx().sess.target) && reason == UnwindTerminateReason::InCleanup) ); @@ -2251,7 +2251,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let funclet; let llbb; let mut bx; - if base::wants_new_eh_instructions(self.cx.sess()) { + if base::wants_new_eh_instructions(&self.cx.sess().target) { // This is a basic block that we're aborting the program for, // notably in an `extern` function. These basic blocks are inserted // so that we assert that `extern` functions do indeed not panic, @@ -2317,7 +2317,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // The `null` in first argument here is actually a RTTI type // descriptor for the C++ personality function, but `catch (...)` // has no type so it's null. - let args = if base::wants_msvc_seh(self.cx.sess()) { + let args = if base::wants_msvc_seh(&self.cx.sess().target) { // This bitmask is a single `HT_IsStdDotDot` flag, which // represents that this is a C++-style `catch (...)` block that // only captures programmatic exceptions, not all SEH diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 6e87a295e9d2b..19218df78a898 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -245,7 +245,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( start_bx.set_personality_fn(cx.eh_personality()); } - let cleanup_kinds = base::wants_new_eh_instructions(tcx.sess) + let cleanup_kinds = base::wants_new_eh_instructions(&tcx.sess.target) .then(|| analyze::cleanup_kinds(&mir, &nop_landing_pads)); let cached_llbbs: IndexVec> = diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 7dad0cc1732dd..b630155c144ea 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -7,10 +7,10 @@ use rustc_lint_defs::builtin::{AARCH64_SOFTFLOAT_NEON, X86_SOFTFLOAT_SSE}; use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_session::Session; +use rustc_session::EarlySession; use rustc_session::diagnostics::feature_err; use rustc_span::{Span, Symbol, edit_distance, sym}; -use rustc_target::spec::{Arch, SanitizerSet}; +use rustc_target::spec::{Arch, SanitizerSet, Target}; use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability}; use smallvec::SmallVec; @@ -190,7 +190,7 @@ pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, /// features). If the list contains a syntactically invalid item (not starting with `+`/`-`) , the /// error callback is invoked. fn parse_rust_feature_list<'a>( - sess: &'a Session, + target: &'a Target, features: &'a str, err_callback: impl Fn(&'a str), mut callback: impl FnMut( @@ -211,14 +211,14 @@ fn parse_rust_feature_list<'a>( } let features_map = - features_map.get_or_insert_with(|| sess.target.rust_target_features_map()); + features_map.get_or_insert_with(|| target.rust_target_features_map()); if !features_map.contains_key(&base_feature) { callback(base_feature, None, true); continue; } - let implied_features = sess.target.implied_target_features(base_feature, &features_map); + let implied_features = target.implied_target_features(base_feature, &features_map); callback(base_feature, Some(implied_features), true) } else if let Some(base_feature) = feature.strip_prefix('-') { // Skip features that are not target features, but rustc features. @@ -227,7 +227,7 @@ fn parse_rust_feature_list<'a>( } let features_map = - features_map.get_or_insert_with(|| sess.target.rust_target_features_map()); + features_map.get_or_insert_with(|| target.rust_target_features_map()); if !features_map.contains_key(&base_feature) { callback(base_feature, None, false); @@ -240,7 +240,7 @@ fn parse_rust_feature_list<'a>( let inverse_implied_features = inverse_implied_features.get_or_insert_with(|| { let mut set: FxHashMap<&str, FxHashSet<&str>> = FxHashMap::default(); - for (f, _, is) in sess.target.rust_target_features() { + for (f, _, is) in target.rust_target_features() { for i in is.iter() { set.entry(i).or_default().insert(f); } @@ -282,7 +282,7 @@ fn parse_rust_feature_list<'a>( /// /// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled elsewhere. pub fn internal_target_features<'a, const N: usize>( - sess: &Session, + sess: &EarlySession, to_backend_features: impl Fn(&'a str) -> SmallVec<[&'a str; N]>, mut target_base_has_feature: impl FnMut(&str) -> bool, ) -> UnordSet { @@ -314,7 +314,7 @@ pub fn internal_target_features<'a, const N: usize>( // Add enabled and remove disabled features. parse_rust_feature_list( - sess, + &sess.target, &sess.opts.cg.target_feature, /* err_callback */ |feature| { @@ -402,7 +402,7 @@ pub fn internal_target_features<'a, const N: usize>( }, ); - if let Some(f) = check_tied_features(sess, &enabled_disabled_features) { + if let Some(f) = check_tied_features(&sess.target, &enabled_disabled_features) { sess.dcx().emit_err(diagnostics::TargetFeatureDisableOrEnable { features: f, span: None, @@ -416,11 +416,11 @@ pub fn internal_target_features<'a, const N: usize>( /// Given a map from target_features to whether they are enabled or disabled, ensure only valid /// combinations are allowed. Returns `Some` if a violation is found. pub fn check_tied_features( - sess: &Session, + target: &Target, features: &FxHashMap<&str, bool>, ) -> Option<&'static [&'static str]> { if !features.is_empty() { - for tied in sess.target.tied_target_features() { + for tied in target.tied_target_features() { // Tied features must be set to the same value, or not set at all let mut tied_iter = tied.iter(); let enabled = features.get(tied_iter.next().unwrap()); @@ -437,7 +437,7 @@ pub fn check_tied_features( /// `extend_backend_features` extends the set of backend features (assumed to be in mutable state /// accessible by that closure) to enable/disable the given Rust feature name. pub fn target_spec_to_backend_features<'a>( - sess: &'a Session, + sess: &'a EarlySession, mut extend_backend_features: impl FnMut(&'a str, /* enable */ bool), ) { // This check handles SM versions that defaults (by LLVM) to unsupported (by Rust) PTX ISA versions. @@ -453,7 +453,7 @@ pub fn target_spec_to_backend_features<'a>( // Compute implied features parse_rust_feature_list( - sess, + &sess.target, &sess.target.features, /* err_callback */ |feature| { @@ -476,11 +476,11 @@ pub fn target_spec_to_backend_features<'a>( /// `extend_backend_features` extends the set of backend features (assumed to be in mutable state /// accessible by that closure) to enable/disable the given Rust feature name. pub fn flag_to_backend_features<'a>( - sess: &'a Session, + sess: &'a EarlySession, mut extend_backend_features: impl FnMut(&'a str, /* enable */ bool), ) { parse_rust_feature_list( - sess, + &sess.target, &sess.opts.cg.target_feature, /* err_callback */ |_feature| { @@ -499,7 +499,7 @@ pub fn flag_to_backend_features<'a>( /// Computes the backend target features to be added to account for retpoline flags. /// Used by both LLVM and GCC since their target features are, conveniently, the same. -pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec) { +pub fn retpoline_features_by_flags(sess: &EarlySession, features: &mut Vec) { // -Zretpoline without -Zretpoline-external-thunk enables // retpoline-indirect-branches and retpoline-indirect-calls target features let unstable_opts = &sess.opts.unstable_opts; @@ -518,7 +518,7 @@ pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec) { } /// Computes the backend target features to be added to account for sanitizer flags. -pub fn sanitizer_features_by_flags(sess: &Session, features: &mut Vec) { +pub fn sanitizer_features_by_flags(sess: &EarlySession, features: &mut Vec) { // It's intentional that this is done only for non-kernel version of hwaddress. This matches // clang behavior. if sess.sanitizers().contains(SanitizerSet::HWADDRESS) { diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 11878c1f5165d..9fda73d483f49 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -9,7 +9,7 @@ use rustc_middle::dep_graph::WorkProductMap; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::config::{OutputFilenames, PrintRequest}; -use rustc_session::{IncrCompSession, Session}; +use rustc_session::{CodegenBackendInit, EarlySession, IncrCompSession, Session}; use rustc_span::Symbol; use rustc_structures::CrateType; @@ -37,13 +37,15 @@ pub trait BackendTypes { pub trait CodegenBackend { fn name(&self) -> &'static str; - fn init(&self, _sess: &Session) {} + fn init(&self, _sess: &EarlySession) -> CodegenBackendInit { + Default::default() + } fn print(&self, _req: &PrintRequest, _out: &mut String, _sess: &Session) {} /// Collect target-specific options that should be set in `cfg(...)`, including /// `target_feature` and support for unstable float types. - fn target_config(&self, _sess: &Session) -> TargetConfig { + fn target_config(&self, _sess: &EarlySession) -> TargetConfig { TargetConfig { internal_target_features: Default::default(), // `true` is used as a default so backends need to acknowledge when they do not @@ -71,23 +73,6 @@ pub trait CodegenBackend { fn print_version(&self) {} - /// Returns a list of all intrinsics that this backend definitely - /// replaces, which means their fallback bodies do not need to be monomorphized. - fn replaced_intrinsics(&self) -> Vec { - vec![] - } - - /// Returns a list of all intrinsics that this backend definitely - /// does *not* replace, which means their fallback bodies can be MIR-inlined. - fn fallback_intrinsics(&self) -> Vec { - vec![] - } - - /// Is ThinLTO supported by this backend? - fn thin_lto_supported(&self) -> bool { - true - } - /// Value printed by `--print=backend-has-zstd`. /// /// Used by compiletest to determine whether tests involving zstd compression diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index da1f7d2a33967..303eb49defa61 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -18,7 +18,7 @@ use rustc_parse::parser::Recovery; use rustc_query_impl::print_query_stack; use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName}; use rustc_session::parse::ParseSess; -use rustc_session::{CompilerIO, EarlyDiagCtxt, Session}; +use rustc_session::{CompilerIO, EarlyDiagCtxt, EarlySession, Session}; use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMapInputs}; use rustc_span::{FileName, sym}; use tracing::trace; @@ -365,7 +365,8 @@ pub struct Config { /// hotswapping branch of cg_clif" for "setting the codegen backend from a /// custom driver where the custom codegen backend has arbitrary data." /// (See #102759.) - pub make_codegen_backend: Option Box + Send>>, + pub make_codegen_backend: + Option Box + Send>>, /// The inner atomic value is set to true when a feature marked as `internal` is /// enabled. Makes it so that "please report a bug" is hidden, as ICEs with @@ -417,8 +418,27 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); + let early_sess = + rustc_session::build_early_session(config.opts, target, config.ice_file); + + let codegen_backend = match config.make_codegen_backend { + None => util::get_codegen_backend( + &early_dcx, + &early_sess.opts.sysroot, + early_sess.opts.unstable_opts.codegen_backend.as_deref(), + &early_sess.target, + ), + Some(make_codegen_backend) => { + // N.B. `make_codegen_backend` takes precedence over + // `target.default_codegen_backend`, which is ignored in this case. + make_codegen_backend(&early_sess) + } + }; + let codegen_backend_init = codegen_backend.init(&early_sess); + let mut sess = rustc_session::build_session( - config.opts, + early_sess, + codegen_backend_init, CompilerIO { input: config.input, output_dir: config.output_dir, @@ -426,30 +446,10 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se temps_dir, }, config.lint_caps, - target, util::rustc_version_str().unwrap_or("unknown"), - config.ice_file, config.using_internal_features, ); - let codegen_backend = match config.make_codegen_backend { - None => util::get_codegen_backend( - &early_dcx, - &sess.opts.sysroot, - sess.opts.unstable_opts.codegen_backend.as_deref(), - &sess.target, - ), - Some(make_codegen_backend) => { - // N.B. `make_codegen_backend` takes precedence over - // `target.default_codegen_backend`, which is ignored in this case. - make_codegen_backend(&sess) - } - }; - codegen_backend.init(&sess); - sess.replaced_intrinsics = FxHashSet::from_iter(codegen_backend.replaced_intrinsics()); - sess.fallback_intrinsics = FxHashSet::from_iter(codegen_backend.fallback_intrinsics()); - sess.thin_lto_supported = codegen_backend.thin_lto_supported(); - let target_config = codegen_backend.target_config(&sess); // Store all of the target features in the session. @@ -463,7 +463,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se util::add_configuration( &mut sess.config, &target_config, - &sess.target, + &sess.early_sess.target, is_nightly_build, is_crt_static, ); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index d3c479f2a22f5..cf8134a8b49c3 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -21,7 +21,10 @@ use rustc_session::config::{ }; use rustc_session::search_paths::SearchPath; use rustc_session::utils::{CanonicalizedPath, NativeLib}; -use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, build_session, getopts}; +use rustc_session::{ + CodegenBackendInit, CompilerIO, EarlyDiagCtxt, Session, build_early_session, build_session, + getopts, +}; use rustc_span::edition::{DEFAULT_EDITION, Edition}; use rustc_span::source_map::{RealFileLoader, SourceMapInputs}; use rustc_span::{FileName, RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm, sym}; @@ -66,13 +69,13 @@ where static USING_INTERNAL_FEATURES: AtomicBool = AtomicBool::new(false); + let sess = build_early_session(sessopts, target, None); let sess = build_session( - sessopts, + sess, + CodegenBackendInit::default(), io, Default::default(), - target, "", - None, &USING_INTERNAL_FEATURES, ); let cfg = parse_cfg(&sess, matches.opt_strs("cfg")); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 1af2094c93d0d..b62e5eb0c01fa 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -24,7 +24,7 @@ use rustc_query_impl::{CollectActiveJobsKind, collect_active_query_jobs}; use rustc_session::config::{ Cfg, Jobs, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple, }; -use rustc_session::{EarlyDiagCtxt, IncrCompSession, Session, filesearch}; +use rustc_session::{EarlyDiagCtxt, EarlySession, IncrCompSession, Session, filesearch}; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMapInputs; use rustc_span::{SessionGlobals, Symbol, sym}; @@ -379,7 +379,7 @@ impl CodegenBackend for DummyCodegenBackend { "dummy" } - fn target_config(&self, sess: &Session) -> TargetConfig { + fn target_config(&self, sess: &EarlySession) -> TargetConfig { let abi_required_features = sess.target.abi_required_features(); let internal_target_features = internal_target_features::<0>( sess, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index f04f40dd17168..86399891096ce 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1,4 +1,5 @@ use std::any::Any; +use std::ops::{Deref, DerefMut}; use std::path::Component::Prefix; use std::path::PathBuf; use std::str::FromStr; @@ -37,8 +38,8 @@ use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use crate::config::{ self, BranchProtection, Cfg, CheckCfg, CoverageLevel, CoverageOptions, DebugInfo, - ErrorOutputType, FunctionReturn, Input, InstrumentCoverage, InstrumentMcount, NATIVE_CPU, - OptLevel, OutFileName, OutputType, PAuthKey, PointerAuthOption, SwitchWithOptPath, + ErrorOutputType, FunctionReturn, Input, InstrumentCoverage, InstrumentMcount, LtoCli, + NATIVE_CPU, OptLevel, OutFileName, OutputType, PAuthKey, PointerAuthOption, SwitchWithOptPath, }; use crate::filesearch::FileSearch; use crate::lint::LintId; @@ -323,16 +324,130 @@ impl PointerAuthConfig { } } +/// Partial session built before the full session. More specifically, `EarlySession` is used to +/// init the codegen backend, and then both pieces are used to build the full `Session`. +pub struct EarlySession { + pub target: Target, + pub host: Target, + pub opts: config::Options, + pub psess: ParseSess, +} + +// JUSTIFICATION: defn of the suggested wrapper fns +#[allow(rustc::bad_opt_access)] +impl EarlySession { + #[inline] + pub fn dcx(&self) -> DiagCtxtHandle<'_> { + self.psess.dcx() + } + + #[inline] + pub fn source_map(&self) -> &SourceMap { + self.psess.source_map() + } + + /// Note: this is simpler than `Session::lto`, hence the `early_` prefix (to more clearly + /// distinguish it). + pub fn early_lto(&self) -> LtoCli { + self.opts.cg.lto + } + + pub fn print_llvm_stats(&self) -> bool { + self.opts.unstable_opts.print_codegen_stats + } + + pub fn print_llvm_stats_json(&self) -> Option<&String> { + self.opts.unstable_opts.print_codegen_stats_json.as_ref() + } + + pub fn relocation_model(&self) -> RelocModel { + self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model) + } + + pub fn code_model(&self) -> Option { + self.opts.cg.code_model.or(self.target.code_model) + } + + pub fn tls_model(&self) -> TlsModel { + self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model) + } + + /// Returns the panic strategy for this compile session. If the user explicitly selected one + /// using '-C panic', use that, otherwise use the panic strategy defined by the target. + pub fn panic_strategy(&self) -> PanicStrategy { + self.opts.cg.panic.unwrap_or(self.target.panic_strategy) + } + + pub fn sanitizers(&self) -> SanitizerSet { + return self.opts.unstable_opts.sanitizer | self.target.options.default_sanitizers; + } + + /// Get the deployment target on Apple platforms based on the standard environment variables, + /// or fall back to the minimum version supported by `rustc`. + /// + /// This should be guarded behind `if sess.target.is_like_darwin`. + pub fn apple_deployment_target(&self) -> apple::OSVersion { + let min = apple::OSVersion::minimum_deployment_target(&self.target); + let env_var = apple::deployment_target_env_var(&self.target.os); + + // FIXME(madsmtm): Track changes to this. + if let Ok(deployment_target) = env::var(env_var) { + match apple::OSVersion::from_str(&deployment_target) { + Ok(version) => { + let os_min = apple::OSVersion::os_minimum_deployment_target(&self.target.os); + // 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 { + self.dcx().emit_warn(diagnostics::AppleDeploymentTarget::TooLow { + env_var, + version: version.fmt_pretty().to_string(), + os_min: os_min.fmt_pretty().to_string(), + }); + } + + // Raise the deployment target to the minimum supported. + version.max(min) + } + Err(error) => { + self.dcx() + .emit_err(diagnostics::AppleDeploymentTarget::Invalid { env_var, error }); + min + } + } + } else { + // If no deployment target variable is set, default to the minimum found above. + min + } + } +} + +/// Some info about the backend, returned by `CodegenBackend::init` and put into the `Session`. +#[derive(Default)] +pub struct CodegenBackendInit { + /// A list of all intrinsics that this backend definitely replaces, which means their fallback + /// bodies do not need to be monomorphized. + pub replaced_intrinsics: Vec, + + /// A list of all intrinsics that this backend definitely does *not* replace, which means their + /// fallback bodies can be MIR-inlined. + pub fallback_intrinsics: Vec, + + /// Is ThinLTO supported by this backend? + pub thin_lto_supported: bool = true, +} + /// Represents the data associated with a compilation /// session for a single crate. pub struct Session { - pub target: Target, - pub host: Target, + /// The `EarlySession` is embedded so it can be passed to functions that need it. + /// `Session::deref{_,mut}` exist so the fields within can be accessed as if they were direct + /// fields of `Session`. + pub early_sess: EarlySession, pub wasm_proc_macro_tuple: TargetTuple, pub wasm_proc_macro_target: Target, - pub opts: config::Options, pub target_tlib_path: SearchPath, - pub psess: ParseSess, pub unstable_features: UnstableFeatures, pub config: Cfg, pub check_config: CheckCfg, @@ -424,6 +539,20 @@ pub struct Session { pub pointer_auth_config: Option, } +impl Deref for Session { + type Target = EarlySession; + + fn deref(&self) -> &EarlySession { + &self.early_sess + } +} + +impl DerefMut for Session { + fn deref_mut(&mut self) -> &mut EarlySession { + &mut self.early_sess + } +} + #[derive(Clone, Copy)] pub enum CodegenUnits { /// Specified by the user. In this case we try fairly hard to produce the @@ -537,16 +666,6 @@ impl Session { self.dcx().set_must_produce_diag() } - #[inline] - pub fn dcx(&self) -> DiagCtxtHandle<'_> { - self.psess.dcx() - } - - #[inline] - pub fn source_map(&self) -> &SourceMap { - self.psess.source_map() - } - pub fn proc_macro_quoted_spans(&self) -> impl Iterator { // This is equivalent to `.iter().copied().enumerate()`, but that isn't possible for // AppendOnlyVec, so we resort to this scheme. @@ -819,14 +938,6 @@ impl Session { self.opts.unstable_opts.verbose_internals } - pub fn print_llvm_stats(&self) -> bool { - self.opts.unstable_opts.print_codegen_stats - } - - pub fn print_llvm_stats_json(&self) -> Option<&String> { - self.opts.unstable_opts.print_codegen_stats_json.as_ref() - } - pub fn verify_llvm_ir(&self) -> bool { self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() } @@ -916,12 +1027,6 @@ impl Session { } } - /// Returns the panic strategy for this compile session. If the user explicitly selected one - /// using '-C panic', use that, otherwise use the panic strategy defined by the target. - pub fn panic_strategy(&self) -> PanicStrategy { - self.opts.cg.panic.unwrap_or(self.target.panic_strategy) - } - pub fn fewer_names(&self) -> bool { if let Some(fewer_names) = self.opts.unstable_opts.fewer_names { fewer_names @@ -954,18 +1059,6 @@ impl Session { self.opts.unstable_opts.contract_checks.unwrap_or(false) } - pub fn relocation_model(&self) -> RelocModel { - self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model) - } - - pub fn code_model(&self) -> Option { - self.opts.cg.code_model.or(self.target.code_model) - } - - pub fn tls_model(&self) -> TlsModel { - self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model) - } - pub fn direct_access_external_data(&self) -> Option { self.opts .unstable_opts @@ -1137,50 +1230,6 @@ impl Session { self.opts.cg.link_dead_code.unwrap_or(false) } - /// Get the deployment target on Apple platforms based on the standard environment variables, - /// or fall back to the minimum version supported by `rustc`. - /// - /// This should be guarded behind `if sess.target.is_like_darwin`. - pub fn apple_deployment_target(&self) -> apple::OSVersion { - let min = apple::OSVersion::minimum_deployment_target(&self.target); - let env_var = apple::deployment_target_env_var(&self.target.os); - - // FIXME(madsmtm): Track changes to this. - if let Ok(deployment_target) = env::var(env_var) { - match apple::OSVersion::from_str(&deployment_target) { - Ok(version) => { - let os_min = apple::OSVersion::os_minimum_deployment_target(&self.target.os); - // 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 { - self.dcx().emit_warn(diagnostics::AppleDeploymentTarget::TooLow { - env_var, - version: version.fmt_pretty().to_string(), - os_min: os_min.fmt_pretty().to_string(), - }); - } - - // Raise the deployment target to the minimum supported. - version.max(min) - } - Err(error) => { - self.dcx() - .emit_err(diagnostics::AppleDeploymentTarget::Invalid { env_var, error }); - min - } - } - } else { - // If no deployment target variable is set, default to the minimum found above. - min - } - } - - pub fn sanitizers(&self) -> SanitizerSet { - return self.opts.unstable_opts.sanitizer | self.target.options.default_sanitizers; - } - pub fn pointer_authentication(&self) -> bool { self.pointer_auth_config.is_some() } @@ -1254,15 +1303,11 @@ fn default_emitter(sopts: &config::Options, source_map: Arc) -> Box, target: Target, - cfg_version: &'static str, ice_file: Option, - using_internal_features: &'static AtomicBool, -) -> Session { +) -> EarlySession { // FIXME: This is not general enough to make the warning lint completely override // normal diagnostic warnings, since the warning lint can also be denied and changed // later via the source code. @@ -1297,6 +1342,24 @@ pub fn build_session( dcx.handle().warn(warning) } + let psess = ParseSess::with_dcx(dcx, source_map); + + EarlySession { target, host, opts: sopts, psess } +} + +// JUSTIFICATION: literally session construction +#[allow(rustc::bad_opt_access)] +pub fn build_session( + early_sess: EarlySession, + codegen_backend_init: CodegenBackendInit, + io: CompilerIO, + driver_lint_caps: FxHashMap, + cfg_version: &'static str, + using_internal_features: &'static AtomicBool, +) -> Session { + let EarlySession { target, host, opts: sopts, psess } = &early_sess; + let dcx = psess.dcx(); + let wasm_proc_macro_tuple = TargetTuple::from_tuple("wasm32-wasip2"); let (wasm_proc_macro_target, target_warnings) = Target::search( &wasm_proc_macro_tuple, @@ -1331,8 +1394,6 @@ pub fn build_session( None }; - let psess = ParseSess::with_dcx(dcx, source_map); - let host_triple = config::host_tuple(); let target_triple = sopts.target_triple.tuple(); // FIXME use host sysroot? @@ -1385,14 +1446,14 @@ pub fn build_session( let pointer_auth_config: Option = PointerAuthConfig::from_raw(&sopts.unstable_opts.pointer_authentication, &target); + let CodegenBackendInit { replaced_intrinsics, fallback_intrinsics, thin_lto_supported } = + codegen_backend_init; + let sess = Session { - target, - host, + early_sess, wasm_proc_macro_tuple, wasm_proc_macro_target, - opts: sopts, target_tlib_path, - psess, unstable_features: UnstableFeatures::from_environment(None), config: Cfg::default(), check_config: CheckCfg::default(), @@ -1414,9 +1475,9 @@ pub fn build_session( target_filesearch, host_filesearch, wasm_proc_macro_filesearch, - replaced_intrinsics: FxHashSet::default(), // filled by `run_compiler` - fallback_intrinsics: FxHashSet::default(), // filled by `run_compiler` - thin_lto_supported: true, // filled by `run_compiler` + replaced_intrinsics: FxHashSet::from_iter(replaced_intrinsics), + fallback_intrinsics: FxHashSet::from_iter(fallback_intrinsics), + thin_lto_supported, mir_opt_bisect_eval_count: AtomicUsize::new(0), removed_rustc_main_attr: AtomicBool::new(false), pointer_auth_config, diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 9110f6cbf6b4e..01ab675faed8f 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -33,7 +33,7 @@ impl<'sm> HasSourceMap<'sm> for &'sm SourceMap { impl<'sm> HasSourceMap<'sm> for &'sm Session { #[inline] fn source_map(self) -> &'sm SourceMap { - self.source_map() + self.early_sess.source_map() } } impl<'sm> HasSourceMap<'sm> for TyCtxt<'sm> { diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 19d1f5d6f461c..4b8e19e8f35f7 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -46,7 +46,7 @@ use rustc_log::tracing::debug; use rustc_middle::query::LocalCrate; use rustc_middle::ty::TyCtxt; use rustc_session::config::{ErrorOutputType, OptLevel}; -use rustc_session::{EarlyDiagCtxt, Session}; +use rustc_session::{EarlyDiagCtxt, EarlySession, Session}; use rustc_structures::CrateType; use crate::log::setup::{deinit_loggers, init_early_loggers, init_late_loggers}; @@ -109,7 +109,7 @@ fn run_many_seeds( /// Generates the codegen backend for code that Miri will interpret: we basically /// use the dummy backend, except that we put the LLVM backend in charge of /// target features. -fn make_miri_codegen_backend(sess: &Session, dep: bool) -> Box { +fn make_miri_codegen_backend(sess: &EarlySession, dep: bool) -> Box { let early_dcx = EarlyDiagCtxt::new(sess.opts.error_format); // Use the target_config method of the default codegen backend (eg LLVM) to ensure the @@ -215,7 +215,7 @@ impl CodegenBackend for MiriCodegenBackend { "miri" } - fn target_config(&self, sess: &Session) -> TargetConfig { + fn target_config(&self, sess: &EarlySession) -> TargetConfig { let native_target_config = self.native.target_config(sess); TargetConfig { internal_target_features: native_target_config.internal_target_features, From e6a3eac5b672e90d92c4c51f1c4ff1bd0172858f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 28 Aug 2026 16:12:32 +1000 Subject: [PATCH 6/7] Change `CodegenBackend::init` to take `&mut self` It currently takes `&self`, which is a bit strange for an `init` method. As a result, the Cranelift and GCC backends have to use types with interior mutability. This commit changes it to `&mut self`. Benefits: - The Cranelift backend can use `Option` instead of `OnceCell` to indicate uninit vs. init. - The GCC backend can avoid `Mutex`, and use `bool` instead of `AtomicBool`, which makes things much simpler. The commit also restructures `GccCodegenBackend` to mirror `CraneliftCodegenBackend`: just contain an `Option`, which makes the uninit vs. init distinction foolproof. (E.g. no need to set `lto_supported` to false and then later overwrite it with the real value.) As part of this the `LockedTargetInfo` type is renamed `SharedTargetInfo` because that better matches its new internals. (All this compiles both with and without the "master" feature set.) --- compiler/rustc_codegen_cranelift/src/lib.rs | 23 ++-- compiler/rustc_codegen_gcc/src/base.rs | 6 +- compiler/rustc_codegen_gcc/src/lib.rs | 109 +++++------------- compiler/rustc_codegen_llvm/src/lib.rs | 2 +- .../rustc_codegen_ssa/src/traits/backend.rs | 2 +- compiler/rustc_interface/src/interface.rs | 2 +- src/tools/miri/src/bin/miri.rs | 2 +- 7 files changed, 52 insertions(+), 94 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 742f08835b7bf..6d071b117c5e2 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -31,7 +31,6 @@ extern crate rustc_target; extern crate rustc_driver; use std::any::Any; -use std::cell::OnceCell; use std::env; use std::sync::Arc; @@ -118,7 +117,8 @@ impl String> Drop for PrintOnPanic { } pub struct CraneliftCodegenBackend { - pub config: OnceCell, + // Set by `init` if not already set. (E.g. by cg_clif.) + pub config: Option, } impl CodegenBackend for CraneliftCodegenBackend { @@ -126,7 +126,7 @@ impl CodegenBackend for CraneliftCodegenBackend { "cranelift" } - fn init(&self, sess: &EarlySession) -> CodegenBackendInit { + fn init(&mut self, sess: &EarlySession) -> CodegenBackendInit { use rustc_session::config::{InstrumentCoverage, LtoCli}; match (sess.target.requires_lto, sess.early_lto()) { @@ -141,12 +141,15 @@ impl CodegenBackend for CraneliftCodegenBackend { .fatal("`-Cinstrument-coverage` is LLVM specific and not supported by Cranelift"); } - let config = self.config.get_or_init(|| { - BackendConfig::from_opts(&sess.opts.cg.llvm_args) - .unwrap_or_else(|err| sess.dcx().fatal(err)) - }); + // Set `config` if not already set. + if self.config.is_none() { + self.config = Some( + BackendConfig::from_opts(&sess.opts.cg.llvm_args) + .unwrap_or_else(|err| sess.dcx().fatal(err)), + ); + } - if config.jit_mode && !sess.opts.output_types.should_codegen() { + if self.config.as_ref().unwrap().jit_mode && !sess.opts.output_types.should_codegen() { sess.dcx().fatal("JIT mode doesn't work with `cargo check`"); } @@ -218,7 +221,7 @@ impl CodegenBackend for CraneliftCodegenBackend { fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { info!("codegen crate {}", tcx.crate_name(LOCAL_CRATE)); - let config = self.config.get().unwrap(); + let config = self.config.as_ref().unwrap(); if config.jit_mode { #[cfg(feature = "jit")] driver::jit::run_jit(tcx, self.target_cpu(tcx.sess), config.jit_args.clone()); @@ -374,5 +377,5 @@ fn build_isa(sess: &Session, jit: bool) -> Arc { /// This is the entrypoint for a hot plugged rustc_codegen_cranelift #[unsafe(no_mangle)] pub fn __rustc_codegen_backend() -> Box { - Box::new(CraneliftCodegenBackend { config: OnceCell::new() }) + Box::new(CraneliftCodegenBackend { config: None }) } diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 74031d0f1628b..a022caf5cf944 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -21,7 +21,7 @@ use rustc_target::spec::{Arch, RelocModel}; use crate::builder::Builder; use crate::context::CodegenCx; -use crate::{GccContext, LockedTargetInfo, LtoMode, SyncContext, gcc_util, new_context}; +use crate::{GccContext, LtoMode, SharedTargetInfo, SyncContext, gcc_util, new_context}; #[cfg(feature = "master")] pub fn visibility_to_gcc(visibility: Visibility) -> gccjit::Visibility { @@ -73,7 +73,7 @@ pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType { pub fn compile_codegen_unit( tcx: TyCtxt<'_>, cgu_name: Symbol, - target_info: LockedTargetInfo, + target_info: SharedTargetInfo, lto_supported: bool, ) -> (ModuleCodegen, u64) { let prof_timer = tcx.prof.generic_activity("codegen_module"); @@ -96,7 +96,7 @@ pub fn compile_codegen_unit( fn module_codegen( tcx: TyCtxt<'_>, cgu_name: Symbol, - target_info: LockedTargetInfo, + target_info: SharedTargetInfo, lto_supported: bool, ) -> ModuleCodegen { let cgu = tcx.codegen_unit(cgu_name); diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 8d8dc58b18b7c..c1daa5ec7f024 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -69,12 +69,10 @@ mod type_of; use std::any::Any; use std::ffi::CString; -use std::fmt::Debug; use std::fs; use std::ops::Deref; use std::path::{Path, PathBuf}; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use gccjit::{CType, Context, OptimizationLevel}; #[cfg(feature = "master")] @@ -116,7 +114,7 @@ impl String> Drop for PrintOnPanic { #[cfg(not(feature = "master"))] #[derive(Debug)] pub struct TargetInfo { - supports_128bit_integers: AtomicBool, + supports_128bit_integers: bool, } #[cfg(not(feature = "master"))] @@ -128,7 +126,7 @@ impl TargetInfo { fn supports_target_dependent_type(&self, typ: CType) -> bool { match typ { CType::UInt128t | CType::Int128t => { - if self.supports_128bit_integers.load(Ordering::SeqCst) { + if self.supports_128bit_integers { return true; } } @@ -138,43 +136,26 @@ impl TargetInfo { } } +type SharedTargetInfo = Arc>; + #[derive(Clone)] -pub struct LockedTargetInfo { - info: Arc>>>, +pub struct BackendConfig { + target_info: SharedTargetInfo, + lto_supported: bool, } -impl Debug for LockedTargetInfo { - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.info.lock().expect("lock").fmt(formatter) - } +#[derive(Clone)] +pub struct GccCodegenBackend { + // `None` before `init`, `Some` after. + pub config: Option, } -impl LockedTargetInfo { - fn cpu_supports(&self, feature: &str) -> bool { - self.info - .lock() - .expect("lock") - .as_ref() - .expect("target info not initialized") - .cpu_supports(feature) - } - - fn supports_target_dependent_type(&self, typ: CType) -> bool { - self.info - .lock() - .expect("lock") - .as_ref() - .expect("target info not initialized") - .supports_target_dependent_type(typ) +impl GccCodegenBackend { + fn config(&self) -> &BackendConfig { + self.config.as_ref().expect("target info not initialized") } } -#[derive(Clone)] -pub struct GccCodegenBackend { - target_info: LockedTargetInfo, - lto_supported: Arc, -} - fn load_libgccjit_if_needed(libgccjit_target_lib_file: &Path) { if gccjit::is_loaded() { // Do not load a libgccjit second time. @@ -195,7 +176,7 @@ impl CodegenBackend for GccCodegenBackend { "gcc" } - fn init(&self, sess: &EarlySession) -> CodegenBackendInit { + fn init(&mut self, sess: &EarlySession) -> CodegenBackendInit { fn file_path(sysroot_path: &Path, sess: &EarlySession) -> PathBuf { let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot_path, &sess.host.llvm_target); @@ -240,14 +221,10 @@ impl CodegenBackend for GccCodegenBackend { context.add_command_line_option(format!("-march={}", target_cpu)); } - *self.target_info.info.lock().expect("lock") = - IntoDynSyncSend(Some(context.get_target_info())); - } - - #[cfg(feature = "master")] - { - let lto_supported = gccjit::is_lto_supported(); - self.lto_supported.store(lto_supported, Ordering::SeqCst); + self.config = Some(BackendConfig { + target_info: Arc::new(IntoDynSyncSend(context.get_target_info())), + lto_supported: gccjit::is_lto_supported(), + }); gccjit::set_global_personality_function_name(b"rust_eh_personality\0"); } @@ -264,15 +241,12 @@ impl CodegenBackend for GccCodegenBackend { gccjit::OutputKind::Assembler, temp_file.to_str().expect("path to str"), ); - self.target_info - .info - .lock() - .expect("lock") - .0 - .as_ref() - .expect("target info not initialized") - .supports_128bit_integers - .store(check_context.get_last_error() == Ok(None), Ordering::SeqCst); + let target_info = + TargetInfo { supports_128bit_integers: check_context.get_last_error() == Ok(None) }; + self.config = Some(BackendConfig { + target_info: Arc::new(IntoDynSyncSend(target_info)), + lto_supported: false, + }); } CodegenBackendInit { @@ -310,7 +284,7 @@ impl CodegenBackend for GccCodegenBackend { } fn target_config(&self, sess: &EarlySession) -> TargetConfig { - target_config(sess, &self.target_info) + target_config(sess, &self.config().target_info) } } @@ -344,12 +318,11 @@ impl ExtraBackendMethods for GccCodegenBackend { module_name: &str, methods: &[AllocatorMethod], ) -> Self::Module { - let lto_supported = self.lto_supported.load(Ordering::SeqCst); let mut mods = GccContext { context: Arc::new(SyncContext::new(new_context(tcx))), relocation_model: tcx.sess.relocation_model(), lto_mode: LtoMode::None, - lto_supported, + lto_supported: self.config().lto_supported, temp_dir: None, }; @@ -364,12 +337,8 @@ impl ExtraBackendMethods for GccCodegenBackend { tcx: TyCtxt<'_>, cgu_name: Symbol, ) -> (ModuleCodegen, u64) { - base::compile_codegen_unit( - tcx, - cgu_name, - self.target_info.clone(), - self.lto_supported.load(Ordering::SeqCst), - ) + let config = self.config(); + base::compile_codegen_unit(tcx, cgu_name, config.target_info.clone(), config.lto_supported) } } @@ -498,21 +467,7 @@ impl WriteBackendMethods for GccCodegenBackend { /// This is the entrypoint for a hot plugged rustc_codegen_gccjit #[unsafe(no_mangle)] pub fn __rustc_codegen_backend() -> Box { - #[cfg(feature = "master")] - let info = { - // Check whether the target supports 128-bit integers, and sized floating point types (like - // Float16). - Arc::new(Mutex::new(IntoDynSyncSend(None))) - }; - #[cfg(not(feature = "master"))] - let info = Arc::new(Mutex::new(IntoDynSyncSend(Some(TargetInfo { - supports_128bit_integers: AtomicBool::new(false), - })))); - - Box::new(GccCodegenBackend { - lto_supported: Arc::new(AtomicBool::new(false)), - target_info: LockedTargetInfo { info }, - }) + Box::new(GccCodegenBackend { config: None }) } fn to_gcc_opt_level(optlevel: Option) -> OptimizationLevel { @@ -529,7 +484,7 @@ fn to_gcc_opt_level(optlevel: Option) -> OptimizationLevel { } /// Returns the features that should be set in `cfg(target_feature)`. -fn target_config(sess: &EarlySession, target_info: &LockedTargetInfo) -> TargetConfig { +fn target_config(sess: &EarlySession, target_info: &SharedTargetInfo) -> TargetConfig { let internal_target_features = internal_target_features( sess, |feature| to_gcc_features(&sess.target, feature), diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 810f281217809..21a1d34a890bf 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -219,7 +219,7 @@ impl CodegenBackend for LlvmCodegenBackend { "llvm" } - fn init(&self, sess: &EarlySession) -> CodegenBackendInit { + fn init(&mut self, sess: &EarlySession) -> CodegenBackendInit { llvm_util::init(sess); // Make sure llvm is inited // autodiff is based on Enzyme, a library which we might not have available, when it was diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 9fda73d483f49..917a833db4641 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -37,7 +37,7 @@ pub trait BackendTypes { pub trait CodegenBackend { fn name(&self) -> &'static str; - fn init(&self, _sess: &EarlySession) -> CodegenBackendInit { + fn init(&mut self, _sess: &EarlySession) -> CodegenBackendInit { Default::default() } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 303eb49defa61..006a320f11637 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -421,7 +421,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se let early_sess = rustc_session::build_early_session(config.opts, target, config.ice_file); - let codegen_backend = match config.make_codegen_backend { + let mut codegen_backend = match config.make_codegen_backend { None => util::get_codegen_backend( &early_dcx, &early_sess.opts.sysroot, diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 4b8e19e8f35f7..bf8b684b1fee9 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -114,7 +114,7 @@ fn make_miri_codegen_backend(sess: &EarlySession, dep: bool) -> Box Date: Fri, 28 Aug 2026 16:37:48 +1000 Subject: [PATCH 7/7] Remove `global_backend_features` query It's now possible to get the backend features (a `Vec`) when the codegen backend is started, pass it back through `CodegenBackendInit`, and just store it in the `Session`. This removes the need for the query. Also: - `WriteBackendMethods::target_machine_factory` no longer needs the `target_features` parameter, because it's now available through the `sess` parameter. - `CodegenContext` no longer needs the `backend_features` field because we can use `sess.global_backend_features` instead. - `CodegenBackend::provide` is now a no-op for all the in-tree backends. I haven't removed it because out-of-tree backends still rely on it. --- .../rustc_codegen_cranelift/src/driver/aot.rs | 1 - compiler/rustc_codegen_cranelift/src/lib.rs | 1 + compiler/rustc_codegen_gcc/src/attributes.rs | 2 +- compiler/rustc_codegen_gcc/src/lib.rs | 10 ++--- compiler/rustc_codegen_llvm/src/asm.rs | 2 +- compiler/rustc_codegen_llvm/src/attributes.rs | 4 +- compiler/rustc_codegen_llvm/src/back/write.rs | 15 ++------ compiler/rustc_codegen_llvm/src/lib.rs | 19 +++++----- compiler/rustc_codegen_ssa/src/back/write.rs | 17 ++------- compiler/rustc_codegen_ssa/src/base.rs | 2 +- compiler/rustc_codegen_ssa/src/lib.rs | 1 - .../rustc_codegen_ssa/src/traits/backend.rs | 2 + .../rustc_codegen_ssa/src/traits/write.rs | 1 - compiler/rustc_middle/src/queries.rs | 8 ---- compiler/rustc_session/src/session.rs | 37 +++++++++++++------ 15 files changed, 52 insertions(+), 70 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index d6c25cf524a5c..f1c16a2795ef9 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -318,7 +318,6 @@ impl WriteBackendMethods for AotDriver { &self, _sess: &Session, _opt_level: OptLevel, - _target_features: &[String], ) -> TargetMachineFactoryFn { Arc::new(|_, _| ()) } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 6d071b117c5e2..368878edcc527 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -154,6 +154,7 @@ impl CodegenBackend for CraneliftCodegenBackend { } CodegenBackendInit { + global_backend_features: vec![], replaced_intrinsics: vec![], fallback_intrinsics: vec![sym::type_id_eq], thin_lto_supported: false, diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index 321e150a1568d..a5cc44a46e154 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -133,7 +133,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>( // FIXME(antoyo): cg_llvm adds global features to each function so that LTO keep them. // Check if GCC requires the same. - let mut global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str()); + let mut global_features = cx.tcx.sess.global_backend_features.iter().map(|s| s.as_str()); function_features.extend(&mut global_features); let target_features = function_features .iter() diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index c1daa5ec7f024..a6ff5a6833044 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -91,7 +91,6 @@ use rustc_data_structures::sync::IntoDynSyncSend; use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; -use rustc_middle::util::Providers; use rustc_session::config::{OptLevel, OutputFilenames}; use rustc_session::{CodegenBackendInit, EarlySession, IncrCompSession, Session}; use rustc_span::{Symbol, sym}; @@ -188,6 +187,8 @@ impl CodegenBackend for GccCodegenBackend { .join("libgccjit.so") } + let global_backend_features = gcc_util::global_gcc_features(sess); + // We use all_paths() instead of only path() in case the path specified by --sysroot is // invalid. // This is the case for instance in Rust for Linux where they specify --sysroot=/dev/null. @@ -250,17 +251,13 @@ impl CodegenBackend for GccCodegenBackend { } CodegenBackendInit { + global_backend_features, replaced_intrinsics: vec![], fallback_intrinsics: vec![sym::type_id_eq], thin_lto_supported: false, } } - fn provide(&self, providers: &mut Providers) { - providers.queries.global_backend_features = - |tcx, ()| gcc_util::global_gcc_features(tcx.sess) - } - fn target_cpu(&self, sess: &Session) -> String { target_cpu(sess).to_owned() } @@ -396,7 +393,6 @@ impl WriteBackendMethods for GccCodegenBackend { &self, _sess: &Session, _opt_level: OptLevel, - _features: &[String], ) -> TargetMachineFactoryFn { // FIXME(antoyo): set opt level. Arc::new(|_, _| ()) diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 6099e25df4b3e..bbbeacb5dc160 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -499,7 +499,7 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { template_str.push_str("\n.att_syntax\n"); } - let target_features = self.tcx.global_backend_features(()).join(","); + let target_features = self.tcx.sess.global_backend_features.join(","); let target_cpu = llvm_util::target_cpu(self.tcx.sess); llvm::append_module_inline_asm( diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index ccb434e2d706f..2af43cc83faf7 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -158,7 +158,7 @@ pub(crate) fn sanitize_attrs<'ll, 'tcx>( } if enabled.contains(SanitizerSet::MEMTAG) { // Check to make sure the mte target feature is actually enabled. - let features = tcx.global_backend_features(()); + let features = &tcx.sess.global_backend_features; let mte_feature = features.iter().map(|s| &s[..]).rfind(|n| ["+mte", "-mte"].contains(&&n[..])); if let None | Some("-mte") = mte_feature { @@ -425,7 +425,7 @@ pub(crate) fn target_features_attr<'ll, 'tcx>( tcx: TyCtxt<'tcx>, function_features: Vec, ) -> Option<&'ll Attribute> { - let global_features = tcx.global_backend_features(()).iter().map(String::as_str); + let global_features = tcx.sess.global_backend_features.iter().map(String::as_str); let function_features = function_features.iter().map(String::as_str); let target_features = global_features.chain(function_features).intersperse(",").collect::(); diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 45247e12c1da9..3d66991ac060d 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -102,11 +102,7 @@ fn write_output_file<'ll>( pub(crate) fn create_informational_target_machine(sess: &Session) -> OwnedTargetMachine { let config = TargetMachineFactoryConfig { split_dwarf_file: None, output_obj_file: None }; - // Can't use query system here quite yet because this function is invoked before the query - // system/tcx is set up. - let features = llvm_util::global_llvm_features(sess, false); - - target_machine_factory(sess, config::OptLevel::No, &features)(sess.dcx(), config) + target_machine_factory(sess, config::OptLevel::No)(sess.dcx(), config) } pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTargetMachine { @@ -124,11 +120,7 @@ pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTar Some(tcx.output_filenames(()).temp_path_for_cgu(OutputType::Object, mod_name)); let config = TargetMachineFactoryConfig { split_dwarf_file, output_obj_file }; - target_machine_factory( - tcx.sess, - tcx.backend_optimization_level(()), - tcx.global_backend_features(()), - )(tcx.dcx(), config) + target_machine_factory(tcx.sess, tcx.backend_optimization_level(()))(tcx.dcx(), config) } fn to_llvm_opt_settings(cfg: config::OptLevel) -> (llvm::CodeGenOptLevel, llvm::CodeGenOptSize) { @@ -190,7 +182,6 @@ fn to_llvm_float_abi(float_abi: Option) -> llvm::FloatAbi { pub(crate) fn target_machine_factory( sess: &Session, optlvl: config::OptLevel, - target_features: &[String], ) -> TargetMachineFactoryFn { // Self-profile timer for creating a _factory_. let _prof_timer = sess.prof.generic_activity("target_machine_factory"); @@ -211,7 +202,7 @@ pub(crate) fn target_machine_factory( 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 features = CString::new(sess.global_backend_features.join(",")).unwrap(); let abi = SmallCStr::new(sess.target.llvm_abiname.desc()); let trap_unreachable = sess.opts.unstable_opts.trap_unreachable.unwrap_or(sess.target.trap_unreachable); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 21a1d34a890bf..61a9d3901587a 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -37,7 +37,6 @@ use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; -use rustc_middle::util::Providers; use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; use rustc_session::{CodegenBackendInit, EarlySession, IncrCompSession, Session}; use rustc_span::{Symbol, sym}; @@ -130,9 +129,8 @@ impl WriteBackendMethods for LlvmCodegenBackend { &self, sess: &Session, optlvl: OptLevel, - target_features: &[String], ) -> TargetMachineFactoryFn { - back::write::target_machine_factory(sess, optlvl, target_features) + back::write::target_machine_factory(sess, optlvl) } fn optimize_and_codegen_fat_lto( sess: &Session, @@ -222,6 +220,9 @@ impl CodegenBackend for LlvmCodegenBackend { fn init(&mut self, sess: &EarlySession) -> CodegenBackendInit { llvm_util::init(sess); // Make sure llvm is inited + let global_backend_features = + llvm_util::global_llvm_features(sess, /* for_cfg */ false); + // autodiff is based on Enzyme, a library which we might not have available, when it was // neither build, nor downloaded via rustup. If autodiff is used, but not available we emit // an early error here and abort compilation. @@ -288,12 +289,12 @@ impl CodegenBackend for LlvmCodegenBackend { // cranelift/GCC even if they have dedicated implementations. let fallback_intrinsics = vec![sym::type_id_eq]; - CodegenBackendInit { replaced_intrinsics, fallback_intrinsics, thin_lto_supported: true } - } - - fn provide(&self, providers: &mut Providers) { - providers.queries.global_backend_features = - |tcx, ()| llvm_util::global_llvm_features(tcx.sess, false) + CodegenBackendInit { + global_backend_features, + replaced_intrinsics, + fallback_intrinsics, + thin_lto_supported: true, + } } fn print(&self, req: &PrintRequest, out: &mut String, sess: &Session) { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 78cdd3e38f68c..1fe6af0285cb5 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -337,7 +337,6 @@ pub struct CodegenContext { pub output_filenames: Arc, pub module_config: Arc, pub opt_level: OptLevel, - pub backend_features: Vec, pub msvc_imps_needed: bool, pub is_pe_coff: bool, pub target_can_use_split_dwarf: bool, @@ -1277,8 +1276,7 @@ fn start_executing_work( }); let opt_level = tcx.backend_optimization_level(()); - let backend_features = tcx.global_backend_features(()).clone(); - let tm_factory = backend.target_machine_factory(tcx.sess, opt_level, &backend_features); + let tm_factory = backend.target_machine_factory(tcx.sess, opt_level); let remark_dir = if let Some(ref dir) = sess.opts.unstable_opts.remark_dir { let result = fs::create_dir_all(dir).and_then(|_| dir.canonicalize()); @@ -1308,7 +1306,6 @@ fn start_executing_work( output_filenames: Arc::clone(tcx.output_filenames(())), module_config: regular_config, opt_level, - backend_features, msvc_imps_needed: msvc_imps_needed(tcx), is_pe_coff: tcx.sess.target.is_like_windows, target_can_use_split_dwarf: tcx.sess.target_can_use_split_dwarf(), @@ -2169,11 +2166,7 @@ impl OngoingCodegen { compiled_modules } MaybeLtoModules::FatLto { cgcx, needs_fat_lto } => { - let tm_factory = self.backend.target_machine_factory( - sess, - cgcx.opt_level, - &cgcx.backend_features, - ); + let tm_factory = self.backend.target_machine_factory(sess, cgcx.opt_level); CompiledModules { modules: vec![do_fat_lto( @@ -2189,11 +2182,7 @@ impl OngoingCodegen { } } MaybeLtoModules::ThinLto { cgcx, needs_thin_lto } => { - let tm_factory = self.backend.target_machine_factory( - sess, - cgcx.opt_level, - &cgcx.backend_features, - ); + let tm_factory = self.backend.target_machine_factory(sess, cgcx.opt_level); CompiledModules { modules: do_thin_lto::( diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 65612e9fce074..857435bd009ff 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -1046,7 +1046,7 @@ impl CrateInfo { let n_crates = crates.len(); let mut info = CrateInfo { target_cpu, - target_features: tcx.global_backend_features(()).clone(), + target_features: tcx.sess.global_backend_features.clone(), crate_types, exported_symbols, linked_symbols, diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index bf2ce74e38d84..1272b26ca0612 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -340,7 +340,6 @@ pub fn provide(providers: &mut Providers) { crate::base::provide(&mut providers.queries); crate::target_features::provide(&mut providers.queries); crate::codegen_attrs::provide(&mut providers.queries); - providers.queries.global_backend_features = |_tcx: TyCtxt<'_>, ()| vec![]; } const RLINK_VERSION: u32 = 1; diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 917a833db4641..96062133b1a49 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -97,6 +97,8 @@ pub trait CodegenBackend { Box::new(crate::back::metadata::DefaultMetadataLoader) } + /// Allows queries to be overridden. Not used by any in-tree backends, but rustc_codegen_spirv + /// and rustc_codegen_nvvm use it. fn provide(&self, _providers: &mut Providers) {} fn target_cpu(&self, sess: &Session) -> String; diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 5bd5b272754b8..bb63e189d20d3 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -32,7 +32,6 @@ pub trait WriteBackendMethods: Clone + 'static { &self, sess: &Session, opt_level: config::OptLevel, - target_features: &[String], ) -> TargetMachineFactoryFn; /// Performs fat LTO by merging all modules into a single one, running autodiff /// if necessary and running any further optimizations diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 5794a6533bd1d..d256aad7d27f2 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -2731,14 +2731,6 @@ rustc_queries! { desc { "performing HIR wf-checking for predicate `{:?}` at item `{:?}`", key.0, key.1 } } - /// The list of backend features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, - /// `--target` and similar). - query global_backend_features(_: ()) -> &'tcx Vec { - arena_cache - eval_always - desc { "computing the backend features for CLI flags" } - } - query check_validity_requirement(key: (ValidityRequirement, ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>)) -> Result> { desc { "checking validity requirement for `{}`: {}", key.1.value, key.0 } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 86399891096ce..62b424fb9c6b3 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -426,15 +426,16 @@ impl EarlySession { /// Some info about the backend, returned by `CodegenBackend::init` and put into the `Session`. #[derive(Default)] pub struct CodegenBackendInit { - /// A list of all intrinsics that this backend definitely replaces, which means their fallback - /// bodies do not need to be monomorphized. + /// See `Session::global_backend_features`. + pub global_backend_features: Vec, + + /// See `Session::replaced_intrinsics`. pub replaced_intrinsics: Vec, - /// A list of all intrinsics that this backend definitely does *not* replace, which means their - /// fallback bodies can be MIR-inlined. + /// See `Session::fallback_intrinsics`. pub fallback_intrinsics: Vec, - /// Is ThinLTO supported by this backend? + /// See `Session::thin_lto_supported`. pub thin_lto_supported: bool = true, } @@ -493,9 +494,15 @@ pub struct Session { /// Set of actually enabled features for the current target, including ones that are not /// in `cfg(target_feature)` because they are unstable or internal-only. /// This is used by the compiler itself when it needs to know which target features are actually - /// going to be enabled in the backend. + /// going to be enabled in the backend (e.g. for knowing which registers inline asm can use). pub internal_target_features: FxIndexSet, + /// The list of backend target features for this session. Not used by Rust itself because the + /// concrete feature names can be backend-specific. This is computed from the target's base + /// features, `-Ctarget-cpu`, `-Ctarget-feature`, and other flags that the current backend + /// models as target features (but that are not considered target features in Rust). + pub global_backend_features: Vec, + /// The version of the rustc process, possibly including a commit hash and description. pub cfg_version: &'static str, @@ -515,11 +522,12 @@ pub struct Session { host_filesearch: Arc, wasm_proc_macro_filesearch: Option>, - /// The names of intrinsics that the current codegen backend replaces - /// with its own implementations. + /// A list of all intrinsics that the current codegen backend definitely replaces with its own + /// implementations, which means their fallback bodies do not need to be monomorphized. pub replaced_intrinsics: FxHashSet, - /// The names of intrinsics that the current codegen backend does *not* replace - /// with its own implementations. + + /// A list of all intrinsics that the current codegen backend definitely does *not* replace + /// with its own implementations, which means their fallback bodies can be MIR-inlined. pub fallback_intrinsics: FxHashSet, /// Does the codegen backend support ThinLTO? @@ -1446,8 +1454,12 @@ pub fn build_session( let pointer_auth_config: Option = PointerAuthConfig::from_raw(&sopts.unstable_opts.pointer_authentication, &target); - let CodegenBackendInit { replaced_intrinsics, fallback_intrinsics, thin_lto_supported } = - codegen_backend_init; + let CodegenBackendInit { + global_backend_features, + replaced_intrinsics, + fallback_intrinsics, + thin_lto_supported, + } = codegen_backend_init; let sess = Session { early_sess, @@ -1468,6 +1480,7 @@ pub fn build_session( miri_unleashed_features: Lock::new(Default::default()), asm_arch, internal_target_features: Default::default(), + global_backend_features, cfg_version, using_internal_features, env_depinfo: Default::default(),