diff --git a/compiler/rustc_codegen_llvm/src/back/command_line_args.rs b/compiler/rustc_codegen_llvm/src/back/command_line_args.rs deleted file mode 100644 index b14713969b34c..0000000000000 --- a/compiler/rustc_codegen_llvm/src/back/command_line_args.rs +++ /dev/null @@ -1,37 +0,0 @@ -#[cfg(test)] -mod tests; - -/// Joins command-line arguments into a single space-separated string, quoting -/// and escaping individual arguments as necessary. -/// -/// The result is intended to be informational, for embedding in debug metadata, -/// and might not be properly quoted/escaped for actual command-line use. -pub(crate) fn quote_command_line_args(args: &[String]) -> String { - // Start with a decent-sized buffer, since rustc invocations tend to be long. - let mut buf = String::with_capacity(128); - - for arg in args { - if !buf.is_empty() { - buf.push(' '); - } - - print_arg_quoted(&mut buf, arg); - } - - buf -} - -/// Equivalent to LLVM's `sys::printArg` with quoting always enabled -/// (see llvm/lib/Support/Program.cpp). -fn print_arg_quoted(buf: &mut String, arg: &str) { - buf.reserve(arg.len() + 2); - - buf.push('"'); - for ch in arg.chars() { - if matches!(ch, '"' | '\\' | '$') { - buf.push('\\'); - } - buf.push(ch); - } - buf.push('"'); -} diff --git a/compiler/rustc_codegen_llvm/src/back/mod.rs b/compiler/rustc_codegen_llvm/src/back/mod.rs index fe3883e8c73e6..6cb89f80ab89a 100644 --- a/compiler/rustc_codegen_llvm/src/back/mod.rs +++ b/compiler/rustc_codegen_llvm/src/back/mod.rs @@ -1,5 +1,4 @@ pub(crate) mod archive; -mod command_line_args; pub(crate) mod lto; pub(crate) mod owned_target_machine; mod profiling; diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 1950b8288d149..98fb4a5134906 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -22,6 +22,7 @@ use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::small_c_str::SmallCStr; use rustc_errors::{DiagCtxtHandle, Level}; use rustc_fs_util::{link_or_copy, path_to_c_string}; +use rustc_middle::middle::debuginfo::CommandLineArgsForDebuginfo; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{ @@ -31,7 +32,6 @@ use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext, sym}; use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::{debug, trace}; -use crate::back::command_line_args::quote_command_line_args; use crate::back::lto::ThinBuffer; use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::profiling::{ @@ -108,7 +108,11 @@ pub(crate) fn create_informational_target_machine( sess: &Session, only_base_features: bool, ) -> OwnedTargetMachine { - let config = TargetMachineFactoryConfig { split_dwarf_file: None, output_obj_file: None }; + let config = TargetMachineFactoryConfig { + split_dwarf_file: None, + output_obj_file: None, + args_for_debuginfo: None, + }; // Can't use query system here quite yet because this function is invoked before the query // system/tcx is set up. let features = llvm_util::global_llvm_features(sess, false, only_base_features); @@ -133,7 +137,12 @@ pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTar mod_name, tcx.sess.invocation_temp.as_deref(), )); - let config = TargetMachineFactoryConfig { split_dwarf_file, output_obj_file }; + + let config = TargetMachineFactoryConfig { + split_dwarf_file, + output_obj_file, + args_for_debuginfo: Some(Arc::clone(tcx.args_for_debuginfo())), + }; target_machine_factory( tcx.sess, @@ -253,19 +262,6 @@ pub(crate) fn target_machine_factory( let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated); - // Command-line information to be included in the target machine. - // This seems to only be used for embedding in PDB debuginfo files. - // FIXME(Zalathar): Maybe skip this for non-PDB targets? - let argv0 = std::env::current_exe() - .unwrap_or_default() - .into_os_string() - .into_string() - .unwrap_or_default(); - let command_line_args = quote_command_line_args(&sess.expanded_args); - // Self-profile counter for the number of bytes produced by command-line quoting. - // Values are summed, so the summary result is cumulative across all TM factories. - sess.prof.artifact_size("quoted_command_line_args", "-", command_line_args.len() as u64); - let debuginfo_compression = sess.opts.debuginfo_compression.to_string(); match sess.opts.debuginfo_compression { rustc_session::config::DebugInfoCompression::Zlib => { @@ -304,6 +300,14 @@ pub(crate) fn target_machine_factory( let split_dwarf_file = path_to_cstring_helper(config.split_dwarf_file); let output_obj_file = path_to_cstring_helper(config.output_obj_file); + let (argv0, quoted_args) = config + .args_for_debuginfo + .as_deref() + .map(|CommandLineArgsForDebuginfo { argv0, quoted_args }| { + (argv0.as_str(), quoted_args.as_str()) + }) + .unwrap_or_default(); + OwnedTargetMachine::new( &triple, &cpu, @@ -326,8 +330,8 @@ pub(crate) fn target_machine_factory( &output_obj_file, &debuginfo_compression, use_emulated_tls, - &argv0, - &command_line_args, + argv0, + quoted_args, use_wasm_eh, ) }) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index cbaf67d734547..237aefa13cd9d 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -25,6 +25,7 @@ use rustc_incremental::{ use rustc_metadata::fs::copy_to_stdout; use rustc_middle::bug; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; +use rustc_middle::middle::debuginfo::CommandLineArgsForDebuginfo; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{ @@ -283,6 +284,10 @@ pub struct TargetMachineFactoryConfig { /// The name of the output object file. Used for setting OutputFilenames in target options /// so that LLVM can emit the CodeView S_OBJNAME record in pdb files pub output_obj_file: Option, + + /// Contains a copy of [`TyCtxt::args_for_debuginfo`] for codegen, + /// or `None` if the target machine is informational only. + pub args_for_debuginfo: Option>, } impl TargetMachineFactoryConfig { @@ -306,7 +311,12 @@ impl TargetMachineFactoryConfig { module_name, cgcx.invocation_temp.as_deref(), )); - TargetMachineFactoryConfig { split_dwarf_file, output_obj_file } + + TargetMachineFactoryConfig { + split_dwarf_file, + output_obj_file, + args_for_debuginfo: Some(Arc::clone(&cgcx.args_for_debuginfo)), + } } } @@ -350,7 +360,7 @@ pub struct CodegenContext { /// This will only be used within debug info, e.g. in the pdb file on windows /// This is mainly useful for other tools that reads that debuginfo to figure out /// how to call the compiler with the same arguments. - pub expanded_args: Vec, + pub args_for_debuginfo: Arc, /// Emitter to use for diagnostics produced during codegen. pub diag_emitter: SharedEmitter, @@ -1153,7 +1163,7 @@ fn start_executing_work( remark: sess.opts.cg.remark.clone(), remark_dir, incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), - expanded_args: tcx.sess.expanded_args.clone(), + args_for_debuginfo: Arc::clone(tcx.args_for_debuginfo()), diag_emitter: shared_emitter.clone(), output_filenames: Arc::clone(tcx.output_filenames(())), module_config: regular_config, diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/command_line_args/mod.rs b/compiler/rustc_codegen_ssa/src/debuginfo/command_line_args/mod.rs new file mode 100644 index 0000000000000..c22587e1302a5 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/debuginfo/command_line_args/mod.rs @@ -0,0 +1,67 @@ +use std::sync::Arc; + +use rustc_middle::middle::debuginfo::CommandLineArgsForDebuginfo; +use rustc_middle::ty::TyCtxt; +use rustc_middle::util::Providers; + +#[cfg(test)] +mod tests; + +pub(crate) fn provide(providers: &mut Providers) { + providers.hooks.args_for_debuginfo = args_for_debuginfo; +} + +/// Hook implementation for [`TyCtxt::args_for_debuginfo`]. +fn args_for_debuginfo<'tcx>(tcx: TyCtxt<'tcx>) -> &'tcx Arc { + tcx.args_for_debuginfo_cache.get_or_init(|| { + // Command-line information to be included in the target machine. + // This seems to only be used for embedding in PDB debuginfo files. + // FIXME(Zalathar): Maybe skip this for non-PDB targets? + let argv0 = std::env::current_exe() + .unwrap_or_default() + .into_os_string() + .into_string() + .unwrap_or_default(); + let quoted_args = quote_command_line_args(&tcx.sess.expanded_args); + + // Self-profile counter for the number of bytes produced by command-line quoting. + tcx.prof.artifact_size("quoted_command_line_args", "-", quoted_args.len() as u64); + + Arc::new(CommandLineArgsForDebuginfo { argv0, quoted_args }) + }) +} + +/// Joins command-line arguments into a single space-separated string, quoting +/// and escaping individual arguments as necessary. +/// +/// The result is intended to be informational, for embedding in debug metadata, +/// and might not be properly quoted/escaped for actual command-line use. +fn quote_command_line_args(args: &[String]) -> String { + // Start with a decent-sized buffer, since rustc invocations tend to be long. + let mut buf = String::with_capacity(128); + + for arg in args { + if !buf.is_empty() { + buf.push(' '); + } + + print_arg_quoted(&mut buf, arg); + } + + buf +} + +/// Equivalent to LLVM's `sys::printArg` with quoting always enabled +/// (see llvm/lib/Support/Program.cpp). +fn print_arg_quoted(buf: &mut String, arg: &str) { + buf.reserve(arg.len() + 2); + + buf.push('"'); + for ch in arg.chars() { + if matches!(ch, '"' | '\\' | '$') { + buf.push('\\'); + } + buf.push(ch); + } + buf.push('"'); +} diff --git a/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs b/compiler/rustc_codegen_ssa/src/debuginfo/command_line_args/tests.rs similarity index 100% rename from compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs rename to compiler/rustc_codegen_ssa/src/debuginfo/command_line_args/tests.rs diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs b/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs index 7c62c03d574c1..22de7310e17aa 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs @@ -3,7 +3,7 @@ use rustc_middle::bug; use rustc_middle::ty::layout::{IntegerExt, PrimitiveExt, TyAndLayout}; use rustc_middle::ty::{self, Ty, TyCtxt}; -// FIXME(eddyb) find a place for this (or a way to replace it). +pub(crate) mod command_line_args; pub mod type_names; /// Returns true if we want to generate a DW_TAG_enumeration_type description for diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 18279a4d05fe0..f101315b4a8df 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -1,5 +1,7 @@ //! Type Names for Debug Info. +// FIXME(eddyb) find a place for this (or a way to replace it). + // Notes on targeting MSVC: // In general, MSVC's debugger attempts to parse all arguments as C++ expressions, // even if the argument is explicitly a symbol name. diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index baba8f9ca3e8b..bfd7cf254d2d8 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -271,6 +271,7 @@ pub enum CodegenErrors { pub fn provide(providers: &mut Providers) { crate::back::symbol_export::provide(providers); crate::base::provide(providers); + crate::debuginfo::command_line_args::provide(providers); crate::target_features::provide(providers); crate::codegen_attrs::provide(providers); providers.queries.global_backend_features = |_tcx: TyCtxt<'_>, ()| vec![]; diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 9d2f0a4523713..9948e0a95467b 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -3,11 +3,14 @@ //! similar to queries, but queries come with a lot of machinery for caching and incremental //! compilation, whereas hooks are just plain function pointers without any of the query magic. +use std::sync::Arc; + use rustc_hir::def_id::{DefId, DefPathHash}; use rustc_session::StableCrateId; use rustc_span::def_id::{CrateNum, LocalDefId}; use rustc_span::{ExpnHash, ExpnId}; +use crate::middle::debuginfo::CommandLineArgsForDebuginfo; use crate::mir; use crate::ty::{Ty, TyCtxt}; @@ -102,6 +105,13 @@ declare_hooks! { /// Ensure the given scalar is valid for the given type. /// This checks non-recursive runtime validity. hook validate_scalar_in_layout(scalar: crate::ty::ScalarInt, ty: Ty<'tcx>) -> bool; + + /// Builds a quoted form of _all_ command-line args, for inclusion in some + /// forms of debuginfo (specifically PDB). + /// + /// This needs to _not_ be a query, because it would ruin incremental + /// compilation for CGUs that would otherwise be considered up-to-date. + hook args_for_debuginfo() -> &'tcx Arc; } #[cold] diff --git a/compiler/rustc_middle/src/middle/debuginfo.rs b/compiler/rustc_middle/src/middle/debuginfo.rs new file mode 100644 index 0000000000000..ea002de03f38b --- /dev/null +++ b/compiler/rustc_middle/src/middle/debuginfo.rs @@ -0,0 +1,5 @@ +#[derive(Debug)] +pub struct CommandLineArgsForDebuginfo { + pub argv0: String, + pub quoted_args: String, +} diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs index 6ca1e6207042a..f6c256daede53 100644 --- a/compiler/rustc_middle/src/middle/mod.rs +++ b/compiler/rustc_middle/src/middle/mod.rs @@ -1,5 +1,6 @@ pub mod codegen_fn_attrs; pub mod debugger_visualizer; +pub mod debuginfo; pub mod dependency_format; pub mod exported_symbols; pub mod lang_items; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 7d3e2c9965dad..98d7a59ec293c 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -66,6 +66,7 @@ use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind, Canonica use crate::lint::lint_level; use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature}; +use crate::middle::debuginfo::CommandLineArgsForDebuginfo; use crate::middle::resolve_bound_vars; use crate::mir::interpret::{self, Allocation, ConstAllocation}; use crate::mir::{Body, Local, Place, PlaceElem, ProjectionKind, Promoted}; @@ -1593,6 +1594,10 @@ pub struct GlobalCtxt<'tcx> { /// A jobserver reference used to release then acquire a token while waiting on a query. pub jobserver_proxy: Arc, + + /// Memoization slot for the [`TyCtxt::args_for_debuginfo`] hook, which needs + /// to not be a query, but should still be cached after being computed once. + pub args_for_debuginfo_cache: OnceLock>, } impl<'tcx> GlobalCtxt<'tcx> { @@ -1818,6 +1823,7 @@ impl<'tcx> TyCtxt<'tcx> { alloc_map: interpret::AllocMap::new(), current_gcx, jobserver_proxy, + args_for_debuginfo_cache: OnceLock::new(), }); // This is a separate function to work around a crash with parallel rustc (#135870)