rustc_mir_transform/
ctfe_limit.rs1use 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 || 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 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 node_data.terminator().successors().any(|succ| doms.dominates(succ, node))
58}