about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan MacKenzie <ecstaticmorse@gmail.com>2020-03-17 16:07:29 -0700
committerDylan MacKenzie <ecstaticmorse@gmail.com>2020-03-17 16:09:53 -0700
commit5b66f27ed34849a849bd93ce662bffeec2e56947 (patch)
tree6676513e215aa86b48345512048784e2ab442123
parent7ceebd98c6a15ae30e772eebb676b63ffa1a8122 (diff)
downloadrust-5b66f27ed34849a849bd93ce662bffeec2e56947.tar.gz
rust-5b66f27ed34849a849bd93ce662bffeec2e56947.zip
Use `const_eval_limit` instead of infinite loop detector
-rw-r--r--src/librustc/mir/interpret/error.rs12
-rw-r--r--src/librustc_mir/const_eval/machine.rs67
-rw-r--r--src/librustc_mir/interpret/memory.rs19
3 files changed, 25 insertions, 73 deletions
diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs
index 0b33408edf0..4b1fad5d0b3 100644
--- a/src/librustc/mir/interpret/error.rs
+++ b/src/librustc/mir/interpret/error.rs
@@ -565,8 +565,10 @@ impl fmt::Debug for UnsupportedOpInfo<'tcx> {
 pub enum ResourceExhaustionInfo {
     /// The stack grew too big.
     StackFrameLimitReached,
-    /// The program ran into an infinite loop.
-    InfiniteLoop,
+    /// The program ran for too long.
+    ///
+    /// The exact limit is set by the `const_eval_limit` attribute.
+    TimeLimitReached,
 }
 
 impl fmt::Debug for ResourceExhaustionInfo {
@@ -576,11 +578,7 @@ impl fmt::Debug for ResourceExhaustionInfo {
             StackFrameLimitReached => {
                 write!(f, "reached the configured maximum number of stack frames")
             }
-            InfiniteLoop => write!(
-                f,
-                "duplicate interpreter state observed here, const evaluation will never \
-                    terminate"
-            ),
+            TimeLimitReached => write!(f, "exceeded interpreter time limit"),
         }
     }
 }
diff --git a/src/librustc_mir/const_eval/machine.rs b/src/librustc_mir/const_eval/machine.rs
index bb661d3d2a3..22b01be299b 100644
--- a/src/librustc_mir/const_eval/machine.rs
+++ b/src/librustc_mir/const_eval/machine.rs
@@ -3,7 +3,6 @@ use rustc::ty::layout::HasTyCtxt;
 use rustc::ty::{self, Ty};
 use std::borrow::{Borrow, Cow};
 use std::collections::hash_map::Entry;
-use std::convert::TryFrom;
 use std::hash::Hash;
 
 use rustc_data_structures::fx::FxHashMap;
@@ -13,13 +12,13 @@ use rustc_span::source_map::Span;
 use rustc_span::symbol::Symbol;
 
 use crate::interpret::{
-    self, snapshot, AllocId, Allocation, GlobalId, ImmTy, InterpCx, InterpResult, Memory,
-    MemoryKind, OpTy, PlaceTy, Pointer, Scalar,
+    self, AllocId, Allocation, GlobalId, ImmTy, InterpCx, InterpResult, Memory, MemoryKind, OpTy,
+    PlaceTy, Pointer, Scalar,
 };
 
 use super::error::*;
 
-impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
+impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter> {
     /// Evaluate a const function where all arguments (if any) are zero-sized types.
     /// The evaluation is memoized thanks to the query system.
     ///
@@ -86,22 +85,13 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
     }
 }
 
