diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 491d6cbbf7969..78d6ff0cb001c 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -75,6 +75,7 @@ fn emit_module( name, kind, object: Some(tmp_file), + dwarf_object: None, bytecode: None, }, work_product, @@ -111,6 +112,7 @@ fn reuse_workproduct_for_cgu( name: cgu.name().to_string(), kind: ModuleKind::Regular, object, + dwarf_object: None, bytecode: None, } } @@ -290,6 +292,7 @@ pub(super) fn run_aot( name: metadata_cgu_name, kind: ModuleKind::Metadata, object: Some(tmp_file), + dwarf_object: None, bytecode: None, }) } else { diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 64fd1d09cc24f..29415973ed073 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -6,7 +6,9 @@ use crate::llvm::{self, build_string, False, True}; use crate::{LlvmCodegenBackend, ModuleLlvm}; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::symbol_export; -use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig}; +use rustc_codegen_ssa::back::write::{ + CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig, +}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind}; use rustc_data_structures::fx::FxHashMap; @@ -728,7 +730,14 @@ pub unsafe fn optimize_thin_module( cgcx: &CodegenContext, ) -> Result, FatalError> { let diag_handler = cgcx.create_diag_handler(); - let tm = (cgcx.tm_factory.0)().map_err(|e| write::llvm_err(&diag_handler, &e))?; + + let module_name = &thin_module.shared.module_names[thin_module.idx]; + let split_dwarf_file = cgcx + .output_filenames + .split_dwarf_filename(cgcx.split_dwarf_kind, Some(module_name.to_str().unwrap())); + let tm_factory_config = TargetMachineFactoryConfig { split_dwarf_file }; + let tm = + (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&diag_handler, &e))?; // Right now the implementation we've got only works over serialized // modules, so we create a fresh new LLVM context and parse the module @@ -736,12 +745,8 @@ pub unsafe fn optimize_thin_module( // crates but for locally codegened modules we may be able to reuse // that LLVM Context and Module. let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); - let llmod_raw = parse_module( - llcx, - &thin_module.shared.module_names[thin_module.idx], - thin_module.data(), - &diag_handler, - )? as *const _; + let llmod_raw = + parse_module(llcx, &module_name, thin_module.data(), &diag_handler)? as *const _; let module = ModuleCodegen { module_llvm: ModuleLlvm { llmod_raw, llcx, tm }, name: thin_module.name().to_string(), diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 7407dfc455d84..3fda1e26dae9e 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -11,7 +11,10 @@ use crate::llvm_util; use crate::type_::Type; use crate::LlvmCodegenBackend; use crate::ModuleLlvm; -use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig}; +use rustc_codegen_ssa::back::write::{ + BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig, + TargetMachineFactoryFn, +}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; use rustc_data_structures::small_c_str::SmallCStr; @@ -20,7 +23,9 @@ use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{self, Lto, OutputType, Passes, SanitizerSet, SwitchWithOptPath}; +use rustc_session::config::{ + self, Lto, OutputType, Passes, SanitizerSet, SplitDwarfKind, SwitchWithOptPath, +}; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::InnerSpan; @@ -49,11 +54,31 @@ pub fn write_output_file( pm: &llvm::PassManager<'ll>, m: &'ll llvm::Module, output: &Path, + dwo_output: Option<&Path>, file_type: llvm::FileType, ) -> Result<(), FatalError> { unsafe { let output_c = path_to_c_string(output); - let result = llvm::LLVMRustWriteOutputFile(target, pm, m, output_c.as_ptr(), file_type); + let result = if let Some(dwo_output) = dwo_output { + let dwo_output_c = path_to_c_string(dwo_output); + llvm::LLVMRustWriteOutputFile( + target, + pm, + m, + output_c.as_ptr(), + dwo_output_c.as_ptr(), + file_type, + ) + } else { + llvm::LLVMRustWriteOutputFile( + target, + pm, + m, + output_c.as_ptr(), + std::ptr::null(), + file_type, + ) + }; result.into_result().map_err(|()| { let msg = format!("could not write output to {}", output.display()); llvm_err(handler, &msg) @@ -62,12 +87,17 @@ pub fn write_output_file( } pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm::TargetMachine { - target_machine_factory(sess, config::OptLevel::No)() + let config = TargetMachineFactoryConfig { split_dwarf_file: None }; + target_machine_factory(sess, config::OptLevel::No)(config) .unwrap_or_else(|err| llvm_err(sess.diagnostic(), &err).raise()) } -pub fn create_target_machine(tcx: TyCtxt<'_>) -> &'static mut llvm::TargetMachine { - target_machine_factory(&tcx.sess, tcx.backend_optimization_level(LOCAL_CRATE))() +pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut llvm::TargetMachine { + let split_dwarf_file = tcx + .output_filenames(LOCAL_CRATE) + .split_dwarf_filename(tcx.sess.opts.debugging_opts.split_dwarf, Some(mod_name)); + let config = TargetMachineFactoryConfig { split_dwarf_file }; + target_machine_factory(&tcx.sess, tcx.backend_optimization_level(LOCAL_CRATE))(config) .unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), &err).raise()) } @@ -122,7 +152,7 @@ fn to_llvm_code_model(code_model: Option) -> llvm::CodeModel { pub fn target_machine_factory( sess: &Session, optlvl: config::OptLevel, -) -> Arc Result<&'static mut llvm::TargetMachine, String> + Send + Sync> { +) -> TargetMachineFactoryFn { let reloc_model = to_llvm_relocation_model(sess.relocation_model()); let (opt_level, _) = to_llvm_opt_settings(optlvl); @@ -163,7 +193,10 @@ pub fn target_machine_factory( let use_init_array = !sess.opts.debugging_opts.use_ctors_section.unwrap_or(sess.target.use_ctors_section); - Arc::new(move || { + Arc::new(move |config: TargetMachineFactoryConfig| { + let split_dwarf_file = config.split_dwarf_file.unwrap_or_default(); + let split_dwarf_file = CString::new(split_dwarf_file.to_str().unwrap()).unwrap(); + let tm = unsafe { llvm::LLVMRustCreateTargetMachine( triple.as_ptr(), @@ -182,6 +215,7 @@ pub fn target_machine_factory( emit_stack_size_section, relax_elf_relocations, use_init_array, + split_dwarf_file.as_ptr(), ) }; @@ -785,7 +819,15 @@ pub(crate) unsafe fn codegen( llmod }; with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file(diag_handler, tm, cpm, llmod, &path, llvm::FileType::AssemblyFile) + write_output_file( + diag_handler, + tm, + cpm, + llmod, + &path, + None, + llvm::FileType::AssemblyFile, + ) })?; } @@ -794,6 +836,15 @@ pub(crate) unsafe fn codegen( let _timer = cgcx .prof .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]); + + let dwo_out = cgcx.output_filenames.temp_path_dwo(module_name); + let dwo_out = match cgcx.split_dwarf_kind { + // Don't change how DWARF is emitted in single mode (or when disabled). + SplitDwarfKind::None | SplitDwarfKind::Single => None, + // Emit (a subset of the) DWARF into a separate file in split mode. + SplitDwarfKind::Split => Some(dwo_out.as_path()), + }; + with_codegen(tm, llmod, config.no_builtins, |cpm| { write_output_file( diag_handler, @@ -801,6 +852,7 @@ pub(crate) unsafe fn codegen( cpm, llmod, &obj_out, + dwo_out, llvm::FileType::ObjectFile, ) })?; @@ -828,6 +880,7 @@ pub(crate) unsafe fn codegen( Ok(module.into_compiled_module( config.emit_obj != EmitObj::None, + cgcx.split_dwarf_kind == SplitDwarfKind::Split, config.emit_bc, &cgcx.output_filenames, )) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 31e43893ac8b1..fa285f3488f84 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -993,9 +993,15 @@ pub fn compile_unit_metadata( let producer = format!("clang LLVM ({})", rustc_producer); let name_in_debuginfo = name_in_debuginfo.to_string_lossy(); - let work_dir = tcx.sess.working_dir.0.to_string_lossy(); let flags = "\0"; - let split_name = ""; + + let out_dir = &tcx.output_filenames(LOCAL_CRATE).out_directory; + let split_name = tcx + .output_filenames(LOCAL_CRATE) + .split_dwarf_filename(tcx.sess.opts.debugging_opts.split_dwarf, Some(codegen_unit_name)) + .unwrap_or_default(); + let out_dir = out_dir.to_str().unwrap(); + let split_name = split_name.to_str().unwrap(); // FIXME(#60020): // @@ -1020,8 +1026,8 @@ pub fn compile_unit_metadata( debug_context.builder, name_in_debuginfo.as_ptr().cast(), name_in_debuginfo.len(), - work_dir.as_ptr().cast(), - work_dir.len(), + out_dir.as_ptr().cast(), + out_dir.len(), llvm::ChecksumKind::None, ptr::null(), 0, @@ -1039,6 +1045,8 @@ pub fn compile_unit_metadata( split_name.as_ptr().cast(), split_name.len(), kind, + 0, + tcx.sess.opts.debugging_opts.split_dwarf_inlining, ); if tcx.sess.opts.debugging_opts.profile { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 5974b59d39e42..a58c2fbd8ab2c 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -19,7 +19,9 @@ use back::write::{create_informational_target_machine, create_target_machine}; pub use llvm_util::target_features; use rustc_ast::expand::allocator::AllocatorKind; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; -use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig}; +use rustc_codegen_ssa::back::write::{ + CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn, +}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::{CodegenResults, CompiledModule}; @@ -34,7 +36,6 @@ use rustc_span::symbol::Symbol; use std::any::Any; use std::ffi::CStr; -use std::sync::Arc; mod back { pub mod archive; @@ -109,7 +110,7 @@ impl ExtraBackendMethods for LlvmCodegenBackend { &self, sess: &Session, optlvl: OptLevel, - ) -> Arc Result<&'static mut llvm::TargetMachine, String> + Send + Sync> { + ) -> TargetMachineFactoryFn { back::write::target_machine_factory(sess, optlvl) } fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str { @@ -331,7 +332,7 @@ impl ModuleLlvm { unsafe { let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names()); let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; - ModuleLlvm { llmod_raw, llcx, tm: create_target_machine(tcx) } + ModuleLlvm { llmod_raw, llcx, tm: create_target_machine(tcx, mod_name) } } } @@ -352,7 +353,13 @@ impl ModuleLlvm { unsafe { let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); let llmod_raw = back::lto::parse_module(llcx, name, buffer, handler)?; - let tm = match (cgcx.tm_factory.0)() { + + let split_dwarf_file = cgcx + .output_filenames + .split_dwarf_filename(cgcx.split_dwarf_kind, Some(name.to_str().unwrap())); + let tm_factory_config = TargetMachineFactoryConfig { split_dwarf_file }; + + let tm = match (cgcx.tm_factory)(tm_factory_config) { Ok(m) => m, Err(e) => { handler.struct_err(&e).emit(); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 41482d18946ad..707aaa2b53ffc 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1830,6 +1830,8 @@ extern "C" { SplitName: *const c_char, SplitNameLen: size_t, kind: DebugEmissionKind, + DWOId: u64, + SplitDebugInlining: bool, ) -> &'a DIDescriptor; pub fn LLVMRustDIBuilderCreateFile( @@ -2151,6 +2153,7 @@ extern "C" { EmitStackSizeSection: bool, RelaxELFRelocations: bool, UseInitArray: bool, + SplitDwarfFile: *const c_char, ) -> Option<&'static mut TargetMachine>; pub fn LLVMRustDisposeTargetMachine(T: &'static mut TargetMachine); pub fn LLVMRustAddBuilderLibraryInfo( @@ -2179,6 +2182,7 @@ extern "C" { PM: &PassManager<'a>, M: &'a Module, Output: *const c_char, + DwoOutput: *const c_char, FileType: FileType, ) -> LLVMRustResult; pub fn LLVMRustOptimizeWithNewPassManager( diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 70cf876a08a51..ccd4d103ddb7f 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -21,7 +21,9 @@ use super::archive::ArchiveBuilder; use super::command::Command; use super::linker::{self, Linker}; use super::rpath::{self, RPathConfig}; -use crate::{looks_like_rust_object_file, CodegenResults, CrateInfo, METADATA_FILENAME}; +use crate::{ + looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, METADATA_FILENAME, +}; use cc::windows_registry; use tempfile::Builder as TempFileBuilder; @@ -96,6 +98,9 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( path.as_ref(), target_cpu, ); + if sess.opts.debugging_opts.split_dwarf == config::SplitDwarfKind::Split { + link_dwarf_object(sess, &out_filename); + } } } if sess.opts.json_artifact_notifications { @@ -107,22 +112,30 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( // Remove the temporary object file and metadata if we aren't saving temps sess.time("link_binary_remove_temps", || { if !sess.opts.cg.save_temps { + let remove_temps_from_module = |module: &CompiledModule| { + if let Some(ref obj) = module.object { + remove(sess, obj); + } + + if let Some(ref obj) = module.dwarf_object { + remove(sess, obj); + } + }; + if sess.opts.output_types.should_codegen() && !preserve_objects_for_their_debuginfo(sess) { - for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { - remove(sess, obj); + for module in &codegen_results.modules { + remove_temps_from_module(module); } } + if let Some(ref metadata_module) = codegen_results.metadata_module { - if let Some(ref obj) = metadata_module.object { - remove(sess, obj); - } + remove_temps_from_module(metadata_module); } + if let Some(ref allocator_module) = codegen_results.allocator_module { - if let Some(ref obj) = allocator_module.object { - remove(sess, obj); - } + remove_temps_from_module(allocator_module); } } }); @@ -279,12 +292,12 @@ pub fn emit_metadata(sess: &Session, metadata: &EncodedMetadata, tmpdir: &MaybeT out_filename } -// Create an 'rlib' -// -// An rlib in its current incarnation is essentially a renamed .a file. The -// rlib primarily contains the object file of the crate, but it also contains -// all of the object files from native libraries. This is done by unzipping -// native libraries and inserting all of the contents into this archive. +/// Create an 'rlib'. +/// +/// An rlib in its current incarnation is essentially a renamed .a file. The rlib primarily contains +/// the object file of the crate, but it also contains all of the object files from native +/// libraries. This is done by unzipping native libraries and inserting all of the contents into +/// this archive. fn link_rlib<'a, B: ArchiveBuilder<'a>>( sess: &'a Session, codegen_results: &CodegenResults, @@ -379,18 +392,17 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( ab } -// Create a static archive -// -// This is essentially the same thing as an rlib, but it also involves adding -// all of the upstream crates' objects into the archive. This will slurp in -// all of the native libraries of upstream dependencies as well. -// -// Additionally, there's no way for us to link dynamic libraries, so we warn -// about all dynamic library dependencies that they're not linked in. -// -// There's no need to include metadata in a static archive, so ensure to not -// link in the metadata object file (and also don't prepare the archive with a -// metadata file). +/// Create a static archive. +/// +/// This is essentially the same thing as an rlib, but it also involves adding all of the upstream +/// crates' objects into the archive. This will slurp in all of the native libraries of upstream +/// dependencies as well. +/// +/// Additionally, there's no way for us to link dynamic libraries, so we warn about all dynamic +/// library dependencies that they're not linked in. +/// +/// There's no need to include metadata in a static archive, so ensure to not link in the metadata +/// object file (and also don't prepare the archive with a metadata file). fn link_staticlib<'a, B: ArchiveBuilder<'a>>( sess: &'a Session, codegen_results: &CodegenResults, @@ -447,10 +459,73 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>( } } -// Create a dynamic library or executable -// -// This will invoke the system linker/cc to create the resulting file. This -// links to all upstream files as well. +fn escape_stdout_stderr_string(s: &[u8]) -> String { + str::from_utf8(s).map(|s| s.to_owned()).unwrap_or_else(|_| { + let mut x = "Non-UTF-8 output: ".to_string(); + x.extend(s.iter().flat_map(|&b| ascii::escape_default(b)).map(char::from)); + x + }) +} + +const LLVM_DWP_EXECUTABLE: &'static str = "rust-llvm-dwp"; + +/// Invoke `llvm-dwp` (shipped alongside rustc) to link `dwo` files from Split DWARF into a `dwp` +/// file. +fn link_dwarf_object<'a>(sess: &'a Session, executable_out_filename: &Path) { + info!("preparing dwp to {}.dwp", executable_out_filename.to_str().unwrap()); + + let dwp_out_filename = executable_out_filename.with_extension("dwp"); + let mut cmd = Command::new(LLVM_DWP_EXECUTABLE); + cmd.arg("-e"); + cmd.arg(executable_out_filename); + cmd.arg("-o"); + cmd.arg(&dwp_out_filename); + + let mut new_path = sess.host_filesearch(PathKind::All).get_tools_search_paths(false); + if let Some(path) = env::var_os("PATH") { + new_path.extend(env::split_paths(&path)); + } + let new_path = env::join_paths(new_path).unwrap(); + cmd.env("PATH", new_path); + + info!("{:?}", &cmd); + match sess.time("run_dwp", || cmd.output()) { + Ok(prog) if !prog.status.success() => { + sess.struct_err(&format!( + "linking dwarf objects with `{}` failed: {}", + LLVM_DWP_EXECUTABLE, prog.status + )) + .note(&format!("{:?}", &cmd)) + .note(&escape_stdout_stderr_string(&prog.stdout)) + .note(&escape_stdout_stderr_string(&prog.stderr)) + .emit(); + info!("linker stderr:\n{}", escape_stdout_stderr_string(&prog.stderr)); + info!("linker stdout:\n{}", escape_stdout_stderr_string(&prog.stdout)); + } + Ok(_) => {} + Err(e) => { + let dwp_not_found = e.kind() == io::ErrorKind::NotFound; + let mut err = if dwp_not_found { + sess.struct_err(&format!("linker `{}` not found", LLVM_DWP_EXECUTABLE)) + } else { + sess.struct_err(&format!("could not exec the linker `{}`", LLVM_DWP_EXECUTABLE)) + }; + + err.note(&e.to_string()); + + if !dwp_not_found { + err.note(&format!("{:?}", &cmd)); + } + + err.emit(); + } + } +} + +/// Create a dynamic library or executable. +/// +/// This will invoke the system linker/cc to create the resulting file. This links to all upstream +/// files as well. fn link_natively<'a, B: ArchiveBuilder<'a>>( sess: &'a Session, crate_type: CrateType, @@ -662,7 +737,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( prog.status )) .note(&format!("{:?}", &cmd)) - .note(&escape_string(&output)) + .note(&escape_stdout_stderr_string(&output)) .emit(); // If MSVC's `link.exe` was expected but the return code @@ -715,8 +790,8 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( sess.abort_if_errors(); } - info!("linker stderr:\n{}", escape_string(&prog.stderr)); - info!("linker stdout:\n{}", escape_string(&prog.stdout)); + info!("linker stderr:\n{}", escape_stdout_stderr_string(&prog.stderr)); + info!("linker stdout:\n{}", escape_stdout_stderr_string(&prog.stdout)); } Err(e) => { let linker_not_found = e.kind() == io::ErrorKind::NotFound; @@ -962,6 +1037,13 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { return false; } + // Single mode keeps debuginfo in the same object file, but in such a way that it it skipped + // by the linker - so it's expected that when codegen units are linked together that this + // debuginfo would be lost without keeping around the temps. + if sess.opts.debugging_opts.split_dwarf == config::SplitDwarfKind::Single { + return true; + } + // If we're on OSX then the equivalent of split dwarf is turned on by // default. The final executable won't actually have any debug information // except it'll have pointers to elsewhere. Historically we've always run @@ -1677,17 +1759,15 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( cmd.take_cmd() } -// # Native library linking -// -// User-supplied library search paths (-L on the command line). These are -// the same paths used to find Rust crates, so some of them may have been -// added already by the previous crate linking code. This only allows them -// to be found at compile time so it is still entirely up to outside -// forces to make sure that library can be found at runtime. -// -// Also note that the native libraries linked here are only the ones located -// in the current crate. Upstream crates with native library dependencies -// may have their native library pulled in above. +/// # Native library linking +/// +/// User-supplied library search paths (-L on the command line). These are the same paths used to +/// find Rust crates, so some of them may have been added already by the previous crate linking +/// code. This only allows them to be found at compile time so it is still entirely up to outside +/// forces to make sure that library can be found at runtime. +/// +/// Also note that the native libraries linked here are only the ones located in the current crate. +/// Upstream crates with native library dependencies may have their native library pulled in above. fn add_local_native_libraries( cmd: &mut dyn Linker, sess: &Session, @@ -1727,11 +1807,10 @@ fn add_local_native_libraries( } } -// # Rust Crate linking -// -// Rust crates are not considered at all when creating an rlib output. All -// dependencies will be linked when producing the final output (instead of -// the intermediate rlib version) +/// # Rust Crate linking +/// +/// Rust crates are not considered at all when creating an rlib output. All dependencies will be +/// linked when producing the final output (instead of the intermediate rlib version). fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( cmd: &mut dyn Linker, sess: &'a Session, @@ -1996,24 +2075,21 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( } } -// Link in all of our upstream crates' native dependencies. Remember that -// all of these upstream native dependencies are all non-static -// dependencies. We've got two cases then: -// -// 1. The upstream crate is an rlib. In this case we *must* link in the -// native dependency because the rlib is just an archive. -// -// 2. The upstream crate is a dylib. In order to use the dylib, we have to -// have the dependency present on the system somewhere. Thus, we don't -// gain a whole lot from not linking in the dynamic dependency to this -// crate as well. -// -// The use case for this is a little subtle. In theory the native -// dependencies of a crate are purely an implementation detail of the crate -// itself, but the problem arises with generic and inlined functions. If a -// generic function calls a native function, then the generic function must -// be instantiated in the target crate, meaning that the native symbol must -// also be resolved in the target crate. +/// Link in all of our upstream crates' native dependencies. Remember that all of these upstream +/// native dependencies are all non-static dependencies. We've got two cases then: +/// +/// 1. The upstream crate is an rlib. In this case we *must* link in the native dependency because +/// the rlib is just an archive. +/// +/// 2. The upstream crate is a dylib. In order to use the dylib, we have to have the dependency +/// present on the system somewhere. Thus, we don't gain a whole lot from not linking in the +/// dynamic dependency to this crate as well. +/// +/// The use case for this is a little subtle. In theory the native dependencies of a crate are +/// purely an implementation detail of the crate itself, but the problem arises with generic and +/// inlined functions. If a generic function calls a native function, then the generic function +/// must be instantiated in the target crate, meaning that the native symbol must also be resolved +/// in the target crate. fn add_upstream_native_libraries( cmd: &mut dyn Linker, sess: &Session, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 7f2bb7b5bcdaf..c84b87964b845 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -274,17 +274,20 @@ impl ModuleConfig { } } -// HACK(eddyb) work around `#[derive]` producing wrong bounds for `Clone`. -pub struct TargetMachineFactory( - pub Arc Result + Send + Sync>, -); - -impl Clone for TargetMachineFactory { - fn clone(&self) -> Self { - TargetMachineFactory(self.0.clone()) - } +/// Configuration passed to the function returned by the `target_machine_factory`. +pub struct TargetMachineFactoryConfig { + /// Split DWARF is enabled in LLVM by checking that `TM.MCOptions.SplitDwarfFile` isn't empty, + /// so the path to the dwarf object has to be provided when we create the target machine. + /// This can be ignored by backends which do not need it for their Split DWARF support. + pub split_dwarf_file: Option, } +pub type TargetMachineFactoryFn = Arc< + dyn Fn(TargetMachineFactoryConfig) -> Result<::TargetMachine, String> + + Send + + Sync, +>; + pub type ExportedSymbols = FxHashMap>>; /// Additional resources used by optimize_and_codegen (not module specific) @@ -305,12 +308,13 @@ pub struct CodegenContext { pub regular_module_config: Arc, pub metadata_module_config: Arc, pub allocator_module_config: Arc, - pub tm_factory: TargetMachineFactory, + pub tm_factory: TargetMachineFactoryFn, pub msvc_imps_needed: bool, pub is_pe_coff: bool, pub target_pointer_width: u32, pub target_arch: String, pub debuginfo: config::DebugInfo, + pub split_dwarf_kind: config::SplitDwarfKind, // Number of cgus excluding the allocator/metadata modules pub total_cgus: usize, @@ -627,6 +631,12 @@ fn produce_final_output_artifacts( } } + if let Some(ref path) = module.dwarf_object { + if !keep_numbered_objects { + remove(sess, path); + } + } + if let Some(ref path) = module.bytecode { if !keep_numbered_bitcode { remove(sess, path); @@ -849,6 +859,7 @@ fn execute_copy_from_cache_work_item( name: module.name, kind: ModuleKind::Regular, object, + dwarf_object: None, bytecode: None, })) } @@ -1020,13 +1031,14 @@ fn start_executing_work( regular_module_config: regular_config, metadata_module_config: metadata_config, allocator_module_config: allocator_config, - tm_factory: TargetMachineFactory(backend.target_machine_factory(tcx.sess, ol)), + tm_factory: backend.target_machine_factory(tcx.sess, ol), total_cgus, msvc_imps_needed: msvc_imps_needed(tcx), is_pe_coff: tcx.sess.target.is_like_windows, target_pointer_width: tcx.sess.target.pointer_width, target_arch: tcx.sess.target.arch.clone(), debuginfo: tcx.sess.opts.debuginfo, + split_dwarf_kind: tcx.sess.opts.debugging_opts.split_dwarf, }; // This is the "main loop" of parallel work happening for parallel codegen. diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 8ec1eed440456..ee889d552412e 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -64,13 +64,15 @@ impl ModuleCodegen { pub fn into_compiled_module( self, emit_obj: bool, + emit_dwarf_obj: bool, emit_bc: bool, outputs: &OutputFilenames, ) -> CompiledModule { let object = emit_obj.then(|| outputs.temp_path(OutputType::Object, Some(&self.name))); + let dwarf_object = emit_dwarf_obj.then(|| outputs.temp_path_dwo(Some(&self.name))); let bytecode = emit_bc.then(|| outputs.temp_path(OutputType::Bitcode, Some(&self.name))); - CompiledModule { name: self.name.clone(), kind: self.kind, object, bytecode } + CompiledModule { name: self.name.clone(), kind: self.kind, object, dwarf_object, bytecode } } } @@ -79,6 +81,7 @@ pub struct CompiledModule { pub name: String, pub kind: ModuleKind, pub object: Option, + pub dwarf_object: Option, pub bytecode: Option, } diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index b9c555c2eb069..f28db2fe84b6b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -1,5 +1,6 @@ use super::write::WriteBackendMethods; use super::CodegenObject; +use crate::back::write::TargetMachineFactoryFn; use crate::{CodegenResults, ModuleCodegen}; use rustc_ast::expand::allocator::AllocatorKind; @@ -21,7 +22,6 @@ use rustc_target::spec::Target; pub use rustc_data_structures::sync::MetadataRef; use std::any::Any; -use std::sync::Arc; pub trait BackendTypes { type Value: CodegenObject; @@ -123,7 +123,7 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se &self, sess: &Session, opt_level: config::OptLevel, - ) -> Arc Result + Send + Sync>; + ) -> TargetMachineFactoryFn; fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str; fn tune_cpu<'b>(&self, sess: &'b Session) -> Option<&'b str>; } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 01d76bb3e94f2..2264908995bb7 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -450,7 +450,8 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( bool AsmComments, bool EmitStackSizeSection, bool RelaxELFRelocations, - bool UseInitArray) { + bool UseInitArray, + const char *SplitDwarfFile) { auto OptLevel = fromRust(RustOptLevel); auto RM = fromRust(RustReloc); @@ -476,6 +477,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.MCOptions.AsmVerbose = AsmComments; Options.MCOptions.PreserveAsmComments = AsmComments; Options.MCOptions.ABIName = ABIStr; + if (SplitDwarfFile) { + Options.MCOptions.SplitDwarfFile = SplitDwarfFile; + } Options.RelaxELFRelocations = RelaxELFRelocations; Options.UseInitArray = UseInitArray; @@ -610,7 +614,7 @@ static TargetMachine::CodeGenFileType fromRust(LLVMRustFileType Type) { extern "C" LLVMRustResult LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, - LLVMModuleRef M, const char *Path, + LLVMModuleRef M, const char *Path, const char *DwoPath, LLVMRustFileType RustFileType) { llvm::legacy::PassManager *PM = unwrap(PMR); auto FileType = fromRust(RustFileType); @@ -626,8 +630,22 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, } buffer_ostream BOS(OS); - unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, false); - PM->run(*unwrap(M)); + if (DwoPath) { + raw_fd_ostream DOS(DwoPath, EC, sys::fs::F_None); + EC.clear(); + if (EC) + ErrorInfo = EC.message(); + if (ErrorInfo != "") { + LLVMRustSetLastError(ErrorInfo.c_str()); + return LLVMRustResult::Failure; + } + buffer_ostream DBOS(DOS); + unwrap(Target)->addPassesToEmitFile(*PM, BOS, &DBOS, FileType, false); + PM->run(*unwrap(M)); + } else { + unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, false); + PM->run(*unwrap(M)); + } // Apparently `addPassesToEmitFile` adds a pointer to our on-the-stack output // stream (OS), so the only real safe place to delete this is here? Don't we diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index e17f933932e6a..c0ff62c17beb5 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -690,13 +690,14 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit( const char *Producer, size_t ProducerLen, bool isOptimized, const char *Flags, unsigned RuntimeVer, const char *SplitName, size_t SplitNameLen, - LLVMRustDebugEmissionKind Kind) { + LLVMRustDebugEmissionKind Kind, + uint64_t DWOId, bool SplitDebugInlining) { auto *File = unwrapDI(FileRef); return wrap(Builder->createCompileUnit(Lang, File, StringRef(Producer, ProducerLen), isOptimized, Flags, RuntimeVer, StringRef(SplitName, SplitNameLen), - fromRust(Kind))); + fromRust(Kind), DWOId, SplitDebugInlining)); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFile( diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b77a8b631e056..c20b11656b229 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -221,6 +221,23 @@ pub enum DebugInfo { Full, } +/// Some debuginfo requires link-time relocation and some does not. LLVM can partition the debuginfo +/// into sections depending on whether or not it requires link-time relocation. Split DWARF +/// provides a mechanism which allows the linker to skip the sections which don't require link-time +/// relocation - either by putting those sections into DWARF object files, or keeping them in the +/// object file in such a way that the linker will skip them. +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +pub enum SplitDwarfKind { + /// Disabled. + None, + /// Sections which do not require relocation are written into the object file but ignored + /// by the linker. + Single, + /// Sections which do not require relocation are written into a DWARF object (`.dwo`) file, + /// which is skipped by the linker by virtue of being a different file. + Split, +} + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] #[derive(Encodable, Decodable)] pub enum OutputType { @@ -533,6 +550,7 @@ impl_stable_hash_via_hash!(OutputFilenames); pub const RLINK_EXT: &str = "rlink"; pub const RUST_CGU_EXT: &str = "rcgu"; +pub const DWARF_OBJECT_EXT: &str = "dwo"; impl OutputFilenames { pub fn new( @@ -566,7 +584,12 @@ impl OutputFilenames { self.temp_path_ext(extension, codegen_unit_name) } - /// Like temp_path, but also supports things where there is no corresponding + /// Like `temp_path`, but specifically for dwarf objects. + pub fn temp_path_dwo(&self, codegen_unit_name: Option<&str>) -> PathBuf { + self.temp_path_ext(DWARF_OBJECT_EXT, codegen_unit_name) + } + + /// Like `temp_path`, but also supports things where there is no corresponding /// OutputType, like noopt-bitcode or lto-bitcode. pub fn temp_path_ext(&self, ext: &str, codegen_unit_name: Option<&str>) -> PathBuf { let mut extension = String::new(); @@ -593,6 +616,37 @@ impl OutputFilenames { path.set_extension(extension); path } + + /// Returns the name of the Split DWARF file - this can differ depending on which Split DWARF + /// mode is being used, which is the logic that this function is intended to encapsulate. + pub fn split_dwarf_filename( + &self, + split_dwarf_kind: SplitDwarfKind, + cgu_name: Option<&str>, + ) -> Option { + self.split_dwarf_path(split_dwarf_kind, cgu_name) + .map(|path| path.strip_prefix(&self.out_directory).unwrap_or(&path).to_path_buf()) + } + + /// Returns the path for the Split DWARF file - this can differ depending on which Split DWARF + /// mode is being used, which is the logic that this function is intended to encapsulate. + pub fn split_dwarf_path( + &self, + split_dwarf_kind: SplitDwarfKind, + cgu_name: Option<&str>, + ) -> Option { + let obj_out = self.temp_path(OutputType::Object, cgu_name); + let dwo_out = self.temp_path_dwo(cgu_name); + match split_dwarf_kind { + SplitDwarfKind::None => None, + // Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes + // (pointing at the path which is being determined here). Use the path to the current + // object file. + SplitDwarfKind::Single => Some(obj_out), + // Split mode emits the DWARF into a different file, use that path. + SplitDwarfKind::Split => Some(dwo_out), + } + } } pub fn host_triple() -> &'static str { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 49a7888fd6a42..81f79f4b0e0fb 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -269,6 +269,7 @@ macro_rules! options { pub const parse_switch_with_opt_path: &str = "an optional path to the profiling data output directory"; pub const parse_merge_functions: &str = "one of: `disabled`, `trampolines`, or `aliases`"; + pub const parse_split_dwarf_kind: &str = "one of: `none`, `single` or `split`"; pub const parse_symbol_mangling_version: &str = "either `legacy` or `v0` (RFC 2603)"; pub const parse_src_file_hash: &str = "either `md5` or `sha1`"; pub const parse_relocation_model: &str = @@ -676,6 +677,19 @@ macro_rules! options { true } + fn parse_split_dwarf_kind( + slot: &mut SplitDwarfKind, + v: Option<&str>, + ) -> bool { + *slot = match v { + Some("none") => SplitDwarfKind::None, + Some("split") => SplitDwarfKind::Split, + Some("single") => SplitDwarfKind::Single, + _ => return false, + }; + true + } + fn parse_symbol_mangling_version( slot: &mut Option, v: Option<&str>, @@ -1088,6 +1102,11 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), + split_dwarf: SplitDwarfKind = (SplitDwarfKind::None, parse_split_dwarf_kind, [UNTRACKED], + "enable generation of split dwarf"), + split_dwarf_inlining: bool = (true, parse_bool, [UNTRACKED], + "provide minimal debug info in the object/executable to facilitate online \ + symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"), symbol_mangling_version: Option = (None, parse_symbol_mangling_version, [TRACKED], "which mangling version to use for symbol names ('legacy' (default) or 'v0')"), diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index cdad1cb4d499d..fbebb26c74620 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -969,15 +969,25 @@ impl Step for Assemble { copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler); + // We prepend this bin directory to the user PATH when linking Rust binaries. To + // avoid shadowing the system LLD we rename the LLD we provide to `rust-lld`. let libdir = builder.sysroot_libdir(target_compiler, target_compiler.host); + let libdir_bin = libdir.parent().unwrap().join("bin"); + t!(fs::create_dir_all(&libdir_bin)); + if let Some(lld_install) = lld_install { let src_exe = exe("lld", target_compiler.host); let dst_exe = exe("rust-lld", target_compiler.host); - // we prepend this bin directory to the user PATH when linking Rust binaries. To - // avoid shadowing the system LLD we rename the LLD we provide to `rust-lld`. - let dst = libdir.parent().unwrap().join("bin"); - t!(fs::create_dir_all(&dst)); - builder.copy(&lld_install.join("bin").join(&src_exe), &dst.join(&dst_exe)); + builder.copy(&lld_install.join("bin").join(&src_exe), &libdir_bin.join(&dst_exe)); + } + + // Similarly, copy `llvm-dwp` into libdir for Split DWARF. + { + let src_exe = exe("llvm-dwp", target_compiler.host); + let dst_exe = exe("rust-llvm-dwp", target_compiler.host); + let llvm_config_bin = builder.ensure(native::Llvm { target: target_compiler.host }); + let llvm_bin_dir = llvm_config_bin.parent().unwrap(); + builder.copy(&llvm_bin_dir.join(&src_exe), &libdir_bin.join(&dst_exe)); } // Ensure that `libLLVM.so` ends up in the newly build compiler directory, diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 360e51ed2bb2a..25905895116fa 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -523,17 +523,20 @@ impl Step for Rustc { // component for now. maybe_install_llvm_runtime(builder, host, image); + let src_dir = builder.sysroot_libdir(compiler, host).parent().unwrap().join("bin"); + let dst_dir = image.join("lib/rustlib").join(&*host.triple).join("bin"); + t!(fs::create_dir_all(&dst_dir)); + // Copy over lld if it's there if builder.config.lld_enabled { let exe = exe("rust-lld", compiler.host); - let src = - builder.sysroot_libdir(compiler, host).parent().unwrap().join("bin").join(&exe); - // for the rationale about this rename check `compile::copy_lld_to_sysroot` - let dst = image.join("lib/rustlib").join(&*host.triple).join("bin").join(&exe); - t!(fs::create_dir_all(&dst.parent().unwrap())); - builder.copy(&src, &dst); + builder.copy(&src_dir.join(&exe), &dst_dir.join(&exe)); } + // Copy over llvm-dwp if it's there + let exe = exe("rust-llvm-dwp", compiler.host); + builder.copy(&src_dir.join(&exe), &dst_dir.join(&exe)); + // Man pages t!(fs::create_dir_all(image.join("share/man/man1"))); let man_src = builder.src.join("src/doc/man"); diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 78b5de7897d1f..b99692e8ba5b8 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -897,7 +897,12 @@ default_test!(Incremental { suite: "incremental" }); -default_test!(Debuginfo { path: "src/test/debuginfo", mode: "debuginfo", suite: "debuginfo" }); +default_test_with_compare_mode!(Debuginfo { + path: "src/test/debuginfo", + mode: "debuginfo", + suite: "debuginfo", + compare_mode: "split-dwarf" +}); host_test!(UiFullDeps { path: "src/test/ui-fulldeps", mode: "ui", suite: "ui-fulldeps" }); diff --git a/src/test/run-make-fulldeps/split-dwarf/Makefile b/src/test/run-make-fulldeps/split-dwarf/Makefile new file mode 100644 index 0000000000000..e1a78e2edfc44 --- /dev/null +++ b/src/test/run-make-fulldeps/split-dwarf/Makefile @@ -0,0 +1,8 @@ +-include ../tools.mk + +# only-linux + +all: + $(RUSTC) -Z split-dwarf=split foo.rs + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) diff --git a/src/test/run-make-fulldeps/split-dwarf/foo.rs b/src/test/run-make-fulldeps/split-dwarf/foo.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/src/test/run-make-fulldeps/split-dwarf/foo.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 55d25fa7c52c2..80fbb06b94691 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -127,6 +127,8 @@ pub enum CompareMode { Nll, Polonius, Chalk, + SplitDwarf, + SplitDwarfSingle, } impl CompareMode { @@ -135,6 +137,8 @@ impl CompareMode { CompareMode::Nll => "nll", CompareMode::Polonius => "polonius", CompareMode::Chalk => "chalk", + CompareMode::SplitDwarf => "split-dwarf", + CompareMode::SplitDwarfSingle => "split-dwarf-single", } } @@ -143,6 +147,8 @@ impl CompareMode { "nll" => CompareMode::Nll, "polonius" => CompareMode::Polonius, "chalk" => CompareMode::Chalk, + "split-dwarf" => CompareMode::SplitDwarf, + "split-dwarf-single" => CompareMode::SplitDwarfSingle, x => panic!("unknown --compare-mode option: {}", x), } } diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 59f64e7df0f41..a1be0a19f6854 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -852,6 +852,8 @@ impl Config { Some(CompareMode::Nll) => name == "compare-mode-nll", Some(CompareMode::Polonius) => name == "compare-mode-polonius", Some(CompareMode::Chalk) => name == "compare-mode-chalk", + Some(CompareMode::SplitDwarf) => name == "compare-mode-split-dwarf", + Some(CompareMode::SplitDwarfSingle) => name == "compare-mode-split-dwarf-single", None => false, } || (cfg!(debug_assertions) && name == "debug") || diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 7af0d91271b0c..828c4e89f0b1f 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2017,6 +2017,12 @@ impl<'test> TestCx<'test> { Some(CompareMode::Chalk) => { rustc.args(&["-Zchalk"]); } + Some(CompareMode::SplitDwarf) => { + rustc.args(&["-Zsplit-dwarf=split"]); + } + Some(CompareMode::SplitDwarfSingle) => { + rustc.args(&["-Zsplit-dwarf=single"]); + } None => {} }