Skip to content

Commit 4865077

Browse files
committed
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.
1 parent bb90f81 commit 4865077

File tree

10 files changed

+135
-7
lines changed

10 files changed

+135
-7
lines changed

compiler/rustc_codegen_llvm/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ codegen_llvm_unknown_ctarget_feature_prefix =
8383
unknown feature specified for `-Ctarget-feature`: `{$feature}`
8484
.note = features must begin with a `+` to enable or `-` to disable it
8585
86+
codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo
87+
8688
codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err}
8789
8890
codegen_llvm_write_ir = failed to write LLVM IR to {$path}

compiler/rustc_codegen_llvm/src/back/write.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@ use crate::back::profiling::{
55
use crate::base;
66
use crate::common;
77
use crate::errors::{
8-
CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, WithLlvmError, WriteBytecode,
8+
CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, UnknownCompression,
9+
WithLlvmError, WriteBytecode,
910
};
1011
use crate::llvm::{self, DiagnosticInfo, PassManager};
1112
use crate::llvm_util;
1213
use crate::type_::Type;
1314
use crate::LlvmCodegenBackend;
1415
use crate::ModuleLlvm;
16+
use llvm::{
17+
LLVMRustLLVMHasZlibCompressionForDebugSymbols, LLVMRustLLVMHasZstdCompressionForDebugSymbols,
18+
};
1519
use rustc_codegen_ssa::back::link::ensure_removed;
1620
use rustc_codegen_ssa::back::write::{
1721
BitcodeSection, CodegenContext, EmitObj, ModuleConfig, TargetMachineFactoryConfig,
@@ -216,6 +220,22 @@ pub fn target_machine_factory(
216220

217221
let force_emulated_tls = sess.target.force_emulated_tls;
218222

223+
let debuginfo_compression = sess.opts.debuginfo_compression.to_string();
224+
match sess.opts.debuginfo_compression {
225+
rustc_session::config::DebugInfoCompression::Zlib => {
226+
if !unsafe { LLVMRustLLVMHasZlibCompressionForDebugSymbols() } {
227+
sess.emit_warning(UnknownCompression { algorithm: "zlib" });
228+
}
229+
}
230+
rustc_session::config::DebugInfoCompression::Zstd => {
231+
if !unsafe { LLVMRustLLVMHasZstdCompressionForDebugSymbols() } {
232+
sess.emit_warning(UnknownCompression { algorithm: "zstd" });
233+
}
234+
}
235+
rustc_session::config::DebugInfoCompression::None => {}
236+
};
237+
let debuginfo_compression = SmallCStr::new(&debuginfo_compression);
238+
219239
Arc::new(move |config: TargetMachineFactoryConfig| {
220240
let split_dwarf_file =
221241
path_mapping.map_prefix(config.split_dwarf_file.unwrap_or_default()).0;
@@ -241,6 +261,7 @@ pub fn target_machine_factory(
241261
relax_elf_relocations,
242262
use_init_array,
243263
split_dwarf_file.as_ptr(),
264+
debuginfo_compression.as_ptr(),
244265
force_emulated_tls,
245266
)
246267
};

compiler/rustc_codegen_llvm/src/errors.rs

+6
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,9 @@ pub(crate) struct WriteBytecode<'a> {
226226
pub(crate) struct CopyBitcode {
227227
pub err: std::io::Error,
228228
}
229+
230+
#[derive(Diagnostic)]
231+
#[diag(codegen_llvm_unknown_debuginfo_compression)]
232+
pub struct UnknownCompression {
233+
pub algorithm: &'static str,
234+
}

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+5
Original file line numberDiff line numberDiff line change
@@ -2131,6 +2131,7 @@ extern "C" {
21312131
RelaxELFRelocations: bool,
21322132
UseInitArray: bool,
21332133
SplitDwarfFile: *const c_char,
2134+
DebugInfoCompression: *const c_char,
21342135
ForceEmulatedTls: bool,
21352136
) -> Option<&'static mut TargetMachine>;
21362137
pub fn LLVMRustDisposeTargetMachine(T: &'static mut TargetMachine);
@@ -2357,6 +2358,10 @@ extern "C" {
23572358

23582359
pub fn LLVMRustIsBitcode(ptr: *const u8, len: usize) -> bool;
23592360

2361+
pub fn LLVMRustLLVMHasZlibCompressionForDebugSymbols() -> bool;
2362+
2363+
pub fn LLVMRustLLVMHasZstdCompressionForDebugSymbols() -> bool;
2364+
23602365
pub fn LLVMRustGetSymbols(
23612366
buf_ptr: *const u8,
23622367
buf_len: usize,

compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
406406
bool RelaxELFRelocations,
407407
bool UseInitArray,
408408
const char *SplitDwarfFile,
409+
const char *DebugInfoCompression,
409410
bool ForceEmulatedTls) {
410411

411412
auto OptLevel = fromRust(RustOptLevel);
@@ -436,6 +437,16 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
436437
if (SplitDwarfFile) {
437438
Options.MCOptions.SplitDwarfFile = SplitDwarfFile;
438439
}
440+
#if LLVM_VERSION_GE(16, 0)
441+
if (!strcmp("zlib", DebugInfoCompression) && llvm::compression::zlib::isAvailable()) {
442+
Options.CompressDebugSections = DebugCompressionType::Zlib;
443+
} else if (!strcmp("zstd", DebugInfoCompression) && llvm::compression::zstd::isAvailable()) {
444+
Options.CompressDebugSections = DebugCompressionType::Zstd;
445+
} else if (!strcmp("none", DebugInfoCompression)) {
446+
Options.CompressDebugSections = DebugCompressionType::None;
447+
}
448+
#endif
449+
439450
Options.RelaxELFRelocations = RelaxELFRelocations;
440451
Options.UseInitArray = UseInitArray;
441452

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -2044,3 +2044,19 @@ extern "C" bool LLVMRustIsNonGVFunctionPointerTy(LLVMValueRef V) {
20442044
}
20452045
return false;
20462046
}
2047+
2048+
extern "C" bool LLVMRustLLVMHasZlibCompressionForDebugSymbols() {
2049+
#if LLVM_VERSION_GE(16, 0)
2050+
return llvm::compression::zlib::isAvailable();
2051+
#else
2052+
return false;
2053+
#endif
2054+
}
2055+
2056+
extern "C" bool LLVMRustLLVMHasZstdCompressionForDebugSymbols() {
2057+
#if LLVM_VERSION_GE(16, 0)
2058+
return llvm::compression::zstd::isAvailable();
2059+
#else
2060+
return false;
2061+
#endif
2062+
}

compiler/rustc_session/src/config.rs

+35-5
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,24 @@ pub enum DebugInfo {
381381
Full,
382382
}
383383

384+
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
385+
pub enum DebugInfoCompression {
386+
None,
387+
Zlib,
388+
Zstd,
389+
}
390+
391+
impl ToString for DebugInfoCompression {
392+
fn to_string(&self) -> String {
393+
match self {
394+
DebugInfoCompression::None => "none",
395+
DebugInfoCompression::Zlib => "zlib",
396+
DebugInfoCompression::Zstd => "zstd",
397+
}
398+
.to_owned()
399+
}
400+
}
401+
384402
/// Split debug-information is enabled by `-C split-debuginfo`, this enum is only used if split
385403
/// debug-information is enabled (in either `Packed` or `Unpacked` modes), and the platform
386404
/// uses DWARF for debug-information.
@@ -1015,6 +1033,7 @@ impl Default for Options {
10151033
crate_types: Vec::new(),
10161034
optimize: OptLevel::No,
10171035
debuginfo: DebugInfo::None,
1036+
debuginfo_compression: DebugInfoCompression::None,
10181037
lint_opts: Vec::new(),
10191038
lint_cap: None,
10201039
describe_lints: false,
@@ -2283,6 +2302,13 @@ fn select_debuginfo(matches: &getopts::Matches, cg: &CodegenOptions) -> DebugInf
22832302
if max_g > max_c { DebugInfo::Full } else { cg.debuginfo }
22842303
}
22852304

2305+
fn select_debuginfo_compression(
2306+
_handler: &EarlyErrorHandler,
2307+
unstable_opts: &UnstableOptions,
2308+
) -> DebugInfoCompression {
2309+
unstable_opts.debuginfo_compression
2310+
}
2311+
22862312
pub(crate) fn parse_assert_incr_state(
22872313
handler: &EarlyErrorHandler,
22882314
opt_assertion: &Option<String>,
@@ -2758,6 +2784,8 @@ pub fn build_session_options(
27582784
// for more details.
27592785
let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No);
27602786
let debuginfo = select_debuginfo(matches, &cg);
2787+
let debuginfo_compression: DebugInfoCompression =
2788+
select_debuginfo_compression(handler, &unstable_opts);
27612789

27622790
let mut search_paths = vec![];
27632791
for s in &matches.opt_strs("L") {
@@ -2834,6 +2862,7 @@ pub fn build_session_options(
28342862
crate_types,
28352863
optimize: opt_level,
28362864
debuginfo,
2865+
debuginfo_compression,
28372866
lint_opts,
28382867
lint_cap,
28392868
describe_lints,
@@ -3119,11 +3148,11 @@ impl PpMode {
31193148
/// how the hash should be calculated when adding a new command-line argument.
31203149
pub(crate) mod dep_tracking {
31213150
use super::{
3122-
BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType,
3123-
InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto, LocationDetail, LtoCli,
3124-
OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, Passes, ResolveDocLinks,
3125-
SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion,
3126-
TraitSolver, TrimmedDefPaths,
3151+
BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, DebugInfoCompression,
3152+
ErrorOutputType, InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto,
3153+
LocationDetail, LtoCli, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes,
3154+
Passes, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath,
3155+
SymbolManglingVersion, TraitSolver, TrimmedDefPaths,
31273156
};
31283157
use crate::lint;
31293158
use crate::options::WasiExecModel;
@@ -3201,6 +3230,7 @@ pub(crate) mod dep_tracking {
32013230
OptLevel,
32023231
LtoCli,
32033232
DebugInfo,
3233+
DebugInfoCompression,
32043234
UnstableFeatures,
32053235
NativeLib,
32063236
NativeLibKind,

compiler/rustc_session/src/options.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ top_level_options!(
139139
/// can influence whether overflow checks are done or not.
140140
debug_assertions: bool [TRACKED],
141141
debuginfo: DebugInfo [TRACKED],
142+
debuginfo_compression: DebugInfoCompression [TRACKED],
142143
lint_opts: Vec<(String, lint::Level)> [TRACKED_NO_CRATE_HASH],
143144
lint_cap: Option<lint::Level> [TRACKED_NO_CRATE_HASH],
144145
describe_lints: bool [UNTRACKED],
@@ -376,6 +377,7 @@ mod desc {
376377
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
377378
pub const parse_cfprotection: &str = "`none`|`no`|`n` (default), `branch`, `return`, or `full`|`yes`|`y` (equivalent to `branch` and `return`)";
378379
pub const parse_debuginfo: &str = "either an integer (0, 1, 2), `none`, `line-directives-only`, `line-tables-only`, `limited`, or `full`";
380+
pub const parse_debuginfo_compression: &str = "one of `none`, `zlib`, or `zstd`";
379381
pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`";
380382
pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of();
381383
pub const parse_optimization_fuel: &str = "crate=integer";
@@ -782,6 +784,19 @@ mod parse {
782784
true
783785
}
784786

787+
pub(crate) fn parse_debuginfo_compression(
788+
slot: &mut DebugInfoCompression,
789+
v: Option<&str>,
790+
) -> bool {
791+
match v {
792+
Some("none") => *slot = DebugInfoCompression::None,
793+
Some("zlib") => *slot = DebugInfoCompression::Zlib,
794+
Some("zstd") => *slot = DebugInfoCompression::Zstd,
795+
_ => return false,
796+
};
797+
true
798+
}
799+
785800
pub(crate) fn parse_linker_flavor(slot: &mut Option<LinkerFlavorCli>, v: Option<&str>) -> bool {
786801
match v.and_then(LinkerFlavorCli::from_str) {
787802
Some(lf) => *slot = Some(lf),
@@ -1422,7 +1437,9 @@ options! {
14221437
"inject the given attribute in the crate"),
14231438
debug_info_for_profiling: bool = (false, parse_bool, [TRACKED],
14241439
"emit discriminators and other data necessary for AutoFDO"),
1425-
debug_macros: bool = (false, parse_bool, [TRACKED],
1440+
debuginfo_compression: DebugInfoCompression = (DebugInfoCompression::None, parse_debuginfo_compression, [TRACKED],
1441+
"compress debug info sections (none, zlib, zstd, default: none)"),
1442+
debug_macros: bool = (false, parse_bool, [TRACKED],
14261443
"emit line numbers debug info inside macros (default: no)"),
14271444
deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED],
14281445
"deduplicate identical diagnostics (default: yes)"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# ignore-cross-compile
2+
include ../tools.mk
3+
4+
# only-linux
5+
# min-llvm-version: 16.0
6+
#
7+
# This tests debuginfo-compression.
8+
9+
all: zlib zstandard
10+
11+
zlib:
12+
$(RUSTC) --crate-name=foo --crate-type=lib --emit=obj -C debuginfo=full -Z debuginfo-compression=zlib foo.rs
13+
test "`ld.lld --compress-debug-sections=zlib 2>&1 | sed 's/.*unknown.*zlib/missing/'`" = missing || readelf -t $(TMPDIR)/foo.o | grep -q ZLIB
14+
15+
zstandard:
16+
$(RUSTC) --crate-name=foo --crate-type=lib --emit=obj -C debuginfo=full -Z debuginfo-compression=zstd foo.rs
17+
test "`ld.lld --compress-debug-sections=zstd 2>&1 | sed 's/.*unknown.*zstd/missing/'`" = missing || readelf -t $(TMPDIR)/foo.o | grep -q ZST
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub fn foo() -> i32 {
2+
42
3+
}

0 commit comments

Comments
 (0)