From 53a7ce318ef71e8b84f8b61814dad2a6f2c2277e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 6 Feb 2025 17:08:02 +0000 Subject: [PATCH 1/4] Add -Zjit-mode flag and use it in cg_clif This flag tells the codegen backend to immediately execute the code it builds. This is exactly what miri needs as it is unable to produce real executables. Adding a builtin flag would allow cargo to add native support for this without miri needing to do a bunch of hacks in its cargo wrapper. Furthermore cg_clif also has a jit mode that benefits from it. --- .../rustc_codegen_cranelift/docs/usage.md | 4 +- .../scripts/cargo-clif.rs | 6 +- .../scripts/filter_profile.rs | 2 +- .../rustc_codegen_cranelift/src/config.rs | 43 --------- .../rustc_codegen_cranelift/src/driver/jit.rs | 9 +- compiler/rustc_codegen_cranelift/src/lib.rs | 59 +++++++----- .../rustc_codegen_ssa/src/traits/backend.rs | 7 ++ compiler/rustc_driver_impl/src/lib.rs | 18 +++- compiler/rustc_interface/src/lib.rs | 1 + compiler/rustc_interface/src/passes.rs | 32 +++++-- compiler/rustc_interface/src/queries.rs | 92 +++++++++++++------ compiler/rustc_metadata/src/rmeta/encoder.rs | 4 + compiler/rustc_session/src/options.rs | 2 + 13 files changed, 159 insertions(+), 120 deletions(-) delete mode 100644 compiler/rustc_codegen_cranelift/src/config.rs diff --git a/compiler/rustc_codegen_cranelift/docs/usage.md b/compiler/rustc_codegen_cranelift/docs/usage.md index 9dcfee4f535a7..b6ba077b8305c 100644 --- a/compiler/rustc_codegen_cranelift/docs/usage.md +++ b/compiler/rustc_codegen_cranelift/docs/usage.md @@ -38,7 +38,7 @@ $ $cg_clif_dir/dist/cargo-clif jit or ```bash -$ $cg_clif_dir/dist/rustc-clif -Cllvm-args=jit-mode -Cprefer-dynamic my_crate.rs +$ $cg_clif_dir/dist/rustc-clif -Zjit-mode -Cprefer-dynamic my_crate.rs ``` ## Shell @@ -47,7 +47,7 @@ These are a few functions that allow you to easily run rust code from the shell ```bash function jit_naked() { - echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-options -Cllvm-args=jit-mode -Cprefer-dynamic + echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-options -Zjit-mode -Cprefer-dynamic } function jit() { diff --git a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs index e391cc7f75a92..c5491a3e0b75d 100644 --- a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs @@ -51,11 +51,7 @@ fn main() { args.remove(0); IntoIterator::into_iter(["rustc".to_string()]) .chain(args) - .chain([ - "--".to_string(), - "-Zunstable-options".to_string(), - "-Cllvm-args=jit-mode".to_string(), - ]) + .chain(["--".to_string(), "-Zjit-mode".to_string()]) .collect() } _ => args, diff --git a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs index 4595063c032dc..891052eeb5303 100755 --- a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs +++ b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs @@ -4,7 +4,7 @@ pushd $(dirname "$0")/../ RUSTC="$(pwd)/dist/rustc-clif" popd -PROFILE=$1 OUTPUT=$2 exec $RUSTC -Zunstable-options -Cllvm-args=jit-mode -Cprefer-dynamic $0 +PROFILE=$1 OUTPUT=$2 exec $RUSTC -Zjit-mode -Cprefer-dynamic $0 #*/ //! This program filters away uninteresting samples and trims uninteresting frames for stackcollapse diff --git a/compiler/rustc_codegen_cranelift/src/config.rs b/compiler/rustc_codegen_cranelift/src/config.rs deleted file mode 100644 index 31bc0374460f4..0000000000000 --- a/compiler/rustc_codegen_cranelift/src/config.rs +++ /dev/null @@ -1,43 +0,0 @@ -/// Configuration of cg_clif as passed in through `-Cllvm-args` and various env vars. -#[derive(Debug)] -pub struct BackendConfig { - /// Should the crate be AOT compiled or JIT executed. - /// - /// Defaults to AOT compilation. Can be set using `-Cllvm-args=jit-mode`. - pub jit_mode: bool, - - /// When JIT mode is enable pass these arguments to the program. - /// - /// Defaults to the value of `CG_CLIF_JIT_ARGS`. - pub jit_args: Vec, -} - -impl BackendConfig { - /// Parse the configuration passed in using `-Cllvm-args`. - pub fn from_opts(opts: &[String]) -> Result { - let mut config = BackendConfig { - jit_mode: false, - jit_args: match std::env::var("CG_CLIF_JIT_ARGS") { - Ok(args) => args.split(' ').map(|arg| arg.to_string()).collect(), - Err(std::env::VarError::NotPresent) => vec![], - Err(std::env::VarError::NotUnicode(s)) => { - panic!("CG_CLIF_JIT_ARGS not unicode: {:?}", s); - } - }, - }; - - for opt in opts { - if opt.starts_with("-import-instr-limit") { - // Silently ignore -import-instr-limit. It is set by rust's build system even when - // testing cg_clif. - continue; - } - match &**opt { - "jit-mode" => config.jit_mode = true, - _ => return Err(format!("Unknown option `{}`", opt)), - } - } - - Ok(config) - } -} diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 3a8ca25a5fc00..5effab03664e7 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -3,6 +3,7 @@ use std::ffi::CString; use std::os::raw::{c_char, c_int}; +use std::process::ExitCode; use cranelift_jit::{JITBuilder, JITModule}; use rustc_codegen_ssa::CrateInfo; @@ -32,11 +33,7 @@ fn create_jit_module(tcx: TyCtxt<'_>) -> (UnwindModule, Option, jit_args: Vec) -> ! { - if !tcx.crate_types().contains(&rustc_session::config::CrateType::Executable) { - tcx.dcx().fatal("can't jit non-executable crate"); - } - +pub(crate) fn run_jit(tcx: TyCtxt<'_>, jit_args: Vec) -> ExitCode { let output_filenames = tcx.output_filenames(()); let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); let (mut jit_module, mut debug_context) = create_jit_module(tcx); @@ -113,7 +110,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, jit_args: Vec) -> ! { argv.push(std::ptr::null()); let ret = f(args.len() as c_int, argv.as_ptr()); - std::process::exit(ret); + ExitCode::from(ret as u8) } fn codegen_and_compile_fn<'tcx>( diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 5fdecd014ac05..520df46f47e02 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -36,8 +36,8 @@ extern crate rustc_target; extern crate rustc_driver; use std::any::Any; -use std::cell::OnceCell; use std::env; +use std::process::ExitCode; use std::sync::Arc; use cranelift_codegen::isa::TargetIsa; @@ -51,7 +51,6 @@ use rustc_session::config::OutputFilenames; use rustc_span::{Symbol, sym}; use rustc_target::spec::{Abi, Arch, Env, Os}; -pub use crate::config::*; use crate::prelude::*; mod abi; @@ -64,7 +63,6 @@ mod codegen_i128; mod common; mod compiler_builtins; mod concurrency_limiter; -mod config; mod constant; mod debuginfo; mod discriminant; @@ -122,9 +120,7 @@ impl String> Drop for PrintOnPanic { } } -pub struct CraneliftCodegenBackend { - pub config: OnceCell, -} +pub struct CraneliftCodegenBackend; impl CodegenBackend for CraneliftCodegenBackend { fn locale_resource(&self) -> &'static str { @@ -149,15 +145,6 @@ impl CodegenBackend for CraneliftCodegenBackend { sess.dcx() .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)) - }); - - if config.jit_mode && !sess.opts.output_types.should_codegen() { - sess.dcx().fatal("JIT mode doesn't work with `cargo check`"); - } } fn target_config(&self, sess: &Session) -> TargetConfig { @@ -206,16 +193,17 @@ 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(); - if config.jit_mode { - #[cfg(feature = "jit")] - driver::jit::run_jit(tcx, config.jit_args.clone()); - #[cfg(not(feature = "jit"))] - tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); - } else { - driver::aot::run_aot(tcx) + for opt in &tcx.sess.opts.cg.llvm_args { + if opt.starts_with("-import-instr-limit") { + // Silently ignore -import-instr-limit. It is set by rust's build system even when + // testing cg_clif. + continue; + } + tcx.sess.dcx().fatal(format!("Unknown option `{}`", opt)); } + + driver::aot::run_aot(tcx) } fn join_codegen( @@ -226,6 +214,29 @@ impl CodegenBackend for CraneliftCodegenBackend { ) -> (CodegenResults, FxIndexMap) { ongoing_codegen.downcast::().unwrap().join(sess, outputs) } + + fn jit_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, args: Vec) -> ExitCode { + info!("jit crate {}", tcx.crate_name(LOCAL_CRATE)); + + for opt in &tcx.sess.opts.cg.llvm_args { + if opt.starts_with("-import-instr-limit") { + // Silently ignore -import-instr-limit. It is set by rust's build system even when + // testing cg_clif. + continue; + } + tcx.sess.dcx().fatal(format!("Unknown option `{}`", opt)); + } + + #[cfg(feature = "jit")] + #[allow(unreachable_code)] + return driver::jit::run_jit(tcx, args); + + #[cfg(not(feature = "jit"))] + { + let _ = args; + tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); + } + } } /// Determine if the Cranelift ir verifier should run. @@ -355,5 +366,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) } diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index cb74e2e46d650..13941292ff64e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -1,5 +1,6 @@ use std::any::Any; use std::hash::Hash; +use std::process::ExitCode; use rustc_ast::expand::allocator::AllocatorMethod; use rustc_data_structures::fx::FxIndexMap; @@ -127,6 +128,12 @@ pub trait CodegenBackend { self.name(), ); } + + /// Used in place of [`codegen_crate`](Self::codegen_crate) when `-Zjit-mode` is passed. + fn jit_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, args: Vec) -> ExitCode { + let _ = args; + tcx.sess.dcx().fatal("-Zjit-mode not supported by the active codegen backend") + } } pub trait ExtraBackendMethods: diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 7820198f2dcf2..07bcea57c866c 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -226,7 +226,15 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) // the compiler with @empty_file as argv[0] and no more arguments. let at_args = at_args.get(1..).unwrap_or_default(); - let args = args::arg_expand_all(&default_early_dcx, at_args); + let mut args = args::arg_expand_all(&default_early_dcx, at_args); + + let (args, jit_args) = if let Some(idx) = args.iter().position(|arg| arg == "--") { + let mut jit_args = args.split_off(idx); + jit_args.remove(0); + (args, jit_args) + } else { + (args, vec![]) + }; let Some(matches) = handle_options(&default_early_dcx, &args) else { return; @@ -245,6 +253,10 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) let has_input = input.is_some(); let (odir, ofile) = make_output(&matches); + if !jit_args.is_empty() && !sopts.unstable_opts.jit_mode { + default_early_dcx.early_fatal("passing arguments after -- requires -Zjit-mode"); + } + drop(default_early_dcx); let mut config = interface::Config { @@ -380,7 +392,7 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) } } - Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)) + Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend, jit_args)) }); // Linking is done outside the `compiler.enter()` so that the @@ -388,7 +400,7 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) if let Some(linker) = linker { linker.link(sess, codegen_backend); } - }) + }); } fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &Path) { diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index ce2398fab9197..d13b397ed75f2 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,5 +1,6 @@ // tidy-alphabetical-start #![feature(decl_macro)] +#![feature(exitcode_exit_method)] #![feature(file_buffered)] #![feature(iter_intersperse)] #![feature(try_blocks)] diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index a0383b187de51..a3e4d402dae2f 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -2,6 +2,7 @@ use std::any::Any; use std::ffi::{OsStr, OsString}; use std::io::{self, BufWriter, Write}; use std::path::{Path, PathBuf}; +use std::process::ExitCode; use std::sync::{Arc, LazyLock, OnceLock}; use std::{env, fs, iter}; @@ -1219,14 +1220,8 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) { } } -/// Runs the codegen backend, after which the AST and analysis can -/// be discarded. -pub(crate) fn start_codegen<'tcx>( - codegen_backend: &dyn CodegenBackend, - tcx: TyCtxt<'tcx>, -) -> (Box, EncodedMetadata) { - tcx.sess.timings.start_section(tcx.sess.dcx(), TimingSection::Codegen); - +/// A couple of checks that need to run before we run codegen. +fn pre_codegen_checks(tcx: TyCtxt<'_>) { // Hook for tests. if let Some((def_id, _)) = tcx.entry_fn(()) && tcx.has_attr(def_id, sym::rustc_delayed_bug_from_inside_query) @@ -1246,6 +1241,17 @@ pub(crate) fn start_codegen<'tcx>( if let Some(guar) = tcx.sess.dcx().has_errors_or_delayed_bugs() { guar.raise_fatal(); } +} + +/// Runs the codegen backend, after which the AST and analysis can +/// be discarded. +pub(crate) fn start_codegen<'tcx>( + codegen_backend: &dyn CodegenBackend, + tcx: TyCtxt<'tcx>, +) -> (Box, EncodedMetadata) { + tcx.sess.timings.start_section(tcx.sess.dcx(), TimingSection::Codegen); + + pre_codegen_checks(tcx); info!("Pre-codegen\n{:?}", tcx.debug_stats()); @@ -1278,6 +1284,16 @@ pub(crate) fn start_codegen<'tcx>( (codegen, metadata) } +pub fn jit_crate<'tcx>( + codegen_backend: &dyn CodegenBackend, + tcx: TyCtxt<'tcx>, + args: Vec, +) -> ExitCode { + pre_codegen_checks(tcx); + + tcx.sess.time("jit_crate", move || codegen_backend.jit_crate(tcx, args)) +} + /// Compute and validate the crate name. pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol { // We validate *all* occurrences of `#![crate_name]`, pick the first find and diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 3799485077ef4..04901d751d781 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -1,8 +1,10 @@ use std::any::Any; +use std::process::ExitCode; use std::sync::Arc; use rustc_codegen_ssa::CodegenResults; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::indexmap::IndexMap; use rustc_data_structures::svh::Svh; use rustc_errors::timings::TimingSection; @@ -16,21 +18,41 @@ use rustc_session::config::{self, OutputFilenames, OutputType}; use crate::errors::FailedWritingFile; use crate::passes; +enum ExitCodeOr { + ExitCode(ExitCode), + Codegen(T), +} + pub struct Linker { dep_graph: DepGraph, output_filenames: Arc, // Only present when incr. comp. is enabled. crate_hash: Option, metadata: EncodedMetadata, - ongoing_codegen: Box, + ongoing_codegen: ExitCodeOr>, } impl Linker { pub fn codegen_and_build_linker( tcx: TyCtxt<'_>, codegen_backend: &dyn CodegenBackend, + jit_args: Vec, ) -> Linker { - let (ongoing_codegen, metadata) = passes::start_codegen(codegen_backend, tcx); + let (ongoing_codegen, metadata) = if tcx.sess.opts.unstable_opts.jit_mode { + if !tcx.sess.opts.output_types.should_codegen() { + tcx.sess.dcx().fatal("JIT mode doesn't work with `cargo check`"); + } + + // FIXME allow the backend to finalize the incr comp session before execution + + ( + ExitCodeOr::ExitCode(passes::jit_crate(codegen_backend, tcx, jit_args)), + EncodedMetadata::empty(), + ) + } else { + let (ongoing_codegen, metadata) = passes::start_codegen(codegen_backend, tcx); + (ExitCodeOr::Codegen(ongoing_codegen), metadata) + }; Linker { dep_graph: tcx.dep_graph.clone(), @@ -46,16 +68,25 @@ impl Linker { } pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) { - let (codegen_results, mut work_products) = sess.time("finish_ongoing_codegen", || { - match self.ongoing_codegen.downcast::() { - // This was a check only build - Ok(codegen_results) => (*codegen_results, IndexMap::default()), - - Err(ongoing_codegen) => { - codegen_backend.join_codegen(ongoing_codegen, sess, &self.output_filenames) - } + let (res, mut work_products) = match self.ongoing_codegen { + ExitCodeOr::ExitCode(exit_code) => { + (ExitCodeOr::ExitCode(exit_code), FxIndexMap::default()) } - }); + ExitCodeOr::Codegen(ongoing_codegen) => sess.time("finish_ongoing_codegen", || { + let (codegen_results, work_products) = match ongoing_codegen + .downcast::() + { + // This was a check only build + Ok(codegen_results) => (*codegen_results, IndexMap::default()), + + Err(ongoing_codegen) => { + codegen_backend.join_codegen(ongoing_codegen, sess, &self.output_filenames) + } + }; + + (ExitCodeOr::Codegen(codegen_results), work_products) + }), + }; sess.timings.end_section(sess.dcx(), TimingSection::Codegen); if sess.opts.incremental.is_some() @@ -95,23 +126,28 @@ impl Linker { return; } - if sess.opts.unstable_opts.no_link { - let rlink_file = self.output_filenames.with_extension(config::RLINK_EXT); - CodegenResults::serialize_rlink( - sess, - &rlink_file, - &codegen_results, - &self.metadata, - &self.output_filenames, - ) - .unwrap_or_else(|error| { - sess.dcx().emit_fatal(FailedWritingFile { path: &rlink_file, error }) - }); - return; - } + match res { + ExitCodeOr::ExitCode(exit_code) => exit_code.exit_process(), + ExitCodeOr::Codegen(codegen_results) => { + if sess.opts.unstable_opts.no_link { + let rlink_file = self.output_filenames.with_extension(config::RLINK_EXT); + CodegenResults::serialize_rlink( + sess, + &rlink_file, + &codegen_results, + &self.metadata, + &self.output_filenames, + ) + .unwrap_or_else(|error| { + sess.dcx().emit_fatal(FailedWritingFile { path: &rlink_file, error }) + }); + return; + } - let _timer = sess.prof.verbose_generic_activity("link_crate"); - let _timing = sess.timings.section_guard(sess.dcx(), TimingSection::Linking); - codegen_backend.link(sess, codegen_results, self.metadata, &self.output_filenames) + let _timer = sess.prof.verbose_generic_activity("link_crate"); + let _timing = sess.timings.section_guard(sess.dcx(), TimingSection::Linking); + codegen_backend.link(sess, codegen_results, self.metadata, &self.output_filenames) + } + } } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index eedb88783ec0e..c83a4a4aa9139 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2345,6 +2345,10 @@ pub struct EncodedMetadata { } impl EncodedMetadata { + pub fn empty() -> EncodedMetadata { + EncodedMetadata { full_metadata: None, stub_metadata: None, path: None, _temp_dir: None } + } + #[inline] pub fn from_path( path: PathBuf, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 21fa3321a30ad..92d63d2449a9f 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2444,6 +2444,8 @@ options! { `=skip-entry` `=skip-exit` Multiple options can be combined with commas."), + jit_mode: bool = (false, parse_bool, [TRACKED], + "enable JIT mode (only supported by some backends, default: no)"), layout_seed: Option = (None, parse_opt_number, [TRACKED], "seed layout randomization"), link_directives: bool = (true, parse_bool, [TRACKED], From 037f86f43001c1a685078563ea50a91febaad6e8 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 6 Feb 2025 17:21:00 +0000 Subject: [PATCH 2/4] Use -Zjit-mode in miri If cargo gets native support for -Zjit-mode, this should allow simplifying cargo-miri a fair bit. --- src/tools/miri/src/bin/miri.rs | 102 +++++++++++++++++++++++++++------ src/tools/miri/src/eval.rs | 17 +++--- 2 files changed, 93 insertions(+), 26 deletions(-) diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 19fbf90246c93..ea2c8731bcfac 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -18,6 +18,7 @@ extern crate rustc_hir_analysis; extern crate rustc_interface; extern crate rustc_log; extern crate rustc_middle; +extern crate rustc_query_system; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; @@ -38,9 +39,12 @@ extern crate tikv_jemalloc_sys as _; mod log; +use std::any::Any; +use std::cell::RefCell; use std::env; use std::num::{NonZero, NonZeroI32}; use std::ops::Range; +use std::process::ExitCode; use std::rc::Rc; use std::str::FromStr; use std::sync::Once; @@ -52,6 +56,8 @@ use miri::{ }; use rustc_abi::ExternAbi; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_codegen_ssa::{CodegenResults, TargetConfig}; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{self, DynSync}; use rustc_driver::Compilation; use rustc_hir::def_id::LOCAL_CRATE; @@ -67,8 +73,9 @@ use rustc_middle::middle::exported_symbols::{ 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, Options}; +use rustc_query_system::dep_graph::{WorkProduct, WorkProductId}; +use rustc_session::config::{CrateType, ErrorOutputType, OptLevel, Options, OutputFilenames}; +use rustc_session::{EarlyDiagCtxt, Session}; use rustc_span::def_id::DefId; use rustc_target::spec::Target; @@ -193,15 +200,64 @@ fn make_miri_codegen_backend(opts: &Options, target: &Target) -> Box( - &mut self, - _: &rustc_interface::interface::Compiler, - tcx: TyCtxt<'tcx>, - ) -> Compilation { +struct MiriCodegenBackend { + miri_config: RefCell>, + many_seeds: RefCell>, + target_config_override: Box TargetConfig>, +} + +impl CodegenBackend for MiriCodegenBackend { + fn name(&self) -> &'static str { + "miri" + } + + fn locale_resource(&self) -> &'static str { + "" + } + + fn target_config(&self, sess: &Session) -> TargetConfig { + (self.target_config_override)(sess) + } + + fn codegen_crate<'tcx>(&self, _tcx: TyCtxt<'tcx>) -> Box { + unreachable!() + } + + fn join_codegen( + &self, + _ongoing_codegen: Box, + _sess: &Session, + _outputs: &OutputFilenames, + ) -> (CodegenResults, FxIndexMap) { + unreachable!() + } + + fn jit_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, mut args: Vec) -> ExitCode { // Compilation is done, interpretation is starting. Deal with diagnostics from the // compilation part. We cannot call `sess.finish_diagnostics()` as then "aborting due to // previous errors" gets printed twice. @@ -219,9 +275,10 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { let (entry_def_id, entry_type) = entry_fn(tcx); // Obtain and complete the Miri configuration. - let mut config = self.miri_config.take().expect("after_analysis must only be called once"); + let config = self.miri_config.take().expect("after_analysis must only be called once"); + // Add filename to `miri` arguments. - config.args.insert(0, tcx.sess.io.input.filestem().to_string()); + args.insert(0, tcx.sess.io.input.filestem().to_string()); // Adjust working directory for interpretation. if let Some(cwd) = env::var_os("MIRI_CWD") { @@ -245,9 +302,9 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { // Invoke the interpreter. let res = if config.genmc_config.is_some() { - assert!(self.many_seeds.is_none()); + assert!(self.many_seeds.borrow().is_none()); run_genmc_mode(tcx, &config, |genmc_ctx: Rc| { - miri::eval_entry(tcx, entry_def_id, entry_type, &config, Some(genmc_ctx)) + miri::eval_entry(tcx, entry_def_id, entry_type, &args, &config, Some(genmc_ctx)) }) } else if let Some(many_seeds) = self.many_seeds.take() { assert!(config.seed.is_none()); @@ -255,10 +312,17 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { let mut config = config.clone(); config.seed = Some(seed); eprintln!("Trying seed: {seed}"); - miri::eval_entry(tcx, entry_def_id, entry_type, &config, /* genmc_ctx */ None) + miri::eval_entry( + tcx, + entry_def_id, + entry_type, + &args, + &config, + /* genmc_ctx */ None, + ) }) } else { - miri::eval_entry(tcx, entry_def_id, entry_type, &config, None) + miri::eval_entry(tcx, entry_def_id, entry_type, &args, &config, None) }; // Process interpreter result. if let Err(return_code) = res { @@ -486,6 +550,7 @@ fn main() { let mut rustc_args = vec![]; let mut after_dashdash = false; + let mut guest_args = vec![]; // Note that we require values to be given with `=`, not with a space. // This matches how rustc parses `-Z`. @@ -498,7 +563,7 @@ fn main() { rustc_args.extend(miri::MIRI_DEFAULT_ARGS.iter().map(ToString::to_string)); } else if after_dashdash { // Everything that comes after `--` is forwarded to the interpreted crate. - miri_config.args.push(arg); + guest_args.push(arg); } else if arg == "--" { after_dashdash = true; } else if arg == "-Zmiri-disable-validation" { @@ -745,7 +810,6 @@ fn main() { many_seeds.map(|seeds| ManySeedsConfig { seeds, keep_going: many_seeds_keep_going }); debug!("rustc arguments: {:?}", rustc_args); - debug!("crate arguments: {:?}", miri_config.args); if !miri_config.native_lib.is_empty() && miri_config.native_lib_enable_tracing { // SAFETY: No other threads are running #[cfg(all(unix, feature = "native-lib"))] @@ -756,5 +820,9 @@ fn main() { ); } } + rustc_args.push("-Zjit-mode".to_owned()); + rustc_args.push("--".to_owned()); + rustc_args.extend(guest_args); + run_compiler_and_exit(&rustc_args, &mut MiriCompilerCalls::new(miri_config, many_seeds)) } diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 0423b0ea5abdf..ddafa19a60152 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -54,8 +54,6 @@ pub struct MiriConfig { pub forwarded_env_vars: Vec, /// Additional environment variables that should be set in the interpreted program. pub set_env_vars: FxHashMap, - /// Command-line arguments passed to the interpreted program. - pub args: Vec, /// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`). pub seed: Option, /// The stacked borrows pointer ids to report about. @@ -130,7 +128,6 @@ impl Default for MiriConfig { ignore_leaks: false, forwarded_env_vars: vec![], set_env_vars: FxHashMap::default(), - args: vec![], seed: None, tracked_pointer_tags: FxHashSet::default(), tracked_alloc_ids: FxHashSet::default(), @@ -276,6 +273,7 @@ pub fn create_ecx<'tcx>( tcx: TyCtxt<'tcx>, entry_id: DefId, entry_type: MiriEntryFnType, + args: &[String], config: &MiriConfig, genmc_ctx: Option>, ) -> InterpResult<'tcx, InterpCx<'tcx, MiriMachine<'tcx>>> { @@ -299,12 +297,11 @@ pub fn create_ecx<'tcx>( } // Compute argc and argv from `config.args`. - let argc = - ImmTy::from_int(i64::try_from(config.args.len()).unwrap(), ecx.machine.layouts.isize); + let argc = ImmTy::from_int(i64::try_from(args.len()).unwrap(), ecx.machine.layouts.isize); let argv = { // Put each argument in memory, collect pointers. - let mut argvs = Vec::>::with_capacity(config.args.len()); - for arg in config.args.iter() { + let mut argvs = Vec::>::with_capacity(args.len()); + for arg in args.iter() { // Make space for `0` terminator. let size = u64::try_from(arg.len()).unwrap().strict_add(1); let arg_type = Ty::new_array(tcx, tcx.types.u8, size); @@ -342,7 +339,7 @@ pub fn create_ecx<'tcx>( // Store command line as UTF-16 for Windows `GetCommandLineW`. if tcx.sess.target.os == Os::Windows { // Construct a command string with all the arguments. - let cmd_utf16: Vec = args_to_utf16_command_string(config.args.iter()); + let cmd_utf16: Vec = args_to_utf16_command_string(args.iter()); let cmd_type = Ty::new_array(tcx, tcx.types.u16, u64::try_from(cmd_utf16.len()).unwrap()); @@ -458,13 +455,15 @@ pub fn eval_entry<'tcx>( tcx: TyCtxt<'tcx>, entry_id: DefId, entry_type: MiriEntryFnType, + args: &[String], config: &MiriConfig, genmc_ctx: Option>, ) -> Result<(), NonZeroI32> { // Copy setting before we move `config`. let ignore_leaks = config.ignore_leaks; - let mut ecx = match create_ecx(tcx, entry_id, entry_type, config, genmc_ctx).report_err() { + let mut ecx = match create_ecx(tcx, entry_id, entry_type, args, config, genmc_ctx).report_err() + { Ok(v) => v, Err(err) => { let (kind, backtrace) = err.into_parts(); From adff3c53a479e304f415d28ed1b2bfad542d93bd Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:46:25 +0000 Subject: [PATCH 3/4] Move -Zjit-mode from bin/miri.rs to cargo-miri --- src/tools/miri/cargo-miri/src/phases.rs | 2 ++ src/tools/miri/src/bin/miri.rs | 7 ++----- src/tools/miri/tests/ui.rs | 1 + 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 0f04397b72d22..b16838d0c7dcd 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -597,6 +597,8 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner cmd.args(args); } + cmd.arg("-Zjit-mode"); + // Then pass binary arguments. cmd.arg("--"); cmd.args(&binary_args); diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index ea2c8731bcfac..609cffb8c7be0 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -550,7 +550,6 @@ fn main() { let mut rustc_args = vec![]; let mut after_dashdash = false; - let mut guest_args = vec![]; // Note that we require values to be given with `=`, not with a space. // This matches how rustc parses `-Z`. @@ -563,9 +562,10 @@ fn main() { rustc_args.extend(miri::MIRI_DEFAULT_ARGS.iter().map(ToString::to_string)); } else if after_dashdash { // Everything that comes after `--` is forwarded to the interpreted crate. - guest_args.push(arg); + rustc_args.push(arg); } else if arg == "--" { after_dashdash = true; + rustc_args.push("--".to_owned()); } else if arg == "-Zmiri-disable-validation" { miri_config.validation = ValidationMode::No; } else if arg == "-Zmiri-recursive-validation" { @@ -820,9 +820,6 @@ fn main() { ); } } - rustc_args.push("-Zjit-mode".to_owned()); - rustc_args.push("--".to_owned()); - rustc_args.extend(guest_args); run_compiler_and_exit(&rustc_args, &mut MiriCompilerCalls::new(miri_config, many_seeds)) } diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 70739eef28838..a73282cc499be 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -197,6 +197,7 @@ fn run_tests( } } config.program.args.push("-Zui-testing".into()); + config.program.args.push("-Zjit-mode".into()); // If we're testing the native-lib functionality, then build the shared object file for testing // external C function calls and push the relevant compiler flag. From 275f29c17e699cdddc7fdfd18253e92358a3c732 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:51:03 +0000 Subject: [PATCH 4/4] fixup! fix run-compiler-twice test --- tests/ui-fulldeps/run-compiler-twice.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui-fulldeps/run-compiler-twice.rs b/tests/ui-fulldeps/run-compiler-twice.rs index 241d9e7efbd72..8b4c74678e3de 100644 --- a/tests/ui-fulldeps/run-compiler-twice.rs +++ b/tests/ui-fulldeps/run-compiler-twice.rs @@ -64,7 +64,7 @@ fn compile(code: String, output: PathBuf, sysroot: Sysroot, linker: Option<&Path output_dir: None, ice_file: None, file_loader: None, - locale_resources: Vec::new(), + locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), lint_caps: Default::default(), psess_created: None, hash_untracked_state: None, @@ -80,7 +80,7 @@ fn compile(code: String, output: PathBuf, sysroot: Sysroot, linker: Option<&Path let krate = rustc_interface::passes::parse(&compiler.sess); let linker = rustc_interface::create_and_enter_global_ctxt(&compiler, krate, |tcx| { let _ = tcx.analysis(()); - Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend) + Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend, vec![]) }); linker.link(&compiler.sess, &*compiler.codegen_backend); });