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 7d6ece02e4a62..b0aad41b469c7 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 af783f31a2136..cfec820adcdd4 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; @@ -46,7 +46,6 @@ use rustc_session::config::OutputFilenames; use rustc_span::{Symbol, sym}; use rustc_target::spec::{Arch, CfgAbi, Env, Os}; -pub use crate::config::*; use crate::prelude::*; mod abi; @@ -58,7 +57,6 @@ mod codegen_f16_f128; mod codegen_i128; mod common; mod compiler_builtins; -mod config; mod constant; mod debuginfo; mod discriminant; @@ -116,9 +114,7 @@ impl String> Drop for PrintOnPanic { } } -pub struct CraneliftCodegenBackend { - pub config: OnceCell, -} +pub struct CraneliftCodegenBackend; impl CodegenBackend for CraneliftCodegenBackend { fn name(&self) -> &'static str { @@ -138,15 +134,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 { @@ -217,16 +204,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( @@ -245,6 +233,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. @@ -376,5 +387,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 6014f1af4bfc3..56d829437d7fe 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 31353d1eac0fb..1b8ee50de5743 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -181,7 +181,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, @@ -202,6 +210,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 { @@ -339,7 +351,8 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) } } - let linker = Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend); + let linker = + Linker::codegen_and_build_linker(tcx, &*compiler.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 38ab1ef8dedf7..f5328ea0742b0 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}; @@ -1264,14 +1265,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) @@ -1291,6 +1286,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()); @@ -1321,6 +1327,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 025a5a55d0abd..6cbcc6b0b6d3e 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::config::{self, OutputFilenames, OutputType}; 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(), @@ -47,19 +70,27 @@ impl Linker { } pub fn link(self, sess: &Session, 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, - &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, + &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() @@ -123,30 +154,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 c232f595d4229..ef40de69eb06c 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2368,6 +2368,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 897aa91f7dbda..07544fa90f8f7 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2473,6 +2473,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/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index f58cec827cf5d..1aab6791ec708 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -594,6 +594,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 442f96ffaa216..19e73dfeb5676 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -33,6 +33,8 @@ 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; @@ -46,19 +48,21 @@ use miri::{ TreeBorrowsParams, ValidationMode, entry_fn, run_genmc_mode, }; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig}; use rustc_data_structures::sync::{self, DynSync}; use rustc_driver::Compilation; use rustc_hir::{self as hir, Node}; use rustc_interface::interface::Config; use rustc_interface::util::DummyCodegenBackend; use rustc_log::tracing::debug; +use rustc_middle::dep_graph::WorkProductMap; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::exported_symbols::{ ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel, }; use rustc_middle::query::LocalCrate; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{CrateType, ErrorOutputType, OptLevel}; +use rustc_session::config::{CrateType, ErrorOutputType, OptLevel, OutputFilenames}; use rustc_session::{EarlyDiagCtxt, Session}; use crate::log::setup::{deinit_loggers, init_early_loggers, init_late_loggers}; @@ -144,19 +148,81 @@ fn make_miri_codegen_backend(sess: &Session) -> Box { } impl rustc_driver::Callbacks for MiriCompilerCalls { - fn config(&mut self, config: &mut rustc_interface::interface::Config) { + fn config(&mut self, config: &mut Config) { + let mut miri_config = self.miri_config.take(); + let mut many_seeds = self.many_seeds.take(); + // We never reach codegen anyway. - config.make_codegen_backend = Some(Box::new(make_miri_codegen_backend)); + config.make_codegen_backend = Some(Box::new(move |sess| { + let early_dcx = EarlyDiagCtxt::new(sess.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, + &sess.opts.sysroot, + None, + &sess.target, + ); + target_config_backend.init(sess); + + Box::new(MiriCodegenBackend { + miri_config: RefCell::new(miri_config.take()), + many_seeds: RefCell::new(many_seeds.take()), + target_config_override: Box::new(move |sess| { + let mut cfg = target_config_backend.target_config(sess); + // The basic types and ABI always work. + cfg.has_reliable_f16 = true; + cfg.has_reliable_f128 = true; + // We always provide the f16 intrinsics, but some are provided via the host, + // so forward its reliability. + cfg.has_reliable_f16_math = cfg!(target_has_reliable_f16_math); + // Many f128 operations are still missing. + cfg.has_reliable_f128_math = false; + cfg + }), + }) + })); // Register our custom extra symbols. config.extra_symbols = miri::sym::EXTRA_SYMBOLS.into(); } +} - fn after_analysis<'tcx>( - &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 target_config(&self, sess: &Session) -> TargetConfig { + (self.target_config_override)(sess) + } + + fn target_cpu(&self, _sess: &Session) -> String { + "miri".to_owned() + } + + fn codegen_crate<'tcx>(&self, _tcx: TyCtxt<'tcx>) -> Box { + unreachable!() + } + + fn join_codegen( + &self, + _ongoing_codegen: Box, + _sess: &Session, + _outputs: &OutputFilenames, + _crate_info: &CrateInfo, + ) -> (CompiledModules, WorkProductMap) { + 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. @@ -174,9 +240,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") { @@ -200,9 +267,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()); @@ -210,10 +277,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 { @@ -459,9 +533,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. - miri_config.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" { @@ -734,7 +809,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))] @@ -745,5 +819,6 @@ fn main() -> ExitCode { ); } } + 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 19b6ff75f95f8..b17705e82fb5b 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. @@ -185,7 +183,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(), @@ -331,6 +328,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>>> { @@ -360,12 +358,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); @@ -403,7 +400,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()); @@ -519,13 +516,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(); diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 633452f6052d7..e34242d1b2d69 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -304,6 +304,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 @@ -412,7 +413,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 { diff --git a/tests/ui-fulldeps/run-compiler-twice.rs b/tests/ui-fulldeps/run-compiler-twice.rs index d99d9c42d547f..ff3925171c684 100644 --- a/tests/ui-fulldeps/run-compiler-twice.rs +++ b/tests/ui-fulldeps/run-compiler-twice.rs @@ -78,7 +78,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); });