Auto merge of #113339 - lqd:respect-filters, r=tmiasko
Filter out short-lived LLVM diagnostics before they reach the rustc handler During profiling I saw remark passes being unconditionally enabled: for example `Machine Optimization Remark Emitter`. The diagnostic remarks enabled by default are [from missed optimizations and opt analyses](https://github.com/rust-lang/rust/pull/113339#discussion_r1259480303). They are created by LLVM, passed to the diagnostic handler on the C++ side, emitted to rust, where they are unpacked, C++ strings are converted to rust, etc. Then they are discarded in the vast majority of the time (i.e. unless some kind of `-Cremark` has enabled some of these passes' output to be printed). These unneeded allocations are very short-lived, basically only lasting between the LLVM pass emitting them and the rust handler where they are discarded. So it doesn't hugely impact max-rss, and is only a slight reduction in instruction count (cachegrind reports a reduction between 0.3% and 0.5%) _on linux_. It's possible that targets without `jemalloc` or with a worse allocator, may optimize these less. It is however significant in the aggregate, looking at the total number of allocated bytes: - it's the biggest source of allocations according to dhat, on the benchmarks I've tried e.g. `syn` or `cargo` - allocations on `syn` are reduced by 440MB, 17% (from 2440722647 bytes total, to 2030461328 bytes) - allocations on `cargo` are reduced by 6.6GB, 19% (from 35371886402 bytes total, to 28723987743 bytes) Some of these diagnostics objects [are allocated in LLVM](https://github.com/rust-lang/rust/pull/113339#discussion_r1252387484) *before* they're emitted to our diagnostic handler, where they'll be filtered out. So we could remove those in the future, but that will require changing a few LLVM call-sites upstream, so I left a FIXME.
This commit is contained in:
commit
f77c624c03
@ -381,29 +381,22 @@ fn report_inline_asm(
|
|||||||
}
|
}
|
||||||
|
|
||||||
llvm::diagnostic::Optimization(opt) => {
|
llvm::diagnostic::Optimization(opt) => {
|
||||||
let enabled = match cgcx.remark {
|
diag_handler.emit_note(FromLlvmOptimizationDiag {
|
||||||
Passes::All => true,
|
filename: &opt.filename,
|
||||||
Passes::Some(ref v) => v.iter().any(|s| *s == opt.pass_name),
|
line: opt.line,
|
||||||
};
|
column: opt.column,
|
||||||
|
pass_name: &opt.pass_name,
|
||||||
if enabled {
|
kind: match opt.kind {
|
||||||
diag_handler.emit_note(FromLlvmOptimizationDiag {
|
OptimizationDiagnosticKind::OptimizationRemark => "success",
|
||||||
filename: &opt.filename,
|
OptimizationDiagnosticKind::OptimizationMissed
|
||||||
line: opt.line,
|
| OptimizationDiagnosticKind::OptimizationFailure => "missed",
|
||||||
column: opt.column,
|
OptimizationDiagnosticKind::OptimizationAnalysis
|
||||||
pass_name: &opt.pass_name,
|
| OptimizationDiagnosticKind::OptimizationAnalysisFPCommute
|
||||||
kind: match opt.kind {
|
| OptimizationDiagnosticKind::OptimizationAnalysisAliasing => "analysis",
|
||||||
OptimizationDiagnosticKind::OptimizationRemark => "success",
|
OptimizationDiagnosticKind::OptimizationRemarkOther => "other",
|
||||||
OptimizationDiagnosticKind::OptimizationMissed
|
},
|
||||||
| OptimizationDiagnosticKind::OptimizationFailure => "missed",
|
message: &opt.message,
|
||||||
OptimizationDiagnosticKind::OptimizationAnalysis
|
});
|
||||||
| OptimizationDiagnosticKind::OptimizationAnalysisFPCommute
|
|
||||||
| OptimizationDiagnosticKind::OptimizationAnalysisAliasing => "analysis",
|
|
||||||
OptimizationDiagnosticKind::OptimizationRemarkOther => "other",
|
|
||||||
},
|
|
||||||
message: &opt.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
llvm::diagnostic::PGO(diagnostic_ref) | llvm::diagnostic::Linker(diagnostic_ref) => {
|
llvm::diagnostic::PGO(diagnostic_ref) | llvm::diagnostic::Linker(diagnostic_ref) => {
|
||||||
let message = llvm::build_string(|s| {
|
let message = llvm::build_string(|s| {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
use super::{DiagnosticInfo, SMDiagnostic};
|
use super::{DiagnosticInfo, SMDiagnostic};
|
||||||
use rustc_span::InnerSpan;
|
use rustc_span::InnerSpan;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum OptimizationDiagnosticKind {
|
pub enum OptimizationDiagnosticKind {
|
||||||
OptimizationRemark,
|
OptimizationRemark,
|
||||||
OptimizationMissed,
|
OptimizationMissed,
|
||||||
|
@ -1892,12 +1892,19 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler(
|
|||||||
LlvmRemarkStreamer(std::move(LlvmRemarkStreamer)) {}
|
LlvmRemarkStreamer(std::move(LlvmRemarkStreamer)) {}
|
||||||
|
|
||||||
virtual bool handleDiagnostics(const DiagnosticInfo &DI) override {
|
virtual bool handleDiagnostics(const DiagnosticInfo &DI) override {
|
||||||
if (this->LlvmRemarkStreamer) {
|
// If this diagnostic is one of the optimization remark kinds, we can check if it's enabled
|
||||||
if (auto *OptDiagBase = dyn_cast<DiagnosticInfoOptimizationBase>(&DI)) {
|
// before emitting it. This can avoid many short-lived allocations when unpacking the
|
||||||
if (OptDiagBase->isEnabled()) {
|
// diagnostic and converting its various C++ strings into rust strings.
|
||||||
|
// FIXME: some diagnostic infos still allocate before we get here, and avoiding that would be
|
||||||
|
// good in the future. That will require changing a few call sites in LLVM.
|
||||||
|
if (auto *OptDiagBase = dyn_cast<DiagnosticInfoOptimizationBase>(&DI)) {
|
||||||
|
if (OptDiagBase->isEnabled()) {
|
||||||
|
if (this->LlvmRemarkStreamer) {
|
||||||
this->LlvmRemarkStreamer->emit(*OptDiagBase);
|
this->LlvmRemarkStreamer->emit(*OptDiagBase);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (DiagnosticHandlerCallback) {
|
if (DiagnosticHandlerCallback) {
|
||||||
|
Loading…
Reference in New Issue
Block a user