From 1b08f986cae27968173afa4ab6cc9d47eb4a704c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Aug 2026 21:47:00 +1000 Subject: [PATCH 1/5] 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 39ca15cd9807aa9de2c4303ad095a785f900b671 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Aug 2026 21:30:09 +1000 Subject: [PATCH 2/5] 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 298b58dd0007f..cebc3ffc2bbdd 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -497,10 +497,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 ac05dbe149bef0b486eb890fa3e6a90fd0059a16 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Aug 2026 16:07:47 +1000 Subject: [PATCH 3/5] 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 0911b0a3feb3b68632274a93a6c5eaf3a2023ea4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 31 Aug 2026 10:02:13 +1000 Subject: [PATCH 4/5] Simplify `OwnedTargetMachine` The `repr(transparent)` isn't necessary: there are no casts or transmutes involving it, and it's not passed by value across an FFI boundary. The `PhantomData` also isn't necessary: the type isn't generic so variance isn't a factor; the `Drop` impl doesn't involve `may_dangle`; and the `NonNull` field means the type is `!Send`/`!Sync` with or without the `PhantomData`. --- compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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..5b1046817c904 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -1,5 +1,4 @@ use std::ffi::CStr; -use std::marker::PhantomData; use std::ptr::NonNull; use rustc_data_structures::small_c_str::SmallCStr; @@ -9,10 +8,8 @@ use crate::llvm; /// Responsible for safely creating and disposing llvm::TargetMachine via ffi functions. /// Not cloneable as there is no clone function for llvm::TargetMachine. -#[repr(transparent)] pub struct OwnedTargetMachine { tm_unique: NonNull, - phantom: PhantomData, } impl OwnedTargetMachine { @@ -71,7 +68,7 @@ impl OwnedTargetMachine { }; NonNull::new(tm_ptr) - .map(|tm_unique| Self { tm_unique, phantom: PhantomData }) + .map(|tm_unique| Self { tm_unique }) .ok_or_else(|| LlvmError::CreateTargetMachine { triple: SmallCStr::from(triple) }) } From 7f0581d42e299ea0824c13338832daa20d442e62 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 27 Aug 2026 09:15:34 +1000 Subject: [PATCH 5/5] 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 an `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 | 49 +++++++++++++++++++ .../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 | 4 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 15 +++++- compiler/rustc_codegen_llvm/src/llvm_util.rs | 24 ++++++--- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 30 +++++++++--- .../custom-target-invalid-llvm-target.rs | 2 +- .../custom-target-invalid-llvm-target.stderr | 2 +- 12 files changed, 118 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..f57368e755add --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs @@ -0,0 +1,49 @@ +use std::ffi::CStr; +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, +} + +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 }) + .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 5b1046817c904..5a1dc8080c2c1 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -38,7 +38,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..3884f5af7f46a 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, /* for_cfg */ 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 54f8ffbb881da..c981b5eaeb730 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 552a91ffee071..29fcb69ff28fc 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -247,7 +247,7 @@ impl CodegenBackend for LlvmCodegenBackend { fn provide(&self, providers: &mut Providers) { providers.queries.global_backend_features = - |tcx, ()| llvm_util::global_llvm_features(tcx.sess, false) + |tcx, ()| llvm_util::global_llvm_features(tcx.sess, /* for_cfg */ false) } fn print(&self, req: &PrintRequest, out: &mut String, sess: &Session) { @@ -493,7 +493,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 05d3bd0b08b95..8551f9b6fb3fe 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; @@ -2362,7 +2363,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); @@ -2404,6 +2404,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 cebc3ffc2bbdd..7090e21e863df 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(); @@ -318,7 +320,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, /* for_cfg */ 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, @@ -329,16 +338,17 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig { }, |feature| { // 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 + // do *not* consider the `-Ctarget-feature`s here (that's why we passed `for_cfg: true` + // to `global_llvm_features` above) because that will be handled later in // `internal_target_features`. if let Some(feat) = to_llvm_features(sess, feature) { // 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; } } @@ -479,7 +489,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), @@ -777,7 +787,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 6fd78c6bde4be..0e75c95763bd9 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -91,15 +91,31 @@ 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(); +extern "C" MCSubtargetInfo * +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; + } + +#if LLVM_VERSION_GE(22, 0) + return TheTarget->createMCSubtargetInfo(Trip, CPU, Features); #else - const MCSubtargetInfo &MCInfo = *Target->getMCSubtargetInfo(); + return TheTarget->createMCSubtargetInfo(Trip.str(), CPU, Features); #endif - return MCInfo.checkFeatures(std::string("+") + Feature); +} + +extern "C" bool LLVMRustMCSubtargetInfoHasFeature(MCSubtargetInfo *MCInfo, + const char *Feature) { + return MCInfo->checkFeatures(std::string("+") + Feature); +} + +extern "C" void LLVMRustDisposeMCSubtargetInfo(MCSubtargetInfo *MCInfo) { + delete 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"