]> Repositorios git - scryer-prolog.git/commitdiff
detect all cycles in roughly linear time and constant space (#2102)
authorMark <[email protected]>
Fri, 13 Oct 2023 05:16:35 +0000 (23:16 -0600)
committerMark <[email protected]>
Sat, 14 Oct 2023 00:05:52 +0000 (18:05 -0600)
src/heap_iter.rs
src/machine/gc.rs
src/machine/machine_state_impl.rs

index ab3b962afd97c21e9f8a95ece454289df1a5a4ce..9dd30b7e21f50be1e849a9488428d64ca7beabbe 100644 (file)
@@ -1,4 +1,6 @@
-pub(crate) use crate::machine::gc::{CycleDetectorUMP, IteratorUMP, StacklessPreOrderHeapIter};
+#[cfg(test)]
+pub(crate) use crate::machine::gc::{IteratorUMP};
+pub(crate) use crate::machine::gc::{CycleDetectorUMP, StacklessPreOrderHeapIter};
 
 use crate::atom_table::*;
 use crate::machine::heap::*;
@@ -503,6 +505,7 @@ impl<'a, ElideLists: ListElisionPolicy> Iterator for StackfulPreOrderHeapIter<'a
     }
 }
 
+#[cfg(test)]
 #[inline(always)]
 pub(crate) fn stackless_preorder_iter(
     heap: &mut Vec<HeapCellValue>,
@@ -1265,7 +1268,6 @@ mod tests {
                 list_loc_as_cell!(1)
             );
 
-            assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1));
             assert_eq!(iter.next(), None);
         }
 
