From 341aa97adb20ca22e0fcb0851014bfa2297ff932 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 23 Sep 2020 16:25:20 +0100 Subject: [PATCH 01/10] llvm: update ffi bindings for split dwarf This commit modifies the FFI bindings to LLVM required for Split DWARF support in rustc. In particular: - `addPassesToEmitFile`'s wrapper, `LLVMRustWriteOutputFile` now takes a `DwoPath` `const char*`. When disabled, `nullptr` should be provided which will preserve existing behaviour. When enabled, the path to the `.dwo` file should be provided. - `createCompileUnit`'s wrapper, `LLVMRustDIBuilderCreateCompileUnit` now has two additional arguments, for the `DWOId` and to enable `SplitDebugInlining`. `DWOId` should always be zero. - `createTargetMachine`'s wrapper, `LLVMRustCreateTargetMachine` has an additional argument which should be provided the path to the `.dwo` when enabled. Signed-off-by: David Wood --- compiler/rustc_codegen_llvm/src/back/write.rs | 11 +++++++- .../src/debuginfo/metadata.rs | 2 ++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 +++ .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 26 ++++++++++++++++--- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 5 ++-- 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 7407dfc455d84..a6b866a0498cb 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -53,7 +53,14 @@ pub fn write_output_file( ) -> 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 = 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) @@ -164,6 +171,7 @@ pub fn target_machine_factory( !sess.opts.debugging_opts.use_ctors_section.unwrap_or(sess.target.use_ctors_section); Arc::new(move || { + let split_dwarf_file = std::ptr::null(); let tm = unsafe { llvm::LLVMRustCreateTargetMachine( triple.as_ptr(), @@ -182,6 +190,7 @@ pub fn target_machine_factory( emit_stack_size_section, relax_elf_relocations, use_init_array, + split_dwarf_file, ) }; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 31e43893ac8b1..41c8a5e570528 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1039,6 +1039,8 @@ pub fn compile_unit_metadata( split_name.as_ptr().cast(), split_name.len(), kind, + 0, + true, ); if tcx.sess.opts.debugging_opts.profile { 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_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( From 57d05d35765a443fbb0b789a823e84ddff7c0cb1 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 23 Sep 2020 16:50:58 +0100 Subject: [PATCH 02/10] session: add `split-dwarf` flag This commit adds a flag for Split DWARF, which enables debuginfo to be split into multiple files. Signed-off-by: David Wood --- compiler/rustc_session/src/config.rs | 17 +++++++++++++++++ compiler/rustc_session/src/options.rs | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b77a8b631e056..b5354513dc2ef 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 { 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')"), From cf49c2a1efe65d8cbe671ef02e5c4d870529da13 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 14 Oct 2020 15:52:16 +0100 Subject: [PATCH 03/10] cg_ssa: correct documentation comments This commit changes some comments to documentation comments so that they can be read on the generated rustdoc. Signed-off-by: David Wood --- compiler/rustc_codegen_ssa/src/back/link.rs | 105 +++++++++----------- 1 file changed, 49 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 70cf876a08a51..5001e97016ce7 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -279,12 +279,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 +379,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 +446,10 @@ 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. +/// 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, @@ -1677,17 +1676,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 +1724,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 +1992,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, From 6890312ea3b7f59bc18de68930524b8853442627 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 23 Sep 2020 16:57:50 +0100 Subject: [PATCH 04/10] cg_ssa: introduce `TargetMachineFactoryFn` alias This commit removes the `TargetMachineFactory` struct and adds a `TargetMachineFactoryFn` type alias which is used everywhere that the previous, long type was used. Signed-off-by: David Wood --- compiler/rustc_codegen_llvm/src/back/lto.rs | 2 +- compiler/rustc_codegen_llvm/src/back/write.rs | 6 ++++-- compiler/rustc_codegen_llvm/src/lib.rs | 9 +++++---- compiler/rustc_codegen_ssa/src/back/write.rs | 16 ++++------------ compiler/rustc_codegen_ssa/src/traits/backend.rs | 4 ++-- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 64fd1d09cc24f..64ca7efb48655 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -728,7 +728,7 @@ 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 tm = (cgcx.tm_factory)().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 diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index a6b866a0498cb..612ed4405bb83 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -11,7 +11,9 @@ 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, TargetMachineFactoryFn, +}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; use rustc_data_structures::small_c_str::SmallCStr; @@ -129,7 +131,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); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 5974b59d39e42..4fe0384b36965 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, 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 { @@ -352,7 +353,7 @@ 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 tm = match (cgcx.tm_factory)() { Ok(m) => m, Err(e) => { handler.struct_err(&e).emit(); diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 7f2bb7b5bcdaf..615eeecf54ea5 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -274,16 +274,8 @@ 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()) - } -} +pub type TargetMachineFactoryFn = + Arc Result<::TargetMachine, String> + Send + Sync>; pub type ExportedSymbols = FxHashMap>>; @@ -305,7 +297,7 @@ 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, @@ -1020,7 +1012,7 @@ 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, 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>; } From 241160de72b5b55187ca54243e2a6e82e336d07c Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 14 Oct 2020 18:16:05 +0100 Subject: [PATCH 05/10] bootstrap: copy `llvm-dwp` to sysroot `llvm-dwp` is required for linking the DWARF objects into DWARF packages when using Split DWARF, especially given that rustc produces multiple DWARF objects (one for each codegen unit). Signed-off-by: David Wood --- src/bootstrap/compile.rs | 20 +++++++++++++++----- src/bootstrap/dist.rs | 15 +++++++++------ 2 files changed, 24 insertions(+), 11 deletions(-) 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"); From e3fdae9d81052cf521298d19f8bb32ba0332b589 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 23 Sep 2020 17:33:54 +0100 Subject: [PATCH 06/10] cg_llvm: implement split dwarf support This commit implements Split DWARF support, wiring up the flag (added in earlier commits) to the modified FFI wrapper (also from earlier commits). Signed-off-by: David Wood --- compiler/rustc_codegen_llvm/src/back/lto.rs | 21 ++-- compiler/rustc_codegen_llvm/src/back/write.rs | 76 ++++++++++--- .../src/debuginfo/metadata.rs | 8 +- compiler/rustc_codegen_llvm/src/lib.rs | 12 ++- compiler/rustc_codegen_ssa/src/back/link.rs | 100 +++++++++++++++--- compiler/rustc_codegen_ssa/src/back/write.rs | 24 ++++- compiler/rustc_codegen_ssa/src/lib.rs | 5 +- compiler/rustc_session/src/config.rs | 28 ++++- 8 files changed, 228 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 64ca7efb48655..834fc02c48ab8 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)().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_file(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 612ed4405bb83..ef99c87053c76 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -12,7 +12,8 @@ use crate::type_::Type; use crate::LlvmCodegenBackend; use crate::ModuleLlvm; use rustc_codegen_ssa::back::write::{ - BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryFn, + BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig, + TargetMachineFactoryFn, }; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; @@ -22,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; @@ -51,18 +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(), - std::ptr::null(), - 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) @@ -71,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_file(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()) } @@ -172,8 +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 || { - let split_dwarf_file = std::ptr::null(); + 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(), @@ -192,7 +215,7 @@ pub fn target_machine_factory( emit_stack_size_section, relax_elf_relocations, use_init_array, - split_dwarf_file, + split_dwarf_file.as_ptr(), ) }; @@ -796,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, + ) })?; } @@ -805,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, @@ -812,6 +852,7 @@ pub(crate) unsafe fn codegen( cpm, llmod, &obj_out, + dwo_out, llvm::FileType::ObjectFile, ) })?; @@ -839,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 41c8a5e570528..a9eff59b6affa 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -995,7 +995,11 @@ pub fn compile_unit_metadata( 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 split_name = tcx + .output_filenames(LOCAL_CRATE) + .split_dwarf_file(tcx.sess.opts.debugging_opts.split_dwarf, Some(codegen_unit_name)) + .unwrap_or_default(); + let split_name = split_name.to_str().unwrap(); // FIXME(#60020): // @@ -1040,7 +1044,7 @@ pub fn compile_unit_metadata( split_name.len(), kind, 0, - true, + 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 4fe0384b36965..a5f125d114d28 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -20,7 +20,7 @@ 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, TargetMachineFactoryFn, + CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn, }; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::ModuleCodegen; @@ -332,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) } } } @@ -353,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)() { + + let split_dwarf_file = cgcx + .output_filenames + .split_dwarf_file(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_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 5001e97016ce7..123d1ae55fb97 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); } } }); @@ -446,6 +459,69 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>( } } +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 @@ -661,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 @@ -714,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; diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 615eeecf54ea5..c84b87964b845 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -274,8 +274,19 @@ impl ModuleConfig { } } -pub type TargetMachineFactoryFn = - Arc Result<::TargetMachine, String> + Send + Sync>; +/// 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>>; @@ -303,6 +314,7 @@ pub struct CodegenContext { 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, @@ -619,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); @@ -841,6 +859,7 @@ fn execute_copy_from_cache_work_item( name: module.name, kind: ModuleKind::Regular, object, + dwarf_object: None, bytecode: None, })) } @@ -1019,6 +1038,7 @@ fn start_executing_work( 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_session/src/config.rs b/compiler/rustc_session/src/config.rs index b5354513dc2ef..b3d4c6e3ec73b 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -550,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( @@ -583,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(); @@ -610,6 +616,26 @@ impl OutputFilenames { path.set_extension(extension); path } + + /// 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_file( + &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 { From 6c4350dc177a3cddb25fd4ac2d60bb5b52290176 Mon Sep 17 00:00:00 2001 From: David Wood Date: Sun, 8 Nov 2020 14:01:23 +0000 Subject: [PATCH 07/10] cg_clif: fix build with split dwarf This commit makes minor changes to the cranelift backend so that it can build given changes in cg_ssa for Split DWARF. Signed-off-by: David Wood --- compiler/rustc_codegen_cranelift/src/driver/aot.rs | 3 +++ 1 file changed, 3 insertions(+) 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 { From 2b22670bdf614374b9371a229e9bcf692f58c6b3 Mon Sep 17 00:00:00 2001 From: David Wood Date: Sun, 8 Nov 2020 17:26:59 +0000 Subject: [PATCH 08/10] tests: add run-make-fulldeps split-dwarf test This commit adds a run-make-fulldeps test which checks that a DWARF package file is emitted. Signed-off-by: David Wood --- src/test/run-make-fulldeps/split-dwarf/Makefile | 8 ++++++++ src/test/run-make-fulldeps/split-dwarf/foo.rs | 1 + 2 files changed, 9 insertions(+) create mode 100644 src/test/run-make-fulldeps/split-dwarf/Makefile create mode 100644 src/test/run-make-fulldeps/split-dwarf/foo.rs 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() {} From 99ad915e32744eb771e9a0968bf7d0d1f52a9a07 Mon Sep 17 00:00:00 2001 From: David Wood Date: Sun, 8 Nov 2020 17:27:33 +0000 Subject: [PATCH 09/10] compiletest: add split dwarf compare mode This commit adds a Split DWARF compare mode to compiletest so that debuginfo tests are also tested using Split DWARF in split mode (and manually in single mode). Signed-off-by: David Wood --- compiler/rustc_codegen_ssa/src/back/link.rs | 7 +++++++ src/bootstrap/test.rs | 7 ++++++- src/tools/compiletest/src/common.rs | 6 ++++++ src/tools/compiletest/src/header.rs | 2 ++ src/tools/compiletest/src/runtest.rs | 6 ++++++ 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 123d1ae55fb97..ccd4d103ddb7f 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1037,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 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/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 => {} } From ee073b5ec54a13b393071298acc54e1fd28cfcdf Mon Sep 17 00:00:00 2001 From: David Wood Date: Sun, 8 Nov 2020 17:17:37 +0000 Subject: [PATCH 10/10] cg_llvm: split dwarf filename and comp dir llvm-dwp concatenates `DW_AT_comp_dir` with `DW_AT_GNU_dwo_name` (only when `DW_AT_comp_dir` exists), which can result in it failing to find the DWARF object files. In earlier testing, `DW_AT_comp_dir` wasn't present in the final object and the current directory was the output directory. When running tests through compiletest, the working directory of the compilation is different from output directory and that resulted in `DW_AT_comp_dir` being in the object file (and set to the current working directory, rather than the output directory), and `DW_AT_GNU_dwo_name` being set to the full path (rather than just the filename), so llvm-dwp was failing. This commit changes the compilation directory provided to LLVM to match the output directory, where DWARF objects are output; and ensures that only the filename is used for `DW_AT_GNU_dwo_name`. Signed-off-by: David Wood --- compiler/rustc_codegen_llvm/src/back/lto.rs | 2 +- compiler/rustc_codegen_llvm/src/back/write.rs | 2 +- .../rustc_codegen_llvm/src/debuginfo/metadata.rs | 10 ++++++---- compiler/rustc_codegen_llvm/src/lib.rs | 2 +- compiler/rustc_session/src/config.rs | 13 ++++++++++++- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 834fc02c48ab8..29415973ed073 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -734,7 +734,7 @@ pub unsafe fn optimize_thin_module( let module_name = &thin_module.shared.module_names[thin_module.idx]; let split_dwarf_file = cgcx .output_filenames - .split_dwarf_file(cgcx.split_dwarf_kind, Some(module_name.to_str().unwrap())); + .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))?; diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index ef99c87053c76..3fda1e26dae9e 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -95,7 +95,7 @@ pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm: 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_file(tcx.sess.opts.debugging_opts.split_dwarf, Some(mod_name)); + .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()) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index a9eff59b6affa..fa285f3488f84 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -993,12 +993,14 @@ 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 out_dir = &tcx.output_filenames(LOCAL_CRATE).out_directory; let split_name = tcx .output_filenames(LOCAL_CRATE) - .split_dwarf_file(tcx.sess.opts.debugging_opts.split_dwarf, Some(codegen_unit_name)) + .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): @@ -1024,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, diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index a5f125d114d28..a58c2fbd8ab2c 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -356,7 +356,7 @@ impl ModuleLlvm { let split_dwarf_file = cgcx .output_filenames - .split_dwarf_file(cgcx.split_dwarf_kind, Some(name.to_str().unwrap())); + .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) { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b3d4c6e3ec73b..c20b11656b229 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -617,9 +617,20 @@ impl OutputFilenames { 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_file( + pub fn split_dwarf_path( &self, split_dwarf_kind: SplitDwarfKind, cgu_name: Option<&str>,