Newer
Older
// © 2023, The RefinedRust Developers and Contributors
//
// This Source Code Form is subject to the terms of the BSD-3-clause License.
// If a copy of the BSD-3-clause license was not distributed with this
// file, You can obtain one at https://opensource.org/license/bsd-3-clause/.
use std::collections::{HashMap, HashSet};
use rr_rustc_interface::middle::mir::tcx::PlaceTy;
use rr_rustc_interface::middle::mir::{
BasicBlock, BasicBlockData, Body, Local, Place, Rvalue, StatementKind, TerminatorKind,
};
use rr_rustc_interface::middle::ty::{Ty, TyCtxt};
/// Analysis for determining which locals are the temporaries used as the result of a checked-op.
pub struct CheckedOpLocalAnalysis<'def, 'tcx> {
tcx: TyCtxt<'tcx>,
bb_queue: Vec<BasicBlock>,
visited_bbs: HashSet<BasicBlock>,
result: HashMap<Local, Ty<'tcx>>,
body: &'def Body<'tcx>,
}
impl<'def, 'tcx> CheckedOpLocalAnalysis<'def, 'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, body: &'def Body<'tcx>) -> Self {
Self {
tcx,
bb_queue: Vec::new(),
visited_bbs: HashSet::new(),
body,
result: HashMap::new(),
}
}
const fn is_checked_op(val: &Rvalue<'tcx>) -> bool {
matches!(*val, Rvalue::CheckedBinaryOp(_, _))
}
/// Get the type of a checked-op result.
/// We discard the second component of the tuple.
fn get_checked_op_result_type(ty: Ty<'tcx>) -> Ty<'tcx> {
let fields = ty.tuple_fields();
*fields.get(0).unwrap()
}
/// Get the type of a place expression.
fn get_type_of_place(&self, pl: &Place<'tcx>) -> PlaceTy<'tcx> {
pl.ty(&self.body.local_decls, self.tcx)
}
/// Get all successors of the basic block (ignoring unwinding).
fn successors_of_bb(bb: &BasicBlockData<'tcx>) -> Vec<BasicBlock> {
return v;
};
match &term.kind {
TerminatorKind::Assert { target, .. }
| TerminatorKind::Drop { target, .. }
| TerminatorKind::Goto { target }
| TerminatorKind::Call {
target: Some(target),
..
} => v.push(*target),
TerminatorKind::InlineAsm {
destination: Some(destination),
TerminatorKind::SwitchInt { targets, .. } => {
for target in targets.all_targets() {
v.push(*target);
}
},
TerminatorKind::Yield { resume, .. } => v.push(*resume),
}
pub fn analyze(&mut self) {
if self.body.basic_blocks.len() > 0 {
self.bb_queue.push(BasicBlock::from_u32(0));
}
while let Some(next_bb) = self.bb_queue.pop() {
if !self.visited_bbs.contains(&next_bb) {
self.visited_bbs.insert(next_bb);
self.visit_bb(next_bb);
}
}
}
pub fn results(self) -> HashMap<Local, Ty<'tcx>> {
self.result
}
fn visit_bb(&mut self, bb_idx: BasicBlock) {
let bb = self.body.basic_blocks.get(bb_idx).unwrap();
// check if a statement is an assignment of a checked op result
let StatementKind::Assign(b) = &stmt.kind else {
continue;
};
let (plc, val) = b.as_ref();
// if this is a checked op, be sure to remember it
// this should be a temporary
assert!(plc.projection.is_empty());
// just use the RHS ty
let ty = self.get_type_of_place(plc);
let ty = Self::get_checked_op_result_type(ty.ty);
self.bb_queue.append(&mut Self::successors_of_bb(bb));