From b7885a469abd41fe7ef1f0b398cd2de35304534c 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 | 16 ++- compiler/rustc_interface/src/lib.rs | 1 + compiler/rustc_interface/src/passes.rs | 32 +++-- compiler/rustc_interface/src/queries.rs | 114 ++++++++++++------ compiler/rustc_metadata/src/rmeta/encoder.rs | 4 + compiler/rustc_session/src/options.rs | 2 + tests/ui-fulldeps/run-compiler-twice.rs | 2 +- 14 files changed, 170 insertions(+), 131 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 32f60615844bc..7c3379f0b2055 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; @@ -36,11 +37,7 @@ fn create_jit_module( (jit_module, cx) } -pub(crate) fn run_jit(tcx: TyCtxt<'_>, target_cpu: String, 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<'_>, target_cpu: String, jit_args: Vec) -> ExitCode { let output_filenames = tcx.output_filenames(()); let crate_info = CrateInfo::new(tcx, target_cpu); let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess); @@ -118,7 +115,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, target_cpu: String, 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 8b0ca770ec067..e01800cbb5967 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -31,8 +31,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; @@ -47,7 +47,6 @@ use rustc_session::{IncrCompSession, Session}; use rustc_span::{Symbol, sym}; use rustc_target::spec::{Arch, CfgAbi, Env, Os}; -pub use crate::config::*; use crate::prelude::*; mod abi; @@ -59,7 +58,6 @@ mod codegen_f16_f128; mod codegen_i128; mod common; mod compiler_builtins; -mod config; mod constant; mod debuginfo; mod discriminant; @@ -117,9 +115,7 @@ impl String> Drop for PrintOnPanic { } } -pub struct CraneliftCodegenBackend { - pub config: OnceCell, -} +pub struct CraneliftCodegenBackend; impl CodegenBackend for CraneliftCodegenBackend { fn name(&self) -> &'static str { @@ -139,15 +135,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 thin_lto_supported(&self) -> bool { @@ -215,16 +202,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, self.target_cpu(tcx.sess), config.jit_args.clone()); - #[cfg(not(feature = "jit"))] - tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); - } else { - Box::new(rustc_codegen_ssa::base::codegen_crate(driver::aot::AotDriver, 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)); } + + Box::new(rustc_codegen_ssa::base::codegen_crate(driver::aot::AotDriver, tcx)) } fn join_codegen( @@ -244,6 +232,29 @@ impl CodegenBackend for CraneliftCodegenBackend { fn fallback_intrinsics(&self) -> Vec { vec![sym::type_id_eq] } + + 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, self.target_cpu(&tcx.sess), 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. @@ -375,5 +386,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 36f4d858d0be5..efbecc05bab5a 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::sync::{DynSend, DynSync}; @@ -158,6 +159,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: Send + Sync + DynSend + DynSync { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index e2c9f3f909b8b..4da61fa207c80 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -182,7 +182,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 (matches, help_only) = match handle_options(&default_early_dcx, &args) { HandledOptions::None => return, @@ -203,6 +211,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 { @@ -328,7 +340,7 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) } } - let linker = Linker::codegen_and_build_linker(tcx, codegen_backend); + let linker = Linker::codegen_and_build_linker(tcx, codegen_backend, jit_args); tcx.report_unused_features(); diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 3bbe790af2e71..0b039e2a24aef 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 b059cae647341..81eb51eef5115 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}; @@ -1281,14 +1282,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, CrateInfo, 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(()) && find_attr!(tcx, def_id, RustcDelayedBugFromInsideQuery) @@ -1308,6 +1303,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, CrateInfo, EncodedMetadata) { + tcx.sess.timings.start_section(tcx.sess.dcx(), TimingSection::Codegen); + + pre_codegen_checks(tcx); info!("Pre-codegen\n{:?}", tcx.debug_stats()); @@ -1352,6 +1358,16 @@ pub(crate) fn start_codegen<'tcx>( (codegen, crate_info, 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 490888f87b38e..b65ba4fcf935d 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -1,4 +1,5 @@ use std::any::Any; +use std::process::ExitCode; use std::sync::Arc; use rustc_codegen_ssa::traits::CodegenBackend; @@ -15,6 +16,11 @@ use rustc_session::{IncrCompSession, Session}; use crate::diagnostics::FailedWritingFile; use crate::passes; +enum ExitCodeOr { + ExitCode(ExitCode), + Codegen(T), +} + pub struct Linker { dep_graph: DepGraph, output_filenames: Arc, @@ -22,15 +28,32 @@ pub struct Linker { crate_hash: Option, crate_info: CrateInfo, 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, crate_info, metadata) = passes::start_codegen(codegen_backend, tcx); + let (ongoing_codegen, crate_info, 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)), + CrateInfo::new(tcx, codegen_backend.target_cpu(&tcx.sess)), + EncodedMetadata::empty(), + ) + } else { + let (ongoing_codegen, crate_info, metadata) = + passes::start_codegen(codegen_backend, tcx); + (ExitCodeOr::Codegen(ongoing_codegen), crate_info, metadata) + }; Linker { dep_graph: tcx.dep_graph.clone(), @@ -52,20 +75,28 @@ impl Linker { incr_comp_session: Option, codegen_backend: &dyn CodegenBackend, ) { - let (compiled_modules, mut work_products) = sess.time("finish_ongoing_codegen", || { - match self.ongoing_codegen.downcast::() { - // This was a check only build - Ok(compiled_modules) => (*compiled_modules, WorkProductMap::default()), - - Err(ongoing_codegen) => codegen_backend.join_codegen( - ongoing_codegen, - sess, - incr_comp_session.as_ref(), - &self.output_filenames, - &self.crate_info, - ), + let (res, mut work_products) = match self.ongoing_codegen { + ExitCodeOr::ExitCode(exit_code) => { + (ExitCodeOr::ExitCode(exit_code), WorkProductMap::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(compiled_modules) => (*compiled_modules, WorkProductMap::default()), + + Err(ongoing_codegen) => codegen_backend.join_codegen( + ongoing_codegen, + sess, + incr_comp_session.as_ref(), + &self.output_filenames, + &self.crate_info, + ), + }; + + (ExitCodeOr::Codegen(codegen_results), work_products) + }), + }; if sess.codegen_units().as_usize() == 1 && sess.opts.unstable_opts.time_llvm_passes { codegen_backend.print_pass_timings() @@ -137,30 +168,35 @@ impl Linker { return; } - if sess.opts.unstable_opts.no_link { - let rlink_file = self.output_filenames.with_extension(config::RLINK_EXT); - CompiledModules::serialize_rlink( - sess, - &rlink_file, - &compiled_modules, - &self.crate_info, - &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(compiled_modules) => { + if sess.opts.unstable_opts.no_link { + let rlink_file = self.output_filenames.with_extension(config::RLINK_EXT); + CompiledModules::serialize_rlink( + sess, + &rlink_file, + &compiled_modules, + &self.crate_info, + &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, - compiled_modules, - self.crate_info, - 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, + compiled_modules, + self.crate_info, + self.metadata, + &self.output_filenames, + ) + } + } } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 228600fa76794..e703b5df107c6 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2380,6 +2380,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 18bee1b1ba7f1..c76c0906242ae 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2592,6 +2592,8 @@ options! { // stands out a lot more during code review making it easier to get caught. internal_testing_features: bool = (false, parse_bool, [TRACKED], "allow certain internal language features to be enabled that help exercise & test the compiler"), + jit_mode: bool = (false, parse_bool, [TRACKED], + "enable JIT mode (only supported by some backends, default: no)"), large_data_threshold: Option = (None, parse_opt_number, [TRACKED], "set the threshold for objects to be stored in a \"large data\" section \ (only effective with -Ccode-model=medium, default: 65536)"), diff --git a/tests/ui-fulldeps/run-compiler-twice.rs b/tests/ui-fulldeps/run-compiler-twice.rs index ae0f41a205bf4..1dd825fbc7dd2 100644 --- a/tests/ui-fulldeps/run-compiler-twice.rs +++ b/tests/ui-fulldeps/run-compiler-twice.rs @@ -79,7 +79,7 @@ fn compile(code: String, output: PathBuf, sysroot: Sysroot, linker: Option<&Path let (linker, incr_comp_session) = 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, incr_comp_session, &*compiler.codegen_backend); }); From acbd1a1269b16ecf0ca7a5aa99317d1a397779d7 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 | 168 +++++++++++++++++++-------------- src/tools/miri/src/eval.rs | 17 ++-- 2 files changed, 107 insertions(+), 78 deletions(-) diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index ae9a64b0abcf4..d261693696dd8 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -23,6 +23,7 @@ rustc_driver::override_c_allocator_in_binary!(); mod log; use std::any::Any; +use std::cell::RefCell; use std::env; use std::num::{NonZero, NonZeroI32}; use std::ops::Range; @@ -55,6 +56,8 @@ struct MiriCompilerCalls { } struct MiriCodegenBackend { + miri_config: RefCell>, + many_seeds: RefCell>, native: Box, dummy: DummyCodegenBackend, /// Whether we are in a dependency or in the to-be-interpreted binary crate @@ -107,7 +110,12 @@ 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: &Session, + dep: bool, + miri_config: Option, + many_seeds: Option, +) -> 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 @@ -120,24 +128,81 @@ fn make_miri_codegen_backend(sess: &Session, dep: bool) -> Box( - &mut self, - _: &rustc_interface::interface::Compiler, - tcx: TyCtxt<'tcx>, - ) -> Compilation { +impl CodegenBackend for MiriCodegenBackend { + fn name(&self) -> &'static str { + "miri" + } + + fn target_config(&self, sess: &Session) -> TargetConfig { + let native_target_config = self.native.target_config(sess); + TargetConfig { + internal_target_features: native_target_config.internal_target_features, + + // The basic types and ABI always work. + has_reliable_f16: true, + has_reliable_f128: true, + // We always provide the f16 intrinsics, but some are provided via the host, + // so forward its reliability. + has_reliable_f16_math: cfg!(target_has_reliable_f16_math), + // Many f128 operations are still missing. + has_reliable_f128_math: false, + } + } + + fn target_cpu(&self, _sess: &Session) -> String { + String::new() + } + + // Everything complicated is forwarded to the dummy backend. + + fn supported_crate_types(&self, sess: &Session) -> Vec { + self.dummy.supported_crate_types(sess) + } + + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { + if self.dep { self.dummy.codegen_crate(tcx) } else { unreachable!() } + } + + fn join_codegen( + &self, + ongoing_codegen: Box, + sess: &Session, + incr_comp_session: Option<&rustc_session::IncrCompSession>, + outputs: &rustc_session::config::OutputFilenames, + crate_info: &CrateInfo, + ) -> (CompiledModules, rustc_middle::dep_graph::WorkProductMap) { + if self.dep { + self.dummy.join_codegen(ongoing_codegen, sess, incr_comp_session, outputs, crate_info) + } else { + 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. @@ -157,7 +222,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { // Obtain and complete the Miri configuration. let mut 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") { @@ -183,7 +248,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { let res = if config.genmc_config.is_some() { assert!(self.many_seeds.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()); @@ -191,68 +256,26 @@ 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 { tcx.dcx().abort_if_errors(); - exit(return_code.get()) + exit(return_code.get()); } else { - // We want to continue here so rustc can do its usual shutdown and finalize the - // incremental session. Our custom codegen backend ensures nothing actually happens. - Compilation::Continue + ExitCode::SUCCESS } } -} - -impl CodegenBackend for MiriCodegenBackend { - fn name(&self) -> &'static str { - "miri" - } - - fn target_config(&self, sess: &Session) -> TargetConfig { - let native_target_config = self.native.target_config(sess); - TargetConfig { - internal_target_features: native_target_config.internal_target_features, - - // The basic types and ABI always work. - has_reliable_f16: true, - has_reliable_f128: true, - // We always provide the f16 intrinsics, but some are provided via the host, - // so forward its reliability. - has_reliable_f16_math: cfg!(target_has_reliable_f16_math), - // Many f128 operations are still missing. - has_reliable_f128_math: false, - } - } - - fn target_cpu(&self, _sess: &Session) -> String { - String::new() - } - - // Everything complicated is forwarded to the dummy backend. - - fn supported_crate_types(&self, sess: &Session) -> Vec { - self.dummy.supported_crate_types(sess) - } - - fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { - self.dummy.codegen_crate(tcx) - } - - fn join_codegen( - &self, - ongoing_codegen: Box, - sess: &Session, - incr_comp_session: Option<&rustc_session::IncrCompSession>, - outputs: &rustc_session::config::OutputFilenames, - crate_info: &CrateInfo, - ) -> (CompiledModules, rustc_middle::dep_graph::WorkProductMap) { - self.dummy.join_codegen(ongoing_codegen, sess, incr_comp_session, outputs, crate_info) - } fn link( &self, @@ -276,8 +299,11 @@ impl rustc_driver::Callbacks for MiriDepCompilerCalls { #[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) { // We don't need actual codegen, we just emit an rlib that Miri can later consume. - config.make_codegen_backend = - Some(Box::new(|sess| make_miri_codegen_backend(sess, /* dep */ true))); + config.make_codegen_backend = Some(Box::new(|sess| { + make_miri_codegen_backend( + sess, /* dep */ true, /* miri_config */ None, /* many_seeds */ None, + ) + })); // 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 @@ -446,6 +472,7 @@ fn main() -> ExitCode { 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`. @@ -458,7 +485,7 @@ fn main() -> ExitCode { 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" { @@ -731,7 +758,6 @@ fn main() -> ExitCode { 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(feature = "native-lib", unix))] @@ -742,6 +768,10 @@ fn main() -> ExitCode { ); } } + 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)) // Note that we *cannot* just return here, in native-lib mode we have to coordinate // with the supervisor process! diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 3ba16971eca84..25eb26cca9333 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -109,8 +109,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. @@ -183,7 +181,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(), @@ -328,6 +325,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>>> { @@ -357,12 +355,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); @@ -400,7 +397,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()); @@ -516,13 +513,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 bad4a30e284e1025cbee863f50b58c92ded21ff1 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 50a0f671aca4d..326a07a724a9e 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -623,6 +623,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 d261693696dd8..58d3da0c50062 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -472,7 +472,6 @@ fn main() -> ExitCode { 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`. @@ -485,9 +484,10 @@ fn main() -> ExitCode { 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" { @@ -768,9 +768,6 @@ fn main() -> ExitCode { ); } } - 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)) // Note that we *cannot* just return here, in native-lib mode we have to coordinate diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index b2fda8e0c62c9..9696aff69f642 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -305,6 +305,7 @@ fn run_tests( config.program.args.push(flag.into()); } } + config.program.args.push("-Zjit-mode".into()); } // If we're testing the native-lib functionality, then build the shared object file for testing From cbe15a3caee1b3e8dd9bed5a3980f16f88443802 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 24 Jun 2026 12:30:17 +0000 Subject: [PATCH 4/4] Improve test failure message a bit --- src/tools/miri/tests/ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 9696aff69f642..d496dc8a9e58c 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -414,7 +414,7 @@ fn ui( WithoutDeps => false, }; run_tests(mode, path, target, with_dependencies, tmpdir) - .with_context(|| format!("ui tests in {path} for {target} failed")) + .with_context(|| format!("{mode} ui tests in {path} for {target} failed")) } fn get_host() -> String {