Skip to content

Commit

Permalink
cg_llvm: implement split dwarf support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
davidtwco committed Dec 16, 2020
1 parent 241160d commit e3fdae9
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 46 deletions.
21 changes: 13 additions & 8 deletions compiler/rustc_codegen_llvm/src/back/lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -728,20 +730,23 @@ pub unsafe fn optimize_thin_module(
cgcx: &CodegenContext<LlvmCodegenBackend>,
) -> Result<ModuleCodegen<ModuleLlvm>, 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
// into that context. One day, however, we may do this for upstream
// 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(),
Expand Down
76 changes: 59 additions & 17 deletions compiler/rustc_codegen_llvm/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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())
}

Expand Down Expand Up @@ -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(),
Expand All @@ -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(),
)
};

Expand Down Expand Up @@ -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,
)
})?;
}

Expand All @@ -805,13 +836,23 @@ 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,
tm,
cpm,
llmod,
&obj_out,
dwo_out,
llvm::FileType::ObjectFile,
)
})?;
Expand Down Expand Up @@ -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,
))
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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):
//
Expand Down Expand Up @@ -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 {
Expand Down
12 changes: 9 additions & 3 deletions compiler/rustc_codegen_llvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) }
}
}

Expand All @@ -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();
Expand Down
100 changes: 88 additions & 12 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
}
}
});
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit e3fdae9

Please sign in to comment.