index 0d37dfec7e446bf4cadca56c39c7657481930edd..8bb38e178e42987f191377cd55ab995e2142f0e6 100644 (file)
@@ -12,7 +12,11 @@ pub(crate) trait UnmarkPolicy {
     fn invert_marker(iter: &mut StacklessPreOrderHeapIter<Self>) where Self: Sized;
     fn cycle_detected(&mut self) where Self: Sized;
     fn mark_phase(&self) -> bool;
-    fn report_list(&mut self, list_loc: usize);
+    fn list_head_cycle_detecting_backward(
+        iter: &mut StacklessPreOrderHeapIter<Self>,
+    ) -> bool where Self: Sized {
+        iter.backward()
+    }
 }
 
 pub(crate) struct IteratorUMP {
@@ -51,15 +55,11 @@ impl UnmarkPolicy for IteratorUMP {
     fn mark_phase(&self) -> bool {
         self.mark_phase
     }
-
-    #[inline(always)]
-    fn report_list(&mut self, _list_loc: usize) {}
 }
 
 pub(crate) struct CycleDetectorUMP {
     mark_phase: bool,
     cycle_detected: bool,
-    list_locs: Vec<usize>,
 }
 
 impl UnmarkPolicy for CycleDetectorUMP {
@@ -84,9 +84,14 @@ impl UnmarkPolicy for CycleDetectorUMP {
         self.mark_phase
     }
 
-    #[inline]
-    fn report_list(&mut self, list_loc: usize) {
-        self.list_locs.push(list_loc);
+    fn list_head_cycle_detecting_backward(
+        iter: &mut StacklessPreOrderHeapIter<Self>,
+    ) -> bool {
+        if !iter.iter_state.cycle_detected && iter.iter_state.mark_phase && iter.detect_list_cycle() {
+            iter.iter_state.cycle_detected = true;
+        }
+
+        iter.backward()
     }
 }
 
@@ -120,9 +125,6 @@ impl UnmarkPolicy for MarkerUMP {
 
     #[inline(always)]
     fn cycle_detected(&mut self) {}
-
-    #[inline(always)]
-    fn report_list(&mut self, _list_loc: usize) {}
 }
 
 #[derive(Debug)]
@@ -182,7 +184,6 @@ impl<'a> StacklessPreOrderHeapIter<'a, CycleDetectorUMP> {
             iter_state: CycleDetectorUMP {
                 mark_phase: true,
                 cycle_detected: false,
-                list_locs: vec![],
             },
         }
     }
@@ -192,13 +193,29 @@ impl<'a> StacklessPreOrderHeapIter<'a, CycleDetectorUMP> {
         self.iter_state.cycle_detected
     }
 
-    #[inline]
-    pub(crate) fn list_locs(mut self) -> Vec<usize> {
-        std::mem::replace(&mut self.iter_state.list_locs, vec![])
+    pub(crate) fn detect_list_cycle(&self) -> bool {
+        use crate::machine::system_calls::BrentAlgState;
+
+        let mut brent_alg_st = BrentAlgState::new(self.current);
+
+        while self.heap[brent_alg_st.hare].get_mark_bit() {
+            let temp = self.heap[brent_alg_st.hare].get_value() as usize;
+
+            if brent_alg_st.step(temp).is_some() || temp == self.current {
+                return true;
+            }
+
+            if temp == self.start {
+                break;
+            }
+        }
+
+        false
     }
 }
 
 impl<'a> StacklessPreOrderHeapIter<'a, IteratorUMP> {
+    #[cfg(test)]
     pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self {
         heap[start].set_forwarding_bit(true);
         let next = heap[start].get_value();
@@ -253,8 +270,13 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
                 match self.heap[self.current].get_tag() {
                     HeapCellValueTag::AttrVar => {
                         let next = self.next;
+                        let current = self.current;
 
                         if let Some(cell) = UMP::forward_attr_var(self) {
+                            if current as u64 != next && self.heap[next as usize].is_ref() {
+                                self.iter_state.cycle_detected();
+                            }
+
                             return Some(cell);
                         }
 
@@ -265,8 +287,13 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
                     }
                     HeapCellValueTag::Var => {
                         let next = self.next;
+                        let current = self.current;
 
                         if let Some(cell) = self.forward_var() {
+                            if current as u64 != next && self.heap[next as usize].is_ref() {
+                                self.iter_state.cycle_detected();
+                            }
+
                             return Some(cell);
                         }
 
@@ -314,7 +341,15 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
 
                         if self.heap[last_cell_loc].get_mark_bit() == self.iter_state.mark_phase() {
                             if self.heap[last_cell_loc-1].get_mark_bit() == self.iter_state.mark_phase() {
-                                self.iter_state.report_list(last_cell_loc - 1);
+                                // the conjunction leading here is a necessary but not sufficient
+                                // condition of the presence of a cycle at the list head.
+                                self.backward();
+
+                                if UMP::list_head_cycle_detecting_backward(self) {
+                                    return None;
+                                }
+
+                                continue;
                             }
                         }
 
@@ -331,12 +366,13 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
                         let cell = self.heap[h];
 
                         let last_cell_loc = h + 1;
-                        self.heap[last_cell_loc].set_forwarding_bit(true);
 
                         self.next = self.heap[last_cell_loc].get_value();
                         self.heap[last_cell_loc].set_value(self.current as u64);
                         self.current = last_cell_loc;
 
+                        self.heap[last_cell_loc].set_forwarding_bit(true);
+
                         return Some(cell);
                     }
                     HeapCellValueTag::PStrOffset => {
@@ -386,7 +422,11 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
                     }
                 }
             } else {
-                if self.backward() {
+                if self.heap[self.current].get_tag() == HeapCellValueTag::Lis {
+                    if UMP::list_head_cycle_detecting_backward(self) {
+                        return None;
+                    }
+                } else if self.backward() {
                     return None;
                 }
             }
index 4ed05354814a5a2b60e47f2d803777baac512413..eef5f18ca2972b8a827fcb16f4e43d3226426ad0 100644 (file)
@@ -1131,27 +1131,29 @@ impl MachineState {
 
     #[inline]
     pub fn is_cyclic_term(&mut self, value: HeapCellValue) -> bool {
-        if value.is_constant() {
+        let value = self.store(self.deref(value));
+
+        if value.is_constant() || value.is_stack_var() {
             return false;
         }
 
-        let mut iter = stackful_preorder_iter::<NonListElider>
-            (&mut self.heap, &mut self.stack, value);
+        let h = self.heap.len();
+        self.heap.push(value);
 
-        while let Some(value) = iter.next() {
-            if value.get_forwarding_bit() {
-                let value = unmark_cell_bits!(heap_bound_store(
-                    iter.heap,
-                    heap_bound_deref(iter.heap, value),
-                ));
+        let found_cycle = {
+            let mut iter = cycle_detecting_stackless_preorder_iter(&mut self.heap, h);
 
-                if value.is_compound(iter.heap) {
-                    return true;
+            while let Some(_) = iter.next() {
+                if iter.found_cycle() {
+                    break;
                 }
             }
-        }
 
-        false
+            iter.found_cycle()
+        };
+
+        self.heap.pop();
+        found_cycle
     }
 
     // arg(+N, +Term, ?Arg)