Skip to content

Commit 4dc3b99

Browse files
PGO: Add a run-make test that makes sure that PGO profiling data is used by the compiler during optimizations.
1 parent 68b6924 commit 4dc3b99

File tree

3 files changed

+77
-0
lines changed

3 files changed

+77
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# needs-profiler-support
2+
3+
-include ../tools.mk
4+
5+
# This test makes sure that PGO profiling data leads to cold functions being
6+
# marked as `cold` and hot functions with `inlinehint`.
7+
# The test program contains an `if` were actual execution only ever takes the
8+
# `else` branch. Accordingly, we expect the function that is never called to
9+
# be marked as cold.
10+
#
11+
# The program is compiled with `-Copt-level=s` because this setting disables
12+
# LLVM's pre-inlining pass (i.e. a pass that does some inlining before it adds
13+
# the profiling instrumentation). Disabling this pass leads to rather
14+
# predictable IR which we need for this test to be stable.
15+
16+
COMMON_FLAGS=-Copt-level=s -Ccodegen-units=1
17+
18+
# LLVM doesn't support instrumenting binaries that use SEH:
19+
# https://bugs.llvm.org/show_bug.cgi?id=41279
20+
#
21+
# Things work fine with -Cpanic=abort though.
22+
ifdef IS_MSVC
23+
COMMON_FLAGS+= -Cpanic=abort
24+
endif
25+
26+
all:
27+
# Compile the test program with instrumentation
28+
$(RUSTC) $(COMMON_FLAGS) -Z pgo-gen="$(TMPDIR)" main.rs
29+
# Run it in order to generate some profiling data
30+
$(call RUN,main some-argument) || exit 1
31+
# Postprocess the profiling data so it can be used by the compiler
32+
$(LD_LIB_PATH_ENVVAR)=$(REAL_LD_LIBRARY_PATH) llvm-profdata merge \
33+
-o "$(TMPDIR)"/merged.profdata \
34+
"$(TMPDIR)"/default_*.profraw
35+
# Compile the test program again, making use of the profiling data
36+
$(RUSTC) $(COMMON_FLAGS) -Z pgo-use="$(TMPDIR)"/merged.profdata --emit=llvm-ir main.rs
37+
# Check that the generate IR contains some things that we expect
38+
#
39+
# We feed the file into LLVM FileCheck tool *in reverse* so that we see the
40+
# line with the function name before the line with the function attributes.
41+
# FileCheck only supports checking that something matches on the next line,
42+
# but not if something matches on the previous line.
43+
tac "$(TMPDIR)"/main.ll | "$(LLVM_FILECHECK)" filecheck-patterns.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Add a check that the IR contains some expected metadata
2+
CHECK: !{!"ProfileFormat", !"InstrProf"}
3+
CHECK: !"ProfileSummary"
4+
5+
# Make sure that the hot function is marked with `inlinehint`
6+
CHECK: define {{.*}} @hot_function
7+
CHECK-NEXT: Function Attrs:{{.*}}inlinehint
8+
9+
# Make sure that the cold function is marked with `cold`
10+
CHECK: define {{.*}} @cold_function
11+
CHECK-NEXT: Function Attrs:{{.*}}cold
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#[no_mangle]
2+
pub fn cold_function(c: u8) {
3+
println!("cold {}", c);
4+
}
5+
6+
#[no_mangle]
7+
pub fn hot_function(c: u8) {
8+
std::env::set_var(format!("var{}", c), format!("hot {}", c));
9+
}
10+
11+
fn main() {
12+
let arg = std::env::args().skip(1).next().unwrap();
13+
14+
for i in 0 .. 1000_000 {
15+
let some_value = arg.as_bytes()[i % arg.len()];
16+
if some_value == b'!' {
17+
// This branch is never taken at runtime
18+
cold_function(some_value);
19+
} else {
20+
hot_function(some_value);
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)