-/// The number of steps between loop detector snapshots.
-/// Should be a power of two for performance reasons.
-const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
-
-// Extra machine state for CTFE, and the Machine instance
-pub struct CompileTimeInterpreter<'mir, 'tcx> {
-    /// When this value is negative, it indicates the number of interpreter
-    /// steps *until* the loop detector is enabled. When it is positive, it is
-    /// the number of steps after the detector has been enabled modulo the loop
-    /// detector period.
-    pub(super) steps_since_detector_enabled: isize,
-
-    pub(super) is_detector_enabled: bool,
-
-    /// Extra state to detect loops.
-    pub(super) loop_detector: snapshot::InfiniteLoopDetector<'mir, 'tcx>,
+/// Extra machine state for CTFE, and the Machine instance
+pub struct CompileTimeInterpreter {
+    /// For now, the number of terminators that can be evaluated before we throw a resource
+    /// exhuastion error.
+    ///
+    /// Setting this to `0` disables the limit and allows the interpreter to run forever.
+    pub steps_remaining: usize,
 }
 
 #[derive(Copy, Clone, Debug)]
@@ -110,16 +100,9 @@ pub struct MemoryExtra {
     pub(super) can_access_statics: bool,
 }
 
-impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
+impl CompileTimeInterpreter {
     pub(super) fn new(const_eval_limit: usize) -> Self {
-        let steps_until_detector_enabled =
-            isize::try_from(const_eval_limit).unwrap_or(std::isize::MAX);
-
-        CompileTimeInterpreter {
-            loop_detector: Default::default(),
-            steps_since_detector_enabled: -steps_until_detector_enabled,
-            is_detector_enabled: const_eval_limit != 0,
-        }
+        CompileTimeInterpreter { steps_remaining: const_eval_limit }
     }
 }
 
@@ -173,8 +156,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> {
     }
 }
 
-crate type CompileTimeEvalContext<'mir, 'tcx> =
-    InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>;
+crate type CompileTimeEvalContext<'mir, 'tcx> = InterpCx<'mir, 'tcx, CompileTimeInterpreter>;
 
 impl interpret::MayLeak for ! {
     #[inline(always)]
@@ -184,7 +166,7 @@ impl interpret::MayLeak for ! {
     }
 }
 
-impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 'tcx> {
+impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter {
     type MemoryKinds = !;
     type PointerTag = ();
     type ExtraFnVal = !;
@@ -346,26 +328,17 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
     }
 
     fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
-        if !ecx.machine.is_detector_enabled {
+        // The step limit has already been hit in a previous call to `before_terminator`.
+        if ecx.machine.steps_remaining == 0 {
             return Ok(());
         }
 
-        {
-            let steps = &mut ecx.machine.steps_since_detector_enabled;
-
-            *steps += 1;
-            if *steps < 0 {
-                return Ok(());
-            }
-
-            *steps %= DETECTOR_SNAPSHOT_PERIOD;
-            if *steps != 0 {
-                return Ok(());
-            }
+        ecx.machine.steps_remaining -= 1;
+        if ecx.machine.steps_remaining == 0 {
+            throw_exhaust!(TimeLimitReached)
         }
 
-        let span = ecx.frame().span;
-        ecx.machine.loop_detector.observe_and_analyze(*ecx.tcx, span, &ecx.memory, &ecx.stack[..])
+        Ok(())
     }
 
     #[inline(always)]
diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs
index 82a467c7ba9..03be71f6174 100644
--- a/src/librustc_mir/interpret/memory.rs
+++ b/src/librustc_mir/interpret/memory.rs
@@ -112,25 +112,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for Memory<'mir, 'tcx, M>
     }
 }
 
-// FIXME: Really we shouldn't clone memory, ever. Snapshot machinery should instead
-// carefully copy only the reachable parts.
-impl<'mir, 'tcx, M> Clone for Memory<'mir, 'tcx, M>
-where
-    M: Machine<'mir, 'tcx, PointerTag = (), AllocExtra = ()>,
-    M::MemoryExtra: Copy,
-    M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation)>,
-{
-    fn clone(&self) -> Self {
-        Memory {
-            alloc_map: self.alloc_map.clone(),
-            extra_fn_ptr_map: self.extra_fn_ptr_map.clone(),
-            dead_alloc_map: self.dead_alloc_map.clone(),
-            extra: self.extra,
-            tcx: self.tcx,
-        }
-    }
-}
-
 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
     pub fn new(tcx: TyCtxtAt<'tcx>, extra: M::MemoryExtra) -> Self {
         Memory {