Skip to content
Permalink
Browse files

PGO: Add regression test for indirect call promotion.

  • Loading branch information
michaelwoerister committed Nov 22, 2019
1 parent 0675d65 commit 68785d961459d3aa13ffa540471cdffe628d07f8
@@ -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
@@ -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()
@@ -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();
}
}
@@ -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[..]);
}
@@ -0,0 +1,7 @@
#![crate_name="opaque"]
#![crate_type="rlib"]

#[no_mangle]
pub fn opaque_f1() {}
#[no_mangle]
pub fn opaque_f2() {}

0 comments on commit 68785d9

Please sign in to comment.
You can’t perform that action at this time.