From: Mark Date: Fri, 13 Oct 2023 05:16:35 +0000 (-0600) Subject: detect all cycles in roughly linear time and constant space (#2102) X-Git-Tag: remove~30 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=e4a677ceead710eecfa33ebdbf5098513c5cf5a8;p=scryer-prolog.git detect all cycles in roughly linear time and constant space (#2102) --- diff --git a/src/heap_iter.rs b/src/heap_iter.rs index ab3b962a..9dd30b7e 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -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, @@ -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); } diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 0d37dfec..8bb38e17 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -12,7 +12,11 @@ pub(crate) trait UnmarkPolicy { fn invert_marker(iter: &mut StacklessPreOrderHeapIter) 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, + ) -> 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, } 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, + ) -> 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 { - 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; } } diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 4ed05354..eef5f18c 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -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:: - (&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)