59 lines
1.9 KiB
Rust
59 lines
1.9 KiB
Rust
//! A pass that inserts the `ConstEvalCounter` instruction into any blocks that have a back edge
|
|
//! (thus indicating there is a loop in the CFG), or whose terminator is a function call.
|
|
use crate::MirPass;
|
|
|
|
use rustc_data_structures::graph::dominators::Dominators;
|
|
use rustc_middle::mir::{
|
|
BasicBlock, BasicBlockData, Body, Statement, StatementKind, TerminatorKind,
|
|
};
|
|
use rustc_middle::ty::TyCtxt;
|
|
|
|
pub struct CtfeLimit;
|
|
|
|
impl<'tcx> MirPass<'tcx> for CtfeLimit {
|
|
#[instrument(skip(self, _tcx, body))]
|
|
fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
|
let doms = body.basic_blocks.dominators();
|
|
let indices: Vec<BasicBlock> = body
|
|
.basic_blocks
|
|
.iter_enumerated()
|
|
.filter_map(|(node, node_data)| {
|
|
if matches!(node_data.terminator().kind, TerminatorKind::Call { .. })
|
|
// Back edges in a CFG indicate loops
|
|
|| has_back_edge(&doms, node, &node_data)
|
|
{
|
|
Some(node)
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect();
|
|
for index in indices {
|
|
insert_counter(
|
|
body.basic_blocks_mut()
|
|
.get_mut(index)
|
|
.expect("basic_blocks index {index} should exist"),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn has_back_edge(
|
|
doms: &Dominators<BasicBlock>,
|
|
node: BasicBlock,
|
|
node_data: &BasicBlockData<'_>,
|
|
) -> bool {
|
|
if !doms.is_reachable(node) {
|
|
return false;
|
|
}
|
|
// Check if any of the dominators of the node are also the node's successor.
|
|
node_data.terminator().successors().any(|succ| doms.dominates(succ, node))
|
|
}
|
|
|
|
fn insert_counter(basic_block_data: &mut BasicBlockData<'_>) {
|
|
basic_block_data.statements.push(Statement {
|
|
source_info: basic_block_data.terminator().source_info,
|
|
kind: StatementKind::ConstEvalCounter,
|
|
});
|
|
}
|