PGO: Add regression test for indirect call promotion.

This commit is contained in:
Michael Woerister 2019-11-22 13:38:28 +01:00
parent 0675d65093
commit 68785d9614
5 changed files with 129 additions and 0 deletions

View file

@ -0,0 +1,36 @@
# needs-profiler-support
-include ../tools.mk
# This test makes sure that indirect call promotion is performed. The test
# programs calls the same function a thousand times through a function pointer.
# Only PGO data provides the information that it actually always is the same
# function. We verify that the indirect call promotion pass inserts a check
# whether it can make a direct call instead of the indirect call.
# LLVM doesn't support instrumenting binaries that use SEH:
# https://github.com/rust-lang/rust/issues/61002
#
# Things work fine with -Cpanic=abort though.
ifdef IS_MSVC
COMMON_FLAGS=-Cpanic=abort
endif
all:
# We don't compile `opaque` with either optimizations or instrumentation.
# We don't compile `opaque` with either optimizations or instrumentation.
$(RUSTC) $(COMMON_FLAGS) opaque.rs
# Compile the test program with instrumentation
mkdir -p "$(TMPDIR)"/prof_data_dir
$(RUSTC) $(COMMON_FLAGS) interesting.rs \
-Cprofile-generate="$(TMPDIR)"/prof_data_dir -O -Ccodegen-units=1
$(RUSTC) $(COMMON_FLAGS) main.rs -Cprofile-generate="$(TMPDIR)"/prof_data_dir -O
# The argument below generates to the expected branch weights
$(call RUN,main) || exit 1
"$(LLVM_BIN_DIR)"/llvm-profdata merge \
-o "$(TMPDIR)"/prof_data_dir/merged.profdata \
"$(TMPDIR)"/prof_data_dir
$(RUSTC) $(COMMON_FLAGS) interesting.rs \
-Cprofile-use="$(TMPDIR)"/prof_data_dir/merged.profdata -O \
-Ccodegen-units=1 --emit=llvm-ir
cat "$(TMPDIR)"/interesting.ll | "$(LLVM_FILECHECK)" filecheck-patterns.txt

View file

@ -0,0 +1,16 @@
CHECK: define void @call_a_bunch_of_functions({{.*}} {
# Make sure that indirect call promotion inserted a check against the most
# frequently called function.
CHECK: %{{.*}} = icmp eq void ()* %{{.*}}, @function_called_always
# Check that the call to `function_called_always` was inlined, so that we
# directly call `opaque_f1` from the upstream crate.
CHECK: call void @opaque_f1()
# Same checks as above, repeated for the trait object case
CHECK: define void @call_a_bunch_of_trait_methods({{.*}}
CHECK: %{{.*}} = icmp eq void ({}*)* %{{.*}}, {{.*}} @foo
CHECK: tail call void @opaque_f2()

View file

@ -0,0 +1,56 @@
#![crate_name="interesting"]
#![crate_type="rlib"]
extern crate opaque;
#[no_mangle]
pub fn function_called_always() {
opaque::opaque_f1();
}
#[no_mangle]
pub fn function_called_never() {
opaque::opaque_f2();
}
#[no_mangle]
pub fn call_a_bunch_of_functions(fns: &[fn()]) {
// Indirect call promotion transforms the below into something like
//
// for f in fns {
// if f == function_called_always {
// function_called_always()
// } else {
// f();
// }
// }
//
// where `function_called_always` actually gets inlined too.
for f in fns {
f();
}
}
pub trait Foo {
fn foo(&self);
}
impl Foo for u32 {
#[no_mangle]
fn foo(&self) {
opaque::opaque_f2();
}
}
#[no_mangle]
pub fn call_a_bunch_of_trait_methods(trait_objects: &[&dyn Foo]) {
// Same as above, just with vtables in between
for x in trait_objects {
x.foo();
}
}

View file

@ -0,0 +1,14 @@
extern crate interesting;
fn main() {
// function pointer case
let fns: Vec<_> = std::iter::repeat(interesting::function_called_always as fn())
.take(1000)
.collect();
interesting::call_a_bunch_of_functions(&fns[..]);
// Trait object case
let trait_objects = vec![0u32; 1000];
let trait_objects: Vec<_> = trait_objects.iter().map(|x| x as &dyn interesting::Foo).collect();
interesting::call_a_bunch_of_trait_methods(&trait_objects[..]);
}

View file

@ -0,0 +1,7 @@
#![crate_name="opaque"]
#![crate_type="rlib"]
#[no_mangle]
pub fn opaque_f1() {}
#[no_mangle]
pub fn opaque_f2() {}