mirror of https://github.com/rust-lang/rust.git
Create stable metric to measure long computation in Const Eval
This patch adds a `MirPass` that tracks the number of back-edges and function calls in the CFG, adds a new MIR instruction to increment a counter every time they are encountered during Const Eval, and emit a warning if a configured limit is breached.
This commit is contained in:
parent
c8e6a9e8b6
commit
360db516cc
|
@ -393,6 +393,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
|
||||||
| mir::StatementKind::AscribeUserType(..)
|
| mir::StatementKind::AscribeUserType(..)
|
||||||
| mir::StatementKind::Coverage(..)
|
| mir::StatementKind::Coverage(..)
|
||||||
| mir::StatementKind::Intrinsic(..)
|
| mir::StatementKind::Intrinsic(..)
|
||||||
|
| mir::StatementKind::ConstEvalCounter
|
||||||
| mir::StatementKind::Nop => {}
|
| mir::StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
|
||||||
LocalMutationIsAllowed::Yes,
|
LocalMutationIsAllowed::Yes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
StatementKind::Nop
|
StatementKind::ConstEvalCounter
|
||||||
|
| StatementKind::Nop
|
||||||
| StatementKind::Retag { .. }
|
| StatementKind::Retag { .. }
|
||||||
| StatementKind::Deinit(..)
|
| StatementKind::Deinit(..)
|
||||||
| StatementKind::SetDiscriminant { .. } => {
|
| StatementKind::SetDiscriminant { .. } => {
|
||||||
|
|
|
@ -620,7 +620,8 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
|
||||||
flow_state,
|
flow_state,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
StatementKind::Nop
|
StatementKind::ConstEvalCounter
|
||||||
|
| StatementKind::Nop
|
||||||
| StatementKind::Retag { .. }
|
| StatementKind::Retag { .. }
|
||||||
| StatementKind::Deinit(..)
|
| StatementKind::Deinit(..)
|
||||||
| StatementKind::SetDiscriminant { .. } => {
|
| StatementKind::SetDiscriminant { .. } => {
|
||||||
|
|
|
@ -1258,6 +1258,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
| StatementKind::StorageDead(..)
|
| StatementKind::StorageDead(..)
|
||||||
| StatementKind::Retag { .. }
|
| StatementKind::Retag { .. }
|
||||||
| StatementKind::Coverage(..)
|
| StatementKind::Coverage(..)
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
| StatementKind::Nop => {}
|
| StatementKind::Nop => {}
|
||||||
StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => {
|
StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => {
|
||||||
bug!("Statement not allowed in this MIR phase")
|
bug!("Statement not allowed in this MIR phase")
|
||||||
|
|
|
@ -91,6 +91,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
mir::StatementKind::FakeRead(..)
|
mir::StatementKind::FakeRead(..)
|
||||||
| mir::StatementKind::Retag { .. }
|
| mir::StatementKind::Retag { .. }
|
||||||
| mir::StatementKind::AscribeUserType(..)
|
| mir::StatementKind::AscribeUserType(..)
|
||||||
|
| mir::StatementKind::ConstEvalCounter
|
||||||
| mir::StatementKind::Nop => {}
|
| mir::StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ use crate::interpret::{
|
||||||
RefTracking, StackPopCleanup,
|
RefTracking, StackPopCleanup,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
const NOTE_ON_UNDEFINED_BEHAVIOR_ERROR: &str = "The rules on what exactly is undefined behavior aren't clear, \
|
const NOTE_ON_UNDEFINED_BEHAVIOR_ERROR: &str = "The rules on what exactly is undefined behavior aren't clear, \
|
||||||
so this check might be overzealous. Please open an issue on the rustc \
|
so this check might be overzealous. Please open an issue on the rustc \
|
||||||
repository if you believe it should not be considered undefined behavior.";
|
repository if you believe it should not be considered undefined behavior.";
|
||||||
|
@ -33,6 +35,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|
||||||
body: &'mir mir::Body<'tcx>,
|
body: &'mir mir::Body<'tcx>,
|
||||||
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
|
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
|
||||||
debug!("eval_body_using_ecx: {:?}, {:?}", cid, ecx.param_env);
|
debug!("eval_body_using_ecx: {:?}, {:?}", cid, ecx.param_env);
|
||||||
|
info!("HERE body is {:#?}", body);
|
||||||
let tcx = *ecx.tcx;
|
let tcx = *ecx.tcx;
|
||||||
assert!(
|
assert!(
|
||||||
cid.promoted.is_some()
|
cid.promoted.is_some()
|
||||||
|
|
|
@ -369,6 +369,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(ecx), ret)]
|
||||||
fn load_mir(
|
fn load_mir(
|
||||||
ecx: &InterpCx<'mir, 'tcx, Self>,
|
ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||||
instance: ty::InstanceDef<'tcx>,
|
instance: ty::InstanceDef<'tcx>,
|
||||||
|
|
|
@ -46,6 +46,9 @@ pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
|
||||||
|
|
||||||
/// The recursion limit (cached from `tcx.recursion_limit(())`)
|
/// The recursion limit (cached from `tcx.recursion_limit(())`)
|
||||||
pub recursion_limit: Limit,
|
pub recursion_limit: Limit,
|
||||||
|
|
||||||
|
pub const_eval_limit: u32,
|
||||||
|
pub const_eval_counter: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread
|
// The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread
|
||||||
|
@ -408,6 +411,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
param_env,
|
param_env,
|
||||||
memory: Memory::new(),
|
memory: Memory::new(),
|
||||||
recursion_limit: tcx.recursion_limit(),
|
recursion_limit: tcx.recursion_limit(),
|
||||||
|
const_eval_limit: 20,
|
||||||
|
const_eval_counter: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -293,6 +293,17 @@ where
|
||||||
Prov: Provenance + 'static,
|
Prov: Provenance + 'static,
|
||||||
M: Machine<'mir, 'tcx, Provenance = Prov>,
|
M: Machine<'mir, 'tcx, Provenance = Prov>,
|
||||||
{
|
{
|
||||||
|
pub fn increment_const_eval_counter(&mut self) {
|
||||||
|
self.const_eval_counter = self.const_eval_counter + 1;
|
||||||
|
if self.const_eval_counter == self.const_eval_limit {
|
||||||
|
let mut warn = self.tcx.sess.struct_warn(format!(
|
||||||
|
"Const eval counter limit ({}) has been crossed",
|
||||||
|
self.const_eval_limit
|
||||||
|
));
|
||||||
|
warn.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Take a value, which represents a (thin or wide) reference, and make it a place.
|
/// Take a value, which represents a (thin or wide) reference, and make it a place.
|
||||||
/// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
|
/// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
|
||||||
///
|
///
|
||||||
|
|
|
@ -129,6 +129,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
// FIXME(#73156): Handle source code coverage in const eval
|
// FIXME(#73156): Handle source code coverage in const eval
|
||||||
Coverage(..) => {}
|
Coverage(..) => {}
|
||||||
|
|
||||||
|
// FIXME(bryangarza): Update this to do some logic!!!
|
||||||
|
ConstEvalCounter => {
|
||||||
|
self.increment_const_eval_counter();
|
||||||
|
}
|
||||||
|
|
||||||
// Defined to do nothing. These are added by optimization passes, to avoid changing the
|
// Defined to do nothing. These are added by optimization passes, to avoid changing the
|
||||||
// size of MIR constantly.
|
// size of MIR constantly.
|
||||||
Nop => {}
|
Nop => {}
|
||||||
|
|
|
@ -693,6 +693,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||||
| StatementKind::AscribeUserType(..)
|
| StatementKind::AscribeUserType(..)
|
||||||
| StatementKind::Coverage(..)
|
| StatementKind::Coverage(..)
|
||||||
| StatementKind::Intrinsic(..)
|
| StatementKind::Intrinsic(..)
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
| StatementKind::Nop => {}
|
| StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -766,6 +766,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
StatementKind::StorageLive(..)
|
StatementKind::StorageLive(..)
|
||||||
| StatementKind::StorageDead(..)
|
| StatementKind::StorageDead(..)
|
||||||
| StatementKind::Coverage(_)
|
| StatementKind::Coverage(_)
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
| StatementKind::Nop => {}
|
| StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1461,6 +1461,7 @@ impl Debug for Statement<'_> {
|
||||||
}
|
}
|
||||||
Coverage(box ref coverage) => write!(fmt, "Coverage::{:?}", coverage.kind),
|
Coverage(box ref coverage) => write!(fmt, "Coverage::{:?}", coverage.kind),
|
||||||
Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
|
Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
|
||||||
|
ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
|
||||||
Nop => write!(fmt, "nop"),
|
Nop => write!(fmt, "nop"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -441,10 +441,14 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn mir_for_ctfe_opt_const_arg(self, def: ty::WithOptConstParam<DefId>) -> &'tcx Body<'tcx> {
|
pub fn mir_for_ctfe_opt_const_arg(self, def: ty::WithOptConstParam<DefId>) -> &'tcx Body<'tcx> {
|
||||||
if let Some((did, param_did)) = def.as_const_arg() {
|
let res = if let Some((did, param_did)) = def.as_const_arg() {
|
||||||
|
info!("calling mir_for_ctfe_of_const_arg for DedId {did:?}");
|
||||||
self.mir_for_ctfe_of_const_arg((did, param_did))
|
self.mir_for_ctfe_of_const_arg((did, param_did))
|
||||||
} else {
|
} else {
|
||||||
|
info!("calling mir_for_ctfe for DefId {:?}", def.did);
|
||||||
self.mir_for_ctfe(def.did)
|
self.mir_for_ctfe(def.did)
|
||||||
}
|
};
|
||||||
|
//info!("RES OF CALLING MIR_FOR_CTFE_OPT_CONST_ARG: {:#?}", res);
|
||||||
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,6 +250,7 @@ pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str {
|
||||||
AscribeUserType(..) => "AscribeUserType",
|
AscribeUserType(..) => "AscribeUserType",
|
||||||
Coverage(..) => "Coverage",
|
Coverage(..) => "Coverage",
|
||||||
Intrinsic(..) => "Intrinsic",
|
Intrinsic(..) => "Intrinsic",
|
||||||
|
ConstEvalCounter => "ConstEvalCounter",
|
||||||
Nop => "Nop",
|
Nop => "Nop",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -286,7 +286,10 @@ pub enum StatementKind<'tcx> {
|
||||||
/// This is permitted for both generators and ADTs. This does not necessarily write to the
|
/// This is permitted for both generators and ADTs. This does not necessarily write to the
|
||||||
/// entire place; instead, it writes to the minimum set of bytes as required by the layout for
|
/// entire place; instead, it writes to the minimum set of bytes as required by the layout for
|
||||||
/// the type.
|
/// the type.
|
||||||
SetDiscriminant { place: Box<Place<'tcx>>, variant_index: VariantIdx },
|
SetDiscriminant {
|
||||||
|
place: Box<Place<'tcx>>,
|
||||||
|
variant_index: VariantIdx,
|
||||||
|
},
|
||||||
|
|
||||||
/// Deinitializes the place.
|
/// Deinitializes the place.
|
||||||
///
|
///
|
||||||
|
@ -355,6 +358,8 @@ pub enum StatementKind<'tcx> {
|
||||||
/// This avoids adding a new block and a terminator for simple intrinsics.
|
/// This avoids adding a new block and a terminator for simple intrinsics.
|
||||||
Intrinsic(Box<NonDivergingIntrinsic<'tcx>>),
|
Intrinsic(Box<NonDivergingIntrinsic<'tcx>>),
|
||||||
|
|
||||||
|
ConstEvalCounter,
|
||||||
|
|
||||||
/// No-op. Useful for deleting instructions without affecting statement indices.
|
/// No-op. Useful for deleting instructions without affecting statement indices.
|
||||||
Nop,
|
Nop,
|
||||||
}
|
}
|
||||||
|
|
|
@ -427,6 +427,7 @@ macro_rules! make_mir_visitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
StatementKind::ConstEvalCounter => {}
|
||||||
StatementKind::Nop => {}
|
StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,6 +271,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
|
||||||
| StatementKind::AscribeUserType(..)
|
| StatementKind::AscribeUserType(..)
|
||||||
| StatementKind::Coverage(..)
|
| StatementKind::Coverage(..)
|
||||||
| StatementKind::Intrinsic(..)
|
| StatementKind::Intrinsic(..)
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
| StatementKind::Nop => None,
|
| StatementKind::Nop => None,
|
||||||
};
|
};
|
||||||
if let Some(destination) = destination {
|
if let Some(destination) = destination {
|
||||||
|
|
|
@ -141,6 +141,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
|
||||||
StatementKind::AscribeUserType(..)
|
StatementKind::AscribeUserType(..)
|
||||||
| StatementKind::Coverage(..)
|
| StatementKind::Coverage(..)
|
||||||
| StatementKind::FakeRead(..)
|
| StatementKind::FakeRead(..)
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
| StatementKind::Nop
|
| StatementKind::Nop
|
||||||
| StatementKind::Retag(..)
|
| StatementKind::Retag(..)
|
||||||
| StatementKind::Intrinsic(..)
|
| StatementKind::Intrinsic(..)
|
||||||
|
|
|
@ -331,6 +331,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||||
| StatementKind::AscribeUserType(..)
|
| StatementKind::AscribeUserType(..)
|
||||||
| StatementKind::Coverage(..)
|
| StatementKind::Coverage(..)
|
||||||
| StatementKind::Intrinsic(..)
|
| StatementKind::Intrinsic(..)
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
| StatementKind::Nop => {}
|
| StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,8 @@ pub trait ValueAnalysis<'tcx> {
|
||||||
StatementKind::Retag(..) => {
|
StatementKind::Retag(..) => {
|
||||||
// We don't track references.
|
// We don't track references.
|
||||||
}
|
}
|
||||||
StatementKind::Nop
|
StatementKind::ConstEvalCounter
|
||||||
|
| StatementKind::Nop
|
||||||
| StatementKind::FakeRead(..)
|
| StatementKind::FakeRead(..)
|
||||||
| StatementKind::Coverage(..)
|
| StatementKind::Coverage(..)
|
||||||
| StatementKind::AscribeUserType(..) => (),
|
| StatementKind::AscribeUserType(..) => (),
|
||||||
|
|
|
@ -104,6 +104,7 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
|
||||||
| StatementKind::AscribeUserType(..)
|
| StatementKind::AscribeUserType(..)
|
||||||
| StatementKind::Coverage(..)
|
| StatementKind::Coverage(..)
|
||||||
| StatementKind::Intrinsic(..)
|
| StatementKind::Intrinsic(..)
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
| StatementKind::Nop => {
|
| StatementKind::Nop => {
|
||||||
// safe (at least as emitted during MIR construction)
|
// safe (at least as emitted during MIR construction)
|
||||||
}
|
}
|
||||||
|
|
|
@ -802,6 +802,8 @@ pub(super) fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span>
|
||||||
| StatementKind::StorageDead(_)
|
| StatementKind::StorageDead(_)
|
||||||
// Coverage should not be encountered, but don't inject coverage coverage
|
// Coverage should not be encountered, but don't inject coverage coverage
|
||||||
| StatementKind::Coverage(_)
|
| StatementKind::Coverage(_)
|
||||||
|
// Ignore `ConstEvalCounter`s
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
// Ignore `Nop`s
|
// Ignore `Nop`s
|
||||||
| StatementKind::Nop => None,
|
| StatementKind::Nop => None,
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
use crate::MirPass;
|
||||||
|
|
||||||
|
use rustc_middle::mir::{BasicBlock, Body, Statement, StatementKind, TerminatorKind};
|
||||||
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
|
||||||
|
use tracing::{info, instrument};
|
||||||
|
|
||||||
|
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();
|
||||||
|
//info!("Got body with {} basic blocks: {:#?}", body.basic_blocks.len(), body.basic_blocks);
|
||||||
|
//info!("With doms: {doms:?}");
|
||||||
|
|
||||||
|
/*
|
||||||
|
for (index, basic_block) in body.basic_blocks.iter().enumerate() {
|
||||||
|
info!("bb{index}: {basic_block:#?}")
|
||||||
|
}*/
|
||||||
|
for (index, basic_block) in body.basic_blocks.iter().enumerate() {
|
||||||
|
info!(
|
||||||
|
"bb{index} -> successors = {:?}",
|
||||||
|
basic_block.terminator().successors().collect::<Vec<BasicBlock>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (index, basic_block) in body.basic_blocks.iter().enumerate() {
|
||||||
|
info!("bb{index} -> unwind = {:?}", basic_block.terminator().unwind())
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dominators = Vec::new();
|
||||||
|
for idom in 0..body.basic_blocks.len() {
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
for inode in 0..body.basic_blocks.len() {
|
||||||
|
let dom = BasicBlock::from_usize(idom);
|
||||||
|
let node = BasicBlock::from_usize(inode);
|
||||||
|
if doms.is_reachable(dom)
|
||||||
|
&& doms.is_reachable(node)
|
||||||
|
&& doms.is_dominated_by(node, dom)
|
||||||
|
{
|
||||||
|
//info!("{idom} dominates {inode}");
|
||||||
|
nodes.push(true);
|
||||||
|
} else {
|
||||||
|
nodes.push(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dominators.push(nodes);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
for idom in 0..body.basic_blocks.len() {
|
||||||
|
print!("{idom} | dom | ");
|
||||||
|
for inode in 0..body.basic_blocks.len() {
|
||||||
|
if dominators[idom][inode] {
|
||||||
|
print!("{inode} | ");
|
||||||
|
} else {
|
||||||
|
print!(" | ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print!("\n");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (index, basic_block) in body.basic_blocks_mut().iter_mut().enumerate() {
|
||||||
|
// info!("bb{index}: {basic_block:#?}");
|
||||||
|
//info!("bb{index} -> successors = {:?}", basic_block.terminator().successors().collect::<Vec<BasicBlock>>());
|
||||||
|
let is_back_edge_or_fn_call = 'label: {
|
||||||
|
match basic_block.terminator().kind {
|
||||||
|
TerminatorKind::Call { .. } => {
|
||||||
|
break 'label true;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
for successor in basic_block.terminator().successors() {
|
||||||
|
let s_index = successor.as_usize();
|
||||||
|
if dominators[s_index][index] {
|
||||||
|
info!("{s_index} to {index} is a loop");
|
||||||
|
break 'label true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
};
|
||||||
|
if is_back_edge_or_fn_call {
|
||||||
|
basic_block.statements.push(Statement {
|
||||||
|
source_info: basic_block.terminator().source_info,
|
||||||
|
kind: StatementKind::ConstEvalCounter,
|
||||||
|
});
|
||||||
|
info!("New basic block statements vector: {:?}", basic_block.statements);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!("With doms: {doms:?}");
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,6 +53,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS
|
||||||
| StatementKind::StorageDead(_)
|
| StatementKind::StorageDead(_)
|
||||||
| StatementKind::Coverage(_)
|
| StatementKind::Coverage(_)
|
||||||
| StatementKind::Intrinsic(_)
|
| StatementKind::Intrinsic(_)
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
| StatementKind::Nop => (),
|
| StatementKind::Nop => (),
|
||||||
|
|
||||||
StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => {
|
StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => {
|
||||||
|
|
|
@ -577,6 +577,7 @@ impl WriteInfo {
|
||||||
self.add_place(**place);
|
self.add_place(**place);
|
||||||
}
|
}
|
||||||
StatementKind::Intrinsic(_)
|
StatementKind::Intrinsic(_)
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
| StatementKind::Nop
|
| StatementKind::Nop
|
||||||
| StatementKind::Coverage(_)
|
| StatementKind::Coverage(_)
|
||||||
| StatementKind::StorageLive(_)
|
| StatementKind::StorageLive(_)
|
||||||
|
|
|
@ -1583,6 +1583,7 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
|
||||||
| StatementKind::AscribeUserType(..)
|
| StatementKind::AscribeUserType(..)
|
||||||
| StatementKind::Coverage(..)
|
| StatementKind::Coverage(..)
|
||||||
| StatementKind::Intrinsic(..)
|
| StatementKind::Intrinsic(..)
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
| StatementKind::Nop => {}
|
| StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ mod const_goto;
|
||||||
mod const_prop;
|
mod const_prop;
|
||||||
mod const_prop_lint;
|
mod const_prop_lint;
|
||||||
mod coverage;
|
mod coverage;
|
||||||
|
mod ctfe_limit;
|
||||||
mod dataflow_const_prop;
|
mod dataflow_const_prop;
|
||||||
mod dead_store_elimination;
|
mod dead_store_elimination;
|
||||||
mod deaggregator;
|
mod deaggregator;
|
||||||
|
@ -349,11 +350,14 @@ fn mir_promoted(
|
||||||
/// Compute the MIR that is used during CTFE (and thus has no optimizations run on it)
|
/// Compute the MIR that is used during CTFE (and thus has no optimizations run on it)
|
||||||
fn mir_for_ctfe(tcx: TyCtxt<'_>, def_id: DefId) -> &Body<'_> {
|
fn mir_for_ctfe(tcx: TyCtxt<'_>, def_id: DefId) -> &Body<'_> {
|
||||||
let did = def_id.expect_local();
|
let did = def_id.expect_local();
|
||||||
if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) {
|
let body = if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) {
|
||||||
tcx.mir_for_ctfe_of_const_arg(def)
|
tcx.mir_for_ctfe_of_const_arg(def)
|
||||||
} else {
|
} else {
|
||||||
tcx.arena.alloc(inner_mir_for_ctfe(tcx, ty::WithOptConstParam::unknown(did)))
|
tcx.arena.alloc(inner_mir_for_ctfe(tcx, ty::WithOptConstParam::unknown(did)))
|
||||||
}
|
};
|
||||||
|
//info!("MIR_FOR_CTFE (DefId = {def_id:?}) body res: {:#?}", body);
|
||||||
|
info!("MIR_FOR_CTFE (DefId = {def_id:?})");
|
||||||
|
body
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `mir_for_ctfe`, but used to get the MIR of a const generic parameter.
|
/// Same as `mir_for_ctfe`, but used to get the MIR of a const generic parameter.
|
||||||
|
@ -447,6 +451,7 @@ fn mir_drops_elaborated_and_const_checked(
|
||||||
|
|
||||||
run_analysis_to_runtime_passes(tcx, &mut body);
|
run_analysis_to_runtime_passes(tcx, &mut body);
|
||||||
|
|
||||||
|
//info!("MIR after runtime passes: {:#?}", body);
|
||||||
tcx.alloc_steal_mir(body)
|
tcx.alloc_steal_mir(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,6 +522,7 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
// CTFE support for aggregates.
|
// CTFE support for aggregates.
|
||||||
&deaggregator::Deaggregator,
|
&deaggregator::Deaggregator,
|
||||||
&Lint(const_prop_lint::ConstProp),
|
&Lint(const_prop_lint::ConstProp),
|
||||||
|
&ctfe_limit::CtfeLimit,
|
||||||
];
|
];
|
||||||
pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial)));
|
pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial)));
|
||||||
}
|
}
|
||||||
|
@ -617,6 +623,7 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
|
||||||
let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst);
|
let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst);
|
||||||
debug!("body: {:#?}", body);
|
debug!("body: {:#?}", body);
|
||||||
run_optimization_passes(tcx, &mut body);
|
run_optimization_passes(tcx, &mut body);
|
||||||
|
//info!("body after OPTIMIZATION: {:#?}", body);
|
||||||
|
|
||||||
debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR");
|
debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR");
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ impl RemoveNoopLandingPads {
|
||||||
| StatementKind::StorageDead(_)
|
| StatementKind::StorageDead(_)
|
||||||
| StatementKind::AscribeUserType(..)
|
| StatementKind::AscribeUserType(..)
|
||||||
| StatementKind::Coverage(..)
|
| StatementKind::Coverage(..)
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
| StatementKind::Nop => {
|
| StatementKind::Nop => {
|
||||||
// These are all noops in a landing pad
|
// These are all noops in a landing pad
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,6 +250,7 @@ fn is_likely_const<'tcx>(mut tracked_place: Place<'tcx>, block: &BasicBlockData<
|
||||||
| StatementKind::Coverage(_)
|
| StatementKind::Coverage(_)
|
||||||
| StatementKind::StorageDead(_)
|
| StatementKind::StorageDead(_)
|
||||||
| StatementKind::Intrinsic(_)
|
| StatementKind::Intrinsic(_)
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
| StatementKind::Nop => {}
|
| StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,6 +319,7 @@ fn find_determining_place<'tcx>(
|
||||||
| StatementKind::AscribeUserType(_, _)
|
| StatementKind::AscribeUserType(_, _)
|
||||||
| StatementKind::Coverage(_)
|
| StatementKind::Coverage(_)
|
||||||
| StatementKind::Intrinsic(_)
|
| StatementKind::Intrinsic(_)
|
||||||
|
| StatementKind::ConstEvalCounter
|
||||||
| StatementKind::Nop => {}
|
| StatementKind::Nop => {}
|
||||||
|
|
||||||
// If the discriminant is set, it is always set
|
// If the discriminant is set, it is always set
|
||||||
|
|
|
@ -517,7 +517,7 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
|
||||||
self.super_statement(statement, location);
|
self.super_statement(statement, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
StatementKind::Nop => {}
|
StatementKind::ConstEvalCounter | StatementKind::Nop => {}
|
||||||
|
|
||||||
StatementKind::StorageLive(_local) | StatementKind::StorageDead(_local) => {}
|
StatementKind::StorageLive(_local) | StatementKind::StorageDead(_local) => {}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
// check-pass
|
||||||
|
#![feature(const_for)]
|
||||||
|
|
||||||
|
const fn labelled_loop() -> u32 {
|
||||||
|
let mut n = 0;
|
||||||
|
'outer: loop {
|
||||||
|
'inner: loop {
|
||||||
|
n = n + 1;
|
||||||
|
if n > 5 && n <= 10 {
|
||||||
|
n = n + 1;
|
||||||
|
continue 'inner
|
||||||
|
}
|
||||||
|
if n > 30 {
|
||||||
|
break 'outer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n
|
||||||
|
}
|
||||||
|
|
||||||
|
const X: u32 = labelled_loop();
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("{X}");
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
warning: Const eval counter limit (20) has been crossed
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
const fn recurse(n: u32) -> u32 {
|
||||||
|
if n == 0 {
|
||||||
|
n
|
||||||
|
} else {
|
||||||
|
recurse(n - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const X: u32 = recurse(30);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("{X}");
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
warning: Const eval counter limit (20) has been crossed
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// check-pass
|
||||||
|
const fn simple_loop(n: u32) -> u32 {
|
||||||
|
let mut index = 0;
|
||||||
|
let mut res = 0;
|
||||||
|
while index < n {
|
||||||
|
res = res + index;
|
||||||
|
index = index + 1;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
const X: u32 = simple_loop(30);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("{X}");
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
warning: Const eval counter limit (20) has been crossed
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
Loading…
Reference in New Issue