Rollup merge of #115358 - durin42:compress-debuginfo, r=oli-obk

debuginfo: add compiler option to allow compressed debuginfo sections

LLVM already supports emitting compressed debuginfo. In debuginfo=full builds, the debug section is often a large amount of data, and it typically compresses very well (3x is not unreasonable.) We add a new knob to allow debuginfo to be compressed when the matching LLVM functionality is present. Like clang, if a known-but-disabled compression mechanism is requested, we disable compression and emit uncompressed debuginfo sections.

The API is different enough on older LLVMs we just pretend the support
is missing on LLVM older than 16.
This commit is contained in:
Matthias Krüger 2023-09-09 00:28:19 +02:00 committed by GitHub
commit aa78b4c368
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 132 additions and 6 deletions

View File

@ -83,6 +83,8 @@ codegen_llvm_unknown_ctarget_feature_prefix =
unknown feature specified for `-Ctarget-feature`: `{$feature}` unknown feature specified for `-Ctarget-feature`: `{$feature}`
.note = features must begin with a `+` to enable or `-` to disable it .note = features must begin with a `+` to enable or `-` to disable it
codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo
codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err} codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err}
codegen_llvm_write_ir = failed to write LLVM IR to {$path} codegen_llvm_write_ir = failed to write LLVM IR to {$path}

View File

