pub mod interproc;
pub mod intraproc;
pub mod transfer;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{Operand, TerminatorKind};
use rustc_middle::ty::{self, TyCtxt};
use rustc_mir_dataflow::Analysis;
use std::cell::RefCell;
use std::rc::Rc;
use super::{AliasAnalysis, FnAliasMap, FnAliasPairs};
use crate::analysis::Analysis as RapxAnalysis;
use intraproc::FnAliasAnalyzer;
pub struct MfpAliasAnalyzer<'tcx> {
tcx: TyCtxt<'tcx>,
fn_map: FxHashMap<DefId, FnAliasPairs>,
}
impl<'tcx> MfpAliasAnalyzer<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>) -> Self {
MfpAliasAnalyzer {
tcx,
fn_map: FxHashMap::default(),
}
}
fn get_arg_count(&self, def_id: DefId) -> Option<usize> {
if !self.tcx.is_mir_available(def_id) {
return None;
}
if let Some(local_def_id) = def_id.as_local() {
if self.tcx.hir_body_const_context(local_def_id).is_some() {
return None;
}
}
let body = self.tcx.optimized_mir(def_id);
Some(body.arg_count)
}
fn analyze_function(
&mut self,
def_id: DefId,
fn_summaries: &Rc<RefCell<FxHashMap<DefId, FnAliasPairs>>>,
) {
let fn_name = self.tcx.def_path_str(def_id);
if !self.tcx.is_mir_available(def_id) {
return;
}
if let Some(local_def_id) = def_id.as_local() {
if self.tcx.hir_body_const_context(local_def_id).is_some() {
return;
}
}
if fn_name.contains("__raw_ptr_deref_dummy") {
return;
}
let body = self.tcx.optimized_mir(def_id);
let analyzer = FnAliasAnalyzer::new(self.tcx, def_id, body, fn_summaries.clone());
let mut results = analyzer
.iterate_to_fixpoint(self.tcx, body, None)
.into_results_cursor(body);
let iter_cnt = *results.analysis().bb_iter_cnt.borrow();
let bb_cnt = body.basic_blocks.iter().len();
rap_debug!(
"[Alias-mfp] {fn_name} {iter_cnt}/{bb_cnt}, analysis ratio: {:.2}",
(iter_cnt as f32) / (bb_cnt as f32)
);
let new_summary = interproc::extract_summary(&mut results, body, def_id);
if let Some(old_summary) = self.fn_map.get_mut(&def_id) {
for alias in new_summary.aliases() {
old_summary.add_alias(alias.clone());
}
} else {
self.fn_map.insert(def_id, new_summary);
}
}
fn collect_reachable_functions(&self, def_id: DefId, reachable: &mut FxHashSet<DefId>) {
if reachable.contains(&def_id) {
return;
}
if self.get_arg_count(def_id).is_none() {
return;
}
reachable.insert(def_id);
let body = self.tcx.optimized_mir(def_id);
for bb_data in body.basic_blocks.iter() {
if let Some(terminator) = &bb_data.terminator {
if let TerminatorKind::Call { func, .. } = &terminator.kind {
if let Operand::Constant(c) = func {
if let ty::FnDef(callee_def_id, _) = c.ty().kind() {
self.collect_reachable_functions(*callee_def_id, reachable);
}
}
}
}
}
}
}
impl<'tcx> RapxAnalysis for MfpAliasAnalyzer<'tcx> {
fn name(&self) -> &'static str {
"Alias Analysis (MFP)"
}
fn run(&mut self) {
let mir_keys = self.tcx.mir_keys(());
let fn_summaries = Rc::new(RefCell::new(FxHashMap::default()));
let mut reachable_functions = FxHashSet::default();
for local_def_id in mir_keys.iter() {
self.collect_reachable_functions(local_def_id.to_def_id(), &mut reachable_functions);
}
for def_id in reachable_functions.iter() {
if let Some(arg_count) = self.get_arg_count(*def_id) {
self.fn_map.insert(*def_id, FnAliasPairs::new(arg_count));
}
}
let reachable_vec: Vec<DefId> = reachable_functions.iter().copied().collect();
const MAX_ITERATIONS: usize = 10;
let mut iteration = 0;
loop {
iteration += 1;
let mut changed = false;
{
let mut summaries = fn_summaries.borrow_mut();
summaries.clear();
for (def_id, summary) in &self.fn_map {
summaries.insert(*def_id, summary.clone());
}
}
for def_id in reachable_vec.iter() {
if !self.fn_map.contains_key(def_id) {
continue;
}
let old_summary = self.fn_map.get(def_id).cloned().unwrap();
self.analyze_function(*def_id, &fn_summaries);
if let Some(new_summary) = self.fn_map.get(def_id) {
let old_aliases: std::collections::HashSet<_> =
old_summary.aliases().iter().cloned().collect();
let new_aliases: std::collections::HashSet<_> =
new_summary.aliases().iter().cloned().collect();
if old_aliases != new_aliases {
changed = true;
rap_trace!(
"Summary changed for {:?}: {} -> {}",
self.tcx.def_path_str(def_id),
old_summary.len(),
new_summary.len()
);
}
}
}
if !changed {
rap_trace!(
"Interprocedural analysis converged after {} iterations",
iteration
);
break;
}
if iteration >= MAX_ITERATIONS {
rap_warn!("Reached maximum iterations ({}), stopping", MAX_ITERATIONS);
break;
}
}
for (fn_id, fn_alias) in &mut self.fn_map {
let fn_name = self.tcx.def_path_str(fn_id);
fn_alias.sort_alias_index();
if fn_alias.len() > 0 {
rap_trace!("Alias found in {:?}: {}", fn_name, fn_alias);
}
}
}
fn reset(&mut self) {
self.fn_map.clear();
}
}
impl<'tcx> AliasAnalysis for MfpAliasAnalyzer<'tcx> {
fn get_fn_alias(&self, def_id: DefId) -> Option<FnAliasPairs> {
self.fn_map.get(&def_id).cloned()
}
fn get_all_fn_alias(&self) -> FnAliasMap {
self.fn_map.clone()
}
}