Skip to content
Snippets Groups Projects
checked_op_analysis.rs 3.94 KiB
Newer Older
Lennard Gäher's avatar
Lennard Gäher committed
// © 2023, The RefinedRust Developers and Contributors
Lennard Gäher's avatar
Lennard Gäher committed
//
// 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::{
Lennard Gäher's avatar
Lennard Gäher committed
    BasicBlock, BasicBlockData, Body, Local, Place, Rvalue, StatementKind, TerminatorKind,
};
use rr_rustc_interface::middle::ty::{Ty, TyCtxt};
Lennard Gäher's avatar
Lennard Gäher committed
/// 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>,
}

Lennard Gäher's avatar
Lennard Gäher committed
impl<'def, 'tcx> CheckedOpLocalAnalysis<'def, 'tcx> {
Lennard Gäher's avatar
Lennard Gäher committed
    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(_, _))
Lennard Gäher's avatar
Lennard Gäher committed
    }

    /// 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> {
Lennard Gäher's avatar
Lennard Gäher committed
        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> {
        let mut v = Vec::new();

        let Some(term) = &bb.terminator else {
            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),
            } => v.push(*destination),
            TerminatorKind::SwitchInt { targets, .. } => {
                for target in targets.all_targets() {
                    v.push(*target);
                }
            },

            TerminatorKind::Yield { resume, .. } => v.push(*resume),
Lennard Gäher's avatar
Lennard Gäher committed
    }

    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() {
Lennard Gäher's avatar
Lennard Gäher committed
            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();
Lennard Gäher's avatar
Lennard Gäher committed
        // check if a statement is an assignment of a checked op result
        for stmt in &bb.statements {
            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
            if Self::is_checked_op(val) {
                // 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.result.insert(plc.local, ty);
        self.bb_queue.append(&mut Self::successors_of_bb(bb));