Skip to content

Commit 1dc3686

Browse files
committed
Auto merge of #66631 - michaelwoerister:additional-pgo-tests, r=alexcrichton
Add additional regression tests for PGO This PR adds regression tests for making sure that - instrumentation records the right counts for branches taken and functions called, and that - the indirect call promotion pass actually is able to promote indirect calls. r? @alexcrichton
2 parents 0f6f66f + 34fbc71 commit 1dc3686

File tree

10 files changed

+251
-0
lines changed

10 files changed

+251
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# needs-profiler-support
2+
3+
-include ../tools.mk
4+
5+
# This test makes sure that instrumented binaries record the right counts for
6+
# functions being called and branches being taken. We run an instrumented binary
7+
# with an argument that causes a know path through the program and then check
8+
# that the expected counts get added to the use-phase LLVM IR.
9+
10+
# LLVM doesn't support instrumenting binaries that use SEH:
11+
# https://github.com/rust-lang/rust/issues/61002
12+
#
13+
# Things work fine with -Cpanic=abort though.
14+
ifdef IS_MSVC
15+
COMMON_FLAGS=-Cpanic=abort
16+
endif
17+
18+
all:
19+
# We don't compile `opaque` with either optimizations or instrumentation.
20+
# We don't compile `opaque` with either optimizations or instrumentation.
21+
$(RUSTC) $(COMMON_FLAGS) opaque.rs
22+
# Compile the test program with instrumentation
23+
mkdir -p "$(TMPDIR)"/prof_data_dir
24+
$(RUSTC) $(COMMON_FLAGS) interesting.rs \
25+
-Cprofile-generate="$(TMPDIR)"/prof_data_dir -O -Ccodegen-units=1
26+
$(RUSTC) $(COMMON_FLAGS) main.rs -Cprofile-generate="$(TMPDIR)"/prof_data_dir -O
27+
# The argument below generates to the expected branch weights
28+
$(call RUN,main aaaaaaaaaaaa2bbbbbbbbbbbb2bbbbbbbbbbbbbbbbcc) || exit 1
29+
"$(LLVM_BIN_DIR)"/llvm-profdata merge \
30+
-o "$(TMPDIR)"/prof_data_dir/merged.profdata \
31+
"$(TMPDIR)"/prof_data_dir
32+
$(RUSTC) $(COMMON_FLAGS) interesting.rs \
33+
-Cprofile-use="$(TMPDIR)"/prof_data_dir/merged.profdata -O \
34+
-Ccodegen-units=1 --emit=llvm-ir
35+
cat "$(TMPDIR)"/interesting.ll | "$(LLVM_FILECHECK)" filecheck-patterns.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
# First, establish that certain !prof labels are attached to the expected
3+
# functions and branching instructions.
4+
5+
CHECK: define void @function_called_twice(i32 %c) {{.*}} !prof [[function_called_twice_id:![0-9]+]] {
6+
CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !prof [[branch_weights0:![0-9]+]]
7+
8+
CHECK: define void @function_called_42_times(i32 %c) {{.*}} !prof [[function_called_42_times_id:![0-9]+]] {
9+
CHECK: switch i32 %c, label {{.*}} [
10+
CHECK-NEXT: i32 97, label {{.*}}
11+
CHECK-NEXT: i32 98, label {{.*}}
12+
CHECK-NEXT: ], !prof [[branch_weights1:![0-9]+]]
13+
14+
CHECK: define void @function_called_never(i32 {{.*}} !prof [[function_called_never_id:![0-9]+]] {
15+
16+
17+
18+
# Now check that those !prof tags hold the expected counts
19+
20+
CHECK: [[function_called_twice_id]] = !{!"function_entry_count", i64 2}
21+
CHECK: [[branch_weights0]] = !{!"branch_weights", i32 2, i32 0}
22+
CHECK: [[function_called_42_times_id]] = !{!"function_entry_count", i64 42}
23+
CHECK: [[branch_weights1]] = !{!"branch_weights", i32 2, i32 12, i32 28}
24+
CHECK: [[function_called_never_id]] = !{!"function_entry_count", i64 0}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#![crate_name="interesting"]
2+
#![crate_type="rlib"]
3+
4+
extern crate opaque;
5+
6+
#[no_mangle]
7+
#[inline(never)]
8+
pub fn function_called_twice(c: char) {
9+
if c == '2' {
10+
// This branch is taken twice
11+
opaque::f1();
12+
} else {
13+
// This branch is never taken
14+
opaque::f2();
15+
}
16+
}
17+
18+
#[no_mangle]
19+
#[inline(never)]
20+
pub fn function_called_42_times(c: char) {
21+
if c == 'a' {
22+
// This branch is taken 12 times
23+
opaque::f1();
24+
} else {
25+
26+
if c == 'b' {
27+
// This branch is taken 28 times
28+
opaque::f2();
29+
} else {
30+
// This branch is taken 2 times
31+
opaque::f3();
32+
}
33+
}
34+
}
35+
36+
#[no_mangle]
37+
#[inline(never)]
38+
pub fn function_called_never(_: char) {
39+
opaque::f1();
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
extern crate interesting;
2+
3+
fn main() {
4+
let arg = std::env::args().skip(1).next().unwrap();
5+
6+
for c in arg.chars() {
7+
if c == '2' {
8+
interesting::function_called_twice(c);
9+
} else {
10+
interesting::function_called_42_times(c);
11+
}
12+
13+
if c == '0' {
14+
interesting::function_called_never(c);
15+
}
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#![crate_name="opaque"]
2+
#![crate_type="rlib"]
3+
4+
pub fn f1() {}
5+
pub fn f2() {}
6+
pub fn f3() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# needs-profiler-support
2+
3+
-include ../tools.mk
4+
5+
# This test makes sure that indirect call promotion is performed. The test
6+
# programs calls the same function a thousand times through a function pointer.
7+
# Only PGO data provides the information that it actually always is the same
8+
# function. We verify that the indirect call promotion pass inserts a check
9+
# whether it can make a direct call instead of the indirect call.
10+
11+
# LLVM doesn't support instrumenting binaries that use SEH:
12+
# https://github.com/rust-lang/rust/issues/61002
13+
#
14+
# Things work fine with -Cpanic=abort though.
15+
ifdef IS_MSVC
16+
COMMON_FLAGS=-Cpanic=abort
17+
endif
18+
19+
all:
20+
# We don't compile `opaque` with either optimizations or instrumentation.
21+
# We don't compile `opaque` with either optimizations or instrumentation.
22+
$(RUSTC) $(COMMON_FLAGS) opaque.rs
23+
# Compile the test program with instrumentation
24+
mkdir -p "$(TMPDIR)"/prof_data_dir
25+
$(RUSTC) $(COMMON_FLAGS) interesting.rs \
26+
-Cprofile-generate="$(TMPDIR)"/prof_data_dir -O -Ccodegen-units=1
27+
$(RUSTC) $(COMMON_FLAGS) main.rs -Cprofile-generate="$(TMPDIR)"/prof_data_dir -O
28+
# The argument below generates to the expected branch weights
29+
$(call RUN,main) || exit 1
30+
"$(LLVM_BIN_DIR)"/llvm-profdata merge \
31+
-o "$(TMPDIR)"/prof_data_dir/merged.profdata \
32+
"$(TMPDIR)"/prof_data_dir
33+
$(RUSTC) $(COMMON_FLAGS) interesting.rs \
34+
-Cprofile-use="$(TMPDIR)"/prof_data_dir/merged.profdata -O \
35+
-Ccodegen-units=1 --emit=llvm-ir
36+
cat "$(TMPDIR)"/interesting.ll | "$(LLVM_FILECHECK)" filecheck-patterns.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CHECK: define void @call_a_bunch_of_functions({{.*}} {
2+
3+
# Make sure that indirect call promotion inserted a check against the most
4+
# frequently called function.
5+
CHECK: %{{.*}} = icmp eq void ()* %{{.*}}, @function_called_always
6+
7+
# Check that the call to `function_called_always` was inlined, so that we
8+
# directly call `opaque_f1` from the upstream crate.
9+
CHECK: call void @opaque_f1()
10+
11+
12+
# Same checks as above, repeated for the trait object case
13+
14+
CHECK: define void @call_a_bunch_of_trait_methods({{.*}}
15+
CHECK: %{{.*}} = icmp eq void ({}*)* %{{.*}}, {{.*}} @foo
16+
CHECK: tail call void @opaque_f2()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#![crate_name="interesting"]
2+
#![crate_type="rlib"]
3+
4+
extern crate opaque;
5+
6+
#[no_mangle]
7+
pub fn function_called_always() {
8+
opaque::opaque_f1();
9+
}
10+
11+
#[no_mangle]
12+
pub fn function_called_never() {
13+
opaque::opaque_f2();
14+
}
15+
16+
#[no_mangle]
17+
pub fn call_a_bunch_of_functions(fns: &[fn()]) {
18+
19+
// Indirect call promotion transforms the below into something like
20+
//
21+
// for f in fns {
22+
// if f == function_called_always {
23+
// function_called_always()
24+
// } else {
25+
// f();
26+
// }
27+
// }
28+
//
29+
// where `function_called_always` actually gets inlined too.
30+
31+
for f in fns {
32+
f();
33+
}
34+
}
35+
36+
37+
pub trait Foo {
38+
fn foo(&self);
39+
}
40+
41+
impl Foo for u32 {
42+
43+
#[no_mangle]
44+
fn foo(&self) {
45+
opaque::opaque_f2();
46+
}
47+
}
48+
49+
#[no_mangle]
50+
pub fn call_a_bunch_of_trait_methods(trait_objects: &[&dyn Foo]) {
51+
52+
// Same as above, just with vtables in between
53+
for x in trait_objects {
54+
x.foo();
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
extern crate interesting;
2+
3+
fn main() {
4+
// function pointer case
5+
let fns: Vec<_> = std::iter::repeat(interesting::function_called_always as fn())
6+
.take(1000)
7+
.collect();
8+
interesting::call_a_bunch_of_functions(&fns[..]);
9+
10+
// Trait object case
11+
let trait_objects = vec![0u32; 1000];
12+
let trait_objects: Vec<_> = trait_objects.iter().map(|x| x as &dyn interesting::Foo).collect();
13+
interesting::call_a_bunch_of_trait_methods(&trait_objects[..]);
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![crate_name="opaque"]
2+
#![crate_type="rlib"]
3+
4+
#[no_mangle]
5+
pub fn opaque_f1() {}
6+
#[no_mangle]
7+
pub fn opaque_f2() {}

0 commit comments

Comments
 (0)