Skip to main content

rustc_mir_transform/
ctfe_limit.rs

1//! A pass that inserts the `ConstEvalCounter` instruction into any blocks that have a back edge
2//! (thus indicating there is a loop in the CFG), or whose terminator is a function call.
3
4use rustc_data_structures::graph::dominators::Dominators;
5use rustc_middle::mir::{
6    BasicBlock, BasicBlockData, Body, Statement, StatementKind, TerminatorKind,
7};
8use rustc_middle::ty::TyCtxt;
9use tracing::instrument;
10
11use crate::PassPolicy;
12
13pub(super) struct CtfeLimit;
14
15impl<'tcx> crate::MirPass<'tcx> for CtfeLimit {
16    #[instrument(skip(self, _tcx, body))]
17    fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
18        let doms = body.basic_blocks.dominators();
19        let indices: Vec<BasicBlock> = body
20            .basic_blocks
21            .iter_enumerated()
22            .filter_map(|(node, node_data)| {
23                if matches!(node_data.terminator().kind, TerminatorKind::Call { .. } | TerminatorKind::TailCall { .. })
24                    // Back edges in a CFG indicate loops
25                    || has_back_edge(doms, node, node_data)
26                {
27                    Some(node)
28                } else {
29                    None
30                }
31            })
32            .collect();
33
34        let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
35        for index in indices {
36            let bbdata = &mut basic_blocks[index];
37            let source_info = bbdata.terminator().source_info;
38            bbdata.statements.push(Statement::new(source_info, StatementKind::ConstEvalCounter));
39        }
40    }
41
42    fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy {
43        // This is part of CTFE diagnostics rather than an optimization.
44        PassPolicy::optional_non_optimization(true)
45    }
46}
47
48fn has_back_edge(
49    doms: &Dominators<BasicBlock>,
50    node: BasicBlock,
51    node_data: &BasicBlockData<'_>,
52) -> bool {
53    if !doms.is_reachable(node) {
54        return false;
55    }
56    // Check if any of the dominators of the node are also the node's successor.
57    node_data.terminator().successors().any(|succ| doms.dominates(succ, node))
58}