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
37 changed files with 233 additions and 9 deletions
|
@ -22,6 +22,8 @@ use crate::interpret::{
|
|||
RefTracking, StackPopCleanup,
|
||||
};
|
||||
|
||||
use tracing::info;
|
||||
|
||||
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 \
|
||||
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>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
|
||||
debug!("eval_body_using_ecx: {:?}, {:?}", cid, ecx.param_env);
|
||||
info!("HERE body is {:#?}", body);
|
||||
let tcx = *ecx.tcx;
|
||||
assert!(
|
||||
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(
|
||||
ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
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(())`)
|
||||
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
|
||||
|
@ -408,6 +411,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
param_env,
|
||||
memory: Memory::new(),
|
||||
recursion_limit: tcx.recursion_limit(),
|
||||
const_eval_limit: 20,
|
||||
const_eval_counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -293,6 +293,17 @@ where
|
|||
Prov: Provenance + 'static,
|
||||
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.
|
||||
/// 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
|
||||
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
|
||||
// size of MIR constantly.
|
||||
Nop => {}
|
||||
|
|
|
@ -693,6 +693,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
| StatementKind::AscribeUserType(..)
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::Intrinsic(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::Nop => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -766,6 +766,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
StatementKind::StorageLive(..)
|
||||
| StatementKind::StorageDead(..)
|
||||
| StatementKind::Coverage(_)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::Nop => {}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue