Skip to content

Commit 2ba7ca2

Browse files
committed
Auto merge of rust-lang#77117 - davidtwco:issue-34651-split-dwarf, r=nagisa
cg_llvm: split dwarf support cc rust-lang#34651 This PR adds initial support for Split DWARF to rustc, based on the implementation in Clang. ##### Current Status This PR currently has functioning split-dwarf, running rustc with `-Zsplit-dwarf=split` when compiling a binary will produce a `dwp` alongside the binary, which contains the linked dwarf objects. ```shell-session $ rustc -Cdebuginfo=2 -Zsplit-dwarf=split -C save-temps ./foo.rs $ ls foo* foo foo.belfx9afw9cmv8.rcgu.dwo foo.belfx9afw9cmv8.rcgu.o foo.foo.7rcbfp3g-cgu.0.rcgu.dwo foo.foo.7rcbfp3g-cgu.0.rcgu.o foo.foo.7rcbfp3g-cgu.1.rcgu.dwo foo.foo.7rcbfp3g-cgu.1.rcgu.o foo.foo.7rcbfp3g-cgu.2.rcgu.dwo foo.foo.7rcbfp3g-cgu.2.rcgu.o foo.foo.7rcbfp3g-cgu.3.rcgu.dwo foo.foo.7rcbfp3g-cgu.3.rcgu.o foo.foo.7rcbfp3g-cgu.4.rcgu.dwo foo.foo.7rcbfp3g-cgu.4.rcgu.o foo.foo.7rcbfp3g-cgu.5.rcgu.dwo foo.foo.7rcbfp3g-cgu.5.rcgu.o foo.foo.7rcbfp3g-cgu.6.rcgu.dwo foo.foo.7rcbfp3g-cgu.6.rcgu.o foo.foo.7rcbfp3g-cgu.7.rcgu.dwo foo.foo.7rcbfp3g-cgu.7.rcgu.o foo.dwp foo.rs $ readelf -wi foo.foo.7rcbfp3g-cgu.0.rcgu.o # ... Compilation Unit @ offset 0x90: Length: 0x2c (32-bit) Version: 4 Abbrev Offset: 0x5b Pointer Size: 8 <0><9b>: Abbrev Number: 1 (DW_TAG_compile_unit) <9c> DW_AT_stmt_list : 0xe8 <a0> DW_AT_comp_dir : (indirect string, offset: 0x13b): /home/david/Projects/rust/rust0 <a4> DW_AT_GNU_dwo_name: (indirect string, offset: 0x15b): foo.foo.7rcbfp3g-cgu.0.rcgu.dwo <a8> DW_AT_GNU_dwo_id : 0x357472a2b032d7b9 <b0> DW_AT_low_pc : 0x0 <b8> DW_AT_ranges : 0x40 <bc> DW_AT_GNU_addr_base: 0x0 # ... ``` ##### To-Do I've opened this PR as a draft to get feedback and work out how we'd expect rustc to work when Split DWARF is requested. It might be easier to read the PR commit-by-commit. - [ ] Add error when Split DWARF is requested on platforms where it doesn't make sense. - [x] Determine whether or not there should be a single `dwo` output from rustc, or one per codegen-unit as exists currently. - [x] Add tests. - [x] Fix `single` mode - currently single mode doesn't change the invocation of `addPassesToEmitFile`, which is correct, but it also needs to change the split dwarf path provided to `createCompileUnit` and `createTargetMachine` so that it's just the final binary (currently it is still a non-existent `dwo` file). r? `@nagisa` cc `@michaelwoerister` `@eddyb` `@alexcrichton` `@rust-lang/wg-incr-comp`
2 parents 268cbfe + ee073b5 commit 2ba7ca2

File tree

22 files changed

+431
-127
lines changed

22 files changed

+431
-127
lines changed

compiler/rustc_codegen_cranelift/src/driver/aot.rs

