#![feature(rustc_private)]
extern crate rustc_borrowck;
extern crate rustc_driver;
extern crate rustc_hir;
extern crate rustc_interface;
extern crate rustc_middle;
use std::{env::consts::DLL_SUFFIX, process::Command};
use anyhow::{Context, Result};
use criterion::{
BenchmarkGroup, Criterion, criterion_group, criterion_main, measurement::WallTime,
};
use flowistry::infoflow::Direction;
use glob::glob;
use rustc_borrowck::consumers::BodyWithBorrowckFacts;
use rustc_hir::{BodyId, ItemKind};
use rustc_middle::{
mir::{Location, Place},
ty::TyCtxt,
};
use rustc_utils::{PlaceExt, mir::borrowck_facts};
#[derive(Clone, Copy, PartialEq, Eq)]
enum AnalysisType {
FlowOnly,
FlowAndDeps,
}
fn analysis<'tcx>(
tcx: TyCtxt<'tcx>,
body_id: BodyId,
body_with_facts: &BodyWithBorrowckFacts<'tcx>,
ty: AnalysisType,
) {
let results = flowistry::infoflow::compute_flow(tcx, body_id, body_with_facts);
if ty == AnalysisType::FlowAndDeps {
let targets = body_with_facts
.body
.local_decls
.indices()
.map(|local| {
let arg = Place::make(local, &[], tcx);
vec![(arg, Location::START.into())]
})
.collect::<Vec<_>>();
flowistry::infoflow::compute_dependencies(&results, targets, Direction::Both);
}
}
struct UnsafeBenchGroup(
criterion::BenchmarkGroup<'static, criterion::measurement::WallTime>,
);
impl UnsafeBenchGroup {
fn new(bench_group: BenchmarkGroup<WallTime>) -> Self {
unsafe {
UnsafeBenchGroup(std::mem::transmute::<
criterion::BenchmarkGroup<'_, criterion::measurement::WallTime>,
criterion::BenchmarkGroup<'static, criterion::measurement::WallTime>,
>(bench_group))
}
}
}
unsafe impl Send for UnsafeBenchGroup {}
struct Callbacks {
group: UnsafeBenchGroup,
}
impl rustc_driver::Callbacks for Callbacks {
fn config(&mut self, config: &mut rustc_interface::Config) {
borrowck_facts::enable_mir_simplification();
config.override_queries = Some(borrowck_facts::override_queries);
}
fn after_analysis<'tcx>(
&mut self,
_compiler: &rustc_interface::interface::Compiler,
tcx: TyCtxt<'tcx>,
) -> rustc_driver::Compilation {
let body_id = tcx
.hir_free_items()
.filter_map(|id| match tcx.hir_item(id).kind {
ItemKind::Fn { body, .. } => Some(body),
_ => None,
})
.next()
.unwrap();
let def_id = tcx.hir_body_owner_def_id(body_id);
let body_with_facts = borrowck_facts::get_body_with_borrowck_facts(tcx, def_id);
for analysis_ty in [AnalysisType::FlowOnly, AnalysisType::FlowAndDeps] {
let bench_id = match analysis_ty {
AnalysisType::FlowOnly => "Flow",
AnalysisType::FlowAndDeps => "Flow + Deps",
};
self.group.0.bench_function(bench_id, |b| {
b.iter(|| analysis(tcx, body_id, body_with_facts, analysis_ty))
});
}
rustc_driver::Compilation::Stop
}
}
fn criterion_benchmark(c: &mut Criterion) {
const TESTS: &[(&str, &str)] = &[
("Locations", "locations.rs"),
("Unique Lifetimes", "lifetimes_unique.rs"),
("Infoflow", "infoflow.rs"),
("Places", "places.rs"),
("Same Lifetime", "lifetimes_same.rs"),
("Nested Structs", "nested_struct.rs"),
];
(|| -> Result<()> {
let current_exe =
std::env::current_exe().context("Failed to find current executable")?;
let curr_dir = current_exe
.parent()
.context("Failed to find path to current exe parent")?;
let test_dir = curr_dir.join("../../../crates/flowistry/benches/tests");
let bench_crate_pattern = curr_dir.join(format!("*libbench_utils*{}", DLL_SUFFIX));
let print_sysroot = Command::new("rustc")
.args(&["--print", "sysroot"])
.output()
.context("Failed to print rustc sysroot")?
.stdout;
let sysroot = String::from_utf8(print_sysroot)?.trim().to_owned();
let shared_object = glob(bench_crate_pattern.to_str().unwrap())?
.nth(0)
.with_context(|| {
format!(
"Failed to find bench_utils shared object in dir {}",
curr_dir.display()
)
})??;
let mut run_bench = |test: (&str, &str)| {
for stress_ty in ["min", "max"] {
let test_name = format!("{} ({})", test.0, stress_ty);
let mut args: Vec<String> = vec!["".into()];
let test_file = std::path::Path::new(stress_ty).join(test.1);
args.extend([test_dir.join(test_file).to_str().unwrap().into()]);
args.extend([
"--extern".into(),
format!("bench_utils={}", shared_object.to_str().unwrap()),
]);
args.extend(["--sysroot".into(), sysroot.clone()]);
let group = UnsafeBenchGroup::new(c.benchmark_group(&test_name));
let mut callbacks = Callbacks { group };
rustc_driver::catch_fatal_errors(|| {
rustc_driver::run_compiler(&args, &mut callbacks)
})
.unwrap();
}
};
match std::env::var("FLOWISTRY_BENCH_TEST") {
Ok(test_file) => {
let test = TESTS
.into_iter()
.find(|t| t.1 == test_file)
.with_context(|| format!("Failed to find test file '{test_file}'"))?;
run_bench(*test);
}
_ => {
for test in TESTS {
run_bench(*test);
}
}
}
Ok(())
})()
.unwrap()
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);