From 4dc3b99b27623dc142998d9009f64bcf48b9c7b2 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 25 Apr 2019 13:11:08 +0200 Subject: [PATCH] PGO: Add a run-make test that makes sure that PGO profiling data is used by the compiler during optimizations. --- src/test/run-make-fulldeps/pgo-use/Makefile | 43 +++++++++++++++++++ .../pgo-use/filecheck-patterns.txt | 11 +++++ src/test/run-make-fulldeps/pgo-use/main.rs | 23 ++++++++++ 3 files changed, 77 insertions(+) create mode 100644 src/test/run-make-fulldeps/pgo-use/Makefile create mode 100644 src/test/run-make-fulldeps/pgo-use/filecheck-patterns.txt create mode 100644 src/test/run-make-fulldeps/pgo-use/main.rs diff --git a/src/test/run-make-fulldeps/pgo-use/Makefile b/src/test/run-make-fulldeps/pgo-use/Makefile new file mode 100644 index 00000000000..52e2bab4b13 --- /dev/null +++ b/src/test/run-make-fulldeps/pgo-use/Makefile @@ -0,0 +1,43 @@ +# needs-profiler-support + +-include ../tools.mk + +# This test makes sure that PGO profiling data leads to cold functions being +# marked as `cold` and hot functions with `inlinehint`. +# The test program contains an `if` were actual execution only ever takes the +# `else` branch. Accordingly, we expect the function that is never called to +# be marked as cold. +# +# The program is compiled with `-Copt-level=s` because this setting disables +# LLVM's pre-inlining pass (i.e. a pass that does some inlining before it adds +# the profiling instrumentation). Disabling this pass leads to rather +# predictable IR which we need for this test to be stable. + +COMMON_FLAGS=-Copt-level=s -Ccodegen-units=1 + +# LLVM doesn't support instrumenting binaries that use SEH: +# https://bugs.llvm.org/show_bug.cgi?id=41279 +# +# Things work fine with -Cpanic=abort though. +ifdef IS_MSVC +COMMON_FLAGS+= -Cpanic=abort +endif + +all: + # Compile the test program with instrumentation + $(RUSTC) $(COMMON_FLAGS) -Z pgo-gen="$(TMPDIR)" main.rs + # Run it in order to generate some profiling data + $(call RUN,main some-argument) || exit 1 + # Postprocess the profiling data so it can be used by the compiler + $(LD_LIB_PATH_ENVVAR)=$(REAL_LD_LIBRARY_PATH) llvm-profdata merge \ + -o "$(TMPDIR)"/merged.profdata \ + "$(TMPDIR)"/default_*.profraw + # Compile the test program again, making use of the profiling data + $(RUSTC) $(COMMON_FLAGS) -Z pgo-use="$(TMPDIR)"/merged.profdata --emit=llvm-ir main.rs + # Check that the generate IR contains some things that we expect + # + # We feed the file into LLVM FileCheck tool *in reverse* so that we see the + # line with the function name before the line with the function attributes. + # FileCheck only supports checking that something matches on the next line, + # but not if something matches on the previous line. + tac "$(TMPDIR)"/main.ll | "$(LLVM_FILECHECK)" filecheck-patterns.txt diff --git a/src/test/run-make-fulldeps/pgo-use/filecheck-patterns.txt b/src/test/run-make-fulldeps/pgo-use/filecheck-patterns.txt new file mode 100644 index 00000000000..6da34f88f2a --- /dev/null +++ b/src/test/run-make-fulldeps/pgo-use/filecheck-patterns.txt @@ -0,0 +1,11 @@ +# Add a check that the IR contains some expected metadata +CHECK: !{!"ProfileFormat", !"InstrProf"} +CHECK: !"ProfileSummary" + +# Make sure that the hot function is marked with `inlinehint` +CHECK: define {{.*}} @hot_function +CHECK-NEXT: Function Attrs:{{.*}}inlinehint + +# Make sure that the cold function is marked with `cold` +CHECK: define {{.*}} @cold_function +CHECK-NEXT: Function Attrs:{{.*}}cold diff --git a/src/test/run-make-fulldeps/pgo-use/main.rs b/src/test/run-make-fulldeps/pgo-use/main.rs new file mode 100644 index 00000000000..eb9192c87e6 --- /dev/null +++ b/src/test/run-make-fulldeps/pgo-use/main.rs @@ -0,0 +1,23 @@ +#[no_mangle] +pub fn cold_function(c: u8) { + println!("cold {}", c); +} + +#[no_mangle] +pub fn hot_function(c: u8) { + std::env::set_var(format!("var{}", c), format!("hot {}", c)); +} + +fn main() { + let arg = std::env::args().skip(1).next().unwrap(); + + for i in 0 .. 1000_000 { + let some_value = arg.as_bytes()[i % arg.len()]; + if some_value == b'!' { + // This branch is never taken at runtime + cold_function(some_value); + } else { + hot_function(some_value); + } + } +}