Skip to content

Commit e3fdae9

Browse files
committed
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 <david@davidtw.co>
1 parent 241160d commit e3fdae9

File tree

8 files changed

+228
-46
lines changed

8 files changed

+228
-46
lines changed

Diff for: 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)().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_file(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(),

Diff for: compiler/rustc_codegen_llvm/src/back/write.rs

+59-17
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use crate::type_::Type;
1212
use crate::LlvmCodegenBackend;
1313
use crate::ModuleLlvm;
1414
use rustc_codegen_ssa::back::write::{
15-
BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryFn,
15+
BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig,
16+
TargetMachineFactoryFn,
1617
};
1718
use rustc_codegen_ssa::traits::*;
1819
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
@@ -22,7 +23,9 @@ use rustc_fs_util::{link_or_copy, path_to_c_string};
2223
use rustc_hir::def_id::LOCAL_CRATE;
2324
use rustc_middle::bug;
2425
use rustc_middle::ty::TyCtxt;
25-
use rustc_session::config::{self, Lto, OutputType, Passes, SanitizerSet, SwitchWithOptPath};
26+
use rustc_session::config::{
27+
self, Lto, OutputType, Passes, SanitizerSet, SplitDwarfKind, SwitchWithOptPath,
28+
};
2629
use rustc_session::Session;
2730
use rustc_span::symbol::sym;
2831
use rustc_span::InnerSpan;
@@ -51,18 +54,31 @@ pub fn write_output_file(
5154
pm: &llvm::PassManager<'ll>,
5255
m: &'ll llvm::Module,
5356
output: &Path,
57+
dwo_output: Option<&Path>,
5458
file_type: llvm::FileType,
5559
) -> Result<(), FatalError> {
5660
unsafe {
5761
let output_c = path_to_c_string(output);
58-
let result = llvm::LLVMRustWriteOutputFile(
59-
target,
60-
pm,
61-
m,
62-
output_c.as_ptr(),
63-
std::ptr::null(),
64-
file_type,
65-
);
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+
};
6682
result.into_result().map_err(|()| {
6783
let msg = format!("could not write output to {}", output.display());
6884
llvm_err(handler, &msg)
@@ -71,12 +87,17 @@ pub fn write_output_file(
7187
}
7288

7389
pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm::TargetMachine {
74-
target_machine_factory(sess, config::OptLevel::No)()
90+
let config = TargetMachineFactoryConfig { split_dwarf_file: None };
91+
target_machine_factory(sess, config::OptLevel::No)(config)
7592
.unwrap_or_else(|err| llvm_err(sess.diagnostic(), &err).raise())
7693
}
7794

78-
pub fn create_target_machine(tcx: TyCtxt<'_>) -> &'static mut llvm::TargetMachine {
79-
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_file(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)
80101
.unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), &err).raise())
81102
}
82103

@@ -172,8 +193,10 @@ pub fn target_machine_factory(
172193
let use_init_array =
173194
!sess.opts.debugging_opts.use_ctors_section.unwrap_or(sess.target.use_ctors_section);
174195

175-
Arc::new(move || {
176-
let split_dwarf_file = std::ptr::null();
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+
177200
let tm = unsafe {
178201
llvm::LLVMRustCreateTargetMachine(
179202
triple.as_ptr(),
@@ -192,7 +215,7 @@ pub fn target_machine_factory(
192215
emit_stack_size_section,
193216
relax_elf_relocations,
194217
use_init_array,
195-
split_dwarf_file,
218+
split_dwarf_file.as_ptr(),
196219
)
197220
};
198221

@@ -796,7 +819,15 @@ pub(crate) unsafe fn codegen(
796819
llmod
797820
};
798821
with_codegen(tm, llmod, config.no_builtins, |cpm| {
799-
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+
)
800831
})?;
801832
}
802833

@@ -805,13 +836,23 @@ pub(crate) unsafe fn codegen(
805836
let _timer = cgcx
806837
.prof
807838
.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+
808848
with_codegen(tm, llmod, config.no_builtins, |cpm| {
809849
write_output_file(
810850
diag_handler,
811851
tm,
812852
cpm,
813853
llmod,
814854
&obj_out,
855+
dwo_out,
815856
llvm::FileType::ObjectFile,
816857
)
817858
})?;
@@ -839,6 +880,7 @@ pub(crate) unsafe fn codegen(
839880

840881
Ok(module.into_compiled_module(
841882
config.emit_obj != EmitObj::None,
883+
cgcx.split_dwarf_kind == SplitDwarfKind::Split,
842884
config.emit_bc,
843885
&cgcx.output_filenames,
844886
))

Diff for: compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,11 @@ pub fn compile_unit_metadata(
995995
let name_in_debuginfo = name_in_debuginfo.to_string_lossy();
996996
let work_dir = tcx.sess.working_dir.0.to_string_lossy();
997997
let flags = "\0";
998-
let split_name = "";
998+
let split_name = tcx
999+
.output_filenames(LOCAL_CRATE)
1000+
.split_dwarf_file(tcx.sess.opts.debugging_opts.split_dwarf, Some(codegen_unit_name))
1001+
.unwrap_or_default();
1002+
let split_name = split_name.to_str().unwrap();
9991003

10001004
// FIXME(#60020):
10011005
//
@@ -1040,7 +1044,7 @@ pub fn compile_unit_metadata(
10401044
split_name.len(),
10411045
kind,
10421046
0,
1043-
true,
1047+
tcx.sess.opts.debugging_opts.split_dwarf_inlining,
10441048
);
10451049

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

Diff for: compiler/rustc_codegen_llvm/src/lib.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub use llvm_util::target_features;
2020
use rustc_ast::expand::allocator::AllocatorKind;
2121
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
2222
use rustc_codegen_ssa::back::write::{
23-
CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryFn,
23+
CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn,
2424
};
2525
use rustc_codegen_ssa::traits::*;
2626
use rustc_codegen_ssa::ModuleCodegen;
@@ -332,7 +332,7 @@ impl ModuleLlvm {
332332
unsafe {
333333
let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names());
334334
let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _;
335-
ModuleLlvm { llmod_raw, llcx, tm: create_target_machine(tcx) }
335+
ModuleLlvm { llmod_raw, llcx, tm: create_target_machine(tcx, mod_name) }
336336
}
337337
}
338338

@@ -353,7 +353,13 @@ impl ModuleLlvm {
353353
unsafe {
354354
let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
355355
let llmod_raw = back::lto::parse_module(llcx, name, buffer, handler)?;
356-
let tm = match (cgcx.tm_factory)() {
356+
357+
let split_dwarf_file = cgcx
358+
.output_filenames
359+
.split_dwarf_file(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) {
357363
Ok(m) => m,
358364
Err(e) => {
359365
handler.struct_err(&e).emit();

Diff for: compiler/rustc_codegen_ssa/src/back/link.rs

+88-12
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ use super::archive::ArchiveBuilder;
2121
use super::command::Command;
2222
use super::linker::{self, Linker};
2323
use super::rpath::{self, RPathConfig};
24-
use crate::{looks_like_rust_object_file, CodegenResults, CrateInfo, METADATA_FILENAME};
24+
use crate::{
25+
looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, METADATA_FILENAME,
26+
};
2527

2628
use cc::windows_registry;
2729
use tempfile::Builder as TempFileBuilder;
@@ -96,6 +98,9 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
9698
path.as_ref(),
9799
target_cpu,
98100
);
101+
if sess.opts.debugging_opts.split_dwarf == config::SplitDwarfKind::Split {
102+
link_dwarf_object(sess, &out_filename);
103+
}
99104
}
100105
}
101106
if sess.opts.json_artifact_notifications {
@@ -107,22 +112,30 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
107112
// Remove the temporary object file and metadata if we aren't saving temps
108113
sess.time("link_binary_remove_temps", || {
109114
if !sess.opts.cg.save_temps {
115+
let remove_temps_from_module = |module: &CompiledModule| {
116+
if let Some(ref obj) = module.object {
117+
remove(sess, obj);
118+
}
119+
120+
if let Some(ref obj) = module.dwarf_object {
121+
remove(sess, obj);
122+
}
123+
};
124+
110125
if sess.opts.output_types.should_codegen()
111126
&& !preserve_objects_for_their_debuginfo(sess)
112127
{
113-
for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
114-
remove(sess, obj);
128+
for module in &codegen_results.modules {
129+
remove_temps_from_module(module);
115130
}
116131
}
132+
117133
if let Some(ref metadata_module) = codegen_results.metadata_module {
118-
if let Some(ref obj) = metadata_module.object {
119-
remove(sess, obj);
120-
}
134+
remove_temps_from_module(metadata_module);
121135
}
136+
122137
if let Some(ref allocator_module) = codegen_results.allocator_module {
123-
if let Some(ref obj) = allocator_module.object {
124-
remove(sess, obj);
125-
}
138+
remove_temps_from_module(allocator_module);
126139
}
127140
}
128141
});
@@ -446,6 +459,69 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>(
446459
}
447460
}
448461

462+
fn escape_stdout_stderr_string(s: &[u8]) -> String {
463+
str::from_utf8(s).map(|s| s.to_owned()).unwrap_or_else(|_| {
464+
let mut x = "Non-UTF-8 output: ".to_string();
465+
x.extend(s.iter().flat_map(|&b| ascii::escape_default(b)).map(char::from));
466+
x
467+
})
468+
}
469+
470+
const LLVM_DWP_EXECUTABLE: &'static str = "rust-llvm-dwp";
471+
472+
/// Invoke `llvm-dwp` (shipped alongside rustc) to link `dwo` files from Split DWARF into a `dwp`
473+
/// file.
474+
fn link_dwarf_object<'a>(sess: &'a Session, executable_out_filename: &Path) {
475+
info!("preparing dwp to {}.dwp", executable_out_filename.to_str().unwrap());
476+
477+
let dwp_out_filename = executable_out_filename.with_extension("dwp");
478+
let mut cmd = Command::new(LLVM_DWP_EXECUTABLE);
479+
cmd.arg("-e");
480+
cmd.arg(executable_out_filename);
481+
cmd.arg("-o");
482+
cmd.arg(&dwp_out_filename);
483+
484+
let mut new_path = sess.host_filesearch(PathKind::All).get_tools_search_paths(false);
485+
if let Some(path) = env::var_os("PATH") {
486+
new_path.extend(env::split_paths(&path));
487+
}
488+
let new_path = env::join_paths(new_path).unwrap();
489+
cmd.env("PATH", new_path);
490+
491+
info!("{:?}", &cmd);
492+
match sess.time("run_dwp", || cmd.output()) {
493+
Ok(prog) if !prog.status.success() => {
494+
sess.struct_err(&format!(
495+
"linking dwarf objects with `{}` failed: {}",
496+
LLVM_DWP_EXECUTABLE, prog.status
497+
))
498+
.note(&format!("{:?}", &cmd))
499+
.note(&escape_stdout_stderr_string(&prog.stdout))
500+
.note(&escape_stdout_stderr_string(&prog.stderr))
501+
.emit();
502+
info!("linker stderr:\n{}", escape_stdout_stderr_string(&prog.stderr));
503+
info!("linker stdout:\n{}", escape_stdout_stderr_string(&prog.stdout));
504+
}
505+
Ok(_) => {}
506+
Err(e) => {
507+
let dwp_not_found = e.kind() == io::ErrorKind::NotFound;
508+
let mut err = if dwp_not_found {
509+
sess.struct_err(&format!("linker `{}` not found", LLVM_DWP_EXECUTABLE))
510+
} else {
511+
sess.struct_err(&format!("could not exec the linker `{}`", LLVM_DWP_EXECUTABLE))
512+
};
513+
514+
err.note(&e.to_string());
515+
516+
if !dwp_not_found {
517+
err.note(&format!("{:?}", &cmd));
518+
}
519+
520+
err.emit();
521+
}
522+
}
523+
}
524+
449525
/// Create a dynamic library or executable.
450526
///
451527
/// 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>>(
661737
prog.status
662738
))
663739
.note(&format!("{:?}", &cmd))
664-
.note(&escape_string(&output))
740+
.note(&escape_stdout_stderr_string(&output))
665741
.emit();
666742

667743
// If MSVC's `link.exe` was expected but the return code
@@ -714,8 +790,8 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
714790

715791
sess.abort_if_errors();
716792
}
717-
info!("linker stderr:\n{}", escape_string(&prog.stderr));
718-
info!("linker stdout:\n{}", escape_string(&prog.stdout));
793+
info!("linker stderr:\n{}", escape_stdout_stderr_string(&prog.stderr));
794+
info!("linker stdout:\n{}", escape_stdout_stderr_string(&prog.stdout));
719795
}
720796
Err(e) => {
721797
let linker_not_found = e.kind() == io::ErrorKind::NotFound;

0 commit comments

Comments
 (0)