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

@ -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<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
@ -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(),

View File

@ -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,
))

View File

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

View File

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

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>,
}

View File

@ -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<PathBuf> {
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 {