@ -5,13 +5,17 @@
use crate::base; use crate::base;
use crate::common; use crate::common;
use crate::errors::{ use crate::errors::{
CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, WithLlvmError, WriteBytecode, CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, UnknownCompression,
WithLlvmError, WriteBytecode,
}; };
use crate::llvm::{self, DiagnosticInfo, PassManager}; use crate::llvm::{self, DiagnosticInfo, PassManager};
use crate::llvm_util; use crate::llvm_util;
use crate::type_::Type; use crate::type_::Type;
use crate::LlvmCodegenBackend; use crate::LlvmCodegenBackend;
use crate::ModuleLlvm; use crate::ModuleLlvm;
use llvm::{
LLVMRustLLVMHasZlibCompressionForDebugSymbols, LLVMRustLLVMHasZstdCompressionForDebugSymbols,
};
use rustc_codegen_ssa::back::link::ensure_removed; use rustc_codegen_ssa::back::link::ensure_removed;
use rustc_codegen_ssa::back::write::{ use rustc_codegen_ssa::back::write::{
BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig, BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig,
@ -234,6 +238,22 @@ pub fn target_machine_factory(
args_cstr_buff args_cstr_buff
}; };
let debuginfo_compression = sess.opts.debuginfo_compression.to_string();
match sess.opts.debuginfo_compression {
rustc_session::config::DebugInfoCompression::Zlib => {
if !unsafe { LLVMRustLLVMHasZlibCompressionForDebugSymbols() } {
sess.emit_warning(UnknownCompression { algorithm: "zlib" });
}
}
rustc_session::config::DebugInfoCompression::Zstd => {
if !unsafe { LLVMRustLLVMHasZstdCompressionForDebugSymbols() } {
sess.emit_warning(UnknownCompression { algorithm: "zstd" });
}
}
rustc_session::config::DebugInfoCompression::None => {}
};
let debuginfo_compression = SmallCStr::new(&debuginfo_compression);
Arc::new(move |config: TargetMachineFactoryConfig| { Arc::new(move |config: TargetMachineFactoryConfig| {
let split_dwarf_file = let split_dwarf_file =
path_mapping.map_prefix(config.split_dwarf_file.unwrap_or_default()).0; path_mapping.map_prefix(config.split_dwarf_file.unwrap_or_default()).0;
@ -259,6 +279,7 @@ pub fn target_machine_factory(
relax_elf_relocations, relax_elf_relocations,
use_init_array, use_init_array,
split_dwarf_file.as_ptr(), split_dwarf_file.as_ptr(),
debuginfo_compression.as_ptr(),
force_emulated_tls, force_emulated_tls,
args_cstr_buff.as_ptr() as *const c_char, args_cstr_buff.as_ptr() as *const c_char,
args_cstr_buff.len(), args_cstr_buff.len(),

View File

@ -226,3 +226,9 @@ pub(crate) struct WriteBytecode<'a> {
pub(crate) struct CopyBitcode { pub(crate) struct CopyBitcode {
pub err: std::io::Error, pub err: std::io::Error,
} }
#[derive(Diagnostic)]
#[diag(codegen_llvm_unknown_debuginfo_compression)]
pub struct UnknownCompression {
pub algorithm: &'static str,
}

View File

@ -2131,6 +2131,7 @@ pub fn LLVMRustCreateTargetMachine(
RelaxELFRelocations: bool, RelaxELFRelocations: bool,
UseInitArray: bool, UseInitArray: bool,
SplitDwarfFile: *const c_char, SplitDwarfFile: *const c_char,
DebugInfoCompression: *const c_char,
ForceEmulatedTls: bool, ForceEmulatedTls: bool,
ArgsCstrBuff: *const c_char, ArgsCstrBuff: *const c_char,
ArgsCstrBuffLen: usize, ArgsCstrBuffLen: usize,
@ -2366,6 +2367,10 @@ pub fn LLVMRustContextConfigureDiagnosticHandler(
pub fn LLVMRustIsBitcode(ptr: *const u8, len: usize) -> bool; pub fn LLVMRustIsBitcode(ptr: *const u8, len: usize) -> bool;
pub fn LLVMRustLLVMHasZlibCompressionForDebugSymbols() -> bool;
pub fn LLVMRustLLVMHasZstdCompressionForDebugSymbols() -> bool;
pub fn LLVMRustGetSymbols( pub fn LLVMRustGetSymbols(
buf_ptr: *const u8, buf_ptr: *const u8,
buf_len: usize, buf_len: usize,

View File

@ -410,6 +410,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
bool RelaxELFRelocations, bool RelaxELFRelocations,
bool UseInitArray, bool UseInitArray,
const char *SplitDwarfFile, const char *SplitDwarfFile,
const char *DebugInfoCompression,
bool ForceEmulatedTls, bool ForceEmulatedTls,
const char *ArgsCstrBuff, size_t ArgsCstrBuffLen) { const char *ArgsCstrBuff, size_t ArgsCstrBuffLen) {
@ -441,6 +442,16 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
if (SplitDwarfFile) { if (SplitDwarfFile) {
Options.MCOptions.SplitDwarfFile = SplitDwarfFile; Options.MCOptions.SplitDwarfFile = SplitDwarfFile;
} }
#if LLVM_VERSION_GE(16, 0)
if (!strcmp("zlib", DebugInfoCompression) && llvm::compression::zlib::isAvailable()) {
Options.CompressDebugSections = DebugCompressionType::Zlib;
} else if (!strcmp("zstd", DebugInfoCompression) && llvm::compression::zstd::isAvailable()) {
Options.CompressDebugSections = DebugCompressionType::Zstd;
} else if (!strcmp("none", DebugInfoCompression)) {
Options.CompressDebugSections = DebugCompressionType::None;
}
#endif
Options.RelaxELFRelocations = RelaxELFRelocations; Options.RelaxELFRelocations = RelaxELFRelocations;
Options.UseInitArray = UseInitArray; Options.UseInitArray = UseInitArray;

View File

@ -2044,3 +2044,19 @@ extern "C" bool LLVMRustIsNonGVFunctionPointerTy(LLVMValueRef V) {
} }
return false; return false;
} }
extern "C" bool LLVMRustLLVMHasZlibCompressionForDebugSymbols() {
#if LLVM_VERSION_GE(16, 0)
return llvm::compression::zlib::isAvailable();
#else
return false;
#endif
}
extern "C" bool LLVMRustLLVMHasZstdCompressionForDebugSymbols() {
#if LLVM_VERSION_GE(16, 0)
return llvm::compression::zstd::isAvailable();
#else
return false;
#endif
}

View File

@ -381,6 +381,24 @@ pub enum DebugInfo {
Full, Full,
} }
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub enum DebugInfoCompression {
None,
Zlib,
Zstd,
}
impl ToString for DebugInfoCompression {
fn to_string(&self) -> String {
match self {
DebugInfoCompression::None => "none",
DebugInfoCompression::Zlib => "zlib",
DebugInfoCompression::Zstd => "zstd",
}
.to_owned()
}
}
/// Split debug-information is enabled by `-C split-debuginfo`, this enum is only used if split /// Split debug-information is enabled by `-C split-debuginfo`, this enum is only used if split
/// debug-information is enabled (in either `Packed` or `Unpacked` modes), and the platform /// debug-information is enabled (in either `Packed` or `Unpacked` modes), and the platform
/// uses DWARF for debug-information. /// uses DWARF for debug-information.
@ -1015,6 +1033,7 @@ fn default() -> Options {
crate_types: Vec::new(), crate_types: Vec::new(),
optimize: OptLevel::No, optimize: OptLevel::No,
debuginfo: DebugInfo::None, debuginfo: DebugInfo::None,
debuginfo_compression: DebugInfoCompression::None,
lint_opts: Vec::new(), lint_opts: Vec::new(),
lint_cap: None, lint_cap: None,
describe_lints: false, describe_lints: false,
@ -2277,6 +2296,13 @@ fn select_debuginfo(matches: &getopts::Matches, cg: &CodegenOptions) -> DebugInf
if max_g > max_c { DebugInfo::Full } else { cg.debuginfo } if max_g > max_c { DebugInfo::Full } else { cg.debuginfo }
} }
fn select_debuginfo_compression(
_handler: &EarlyErrorHandler,
unstable_opts: &UnstableOptions,
) -> DebugInfoCompression {
unstable_opts.debuginfo_compression
}
pub(crate) fn parse_assert_incr_state( pub(crate) fn parse_assert_incr_state(
handler: &EarlyErrorHandler, handler: &EarlyErrorHandler,
opt_assertion: &Option<String>, opt_assertion: &Option<String>,
@ -2752,6 +2778,8 @@ pub fn build_session_options(
// for more details. // for more details.
let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No); let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No);
let debuginfo = select_debuginfo(matches, &cg); let debuginfo = select_debuginfo(matches, &cg);
let debuginfo_compression: DebugInfoCompression =
select_debuginfo_compression(handler, &unstable_opts);
let mut search_paths = vec![]; let mut search_paths = vec![];
for s in &matches.opt_strs("L") { for s in &matches.opt_strs("L") {
@ -2828,6 +2856,7 @@ pub fn build_session_options(
crate_types, crate_types,
optimize: opt_level, optimize: opt_level,
debuginfo, debuginfo,
debuginfo_compression,
lint_opts, lint_opts,
lint_cap, lint_cap,
describe_lints, describe_lints,
@ -3113,11 +3142,11 @@ pub fn needs_analysis(&self) -> bool {
/// how the hash should be calculated when adding a new command-line argument. /// how the hash should be calculated when adding a new command-line argument.
pub(crate) mod dep_tracking { pub(crate) mod dep_tracking {
use super::{ use super::{
BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType, BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, DebugInfoCompression,
InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, ErrorOutputType, InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto,
OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, Passes, ResolveDocLinks, LocationDetail, LtoCli, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes,
SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, Passes, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath,
TraitSolver, TrimmedDefPaths, SymbolManglingVersion, TraitSolver, TrimmedDefPaths,
}; };
use crate::lint; use crate::lint;
use crate::options::WasiExecModel; use crate::options::WasiExecModel;
@ -3195,6 +3224,7 @@ fn hash(
OptLevel, OptLevel,
LtoCli, LtoCli,
DebugInfo, DebugInfo,
DebugInfoCompression,
UnstableFeatures, UnstableFeatures,
NativeLib, NativeLib,
NativeLibKind, NativeLibKind,

View File

@ -139,6 +139,7 @@ pub struct Options {
/// can influence whether overflow checks are done or not. /// can influence whether overflow checks are done or not.
debug_assertions: bool [TRACKED], debug_assertions: bool [TRACKED],
debuginfo: DebugInfo [TRACKED], debuginfo: DebugInfo [TRACKED],
debuginfo_compression: DebugInfoCompression [TRACKED],
lint_opts: Vec<(String, lint::Level)> [TRACKED_NO_CRATE_HASH], lint_opts: Vec<(String, lint::Level)> [TRACKED_NO_CRATE_HASH],
lint_cap: Option<lint::Level> [TRACKED_NO_CRATE_HASH], lint_cap: Option<lint::Level> [TRACKED_NO_CRATE_HASH],
describe_lints: bool [UNTRACKED], describe_lints: bool [UNTRACKED],
@ -376,6 +377,7 @@ mod desc {
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
pub const parse_cfprotection: &str = "`none`|`no`|`n` (default), `branch`, `return`, or `full`|`yes`|`y` (equivalent to `branch` and `return`)"; pub const parse_cfprotection: &str = "`none`|`no`|`n` (default), `branch`, `return`, or `full`|`yes`|`y` (equivalent to `branch` and `return`)";
pub const parse_debuginfo: &str = "either an integer (0, 1, 2), `none`, `line-directives-only`, `line-tables-only`, `limited`, or `full`"; pub const parse_debuginfo: &str = "either an integer (0, 1, 2), `none`, `line-directives-only`, `line-tables-only`, `limited`, or `full`";
pub const parse_debuginfo_compression: &str = "one of `none`, `zlib`, or `zstd`";
pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`"; pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`";
pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of(); pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of();
pub const parse_optimization_fuel: &str = "crate=integer"; pub const parse_optimization_fuel: &str = "crate=integer";
@ -782,6 +784,19 @@ pub(crate) fn parse_debuginfo(slot: &mut DebugInfo, v: Option<&str>) -> bool {
true true
} }
pub(crate) fn parse_debuginfo_compression(
slot: &mut DebugInfoCompression,
v: Option<&str>,
) -> bool {
match v {
Some("none") => *slot = DebugInfoCompression::None,
Some("zlib") => *slot = DebugInfoCompression::Zlib,
Some("zstd") => *slot = DebugInfoCompression::Zstd,
_ => return false,
};
true
}
pub(crate) fn parse_linker_flavor(slot: &mut Option<LinkerFlavorCli>, v: Option<&str>) -> bool { pub(crate) fn parse_linker_flavor(slot: &mut Option<LinkerFlavorCli>, v: Option<&str>) -> bool {
match v.and_then(LinkerFlavorCli::from_str) { match v.and_then(LinkerFlavorCli::from_str) {
Some(lf) => *slot = Some(lf), Some(lf) => *slot = Some(lf),
@ -1424,6 +1439,8 @@ pub(crate) fn parse_dump_solver_proof_tree(
"emit discriminators and other data necessary for AutoFDO"), "emit discriminators and other data necessary for AutoFDO"),
debug_macros: bool = (false, parse_bool, [TRACKED], debug_macros: bool = (false, parse_bool, [TRACKED],
"emit line numbers debug info inside macros (default: no)"), "emit line numbers debug info inside macros (default: no)"),
debuginfo_compression: DebugInfoCompression = (DebugInfoCompression::None, parse_debuginfo_compression, [TRACKED],
"compress debug info sections (none, zlib, zstd, default: none)"),
deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED], deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED],
"deduplicate identical diagnostics (default: yes)"), "deduplicate identical diagnostics (default: yes)"),
dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED], dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED],

View File

@ -0,0 +1,15 @@
# ignore-cross-compile
include ../tools.mk
# only-linux
# min-llvm-version: 16.0
#
# This tests debuginfo-compression.
all: zlib zstandard
zlib:
test "`$(RUSTC) --crate-name=foo --crate-type=lib --emit=obj -C debuginfo=full -Z debuginfo-compression=zlib foo.rs 2>&1 | sed 's/.*unknown.*zlib.*/missing/' | head -n 1`" = missing || readelf -t $(TMPDIR)/foo.o | grep -q ZLIB
zstandard:
test "`$(RUSTC) --crate-name=foo --crate-type=lib --emit=obj -C debuginfo=full -Z debuginfo-compression=zstd foo.rs 2>&1 | sed 's/.*unknown.*zstd.*/missing/' | head -n 1`" = missing || readelf -t $(TMPDIR)/foo.o | grep -q ZST

View File

@ -0,0 +1,3 @@
pub fn foo() -> i32 {
42
}