+3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ fn emit_module(
7575
name,
7676
kind,
7777
object: Some(tmp_file),
78+
dwarf_object: None,
7879
bytecode: None,
7980
},
8081
work_product,
@@ -111,6 +112,7 @@ fn reuse_workproduct_for_cgu(
111112
name: cgu.name().to_string(),
112113
kind: ModuleKind::Regular,
113114
object,
115+
dwarf_object: None,
114116
bytecode: None,
115117
}
116118
}
@@ -290,6 +292,7 @@ pub(super) fn run_aot(
290292
name: metadata_cgu_name,
291293
kind: ModuleKind::Metadata,
292294
object: Some(tmp_file),
295+
dwarf_object: None,
293296
bytecode: None,
294297
})
295298
} else {

compiler/rustc_codegen_llvm/src/back/lto.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use crate::llvm::{self, build_string, False, True};
66
use crate::{LlvmCodegenBackend, ModuleLlvm};
77
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared};
88
use rustc_codegen_ssa::back::symbol_export;
9-
use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig};
9+
use rustc_codegen_ssa::back::write::{
10+
CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig,
11+
};
1012
use rustc_codegen_ssa::traits::*;
1113
use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind};
1214
use rustc_data_structures::fx::FxHashMap;
@@ -728,20 +730,23 @@ pub unsafe fn optimize_thin_module(
728730
cgcx: &CodegenContext<LlvmCodegenBackend>,
729731
) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
730732
let diag_handler = cgcx.create_diag_handler();
731-
let tm = (cgcx.tm_factory.0)().map_err(|e| write::llvm_err(&diag_handler, &e))?;
733+
734+
let module_name = &thin_module.shared.module_names[thin_module.idx];
735+
let split_dwarf_file = cgcx
736+
.output_filenames
737+
.split_dwarf_filename(cgcx.split_dwarf_kind, Some(module_name.to_str().unwrap()));
738+
let tm_factory_config = TargetMachineFactoryConfig { split_dwarf_file };
739+
let tm =
740+
(cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&diag_handler, &e))?;
732741

