Rollup merge of #113723 - khei4:khei4/llvm-stats, r=oli-obk,nikic

Resurrect: rustc_llvm: Add a -Z `print-codegen-stats` option to expose LLVM statistics.

This resurrects PR https://github.com/rust-lang/rust/pull/104000, which has sat idle for a while. And I want to see the effect of stack-move optimizations on LLVM (like https://reviews.llvm.org/D153453) :).

I have applied the changes requested by `@oli-obk` and `@nagisa`  https://github.com/rust-lang/rust/pull/104000#discussion_r1014625377 and https://github.com/rust-lang/rust/pull/104000#discussion_r1014642482 in the latest commits.

r? `@oli-obk`

-----

LLVM has a neat [statistics](https://llvm.org/docs/ProgrammersManual.html#the-statistic-class-stats-option) feature that tracks how often optimizations kick in. It's very handy for optimization work. Since we expose the LLVM pass timings, I thought it made sense to expose the LLVM statistics too.

-----
(Edit: fix broken link
(Edit2: fix segmentation fault and use malloc

If `rustc` is built with
```toml
[llvm]
assertions = true
```
Then you can see like
```
rustc +stage1 -Z print-codegen-stats -C opt-level=3  tmp.rs
===-------------------------------------------------------------------------===
                          ... Statistics Collected ...
===-------------------------------------------------------------------------===
         3 aa                           - Number of MayAlias results
       193 aa                           - Number of MustAlias results
       531 aa                           - Number of NoAlias results
...
```

And the current default build emits only
```
$ rustc +stage1 -Z print-codegen-stats -C opt-level=3  tmp.rs
===-------------------------------------------------------------------------===
                          ... Statistics Collected ...
===-------------------------------------------------------------------------===
$
```
This might be better to emit the message to tell assertion flag necessity, but now I can't find how to do that...
This commit is contained in:
Matthias Krüger 2023-07-21 06:52:27 +02:00 committed by GitHub
commit 2734b5ada9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 69 additions and 5 deletions

View File

@ -239,6 +239,10 @@ impl WriteBackendMethods for GccCodegenBackend {
unimplemented!(); unimplemented!();
} }
fn print_statistics(&self) {
unimplemented!()
}
unsafe fn optimize(_cgcx: &CodegenContext<Self>, _diag_handler: &Handler, module: &ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<(), FatalError> { unsafe fn optimize(_cgcx: &CodegenContext<Self>, _diag_handler: &Handler, module: &ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<(), FatalError> {
module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level)); module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level));
Ok(()) Ok(())

View File

@ -46,6 +46,7 @@ use rustc_span::symbol::Symbol;
use std::any::Any; use std::any::Any;
use std::ffi::CStr; use std::ffi::CStr;
use std::io::Write;
mod back { mod back {
pub mod archive; pub mod archive;
@ -178,7 +179,28 @@ impl WriteBackendMethods for LlvmCodegenBackend {
type ThinBuffer = back::lto::ThinBuffer; type ThinBuffer = back::lto::ThinBuffer;
fn print_pass_timings(&self) { fn print_pass_timings(&self) {
unsafe { unsafe {
llvm::LLVMRustPrintPassTimings(); let mut size = 0;
let cstr = llvm::LLVMRustPrintPassTimings(&mut size as *mut usize);
if cstr.is_null() {
println!("failed to get pass timings");
} else {
let timings = std::slice::from_raw_parts(cstr as *const u8, size);
std::io::stdout().write_all(timings).unwrap();
libc::free(cstr as *mut _);
}
}
}
fn print_statistics(&self) {
unsafe {
let mut size = 0;
let cstr = llvm::LLVMRustPrintStatistics(&mut size as *mut usize);
if cstr.is_null() {
println!("failed to get pass stats");
} else {
let stats = std::slice::from_raw_parts(cstr as *const u8, size);
std::io::stdout().write_all(stats).unwrap();
libc::free(cstr as *mut _);
}
} }
} }
fn run_link( fn run_link(

View File

@ -1868,7 +1868,10 @@ extern "C" {
pub fn LLVMRustGetLastError() -> *const c_char; pub fn LLVMRustGetLastError() -> *const c_char;
/// Print the pass timings since static dtors aren't picking them up. /// Print the pass timings since static dtors aren't picking them up.
pub fn LLVMRustPrintPassTimings(); pub fn LLVMRustPrintPassTimings(size: *const size_t) -> *const c_char;
/// Print the statistics since static dtors aren't picking them up.
pub fn LLVMRustPrintStatistics(size: *const size_t) -> *const c_char;
pub fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type; pub fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type;

View File

@ -110,6 +110,10 @@ unsafe fn configure_llvm(sess: &Session) {
// Use non-zero `import-instr-limit` multiplier for cold callsites. // Use non-zero `import-instr-limit` multiplier for cold callsites.
add("-import-cold-multiplier=0.1", false); add("-import-cold-multiplier=0.1", false);
if sess.print_llvm_stats() {
add("-stats", false);
}
for arg in sess_args { for arg in sess_args {
add(&(*arg), true); add(&(*arg), true);
} }

View File

@ -1945,6 +1945,10 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
self.backend.print_pass_timings() self.backend.print_pass_timings()
} }
if sess.print_llvm_stats() {
self.backend.print_statistics()
}
( (
CodegenResults { CodegenResults {
metadata: self.metadata, metadata: self.metadata,

View File

@ -35,6 +35,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone {
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError>; ) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError>;
fn print_pass_timings(&self); fn print_pass_timings(&self);
fn print_statistics(&self);
unsafe fn optimize( unsafe fn optimize(
cgcx: &CodegenContext<Self>, cgcx: &CodegenContext<Self>,
diag_handler: &Handler, diag_handler: &Handler,

View File

@ -715,6 +715,7 @@ fn test_unstable_options_tracking_hash() {
untracked!(perf_stats, true); untracked!(perf_stats, true);
// `pre_link_arg` is omitted because it just forwards to `pre_link_args`. // `pre_link_arg` is omitted because it just forwards to `pre_link_args`.
untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]);
untracked!(print_codegen_stats, true);
untracked!(print_llvm_passes, true); untracked!(print_llvm_passes, true);
untracked!(print_mono_items, Some(String::from("abc"))); untracked!(print_mono_items, Some(String::from("abc")));
untracked!(print_type_sizes, true); untracked!(print_type_sizes, true);

View File

@ -1,4 +1,5 @@
#include "LLVMWrapper.h" #include "LLVMWrapper.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DiagnosticHandler.h" #include "llvm/IR/DiagnosticHandler.h"
#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticInfo.h"
@ -111,9 +112,26 @@ extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M,
unwrap(M)->setTargetTriple(Triple::normalize(Triple)); unwrap(M)->setTargetTriple(Triple::normalize(Triple));
} }
extern "C" void LLVMRustPrintPassTimings() { extern "C" const char *LLVMRustPrintPassTimings(size_t *Len) {
raw_fd_ostream OS(2, false); // stderr. std::string buf;
TimerGroup::printAll(OS); raw_string_ostream SS(buf);
TimerGroup::printAll(SS);
SS.flush();
*Len = buf.length();
char *CStr = (char *)malloc(*Len);
memcpy(CStr, buf.c_str(), *Len);
return CStr;
}
extern "C" const char *LLVMRustPrintStatistics(size_t *Len) {
std::string buf;
raw_string_ostream SS(buf);
llvm::PrintStatistics(SS);
SS.flush();
*Len = buf.length();
char *CStr = (char *)malloc(*Len);
memcpy(CStr, buf.c_str(), *Len);
return CStr;
} }
extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name, extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name,

View File

@ -1668,6 +1668,9 @@ options! {
"use a more precise version of drop elaboration for matches on enums (default: yes). \ "use a more precise version of drop elaboration for matches on enums (default: yes). \
This results in better codegen, but has caused miscompilations on some tier 2 platforms. \ This results in better codegen, but has caused miscompilations on some tier 2 platforms. \
See #77382 and #74551."), See #77382 and #74551."),
#[rustc_lint_opt_deny_field_access("use `Session::print_codegen_stats` instead of this field")]
print_codegen_stats: bool = (false, parse_bool, [UNTRACKED],
"print codegen statistics (default: no)"),
print_fuel: Option<String> = (None, parse_opt_string, [TRACKED], print_fuel: Option<String> = (None, parse_opt_string, [TRACKED],
"make rustc print the total optimization fuel used by a crate"), "make rustc print the total optimization fuel used by a crate"),
print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], print_llvm_passes: bool = (false, parse_bool, [UNTRACKED],

View File

@ -1057,6 +1057,10 @@ impl Session {
self.opts.unstable_opts.verbose self.opts.unstable_opts.verbose
} }
pub fn print_llvm_stats(&self) -> bool {
self.opts.unstable_opts.print_codegen_stats
}
pub fn verify_llvm_ir(&self) -> bool { pub fn verify_llvm_ir(&self) -> bool {
self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some()
} }