pub mod fn_simulator;
pub mod interprocedural;
use rustc_hir::def_id::DefId;
use rustc_middle::{
mir::{Local, Operand},
ty::{Ty, TyCtxt, TyKind},
};
use super::slicer::ForgetReason;
use crate::helpers::mir_utils;
#[derive(Clone, Debug)]
pub struct CallDependencySummary {
pub callee: Option<DefId>,
pub name: String,
pub return_depends_on_args: Vec<usize>,
pub may_write_args: Vec<usize>,
pub unsupported: bool,
}
impl CallDependencySummary {
fn unknown(callee: Option<DefId>, name: String, arg_count: usize) -> Self {
Self {
callee,
name,
return_depends_on_args: (0..arg_count).collect(),
may_write_args: Vec::new(),
unsupported: true,
}
}
}
#[derive(Clone, Debug)]
pub struct CallEffectSummary {
pub callee: Option<DefId>,
pub name: String,
pub destination: Option<Local>,
pub effects: Vec<CallEffect>,
pub unsupported: bool,
}
impl CallEffectSummary {
fn unknown(callee: Option<DefId>, name: String, destination: Option<Local>) -> Self {
Self {
callee,
name,
destination,
effects: Vec::new(),
unsupported: true,
}
}
}
#[derive(Clone, Debug)]
pub enum CallEffect {
ReturnAliasArg { arg: usize },
ReturnPointerFromArg { arg: usize },
ReturnPointerAdd {
base_arg: usize,
offset_arg: usize,
stride: Option<u64>,
},
ReturnPointerSub {
base_arg: usize,
offset_arg: usize,
stride: Option<u64>,
},
ReturnNonZero,
ReturnAligned { align: u64, ty_name: String },
ReturnConst { value: u64, label: String },
ReadMemory { arg: usize },
WriteMemory { pointer_arg: usize },
ReturnLengthOfArg { arg: usize },
ReturnIsEmptyOfArg { arg: usize },
ReturnMin { lhs_arg: usize, rhs_arg: usize },
ReturnTupleFieldLength { field: usize, from_arg: usize },
OwnsInitMemory { arg: usize },
ChecksIndexBoundsDisjoint { indices_arg: usize, len_arg: usize },
ReturnBoundedRange { bounds_arg: usize },
ReturnLcmSplit { receiver_arg: usize },
ForgetArgFacts { arg: usize, reason: ForgetReason },
}
pub fn dependency_summary<'tcx>(
tcx: TyCtxt<'tcx>,
func: &Operand<'tcx>,
arg_count: usize,
) -> CallDependencySummary {
let callee = mir_utils::dep_callee_def_id(func);
let name = mir_utils::call_name(tcx, func);
if let Some(summary) = fn_simulator::lookup_dependency(callee, &name, arg_count) {
return summary;
}
if let Some(callee) = callee {
if name.contains("::intrinsics::")
|| name.starts_with("intrinsics::")
|| name.ends_with("::drop_in_place")
{
return CallDependencySummary::unknown(Some(callee), name, arg_count);
}
if let Some(must_write_args) = interprocedural::local_must_write_args(tcx, callee) {
if !must_write_args.is_empty() {
return CallDependencySummary {
callee: Some(callee),
name,
return_depends_on_args: Vec::new(),
may_write_args: must_write_args
.into_iter()
.filter(|index| *index < arg_count)
.collect(),
unsupported: false,
};
}
}
if let Some(return_deps) = interprocedural::local_return_dependencies(tcx, callee) {
return CallDependencySummary {
callee: Some(callee),
name,
return_depends_on_args: return_deps
.into_iter()
.filter(|index| *index < arg_count)
.collect(),
may_write_args: Vec::new(),
unsupported: false,
};
}
}
CallDependencySummary::unknown(callee, name, arg_count)
}
pub fn effect_summary<'tcx>(
tcx: TyCtxt<'tcx>,
caller: DefId,
func: &Operand<'tcx>,
destination: Local,
) -> CallEffectSummary {
let callee = mir_utils::dep_callee_def_id(func);
let name = mir_utils::call_name(tcx, func);
if let Some(summary) = fn_simulator::lookup_effect(tcx, caller, callee, &name, func, destination) {
return summary;
}
if let Some(callee) = callee {
if name.contains("::intrinsics::")
|| name.starts_with("intrinsics::")
|| name.ends_with("::drop_in_place")
{
return CallEffectSummary::unknown(Some(callee), name, Some(destination));
}
if let Some(must_write_args) = interprocedural::local_must_write_args(tcx, callee) {
let effects: Vec<_> = must_write_args
.into_iter()
.map(|arg| CallEffect::WriteMemory { pointer_arg: arg })
.collect();
if !effects.is_empty() {
return CallEffectSummary {
callee: Some(callee),
name,
destination: Some(destination),
effects,
unsupported: false,
};
}
}
if let Some(effect) = interprocedural::try_pointer_arith_wrapper_effect(tcx, callee, Some(destination)) {
return CallEffectSummary {
callee: Some(callee),
name,
destination: Some(destination),
effects: vec![effect],
unsupported: false,
};
}
if let Some((indices_arg, len_arg)) = interprocedural::detect_index_disjoint_validator(tcx, callee)
.or_else(|| interprocedural::named_index_disjoint_validator(&name))
{
return CallEffectSummary {
callee: Some(callee),
name,
destination: Some(destination),
effects: vec![CallEffect::ChecksIndexBoundsDisjoint {
indices_arg,
len_arg,
}],
unsupported: false,
};
}
if let Some(return_deps) = interprocedural::local_return_dependencies(tcx, callee) {
return CallEffectSummary {
callee: Some(callee),
name,
destination: Some(destination),
effects: return_deps
.into_iter()
.map(|arg| CallEffect::ReturnAliasArg { arg })
.collect(),
unsupported: false,
};
}
}
CallEffectSummary::unknown(callee, name, Some(destination))
}
pub fn call_args_preserve_layout<'tcx>(arg_tys: impl Iterator<Item = Ty<'tcx>>) -> bool {
arg_tys.map(|ty| ty_is_layout_safe_inner(ty, 0)).all(|safe| safe)
}
fn ty_is_layout_safe_inner(ty: Ty<'_>, depth: usize) -> bool {
if depth > 6 {
return false;
}
match ty.kind() {
TyKind::Bool
| TyKind::Char
| TyKind::Int(_)
| TyKind::Uint(_)
| TyKind::Float(_)
| TyKind::Str
| TyKind::Param(_)
| TyKind::Closure(..)
| TyKind::Never => true,
TyKind::Slice(inner) | TyKind::Array(inner, _) => {
ty_is_layout_safe_inner(*inner, depth + 1)
}
TyKind::Ref(_, _, rustc_middle::ty::Mutability::Not) => true,
TyKind::Ref(_, inner, rustc_middle::ty::Mutability::Mut) => {
ty_is_layout_safe_inner(*inner, depth + 1)
}
TyKind::Tuple(elems) => elems.iter().all(|e| ty_is_layout_safe_inner(e, depth + 1)),
_ => false,
}
}