733742
// Right now the implementation we've got only works over serialized
734743
// modules, so we create a fresh new LLVM context and parse the module
735744
// into that context. One day, however, we may do this for upstream
736745
// crates but for locally codegened modules we may be able to reuse
737746
// that LLVM Context and Module.
738747
let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
739-
let llmod_raw = parse_module(
740-
llcx,
741-
&thin_module.shared.module_names[thin_module.idx],
742-
thin_module.data(),
743-
&diag_handler,
744-
)? as *const _;
748+
let llmod_raw =
749+
parse_module(llcx, &module_name, thin_module.data(), &diag_handler)? as *const _;
745750
let module = ModuleCodegen {
746751
module_llvm: ModuleLlvm { llmod_raw, llcx, tm },
747752
name: thin_module.name().to_string(),

compiler/rustc_codegen_llvm/src/back/write.rs

+62-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ use crate::llvm_util;
1111
use crate::type_::Type;
1212
use crate::LlvmCodegenBackend;
1313
use crate::ModuleLlvm;
14-
use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig};
14+
use rustc_codegen_ssa::back::write::{
15+
BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig,
16+
TargetMachineFactoryFn,
17+
};
1518
use rustc_codegen_ssa::traits::*;
1619
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
1720
use rustc_data_structures::small_c_str::SmallCStr;
@@ -20,7 +23,9 @@ use rustc_fs_util::{link_or_copy, path_to_c_string};
2023
use rustc_hir::def_id::LOCAL_CRATE;
2124
use rustc_middle::bug;
2225
use rustc_middle::ty::TyCtxt;
23-
use rustc_session::config::{self, Lto, OutputType, Passes, SanitizerSet, SwitchWithOptPath};
26+
use rustc_session::config::{
27+
self, Lto, OutputType, Passes, SanitizerSet, SplitDwarfKind, SwitchWithOptPath,
28+
};
2429
use rustc_session::Session;
2530
use rustc_span::symbol::sym;
2631
use rustc_span::InnerSpan;
@@ -49,11 +54,31 @@ pub fn write_output_file(
4954
pm: &llvm::PassManager<'ll>,
5055
m: &'ll llvm::Module,
5156
output: &Path,
57+
dwo_output: Option<&Path>,
5258
file_type: llvm::FileType,
5359
) -> Result<(), FatalError> {
5460
unsafe {
5561
let output_c = path_to_c_string(output);
56-
let result = llvm::LLVMRustWriteOutputFile(target, pm, m, output_c.as_ptr(), file_type);
62+
let result = if let Some(dwo_output) = dwo_output {
63+
let dwo_output_c = path_to_c_string(dwo_output);
64+
llvm::LLVMRustWriteOutputFile(
65+
target,
66+
pm,
67+
m,
68+
output_c.as_ptr(),
69+
dwo_output_c.as_ptr(),
70+
file_type,
71+
)
72+
} else {
73+
llvm::LLVMRustWriteOutputFile(
74+
target,
75+
pm,
76+
m,
77+
output_c.as_ptr(),
78+
std::ptr::null(),
79+
file_type,
80+
)
81+
};
5782
result.into_result().map_err(|()| {
5883
let msg = format!("could not write output to {}", output.display());
5984
llvm_err(handler, &msg)
@@ -62,12 +87,17 @@ pub fn write_output_file(
6287
}
6388

6489
pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm::TargetMachine {
65-
target_machine_factory(sess, config::OptLevel::No)()
90+
let config = TargetMachineFactoryConfig { split_dwarf_file: None };
91+
target_machine_factory(sess, config::OptLevel::No)(config)
6692
.unwrap_or_else(|err| llvm_err(sess.diagnostic(), &err).raise())
6793
}
6894

69-
pub fn create_target_machine(tcx: TyCtxt<'_>) -> &'static mut llvm::TargetMachine {
70-
target_machine_factory(&tcx.sess, tcx.backend_optimization_level(LOCAL_CRATE))()
95+
pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut llvm::TargetMachine {
96+
let split_dwarf_file = tcx
97+
.output_filenames(LOCAL_CRATE)
98+
.split_dwarf_filename(tcx.sess.opts.debugging_opts.split_dwarf, Some(mod_name));
99+
let config = TargetMachineFactoryConfig { split_dwarf_file };
100+
target_machine_factory(&tcx.sess, tcx.backend_optimization_level(LOCAL_CRATE))(config)
71101
.unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), &err).raise())
72102
}
73103

@@ -122,7 +152,7 @@ fn to_llvm_code_model(code_model: Option<CodeModel>) -> llvm::CodeModel {
122152
pub fn target_machine_factory(
123153
sess: &Session,
124154
optlvl: config::OptLevel,
125-
) -> Arc<dyn Fn() -> Result<&'static mut llvm::TargetMachine, String> + Send + Sync> {
155+
) -> TargetMachineFactoryFn<LlvmCodegenBackend> {
126156
let reloc_model = to_llvm_relocation_model(sess.relocation_model());
127157

128158
let (opt_level, _) = to_llvm_opt_settings(optlvl);
@@ -163,7 +193,10 @@ pub fn target_machine_factory(
163193
let use_init_array =
164194
!sess.opts.debugging_opts.use_ctors_section.unwrap_or(sess.target.use_ctors_section);
165195

166-
Arc::new(move || {
196+
Arc::new(move |config: TargetMachineFactoryConfig| {
197+
let split_dwarf_file = config.split_dwarf_file.unwrap_or_default();
198+
let split_dwarf_file = CString::new(split_dwarf_file.to_str().unwrap()).unwrap();
199+
167200
let tm = unsafe {
168201
llvm::LLVMRustCreateTargetMachine(
169202
triple.as_ptr(),
@@ -182,6 +215,7 @@ pub fn target_machine_factory(
182215
emit_stack_size_section,
183216
relax_elf_relocations,
184217
use_init_array,
218+
split_dwarf_file.as_ptr(),
185219
)
186220
};
187221

@@ -785,7 +819,15 @@ pub(crate) unsafe fn codegen(
785819
llmod
786820
};
787821
with_codegen(tm, llmod, config.no_builtins, |cpm| {
788-
write_output_file(diag_handler, tm, cpm, llmod, &path, llvm::FileType::AssemblyFile)
822+
write_output_file(
823+
diag_handler,
824+
tm,
825+
cpm,
826+
llmod,
827+
&path,
828+
None,
829+
llvm::FileType::AssemblyFile,
830+
)
789831
})?;
790832
}
791833

@@ -794,13 +836,23 @@ pub(crate) unsafe fn codegen(
794836
let _timer = cgcx
795837
.prof
796838
.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]);
839+
840+
let dwo_out = cgcx.output_filenames.temp_path_dwo(module_name);
841+
let dwo_out = match cgcx.split_dwarf_kind {
842+
// Don't change how DWARF is emitted in single mode (or when disabled).
843+
SplitDwarfKind::None | SplitDwarfKind::Single => None,
844+
// Emit (a subset of the) DWARF into a separate file in split mode.
845+
SplitDwarfKind::Split => Some(dwo_out.as_path()),
846+
};
847+
797848
with_codegen(tm, llmod, config.no_builtins, |cpm| {
798849
write_output_file(
799850
diag_handler,
800851
tm,
801852
cpm,
802853
llmod,
803854
&obj_out,
855+
dwo_out,
804856
llvm::FileType::ObjectFile,
805857
)
806858
})?;
@@ -828,6 +880,7 @@ pub(crate) unsafe fn codegen(
828880

829881
Ok(module.into_compiled_module(
830882
config.emit_obj != EmitObj::None,
883+
cgcx.split_dwarf_kind == SplitDwarfKind::Split,
831884
config.emit_bc,
832885
&cgcx.output_filenames,
833886
))

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -993,9 +993,15 @@ pub fn compile_unit_metadata(
993993
let producer = format!("clang LLVM ({})", rustc_producer);
994994

995995
let name_in_debuginfo = name_in_debuginfo.to_string_lossy();
996-
let work_dir = tcx.sess.working_dir.0.to_string_lossy();
997996
let flags = "\0";
998-
let split_name = "";
997+
998+
let out_dir = &tcx.output_filenames(LOCAL_CRATE).out_directory;
999+
let split_name = tcx
1000+
.output_filenames(LOCAL_CRATE)
1001+
.split_dwarf_filename(tcx.sess.opts.debugging_opts.split_dwarf, Some(codegen_unit_name))
1002+
.unwrap_or_default();
1003+
let out_dir = out_dir.to_str().unwrap();
1004+
let split_name = split_name.to_str().unwrap();
9991005

10001006
// FIXME(#60020):
10011007
//
@@ -1020,8 +1026,8 @@ pub fn compile_unit_metadata(
10201026
debug_context.builder,
10211027
name_in_debuginfo.as_ptr().cast(),
10221028
name_in_debuginfo.len(),
1023-
work_dir.as_ptr().cast(),
1024-
work_dir.len(),
1029+
out_dir.as_ptr().cast(),
1030+
out_dir.len(),
10251031
llvm::ChecksumKind::None,
10261032
ptr::null(),
10271033
0,
@@ -1039,6 +1045,8 @@ pub fn compile_unit_metadata(
10391045
split_name.as_ptr().cast(),
10401046
split_name.len(),
10411047
kind,
1048+
0,
1049+
tcx.sess.opts.debugging_opts.split_dwarf_inlining,
10421050
);
10431051

10441052
if tcx.sess.opts.debugging_opts.profile {

compiler/rustc_codegen_llvm/src/lib.rs

+12-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ use back::write::{create_informational_target_machine, create_target_machine};
1919
pub use llvm_util::target_features;
2020
use rustc_ast::expand::allocator::AllocatorKind;
2121
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
22-
use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig};
22+
use rustc_codegen_ssa::back::write::{
23+
CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn,
24+
};
2325
use rustc_codegen_ssa::traits::*;
2426
use rustc_codegen_ssa::ModuleCodegen;
2527
use rustc_codegen_ssa::{CodegenResults, CompiledModule};
@@ -34,7 +36,6 @@ use rustc_span::symbol::Symbol;
3436

3537
use std::any::Any;
3638
use std::ffi::CStr;
37-
use std::sync::Arc;
3839

3940
mod back {
4041
pub mod archive;
@@ -109,7 +110,7 @@ impl ExtraBackendMethods for LlvmCodegenBackend {
109110
&self,
110111
sess: &Session,
111112
optlvl: OptLevel,
112-
) -> Arc<dyn Fn() -> Result<&'static mut llvm::TargetMachine, String> + Send + Sync> {
113+
) -> TargetMachineFactoryFn<Self> {
113114
back::write::target_machine_factory(sess, optlvl)
114115
}
115116
fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str {
@@ -331,7 +332,7 @@ impl ModuleLlvm {
331332
unsafe {
332333
let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names());
333334
let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _;
334-
ModuleLlvm { llmod_raw, llcx, tm: create_target_machine(tcx) }
335+
ModuleLlvm { llmod_raw, llcx, tm: create_target_machine(tcx, mod_name) }
335336
}
336337
}
337338

@@ -352,7 +353,13 @@ impl ModuleLlvm {
352353
unsafe {
353354
let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
354355
let llmod_raw = back::lto::parse_module(llcx, name, buffer, handler)?;
355-
let tm = match (cgcx.tm_factory.0)() {
356+
357+
let split_dwarf_file = cgcx
358+
.output_filenames
359+
.split_dwarf_filename(cgcx.split_dwarf_kind, Some(name.to_str().unwrap()));
360+
let tm_factory_config = TargetMachineFactoryConfig { split_dwarf_file };
361+
362+
let tm = match (cgcx.tm_factory)(tm_factory_config) {
356363
Ok(m) => m,
357364
Err(e) => {
358365
handler.struct_err(&e).emit();

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1830,6 +1830,8 @@ extern "C" {
18301830
SplitName: *const c_char,
18311831
SplitNameLen: size_t,
18321832
kind: DebugEmissionKind,
1833+
DWOId: u64,
1834+
SplitDebugInlining: bool,
18331835
) -> &'a DIDescriptor;
18341836

18351837
pub fn LLVMRustDIBuilderCreateFile(
@@ -2151,6 +2153,7 @@ extern "C" {
21512153
EmitStackSizeSection: bool,
21522154
RelaxELFRelocations: bool,
21532155
UseInitArray: bool,
2156+
SplitDwarfFile: *const c_char,
21542157
) -> Option<&'static mut TargetMachine>;
21552158
pub fn LLVMRustDisposeTargetMachine(T: &'static mut TargetMachine);
21562159
pub fn LLVMRustAddBuilderLibraryInfo(
@@ -2179,6 +2182,7 @@ extern "C" {
21792182
PM: &PassManager<'a>,
21802183
M: &'a Module,
21812184
Output: *const c_char,
2185+
DwoOutput: *const c_char,
21822186
FileType: FileType,
21832187
) -> LLVMRustResult;
21842188
pub fn LLVMRustOptimizeWithNewPassManager(

0 commit comments

Comments
 (0)