]> git.lizzy.rs Git - rust.git/commitdiff
Enable loop detector in step loop
authorDylan MacKenzie <ecstaticmorse@gmail.com>
Sun, 24 Jun 2018 19:46:23 +0000 (12:46 -0700)
committerDylan MacKenzie <ecstaticmorse@gmail.com>
Wed, 4 Jul 2018 21:36:07 +0000 (14:36 -0700)
The detector runs every `DETECTOR_SNAPSHOT_PERIOD` steps. Since the
number of steps can increase by more than 1 (I'd like to remove this),
the detector may fail if the step counter is incremented past the
scheduled detection point during the loop.

src/librustc_mir/interpret/eval_context.rs
src/librustc_mir/interpret/step.rs

index 10fd97365f97ed3f07f32404816b36f4307dc36e..3375b6edf1a1854afab301ed0d11d192052fdea7 100644 (file)
@@ -45,7 +45,7 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
 
     /// The number of terminators to be evaluated before enabling the infinite
     /// loop detector.
-    pub(crate) steps_until_detector_enabled: usize,
+    pub(crate) steps_until_detector_enabled: isize,
 
     pub(crate) loop_detector: InfiniteLoopDetector<'a, 'mir, 'tcx, M>,
 }
@@ -175,12 +175,17 @@ impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M>
     where M: Clone + Eq + Hash + Machine<'mir, 'tcx>,
           'tcx: 'a + 'mir,
 {
-    pub fn observe(
+    /// Returns `true` if the loop detector has not yet observed a snapshot.
+    pub fn is_empty(&self) -> bool {
+        self.bloom.is_empty()
+    }
+
+    pub fn observe_and_analyze(
         &mut self,
         machine: &M,
         stack: &Vec<Frame<'mir, 'tcx>>,
         memory: &Memory<'a, 'mir, 'tcx, M>,
-    ) -> EvalResult<'_, ()> {
+    ) -> EvalResult<'tcx, ()> {
         let snapshot = (machine, stack, memory);
 
         let mut fx = FxHasher::default();
@@ -286,7 +291,7 @@ fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout {
     }
 }
 
-const MAX_TERMINATORS: usize = 1_000_000;
+const MAX_TERMINATORS: isize = 1_000_000;
 
 impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
     pub fn new(
@@ -656,7 +661,7 @@ pub(super) fn eval_rvalue_into_place(
             }
 
             Aggregate(ref kind, ref operands) => {
-                self.inc_step_counter_and_detect_loops(operands.len());
+                self.inc_step_counter_and_detect_loops(operands.len())?;
 
                 let (dest, active_field_index) = match **kind {
                     mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => {
index dac7c0610c0ef2e503064086706f2b23f43bd030..b0c08304969f608363de02994170b6ed60e02c33 100644 (file)
 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
     where M: Clone + Eq + Hash,
 {
-    pub fn inc_step_counter_and_detect_loops(&mut self, n: usize) {
-        self.steps_until_detector_enabled
-            = self.steps_until_detector_enabled.saturating_sub(n);
+    /// Returns `true` if the loop detector should take a snapshot during the current step.
+    pub fn is_loop_detector_scheduled(&self) -> bool {
+        /// The number of steps between loop detector snapshots.
+        /// Should be a power of two for performance reasons.
+        const LOOP_SNAPSHOT_PERIOD: isize = 1 << 8;
+
+        let steps = self.steps_until_detector_enabled;
+        steps <= 0 && steps % LOOP_SNAPSHOT_PERIOD == 0
+    }
+
+    pub fn inc_step_counter_and_detect_loops(&mut self, n: usize) -> EvalResult<'tcx, ()> {
+        // TODO: Remove `as` cast
+        self.steps_until_detector_enabled =
+            self.steps_until_detector_enabled.saturating_sub(n as isize);
 
-        if self.steps_until_detector_enabled == 0 {
-            let _ = self.loop_detector.observe(&self.machine, &self.stack, &self.memory); // TODO: Handle error
+        if !self.is_loop_detector_scheduled() {
+            return Ok(());
+        }
+
+        if self.loop_detector.is_empty() {
+            // First run of the loop detector
 
             // FIXME(#49980): make this warning a lint
-            self.tcx.sess.span_warn(self.frame().span, "Constant evaluating a complex constant, this might take some time");
-            self.steps_until_detector_enabled = 1_000_000;
+            self.tcx.sess.span_warn(self.frame().span,
+                "Constant evaluating a complex constant, this might take some time");
         }
+
+        self.loop_detector.observe_and_analyze(&self.machine, &self.stack, &self.memory)
     }
 
     /// Returns true as long as there are more things to do.
@@ -44,7 +61,7 @@ pub fn step(&mut self) -> EvalResult<'tcx, bool> {
             return Ok(true);
         }
 
-        self.inc_step_counter_and_detect_loops(1);
+        self.inc_step_counter_and_detect_loops(1)?;
 
         let terminator = basic_block.terminator();
         assert_eq!(old_frames, self.cur_frame());