From 6c36d067d7e71f1937b821a5d977712cf4d0ccc0 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 4 Oct 2023 11:57:09 -0600 Subject: [PATCH 01/46] Revert "consider Str, PStrLoc in ElideLists of StackfulHeapIterator (#2075)" This reverts commit 1e60eeef3450cdb6817f8f626fb32be6018d83c2. --- src/heap_iter.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/heap_iter.rs b/src/heap_iter.rs index 0bfbd465..ffb8df90 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -236,17 +236,17 @@ impl<'a, ElideLists: ListElisionPolicy> StackfulPreOrderHeapIter<'a, ElideLists> let cell = self.read_cell(loc); read_heap_cell!(cell, - (HeapCellValueTag::Lis | - HeapCellValueTag::Str | - HeapCellValueTag::PStrLoc, vh) => { + (HeapCellValueTag::Lis, vh) => { let forward = if ElideLists::elide_lists() { true } else { cell.get_mark_bit() }; if forward && self.heap[vh].get_mark_bit() { self.read_cell_mut(loc).set_forwarding_bit(true); } } - (HeapCellValueTag::AttrVar | - HeapCellValueTag::Var, vh) => { + (HeapCellValueTag::Str | + HeapCellValueTag::AttrVar | + HeapCellValueTag::Var | + HeapCellValueTag::PStrLoc, vh) => { if self.heap[vh].get_mark_bit() { self.read_cell_mut(loc).set_forwarding_bit(true); } From 1bfdea75273d40c4b316619194be62cbea481b85 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 4 Oct 2023 13:22:21 -0600 Subject: [PATCH 02/46] rewrite ground_test, add tests for ground/1 (#2075) --- src/heap_iter.rs | 111 ++++++++++++++++++++++++++++-- src/machine/machine_state_impl.rs | 50 +------------- src/tests/ground.pl | 83 ++++++++++++++++++++++ tests/scryer/src_tests.rs | 9 +++ 4 files changed, 201 insertions(+), 52 deletions(-) create mode 100644 src/tests/ground.pl diff --git a/src/heap_iter.rs b/src/heap_iter.rs index ffb8df90..d3b62527 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -12,6 +12,112 @@ use modular_bitfield::prelude::*; use std::ops::Deref; use std::vec::Vec; +/* + * Unlike StackfulPreOrderHeapIter, this iterator not only marks + * cyclic terms for the sake of skipping them at the second visit but + * leaves them marked until it is dropped. This makes for, e.g., more + * efficient ground/1 and term_variables/2 definitions. + */ + +pub struct EagerStackfulPreOrderHeapIter<'a> { + iter_stack: Vec, + mark_stack: Vec, + heap: &'a mut Heap, +} + +impl<'a> Drop for EagerStackfulPreOrderHeapIter<'a> { + fn drop(&mut self) { + while let Some(h) = self.mark_stack.pop() { + self.heap[h].set_mark_bit(false); + } + } +} + +impl<'a> EagerStackfulPreOrderHeapIter<'a> { + pub fn new(heap: &'a mut Heap, value: HeapCellValue) -> Self { + Self { + iter_stack: vec![value], + mark_stack: vec![], + heap, + } + } + + fn follow(&mut self) -> Option { + while let Some(value) = self.iter_stack.pop() { + if value.get_mark_bit() { + continue; + } + + read_heap_cell!(value, + (HeapCellValueTag::Str, s) => { + if self.heap[s].get_mark_bit() { + continue; + } + + let arity = cell_as_atom_cell!(self.heap[s]).get_arity(); + + self.heap[s].set_mark_bit(true); + self.mark_stack.push(s); + + for idx in (s + 1 .. s + arity + 1).rev() { + self.iter_stack.push(self.heap[idx]); + } + } + (HeapCellValueTag::Lis, l) => { + self.iter_stack.push(self.heap[l+1]); + self.iter_stack.push(self.heap[l]); + + self.heap[l].set_mark_bit(true); + self.mark_stack.push(l); + + self.heap[l+1].set_mark_bit(true); + self.mark_stack.push(l+1); + } + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { + let var_value = self.heap[h]; + + if !(var_value.is_var() && var_value.get_value() as usize == h) { + self.iter_stack.push(self.heap[h]); + continue; + } + } + (HeapCellValueTag::PStrLoc, h) => { + let h = if self.heap[h].get_tag() == HeapCellValueTag::PStr { + h + } else { + debug_assert_eq!(self.heap[h].get_tag(), HeapCellValueTag::PStrOffset); + self.heap[h].get_value() as usize + }; + + if self.heap[h].get_mark_bit() { + continue; + } + + self.heap[h].set_mark_bit(true); + + self.iter_stack.push(self.heap[h+1]); + self.mark_stack.push(h); + } + _ => { + } + ); + + return Some(value); + } + + None + } +} + +impl<'a> Iterator for EagerStackfulPreOrderHeapIter<'a> { + type Item = HeapCellValue; + + #[inline] + fn next(&mut self) -> Option { + self.follow() + } +} + #[derive(BitfieldSpecifier, Clone, Copy, Debug, PartialEq, Eq)] #[bits = 2] enum IterStackLocTag { @@ -198,11 +304,6 @@ impl<'a, ElideLists> StackfulPreOrderHeapIter<'a, ElideLists> { None } - #[inline] - pub fn stack_len(&self) -> usize { - self.stack.len() - } - fn push_if_unmarked(&mut self, loc: IterStackLoc) { let cell = self.read_cell_mut(loc); diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 75c3deb2..4510fa5d 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -1621,56 +1621,12 @@ impl MachineState { // returns true on failure. pub fn ground_test(&mut self) -> bool { - use fxhash::FxBuildHasher; + let iter = EagerStackfulPreOrderHeapIter::new(&mut self.heap, self.registers[1]); - if self.registers[1].is_constant() { - return false; - } - - let value = self.store(self.deref(self.registers[1])); - - if value.is_stack_var() { - return true; - } - - let mut visited = IndexSet::with_hasher(FxBuildHasher::default()); - let mut iter = stackful_preorder_iter::(&mut self.heap, &mut self.stack, value); - let mut stack_len = 0; - - let is_var = |heap: &Heap, value: HeapCellValue| -> bool { - let value = unmark_cell_bits!(value); - - if value.is_var() { - let value = heap_bound_store(heap, heap_bound_deref(heap, value)); - - if value.is_var() { - return true; - } - } - - false - }; - - while let Some(value) = iter.next() { - if is_var(iter.heap, value) { + for term in iter { + if term.is_var() { return true; } - - if value.is_ref() { - if visited.contains(&value) { - while iter.stack_len() > stack_len { - if let Some(value) = iter.pop_stack() { - if is_var(iter.heap, value) { - return true; - } - } - } - } else { - visited.insert(value); - } - } - - stack_len = iter.stack_len(); } false diff --git a/src/tests/ground.pl b/src/tests/ground.pl new file mode 100644 index 00000000..09c398c0 --- /dev/null +++ b/src/tests/ground.pl @@ -0,0 +1,83 @@ +/**/ + +:- use_module(library(format)). +:- use_module(library(dcgs)). +:- use_module(library(lists)). +:- use_module(library(debug)). +:- use_module(library(atts)). + +:- attribute a/1. + +a(Var) :- put_atts(Var, +a(hello)). + +test("ground#239", ( + % double negate to avoid residual goal being printed + \+ \+ (a(X), var(X), \+ ground(X)) +)). + +test("ground#1411",( + G_0 = ( A=s(A) ), G_0, + ground(A), ground(G_0) +)). + +test("ground#2065",( + A = [B|_C], B = [A], \+ ground(B), \+ground([B]) +)). + +test("ground#2073",( + \+ ground(_-1+_-1), + \+ ground(1-1-_) +)). + +test("ground#2075",( + G_0 = (_,_,ground(_)), + G_0 = (D=[D|_],_=D*[],ground(D)), + \+ G_0, + _=_B*_,_D=_B*_A,_B=_B*_D,\+ ground(_B), + A=[A|B],B=A*B,ground(A) +)). + +main :- + findall(test(Name, Goal), test(Name, Goal), Tests), + run_tests(Tests, Failed), + show_failed(Failed), + halt. + +main_quiet :- + findall(test(Name, Goal), test(Name, Goal), Tests), + run_tests_quiet(Tests, Failed), + ( Failed = [] -> + format("All tests passed", []) + ; format("Some tests failed", []) + ), + halt. + +run_tests([], []). +run_tests([test(Name, Goal)|Tests], Failed) :- + format("Running test \"~s\"~n", [Name]), + ( call(Goal) -> + Failed = Failed1 + ; format("Failed test \"~s\"~n", [Name]), + Failed = [Name|Failed1] + ), + run_tests(Tests, Failed1). + +run_tests_quiet([], []). +run_tests_quiet([test(Name, Goal)|Tests], Failed) :- + ( call(Goal) -> + Failed = Failed1 + ; Failed = [Name|Failed1] + ), + run_tests_quiet(Tests, Failed1). + +portray_failed_([]) --> []. +portray_failed_([F|Fs]) --> + "\"", F, "\"", "\n", portray_failed_(Fs). + +portray_failed([]) --> []. +portray_failed([F|Fs]) --> + "\n", "Failed tests:", "\n", portray_failed_([F|Fs]). + +show_failed(Failed) :- + phrase(portray_failed(Failed), F), + format("~s", [F]). diff --git a/tests/scryer/src_tests.rs b/tests/scryer/src_tests.rs index 5572b75f..8a04abbe 100644 --- a/tests/scryer/src_tests.rs +++ b/tests/scryer/src_tests.rs @@ -84,3 +84,12 @@ fn dif_tests() { "All tests passed", ); } + +#[test] +fn ground_tests() { + run_top_level_test_with_args( + &["src/tests/ground.pl", "-f", "-g", "main_quiet"], + "", + "All tests passed", + ); +} From 0ad4427f83beb2f3bda59648a0a099ec75dd0e3b Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 4 Oct 2023 15:12:25 -0600 Subject: [PATCH 03/46] use eager_stackful_preorder_iter in variable_set, add term_variables/1 tests --- src/heap_iter.rs | 8 +++ src/machine/machine_state_impl.rs | 2 +- src/machine/system_calls.rs | 17 ++----- src/tests/term_variables.pl | 84 +++++++++++++++++++++++++++++++ tests/scryer/src_tests.rs | 9 ++++ 5 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 src/tests/term_variables.pl diff --git a/src/heap_iter.rs b/src/heap_iter.rs index d3b62527..bd583632 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -12,6 +12,14 @@ use modular_bitfield::prelude::*; use std::ops::Deref; use std::vec::Vec; +#[inline(always)] +pub fn eager_stackful_preorder_iter( + heap: &mut Heap, + value: HeapCellValue, +) -> EagerStackfulPreOrderHeapIter { + EagerStackfulPreOrderHeapIter::new(heap, value) +} + /* * Unlike StackfulPreOrderHeapIter, this iterator not only marks * cyclic terms for the sake of skipping them at the second visit but diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 4510fa5d..4ed05354 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -1621,7 +1621,7 @@ impl MachineState { // returns true on failure. pub fn ground_test(&mut self) -> bool { - let iter = EagerStackfulPreOrderHeapIter::new(&mut self.heap, self.registers[1]); + let iter = eager_stackful_preorder_iter(&mut self.heap, self.registers[1]); for term in iter { if term.is_var() { diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 40881c54..ba211683 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -583,20 +583,11 @@ impl MachineState { seen_set: &mut IndexSet, value: HeapCellValue, ) { - let mut iter = stackful_preorder_iter::(&mut self.heap, &mut self.stack, value); + let iter = eager_stackful_preorder_iter(&mut self.heap, value); - while let Some(value) = iter.next() { - let value = unmark_cell_bits!(value); - - if value.is_var() { - let value = unmark_cell_bits!(heap_bound_store( - iter.heap, - heap_bound_deref(iter.heap, value) - )); - - if value.is_var() { - seen_set.insert(value); - } + for term in iter { + if term.is_var() { + seen_set.insert(term); } } } diff --git a/src/tests/term_variables.pl b/src/tests/term_variables.pl new file mode 100644 index 00000000..57c55ed1 --- /dev/null +++ b/src/tests/term_variables.pl @@ -0,0 +1,84 @@ +/**/ + +:- use_module(library(format)). +:- use_module(library(dcgs)). +:- use_module(library(lists)). +:- use_module(library(debug)). + +test("term_variables#1400", ( + term_variables(A+B*C/B-D, Vars), + term_variables(t(A,B,C,D), Vars), + Vars = [A,B,C,D] +)). + +test("term_variables#1405", ( + \+ (B=[C|D],C=[_|D],C=[B|B], term_variables(B,_), false) +)). + +test("term_variables#1409", ( + G_0 = (A=[B|B],A=[C|C]), G_0, term_variables(G_0, Vars), Vars = [B] +)). + +test("term_variables#1410", ( + \+ \+ (G_0 = ( A=s(A) ), G_0, term_variables(G_0, Vars), Vars = []), + E_0 = (_=[B|B]), G_0 = (E_0,\_=B), G_0, term_variables(G_0, Vars) +)). + +test("term_variables#1412", ( + G_0 = =([A|B],[A|B]), G_0, term_variables(G_0, Vars), + Vars = [A,B] +)). + +test("term_variables#1414", ( + \+ (\B=A,C=[A|D],B=[a,b|E],C=[D|E], term_variables(\E,_), false) +)). + +test("term_variables#2063", ( + A=[B|C], B=[A], term_variables([B], Vars), + Vars = [C] +)). + +main :- + findall(test(Name, Goal), test(Name, Goal), Tests), + run_tests(Tests, Failed), + show_failed(Failed), + halt. + +main_quiet :- + findall(test(Name, Goal), test(Name, Goal), Tests), + run_tests_quiet(Tests, Failed), + ( Failed = [] -> + format("All tests passed", []) + ; format("Some tests failed", []) + ), + halt. + +run_tests([], []). +run_tests([test(Name, Goal)|Tests], Failed) :- + format("Running test \"~s\"~n", [Name]), + ( call(Goal) -> + Failed = Failed1 + ; format("Failed test \"~s\"~n", [Name]), + Failed = [Name|Failed1] + ), + run_tests(Tests, Failed1). + +run_tests_quiet([], []). +run_tests_quiet([test(Name, Goal)|Tests], Failed) :- + ( call(Goal) -> + Failed = Failed1 + ; Failed = [Name|Failed1] + ), + run_tests_quiet(Tests, Failed1). + +portray_failed_([]) --> []. +portray_failed_([F|Fs]) --> + "\"", F, "\"", "\n", portray_failed_(Fs). + +portray_failed([]) --> []. +portray_failed([F|Fs]) --> + "\n", "Failed tests:", "\n", portray_failed_([F|Fs]). + +show_failed(Failed) :- + phrase(portray_failed(Failed), F), + format("~s", [F]). diff --git a/tests/scryer/src_tests.rs b/tests/scryer/src_tests.rs index 8a04abbe..edc43d32 100644 --- a/tests/scryer/src_tests.rs +++ b/tests/scryer/src_tests.rs @@ -93,3 +93,12 @@ fn ground_tests() { "All tests passed", ); } + +#[test] +fn term_variables_tests() { + run_top_level_test_with_args( + &["src/tests/term_variables.pl", "-f", "-g", "main_quiet"], + "", + "All tests passed", + ); +} From fa68fa211cb230d5b3a46b68290e6860450c2249 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 5 Oct 2023 20:21:28 -0600 Subject: [PATCH 04/46] replace eager_stackful_iter's mark stack with a second unmark phase --- src/heap_iter.rs | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/heap_iter.rs b/src/heap_iter.rs index bd583632..e144ae3e 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -28,64 +28,62 @@ pub fn eager_stackful_preorder_iter( */ pub struct EagerStackfulPreOrderHeapIter<'a> { + start_value: HeapCellValue, iter_stack: Vec, - mark_stack: Vec, + mark_phase: bool, heap: &'a mut Heap, } impl<'a> Drop for EagerStackfulPreOrderHeapIter<'a> { fn drop(&mut self) { - while let Some(h) = self.mark_stack.pop() { - self.heap[h].set_mark_bit(false); - } + self.mark_phase = false; + + self.iter_stack.clear(); + self.start_value.set_mark_bit(true); + self.iter_stack.push(self.start_value); + + while let Some(_) = self.follow() {} } } impl<'a> EagerStackfulPreOrderHeapIter<'a> { pub fn new(heap: &'a mut Heap, value: HeapCellValue) -> Self { Self { + start_value: value, iter_stack: vec![value], - mark_stack: vec![], + mark_phase: true, heap, } } fn follow(&mut self) -> Option { while let Some(value) = self.iter_stack.pop() { - if value.get_mark_bit() { + if value.get_mark_bit() == self.mark_phase { continue; } read_heap_cell!(value, (HeapCellValueTag::Str, s) => { - if self.heap[s].get_mark_bit() { - continue; - } - let arity = cell_as_atom_cell!(self.heap[s]).get_arity(); - self.heap[s].set_mark_bit(true); - self.mark_stack.push(s); - for idx in (s + 1 .. s + arity + 1).rev() { self.iter_stack.push(self.heap[idx]); + self.heap[idx].set_mark_bit(self.mark_phase); } } (HeapCellValueTag::Lis, l) => { self.iter_stack.push(self.heap[l+1]); self.iter_stack.push(self.heap[l]); - self.heap[l].set_mark_bit(true); - self.mark_stack.push(l); - - self.heap[l+1].set_mark_bit(true); - self.mark_stack.push(l+1); + self.heap[l].set_mark_bit(self.mark_phase); + self.heap[l+1].set_mark_bit(self.mark_phase); } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { let var_value = self.heap[h]; if !(var_value.is_var() && var_value.get_value() as usize == h) { - self.iter_stack.push(self.heap[h]); + self.heap[h].set_mark_bit(self.mark_phase); + self.iter_stack.push(var_value); continue; } } @@ -97,14 +95,12 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> { self.heap[h].get_value() as usize }; - if self.heap[h].get_mark_bit() { + if self.heap[h].get_mark_bit() == self.mark_phase { continue; } - self.heap[h].set_mark_bit(true); - + self.heap[h].set_mark_bit(self.mark_phase); self.iter_stack.push(self.heap[h+1]); - self.mark_stack.push(h); } _ => { } From 7ed38d6c6cb23738e570590d1dd7acfca463a08a Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Fri, 6 Oct 2023 22:55:46 +0200 Subject: [PATCH 05/46] FIXED: reification of (/)/2 for undefined subexpressions This addresses #2078 and #2079. --- src/lib/clpz.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index f438043d..4a40e414 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3549,7 +3549,8 @@ parse_reified(E, R, D, m(max(A,B)) => [d(D), p(pgeq(R, A)), p(pgeq(R, B)), p(pmax(A,B,R)), a(A,B,R)], m(min(A,B)) => [d(D), p(pgeq(A, R)), p(pgeq(B, R)), p(pmin(A,B,R)), a(A,B,R)], m(abs(A)) => [g(#R#>=0), d(D), p(pabs(A, R)), a(A,R)], - m(A/B) => [p(preified_slash(A,B,D,R)), a(A,B,R)], + m(A/B) => [d(D1), p(preified_slash(A,B,D2,R)), + p(reified_and(D1,[],D2,[],D)),a(D2),a(A,B,R)], m(A//B) => [skeleton(A,B,D,R,ptzdiv)], m(A div B) => [skeleton(A,B,D,R,pdiv)], m(A mod B) => [skeleton(A,B,D,R,pmod)], From 3fc969b38b4359e1f4d36909554dc5ecfd0ef7b7 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Fri, 6 Oct 2023 22:58:56 +0200 Subject: [PATCH 06/46] reorder and realign entries to form a contiguous group starting with d(D) --- src/lib/clpz.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 4a40e414..b29847be 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3548,14 +3548,14 @@ parse_reified(E, R, D, m(-A) => [d(D), p(ptimes(-1,A,R)), a(R)], m(max(A,B)) => [d(D), p(pgeq(R, A)), p(pgeq(R, B)), p(pmax(A,B,R)), a(A,B,R)], m(min(A,B)) => [d(D), p(pgeq(A, R)), p(pgeq(B, R)), p(pmin(A,B,R)), a(A,B,R)], - m(abs(A)) => [g(#R#>=0), d(D), p(pabs(A, R)), a(A,R)], + m(abs(A)) => [d(D), g(#R#>=0), p(pabs(A, R)), a(A,R)], + m(A^B) => [d(D), p(pexp(A,B,R)), a(A,B,R)], m(A/B) => [d(D1), p(preified_slash(A,B,D2,R)), p(reified_and(D1,[],D2,[],D)),a(D2),a(A,B,R)], m(A//B) => [skeleton(A,B,D,R,ptzdiv)], m(A div B) => [skeleton(A,B,D,R,pdiv)], m(A mod B) => [skeleton(A,B,D,R,pmod)], m(A rem B) => [skeleton(A,B,D,R,prem)], - m(A^B) => [d(D), p(pexp(A,B,R)), a(A,B,R)], % bitwise operations m(\A) => [function(D,\,A,R)], m(msb(A)) => [g(#A#>0) ,function(D,msb,A,R)], From 1ab14ea5193597f6c43e25623bfb203acdc0bd6b Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 7 Oct 2023 18:48:49 -0600 Subject: [PATCH 07/46] mark both components of a PStrLoc (#2082) --- src/heap_iter.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/heap_iter.rs b/src/heap_iter.rs index e144ae3e..ef5e4ac5 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -80,9 +80,9 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> { } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { let var_value = self.heap[h]; + self.heap[h].set_mark_bit(self.mark_phase); if !(var_value.is_var() && var_value.get_value() as usize == h) { - self.heap[h].set_mark_bit(self.mark_phase); self.iter_stack.push(var_value); continue; } @@ -99,8 +99,12 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> { continue; } + let value = self.heap[h+1]; + self.heap[h].set_mark_bit(self.mark_phase); - self.iter_stack.push(self.heap[h+1]); + self.heap[h+1].set_mark_bit(self.mark_phase); + + self.iter_stack.push(value); } _ => { } From 51c00fce5762a28ac3bc3dc07b18611890fd4fd1 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 8 Oct 2023 09:26:22 +0200 Subject: [PATCH 08/46] adapt query to Scryer Prolog --- src/lib/clpz.pl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index b29847be..18475721 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3647,8 +3647,12 @@ reified_goal(l(L), _) --> [[L]]. parse_init_dcg([], _) --> []. parse_init_dcg([V|Vs], P) --> [{init_propagator(V, P)}], parse_init_dcg(Vs, P). -%?- set_prolog_flag(answer_write_options, [portray(true)]), -% clpz:parse_reified_clauses(Cs), maplist(portray_clause, Cs). +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +?- use_module(library(lists)), + use_module(library(format)), + clpz:parse_reified_clauses(Cs), + maplist(portray_clause, Cs). +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ reify(E, B) :- reify(E, B, _). From 4d910f6bfe346ac4e0af20644a6ab6d0bb99f6fd Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 8 Oct 2023 09:43:10 +0200 Subject: [PATCH 09/46] FIXED: variables in reified propagators must share the same queue Otherwise, propagation steps may be inadvertently omitted, if propagators are scheduled in a different queue. This addresses #2084. --- src/lib/clpz.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 18475721..7bace705 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3625,7 +3625,8 @@ reified_goal(g(Goal), _) --> [{Goal}]. reified_goal(p(Vs, Prop), _) --> [{make_propagator(Prop, P)}], parse_init_dcg(Vs, P), - [{trigger_once(P)}], + [{variables_same_queue(Vs), + trigger_once(P)}], [( { propagator_state(P, S), S == dead } -> [] ; [p(P)])]. reified_goal(p(Prop), Ds) --> { term_variables(Prop, Vs) }, From 8121dce2a46889ed0a74fe3dba068781e26b51ac Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 8 Oct 2023 09:43:59 +0200 Subject: [PATCH 10/46] instead of prdiv, use ptimes directly --- src/lib/clpz.pl | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 7bace705..023f3747 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -2572,7 +2572,7 @@ parse_clpz(E, R, m(A mod B) => [g(B #\= 0), p(pmod(A, B, R))], m(A rem B) => [g(B #\= 0), p(prem(A, B, R))], m(abs(A)) => [g(#R #>= 0), p(pabs(A, R))], - m(A/B) => [g(B #\= 0), p(prdiv(A, B, R))], + m(A/B) => [g(B #\= 0), p(ptimes(R, B, A))], m(A//B) => [g(B #\= 0), p(ptzdiv(A, B, R))], m(A div B) => [g(#R #= (A - (A mod B)) // B)], m(A^B) => [p(pexp(A, B, R))], @@ -4918,11 +4918,6 @@ run_propagator(ptimes(X,Y,Z), MState) --> run_propagator(pdiv(X,Y,Z), MState) --> { kill(MState), Z #= (X-(X mod Y)) // Y }. -% X rdiv Y = Z -run_propagator(prdiv(X,Y,Z), MState) --> - { kill(MState), Z*Y #= X }. - - % X // Y = Z (round towards zero) run_propagator(ptzdiv(X,Y,Z), MState) --> ( nonvar(X) -> @@ -7786,7 +7781,6 @@ attribute_goal_(x_neq_y_plus_z(X,Y,Z)) --> [#X #\= #Y + #Z]. attribute_goal_(x_leq_y_plus_c(X,Y,C)) --> [#X #=< #Y + C]. attribute_goal_(ptzdiv(X,Y,Z)) --> [#X // #Y #= #Z]. attribute_goal_(pdiv(X,Y,Z)) --> [#X div #Y #= #Z]. -attribute_goal_(prdiv(X,Y,Z)) --> [#X / #Y #= #Z]. attribute_goal_(pexp(X,Y,Z)) --> [#X ^ #Y #= #Z]. attribute_goal_(psign(X,Y)) --> [#Y #= sign(#X)]. attribute_goal_(pabs(X,Y)) --> [#Y #= abs(#X)]. From ff63eacf2cfcc53db81e96d7917a552748270bc4 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 8 Oct 2023 11:28:52 +0200 Subject: [PATCH 11/46] replace list//1 by seq//1 from library(dcgs) --- src/lib/clpz.pl | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 023f3747..a2d37014 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -1030,8 +1030,8 @@ term_expansion(Term0, Term) :- once(duodcg_body(Body0, Body, As0, As, Bs0, Bs)). duodcg_body([], (As0=As,Bs0=Bs), As0, As, Bs0, Bs). -duodcg_body(Xs+Ys, (phrase(list(Xs), As0, As), - phrase(list(Ys), Bs0, Bs)), As0, As, Bs0, Bs). +duodcg_body(Xs+Ys, (phrase(seq(Xs), As0, As), + phrase(seq(Ys), Bs0, Bs)), As0, As, Bs0, Bs). duodcg_body({Goal}, call(Goal), As, As, Bs, Bs). duodcg_body((A0,B0), (A,B), As0, As, Bs0, Bs) :- duodcg_body(A0, A, As0, As1, Bs0, Bs1), @@ -3698,7 +3698,7 @@ reify_(tuples_in(Tuples, Relation), B) --> #B #<==> And }, propagator_init_trigger([B], tuples_not_in(Tuples, Relation, B)), kill_reified_tuples(Bs, Ps, Bs), - list(Ps), + seq(Ps), as([B|Bs]). reify_(finite_domain(V), B) --> propagator_init_trigger(reified_fd(V,B)), @@ -3730,20 +3730,17 @@ arithmetic(L, R, B, Functor) --> { phrase((parse_reified_clpz(L, LR, LD), parse_reified_clpz(R, RR, RD)), Ps), Prop =.. [Functor,LD,LR,RD,RR,Ps,B] }, - list(Ps), + seq(Ps), propagator_init_trigger([LD,LR,RD,RR,B], Prop), a(B). boolean(L, R, B, Functor) --> { reify(L, LR, Ps1), reify(R, RR, Ps2), Prop =.. [Functor,LR,Ps1,RR,Ps2,B] }, - list(Ps1), list(Ps2), + seq(Ps1), seq(Ps2), propagator_init_trigger([LR,RR,B], Prop), a(LR, RR, B). -list([]) --> []. -list([L|Ls]) --> [L], list(Ls). - a(X,Y,B) --> ( nonvar(X) -> a(Y, B) ; nonvar(Y) -> a(X, B) @@ -6056,7 +6053,7 @@ domain_to_list(Domain, List) :- phrase(domain_to_list(Domain), List). domain_to_list(split(_, Left, Right)) --> domain_to_list(Left), domain_to_list(Right). domain_to_list(empty) --> []. -domain_to_list(from_to(n(F),n(T))) --> { numlist(F, T, Ns) }, list(Ns). +domain_to_list(from_to(n(F),n(T))) --> { numlist(F, T, Ns) }, seq(Ns). difference_arcs([], []) --> []. difference_arcs([V|Vs], FL0) --> @@ -7754,7 +7751,7 @@ attributes_goals([propagator(P, State)|As]) --> ; maplist(unwrap_with(=), Gs, Gs1) ), maplist(with_clpz, Gs1, Gs2) }, - list(Gs2) + seq(Gs2) ; [P] % possibly user-defined constraint ), attributes_goals(As). From 5cce8ddd7da6414a3e0c29b9085c637c98c0ad3f Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 8 Oct 2023 11:53:06 +0200 Subject: [PATCH 12/46] ENHANCED: avoid pending residual constraints in disentailed reified (div)/2 This addresses #2083: ?- #\0#=0//0 div 2. true. --- src/lib/clpz.pl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index a2d37014..bdc747f9 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3521,7 +3521,8 @@ L #\ R :- (L #\/ R) #/\ #\ (L #/\ R). means that V is an auxiliary variable that was introduced while parsing a compound expression. a(X,V) means V is auxiliary unless it is ==/2 X, and a(X,Y,V) means V is auxiliary unless it is ==/2 X - or Y. l(L) means the literal L occurs in the described list. + or Y. l(L) means the literal L occurs in the described list, + and ls(Ls) means the literals Ls occur in the described list. When a constraint becomes entailed or subexpressions become undefined, created auxiliary constraints are killed, and the @@ -3552,8 +3553,11 @@ parse_reified(E, R, D, m(A^B) => [d(D), p(pexp(A,B,R)), a(A,B,R)], m(A/B) => [d(D1), p(preified_slash(A,B,D2,R)), p(reified_and(D1,[],D2,[],D)),a(D2),a(A,B,R)], + m(A div B) => [d(D1), + g(phrase(parse_reified_clpz(((A-(A mod B)) // B), R, D2), Ps)), + ls(Ps), + p(reified_and(D1,[],D2,[],D)),a(D2),a(A,B,R)], m(A//B) => [skeleton(A,B,D,R,ptzdiv)], - m(A div B) => [skeleton(A,B,D,R,pdiv)], m(A mod B) => [skeleton(A,B,D,R,pmod)], m(A rem B) => [skeleton(A,B,D,R,prem)], % bitwise operations @@ -3644,6 +3648,7 @@ reified_goal(a(V), _) --> [a(V)]. reified_goal(a(X,V), _) --> [a(X,V)]. reified_goal(a(X,Y,V), _) --> [a(X,Y,V)]. reified_goal(l(L), _) --> [[L]]. +reified_goal(ls(Ls), _) --> [seq(Ls)]. parse_init_dcg([], _) --> []. parse_init_dcg([V|Vs], P) --> [{init_propagator(V, P)}], parse_init_dcg(Vs, P). @@ -4911,10 +4916,6 @@ run_propagator(ptimes(X,Y,Z), MState) --> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% X div Y = Z -run_propagator(pdiv(X,Y,Z), MState) --> - { kill(MState), Z #= (X-(X mod Y)) // Y }. - % X // Y = Z (round towards zero) run_propagator(ptzdiv(X,Y,Z), MState) --> ( nonvar(X) -> From f34703a279b3fe2609ba7f101cbf609cc119b8da Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 8 Oct 2023 12:07:17 +0200 Subject: [PATCH 13/46] remove no longer needed goal projection for pdiv --- src/lib/clpz.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index bdc747f9..274275b2 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -7778,7 +7778,6 @@ attribute_goal_(x_eq_abs_plus_v(X,V)) --> [#X #= abs(#X) + #V]. attribute_goal_(x_neq_y_plus_z(X,Y,Z)) --> [#X #\= #Y + #Z]. attribute_goal_(x_leq_y_plus_c(X,Y,C)) --> [#X #=< #Y + C]. attribute_goal_(ptzdiv(X,Y,Z)) --> [#X // #Y #= #Z]. -attribute_goal_(pdiv(X,Y,Z)) --> [#X div #Y #= #Z]. attribute_goal_(pexp(X,Y,Z)) --> [#X ^ #Y #= #Z]. attribute_goal_(psign(X,Y)) --> [#Y #= sign(#X)]. attribute_goal_(pabs(X,Y)) --> [#Y #= abs(#X)]. From 743412de33a1ce6e792c0e905a9023b858c870bc Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 8 Oct 2023 18:41:02 +0200 Subject: [PATCH 14/46] ENHANCED: Remove no longer needed morphed propagators. This addresses all remaining cases from #2083, excepting (//)/2: ?- #\ 1#=(X*X)/0. clpz:(X in inf..sup). ?- #\ 1#=(X+X)/0. clpz:(X in inf..sup). Still remaining: ?- #\ 0#=(Y// -1)/0. clpz:(-1*Y#=_A). --- src/lib/clpz.pl | 53 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 274275b2..0b789774 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -2636,14 +2636,26 @@ parse_goals([]) --> []. parse_goals([G|Gs]) --> parse_goal(G), parse_goals(Gs). parse_goal(g(Goal)) --> [Goal]. -parse_goal(p(Prop)) --> - { term_variables(Prop, Vs) }, +parse_goal(p(Prop0)) --> + { term_variables(Prop0, Vs), + morphing_propagator(Prop0, Prop, _) }, [make_propagator(Prop, P), new_queue(Q0), phrase(init_propagator_(Vs, P), [Q0], [Q]), variables_same_queue(Vs), trigger_once_(P, Q)]. +morphing(pplus). +morphing(ptimes). + +morphing_propagator(P0, P, Target) :- + P0 =.. [F|Args0], + ( morphing(F) -> + append(Args0, [Target], Args) + ; Args = Args0 + ), + P =.. [F|Args]. + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ?- use_module(library(lists)), use_module(library(format)), @@ -2907,8 +2919,9 @@ match_goals([G|Gs], F) --> match_goal(G, F), match_goals(Gs, F). match_goal(r(X,Y), F) --> { G =.. [F,X,Y] }, [G]. match_goal(d(X,Y), _) --> [parse_clpz(X, Y)]. match_goal(g(Goal), _) --> [Goal]. -match_goal(p(Prop), _) --> - { term_variables(Prop, Vs) }, +match_goal(p(Prop0), _) --> + { term_variables(Prop0, Vs), + morphing_propagator(Prop0, Prop, _) }, [make_propagator(Prop, P), new_queue(Q0), phrase(init_propagator_(Vs, P), [Q0], [Q]), @@ -3632,8 +3645,14 @@ reified_goal(p(Vs, Prop), _) --> [{variables_same_queue(Vs), trigger_once(P)}], [( { propagator_state(P, S), S == dead } -> [] ; [p(P)])]. -reified_goal(p(Prop), Ds) --> - { term_variables(Prop, Vs) }, +reified_goal(p(Prop0), Ds) --> + { term_variables(Prop0, Vs), + morphing_propagator(Prop0, Prop, Target), + ( functor(Prop0, F, _), morphing(F) -> + Ts = [p(Target)] + ; Ts = [] + ) }, + [Ts], reified_goal(p(Vs,Prop), Ds). reified_goal(function(D,Op,A,B,R), Ds) --> reified_goals([d(D),p(pfunction(Op,A,B,R)),a(A,B,R)], Ds). @@ -4777,7 +4796,7 @@ run_propagator(scalar_product_eq(Cs0,Vs0,P0), MState) --> ) }. % X + Y = Z -run_propagator(pplus(X,Y,Z), MState) --> +run_propagator(pplus(X,Y,Z,Morph), MState) --> ( nonvar(X) -> ( X =:= 0 -> kill(MState), Y = Z ; Y == Z -> kill(MState), X =:= 0 @@ -4796,7 +4815,7 @@ run_propagator(pplus(X,Y,Z), MState) --> ; [] ) ) - ; nonvar(Y) -> run_propagator(pplus(Y,X,Z), MState) + ; nonvar(Y) -> run_propagator(pplus(Y,X,Z,Morph), MState) ; nonvar(Z) -> ( X == Y -> kill(MState), { even(Z), X is Z // 2 } ; { fd_get(X, XD, _), @@ -4813,7 +4832,10 @@ run_propagator(pplus(X,Y,Z), MState) --> ; [] ) ) - ; ( X == Y -> { kill(MState), 2*X #= Z } + ; ( X == Y -> + kill(MState), + { make_propagator(ptimes(2,X,Z,_), Morph) }, + init_propagator_([X,Z], Morph) ; X == Z -> kill(MState), Y = 0 ; Y == Z -> kill(MState), X = 0 ; { fd_get(X, XD, XL, XU, XPs), @@ -4839,7 +4861,7 @@ run_propagator(pplus(X,Y,Z), MState) --> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -run_propagator(ptimes(X,Y,Z), MState) --> +run_propagator(ptimes(X,Y,Z,Morph), MState) --> ( nonvar(X) -> ( nonvar(Y) -> kill(MState), Z is X * Y ; X =:= 0 -> kill(MState), Z = 0 @@ -4859,7 +4881,7 @@ run_propagator(ptimes(X,Y,Z), MState) --> ) ) ) - ; nonvar(Y) -> run_propagator(ptimes(Y,X,Z), MState) + ; nonvar(Y) -> run_propagator(ptimes(Y,X,Z,Morph), MState) ; nonvar(Z) -> ( X == Y -> kill(MState), @@ -4885,7 +4907,10 @@ run_propagator(ptimes(X,Y,Z), MState) --> ; neq_num(X, 0), neq_num(Y, 0) ) ) - ; ( X == Y -> kill(MState), { X^2 #= Z } + ; ( X == Y -> + kill(MState), + { make_propagator(pexp(X,2,Z), Morph) }, + init_propagator_([X,Z], Morph) ; { fd_get(X, XD, XL, XU, XPs), fd_get(Y, _, YL, YU, _), fd_get(Z, ZD, ZL, ZU, _) }, @@ -7770,9 +7795,9 @@ bare_integer(V0, V) :- ( integer(V0) -> V = V0 ; V = #V0 ). attribute_goal_(presidual(Goal)) --> [Goal]. attribute_goal_(pgeq(A,B)) --> [#A #>= #B]. -attribute_goal_(pplus(X,Y,Z)) --> [#X + #Y #= #Z]. +attribute_goal_(pplus(X,Y,Z,_)) --> [#X + #Y #= #Z]. attribute_goal_(pneq(A,B)) --> [#A #\= #B]. -attribute_goal_(ptimes(X,Y,Z)) --> [#X * #Y #= #Z]. +attribute_goal_(ptimes(X,Y,Z,_)) --> [#X * #Y #= #Z]. attribute_goal_(absdiff_neq(X,Y,C)) --> [abs(#X - #Y) #\= C]. attribute_goal_(x_eq_abs_plus_v(X,V)) --> [#X #= abs(#X) + #V]. attribute_goal_(x_neq_y_plus_z(X,Y,Z)) --> [#X #\= #Y + #Z]. From 4b9cf0952edcb8f0b29f245b6462b762bb1714df Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 9 Oct 2023 11:38:01 -0600 Subject: [PATCH 15/46] do not push stack variables to the heap in term_variables (#2087) --- src/machine/system_calls.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index ba211683..359e8e9a 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -6872,6 +6872,17 @@ impl Machine { return; } + let stored_v = if stored_v.is_stack_var() { + let h = self.machine_st.heap.len(); + + self.machine_st.heap.push(heap_loc_as_cell!(h)); + self.machine_st.bind(Ref::heap_cell(h), stored_v); + + heap_loc_as_cell!(h) + } else { + stored_v + }; + let mut seen_set = IndexSet::with_hasher(FxBuildHasher::default()); self.machine_st.variable_set(&mut seen_set, stored_v); From cacc7f31930739f1b97be34bdbe12b54481e11cc Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Mon, 9 Oct 2023 20:42:28 +0200 Subject: [PATCH 16/46] FIXED: Queue triggered propagators to be processed after pexp/3 finishes If reification constraints (such as reified equality) are triggered here, then they may wish to disable this propagator and remove attributes from auxiliary variables. If the pexp/3 propagation is interrupted for that purpose, then the attributes will be unintentionally reattached by the following fd_put/3 calls in this propagator. We must ensure that this propagator completely finishes, so we queue the triggered propagators for later processing. geq/2 implements propagator activation outside the queue, and thus should not be used in propagators in the way it was used here. pexp/3 by itself may not seem particularly important. However, it can arise by metamorphosis from Var*Var. Example: ?- A#<==> -1#=C*C, C in 0..1. A = 0, clpz:(C in 0..1). This addresses #2089. --- src/lib/clpz.pl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 0b789774..4308e038 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -5498,7 +5498,9 @@ run_propagator(pexp(X,Y,Z), MState) --> ) ; nonvar(Y), Y > 0 -> ( { even(Y) } -> - { geq(Z, 0) } + { fd_get(Z, ZD0, ZPs0), + domain_remove_smaller_than(ZD0, 0, ZDG0) }, + fd_put(Z, ZDG0, ZPs0) ; true ), ( { fd_get(X, XD, XL, XU, _), fd_get(Z, ZD, ZL, ZU, ZPs) } -> From 99348ec309a5f8099f15b536f34a7c5910ee1a55 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Mon, 9 Oct 2023 21:31:44 +0200 Subject: [PATCH 17/46] ENHANCED: Omit projection of morphed (^)/2 in disentailed constraints. Example: ?- B #<==> (0^Y/0) #= Z. B = 0, clpz:(Y in 0..sup), clpz:(Z in inf..sup). --- src/lib/clpz.pl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 4308e038..d4e59d45 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -2647,6 +2647,7 @@ parse_goal(p(Prop0)) --> morphing(pplus). morphing(ptimes). +morphing(pexp). morphing_propagator(P0, P, Target) :- P0 =.. [F|Args0], @@ -4909,7 +4910,7 @@ run_propagator(ptimes(X,Y,Z,Morph), MState) --> ) ; ( X == Y -> kill(MState), - { make_propagator(pexp(X,2,Z), Morph) }, + { make_propagator(pexp(X,2,Z,_), Morph) }, init_propagator_([X,Z], Morph) ; { fd_get(X, XD, XL, XU, XPs), fd_get(Y, _, YL, YU, _), @@ -5436,9 +5437,13 @@ run_propagator(pmin(X,Y,Z), MState) --> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% % Z = X ^ Y -run_propagator(pexp(X,Y,Z), MState) --> +run_propagator(pexp(X,Y,Z,Morph), MState) --> ( X == 1 -> kill(MState), Z = 1 - ; X == 0 -> kill(MState), queue_goal((Z in 0..1, Y #>= 0, Z #<==> Y #= 0)) + ; X == 0 -> + kill(MState), + queue_goal((Z in 0..1, Y #>= 0)), + { make_propagator(reified_eq(1,Y,1,0,[],Z), Morph) }, + init_propagator_([X,Z], Morph) ; Y == 0 -> kill(MState), Z = 1 ; Y == 1 -> kill(MState), Z = X ; nonvar(X) -> @@ -7805,7 +7810,7 @@ attribute_goal_(x_eq_abs_plus_v(X,V)) --> [#X #= abs(#X) + #V]. attribute_goal_(x_neq_y_plus_z(X,Y,Z)) --> [#X #\= #Y + #Z]. attribute_goal_(x_leq_y_plus_c(X,Y,C)) --> [#X #=< #Y + C]. attribute_goal_(ptzdiv(X,Y,Z)) --> [#X // #Y #= #Z]. -attribute_goal_(pexp(X,Y,Z)) --> [#X ^ #Y #= #Z]. +attribute_goal_(pexp(X,Y,Z,_)) --> [#X ^ #Y #= #Z]. attribute_goal_(psign(X,Y)) --> [#Y #= sign(#X)]. attribute_goal_(pabs(X,Y)) --> [#Y #= abs(#X)]. attribute_goal_(pmod(X,M,K)) --> [#X mod #M #= #K]. From 1c3df1cdd7e2cbdaa897d63b9dbd0edf5bcbb0b4 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Mon, 9 Oct 2023 21:55:11 +0200 Subject: [PATCH 18/46] attach the propagator to Y --- src/lib/clpz.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index d4e59d45..ec299350 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -5443,7 +5443,7 @@ run_propagator(pexp(X,Y,Z,Morph), MState) --> kill(MState), queue_goal((Z in 0..1, Y #>= 0)), { make_propagator(reified_eq(1,Y,1,0,[],Z), Morph) }, - init_propagator_([X,Z], Morph) + init_propagator_([Y,Z], Morph) ; Y == 0 -> kill(MState), Z = 1 ; Y == 1 -> kill(MState), Z = X ; nonvar(X) -> From 8329d222cbbc1b90e3f57451e7a9c6faf90cedad Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Mon, 9 Oct 2023 22:59:51 +0200 Subject: [PATCH 19/46] ENHANCED: use (+)/2 to express unary minus This makes answers a bit shorter and more readable. Example: ?- X #= -Y. clpz:(X+Y#=0). This addresses #2058. --- src/lib/clpz.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index d4e59d45..5e73a14f 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -2566,7 +2566,7 @@ parse_clpz(E, R, g(power_var_num(E, V, N)) => [p(pexp(V, N, R))], m(A*B) => [p(ptimes(A, B, R))], m(A-B) => [p(pplus(R,B,A))], - m(-A) => [p(ptimes(-1,A,R))], + m(-A) => [p(pplus(A,R,0))], m(max(A,B)) => [g(A #=< #R), g(B #=< R), p(pmax(A, B, R))], m(min(A,B)) => [g(A #>= #R), g(B #>= R), p(pmin(A, B, R))], m(A mod B) => [g(B #\= 0), p(pmod(A, B, R))], @@ -2824,7 +2824,7 @@ matches([ m(var(X) #= var(Y)+var(Z)) => [p(pplus(Y,Z,X))], m(var(X) #= var(Y)-var(Z)) => [p(pplus(X,Z,Y))], m(var(X) #= var(Y)*var(Z)) => [p(ptimes(Y,Z,X))], - m(var(X) #= -var(Z)) => [p(ptimes(-1, Z, X))], + m(var(X) #= -var(Z)) => [p(pplus(X,Z,0))], m_c(any(X) #= any(Y), left_right_linsum_const(X, Y, Cs, Vs, S)) => [g(scalar_product_(#=, Cs, Vs, S))], m_c(var(X) #= abs(var(Y)) + any(V0), X == Y) => [d(V0,V),p(x_eq_abs_plus_v(X,V))], @@ -3560,7 +3560,7 @@ parse_reified(E, R, D, m(A+B) => [d(D), p(pplus(A,B,R)), a(A,B,R)], m(A*B) => [d(D), p(ptimes(A,B,R)), a(A,B,R)], m(A-B) => [d(D), p(pplus(R,B,A)), a(A,B,R)], - m(-A) => [d(D), p(ptimes(-1,A,R)), a(R)], + m(-A) => [d(D), p(pplus(A,R,0)), a(R)], m(max(A,B)) => [d(D), p(pgeq(R, A)), p(pgeq(R, B)), p(pmax(A,B,R)), a(A,B,R)], m(min(A,B)) => [d(D), p(pgeq(A, R)), p(pgeq(B, R)), p(pmin(A,B,R)), a(A,B,R)], m(abs(A)) => [d(D), g(#R#>=0), p(pabs(A, R)), a(A,R)], From 7c1cd18a0640c11bdb5e55e05ab0ee2fee0fe02a Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Mon, 9 Oct 2023 23:13:09 +0200 Subject: [PATCH 20/46] Z --> Y --- src/lib/clpz.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 5e73a14f..b2fec0c8 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -2824,7 +2824,7 @@ matches([ m(var(X) #= var(Y)+var(Z)) => [p(pplus(Y,Z,X))], m(var(X) #= var(Y)-var(Z)) => [p(pplus(X,Z,Y))], m(var(X) #= var(Y)*var(Z)) => [p(ptimes(Y,Z,X))], - m(var(X) #= -var(Z)) => [p(pplus(X,Z,0))], + m(var(X) #= -var(Y)) => [p(pplus(X,Y,0))], m_c(any(X) #= any(Y), left_right_linsum_const(X, Y, Cs, Vs, S)) => [g(scalar_product_(#=, Cs, Vs, S))], m_c(var(X) #= abs(var(Y)) + any(V0), X == Y) => [d(V0,V),p(x_eq_abs_plus_v(X,V))], From 77de570aa41119fb8c527e4193184d66fdcb683c Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 10 Oct 2023 11:13:42 -0600 Subject: [PATCH 21/46] report pre-marked values from eager stackful iterator (#2097) --- src/heap_iter.rs | 8 ++++++++ src/tests/term_variables.pl | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/heap_iter.rs b/src/heap_iter.rs index ef5e4ac5..02d87ace 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -59,6 +59,14 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> { fn follow(&mut self) -> Option { while let Some(value) = self.iter_stack.pop() { if value.get_mark_bit() == self.mark_phase { + if value.is_var() { + let h = value.get_value() as usize; + + if self.heap[h].is_var() && self.heap[h].get_value() as usize == h { + return Some(unmark_cell_bits!(value)); + } + } + continue; } diff --git a/src/tests/term_variables.pl b/src/tests/term_variables.pl index 57c55ed1..01dde0d1 100644 --- a/src/tests/term_variables.pl +++ b/src/tests/term_variables.pl @@ -38,6 +38,17 @@ test("term_variables#2063", ( Vars = [C] )). +test("term_variables#2097", ( + termt(T), term_variables(T,Vs), + T = [[[A|B]|A]|A], Vs == [A,B] +)). + +termt(T) :- + T = [T1|T2], + T1 = [T3|A], + T3 = [A|_], + T2 = A. + main :- findall(test(Name, Goal), test(Name, Goal), Tests), run_tests(Tests, Failed), From 282633c877fe1cf9fa39a900c33e4fb05922f43b Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 10 Oct 2023 19:42:42 +0200 Subject: [PATCH 22/46] ENHANCED: Queue morphed propagators to give them a chance for propagation. This addresses #2096: ?- B in -2..0, 0#<==>0#=0/(B*B),labeling([],[B]). B = 0. ?- A#<==>A#=A/A^2,A=0. A = 0. --- src/lib/clpz.pl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index ff7cfe4f..77bd7ba6 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -2657,6 +2657,12 @@ morphing_propagator(P0, P, Target) :- ), P =.. [F|Args]. +morph_into_propagator(MState, Vs, Propagator, Morph) --> + kill(MState), + { make_propagator(Propagator, Morph) }, + init_propagator_(Vs, Morph), + trigger_prop(Morph). + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ?- use_module(library(lists)), use_module(library(format)), @@ -4834,9 +4840,7 @@ run_propagator(pplus(X,Y,Z,Morph), MState) --> ) ) ; ( X == Y -> - kill(MState), - { make_propagator(ptimes(2,X,Z,_), Morph) }, - init_propagator_([X,Z], Morph) + morph_into_propagator(MState, [X,Z], ptimes(2,X,Z,_), Morph) ; X == Z -> kill(MState), Y = 0 ; Y == Z -> kill(MState), X = 0 ; { fd_get(X, XD, XL, XU, XPs), @@ -4909,9 +4913,7 @@ run_propagator(ptimes(X,Y,Z,Morph), MState) --> ) ) ; ( X == Y -> - kill(MState), - { make_propagator(pexp(X,2,Z,_), Morph) }, - init_propagator_([X,Z], Morph) + morph_into_propagator(MState, [X,Z], pexp(X,2,Z,_), Morph) ; { fd_get(X, XD, XL, XU, XPs), fd_get(Y, _, YL, YU, _), fd_get(Z, ZD, ZL, ZU, _) }, @@ -5440,10 +5442,8 @@ run_propagator(pmin(X,Y,Z), MState) --> run_propagator(pexp(X,Y,Z,Morph), MState) --> ( X == 1 -> kill(MState), Z = 1 ; X == 0 -> - kill(MState), queue_goal((Z in 0..1, Y #>= 0)), - { make_propagator(reified_eq(1,Y,1,0,[],Z), Morph) }, - init_propagator_([Y,Z], Morph) + morph_into_propagator(MState, [Y,Z], reified_eq(1,Y,1,0,[],Z), Morph) ; Y == 0 -> kill(MState), Z = 1 ; Y == 1 -> kill(MState), Z = X ; nonvar(X) -> From 32af04792592aa2292676ee9ee4770856c08b317 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 10 Oct 2023 19:49:48 +0200 Subject: [PATCH 23/46] remove definition and calls of do_queue/0, which has become a NOP --- src/lib/clpz.pl | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 77bd7ba6..76dee8bb 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -1957,7 +1957,6 @@ choice_order_variable(step, Order, Var, Vars, Vars0, Selection, Consistency) :- ( Var = Next, label(Vars, Selection, Order, step, Consistency) ; neq_num(Var, Next), - do_queue, label(Vars0, Selection, Order, step, Consistency) ). choice_order_variable(enum, Order, Var, Vars, _, Selection, Consistency) :- @@ -2753,14 +2752,12 @@ geq(A, B) :- ) ; ( AI cis_geq n(B) -> true ; domain_remove_smaller_than(AD, B, AD1), - fd_put(A, AD1, APs), - do_queue + fd_put(A, AD1, APs) ) ) ; fd_get(B, BD, BPs) -> domain_remove_greater_than(BD, A, BD1), - fd_put(B, BD1, BPs), - do_queue + fd_put(B, BD1, BPs) ; A >= B ). @@ -3316,7 +3313,7 @@ integer_kroot_leq(L, U, N, K, R) :- % When reasoning over integers, replace (=\=)/2 by (#\=)/2 to obtain more % general relations. -X #\= Y :- clpz_neq(X, Y), do_queue. +X #\= Y :- clpz_neq(X, Y). % X #\= Y + Z @@ -3379,7 +3376,7 @@ X #< Y :- Y #> X. % X in inf.. -4\/1..9\/81..sup. % ``` -#\ Q :- reify(Q, 0), do_queue. +#\ Q :- reify(Q, 0). %% #<==>(?P, ?Q) % @@ -3417,7 +3414,7 @@ X #< Y :- Y #> X. % Z = 2. % ``` -L #<==> R :- reify(L, B), reify(R, B), do_queue. +L #<==> R :- reify(L, B), reify(R, B). %% #==>(?P, ?Q) % @@ -3452,7 +3449,7 @@ L #<== R :- R #==> L. % % P and Q hold. -L #/\ R :- reify(L, 1), reify(R, 1), do_queue. +L #/\ R :- reify(L, 1), reify(R, 1). conjunctive_neqs_var_drep(Eqs, Var, Drep) :- conjunctive_neqs_var(Eqs, Var), @@ -3906,7 +3903,6 @@ domain(V, Dom) :- domains_intersection(Dom, Dom0, Dom1), %format("intersected\n: ~w\n ~w\n==> ~w\n\n", [Dom,Dom0,Dom1]), fd_put(V, Dom1, VPs), - do_queue, reinforce(V) ; domain_contains(Dom, V) ). @@ -4221,7 +4217,6 @@ activate_propagator(propagator(P,State)) --> enable_queue :- true. % NOP disable_queue :- true. % NOP -do_queue. % NOP %do_queue --> print_queue, { false }. do_queue --> @@ -6446,8 +6441,7 @@ num_infinite(Var, N0, N) :- weak_arc_all_distinct(Ls) :- must_be(list, Ls), Orig = original_goal(_, weak_arc_all_distinct(Ls)), - all_distinct(Ls, [], Orig), - do_queue. + all_distinct(Ls, [], Orig). all_distinct([], _, _). all_distinct([X|Right], Left, Orig) :- @@ -6760,8 +6754,10 @@ gcc_pairs([Key-Num0|KNs], Vs, [Key-Num|Rest]) :- gcc_global(Vs, KNs) :- gcc_check(KNs), - % reach fix-point: all elements of clpz_gcc_vs must be variables - do_queue, + % previously: call do_queue/0 (now a NOP) here to reach a + % fix-point: all elements of clpz_gcc_vs must be variables. We + % must ensure this holds if gcc_check/1 is later rewritten to + % actually disable the queue. with_local_attributes(Vs, (gcc_arcs(KNs, S, Vals), variables_with_num_occurrences(Vs, VNs), From 26fdf83a483ac98eae92aa56f13326d1af335093 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 10 Oct 2023 19:53:26 +0200 Subject: [PATCH 24/46] update comment to reflect the used propagators --- src/lib/clpz.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 76dee8bb..2432d6b1 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3545,9 +3545,9 @@ L #\ R :- (L #\/ R) #/\ #\ (L #/\ R). undefined, created auxiliary constraints are killed, and the "clpz" attribute is removed from auxiliary variables. - For mod/2, div/2, rem/2 etc. we create a skeleton propagator and - remember it as an auxiliary constraint. The pskeleton propagator - can use the skeleton when the constraint is defined. + For (//)/2, (mod)/2 and (rem)/2, we create a skeleton propagator + and remember it as an auxiliary constraint. The pskeleton + propagator can use the skeleton when the constraint is defined. We cannot use a skeleton propagator for (/)/2, since (/)/2 can fail in cases such as 0 #==> X #= 1/2, where we expect success. From 8de3498e07cfb155dac28bd85e9e88dd2147078b Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 10 Oct 2023 19:54:12 +0200 Subject: [PATCH 25/46] use round brackets around operators to form valid Prolog terms --- src/lib/clpz.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 2432d6b1..faad7c0f 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3537,9 +3537,9 @@ L #\ R :- (L #\/ R) #/\ #\ (L #/\ R). d(D) that states D is 1 iff all subexpressions are defined. a(V) means that V is an auxiliary variable that was introduced while parsing a compound expression. a(X,V) means V is auxiliary unless - it is ==/2 X, and a(X,Y,V) means V is auxiliary unless it is ==/2 X - or Y. l(L) means the literal L occurs in the described list, - and ls(Ls) means the literals Ls occur in the described list. + it is (==)/2 X, and a(X,Y,V) means V is auxiliary unless it is + (==)/2 X or Y. l(L) means the literal L occurs in the described + list, and ls(Ls) means the literals Ls occur in the described list. When a constraint becomes entailed or subexpressions become undefined, created auxiliary constraints are killed, and the From b5fdde08aa63100a1c8b6d12366d496c1f0d5324 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 10 Oct 2023 16:02:38 -0600 Subject: [PATCH 26/46] follow marked variables to end in eager_stackful_iter (#2100, #2101) --- src/heap_iter.rs | 29 ++++++++++++++++++++--------- src/tests/term_variables.pl | 22 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/heap_iter.rs b/src/heap_iter.rs index 02d87ace..032ce6bd 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -56,18 +56,29 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> { } } + #[inline] + fn is_self_ref_var(&self, value: HeapCellValue) -> bool { + if value.is_var() { + let h = value.get_value() as usize; + + if self.heap[h].is_var() && self.heap[h].get_value() as usize == h { + return true; + } + } + + false + } + fn follow(&mut self) -> Option { while let Some(value) = self.iter_stack.pop() { if value.get_mark_bit() == self.mark_phase { - if value.is_var() { - let h = value.get_value() as usize; - - if self.heap[h].is_var() && self.heap[h].get_value() as usize == h { - return Some(unmark_cell_bits!(value)); - } + // follow marked variables to their end. only marked + // non-variables are ignored. + if self.is_self_ref_var(value) { + return Some(unmark_cell_bits!(value)); + } else if !value.is_var() { + continue; } - - continue; } read_heap_cell!(value, @@ -90,7 +101,7 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> { let var_value = self.heap[h]; self.heap[h].set_mark_bit(self.mark_phase); - if !(var_value.is_var() && var_value.get_value() as usize == h) { + if !(self.heap[h].is_var() && self.heap[h].get_value() as usize == h) { self.iter_stack.push(var_value); continue; } diff --git a/src/tests/term_variables.pl b/src/tests/term_variables.pl index 01dde0d1..dce49c83 100644 --- a/src/tests/term_variables.pl +++ b/src/tests/term_variables.pl @@ -43,12 +43,34 @@ test("term_variables#2097", ( T = [[[A|B]|A]|A], Vs == [A,B] )). +test("term_variables#2100", ( + termt2(T), term_variables(T,Vs), + T = [[T|_B]|_A], Vs == [_A,_B] +)). + +test("term_variables#2101", ( + termt3(T), term_variables(T,Vs), + T = [[[[A|B]|A]|A]|A], Vs == [A, B] +)). + termt(T) :- T = [T1|T2], T1 = [T3|A], T3 = [A|_], T2 = A. +termt2(T) :- + T = [T1|_B], + T1 = [T|_A]. + +termt3(T) :- + T = [T1|T0], + T1 = [T2|T3], + T2 = [T4|A], + T4 = [A|_], + T3 = A, + T0 = A. + main :- findall(test(Name, Goal), test(Name, Goal), Tests), run_tests(Tests, Failed), From 902b08e6575a0bbab9b95ffcbfb68f15e3d5733f Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 10 Oct 2023 16:02:38 -0600 Subject: [PATCH 27/46] follow marked variables to end in eager_stackful_iter (#2100, #2101) --- src/heap_iter.rs | 47 +++++++++++++++++++++++++------------ src/tests/term_variables.pl | 22 +++++++++++++++++ 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/heap_iter.rs b/src/heap_iter.rs index 02d87ace..b8fd9dae 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -56,18 +56,29 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> { } } + #[inline] + fn is_self_ref_var(&self, value: HeapCellValue) -> bool { + if value.is_var() { + let h = value.get_value() as usize; + + if self.heap[h].is_var() && self.heap[h].get_value() as usize == h { + return true; + } + } + + false + } + fn follow(&mut self) -> Option { while let Some(value) = self.iter_stack.pop() { if value.get_mark_bit() == self.mark_phase { - if value.is_var() { - let h = value.get_value() as usize; - - if self.heap[h].is_var() && self.heap[h].get_value() as usize == h { - return Some(unmark_cell_bits!(value)); - } + // follow marked variables to their end. only marked + // non-variables are ignored. + if self.is_self_ref_var(value) { + return Some(unmark_cell_bits!(value)); + } else if !value.is_var() { + continue; } - - continue; } read_heap_cell!(value, @@ -75,22 +86,28 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> { let arity = cell_as_atom_cell!(self.heap[s]).get_arity(); for idx in (s + 1 .. s + arity + 1).rev() { - self.iter_stack.push(self.heap[idx]); - self.heap[idx].set_mark_bit(self.mark_phase); + if self.heap[idx].get_mark_bit() != self.mark_phase { + self.iter_stack.push(self.heap[idx]); + self.heap[idx].set_mark_bit(self.mark_phase); + } } } (HeapCellValueTag::Lis, l) => { - self.iter_stack.push(self.heap[l+1]); - self.iter_stack.push(self.heap[l]); + if self.heap[l+1].get_mark_bit() != self.mark_phase { + self.iter_stack.push(self.heap[l+1]); + self.heap[l+1].set_mark_bit(self.mark_phase); + } - self.heap[l].set_mark_bit(self.mark_phase); - self.heap[l+1].set_mark_bit(self.mark_phase); + if self.heap[l].get_mark_bit() != self.mark_phase { + self.iter_stack.push(self.heap[l]); + self.heap[l].set_mark_bit(self.mark_phase); + } } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { let var_value = self.heap[h]; self.heap[h].set_mark_bit(self.mark_phase); - if !(var_value.is_var() && var_value.get_value() as usize == h) { + if !(self.heap[h].is_var() && self.heap[h].get_value() as usize == h) { self.iter_stack.push(var_value); continue; } diff --git a/src/tests/term_variables.pl b/src/tests/term_variables.pl index 01dde0d1..f3ef5b32 100644 --- a/src/tests/term_variables.pl +++ b/src/tests/term_variables.pl @@ -43,12 +43,34 @@ test("term_variables#2097", ( T = [[[A|B]|A]|A], Vs == [A,B] )). +test("term_variables#2100", ( + termt2(T), term_variables(T,Vs), + T = [[T|A]|B], Vs == [A,B] +)). + +test("term_variables#2101", ( + termt3(T), term_variables(T,Vs), + T = [[[[A|B]|A]|A]|A], Vs == [A, B] +)). + termt(T) :- T = [T1|T2], T1 = [T3|A], T3 = [A|_], T2 = A. +termt2(T) :- + T = [T1|_B], + T1 = [T|_A]. + +termt3(T) :- + T = [T1|T0], + T1 = [T2|T3], + T2 = [T4|A], + T4 = [A|_], + T3 = A, + T0 = A. + main :- findall(test(Name, Goal), test(Name, Goal), Tests), run_tests(Tests, Failed), From 1163d14ea1e71442255671776c2522de75b88028 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 11 Oct 2023 12:41:45 -0600 Subject: [PATCH 28/46] fix control construct bugs, iter indentation (#947) --- src/heap_iter.rs | 20 ++++++++++---------- src/lib/builtins.pl | 44 ++++++++++++++++++++++++++++---------------- src/machine/unify.rs | 3 +++ 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/src/heap_iter.rs b/src/heap_iter.rs index b8fd9dae..d36855da 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -86,22 +86,22 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> { let arity = cell_as_atom_cell!(self.heap[s]).get_arity(); for idx in (s + 1 .. s + arity + 1).rev() { - if self.heap[idx].get_mark_bit() != self.mark_phase { + if self.heap[idx].get_mark_bit() != self.mark_phase { self.iter_stack.push(self.heap[idx]); self.heap[idx].set_mark_bit(self.mark_phase); - } + } } } (HeapCellValueTag::Lis, l) => { - if self.heap[l+1].get_mark_bit() != self.mark_phase { - self.iter_stack.push(self.heap[l+1]); - self.heap[l+1].set_mark_bit(self.mark_phase); - } + if self.heap[l+1].get_mark_bit() != self.mark_phase { + self.iter_stack.push(self.heap[l+1]); + self.heap[l+1].set_mark_bit(self.mark_phase); + } - if self.heap[l].get_mark_bit() != self.mark_phase { - self.iter_stack.push(self.heap[l]); - self.heap[l].set_mark_bit(self.mark_phase); - } + if self.heap[l].get_mark_bit() != self.mark_phase { + self.iter_stack.push(self.heap[l]); + self.heap[l].set_mark_bit(self.mark_phase); + } } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { let var_value = self.heap[h]; diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index b7b60df9..34f607f6 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -328,6 +328,9 @@ staggered_sc(_, G) :- call(G). % to reason about the programs. Also restricts the ability to run the program with alternative execution strategies !. +:- non_counted_backtracking get_cp/1. +get_cp(B) :- '$get_cp'(B). + :- non_counted_backtracking set_cp/1. set_cp(B) :- '$set_cp'(B). @@ -359,24 +362,21 @@ cont_list_goal(Conts, '$call'(builtins:dispatch_call_list(Conts))). :- non_counted_backtracking dispatch_prep/3. -dispatch_prep(Gs, B, [Cont|Conts]) :- +dispatch_prep(Gs, B, Conts) :- ( callable(Gs) -> strip_module(Gs, M, Gs0), ( nonvar(Gs0), - dispatch_prep_(Gs0, B, [Cont|Conts]) -> + dispatch_prep_(Gs0, B, Conts) -> true ; Gs0 == ! -> - Cont = '$call'(builtins:set_cp(B)), - Conts = [] + Conts = ['$call'(builtins:set_cp(B))] ; nonvar(Gs0), \+ callable(Gs0) -> throw(dispatch_prep_error) - ; Cont = Gs, - Conts = [] + ; Conts = [Gs] ) ; var(Gs) -> - Cont = Gs, - Conts = [] + Conts = [Gs] ; throw(dispatch_prep_error) ). @@ -387,20 +387,32 @@ dispatch_prep_((G1, G2), B, [Cont|Conts]) :- dispatch_prep(G1, B, IConts1), cont_list_goal(IConts1, Cont), dispatch_prep(G2, B, Conts). -dispatch_prep_((G1 ; G2), B, [Cont|Conts]) :- - dispatch_prep(G1, B, IConts0), +dispatch_prep_((G1 ; G2), B, Conts) :- + ( nonvar(G1) -> + ( G1 = (G11 -> G12) -> + dispatch_prep(G11, B, IConts2), + dispatch_prep(G12, B, IConts3), + cont_list_goal(IConts2, Cont2), + cont_list_goal(IConts3, Cont3), + Cont0 = '$call'(builtins:staggered_if_then(Cont2, Cont3)) + ; dispatch_prep(G1, B, IConts0), + dispatch_prep(G2, B, IConts1), + cont_list_goal(IConts0, Cont0) + ) + ; dispatch_prep(G1, B1, IConts0), + cont_list_goal(IConts0, Cont0) + ), dispatch_prep(G2, B, IConts1), cont_list_goal(IConts0, Cont0), cont_list_goal(IConts1, Cont1), - Cont = '$call'(builtins:staggered_sc(Cont0, Cont1)), - Conts = []. -dispatch_prep_((G1 -> G2), B, [Cont|Conts]) :- - dispatch_prep(G1, B, IConts1), + Conts = ['$call'(builtins:staggered_sc(Cont0, Cont1))]. +dispatch_prep_((G1 -> G2), B, Conts) :- + dispatch_prep(G1, B1, IConts1), dispatch_prep(G2, B, IConts2), cont_list_goal(IConts1, Cont1), cont_list_goal(IConts2, Cont2), - Cont = '$call'(builtins:staggered_if_then(Cont1, Cont2)), - Conts = []. + Conts = ['$call'(builtins:get_cp(B1)), + '$call'(builtins:staggered_if_then(Cont1, Cont2))]. :- non_counted_backtracking dispatch_call_list/1. diff --git a/src/machine/unify.rs b/src/machine/unify.rs index f9c03189..104c5157 100644 --- a/src/machine/unify.rs +++ b/src/machine/unify.rs @@ -691,6 +691,9 @@ pub(crate) trait Unifier: DerefMut { (HeapCellValueTag::Cons, ptr_1) => { Self::unify_constant(self, ptr_1, d2); } + (HeapCellValueTag::CutPoint, n1) => { + Self::unify_fixnum(self, n1, d2); + } _ => { unreachable!(); } From 6ed9a9983255d0ee44d180b21888c373c818b753 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 12 Oct 2023 21:04:22 +0200 Subject: [PATCH 29/46] ENHANCED: Omit unnecessary residual constraints in disentailed reified (//)/2 Example: ?- #\ 0#=(Y// -1)/0. %@ clpz:(Y in inf..sup). This addresses #2104. --- src/lib/clpz.pl | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index faad7c0f..26373bb0 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -2647,18 +2647,22 @@ parse_goal(p(Prop0)) --> morphing(pplus). morphing(ptimes). morphing(pexp). +morphing(ptzdiv). morphing_propagator(P0, P, Target) :- P0 =.. [F|Args0], ( morphing(F) -> - append(Args0, [Target], Args) - ; Args = Args0 + append(Args0, [Last], Args), + Target = p(Last) + ; Args = Args0, + Target = none ), P =.. [F|Args]. -morph_into_propagator(MState, Vs, Propagator, Morph) --> +morph_into_propagator(MState, Vs, P0, Morph) --> kill(MState), - { make_propagator(Propagator, Morph) }, + { morphing_propagator(P0, P, _), + make_propagator(P, Morph) }, init_propagator_(Vs, Morph), trigger_prop(Morph). @@ -3651,19 +3655,17 @@ reified_goal(p(Vs, Prop), _) --> [( { propagator_state(P, S), S == dead } -> [] ; [p(P)])]. reified_goal(p(Prop0), Ds) --> { term_variables(Prop0, Vs), - morphing_propagator(Prop0, Prop, Target), - ( functor(Prop0, F, _), morphing(F) -> - Ts = [p(Target)] - ; Ts = [] - ) }, - [Ts], + morphing_propagator(Prop0, Prop, Target) }, + target_propagator(Target), reified_goal(p(Vs,Prop), Ds). reified_goal(function(D,Op,A,B,R), Ds) --> reified_goals([d(D),p(pfunction(Op,A,B,R)),a(A,B,R)], Ds). reified_goal(function(D,Op,A,R), Ds) --> reified_goals([d(D),p(pfunction(Op,A,R)),a(A,R)], Ds). reified_goal(skeleton(A,B,D,R,F), Ds) --> - { Prop =.. [F,X,Y,Z] }, + { Prop0 =.. [F,X,Y,Z], + morphing_propagator(Prop0, Prop, Target) }, + target_propagator(Target), reified_goals([d(D1),l(p(P)),g(make_propagator(Prop, P)), p([A,B,D2,R], pskeleton(A,B,D2,[X,Y,Z]-P,R,F)), p(reified_and(D1,[],D2,[],D)),a(D2),a(A,B,R)], Ds). @@ -3671,7 +3673,10 @@ reified_goal(a(V), _) --> [a(V)]. reified_goal(a(X,V), _) --> [a(X,V)]. reified_goal(a(X,Y,V), _) --> [a(X,Y,V)]. reified_goal(l(L), _) --> [[L]]. -reified_goal(ls(Ls), _) --> [seq(Ls)]. +reified_goal(ls(Ls), _) --> [Ls]. + +target_propagator(p(Prop)) --> [[p(Prop)]]. +target_propagator(none) --> []. parse_init_dcg([], _) --> []. parse_init_dcg([V|Vs], P) --> [{init_propagator(V, P)}], parse_init_dcg(Vs, P). @@ -4835,7 +4840,7 @@ run_propagator(pplus(X,Y,Z,Morph), MState) --> ) ) ; ( X == Y -> - morph_into_propagator(MState, [X,Z], ptimes(2,X,Z,_), Morph) + morph_into_propagator(MState, [X,Z], ptimes(2,X,Z), Morph) ; X == Z -> kill(MState), Y = 0 ; Y == Z -> kill(MState), X = 0 ; { fd_get(X, XD, XL, XU, XPs), @@ -4908,7 +4913,7 @@ run_propagator(ptimes(X,Y,Z,Morph), MState) --> ) ) ; ( X == Y -> - morph_into_propagator(MState, [X,Z], pexp(X,2,Z,_), Morph) + morph_into_propagator(MState, [X,Z], pexp(X,2,Z), Morph) ; { fd_get(X, XD, XL, XU, XPs), fd_get(Y, _, YL, YU, _), fd_get(Z, ZD, ZL, ZU, _) }, @@ -4940,7 +4945,7 @@ run_propagator(ptimes(X,Y,Z,Morph), MState) --> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % X // Y = Z (round towards zero) -run_propagator(ptzdiv(X,Y,Z), MState) --> +run_propagator(ptzdiv(X,Y,Z,Morph), MState) --> ( nonvar(X) -> ( nonvar(Y) -> kill(MState), Y =\= 0, Z is X // Y ; { fd_get(Y, YD, YL, YU, YPs) }, @@ -4984,7 +4989,8 @@ run_propagator(ptzdiv(X,Y,Z), MState) --> ; nonvar(Y) -> Y =\= 0, ( Y =:= 1 -> kill(MState), X = Z - ; Y =:= -1 -> kill(MState), { Z #= -X } + ; Y =:= -1 -> + morph_into_propagator(MState, [X,Z], pplus(X,Z,0), Morph) ; { fd_get(X, XD, XL, XU, XPs) }, ( nonvar(Z) -> kill(MState), @@ -7805,7 +7811,7 @@ attribute_goal_(absdiff_neq(X,Y,C)) --> [abs(#X - #Y) #\= C]. attribute_goal_(x_eq_abs_plus_v(X,V)) --> [#X #= abs(#X) + #V]. attribute_goal_(x_neq_y_plus_z(X,Y,Z)) --> [#X #\= #Y + #Z]. attribute_goal_(x_leq_y_plus_c(X,Y,C)) --> [#X #=< #Y + C]. -attribute_goal_(ptzdiv(X,Y,Z)) --> [#X // #Y #= #Z]. +attribute_goal_(ptzdiv(X,Y,Z,_)) --> [#X // #Y #= #Z]. attribute_goal_(pexp(X,Y,Z,_)) --> [#X ^ #Y #= #Z]. attribute_goal_(psign(X,Y)) --> [#Y #= sign(#X)]. attribute_goal_(pabs(X,Y)) --> [#Y #= abs(#X)]. From 721cf20cf7f7b38f545e45f4463b179b362e10e4 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 12 Oct 2023 23:01:37 +0200 Subject: [PATCH 30/46] shift morphing to the more general p/2 case --- src/lib/clpz.pl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 26373bb0..771cd857 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3647,16 +3647,16 @@ reified_goal(d(D), Ds) --> ; { domain_error(one_or_two_element_list, Ds) } ). reified_goal(g(Goal), _) --> [{Goal}]. -reified_goal(p(Vs, Prop), _) --> +reified_goal(p(Vs, Prop0), _) --> + { morphing_propagator(Prop0, Prop, Target) }, [{make_propagator(Prop, P)}], + target_propagator(Target), parse_init_dcg(Vs, P), [{variables_same_queue(Vs), trigger_once(P)}], [( { propagator_state(P, S), S == dead } -> [] ; [p(P)])]. -reified_goal(p(Prop0), Ds) --> - { term_variables(Prop0, Vs), - morphing_propagator(Prop0, Prop, Target) }, - target_propagator(Target), +reified_goal(p(Prop), Ds) --> + { term_variables(Prop, Vs) }, reified_goal(p(Vs,Prop), Ds). reified_goal(function(D,Op,A,B,R), Ds) --> reified_goals([d(D),p(pfunction(Op,A,B,R)),a(A,B,R)], Ds). From 1ea397a80736184ee7f6fc83a3392fa1ddb16453 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 13 Oct 2023 14:33:36 -0600 Subject: [PATCH 31/46] correct (mod)/2 (#2103, #2107) --- src/machine/arithmetic_ops.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index 149338b5..d1f764d6 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -1,5 +1,6 @@ -use dashu::base::Abs; -use dashu::base::Gcd; +use dashu::base::{Abs, Gcd, UnsignedAbs}; +use dashu::integer::IBig; +use dashu::integer::fast_div::ConstDivisor; use divrem::*; use num_order::NumOrd; @@ -854,7 +855,9 @@ pub(crate) fn modulus(x: Number, y: Number, arena: &mut Arena) -> Result &Integer::ZERO { ((n1 + Integer::ONE) / n2) - Integer::ONE } else { - n1 / n2 + let ring = ConstDivisor::new(n2.unsigned_abs()); + let n1 = n1.clone(); + IBig::from(ring.reduce(n1).residue()) } } From 8aadc99f1d06f500603b318f7be35885e358719c Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 12 Oct 2023 11:22:57 -0600 Subject: [PATCH 32/46] fix bugs in marker algorithm iterator --- src/heap_iter.rs | 350 ++++++++++++++--------------- src/machine/gc.rs | 552 +++++++++++++++++++++++++++------------------- src/types.rs | 1 + 3 files changed, 507 insertions(+), 396 deletions(-) diff --git a/src/heap_iter.rs b/src/heap_iter.rs index d36855da..ab3b962a 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -1,5 +1,4 @@ -#[cfg(test)] -pub(crate) use crate::machine::gc::{IteratorUMP, StacklessPreOrderHeapIter}; +pub(crate) use crate::machine::gc::{CycleDetectorUMP, IteratorUMP, StacklessPreOrderHeapIter}; use crate::atom_table::*; use crate::machine::heap::*; @@ -504,15 +503,23 @@ impl<'a, ElideLists: ListElisionPolicy> Iterator for StackfulPreOrderHeapIter<'a } } -#[cfg(test)] #[inline(always)] pub(crate) fn stackless_preorder_iter( heap: &mut Vec, - cell: HeapCellValue, + start: usize, ) -> StacklessPreOrderHeapIter { - StacklessPreOrderHeapIter::::new(heap, cell) + StacklessPreOrderHeapIter::::new(heap, start) } + +pub(crate) fn cycle_detecting_stackless_preorder_iter( + heap: &mut Heap, + start: usize, +) -> StacklessPreOrderHeapIter { + StacklessPreOrderHeapIter::::new(heap, start) +} + + #[inline(always)] pub(crate) fn stackful_preorder_iter<'a, ElideLists: ListElisionPolicy>( heap: &'a mut Vec, @@ -663,9 +670,9 @@ pub(crate) type RightistPostOrderHeapIter<'a> = #[inline] pub(crate) fn stackless_post_order_iter<'a>( heap: &'a mut Heap, - cell: HeapCellValue, + start: usize, ) -> RightistPostOrderHeapIter<'a> { - PostOrderIterator::new(stackless_preorder_iter(heap, cell)) + PostOrderIterator::new(stackless_preorder_iter(heap, start)) } #[cfg(test)] @@ -685,8 +692,10 @@ mod tests { .heap .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); + wam.machine_st.heap.push(str_loc_as_cell!(0)); + { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 3); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -718,8 +727,10 @@ mod tests { ] )); + wam.machine_st.heap.push(str_loc_as_cell!(0)); + for _ in 0..20 { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 5); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -762,7 +773,7 @@ mod tests { )); for _ in 0..200000 { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -793,7 +804,7 @@ mod tests { { wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -814,7 +825,7 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -849,7 +860,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -883,8 +894,10 @@ mod tests { put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; + wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 2); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!( @@ -898,8 +911,7 @@ mod tests { assert_eq!(wam.machine_st.heap[0], pstr_cell); assert_eq!(wam.machine_st.heap[1], heap_loc_as_cell!(1)); - wam.machine_st.heap.pop(); - wam.machine_st.heap.push(pstr_loc_as_cell!(2)); + wam.machine_st.heap[1] = pstr_loc_as_cell!(3); let pstr_second_var_cell = put_partial_string(&mut wam.machine_st.heap, "def", &wam.machine_st.atom_tbl); @@ -907,36 +919,40 @@ mod tests { let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 2); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - heap_loc_as_cell!(3), + heap_loc_as_cell!(4), ); assert!(iter.next().is_none()); } assert_eq!(wam.machine_st.heap[0], pstr_cell); - assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(2)); - assert_eq!(wam.machine_st.heap[2], pstr_second_cell); - assert_eq!(wam.machine_st.heap[3], heap_loc_as_cell!(3)); + assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(3)); + assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(0)); + assert_eq!(wam.machine_st.heap[3], pstr_second_cell); + assert_eq!(wam.machine_st.heap[4], heap_loc_as_cell!(4)); wam.machine_st.heap.pop(); - wam.machine_st.heap.push(pstr_loc_as_cell!(4)); + wam.machine_st.heap.push(pstr_loc_as_cell!(5)); wam.machine_st.heap.push(pstr_offset_as_cell!(0)); wam.machine_st .heap .push(fixnum_as_cell!(Fixnum::build_with(2))); + wam.machine_st.heap[2] = heap_loc_as_cell!(4); + { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(4)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 2); let pstr_offset_cell = pstr_offset_as_cell!(0); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); + assert_eq!(unmark_cell_bits!(iter.next().unwrap()), fixnum_as_cell!(Fixnum::build_with(2))); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); @@ -946,19 +962,19 @@ mod tests { assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(4) + pstr_loc_as_cell!(3) ); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_offset_as_cell!(0) + pstr_loc_as_cell!(5) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[5]), + pstr_offset_as_cell!(0) + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[6]), fixnum_as_cell!(Fixnum::build_with(2)) ); @@ -974,31 +990,31 @@ mod tests { .heap .push(fixnum_as_cell!(Fixnum::build_with(0i64))); + wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 6); let pstr_offset_cell = pstr_offset_as_cell!(0); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); + assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_loc_as_cell!(4)); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); + assert_eq!(unmark_cell_bits!(iter.next().unwrap()), fixnum_as_cell!(Fixnum::build_with(0))); assert_eq!(iter.next(), None); } all_cells_unmarked(&wam.machine_st.heap); - wam.machine_st.heap.pop(); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(1i64))); + wam.machine_st.heap[5] = fixnum_as_cell!(Fixnum::build_with(1i64)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 6); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); + assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_loc_as_cell!(4)); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1008,13 +1024,17 @@ mod tests { unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0) ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + fixnum_as_cell!(Fixnum::build_with(1)) + ); assert_eq!(iter.next(), None); - - assert_eq!(iter.heap[4], pstr_offset_as_cell!(0)); - assert_eq!(iter.heap[5], fixnum_as_cell!(Fixnum::build_with(1i64))); } + assert_eq!(wam.machine_st.heap[4], pstr_offset_as_cell!(0)); + assert_eq!(wam.machine_st.heap[5], fixnum_as_cell!(Fixnum::build_with(1i64))); + all_cells_unmarked(&wam.machine_st.heap); wam.machine_st.heap.clear(); @@ -1030,7 +1050,7 @@ mod tests { wam.machine_st.heap.extend(functor); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1067,26 +1087,13 @@ mod tests { atom_as_cell!(f_atom, 3) ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(a_atom) - ); - assert_eq!(iter.next(), None); } all_cells_unmarked(&wam.machine_st.heap); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1133,7 +1140,7 @@ mod tests { assert_eq!(wam.machine_st.heap[4], empty_list_as_cell!()); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1165,7 +1172,7 @@ mod tests { wam.machine_st.heap[4] = list_loc_as_cell!(1); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1203,19 +1210,6 @@ mod tests { atom_as_cell!(f_atom, 3) ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(a_atom) - ); - assert_eq!(iter.next(), None); } @@ -1227,12 +1221,12 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(2)); wam.machine_st.heap.push(heap_loc_as_cell!(3)); wam.machine_st.heap.push(heap_loc_as_cell!(3)); + wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 4); assert_eq!(iter.next().unwrap(), heap_loc_as_cell!(3)); - assert_eq!(iter.next(), None); } @@ -1263,7 +1257,7 @@ mod tests { wam.machine_st.heap.push(list_loc_as_cell!(1)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1)); assert_eq!( @@ -1272,9 +1266,6 @@ mod tests { ); assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1)); - // this is what happens! this next line! We would like it not to happen though. - assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1)); - assert_eq!(iter.next(), None); } @@ -1310,9 +1301,10 @@ mod tests { wam.machine_st.heap.push(attr_var_as_cell!(11)); // linked from 7. wam.machine_st.heap.push(heap_loc_as_cell!(12)); + wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 13); assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1)); @@ -1351,6 +1343,7 @@ mod tests { let clpz_atom = atom!("clpz"); let p_atom = atom!("p"); + wam.machine_st.heap.pop(); wam.machine_st.heap.pop(); wam.machine_st.heap.push(heap_loc_as_cell!(13)); // 12 @@ -1365,9 +1358,10 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); // 21 wam.machine_st.heap.push(atom_as_cell!(p_atom, 1)); // 22 wam.machine_st.heap.push(heap_loc_as_cell!(23)); // 23 + wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 24); assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1)); @@ -1502,10 +1496,9 @@ mod tests { wam.machine_st.heap.clear(); { - let mut iter = stackless_preorder_iter( - &mut wam.machine_st.heap, - fixnum_as_cell!(Fixnum::build_with(0)), - ); + wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0))); + + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1517,8 +1510,6 @@ mod tests { all_cells_unmarked(&wam.machine_st.heap); - assert_eq!(wam.machine_st.heap.len(), 0); - wam.machine_st.heap.clear(); wam.machine_st.heap.push(str_loc_as_cell!(1)); @@ -1528,7 +1519,7 @@ mod tests { wam.machine_st.heap.push(atom_as_cell!(atom!("y"))); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, str_loc_as_cell!(1)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1540,7 +1531,7 @@ mod tests { atom_as_cell!(atom!("y")) ); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(1)); + assert_eq!(unmark_cell_bits!(iter.next().unwrap()), heap_loc_as_cell!(0)); assert!(iter.next().is_none()); } @@ -1552,9 +1543,10 @@ mod tests { wam.machine_st.heap.push(atom_as_cell!(atom!("g"), 2)); wam.machine_st.heap.push(str_loc_as_cell!(0)); wam.machine_st.heap.push(atom_as_cell!(atom!("y"))); + wam.machine_st.heap.push(str_loc_as_cell!(0)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 3); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1587,7 +1579,7 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(7)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 7); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1645,9 +1637,10 @@ mod tests { wam.machine_st.heap.push(atom_as_cell!(atom!("f"), 2)); wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(1)); + wam.machine_st.heap.push(str_loc_as_cell!(0)); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 3); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1674,38 +1667,68 @@ mod tests { wam.machine_st.heap.clear(); // representation of one of the heap terms as in issue #1384. - /* - wam.machine_st.heap.push(list_loc_as_cell!(7)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(list_loc_as_cell!(5)); - wam.machine_st.heap.push(empty_list_as_cell!()); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); - wam.machine_st.heap.push(empty_list_as_cell!()); - wam.machine_st.heap.push(heap_loc_as_cell!(3)); + wam.machine_st.heap.push(list_loc_as_cell!(7)); // 0 + wam.machine_st.heap.push(heap_loc_as_cell!(0)); // 1 + wam.machine_st.heap.push(list_loc_as_cell!(3)); // 2 + wam.machine_st.heap.push(list_loc_as_cell!(5)); // 3 + wam.machine_st.heap.push(empty_list_as_cell!()); // 4 + wam.machine_st.heap.push(heap_loc_as_cell!(2)); // 5 + wam.machine_st.heap.push(heap_loc_as_cell!(2)); // 6 + wam.machine_st.heap.push(empty_list_as_cell!()); // 7 + wam.machine_st.heap.push(heap_loc_as_cell!(3)); // 8 - { - let mut iter = stackless_preorder_iter( - &mut wam.machine_st.heap, - heap_loc_as_cell!(0), - ); + wam.machine_st.heap.push(heap_loc_as_cell!(0)); - while let Some(_) = iter.next() { - print_heap_terms(iter.heap.iter(), 0); - println!(""); - } + { + let mut iter = stackless_preorder_iter( + &mut wam.machine_st.heap, + 9, + ); - /* - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(atom!("f"), 2) - ); + /* + while let Some(_) = iter.next() { + print_heap_terms(iter.heap.iter(), 0); + println!(""); + } + */ - assert!(iter.next().is_none()); - */ - } - */ + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + list_loc_as_cell!(7) + ); + + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + list_loc_as_cell!(5) + ); + + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + heap_loc_as_cell!(2) + ); + + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + list_loc_as_cell!(3) + ); + + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + empty_list_as_cell!() + ); + + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + heap_loc_as_cell!(2) + ); + + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + empty_list_as_cell!() + ); + + assert_eq!(iter.next(), None); + } } #[test] @@ -2797,8 +2820,10 @@ mod tests { .heap .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); + wam.machine_st.heap.push(str_loc_as_cell!(0)); + { - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 3); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2828,8 +2853,10 @@ mod tests { ] )); + wam.machine_st.heap.push(str_loc_as_cell!(0)); + for _ in 0..20 { - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 5); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(0)); @@ -2860,7 +2887,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(0)); let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + stackless_post_order_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2876,8 +2903,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2903,7 +2929,7 @@ mod tests { { let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + stackless_post_order_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2936,7 +2962,7 @@ mod tests { { let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + stackless_post_order_iter(&mut wam.machine_st.heap, 0); // the cycle will be iterated twice before being detected. assert_eq!( @@ -2965,7 +2991,7 @@ mod tests { { let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + stackless_post_order_iter(&mut wam.machine_st.heap, 0); // cut the iteration short to check that all cells are // unmarked and unforwarded by the Drop instance of @@ -2999,9 +3025,11 @@ mod tests { put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; + wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + { let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + stackless_post_order_iter(&mut wam.machine_st.heap, 2); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -3013,6 +3041,7 @@ mod tests { assert_eq!(iter.next(), None); } + wam.machine_st.heap.pop(); wam.machine_st.heap.pop(); wam.machine_st.heap.push(pstr_loc_as_cell!(2)); @@ -3021,9 +3050,10 @@ mod tests { let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; + wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + { - let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 4); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -3048,9 +3078,10 @@ mod tests { .heap .push(fixnum_as_cell!(Fixnum::build_with(0))); + wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + { - let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 7); let mut pstr_loc_cell = pstr_loc_as_cell!(0); pstr_loc_cell.set_forwarding_bit(true); @@ -3058,11 +3089,7 @@ mod tests { // assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(0i64))); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - pstr_offset_as_cell!(0) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - pstr_offset_as_cell!(0) + heap_loc_as_cell!(3) ); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); @@ -3073,28 +3100,27 @@ mod tests { all_cells_unmarked(&wam.machine_st.heap); + wam.machine_st.heap.pop(); wam.machine_st.heap.pop(); wam.machine_st .heap .push(fixnum_as_cell!(Fixnum::build_with(1))); + wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + { - let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 7); //assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(1))); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - pstr_offset_as_cell!(0) + heap_loc_as_cell!(3) ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - pstr_offset_as_cell!(0) + pstr_second_cell ); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(iter.next(), None); } @@ -3110,9 +3136,10 @@ mod tests { wam.machine_st.heap.extend(functor); + wam.machine_st.heap.push(heap_loc_as_cell!(0)); + { - let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 9); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -3142,18 +3169,6 @@ mod tests { list_loc_as_cell!(3) ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(a_atom) - ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), atom_as_cell!(f_atom, 3) @@ -3172,8 +3187,7 @@ mod tests { wam.machine_st.heap[4] = list_loc_as_cell!(1); { - let mut iter = - stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -3193,25 +3207,15 @@ mod tests { atom_as_cell!(f_atom, 3) ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(a_atom) - ); - assert_eq!( unmark_cell_bits!(iter.next().unwrap()), atom_as_cell!(f_atom, 3) ); - assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1)); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + list_loc_as_cell!(1) + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 5b4895bd..0d37dfec 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -5,44 +5,94 @@ use crate::types::*; #[cfg(test)] use crate::heap_iter::{FocusedHeapIter, HeapOrStackTag, IterStackLoc}; -use core::marker::PhantomData; - pub(crate) trait UnmarkPolicy { - fn unmark(heap: &mut [HeapCellValue], current: usize); - fn mark(heap: &mut [HeapCellValue], current: usize); fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter) -> Option where Self: Sized; + 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); } -pub(crate) struct IteratorUMP; +pub(crate) struct IteratorUMP { + mark_phase: bool, +} + +fn invert_marker(iter: &mut StacklessPreOrderHeapIter) { + if iter.heap[iter.start].get_forwarding_bit() { + while !iter.backward() {} + } + + iter.heap[iter.start].set_forwarding_bit(true); + + iter.next = iter.heap[iter.start].get_value(); + iter.current = iter.start; + + while let Some(_) = iter.forward() {} +} impl UnmarkPolicy for IteratorUMP { #[inline(always)] - fn unmark(heap: &mut [HeapCellValue], current: usize) { - heap[current].set_mark_bit(false); + fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter) -> Option { + iter.forward_var() + } + + #[inline] + fn invert_marker(iter: &mut StacklessPreOrderHeapIter) { + iter.iter_state.mark_phase = false; + invert_marker(iter); } #[inline(always)] - fn mark(_heap: &mut [HeapCellValue], _current: usize) {} + fn cycle_detected(&mut self) {} + #[inline] + 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 { #[inline(always)] fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter) -> Option { iter.forward_var() } + + #[inline] + fn invert_marker(iter: &mut StacklessPreOrderHeapIter) { + iter.iter_state.mark_phase = false; + invert_marker(iter); + } + + #[inline] + fn cycle_detected(&mut self) { + self.cycle_detected = true; + } + + #[inline(always)] + fn mark_phase(&self) -> bool { + self.mark_phase + } + + #[inline] + fn report_list(&mut self, list_loc: usize) { + self.list_locs.push(list_loc); + } } struct MarkerUMP {} impl UnmarkPolicy for MarkerUMP { - #[inline(always)] - fn unmark(_heap: &mut [HeapCellValue], _current: usize) {} - - #[inline(always)] - fn mark(heap: &mut [HeapCellValue], current: usize) { - heap[current].set_mark_bit(true); - } - #[inline(always)] fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter) -> Option { if iter.heap[iter.current + 1].get_forwarding_bit() { @@ -60,16 +110,28 @@ impl UnmarkPolicy for MarkerUMP { iter.heap[iter.current].set_forwarding_bit(true); // forward the attr vars list. None } + + fn invert_marker(_iter: &mut StacklessPreOrderHeapIter) {} + + #[inline(always)] + fn mark_phase(&self) -> bool { + true + } + + #[inline(always)] + fn cycle_detected(&mut self) {} + + #[inline(always)] + fn report_list(&mut self, _list_loc: usize) {} } #[derive(Debug)] pub(crate) struct StacklessPreOrderHeapIter<'a, UMP: UnmarkPolicy> { - pub(crate) heap: &'a mut Vec, - orig_heap_len: usize, + pub(crate) heap: &'a mut [HeapCellValue], start: usize, current: usize, next: u64, - _marker: PhantomData, + iter_state: UMP, } #[cfg(test)] @@ -82,79 +144,96 @@ impl<'a> FocusedHeapIter for StacklessPreOrderHeapIter<'a, IteratorUMP> { impl<'a, UMP: UnmarkPolicy> Drop for StacklessPreOrderHeapIter<'a, UMP> { fn drop(&mut self) { + UMP::invert_marker(self); + if self.current == self.start { - self.heap.truncate(self.orig_heap_len); return; } while !self.backward() {} - - self.heap.truncate(self.orig_heap_len); } } impl<'a> StacklessPreOrderHeapIter<'a, MarkerUMP> { - pub(crate) fn new(heap: &'a mut Vec, cell: HeapCellValue) -> Self { - let orig_heap_len = heap.len(); - let start = orig_heap_len; - - heap.push(cell); - + pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self { heap[start].set_forwarding_bit(true); let next = heap[start].get_value(); Self { heap, - orig_heap_len, start, current: start, next, - _marker: PhantomData, + iter_state: MarkerUMP {}, } } } -impl<'a> StacklessPreOrderHeapIter<'a, IteratorUMP> { - #[cfg(test)] - pub(crate) fn new(heap: &'a mut Vec, cell: HeapCellValue) -> Self { - let orig_heap_len = heap.len(); - let start = orig_heap_len + 1; - - heap.push(cell); - heap.push(heap_loc_as_cell!(orig_heap_len)); - +impl<'a> StacklessPreOrderHeapIter<'a, CycleDetectorUMP> { + pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self { heap[start].set_forwarding_bit(true); let next = heap[start].get_value(); Self { heap, - orig_heap_len, start, current: start, next, - _marker: PhantomData, + iter_state: CycleDetectorUMP { + mark_phase: true, + cycle_detected: false, + list_locs: vec![], + }, + } + } + + #[inline] + pub(crate) fn found_cycle(&self) -> bool { + self.iter_state.cycle_detected + } + + #[inline] + pub(crate) fn list_locs(mut self) -> Vec { + std::mem::replace(&mut self.iter_state.list_locs, vec![]) + } +} + +impl<'a> StacklessPreOrderHeapIter<'a, IteratorUMP> { + pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self { + heap[start].set_forwarding_bit(true); + let next = heap[start].get_value(); + + Self { + heap, + start, + current: start, + next, + iter_state: IteratorUMP { + mark_phase: true, + }, } } } impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { - fn backward_and_return(&mut self) -> Option { - let current = self.current; + fn backward_and_return(&mut self) -> HeapCellValue { + let mut current = self.heap[self.current]; + current.set_value(self.next); if self.backward() { // set the f and m bits on the heap cell at start // so we invoke backward() and return None next call. self.heap[self.current].set_forwarding_bit(true); - self.heap[self.current].set_mark_bit(true); + self.heap[self.current].set_mark_bit(self.iter_state.mark_phase()); } - Some(self.heap[current]) + current } fn forward_var(&mut self) -> Option { if self.heap[self.next as usize].get_forwarding_bit() { - return self.backward_and_return(); + return Some(self.backward_and_return()); } let temp = self.heap[self.next as usize].get_value(); @@ -168,31 +247,38 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { fn forward(&mut self) -> Option { loop { - if !self.heap[self.current].get_mark_bit() { - self.heap[self.current].set_mark_bit(true); + if self.heap[self.current].get_mark_bit() != self.iter_state.mark_phase() { + self.heap[self.current].set_mark_bit(self.iter_state.mark_phase()); match self.heap[self.current].get_tag() { HeapCellValueTag::AttrVar => { + let next = self.next; + if let Some(cell) = UMP::forward_attr_var(self) { return Some(cell); } - if self.heap[self.next as usize].get_mark_bit() { - return Some(attr_var_as_cell!(self.current)); + if self.heap[self.next as usize].get_mark_bit() == self.iter_state.mark_phase() { + let tag = HeapCellValueTag::AttrVar; + return Some(HeapCellValue::build_with(tag, next)); } } HeapCellValueTag::Var => { + let next = self.next; + if let Some(cell) = self.forward_var() { return Some(cell); } - if self.heap[self.next as usize].get_mark_bit() { - return Some(heap_loc_as_cell!(self.current)); + if self.heap[self.next as usize].get_mark_bit() == self.iter_state.mark_phase() { + let tag = HeapCellValueTag::Var; + return Some(HeapCellValue::build_with(tag, next)); } } HeapCellValueTag::Str => { if self.heap[self.next as usize + 1].get_forwarding_bit() { - return self.backward_and_return(); + self.iter_state.cycle_detected(); + return Some(self.backward_and_return()); } let h = self.next as usize; @@ -216,43 +302,40 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let last_cell_loc = self.next as usize + 1; if self.heap[last_cell_loc].get_forwarding_bit() { - return self.backward_and_return(); + self.iter_state.cycle_detected(); + return Some(self.backward_and_return()); } - 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); + + 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); + } + } + return Some(list_loc_as_cell!(last_cell_loc - 1)); } HeapCellValueTag::PStrLoc => { let h = self.next as usize; - let cell = self.heap[h]; if self.heap[h + 1].get_forwarding_bit() { - return self.backward_and_return(); + self.iter_state.cycle_detected(); + return Some(self.backward_and_return()); } - if self.heap[h].get_tag() == HeapCellValueTag::PStr { - let last_cell_loc = h + 1; - self.heap[last_cell_loc].set_forwarding_bit(true); + let cell = self.heap[h]; - 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; - } else { - debug_assert!(self.heap[h].get_tag() == HeapCellValueTag::PStrOffset); + let last_cell_loc = h + 1; + self.heap[last_cell_loc].set_forwarding_bit(true); - self.next = self.heap[h].get_value(); - self.heap[h].set_value(self.current as u64); - self.current = h; - - if self.heap[h].get_mark_bit() { - continue; - } - } + 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; return Some(cell); } @@ -260,13 +343,11 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let h = self.next as usize; let cell = self.heap[h]; - // mark the Fixnum offset. - UMP::mark(self.heap, self.current + 1); - let last_cell_loc = h + 1; if self.heap[last_cell_loc].get_forwarding_bit() { - return self.backward_and_return(); + self.iter_state.cycle_detected(); + return Some(self.backward_and_return()); } if self.heap[h].get_tag() == HeapCellValueTag::PStr { @@ -290,7 +371,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let arity = AtomCell::from_bytes(cell.into_bytes()).get_arity(); if arity == 0 { - return self.backward_and_return(); + return Some(self.backward_and_return()); } else if self.backward() { return None; } @@ -301,7 +382,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { } } _ => { - return self.backward_and_return(); + return Some(self.backward_and_return()); } } } else { @@ -316,15 +397,13 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { while !self.heap[self.current].get_forwarding_bit() { let temp = self.heap[self.current].get_value(); - UMP::unmark(self.heap, self.current); - self.heap[self.current].set_value(self.next); + self.next = self.current as u64; self.current = temp as usize; } self.heap[self.current].set_forwarding_bit(false); - UMP::unmark(self.heap, self.current); if self.current == self.start { return true; @@ -351,8 +430,8 @@ impl<'a, UMP: UnmarkPolicy> Iterator for StacklessPreOrderHeapIter<'a, UMP> { } } -pub fn mark_cells(heap: &mut Heap, cell: HeapCellValue) { - let mut iter = StacklessPreOrderHeapIter::::new(heap, cell); +pub fn mark_cells(heap: &mut Heap, start: usize) { + let mut iter = StacklessPreOrderHeapIter::::new(heap, start); while let Some(_) = iter.forward() {} } @@ -369,62 +448,76 @@ mod tests { let a_atom = atom!("a"); let b_atom = atom!("b"); + wam.machine_st.heap.push(str_loc_as_cell!(1)); + wam.machine_st .heap .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); - mark_cells(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), + str_loc_as_cell!(1) + ); + + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(f_atom, 2) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), + unmark_cell_bits!(wam.machine_st.heap[2]), atom_as_cell!(a_atom) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), + unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(b_atom) ); wam.machine_st.heap.clear(); + wam.machine_st.heap.push(str_loc_as_cell!(1)); + wam.machine_st.heap.extend(functor!( f_atom, [ atom(a_atom), atom(b_atom), atom(a_atom), - cell(str_loc_as_cell!(0)) + cell(str_loc_as_cell!(1)) ] )); - mark_cells(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), + str_loc_as_cell!(1) + ); + + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(f_atom, 4) ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - atom_as_cell!(a_atom) - ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[2]), - atom_as_cell!(b_atom) + atom_as_cell!(a_atom) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[3]), - atom_as_cell!(a_atom) + atom_as_cell!(b_atom) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), - str_loc_as_cell!(0) + atom_as_cell!(a_atom) + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[5]), + str_loc_as_cell!(1) ); for cell in &mut wam.machine_st.heap { @@ -432,9 +525,9 @@ mod tests { } // make the structure doubly cyclic. - wam.machine_st.heap[2] = str_loc_as_cell!(0); + wam.machine_st.heap[2] = str_loc_as_cell!(1); - mark_cells(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -452,7 +545,7 @@ mod tests { ] )); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -485,7 +578,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(0)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -503,7 +596,7 @@ mod tests { wam.machine_st.heap.push(atom_as_cell!(b_atom)); wam.machine_st.heap.push(empty_list_as_cell!()); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -537,7 +630,7 @@ mod tests { // now make the list cyclic. wam.machine_st.heap.push(heap_loc_as_cell!(0)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -569,7 +662,7 @@ mod tests { // make the list doubly cyclic. wam.machine_st.heap[3] = heap_loc_as_cell!(0); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -586,7 +679,7 @@ mod tests { wam.machine_st.heap.push(stream_cell); wam.machine_st.heap.push(empty_list_as_cell!()); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -617,7 +710,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(3)); wam.machine_st.heap.push(heap_loc_as_cell!(0)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -644,18 +737,20 @@ mod tests { // two-part complete string, then a three-part cyclic string // involving an uncompacted list of chars. - let pstr_var_cell = - put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); + wam.machine_st.heap.push(pstr_loc_as_cell!(1)); + + let pstr_var_cell = put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_loc_as_cell!(1)); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - heap_loc_as_cell!(1) + unmark_cell_bits!(wam.machine_st.heap[2]), + heap_loc_as_cell!(2) ); wam.machine_st.heap.pop(); @@ -664,25 +759,25 @@ mod tests { cell.set_mark_bit(false); } - wam.machine_st.heap.push(pstr_loc_as_cell!(2)); + wam.machine_st.heap.push(pstr_loc_as_cell!(3)); let pstr_second_var_cell = put_partial_string(&mut wam.machine_st.heap, "def", &wam.machine_st.atom_tbl); let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) + unmark_cell_bits!(wam.machine_st.heap[2]), + pstr_loc_as_cell!(3) ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - heap_loc_as_cell!(3) + unmark_cell_bits!(wam.machine_st.heap[4]), + heap_loc_as_cell!(4) ); for cell in &mut wam.machine_st.heap { @@ -690,141 +785,137 @@ mod tests { } wam.machine_st.heap.pop(); - wam.machine_st.heap.push(pstr_loc_as_cell!(4)); - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); + wam.machine_st.heap.push(pstr_loc_as_cell!(5)); + wam.machine_st.heap.push(pstr_offset_as_cell!(1)); wam.machine_st .heap .push(fixnum_as_cell!(Fixnum::build_with(2))); + wam.machine_st.heap.push(pstr_loc_as_cell!(5)); - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(4)); + mark_cells(&mut wam.machine_st.heap, 7); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap[1 ..]); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(4) + unmark_cell_bits!(wam.machine_st.heap[2]), + pstr_loc_as_cell!(3) ); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_offset_as_cell!(0) + pstr_loc_as_cell!(5) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[5]), + pstr_offset_as_cell!(1) + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[6]), fixnum_as_cell!(Fixnum::build_with(2)) ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[7]), + pstr_loc_as_cell!(5) + ); for cell in &mut wam.machine_st.heap { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(3)); + wam.machine_st.heap[7] = heap_loc_as_cell!(2); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + mark_cells(&mut wam.machine_st.heap, 7); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + all_cells_marked_and_unforwarded(&wam.machine_st.heap[1..]); + + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(4) + unmark_cell_bits!(wam.machine_st.heap[2]), + pstr_loc_as_cell!(3) ); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_offset_as_cell!(0) + pstr_loc_as_cell!(5) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[5]), + pstr_offset_as_cell!(1) + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[6]), fixnum_as_cell!(Fixnum::build_with(2)) ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[7]), + heap_loc_as_cell!(2) + ); for cell in &mut wam.machine_st.heap { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(2)); + wam.machine_st.heap[7] = pstr_loc_as_cell!(1); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + mark_cells(&mut wam.machine_st.heap, 7); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + all_cells_marked_and_unforwarded(&wam.machine_st.heap[1..]); + + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(4) + unmark_cell_bits!(wam.machine_st.heap[2]), + pstr_loc_as_cell!(3) ); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_offset_as_cell!(0) + pstr_loc_as_cell!(5) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[5]), + pstr_offset_as_cell!(1) + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[6]), fixnum_as_cell!(Fixnum::build_with(2)) ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[7]), + pstr_loc_as_cell!(1) + ); for cell in &mut wam.machine_st.heap { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(1)); + wam.machine_st.heap[7] = heap_loc_as_cell!(0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + mark_cells(&mut wam.machine_st.heap, 7); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + all_cells_marked_and_unforwarded(&wam.machine_st.heap[1..]); + + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(4) + unmark_cell_bits!(wam.machine_st.heap[2]), + pstr_loc_as_cell!(3) ); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_offset_as_cell!(0) + pstr_loc_as_cell!(5) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[5]), + pstr_offset_as_cell!(1) + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[6]), fixnum_as_cell!(Fixnum::build_with(2)) ); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } - - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); - - all_cells_marked_and_unforwarded(&wam.machine_st.heap); - - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(4) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_offset_as_cell!(0) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - fixnum_as_cell!(Fixnum::build_with(2)) + unmark_cell_bits!(wam.machine_st.heap[7]), + heap_loc_as_cell!(0) ); wam.machine_st.heap.truncate(4); @@ -836,45 +927,46 @@ mod tests { wam.machine_st .heap .push(atom_as_cell!(atom!("irrelevant stuff"))); - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); + wam.machine_st.heap.push(pstr_offset_as_cell!(1)); wam.machine_st .heap .push(fixnum_as_cell!(Fixnum::build_with(2))); - wam.machine_st.heap[3] = pstr_loc_as_cell!(5); + // this is at index 7 + wam.machine_st.heap.push(pstr_loc_as_cell!(5)); - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(5)); + mark_cells(&mut wam.machine_st.heap, 7); - assert!(wam.machine_st.heap[0].get_mark_bit()); + assert!(!wam.machine_st.heap[0].get_mark_bit()); assert!(wam.machine_st.heap[1].get_mark_bit()); assert!(wam.machine_st.heap[2].get_mark_bit()); assert!(wam.machine_st.heap[3].get_mark_bit()); - assert!(!wam.machine_st.heap[4].get_mark_bit()); + assert!(wam.machine_st.heap[4].get_mark_bit()); assert!(wam.machine_st.heap[5].get_mark_bit()); assert!(wam.machine_st.heap[6].get_mark_bit()); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - pstr_loc_as_cell!(5) + unmark_cell_bits!(wam.machine_st.heap[2]), + pstr_loc_as_cell!(3) ); + assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[4]), atom_as_cell!(atom!("irrelevant stuff")) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_offset_as_cell!(0) + pstr_offset_as_cell!(1) ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[6]), fixnum_as_cell!(Fixnum::build_with(2)) ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[7]), + pstr_loc_as_cell!(5) + ); wam.machine_st.heap.clear(); @@ -896,7 +988,9 @@ mod tests { .heap .push(fixnum_as_cell!(Fixnum::build_with(2))); - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(7)); + wam.machine_st.heap.push(pstr_loc_as_cell!(7)); + + mark_cells(&mut wam.machine_st.heap, 9); assert!(!wam.machine_st.heap[0].get_mark_bit()); assert!(wam.machine_st.heap[1].get_mark_bit()); @@ -943,7 +1037,9 @@ mod tests { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(5)); + wam.machine_st.heap[9] = heap_loc_as_cell!(5); + + mark_cells(&mut wam.machine_st.heap, 9); assert!(!wam.machine_st.heap[0].get_mark_bit()); assert!(wam.machine_st.heap[1].get_mark_bit()); @@ -994,7 +1090,9 @@ mod tests { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(4)); + wam.machine_st.heap[9] = pstr_loc_as_cell!(4); + + mark_cells(&mut wam.machine_st.heap, 9); assert!(!wam.machine_st.heap[0].get_mark_bit()); assert!(wam.machine_st.heap[1].get_mark_bit()); @@ -1045,7 +1143,9 @@ mod tests { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(2)); + mark_cells(&mut wam.machine_st.heap, 9); + + wam.machine_st.heap[9] = heap_loc_as_cell!(2); assert!(!wam.machine_st.heap[0].get_mark_bit()); assert!(wam.machine_st.heap[1].get_mark_bit()); @@ -1096,7 +1196,9 @@ mod tests { cell.set_mark_bit(false); } - mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(1)); + wam.machine_st.heap[9] = pstr_loc_as_cell!(1); + + mark_cells(&mut wam.machine_st.heap, 9); assert!(!wam.machine_st.heap[0].get_mark_bit()); assert!(wam.machine_st.heap[1].get_mark_bit()); @@ -1161,7 +1263,7 @@ mod tests { wam.machine_st.heap.push(pstr_loc_as_cell!(0)); wam.machine_st.heap.push(empty_list_as_cell!()); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(4)); + mark_cells(&mut wam.machine_st.heap, 4); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1207,7 +1309,7 @@ mod tests { wam.machine_st.heap.push(pstr_loc_as_cell!(0)); wam.machine_st.heap.push(heap_loc_as_cell!(4)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(4)); + mark_cells(&mut wam.machine_st.heap, 4); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1250,7 +1352,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(3)); wam.machine_st.heap.push(heap_loc_as_cell!(3)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1278,7 +1380,7 @@ mod tests { wam.machine_st.heap.push(list_loc_as_cell!(1)); wam.machine_st.heap.push(list_loc_as_cell!(1)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1313,7 +1415,7 @@ mod tests { wam.machine_st.heap.push(attr_var_as_cell!(11)); // linked from 7. wam.machine_st.heap.push(heap_loc_as_cell!(12)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1394,7 +1496,7 @@ mod tests { wam.machine_st.heap.push(atom_as_cell!(p_atom, 1)); // 22 wam.machine_st.heap.push(heap_loc_as_cell!(23)); // 23 - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1506,7 +1608,7 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(5)); wam.machine_st.heap.push(list_loc_as_cell!(5)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&mut wam.machine_st.heap[0..24]); @@ -1628,7 +1730,7 @@ mod tests { .heap .push(fixnum_as_cell!(Fixnum::build_with(0))); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); assert_eq!(wam.machine_st.heap.len(), 1); @@ -1647,7 +1749,7 @@ mod tests { wam.machine_st.heap.push(str_loc_as_cell!(4)); wam.machine_st.heap.push(empty_list_as_cell!()); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(7)); + mark_cells(&mut wam.machine_st.heap, 7); assert_eq!(wam.machine_st.heap.len(), 10); @@ -1699,8 +1801,9 @@ mod tests { wam.machine_st.heap.push(atom_as_cell!(atom!("f"), 2)); wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(1)); + wam.machine_st.heap.push(str_loc_as_cell!(0)); - mark_cells(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 3); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1729,7 +1832,9 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); wam.machine_st.heap.push(heap_loc_as_cell!(2)); - mark_cells(&mut wam.machine_st.heap, list_loc_as_cell!(5)); + wam.machine_st.heap.push(list_loc_as_cell!(5)); + + mark_cells(&mut wam.machine_st.heap, 7); all_cells_marked_and_unforwarded(&wam.machine_st.heap); @@ -1775,8 +1880,9 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(2)); wam.machine_st.heap.push(empty_list_as_cell!()); // C = [[]|B]. wam.machine_st.heap.push(heap_loc_as_cell!(3)); + wam.machine_st.heap.push(heap_loc_as_cell!(0)); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 9); assert!(wam.machine_st.heap[0].get_mark_bit()); assert!(!wam.machine_st.heap[1].get_mark_bit()); @@ -1841,7 +1947,7 @@ mod tests { .heap .push(fixnum_as_cell!(Fixnum::build_with(4))); - mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + mark_cells(&mut wam.machine_st.heap, 0); all_cells_marked_and_unforwarded(&wam.machine_st.heap); diff --git a/src/types.rs b/src/types.rs index cf2159ba..043bd221 100644 --- a/src/types.rs +++ b/src/types.rs @@ -430,6 +430,7 @@ impl HeapCellValue { HeapCellValueTag::Cons | HeapCellValueTag::F64 | HeapCellValueTag::Fixnum + | HeapCellValueTag::CutPoint | HeapCellValueTag::Char | HeapCellValueTag::CStr => true, HeapCellValueTag::Atom => cell_as_atom_cell!(self).get_arity() == 0, From e4a677ceead710eecfa33ebdbf5098513c5cf5a8 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 12 Oct 2023 23:16:35 -0600 Subject: [PATCH 33/46] detect all cycles in roughly linear time and constant space (#2102) --- src/heap_iter.rs | 6 ++- src/machine/gc.rs | 76 +++++++++++++++++++++++-------- src/machine/machine_state_impl.rs | 28 ++++++------ 3 files changed, 77 insertions(+), 33 deletions(-) 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) From 669023914a6a797159d40f971870117d07a737ec Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 14 Oct 2023 12:05:57 -0600 Subject: [PATCH 34/46] add bounds checks for stackless iterator (#2110) --- src/machine/gc.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 8bb38e17..5b1ccca3 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -280,9 +280,11 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { return Some(cell); } - if self.heap[self.next as usize].get_mark_bit() == self.iter_state.mark_phase() { - let tag = HeapCellValueTag::AttrVar; - return Some(HeapCellValue::build_with(tag, next)); + if self.next < self.heap.len() as u64 { + if self.heap[self.next as usize].get_mark_bit() == self.iter_state.mark_phase() { + let tag = HeapCellValueTag::AttrVar; + return Some(HeapCellValue::build_with(tag, next)); + } } } HeapCellValueTag::Var => { @@ -297,9 +299,11 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { return Some(cell); } - if self.heap[self.next as usize].get_mark_bit() == self.iter_state.mark_phase() { - let tag = HeapCellValueTag::Var; - return Some(HeapCellValue::build_with(tag, next)); + if self.next < self.heap.len() as u64 { + if self.heap[self.next as usize].get_mark_bit() == self.iter_state.mark_phase() { + let tag = HeapCellValueTag::Var; + return Some(HeapCellValue::build_with(tag, next)); + } } } HeapCellValueTag::Str => { From dc08c26d9f2fb7cd35696fe4e17949ea5d258904 Mon Sep 17 00:00:00 2001 From: notoria Date: Sat, 14 Oct 2023 16:10:31 +0200 Subject: [PATCH 35/46] Trigger propagator for (xor)/2 --- src/lib/clpz.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 771cd857..705ffeb7 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3591,7 +3591,7 @@ parse_reified(E, R, D, m(A>>B) => [function(D,>>,A,B,R)], m(A/\B) => [function(D,/\,A,B,R)], m(A\/B) => [function(D,\/,A,B,R)], - m(xor(A, B)) => [function(D,xor,A,B,R)], + m(xor(A, B)) => [skeleton(A,B,D,R,pxor)], g(true) => [g(domain_error(clpz_expression, E))]] ). From a1b71f04406ad044834ea1fa573a548abc837f0f Mon Sep 17 00:00:00 2001 From: notoria Date: Sat, 14 Oct 2023 16:11:59 +0200 Subject: [PATCH 36/46] Trigger propagator for sign/1 --- src/lib/clpz.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 705ffeb7..add0f356 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3586,7 +3586,7 @@ parse_reified(E, R, D, m(msb(A)) => [g(#A#>0) ,function(D,msb,A,R)], m(lsb(A)) => [g(#A#>0), function(D,lsb,A,R)], m(popcount(A)) => [function(D,popcount,A,R)], - m(sign(A)) => [function(D,sign,A,R)], + m(sign(A)) => [d(D), p(psign(A, R)), a(A,R)], m(A< [function(D,<<,A,B,R)], m(A>>B) => [function(D,>>,A,B,R)], m(A/\B) => [function(D,/\,A,B,R)], From c6fcbe20e11954d00eff6f2ab96ea88537cdbf26 Mon Sep 17 00:00:00 2001 From: notoria Date: Sat, 14 Oct 2023 16:13:52 +0200 Subject: [PATCH 37/46] Trigger propagator for popcount/1 --- src/lib/clpz.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index add0f356..ddc2ee3e 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3585,7 +3585,7 @@ parse_reified(E, R, D, m(\A) => [function(D,\,A,R)], m(msb(A)) => [g(#A#>0) ,function(D,msb,A,R)], m(lsb(A)) => [g(#A#>0), function(D,lsb,A,R)], - m(popcount(A)) => [function(D,popcount,A,R)], + m(popcount(A)) => [d(D), p(ppopcount(A, R)), a(A,R)], m(sign(A)) => [d(D), p(psign(A, R)), a(A,R)], m(A< [function(D,<<,A,B,R)], m(A>>B) => [function(D,>>,A,B,R)], From 62e6ca02f9f1ef45cdf5fe7b691469b2d6e99945 Mon Sep 17 00:00:00 2001 From: notoria Date: Sat, 14 Oct 2023 16:15:03 +0200 Subject: [PATCH 38/46] Reify (^)/2 Like (/)/2, (^)/2 can fail in cases such as 0 #==> X #= 2^(-1), where success is expected. --- src/lib/clpz.pl | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index ddc2ee3e..f766771a 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3571,7 +3571,8 @@ parse_reified(E, R, D, m(max(A,B)) => [d(D), p(pgeq(R, A)), p(pgeq(R, B)), p(pmax(A,B,R)), a(A,B,R)], m(min(A,B)) => [d(D), p(pgeq(A, R)), p(pgeq(B, R)), p(pmin(A,B,R)), a(A,B,R)], m(abs(A)) => [d(D), g(#R#>=0), p(pabs(A, R)), a(A,R)], - m(A^B) => [d(D), p(pexp(A,B,R)), a(A,B,R)], + m(A^B) => [d(D1), p(preified_exp(A,B,D2,R)), + p(reified_and(D1,[],D2,[],D)),a(D2),a(A,B,R)], m(A/B) => [d(D1), p(preified_slash(A,B,D2,R)), p(reified_and(D1,[],D2,[],D)),a(D2),a(A,B,R)], m(A div B) => [d(D1), @@ -5939,6 +5940,35 @@ run_propagator(preified_slash(X, Y, D, R), MState) --> ; [] ). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +run_propagator(preified_exp(X, Y, D, R), MState) --> + ( X == 1 -> + kill(MState), + D = 1, + R = 1 + ; Y == 0 -> + kill(MState), + D = 1, + R = 1 + ; Y == 1 -> + kill(MState), + D = 1, + R = X + ; nonvar(X), + nonvar(Y) -> + kill(MState), + ( ( abs(X) =:= 1 ; Y >= 0 ) -> + D = 1, + R is X^Y + ; D = 0 + ) + ; D == 1 -> + kill(MState), + queue_goal(X^Y #= R) + ; [] + ). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -7872,6 +7902,7 @@ attribute_goal_(reified_and(X,_,Y,_,B)) --> [#X #/\ #Y #<==> #B]. attribute_goal_(reified_or(X, _, Y, _, B)) --> [#X #\/ #Y #<==> #B]. attribute_goal_(reified_not(X, Y)) --> [#\ #X #<==> #Y]. attribute_goal_(preified_slash(X, Y, _, R)) --> [#X/ #Y #= R]. +attribute_goal_(preified_exp(X, Y, _, R)) --> [#X^ #Y #= R]. attribute_goal_(pimpl(X, Y, _)) --> [#X #==> #Y]. attribute_goal_(pfunction(Op, A, B, R)) --> { Expr =.. [Op,#A,#B] }, From 307cb56ef5fccac7a50c331a09f9a99e5c6eb2d0 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 14 Oct 2023 13:00:58 -0600 Subject: [PATCH 39/46] fix bugs & incompleteness of cycle-detecting stackless iterator (#2111) --- src/machine/gc.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 5b1ccca3..a0a802db 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -17,6 +17,7 @@ pub(crate) trait UnmarkPolicy { ) -> bool where Self: Sized { iter.backward() } + fn detect_list_tail_cycle(_iter: &mut StacklessPreOrderHeapIter) where Self: Sized {} } pub(crate) struct IteratorUMP { @@ -87,12 +88,18 @@ impl UnmarkPolicy for CycleDetectorUMP { 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; + if !iter.iter_state.cycle_detected && iter.iter_state.mark_phase { + iter.iter_state.cycle_detected = iter.detect_list_cycle(); } iter.backward() } + + fn detect_list_tail_cycle(iter: &mut StacklessPreOrderHeapIter) { + if iter.iter_state.mark_phase && !iter.iter_state.cycle_detected { + iter.iter_state.cycle_detected = iter.detect_list_cycle(); + } + } } struct MarkerUMP {} @@ -273,7 +280,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { 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() { + if current as u64 != next && self.heap[next as usize].is_compound(self.heap) { self.iter_state.cycle_detected(); } @@ -292,7 +299,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let current = self.current; if let Some(cell) = self.forward_var() { - if current as u64 != next && self.heap[next as usize].is_ref() { + if current as u64 != next && self.heap[next as usize].is_compound(self.heap) { self.iter_state.cycle_detected(); } @@ -347,6 +354,11 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { if self.heap[last_cell_loc-1].get_mark_bit() == self.iter_state.mark_phase() { // the conjunction leading here is a necessary but not sufficient // condition of the presence of a cycle at the list head. + + if last_cell_loc == self.current { + UMP::detect_list_tail_cycle(self); + } + self.backward(); if UMP::list_head_cycle_detecting_backward(self) { From 379c252b897975e6a5c6d03e77f2022143dc7f22 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 14 Oct 2023 14:03:47 -0600 Subject: [PATCH 40/46] correct cyclic variable check in cycle detecting stackless iterator (#2111, #2113) --- src/machine/gc.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/machine/gc.rs b/src/machine/gc.rs index a0a802db..bb90a29c 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -269,6 +269,20 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { None } + #[inline] + fn is_cyclic(&self, h: usize) -> bool { + if self.heap[h].is_var() { + self.heap[h].get_forwarding_bit() + } else if self.heap[h].is_ref() { + // the cell h in the second branch contains its original + // value whether h is marked or unmarked, meaning the + // is_compound check is well-founded in either case. + self.heap[h].get_forwarding_bit() || self.heap[h].is_compound(self.heap) + } else { + false + } + } + fn forward(&mut self) -> Option { loop { if self.heap[self.current].get_mark_bit() != self.iter_state.mark_phase() { @@ -280,7 +294,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let current = self.current; if let Some(cell) = UMP::forward_attr_var(self) { - if current as u64 != next && self.heap[next as usize].is_compound(self.heap) { + if current as u64 != next && self.is_cyclic(next as usize) { self.iter_state.cycle_detected(); } @@ -299,7 +313,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let current = self.current; if let Some(cell) = self.forward_var() { - if current as u64 != next && self.heap[next as usize].is_compound(self.heap) { + if current as u64 != next && self.is_cyclic(next as usize) { self.iter_state.cycle_detected(); } From d96c9e00b7d959c297d59efa4e66abc4b4511bbc Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 14 Oct 2023 16:37:18 -0600 Subject: [PATCH 41/46] correct more acyclic_term/1 issues (#2111, #2114), add acyclic_term tests --- src/machine/gc.rs | 18 ++-- src/tests/acyclic_term.pl | 185 ++++++++++++++++++++++++++++++++++++++ tests/scryer/src_tests.rs | 9 ++ 3 files changed, 203 insertions(+), 9 deletions(-) create mode 100644 src/tests/acyclic_term.pl diff --git a/src/machine/gc.rs b/src/machine/gc.rs index bb90a29c..d7837395 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -270,14 +270,14 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { } #[inline] - fn is_cyclic(&self, h: usize) -> bool { - if self.heap[h].is_var() { - self.heap[h].get_forwarding_bit() - } else if self.heap[h].is_ref() { - // the cell h in the second branch contains its original - // value whether h is marked or unmarked, meaning the + fn is_cyclic(&self, var_current: usize, var_next: usize) -> bool { + if self.heap[var_next].is_var() { + var_current != var_next && self.current + 1 != var_current + } else if self.heap[var_next].is_ref() { + // the cell var_next in the second branch contains its original + // value whether var_next is marked or unmarked, meaning the // is_compound check is well-founded in either case. - self.heap[h].get_forwarding_bit() || self.heap[h].is_compound(self.heap) + self.heap[var_next].get_forwarding_bit() } else { false } @@ -294,7 +294,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let current = self.current; if let Some(cell) = UMP::forward_attr_var(self) { - if current as u64 != next && self.is_cyclic(next as usize) { + if self.is_cyclic(current, next as usize) { self.iter_state.cycle_detected(); } @@ -313,7 +313,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let current = self.current; if let Some(cell) = self.forward_var() { - if current as u64 != next && self.is_cyclic(next as usize) { + if self.is_cyclic(current, next as usize) { self.iter_state.cycle_detected(); } diff --git a/src/tests/acyclic_term.pl b/src/tests/acyclic_term.pl new file mode 100644 index 00000000..a7354a3f --- /dev/null +++ b/src/tests/acyclic_term.pl @@ -0,0 +1,185 @@ +:- use_module(library(format)). + +term1(A) :- + B=[C|D], + A=[D|C], + B=[C|B]. + +term2(A) :- + A=[B|C], + D=[C|C], + D=[B|D]. + +term3(A) :- + A=[_B|C], + D=[C|_E], + A=[C|D]. + +test("acyclic_term_1", ( + L = [_Y,[M,B],B|M], acyclic_term(L) +)). + +test("acyclic_term_2", ( + L = [_Y,[M,_B,L]|M], \+ acyclic_term(L) +)). + +test("acyclic_term_3", ( + L = [_Y,[M,B,L,B]|M], \+ acyclic_term(L) +)). + +test("acyclic_term_4", ( + L = [_Y,[L,_A,_B]|_M], \+ acyclic_term(L) +)). + +test("acyclic_term_5", ( + L = [_Y,[M,_A,_B]|M], acyclic_term(L) +)). + +test("acyclic_term_6", ( + L = [_Y,[L,_A,_B]|_M], \+ acyclic_term(L) +)). + +test("acyclic_term_7", ( + L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(T) +)). + +test("acyclic_term_8", ( + L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(L) +)). + +test("acyclic_term_9", ( + L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(A) +)). + +test("acyclic_term_10", ( + L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(Y) +)). + +test("acyclic_term_11", ( + L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(X) +)). + +test("acyclic_term_12", ( + A = [1|2], X = A, T=a(X, A), acyclic_term(T) +)). + +test("acyclic_term_13", ( + A = [A|2], X = A, T=a(X, A), \+ acyclic_term(T) +)). + +test("acyclic_term_13", ( + A = [T|2], X = A, T=a(X, A), \+ acyclic_term(T) +)). + +test("acyclic_term_14", ( + T = [_A|T], \+ acyclic_term(T) +)). + +test("acyclic_term_15", ( + T = [T|_L], \+ acyclic_term(T) +)). + +test("acyclic_term_16", ( + A = [1|A], X = A, T=a(X, A), \+ acyclic_term(T) +)). + +test("acyclic_term_17", ( + T = [_A| [[[[L|T]|[]]]]], acyclic_term(L) +)). + +test("acyclic_term_18", ( + T = [A| [[[[_L|T]|[]]]]], acyclic_term(A) +)). + +test("acyclic_term_19", ( + T = [_A| [[[[_L|T]|[]]]]], \+ acyclic_term(T) +)). + +test("acyclic_term_20", ( + A = [_C|_B], X = A, T=a(t(X,A), A), acyclic_term(T) +)). + +test("acyclic_term_21", ( + X = [a | Rest], Rest = [_Y | Rest], \+ acyclic_term(X) +)). + +test("acyclic_term_22", ( + _X = [a | Rest], Rest = [_Y | Rest], \+ acyclic_term(Rest) +)). + +test("acyclic_term_23", ( + T = [[_A, T]], G = [1|T], \+ acyclic_term(G) +)). + +test("acyclic_term_24", ( + T = [[_A, T]], \+ acyclic_term(T) +)). + +test("acyclic_term_25", ( + T = [[_, _], T], \+ acyclic_term(T) +)). + +test("acyclic_term_26", ( + T = [[T, _], 1], \+ acyclic_term(T) +)). + +test("acyclic_term#2111_1", ( + term1(A), \+ acyclic_term(A) +)). + +test("acyclic_term#2111_2", ( + term2(A), \+ acyclic_term(A) +)). + +test("acyclic_term#2111_3", ( + term3(A), \+ acyclic_term(A) +)). + +test("acyclic_term#2113", ( + A=[]*B,B=[]*B, \+ acyclic_term(A) +)). + +test("acyclic_term#2114", ( + A=B*B, acyclic_term(A) +)). + +test("acyclic_term_27", ( + T = str(A,A), acyclic_term(T) +)). + +test("acyclic_term_28", ( + T = str(A,A,A), acyclic_term(T) +)). + +main :- + findall(test(Name, Goal), test(Name, Goal), Tests), + run_tests(Tests, Failed), + show_failed(Failed), + halt. + +main_quiet :- + findall(test(Name, Goal), test(Name, Goal), Tests), + run_tests_quiet(Tests, Failed), + ( Failed = [] -> + format("All tests passed", []) + ; format("Some tests failed: ~w~n", [Failed]) + ), + halt. + +run_tests([], []). +run_tests([test(Name, Goal)|Tests], Failed) :- + format("Running test \"~s\"~n", [Name]), + ( call(Goal) -> + Failed = Failed1 + ; format("Failed test \"~s\"~n", [Name]), + Failed = [Name|Failed1] + ), + run_tests(Tests, Failed1). + +run_tests_quiet([], []). +run_tests_quiet([test(Name, Goal)|Tests], Failed) :- + ( call(Goal) -> + Failed = Failed1 + ; Failed = [Name|Failed1] + ), + run_tests_quiet(Tests, Failed1). diff --git a/tests/scryer/src_tests.rs b/tests/scryer/src_tests.rs index edc43d32..05d24c9e 100644 --- a/tests/scryer/src_tests.rs +++ b/tests/scryer/src_tests.rs @@ -102,3 +102,12 @@ fn term_variables_tests() { "All tests passed", ); } + +#[test] +fn acyclic_term_tests() { + run_top_level_test_with_args( + &["src/tests/acyclic_term.pl", "-f", "-g", "main_quiet"], + "", + "All tests passed", + ); +} From 7875b96956890d268af7b95b0b24ecdc9ea2b2fd Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 14 Oct 2023 23:56:46 -0600 Subject: [PATCH 42/46] simplify stackless iterator is_cyclic (#2111) --- src/machine/gc.rs | 9 ++------- src/tests/acyclic_term.pl | 29 +++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/machine/gc.rs b/src/machine/gc.rs index d7837395..4b5327c3 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -272,14 +272,9 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { #[inline] fn is_cyclic(&self, var_current: usize, var_next: usize) -> bool { if self.heap[var_next].is_var() { - var_current != var_next && self.current + 1 != var_current - } else if self.heap[var_next].is_ref() { - // the cell var_next in the second branch contains its original - // value whether var_next is marked or unmarked, meaning the - // is_compound check is well-founded in either case. - self.heap[var_next].get_forwarding_bit() + !self.heap[var_next].get_forwarding_bit() && var_current != var_next } else { - false + self.heap[var_next].is_ref() } } diff --git a/src/tests/acyclic_term.pl b/src/tests/acyclic_term.pl index a7354a3f..7a4676ee 100644 --- a/src/tests/acyclic_term.pl +++ b/src/tests/acyclic_term.pl @@ -15,6 +15,10 @@ term3(A) :- D=[C|_E], A=[C|D]. +term4(A) :- + A=[B|C], + C=[C|B]. + test("acyclic_term_1", ( L = [_Y,[M,B],B|M], acyclic_term(L) )). @@ -123,6 +127,19 @@ test("acyclic_term_26", ( T = [[T, _], 1], \+ acyclic_term(T) )). +test("acyclic_term_27", ( + T = str(A,A), acyclic_term(T) +)). + +test("acyclic_term_28", ( + T = str(A,A,A), acyclic_term(T) +)). + +test("acyclic_term_29", ( + A = s(B, d(Y)), Y = B, acyclic_term(A), + acyclic_term(B), acyclic_term(Y) +)). + test("acyclic_term#2111_1", ( term1(A), \+ acyclic_term(A) )). @@ -135,6 +152,10 @@ test("acyclic_term#2111_3", ( term3(A), \+ acyclic_term(A) )). +test("acyclic_term#2111_4", ( + term4(A), \+ acyclic_term(A) +)). + test("acyclic_term#2113", ( A=[]*B,B=[]*B, \+ acyclic_term(A) )). @@ -143,14 +164,6 @@ test("acyclic_term#2114", ( A=B*B, acyclic_term(A) )). -test("acyclic_term_27", ( - T = str(A,A), acyclic_term(T) -)). - -test("acyclic_term_28", ( - T = str(A,A,A), acyclic_term(T) -)). - main :- findall(test(Name, Goal), test(Name, Goal), Tests), run_tests(Tests, Failed), From 3fb2e451a21d33be8c7ba7a88a4ba661e787457e Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 15 Oct 2023 00:58:15 -0600 Subject: [PATCH 43/46] improve cycle detection in detect_list_cycles (#2111) --- src/machine/gc.rs | 2 +- src/tests/acyclic_term.pl | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 4b5327c3..7958cf67 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -213,7 +213,7 @@ impl<'a> StacklessPreOrderHeapIter<'a, CycleDetectorUMP> { } if temp == self.start { - break; + return self.heap[temp].get_value() == self.current as u64; } } diff --git a/src/tests/acyclic_term.pl b/src/tests/acyclic_term.pl index 7a4676ee..fd8ad229 100644 --- a/src/tests/acyclic_term.pl +++ b/src/tests/acyclic_term.pl @@ -19,6 +19,11 @@ term4(A) :- A=[B|C], C=[C|B]. +term5(A) :- + A=[_B|C], + D=[_E|C], + A=[C|D]. + test("acyclic_term_1", ( L = [_Y,[M,B],B|M], acyclic_term(L) )). @@ -156,6 +161,10 @@ test("acyclic_term#2111_4", ( term4(A), \+ acyclic_term(A) )). +test("acyclic_term#2111_5", ( + term5(A), \+ acyclic_term(A) +)). + test("acyclic_term#2113", ( A=[]*B,B=[]*B, \+ acyclic_term(A) )). @@ -175,7 +184,7 @@ main_quiet :- run_tests_quiet(Tests, Failed), ( Failed = [] -> format("All tests passed", []) - ; format("Some tests failed: ~w~n", [Failed]) + ; format("Some tests failed", []) ), halt. From 3a6aee72a3dd0084b08d0b30d15d5bc3a05907a2 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 15 Oct 2023 01:28:42 -0600 Subject: [PATCH 44/46] correct is_cyclic again for non-variable ref cells (#2116) --- src/machine/gc.rs | 4 +++- src/tests/acyclic_term.pl | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 7958cf67..25f30a00 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -273,8 +273,10 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { fn is_cyclic(&self, var_current: usize, var_next: usize) -> bool { if self.heap[var_next].is_var() { !self.heap[var_next].get_forwarding_bit() && var_current != var_next + } else if self.heap[var_next].is_ref() { + self.heap[var_next].get_mark_bit() } else { - self.heap[var_next].is_ref() + false } } diff --git a/src/tests/acyclic_term.pl b/src/tests/acyclic_term.pl index fd8ad229..cbbf2466 100644 --- a/src/tests/acyclic_term.pl +++ b/src/tests/acyclic_term.pl @@ -173,6 +173,10 @@ test("acyclic_term#2114", ( A=B*B, acyclic_term(A) )). +test("acyclic_term#2116", ( + A=B*B,B=[]*[], acyclic_term(A) +)). + main :- findall(test(Name, Goal), test(Name, Goal), Tests), run_tests(Tests, Failed), From e2000859b670889d63d8d3525ac3ef1ff11877bc Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 15 Oct 2023 13:07:41 -0600 Subject: [PATCH 45/46] add backward looking cyclicity check for variables in cycle detecting stackless iterator (#2111, #2117) --- src/machine/gc.rs | 27 ++++++++++++++++++++------- src/tests/acyclic_term.pl | 14 +++++++++++++- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 25f30a00..5ffd7920 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -12,12 +12,15 @@ 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 var_rooted_cycle(_iter: &mut StacklessPreOrderHeapIter, _var_loc: usize, _next: usize) + where + Self: Sized {} + fn detect_list_tail_cycle(_iter: &mut StacklessPreOrderHeapIter) where Self: Sized {} fn list_head_cycle_detecting_backward( iter: &mut StacklessPreOrderHeapIter, ) -> bool where Self: Sized { iter.backward() } - fn detect_list_tail_cycle(_iter: &mut StacklessPreOrderHeapIter) where Self: Sized {} } pub(crate) struct IteratorUMP { @@ -89,7 +92,7 @@ impl UnmarkPolicy for CycleDetectorUMP { iter: &mut StacklessPreOrderHeapIter, ) -> bool { if !iter.iter_state.cycle_detected && iter.iter_state.mark_phase { - iter.iter_state.cycle_detected = iter.detect_list_cycle(); + iter.iter_state.cycle_detected = iter.detect_list_cycle(iter.current); } iter.backward() @@ -97,7 +100,13 @@ impl UnmarkPolicy for CycleDetectorUMP { fn detect_list_tail_cycle(iter: &mut StacklessPreOrderHeapIter) { if iter.iter_state.mark_phase && !iter.iter_state.cycle_detected { - iter.iter_state.cycle_detected = iter.detect_list_cycle(); + iter.iter_state.cycle_detected = iter.detect_list_cycle(iter.current); + } + } + + fn var_rooted_cycle(iter: &mut StacklessPreOrderHeapIter, var_loc: usize, next: usize) { + if var_loc != next && iter.iter_state.mark_phase && !iter.iter_state.cycle_detected { + iter.iter_state.cycle_detected = iter.detect_list_cycle(next); } } } @@ -200,7 +209,7 @@ impl<'a> StacklessPreOrderHeapIter<'a, CycleDetectorUMP> { self.iter_state.cycle_detected } - pub(crate) fn detect_list_cycle(&self) -> bool { + pub(crate) fn detect_list_cycle(&self, next: usize) -> bool { use crate::machine::system_calls::BrentAlgState; let mut brent_alg_st = BrentAlgState::new(self.current); @@ -208,12 +217,12 @@ impl<'a> StacklessPreOrderHeapIter<'a, CycleDetectorUMP> { 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 { + if brent_alg_st.step(temp).is_some() || temp == next { return true; } if temp == self.start { - return self.heap[temp].get_value() == self.current as u64; + break; } } @@ -272,7 +281,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { #[inline] fn is_cyclic(&self, var_current: usize, var_next: usize) -> bool { if self.heap[var_next].is_var() { - !self.heap[var_next].get_forwarding_bit() && var_current != var_next + self.heap[var_next].get_mark_bit() && var_current != var_next } else if self.heap[var_next].is_ref() { self.heap[var_next].get_mark_bit() } else { @@ -296,6 +305,8 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { } return Some(cell); + } else if self.heap[next as usize].get_mark_bit() == self.iter_state.mark_phase() { + UMP::var_rooted_cycle(self, current, next as usize); } if self.next < self.heap.len() as u64 { @@ -315,6 +326,8 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { } return Some(cell); + } else if self.heap[next as usize].get_mark_bit() == self.iter_state.mark_phase() { + UMP::var_rooted_cycle(self, current, next as usize); } if self.next < self.heap.len() as u64 { diff --git a/src/tests/acyclic_term.pl b/src/tests/acyclic_term.pl index cbbf2466..047a1c42 100644 --- a/src/tests/acyclic_term.pl +++ b/src/tests/acyclic_term.pl @@ -24,6 +24,10 @@ term5(A) :- D=[_E|C], A=[C|D]. +term6(A) :- + A=[B|B], + B=[C|C]. + test("acyclic_term_1", ( L = [_Y,[M,B],B|M], acyclic_term(L) )). @@ -165,6 +169,10 @@ test("acyclic_term#2111_5", ( term5(A), \+ acyclic_term(A) )). +test("acyclic_term#2111_6", ( + term6(A), acyclic_term(A) +)). + test("acyclic_term#2113", ( A=[]*B,B=[]*B, \+ acyclic_term(A) )). @@ -177,6 +185,10 @@ test("acyclic_term#2116", ( A=B*B,B=[]*[], acyclic_term(A) )). +test("acyclic_term#2117", ( + A=[]*A,B=[]*A, \+ acyclic_term(B) +)). + main :- findall(test(Name, Goal), test(Name, Goal), Tests), run_tests(Tests, Failed), @@ -188,7 +200,7 @@ main_quiet :- run_tests_quiet(Tests, Failed), ( Failed = [] -> format("All tests passed", []) - ; format("Some tests failed", []) + ; format("Some tests failed: ~w~n", [Failed]) ), halt. From dff2e7384200b37518bfb0c359618f40c104d1c5 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 15 Oct 2023 15:14:38 -0600 Subject: [PATCH 46/46] more eagerly check for cyclicity of variables in cycle detecting stackless iterator (#2121) --- src/machine/gc.rs | 38 ++++++++++++++++++++++---------------- src/tests/acyclic_term.pl | 12 +++++++++++- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 5ffd7920..9b0f958f 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -12,7 +12,7 @@ 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 var_rooted_cycle(_iter: &mut StacklessPreOrderHeapIter, _var_loc: usize, _next: usize) + fn var_rooted_cycle(_iter: &mut StacklessPreOrderHeapIter, _next: usize) where Self: Sized {} fn detect_list_tail_cycle(_iter: &mut StacklessPreOrderHeapIter) where Self: Sized {} @@ -104,8 +104,8 @@ impl UnmarkPolicy for CycleDetectorUMP { } } - fn var_rooted_cycle(iter: &mut StacklessPreOrderHeapIter, var_loc: usize, next: usize) { - if var_loc != next && iter.iter_state.mark_phase && !iter.iter_state.cycle_detected { + fn var_rooted_cycle(iter: &mut StacklessPreOrderHeapIter, next: usize) { + if iter.current != next && iter.iter_state.mark_phase && !iter.iter_state.cycle_detected { iter.iter_state.cycle_detected = iter.detect_list_cycle(next); } } @@ -279,9 +279,13 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { } #[inline] - fn is_cyclic(&self, var_current: usize, var_next: usize) -> bool { + fn is_cyclic(&self, var_current: usize, var_next: usize, var_f: bool) -> bool { if self.heap[var_next].is_var() { - self.heap[var_next].get_mark_bit() && var_current != var_next + // the third conjunct covers the case where var_current + // was just unforwarded by forward_var() and so + // self.current + 1 == var_current. see acyclic_term#2121 + // & acyclic_term_30 for examples of how this occurs. + self.heap[var_next].get_mark_bit() && var_current != var_next && !var_f } else if self.heap[var_next].is_ref() { self.heap[var_next].get_mark_bit() } else { @@ -298,15 +302,18 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { HeapCellValueTag::AttrVar => { let next = self.next; let current = self.current; + let f = self.heap[self.current].get_forwarding_bit(); + + if self.heap[next as usize].get_mark_bit() == self.iter_state.mark_phase() { + UMP::var_rooted_cycle(self, next as usize); + } if let Some(cell) = UMP::forward_attr_var(self) { - if self.is_cyclic(current, next as usize) { + if self.is_cyclic(current, next as usize, f) { self.iter_state.cycle_detected(); } return Some(cell); - } else if self.heap[next as usize].get_mark_bit() == self.iter_state.mark_phase() { - UMP::var_rooted_cycle(self, current, next as usize); } if self.next < self.heap.len() as u64 { @@ -319,15 +326,18 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { HeapCellValueTag::Var => { let next = self.next; let current = self.current; + let f = self.heap[self.current].get_forwarding_bit(); + + if self.heap[next as usize].get_mark_bit() == self.iter_state.mark_phase() { + UMP::var_rooted_cycle(self, next as usize); + } if let Some(cell) = self.forward_var() { - if self.is_cyclic(current, next as usize) { + if self.is_cyclic(current, next as usize, f) { self.iter_state.cycle_detected(); } return Some(cell); - } else if self.heap[next as usize].get_mark_bit() == self.iter_state.mark_phase() { - UMP::var_rooted_cycle(self, current, next as usize); } if self.next < self.heap.len() as u64 { @@ -462,11 +472,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { } } } else { - if self.heap[self.current].get_tag() == HeapCellValueTag::Lis { - if UMP::list_head_cycle_detecting_backward(self) { - return None; - } - } else if self.backward() { + if self.backward() { return None; } } diff --git a/src/tests/acyclic_term.pl b/src/tests/acyclic_term.pl index 047a1c42..e340d09c 100644 --- a/src/tests/acyclic_term.pl +++ b/src/tests/acyclic_term.pl @@ -149,6 +149,11 @@ test("acyclic_term_29", ( acyclic_term(B), acyclic_term(Y) )). +test("acyclic_term_30", ( + A=str(B,B,B), C=str(A,_D,B), acyclic_term(C), + acyclic_term(A), acyclic_term(B) +)). + test("acyclic_term#2111_1", ( term1(A), \+ acyclic_term(A) )). @@ -189,6 +194,11 @@ test("acyclic_term#2117", ( A=[]*A,B=[]*A, \+ acyclic_term(B) )). +test("acyclic_term#2121", ( + A=B*B, C=A*B, acyclic_term(C), + acyclic_term(A), acyclic_term(B) +)). + main :- findall(test(Name, Goal), test(Name, Goal), Tests), run_tests(Tests, Failed), @@ -200,7 +210,7 @@ main_quiet :- run_tests_quiet(Tests, Failed), ( Failed = [] -> format("All tests passed", []) - ; format("Some tests failed: ~w~n", [Failed]) + ; format("Some tests failed", []) ), halt.