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>
This commit is contained in:
David Wood 2020-09-23 17:33:54 +01:00
parent 241160de72
commit e3fdae9d81
No known key found for this signature in database
GPG key ID: 2592E76C87381FD9
8 changed files with 228 additions and 46 deletions

View file

@ -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;

View file

@ -274,8 +274,19 @@ impl ModuleConfig {
}
}
pub type TargetMachineFactoryFn<B> =
Arc<dyn Fn() -> Result<<B as WriteBackendMethods>::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<PathBuf>,
}
pub type TargetMachineFactoryFn<B> = Arc<
dyn Fn(TargetMachineFactoryConfig) -> Result<<B as WriteBackendMethods>::TargetMachine, String>
+ Send
+ Sync,
>;
pub type ExportedSymbols = FxHashMap<CrateNum, Arc<Vec<(String, SymbolExportLevel)>>>;
@ -303,6 +314,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
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<B: ExtraBackendMethods>(
name: module.name,
kind: ModuleKind::Regular,
object,
dwarf_object: None,
bytecode: None,
}))
}
@ -1019,6 +1038,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
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.

View file

@ -64,13 +64,15 @@ impl<M> ModuleCodegen<M> {
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<PathBuf>,
pub dwarf_object: Option<PathBuf>,
pub bytecode: Option<PathBuf>,
}