From 99649708fe95e6e00b731fc8734c623875f9a73d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:57:42 +0000 Subject: [PATCH 1/3] Use cg_llvm's target_config in miri --- compiler/rustc_interface/src/interface.rs | 5 +-- compiler/rustc_interface/src/util.rs | 10 ++++-- src/tools/miri/src/bin/miri.rs | 36 ++++++++++++++++++- src/tools/miri/src/lib.rs | 1 - .../pass/target-cpu-implies-target-feature.rs | 12 +++++++ 5 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 src/tools/miri/tests/pass/target-cpu-implies-target-feature.rs diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index af1c35774a67a..c0f8f33692e8c 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -24,6 +24,7 @@ use rustc_session::parse::ParseSess; use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, lint}; use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMapInputs}; use rustc_span::{FileName, sym}; +use rustc_target::spec::Target; use tracing::trace; use crate::util; @@ -385,7 +386,7 @@ pub struct Config { /// custom driver where the custom codegen backend has arbitrary data." /// (See #102759.) pub make_codegen_backend: - Option Box + Send>>, + Option Box + Send>>, /// Registry of diagnostics codes. pub registry: Registry, @@ -453,7 +454,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se 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(&config.opts) + make_codegen_backend(&config.opts, &target) } }; diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index de4175c3511e7..7e9c135661630 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -339,7 +339,7 @@ pub fn get_codegen_backend( filename if filename.contains('.') => { load_backend_from_dylib(early_dcx, filename.as_ref()) } - "dummy" => || Box::new(DummyCodegenBackend), + "dummy" => || Box::new(DummyCodegenBackend { target_config_override: None }), #[cfg(feature = "llvm")] "llvm" => rustc_codegen_llvm::LlvmCodegenBackend::new, backend_name => get_codegen_sysroot(early_dcx, sysroot, backend_name), @@ -352,7 +352,9 @@ pub fn get_codegen_backend( unsafe { load() } } -struct DummyCodegenBackend; +pub struct DummyCodegenBackend { + pub target_config_override: Option TargetConfig>>, +} impl CodegenBackend for DummyCodegenBackend { fn locale_resource(&self) -> &'static str { @@ -364,6 +366,10 @@ impl CodegenBackend for DummyCodegenBackend { } fn target_config(&self, sess: &Session) -> TargetConfig { + if let Some(target_config_override) = &self.target_config_override { + return target_config_override(sess); + } + let abi_required_features = sess.target.abi_required_features(); let (target_features, unstable_target_features) = cfg_target_feature::<0>( sess, diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 9f1ff238359a1..f5d06c9f8c242 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -10,6 +10,7 @@ // The rustc crates we need extern crate rustc_abi; +extern crate rustc_codegen_ssa; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_hir; @@ -19,6 +20,7 @@ extern crate rustc_log; extern crate rustc_middle; extern crate rustc_session; extern crate rustc_span; +extern crate rustc_target; /// See docs in https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc/src/main.rs /// and https://github.com/rust-lang/rust/pull/146627 for why we need this. @@ -36,6 +38,7 @@ use std::num::{NonZero, NonZeroI32}; use std::ops::Range; use std::rc::Rc; use std::str::FromStr; +use std::sync::Once; use std::sync::atomic::{AtomicU32, Ordering}; use miri::{ @@ -43,12 +46,14 @@ use miri::{ ProvenanceMode, TreeBorrowsParams, ValidationMode, run_genmc_mode, }; use rustc_abi::ExternAbi; +use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::sync::{self, DynSync}; use rustc_driver::Compilation; use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::{self as hir, Node}; use rustc_hir_analysis::check::check_function_signature; use rustc_interface::interface::Config; +use rustc_interface::util::DummyCodegenBackend; use rustc_log::tracing::debug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::exported_symbols::{ @@ -58,8 +63,9 @@ use rustc_middle::query::LocalCrate; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::EarlyDiagCtxt; -use rustc_session::config::{CrateType, ErrorOutputType, OptLevel}; +use rustc_session::config::{CrateType, ErrorOutputType, OptLevel, Options}; use rustc_span::def_id::DefId; +use rustc_target::spec::Target; use crate::log::setup::{deinit_loggers, init_early_loggers, init_late_loggers}; @@ -161,7 +167,31 @@ 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(opts: &Options, target: &Target) -> Box { + let early_dcx = EarlyDiagCtxt::new(opts.error_format); + + // Use the target_config method of the default codegen backend (eg LLVM) to ensure the + // calculated target features match said backend by respecting eg -Ctarget-cpu. + let target_config_backend = + rustc_interface::util::get_codegen_backend(&early_dcx, &opts.sysroot, None, &target); + let target_config_backend_init = Once::new(); + + Box::new(DummyCodegenBackend { + target_config_override: Some(Box::new(move |sess| { + target_config_backend_init.call_once(|| target_config_backend.init(sess)); + target_config_backend.target_config(sess) + })), + }) +} + impl rustc_driver::Callbacks for MiriCompilerCalls { + fn config(&mut self, config: &mut rustc_interface::interface::Config) { + config.make_codegen_backend = Some(Box::new(make_miri_codegen_backend)); + } + fn after_analysis<'tcx>( &mut self, _: &rustc_interface::interface::Compiler, @@ -244,6 +274,10 @@ struct MiriBeRustCompilerCalls { impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { #[allow(rustc::potential_query_instability)] // rustc_codegen_ssa (where this code is copied from) also allows this lint fn config(&mut self, config: &mut Config) { + if self.target_crate { + config.make_codegen_backend = Some(Box::new(make_miri_codegen_backend)); + } + if config.opts.prints.is_empty() && self.target_crate { #[allow(rustc::bad_opt_access)] // tcx does not exist yet { diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 5ec2e8edd857d..b30395b738b17 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -165,7 +165,6 @@ pub use crate::shims::unwind::{CatchUnwindData, EvalContextExt as _}; /// Also disable the MIR pass that inserts an alignment check on every pointer dereference. Miri /// does that too, and with a better error message. pub const MIRI_DEFAULT_ARGS: &[&str] = &[ - "-Zcodegen-backend=dummy", "--cfg=miri", "-Zalways-encode-mir", "-Zextra-const-ub-checks", diff --git a/src/tools/miri/tests/pass/target-cpu-implies-target-feature.rs b/src/tools/miri/tests/pass/target-cpu-implies-target-feature.rs new file mode 100644 index 0000000000000..a55517c02e0cb --- /dev/null +++ b/src/tools/miri/tests/pass/target-cpu-implies-target-feature.rs @@ -0,0 +1,12 @@ +// Test that target-cpu implies the correct target features +//@only-target: x86_64 +//@compile-flags: -C target-cpu=x86-64-v4 + +fn main() { + assert!(is_x86_feature_detected!("avx512bw")); + assert!(is_x86_feature_detected!("avx512cd")); + assert!(is_x86_feature_detected!("avx512dq")); + assert!(is_x86_feature_detected!("avx512f")); + assert!(is_x86_feature_detected!("avx512vl")); + assert!(!is_x86_feature_detected!("avx512vpopcntdq")); +} From d3901cd6ff36e3869ccacfb238a15725ebe66dcc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 27 Nov 2025 19:46:01 +0100 Subject: [PATCH 2/3] simplify and explain MiriBeRustCompilerCalls logic for crate types --- src/tools/miri/src/bin/miri.rs | 128 +++++++++++++++++---------------- 1 file changed, 65 insertions(+), 63 deletions(-) diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index f5d06c9f8c242..57b052d0dfe09 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -274,15 +274,20 @@ struct MiriBeRustCompilerCalls { impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { #[allow(rustc::potential_query_instability)] // rustc_codegen_ssa (where this code is copied from) also allows this lint fn config(&mut self, config: &mut Config) { - if self.target_crate { - config.make_codegen_backend = Some(Box::new(make_miri_codegen_backend)); + if !self.target_crate { + // For a host crate, we fully behave like rustc. + return; } + // For a target crate, we emit an rlib that Miri can later consume. + config.make_codegen_backend = Some(Box::new(make_miri_codegen_backend)); - if config.opts.prints.is_empty() && self.target_crate { + // Avoid warnings about unsupported crate types. However, only do that we we are *not* being + // queried by cargo about the supported crate types so that cargo still receives the + // warnings it expects. + if config.opts.prints.is_empty() { #[allow(rustc::bad_opt_access)] // tcx does not exist yet { let any_crate_types = !config.opts.crate_types.is_empty(); - // Avoid warnings about unsupported crate types. config .opts .crate_types @@ -293,66 +298,63 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { assert!(!config.opts.crate_types.is_empty()); } } - - // Queries overridden here affect the data stored in `rmeta` files of dependencies, - // which will be used later in non-`MIRI_BE_RUSTC` mode. - config.override_queries = Some(|_, local_providers| { - // We need to add #[used] symbols to exported_symbols for `lookup_link_section`. - // FIXME handle this somehow in rustc itself to avoid this hack. - local_providers.exported_non_generic_symbols = |tcx, LocalCrate| { - let reachable_set = tcx.with_stable_hashing_context(|hcx| { - tcx.reachable_set(()).to_sorted(&hcx, true) - }); - tcx.arena.alloc_from_iter( - // This is based on: - // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L62-L63 - // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L174 - reachable_set.into_iter().filter_map(|&local_def_id| { - // Do the same filtering that rustc does: - // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L84-L102 - // Otherwise it may cause unexpected behaviours and ICEs - // (https://github.com/rust-lang/rust/issues/86261). - let is_reachable_non_generic = matches!( - tcx.hir_node_by_def_id(local_def_id), - Node::Item(&hir::Item { - kind: hir::ItemKind::Static(..) | hir::ItemKind::Fn{ .. }, - .. - }) | Node::ImplItem(&hir::ImplItem { - kind: hir::ImplItemKind::Fn(..), - .. - }) - if !tcx.generics_of(local_def_id).requires_monomorphization(tcx) - ); - if !is_reachable_non_generic { - return None; - } - let codegen_fn_attrs = tcx.codegen_fn_attrs(local_def_id); - if codegen_fn_attrs.contains_extern_indicator() - || codegen_fn_attrs - .flags - .contains(CodegenFnAttrFlags::USED_COMPILER) - || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) - { - Some(( - ExportedSymbol::NonGeneric(local_def_id.to_def_id()), - // Some dummy `SymbolExportInfo` here. We only use - // `exported_symbols` in shims/foreign_items.rs and the export info - // is ignored. - SymbolExportInfo { - level: SymbolExportLevel::C, - kind: SymbolExportKind::Text, - used: false, - rustc_std_internal_symbol: false, - }, - )) - } else { - None - } - }), - ) - } - }); } + + // Queries overridden here affect the data stored in `rmeta` files of dependencies, + // which will be used later in non-`MIRI_BE_RUSTC` mode. + config.override_queries = Some(|_, local_providers| { + // We need to add #[used] symbols to exported_symbols for `lookup_link_section`. + // FIXME handle this somehow in rustc itself to avoid this hack. + local_providers.exported_non_generic_symbols = |tcx, LocalCrate| { + let reachable_set = tcx + .with_stable_hashing_context(|hcx| tcx.reachable_set(()).to_sorted(&hcx, true)); + tcx.arena.alloc_from_iter( + // This is based on: + // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L62-L63 + // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L174 + reachable_set.into_iter().filter_map(|&local_def_id| { + // Do the same filtering that rustc does: + // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L84-L102 + // Otherwise it may cause unexpected behaviours and ICEs + // (https://github.com/rust-lang/rust/issues/86261). + let is_reachable_non_generic = matches!( + tcx.hir_node_by_def_id(local_def_id), + Node::Item(&hir::Item { + kind: hir::ItemKind::Static(..) | hir::ItemKind::Fn{ .. }, + .. + }) | Node::ImplItem(&hir::ImplItem { + kind: hir::ImplItemKind::Fn(..), + .. + }) + if !tcx.generics_of(local_def_id).requires_monomorphization(tcx) + ); + if !is_reachable_non_generic { + return None; + } + let codegen_fn_attrs = tcx.codegen_fn_attrs(local_def_id); + if codegen_fn_attrs.contains_extern_indicator() + || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) + || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) + { + Some(( + ExportedSymbol::NonGeneric(local_def_id.to_def_id()), + // Some dummy `SymbolExportInfo` here. We only use + // `exported_symbols` in shims/foreign_items.rs and the export info + // is ignored. + SymbolExportInfo { + level: SymbolExportLevel::C, + kind: SymbolExportKind::Text, + used: false, + rustc_std_internal_symbol: false, + }, + )) + } else { + None + } + }), + ) + } + }); } fn after_analysis<'tcx>( From 57b57b8b56e5d709dd749ecea0129147801f74e1 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 27 Nov 2025 19:30:17 +0000 Subject: [PATCH 3/3] Improve test --- .../tests/pass/target-cpu-implies-target-feature.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/tests/pass/target-cpu-implies-target-feature.rs b/src/tools/miri/tests/pass/target-cpu-implies-target-feature.rs index a55517c02e0cb..1acbd51ae81fe 100644 --- a/src/tools/miri/tests/pass/target-cpu-implies-target-feature.rs +++ b/src/tools/miri/tests/pass/target-cpu-implies-target-feature.rs @@ -3,10 +3,14 @@ //@compile-flags: -C target-cpu=x86-64-v4 fn main() { + assert!(cfg!(target_feature = "avx2")); + assert!(cfg!(target_feature = "avx512bw")); + assert!(cfg!(target_feature = "avx512cd")); + assert!(cfg!(target_feature = "avx512dq")); + assert!(cfg!(target_feature = "avx512f")); + assert!(cfg!(target_feature = "avx512vl")); assert!(is_x86_feature_detected!("avx512bw")); - assert!(is_x86_feature_detected!("avx512cd")); - assert!(is_x86_feature_detected!("avx512dq")); - assert!(is_x86_feature_detected!("avx512f")); - assert!(is_x86_feature_detected!("avx512vl")); + + assert!(cfg!(not(target_feature = "avx512vpopcntdq"))); assert!(!is_x86_feature_detected!("avx512vpopcntdq")); }