move loop detector constants to the module that uses them; make lifetime order in ConstPropagator consistent with Memory
This commit is contained in:
parent
b1453dda0f
commit
c16336a014
3 changed files with 28 additions and 29 deletions
|
@ -34,6 +34,13 @@ use interpret::{self,
|
||||||
snapshot,
|
snapshot,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Number of steps until the detector even starts doing anything.
|
||||||
|
/// Also, a warning is shown to the user when this number is reached.
|
||||||
|
const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000;
|
||||||
|
/// The number of steps between loop detector snapshots.
|
||||||
|
/// Should be a power of two for performance reasons.
|
||||||
|
const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
|
||||||
|
|
||||||
pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
|
pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
|
||||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
instance: Instance<'tcx>,
|
instance: Instance<'tcx>,
|
||||||
|
@ -245,7 +252,7 @@ impl<'a, 'mir, 'tcx> CompileTimeInterpreter<'a, 'mir, 'tcx> {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
CompileTimeInterpreter {
|
CompileTimeInterpreter {
|
||||||
loop_detector: Default::default(),
|
loop_detector: Default::default(),
|
||||||
steps_since_detector_enabled: -snapshot::STEPS_UNTIL_DETECTOR_ENABLED,
|
steps_since_detector_enabled: -STEPS_UNTIL_DETECTOR_ENABLED,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,22 +356,15 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
*steps %= snapshot::DETECTOR_SNAPSHOT_PERIOD;
|
*steps %= DETECTOR_SNAPSHOT_PERIOD;
|
||||||
if *steps != 0 {
|
if *steps != 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ecx.machine.loop_detector.is_empty() {
|
|
||||||
// First run of the loop detector
|
|
||||||
|
|
||||||
// FIXME(#49980): make this warning a lint
|
|
||||||
ecx.tcx.sess.span_warn(ecx.frame().span,
|
|
||||||
"Constant evaluating a complex constant, this might take some time");
|
|
||||||
}
|
|
||||||
|
|
||||||
ecx.machine.loop_detector.observe_and_analyze(
|
ecx.machine.loop_detector.observe_and_analyze(
|
||||||
&ecx.tcx,
|
&ecx.tcx,
|
||||||
|
ecx.frame().span,
|
||||||
&ecx.memory,
|
&ecx.memory,
|
||||||
&ecx.stack[..],
|
&ecx.stack[..],
|
||||||
)
|
)
|
||||||
|
|
|
@ -28,13 +28,6 @@ use super::eval_context::{LocalValue, StackPopCleanup};
|
||||||
use super::{Frame, Memory, Operand, MemPlace, Place, Value};
|
use super::{Frame, Memory, Operand, MemPlace, Place, Value};
|
||||||
use const_eval::CompileTimeInterpreter;
|
use const_eval::CompileTimeInterpreter;
|
||||||
|
|
||||||
/// Number of steps until the detector even starts doing anything.
|
|
||||||
/// Also, a warning is shown to the user when this number is reached.
|
|
||||||
pub(crate) const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000;
|
|
||||||
/// The number of steps between loop detector snapshots.
|
|
||||||
/// Should be a power of two for performance reasons.
|
|
||||||
pub(crate) const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir> {
|
pub(crate) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir> {
|
||||||
/// The set of all `EvalSnapshot` *hashes* observed by this detector.
|
/// The set of all `EvalSnapshot` *hashes* observed by this detector.
|
||||||
|
@ -53,28 +46,31 @@ pub(crate) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir> {
|
||||||
|
|
||||||
impl<'a, 'mir, 'tcx> InfiniteLoopDetector<'a, 'mir, 'tcx>
|
impl<'a, 'mir, 'tcx> InfiniteLoopDetector<'a, 'mir, 'tcx>
|
||||||
{
|
{
|
||||||
/// Returns `true` if the loop detector has not yet observed a snapshot.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.hashes.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn observe_and_analyze<'b>(
|
pub fn observe_and_analyze<'b>(
|
||||||
&mut self,
|
&mut self,
|
||||||
tcx: &TyCtxt<'b, 'tcx, 'tcx>,
|
tcx: &TyCtxt<'b, 'tcx, 'tcx>,
|
||||||
|
span: Span,
|
||||||
memory: &Memory<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>,
|
memory: &Memory<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>,
|
||||||
stack: &[Frame<'mir, 'tcx>],
|
stack: &[Frame<'mir, 'tcx>],
|
||||||
) -> EvalResult<'tcx, ()> {
|
) -> EvalResult<'tcx, ()> {
|
||||||
|
// Compute stack's hash before copying anything
|
||||||
let mut hcx = tcx.get_stable_hashing_context();
|
let mut hcx = tcx.get_stable_hashing_context();
|
||||||
let mut hasher = StableHasher::<u64>::new();
|
let mut hasher = StableHasher::<u64>::new();
|
||||||
stack.hash_stable(&mut hcx, &mut hasher);
|
stack.hash_stable(&mut hcx, &mut hasher);
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
|
|
||||||
|
// Check if we know that hash already
|
||||||
|
if self.hashes.is_empty() {
|
||||||
|
// FIXME(#49980): make this warning a lint
|
||||||
|
tcx.sess.span_warn(span,
|
||||||
|
"Constant evaluating a complex constant, this might take some time");
|
||||||
|
}
|
||||||
if self.hashes.insert(hash) {
|
if self.hashes.insert(hash) {
|
||||||
// No collision
|
// No collision
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need to make a full copy. NOW things that to get really expensive.
|
||||||
info!("snapshotting the state of the interpreter");
|
info!("snapshotting the state of the interpreter");
|
||||||
|
|
||||||
if self.snapshots.insert(EvalSnapshot::new(memory, stack)) {
|
if self.snapshots.insert(EvalSnapshot::new(memory, stack)) {
|
||||||
|
@ -461,6 +457,9 @@ impl<'a, 'mir, 'tcx> Eq for EvalSnapshot<'a, 'mir, 'tcx>
|
||||||
impl<'a, 'mir, 'tcx> PartialEq for EvalSnapshot<'a, 'mir, 'tcx>
|
impl<'a, 'mir, 'tcx> PartialEq for EvalSnapshot<'a, 'mir, 'tcx>
|
||||||
{
|
{
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
// FIXME: This looks to be a *ridicolously expensive* comparison operation.
|
||||||
|
// Doesn't this make tons of copies? Either `snapshot` is very badly named,
|
||||||
|
// or it does!
|
||||||
self.snapshot() == other.snapshot()
|
self.snapshot() == other.snapshot()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,9 +68,9 @@ impl MirPass for ConstProp {
|
||||||
type Const<'tcx> = (OpTy<'tcx>, Span);
|
type Const<'tcx> = (OpTy<'tcx>, Span);
|
||||||
|
|
||||||
/// Finds optimization opportunities on the MIR.
|
/// Finds optimization opportunities on the MIR.
|
||||||
struct ConstPropagator<'b, 'a, 'tcx:'a+'b> {
|
struct ConstPropagator<'a, 'mir, 'tcx:'a+'mir> {
|
||||||
ecx: EvalContext<'a, 'b, 'tcx, CompileTimeInterpreter<'a, 'b, 'tcx>>,
|
ecx: EvalContext<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>,
|
||||||
mir: &'b Mir<'tcx>,
|
mir: &'mir Mir<'tcx>,
|
||||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
source: MirSource,
|
source: MirSource,
|
||||||
places: IndexVec<Local, Option<Const<'tcx>>>,
|
places: IndexVec<Local, Option<Const<'tcx>>>,
|
||||||
|
@ -101,12 +101,12 @@ impl<'a, 'b, 'tcx> HasTyCtxt<'tcx> for &'a ConstPropagator<'a, 'b, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
|
||||||
fn new(
|
fn new(
|
||||||
mir: &'b Mir<'tcx>,
|
mir: &'mir Mir<'tcx>,
|
||||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
source: MirSource,
|
source: MirSource,
|
||||||
) -> ConstPropagator<'b, 'a, 'tcx> {
|
) -> ConstPropagator<'a, 'mir, 'tcx> {
|
||||||
let param_env = tcx.param_env(source.def_id);
|
let param_env = tcx.param_env(source.def_id);
|
||||||
let substs = Substs::identity_for_item(tcx, source.def_id);
|
let substs = Substs::identity_for_item(tcx, source.def_id);
|
||||||
let instance = Instance::new(source.def_id, substs);
|
let instance = Instance::new(source.def_id, substs);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue