mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-21 20:23:21 +00:00
Auto merge of #77117 - davidtwco:issue-34651-split-dwarf, r=nagisa
cg_llvm: split dwarf support cc #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`
This commit is contained in:
commit
2ba7ca2bbb
@ -75,6 +75,7 @@ fn emit_module(
|
||||
name,
|
||||
kind,
|
||||
object: Some(tmp_file),
|
||||
dwarf_object: None,
|
||||
bytecode: None,
|
||||
},
|
||||
work_product,
|
||||
@ -111,6 +112,7 @@ fn reuse_workproduct_for_cgu(
|
||||
name: cgu.name().to_string(),
|
||||
kind: ModuleKind::Regular,
|
||||
object,
|
||||
dwarf_object: None,
|
||||
bytecode: None,
|
||||
}
|
||||
}
|
||||
@ -290,6 +292,7 @@ pub(super) fn run_aot(
|
||||
name: metadata_cgu_name,
|
||||
kind: ModuleKind::Metadata,
|
||||
object: Some(tmp_file),
|
||||
dwarf_object: None,
|
||||
bytecode: None,
|
||||
})
|
||||
} else {
|
||||
|
@ -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.0)().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_filename(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(),
|
||||
|
@ -11,7 +11,10 @@ use crate::llvm_util;
|
||||
use crate::type_::Type;
|
||||
use crate::LlvmCodegenBackend;
|
||||
use crate::ModuleLlvm;
|
||||
use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig};
|
||||
use rustc_codegen_ssa::back::write::{
|
||||
BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig,
|
||||
TargetMachineFactoryFn,
|
||||
};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
|
||||
use rustc_data_structures::small_c_str::SmallCStr;
|
||||
@ -20,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;
|
||||
@ -49,11 +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(), 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)
|
||||
@ -62,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_filename(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())
|
||||
}
|
||||
|
||||
@ -122,7 +152,7 @@ fn to_llvm_code_model(code_model: Option<CodeModel>) -> llvm::CodeModel {
|
||||
pub fn target_machine_factory(
|
||||
sess: &Session,
|
||||
optlvl: config::OptLevel,
|
||||
) -> Arc<dyn Fn() -> Result<&'static mut llvm::TargetMachine, String> + Send + Sync> {
|
||||
) -> TargetMachineFactoryFn<LlvmCodegenBackend> {
|
||||
let reloc_model = to_llvm_relocation_model(sess.relocation_model());
|
||||
|
||||
let (opt_level, _) = to_llvm_opt_settings(optlvl);
|
||||
@ -163,7 +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 || {
|
||||
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(),
|
||||
@ -182,6 +215,7 @@ pub fn target_machine_factory(
|
||||
emit_stack_size_section,
|
||||
relax_elf_relocations,
|
||||
use_init_array,
|
||||
split_dwarf_file.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
@ -785,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,
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
@ -794,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,
|
||||
@ -801,6 +852,7 @@ pub(crate) unsafe fn codegen(
|
||||
cpm,
|
||||
llmod,
|
||||
&obj_out,
|
||||
dwo_out,
|
||||
llvm::FileType::ObjectFile,
|
||||
)
|
||||
})?;
|
||||
@ -828,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,
|
||||
))
|
||||
|
@ -993,9 +993,15 @@ pub fn compile_unit_metadata(
|
||||
let producer = format!("clang LLVM ({})", rustc_producer);
|
||||
|
||||
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 out_dir = &tcx.output_filenames(LOCAL_CRATE).out_directory;
|
||||
let split_name = tcx
|
||||
.output_filenames(LOCAL_CRATE)
|
||||
.split_dwarf_filename(tcx.sess.opts.debugging_opts.split_dwarf, Some(codegen_unit_name))
|
||||
.unwrap_or_default();
|
||||
let out_dir = out_dir.to_str().unwrap();
|
||||
let split_name = split_name.to_str().unwrap();
|
||||
|
||||
// FIXME(#60020):
|
||||
//
|
||||
@ -1020,8 +1026,8 @@ pub fn compile_unit_metadata(
|
||||
debug_context.builder,
|
||||
name_in_debuginfo.as_ptr().cast(),
|
||||
name_in_debuginfo.len(),
|
||||
work_dir.as_ptr().cast(),
|
||||
work_dir.len(),
|
||||
out_dir.as_ptr().cast(),
|
||||
out_dir.len(),
|
||||
llvm::ChecksumKind::None,
|
||||
ptr::null(),
|
||||
0,
|
||||
@ -1039,6 +1045,8 @@ pub fn compile_unit_metadata(
|
||||
split_name.as_ptr().cast(),
|
||||
split_name.len(),
|
||||
kind,
|
||||
0,
|
||||
tcx.sess.opts.debugging_opts.split_dwarf_inlining,
|
||||
);
|
||||
|
||||
if tcx.sess.opts.debugging_opts.profile {
|
||||
|
@ -19,7 +19,9 @@ use back::write::{create_informational_target_machine, create_target_machine};
|
||||
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};
|
||||
use rustc_codegen_ssa::back::write::{
|
||||
CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn,
|
||||
};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_codegen_ssa::ModuleCodegen;
|
||||
use rustc_codegen_ssa::{CodegenResults, CompiledModule};
|
||||
@ -34,7 +36,6 @@ use rustc_span::symbol::Symbol;
|
||||
|
||||
use std::any::Any;
|
||||
use std::ffi::CStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod back {
|
||||
pub mod archive;
|
||||
@ -109,7 +110,7 @@ impl ExtraBackendMethods for LlvmCodegenBackend {
|
||||
&self,
|
||||
sess: &Session,
|
||||
optlvl: OptLevel,
|
||||
) -> Arc<dyn Fn() -> Result<&'static mut llvm::TargetMachine, String> + Send + Sync> {
|
||||
) -> TargetMachineFactoryFn<Self> {
|
||||
back::write::target_machine_factory(sess, optlvl)
|
||||
}
|
||||
fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str {
|
||||
@ -331,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) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,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.0)() {
|
||||
|
||||
let split_dwarf_file = cgcx
|
||||
.output_filenames
|
||||
.split_dwarf_filename(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();
|
||||
|
@ -1830,6 +1830,8 @@ extern "C" {
|
||||
SplitName: *const c_char,
|
||||
SplitNameLen: size_t,
|
||||
kind: DebugEmissionKind,
|
||||
DWOId: u64,
|
||||
SplitDebugInlining: bool,
|
||||
) -> &'a DIDescriptor;
|
||||
|
||||
pub fn LLVMRustDIBuilderCreateFile(
|
||||
@ -2151,6 +2153,7 @@ extern "C" {
|
||||
EmitStackSizeSection: bool,
|
||||
RelaxELFRelocations: bool,
|
||||
UseInitArray: bool,
|
||||
SplitDwarfFile: *const c_char,
|
||||
) -> Option<&'static mut TargetMachine>;
|
||||
pub fn LLVMRustDisposeTargetMachine(T: &'static mut TargetMachine);
|
||||
pub fn LLVMRustAddBuilderLibraryInfo(
|
||||
@ -2179,6 +2182,7 @@ extern "C" {
|
||||
PM: &PassManager<'a>,
|
||||
M: &'a Module,
|
||||
Output: *const c_char,
|
||||
DwoOutput: *const c_char,
|
||||
FileType: FileType,
|
||||
) -> LLVMRustResult;
|
||||
pub fn LLVMRustOptimizeWithNewPassManager(
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -279,12 +292,12 @@ pub fn emit_metadata(sess: &Session, metadata: &EncodedMetadata, tmpdir: &MaybeT
|
||||
out_filename
|
||||
}
|
||||
|
||||
// Create an 'rlib'
|
||||
//
|
||||
// An rlib in its current incarnation is essentially a renamed .a file. The
|
||||
// rlib primarily contains the object file of the crate, but it also contains
|
||||
// all of the object files from native libraries. This is done by unzipping
|
||||
// native libraries and inserting all of the contents into this archive.
|
||||
/// Create an 'rlib'.
|
||||
///
|
||||
/// An rlib in its current incarnation is essentially a renamed .a file. The rlib primarily contains
|
||||
/// the object file of the crate, but it also contains all of the object files from native
|
||||
/// libraries. This is done by unzipping native libraries and inserting all of the contents into
|
||||
/// this archive.
|
||||
fn link_rlib<'a, B: ArchiveBuilder<'a>>(
|
||||
sess: &'a Session,
|
||||
codegen_results: &CodegenResults,
|
||||
@ -379,18 +392,17 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
|
||||
ab
|
||||
}
|
||||
|
||||
// Create a static archive
|
||||
//
|
||||
// This is essentially the same thing as an rlib, but it also involves adding
|
||||
// all of the upstream crates' objects into the archive. This will slurp in
|
||||
// all of the native libraries of upstream dependencies as well.
|
||||
//
|
||||
// Additionally, there's no way for us to link dynamic libraries, so we warn
|
||||
// about all dynamic library dependencies that they're not linked in.
|
||||
//
|
||||
// There's no need to include metadata in a static archive, so ensure to not
|
||||
// link in the metadata object file (and also don't prepare the archive with a
|
||||
// metadata file).
|
||||
/// Create a static archive.
|
||||
///
|
||||
/// This is essentially the same thing as an rlib, but it also involves adding all of the upstream
|
||||
/// crates' objects into the archive. This will slurp in all of the native libraries of upstream
|
||||
/// dependencies as well.
|
||||
///
|
||||
/// Additionally, there's no way for us to link dynamic libraries, so we warn about all dynamic
|
||||
/// library dependencies that they're not linked in.
|
||||
///
|
||||
/// There's no need to include metadata in a static archive, so ensure to not link in the metadata
|
||||
/// object file (and also don't prepare the archive with a metadata file).
|
||||
fn link_staticlib<'a, B: ArchiveBuilder<'a>>(
|
||||
sess: &'a Session,
|
||||
codegen_results: &CodegenResults,
|
||||
@ -447,10 +459,73 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>(
|
||||
}
|
||||
}
|
||||
|
||||
// Create a dynamic library or executable
|
||||
//
|
||||
// This will invoke the system linker/cc to create the resulting file. This
|
||||
// links to all upstream files as well.
|
||||
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
|
||||
/// files as well.
|
||||
fn link_natively<'a, B: ArchiveBuilder<'a>>(
|
||||
sess: &'a Session,
|
||||
crate_type: CrateType,
|
||||
@ -662,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
|
||||
@ -715,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;
|
||||
@ -962,6 +1037,13 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Single mode keeps debuginfo in the same object file, but in such a way that it it skipped
|
||||
// by the linker - so it's expected that when codegen units are linked together that this
|
||||
// debuginfo would be lost without keeping around the temps.
|
||||
if sess.opts.debugging_opts.split_dwarf == config::SplitDwarfKind::Single {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we're on OSX then the equivalent of split dwarf is turned on by
|
||||
// default. The final executable won't actually have any debug information
|
||||
// except it'll have pointers to elsewhere. Historically we've always run
|
||||
@ -1677,17 +1759,15 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
|
||||
cmd.take_cmd()
|
||||
}
|
||||
|
||||
// # Native library linking
|
||||
//
|
||||
// User-supplied library search paths (-L on the command line). These are
|
||||
// the same paths used to find Rust crates, so some of them may have been
|
||||
// added already by the previous crate linking code. This only allows them
|
||||
// to be found at compile time so it is still entirely up to outside
|
||||
// forces to make sure that library can be found at runtime.
|
||||
//
|
||||
// Also note that the native libraries linked here are only the ones located
|
||||
// in the current crate. Upstream crates with native library dependencies
|
||||
// may have their native library pulled in above.
|
||||
/// # Native library linking
|
||||
///
|
||||
/// User-supplied library search paths (-L on the command line). These are the same paths used to
|
||||
/// find Rust crates, so some of them may have been added already by the previous crate linking
|
||||
/// code. This only allows them to be found at compile time so it is still entirely up to outside
|
||||
/// forces to make sure that library can be found at runtime.
|
||||
///
|
||||
/// Also note that the native libraries linked here are only the ones located in the current crate.
|
||||
/// Upstream crates with native library dependencies may have their native library pulled in above.
|
||||
fn add_local_native_libraries(
|
||||
cmd: &mut dyn Linker,
|
||||
sess: &Session,
|
||||
@ -1727,11 +1807,10 @@ fn add_local_native_libraries(
|
||||
}
|
||||
}
|
||||
|
||||
// # Rust Crate linking
|
||||
//
|
||||
// Rust crates are not considered at all when creating an rlib output. All
|
||||
// dependencies will be linked when producing the final output (instead of
|
||||
// the intermediate rlib version)
|
||||
/// # Rust Crate linking
|
||||
///
|
||||
/// Rust crates are not considered at all when creating an rlib output. All dependencies will be
|
||||
/// linked when producing the final output (instead of the intermediate rlib version).
|
||||
fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
|
||||
cmd: &mut dyn Linker,
|
||||
sess: &'a Session,
|
||||
@ -1996,24 +2075,21 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
|
||||
}
|
||||
}
|
||||
|
||||
// Link in all of our upstream crates' native dependencies. Remember that
|
||||
// all of these upstream native dependencies are all non-static
|
||||
// dependencies. We've got two cases then:
|
||||
//
|
||||
// 1. The upstream crate is an rlib. In this case we *must* link in the
|
||||
// native dependency because the rlib is just an archive.
|
||||
//
|
||||
// 2. The upstream crate is a dylib. In order to use the dylib, we have to
|
||||
// have the dependency present on the system somewhere. Thus, we don't
|
||||
// gain a whole lot from not linking in the dynamic dependency to this
|
||||
// crate as well.
|
||||
//
|
||||
// The use case for this is a little subtle. In theory the native
|
||||
// dependencies of a crate are purely an implementation detail of the crate
|
||||
// itself, but the problem arises with generic and inlined functions. If a
|
||||
// generic function calls a native function, then the generic function must
|
||||
// be instantiated in the target crate, meaning that the native symbol must
|
||||
// also be resolved in the target crate.
|
||||
/// Link in all of our upstream crates' native dependencies. Remember that all of these upstream
|
||||
/// native dependencies are all non-static dependencies. We've got two cases then:
|
||||
///
|
||||
/// 1. The upstream crate is an rlib. In this case we *must* link in the native dependency because
|
||||
/// the rlib is just an archive.
|
||||
///
|
||||
/// 2. The upstream crate is a dylib. In order to use the dylib, we have to have the dependency
|
||||
/// present on the system somewhere. Thus, we don't gain a whole lot from not linking in the
|
||||
/// dynamic dependency to this crate as well.
|
||||
///
|
||||
/// The use case for this is a little subtle. In theory the native dependencies of a crate are
|
||||
/// purely an implementation detail of the crate itself, but the problem arises with generic and
|
||||
/// inlined functions. If a generic function calls a native function, then the generic function
|
||||
/// must be instantiated in the target crate, meaning that the native symbol must also be resolved
|
||||
/// in the target crate.
|
||||
fn add_upstream_native_libraries(
|
||||
cmd: &mut dyn Linker,
|
||||
sess: &Session,
|
||||
|
@ -274,17 +274,20 @@ impl ModuleConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// HACK(eddyb) work around `#[derive]` producing wrong bounds for `Clone`.
|
||||
pub struct TargetMachineFactory<B: WriteBackendMethods>(
|
||||
pub Arc<dyn Fn() -> Result<B::TargetMachine, String> + Send + Sync>,
|
||||
);
|
||||
|
||||
impl<B: WriteBackendMethods> Clone for TargetMachineFactory<B> {
|
||||
fn clone(&self) -> Self {
|
||||
TargetMachineFactory(self.0.clone())
|
||||
}
|
||||
/// 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)>>>;
|
||||
|
||||
/// Additional resources used by optimize_and_codegen (not module specific)
|
||||
@ -305,12 +308,13 @@ pub struct CodegenContext<B: WriteBackendMethods> {
|
||||
pub regular_module_config: Arc<ModuleConfig>,
|
||||
pub metadata_module_config: Arc<ModuleConfig>,
|
||||
pub allocator_module_config: Arc<ModuleConfig>,
|
||||
pub tm_factory: TargetMachineFactory<B>,
|
||||
pub tm_factory: TargetMachineFactoryFn<B>,
|
||||
pub msvc_imps_needed: bool,
|
||||
pub is_pe_coff: bool,
|
||||
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,
|
||||
@ -627,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);
|
||||
@ -849,6 +859,7 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
|
||||
name: module.name,
|
||||
kind: ModuleKind::Regular,
|
||||
object,
|
||||
dwarf_object: None,
|
||||
bytecode: None,
|
||||
}))
|
||||
}
|
||||
@ -1020,13 +1031,14 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
||||
regular_module_config: regular_config,
|
||||
metadata_module_config: metadata_config,
|
||||
allocator_module_config: allocator_config,
|
||||
tm_factory: TargetMachineFactory(backend.target_machine_factory(tcx.sess, ol)),
|
||||
tm_factory: backend.target_machine_factory(tcx.sess, ol),
|
||||
total_cgus,
|
||||
msvc_imps_needed: msvc_imps_needed(tcx),
|
||||
is_pe_coff: tcx.sess.target.is_like_windows,
|
||||
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.
|
||||
|
@ -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>,
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::write::WriteBackendMethods;
|
||||
use super::CodegenObject;
|
||||
use crate::back::write::TargetMachineFactoryFn;
|
||||
use crate::{CodegenResults, ModuleCodegen};
|
||||
|
||||
use rustc_ast::expand::allocator::AllocatorKind;
|
||||
@ -21,7 +22,6 @@ use rustc_target::spec::Target;
|
||||
pub use rustc_data_structures::sync::MetadataRef;
|
||||
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait BackendTypes {
|
||||
type Value: CodegenObject;
|
||||
@ -123,7 +123,7 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se
|
||||
&self,
|
||||
sess: &Session,
|
||||
opt_level: config::OptLevel,
|
||||
) -> Arc<dyn Fn() -> Result<Self::TargetMachine, String> + Send + Sync>;
|
||||
) -> TargetMachineFactoryFn<Self>;
|
||||
fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str;
|
||||
fn tune_cpu<'b>(&self, sess: &'b Session) -> Option<&'b str>;
|
||||
}
|
||||
|
@ -450,7 +450,8 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
|
||||
bool AsmComments,
|
||||
bool EmitStackSizeSection,
|
||||
bool RelaxELFRelocations,
|
||||
bool UseInitArray) {
|
||||
bool UseInitArray,
|
||||
const char *SplitDwarfFile) {
|
||||
|
||||
auto OptLevel = fromRust(RustOptLevel);
|
||||
auto RM = fromRust(RustReloc);
|
||||
@ -476,6 +477,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
|
||||
Options.MCOptions.AsmVerbose = AsmComments;
|
||||
Options.MCOptions.PreserveAsmComments = AsmComments;
|
||||
Options.MCOptions.ABIName = ABIStr;
|
||||
if (SplitDwarfFile) {
|
||||
Options.MCOptions.SplitDwarfFile = SplitDwarfFile;
|
||||
}
|
||||
Options.RelaxELFRelocations = RelaxELFRelocations;
|
||||
Options.UseInitArray = UseInitArray;
|
||||
|
||||
@ -610,7 +614,7 @@ static TargetMachine::CodeGenFileType fromRust(LLVMRustFileType Type) {
|
||||
|
||||
extern "C" LLVMRustResult
|
||||
LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR,
|
||||
LLVMModuleRef M, const char *Path,
|
||||
LLVMModuleRef M, const char *Path, const char *DwoPath,
|
||||
LLVMRustFileType RustFileType) {
|
||||
llvm::legacy::PassManager *PM = unwrap<llvm::legacy::PassManager>(PMR);
|
||||
auto FileType = fromRust(RustFileType);
|
||||
@ -626,8 +630,22 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR,
|
||||
}
|
||||
|
||||
buffer_ostream BOS(OS);
|
||||
unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, false);
|
||||
PM->run(*unwrap(M));
|
||||
if (DwoPath) {
|
||||
raw_fd_ostream DOS(DwoPath, EC, sys::fs::F_None);
|
||||
EC.clear();
|
||||
if (EC)
|
||||
ErrorInfo = EC.message();
|
||||
if (ErrorInfo != "") {
|
||||
LLVMRustSetLastError(ErrorInfo.c_str());
|
||||
return LLVMRustResult::Failure;
|
||||
}
|
||||
buffer_ostream DBOS(DOS);
|
||||
unwrap(Target)->addPassesToEmitFile(*PM, BOS, &DBOS, FileType, false);
|
||||
PM->run(*unwrap(M));
|
||||
} else {
|
||||
unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, false);
|
||||
PM->run(*unwrap(M));
|
||||
}
|
||||
|
||||
// Apparently `addPassesToEmitFile` adds a pointer to our on-the-stack output
|
||||
// stream (OS), so the only real safe place to delete this is here? Don't we
|
||||
|
@ -690,13 +690,14 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit(
|
||||
const char *Producer, size_t ProducerLen, bool isOptimized,
|
||||
const char *Flags, unsigned RuntimeVer,
|
||||
const char *SplitName, size_t SplitNameLen,
|
||||
LLVMRustDebugEmissionKind Kind) {
|
||||
LLVMRustDebugEmissionKind Kind,
|
||||
uint64_t DWOId, bool SplitDebugInlining) {
|
||||
auto *File = unwrapDI<DIFile>(FileRef);
|
||||
|
||||
return wrap(Builder->createCompileUnit(Lang, File, StringRef(Producer, ProducerLen),
|
||||
isOptimized, Flags, RuntimeVer,
|
||||
StringRef(SplitName, SplitNameLen),
|
||||
fromRust(Kind)));
|
||||
fromRust(Kind), DWOId, SplitDebugInlining));
|
||||
}
|
||||
|
||||
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFile(
|
||||
|
@ -221,6 +221,23 @@ pub enum DebugInfo {
|
||||
Full,
|
||||
}
|
||||
|
||||
/// Some debuginfo requires link-time relocation and some does not. LLVM can partition the debuginfo
|
||||
/// into sections depending on whether or not it requires link-time relocation. Split DWARF
|
||||
/// provides a mechanism which allows the linker to skip the sections which don't require link-time
|
||||
/// relocation - either by putting those sections into DWARF object files, or keeping them in the
|
||||
/// object file in such a way that the linker will skip them.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
|
||||
pub enum SplitDwarfKind {
|
||||
/// Disabled.
|
||||
None,
|
||||
/// Sections which do not require relocation are written into the object file but ignored
|
||||
/// by the linker.
|
||||
Single,
|
||||
/// Sections which do not require relocation are written into a DWARF object (`.dwo`) file,
|
||||
/// which is skipped by the linker by virtue of being a different file.
|
||||
Split,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
|
||||
#[derive(Encodable, Decodable)]
|
||||
pub enum OutputType {
|
||||
@ -533,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(
|
||||
@ -566,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();
|
||||
@ -593,6 +616,37 @@ impl OutputFilenames {
|
||||
path.set_extension(extension);
|
||||
path
|
||||
}
|
||||
|
||||
/// Returns the name of 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_filename(
|
||||
&self,
|
||||
split_dwarf_kind: SplitDwarfKind,
|
||||
cgu_name: Option<&str>,
|
||||
) -> Option<PathBuf> {
|
||||
self.split_dwarf_path(split_dwarf_kind, cgu_name)
|
||||
.map(|path| path.strip_prefix(&self.out_directory).unwrap_or(&path).to_path_buf())
|
||||
}
|
||||
|
||||
/// 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_path(
|
||||
&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 {
|
||||
|
@ -269,6 +269,7 @@ macro_rules! options {
|
||||
pub const parse_switch_with_opt_path: &str =
|
||||
"an optional path to the profiling data output directory";
|
||||
pub const parse_merge_functions: &str = "one of: `disabled`, `trampolines`, or `aliases`";
|
||||
pub const parse_split_dwarf_kind: &str = "one of: `none`, `single` or `split`";
|
||||
pub const parse_symbol_mangling_version: &str = "either `legacy` or `v0` (RFC 2603)";
|
||||
pub const parse_src_file_hash: &str = "either `md5` or `sha1`";
|
||||
pub const parse_relocation_model: &str =
|
||||
@ -676,6 +677,19 @@ macro_rules! options {
|
||||
true
|
||||
}
|
||||
|
||||
fn parse_split_dwarf_kind(
|
||||
slot: &mut SplitDwarfKind,
|
||||
v: Option<&str>,
|
||||
) -> bool {
|
||||
*slot = match v {
|
||||
Some("none") => SplitDwarfKind::None,
|
||||
Some("split") => SplitDwarfKind::Split,
|
||||
Some("single") => SplitDwarfKind::Single,
|
||||
_ => return false,
|
||||
};
|
||||
true
|
||||
}
|
||||
|
||||
fn parse_symbol_mangling_version(
|
||||
slot: &mut Option<SymbolManglingVersion>,
|
||||
v: Option<&str>,
|
||||
@ -1088,6 +1102,11 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
||||
"hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
|
||||
strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
|
||||
"tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
|
||||
split_dwarf: SplitDwarfKind = (SplitDwarfKind::None, parse_split_dwarf_kind, [UNTRACKED],
|
||||
"enable generation of split dwarf"),
|
||||
split_dwarf_inlining: bool = (true, parse_bool, [UNTRACKED],
|
||||
"provide minimal debug info in the object/executable to facilitate online \
|
||||
symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"),
|
||||
symbol_mangling_version: Option<SymbolManglingVersion> = (None,
|
||||
parse_symbol_mangling_version, [TRACKED],
|
||||
"which mangling version to use for symbol names ('legacy' (default) or 'v0')"),
|
||||
|
@ -969,15 +969,25 @@ impl Step for Assemble {
|
||||
|
||||
copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler);
|
||||
|
||||
// We prepend this bin directory to the user PATH when linking Rust binaries. To
|
||||
// avoid shadowing the system LLD we rename the LLD we provide to `rust-lld`.
|
||||
let libdir = builder.sysroot_libdir(target_compiler, target_compiler.host);
|
||||
let libdir_bin = libdir.parent().unwrap().join("bin");
|
||||
t!(fs::create_dir_all(&libdir_bin));
|
||||
|
||||
if let Some(lld_install) = lld_install {
|
||||
let src_exe = exe("lld", target_compiler.host);
|
||||
let dst_exe = exe("rust-lld", target_compiler.host);
|
||||
// we prepend this bin directory to the user PATH when linking Rust binaries. To
|
||||
// avoid shadowing the system LLD we rename the LLD we provide to `rust-lld`.
|
||||
let dst = libdir.parent().unwrap().join("bin");
|
||||
t!(fs::create_dir_all(&dst));
|
||||
builder.copy(&lld_install.join("bin").join(&src_exe), &dst.join(&dst_exe));
|
||||
builder.copy(&lld_install.join("bin").join(&src_exe), &libdir_bin.join(&dst_exe));
|
||||
}
|
||||
|
||||
// Similarly, copy `llvm-dwp` into libdir for Split DWARF.
|
||||
{
|
||||
let src_exe = exe("llvm-dwp", target_compiler.host);
|
||||
let dst_exe = exe("rust-llvm-dwp", target_compiler.host);
|
||||
let llvm_config_bin = builder.ensure(native::Llvm { target: target_compiler.host });
|
||||
let llvm_bin_dir = llvm_config_bin.parent().unwrap();
|
||||
builder.copy(&llvm_bin_dir.join(&src_exe), &libdir_bin.join(&dst_exe));
|
||||
}
|
||||
|
||||
// Ensure that `libLLVM.so` ends up in the newly build compiler directory,
|
||||
|
@ -523,17 +523,20 @@ impl Step for Rustc {
|
||||
// component for now.
|
||||
maybe_install_llvm_runtime(builder, host, image);
|
||||
|
||||
let src_dir = builder.sysroot_libdir(compiler, host).parent().unwrap().join("bin");
|
||||
let dst_dir = image.join("lib/rustlib").join(&*host.triple).join("bin");
|
||||
t!(fs::create_dir_all(&dst_dir));
|
||||
|
||||
// Copy over lld if it's there
|
||||
if builder.config.lld_enabled {
|
||||
let exe = exe("rust-lld", compiler.host);
|
||||
let src =
|
||||
builder.sysroot_libdir(compiler, host).parent().unwrap().join("bin").join(&exe);
|
||||
// for the rationale about this rename check `compile::copy_lld_to_sysroot`
|
||||
let dst = image.join("lib/rustlib").join(&*host.triple).join("bin").join(&exe);
|
||||
t!(fs::create_dir_all(&dst.parent().unwrap()));
|
||||
builder.copy(&src, &dst);
|
||||
builder.copy(&src_dir.join(&exe), &dst_dir.join(&exe));
|
||||
}
|
||||
|
||||
// Copy over llvm-dwp if it's there
|
||||
let exe = exe("rust-llvm-dwp", compiler.host);
|
||||
builder.copy(&src_dir.join(&exe), &dst_dir.join(&exe));
|
||||
|
||||
// Man pages
|
||||
t!(fs::create_dir_all(image.join("share/man/man1")));
|
||||
let man_src = builder.src.join("src/doc/man");
|
||||
|
@ -897,7 +897,12 @@ default_test!(Incremental {
|
||||
suite: "incremental"
|
||||
});
|
||||
|
||||
default_test!(Debuginfo { path: "src/test/debuginfo", mode: "debuginfo", suite: "debuginfo" });
|
||||
default_test_with_compare_mode!(Debuginfo {
|
||||
path: "src/test/debuginfo",
|
||||
mode: "debuginfo",
|
||||
suite: "debuginfo",
|
||||
compare_mode: "split-dwarf"
|
||||
});
|
||||
|
||||
host_test!(UiFullDeps { path: "src/test/ui-fulldeps", mode: "ui", suite: "ui-fulldeps" });
|
||||
|
||||
|
8
src/test/run-make-fulldeps/split-dwarf/Makefile
Normal file
8
src/test/run-make-fulldeps/split-dwarf/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
-include ../tools.mk
|
||||
|
||||
# only-linux
|
||||
|
||||
all:
|
||||
$(RUSTC) -Z split-dwarf=split foo.rs
|
||||
rm $(TMPDIR)/foo.dwp
|
||||
rm $(TMPDIR)/$(call BIN,foo)
|
1
src/test/run-make-fulldeps/split-dwarf/foo.rs
Normal file
1
src/test/run-make-fulldeps/split-dwarf/foo.rs
Normal file
@ -0,0 +1 @@
|
||||
fn main() {}
|
@ -127,6 +127,8 @@ pub enum CompareMode {
|
||||
Nll,
|
||||
Polonius,
|
||||
Chalk,
|
||||
SplitDwarf,
|
||||
SplitDwarfSingle,
|
||||
}
|
||||
|
||||
impl CompareMode {
|
||||
@ -135,6 +137,8 @@ impl CompareMode {
|
||||
CompareMode::Nll => "nll",
|
||||
CompareMode::Polonius => "polonius",
|
||||
CompareMode::Chalk => "chalk",
|
||||
CompareMode::SplitDwarf => "split-dwarf",
|
||||
CompareMode::SplitDwarfSingle => "split-dwarf-single",
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,6 +147,8 @@ impl CompareMode {
|
||||
"nll" => CompareMode::Nll,
|
||||
"polonius" => CompareMode::Polonius,
|
||||
"chalk" => CompareMode::Chalk,
|
||||
"split-dwarf" => CompareMode::SplitDwarf,
|
||||
"split-dwarf-single" => CompareMode::SplitDwarfSingle,
|
||||
x => panic!("unknown --compare-mode option: {}", x),
|
||||
}
|
||||
}
|
||||
|
@ -852,6 +852,8 @@ impl Config {
|
||||
Some(CompareMode::Nll) => name == "compare-mode-nll",
|
||||
Some(CompareMode::Polonius) => name == "compare-mode-polonius",
|
||||
Some(CompareMode::Chalk) => name == "compare-mode-chalk",
|
||||
Some(CompareMode::SplitDwarf) => name == "compare-mode-split-dwarf",
|
||||
Some(CompareMode::SplitDwarfSingle) => name == "compare-mode-split-dwarf-single",
|
||||
None => false,
|
||||
} ||
|
||||
(cfg!(debug_assertions) && name == "debug") ||
|
||||
|
@ -2017,6 +2017,12 @@ impl<'test> TestCx<'test> {
|
||||
Some(CompareMode::Chalk) => {
|
||||
rustc.args(&["-Zchalk"]);
|
||||
}
|
||||
Some(CompareMode::SplitDwarf) => {
|
||||
rustc.args(&["-Zsplit-dwarf=split"]);
|
||||
}
|
||||
Some(CompareMode::SplitDwarfSingle) => {
|
||||
rustc.args(&["-Zsplit-dwarf=single"]);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user