optimized up to section 5.10

This commit is contained in:
Mark Thom
2017-03-25 01:58:54 -06:00
parent 24b60eb2c6
commit d65970ee57
8 changed files with 321 additions and 76 deletions

2
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
[root]
name = "rusty-wam"
version = "0.5.7"
version = "0.5.10"
dependencies = [
"lalrpop 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)",
"lalrpop-util 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@@ -1,6 +1,6 @@
[package]
name = "rusty-wam"
version = "0.5.7"
version = "0.5.10"
authors = ["Mark Thom"]
build = "build.rs"

View File

@@ -11,8 +11,8 @@ pure Prolog.
Pure Prolog is implemented as a simple REPL. "Pure Prolog" is Prolog
without cut, meta- or extra-logical operators, or side effects of any
kind. In terms of the tutorial pacing, the work has progressed to the
to the end of section 5.7, skipping past 5.4. Atoms and lists are the
only two data types currently supported.
end of section 5.9, skipping past 5.4. Atoms and lists are the only
two data types currently supported.
While proper environment trimming code is emitted by the code
generator, it has no effect on the bytecode WAM, which lacks
@@ -21,7 +21,7 @@ frames.
## Tutorial
To enter a multi-clause predicate, the brackets ":{" and "}:" are used
as delimiters. They must be entirely contained with their own lines.
as delimiters. They must be contained entirely within their own lines.
For example,
```

View File

@@ -90,6 +90,13 @@ mod tests {
assert_eq!(submit(&mut wam, "?- p(f(X, g(Y), Z), g(Z), h).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(Z, Y, X).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(f(X, Y, Z), Y, h).").failed_query(), false);
submit(&mut wam, "p(_, f(_, Y, _)) :- h(Y).");
submit(&mut wam, "h(y).");
assert_eq!(submit(&mut wam, "?- p(_, f(_, Y, _)).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(_, f(_, y, _)).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(_, f(_, z, _)).").failed_query(), true);
}
#[test]
@@ -173,6 +180,18 @@ mod tests {
assert_eq!(submit(&mut wam, "?- p(X, X, X).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(X, Y, X).").failed_query(), false);
assert_eq!(submit(&mut wam, "?- p(f(f(X)), h(f(X)), Y).").failed_query(), true);
submit(&mut wam, "p(X) :- f(Y), g(Y), i(X, Y).");
submit(&mut wam, "g(f(a)). g(f(b)). g(f(c)).");
submit(&mut wam, "f(f(a)). f(f(b)). f(f(c)).");
submit(&mut wam, "i(X, X).");
assert_eq!(submit(&mut wam, "?- p(X).").failed_query(), false);
submit(&mut wam, "p(X) :- f(f(Y)), g(Y, f(Y)), i(X, f(Y)).");
submit(&mut wam, "g(Y, f(Y)) :- g(f(Y)).");
assert_eq!(submit(&mut wam, "?- p(X).").failed_query(), false);
}
#[test]

View File

@@ -1,4 +1,5 @@
use std::cell::Cell;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::ops::{Add, AddAssign};
use std::vec::Vec;
@@ -40,7 +41,7 @@ pub enum Level {
Deep, Shallow
}
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum RegType {
Perm(usize),
Temp(usize)
@@ -141,6 +142,7 @@ pub enum FactInstruction {
GetValue(RegType, usize),
GetVariable(RegType, usize),
UnifyConstant(Constant),
UnifyLocalValue(RegType),
UnifyVariable(RegType),
UnifyValue(RegType),
UnifyVoid(usize)
@@ -150,9 +152,11 @@ pub enum QueryInstruction {
PutConstant(Level, Constant, RegType),
PutList(Level, RegType),
PutStructure(Level, Atom, usize, RegType),
PutUnsafeValue(usize, usize),
PutValue(RegType, usize),
PutVariable(RegType, usize),
SetConstant(Constant),
SetLocalValue(RegType),
SetVariable(RegType),
SetValue(RegType),
SetVoid(usize)
@@ -196,6 +200,7 @@ impl<'a> From<&'a Line> for LineOrCodeOffset<'a> {
pub type Code = Vec<Line>;
#[derive(Clone, PartialEq)]
pub enum Addr {
Con(Constant),
@@ -220,6 +225,13 @@ impl Addr {
_ => None
}
}
pub fn is_protected(&self, e: usize) -> bool {
match self {
&Addr::StackCell(fr, _) if fr > e => false,
_ => true
}
}
}
impl From<Ref> for Addr {
@@ -275,6 +287,26 @@ impl HeapCellValue {
}
}
impl PartialOrd for Ref {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (*self, *other) {
(Ref::HeapCell(hc1), Ref::HeapCell(hc2)) =>
Some(hc1.cmp(&hc2)),
(Ref::HeapCell(_), _) =>
Some(Ordering::Less),
(Ref::StackCell(fr1, sc1), Ref::StackCell(fr2, sc2)) =>
if fr1 < fr2 {
Some(Ordering::Less)
} else if fr1 == fr2 {
Some(sc1.cmp(&sc2))
} else {
Some(Ordering::Greater)
},
_ => Some(Ordering::Greater)
}
}
}
#[derive(Clone, Copy)]
pub enum CodePtr {
DirEntry(usize),

View File

@@ -250,7 +250,8 @@ enum VarStatus {
}
pub struct CodeGenerator<'a> {
marker: TermMarker<'a>
marker: TermMarker<'a>,
var_count: HashMap<&'a Var, usize>
}
type VariableFixture<'a> = (VarStatus, Vec<&'a Cell<VarReg>>);
@@ -258,37 +259,41 @@ type VariableFixtures<'a> = HashMap<&'a Var, VariableFixture<'a>>;
impl<'a> CodeGenerator<'a> {
pub fn new() -> Self {
CodeGenerator { marker: TermMarker::new() }
CodeGenerator { marker: TermMarker::new(),
var_count: HashMap::new() }
}
pub fn vars(&self) -> &HashMap<&Var, VarReg> {
&self.marker.bindings
}
#[allow(dead_code)]
fn count_vars(term: &Term) -> HashMap<&Var, usize> {
fn update_var_count<Iter>(&mut self, iter: Iter)
where Iter : Iterator<Item=TermRef<'a>>
{
let mut var_count = HashMap::new();
for term in term.breadth_first_iter() {
if let TermRef::Var(_, _, ref var) = term {
let entry = var_count.entry(*var).or_insert(0);
for term in iter {
if let TermRef::Var(_, _, var) = term {
if self.marker.contains_var(var) {
var_count.insert(var, 2);
continue;
}
let entry = var_count.entry(var).or_insert(0);
*entry += 1;
}
}
var_count
self.var_count = var_count;
}
#[allow(dead_code)]
fn all_singleton_vars(terms: &Vec<Box<Term>>,
var_count: &HashMap<&Var, usize>)
-> bool
fn all_singleton_vars(&self, terms: &Vec<Box<Term>>) -> bool
{
for term in terms {
match term.as_ref() {
&Term::AnonVar => {},
&Term::Var(ref cell, ref var) if cell.get().is_temp() =>
if var_count.get(var).unwrap() != &1 {
if self.var_count.get(var).unwrap() != &1 {
return false;
},
_ => return false
@@ -298,7 +303,6 @@ impl<'a> CodeGenerator<'a> {
true
}
#[allow(dead_code)]
fn void_subterms<Target>(subterms: usize) -> Target
where Target: CompilationTarget<'a>
{
@@ -416,7 +420,6 @@ impl<'a> CodeGenerator<'a> {
{
let iter = Target::iter(term);
let mut target = Vec::new();
let var_count = Self::count_vars(term);
for term in iter {
match term {
@@ -424,7 +427,7 @@ impl<'a> CodeGenerator<'a> {
target.push(self.to_structure(lvl, cell, atom, terms.len()));
if !has_exposed_vars {
if Self::all_singleton_vars(terms, &var_count) {
if self.all_singleton_vars(terms) {
target.push(Self::void_subterms(terms.len()));
continue;
}
@@ -547,7 +550,7 @@ impl<'a> CodeGenerator<'a> {
for &(term_status, _) in vs.values() {
if let VarStatus::Permanent(i) = term_status {
if i >= index {
if i > index {
var_count += 1;
}
}
@@ -556,36 +559,7 @@ impl<'a> CodeGenerator<'a> {
var_count
}
pub fn compile_rule(&mut self, rule: &'a Rule) -> Code {
let vs = Self::mark_perm_vars(&rule);
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
let perm_vars = Self::vars_above_threshold(&vs, 1);
let mut body = Vec::new();
if clauses.len() > 0 {
body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
}
self.marker.advance(p0);
body.push(Line::Fact(self.compile_target(p0, false)));
self.marker.advance_at_head(p1);
body.push(Line::Query(self.compile_target(p1, false)));
Self::add_conditional_call(&mut body, p1, perm_vars);
body = clauses.iter().enumerate()
.map(|(i, ref term)| {
let num_vars = Self::vars_above_threshold(&vs, i+2);
self.compile_internal_query(term, num_vars)
})
.fold(body, |mut body, ref mut cqs| {
body.append(cqs);
body
});
// now perform LCO.
fn lco(body: &mut Code, rule: &'a Rule) -> usize {
let last_arity = rule.last_clause().arity();
let mut dealloc_index = body.len() - 1;
@@ -599,6 +573,103 @@ impl<'a> CodeGenerator<'a> {
_ => dealloc_index = body.len()
};
dealloc_index
}
fn mark_unsafe_query_vars(head: &Term,
vs: &VariableFixtures,
query: &mut CompiledQuery)
{
let mut unsafe_vars = HashMap::new();
for &(_, ref cb) in vs.values() {
if !cb.is_empty() {
let index = cb.first().unwrap().get().norm();
unsafe_vars.insert(index, false);
}
}
for term_ref in head.breadth_first_iter() {
match term_ref {
TermRef::Var(_, cell, _) => {
unsafe_vars.remove(&cell.get().norm());
},
_ => {}
};
}
for query_instr in query.iter_mut() {
match query_instr {
&mut QueryInstruction::PutValue(RegType::Perm(i), arg) =>
if let Some(found) = unsafe_vars.get_mut(&RegType::Perm(i)) {
if !*found {
*found = true;
*query_instr = QueryInstruction::PutUnsafeValue(i, arg);
}
},
&mut QueryInstruction::SetVariable(reg)
| &mut QueryInstruction::PutVariable(reg, _) =>
if let Some(found) = unsafe_vars.get_mut(&reg) {
*found = true;
},
&mut QueryInstruction::SetValue(reg) =>
if let Some(found) = unsafe_vars.get_mut(&reg) {
if !*found {
*found = true;
*query_instr = QueryInstruction::SetLocalValue(reg);
}
},
_ => {}
};
}
}
pub fn compile_rule(&mut self, rule: &'a Rule) -> Code {
let vs = Self::mark_perm_vars(&rule);
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
let perm_vars = Self::vars_above_threshold(&vs, 0);
let mut body = Vec::new();
if clauses.len() > 0 {
body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
}
let iter = p0.breadth_first_iter().chain(p1.breadth_first_iter());
self.update_var_count(iter);
self.marker.advance(p0);
body.push(Line::Fact(self.compile_target(p0, false)));
self.marker.advance_at_head(p1);
body.push(Line::Query(self.compile_target(p1, false)));
Self::add_conditional_call(&mut body, p1, perm_vars);
body = clauses.iter().enumerate()
.map(|(i, ref term)| {
let num_vars = Self::vars_above_threshold(&vs, i+1);
self.compile_internal_query(term, num_vars)
})
.fold(body, |mut body, ref mut cqs| {
body.append(cqs);
body
});
if clauses.len() > 0 {
let mut index = body.len() - 1;
if let &Line::Control(_) = body.last().unwrap() {
index -= 1;
}
if let &mut Line::Query(ref mut query) = &mut body[index] {
Self::mark_unsafe_query_vars(p0, &vs, query);
}
}
let dealloc_index = Self::lco(&mut body, &rule);
if clauses.len() > 0 {
body.insert(dealloc_index, Line::Control(ControlInstruction::Deallocate));
}
@@ -606,18 +677,51 @@ impl<'a> CodeGenerator<'a> {
body
}
fn mark_unsafe_fact_vars(fact: &mut CompiledFact, bindings: &HashMap<&Var, VarReg>)
{
let mut unsafe_vars = HashMap::new();
for var_reg in bindings.values() {
unsafe_vars.insert(var_reg.norm(), false);
}
for fact_instr in fact.iter_mut() {
match fact_instr {
&mut FactInstruction::UnifyValue(reg) =>
if let Some(found) = unsafe_vars.get_mut(&reg) {
if !*found {
*found = true;
*fact_instr = FactInstruction::UnifyLocalValue(reg);
}
},
&mut FactInstruction::UnifyVariable(reg) => {
if let Some(found) = unsafe_vars.get_mut(&reg) {
*found = true;
}
},
_ => {}
};
}
}
pub fn compile_fact(&mut self, term: &'a Term) -> Code {
self.marker.advance(term);
self.update_var_count(term.breadth_first_iter());
let mut compiled_fact = vec![Line::Fact(self.compile_target(term, false))];
let mut compiled_fact = self.compile_target(term, false);
Self::mark_unsafe_fact_vars(&mut compiled_fact, self.vars());
let mut compiled_fact = vec![Line::Fact(compiled_fact)];
let proceed = Line::Control(ControlInstruction::Proceed);
compiled_fact.push(proceed);
compiled_fact
}
fn compile_internal_query(&mut self, term: &'a Term, index: usize) -> Code {
fn compile_internal_query(&mut self, term: &'a Term, index: usize) -> Code
{
self.marker.advance(term);
self.update_var_count(term.breadth_first_iter());
let mut compiled_query = vec![Line::Query(self.compile_target(term, false))];
Self::add_conditional_call(&mut compiled_query, term, index);
@@ -627,9 +731,10 @@ impl<'a> CodeGenerator<'a> {
pub fn compile_query(&mut self, term: &'a Term) -> Code {
self.marker.advance(term);
self.update_var_count(term.breadth_first_iter());
let mut compiled_query = vec![Line::Query(self.compile_target(term, true))];
Self::add_conditional_call(&mut compiled_query, term, 1);
Self::add_conditional_call(&mut compiled_query, term, 0);
compiled_query
}

View File

@@ -44,6 +44,8 @@ impl fmt::Display for FactInstruction {
write!(f, "unify_constant {}", constant),
&FactInstruction::UnifyVariable(ref r) =>
write!(f, "unify_variable {}", r),
&FactInstruction::UnifyLocalValue(ref r) =>
write!(f, "unify_local_value {}", r),
&FactInstruction::UnifyValue(ref r) =>
write!(f, "unify_value {}", r),
&FactInstruction::UnifyVoid(n) =>
@@ -67,12 +69,16 @@ impl fmt::Display for QueryInstruction {
write!(f, "put_structure {}/{}, {}", name, arity, r),
&QueryInstruction::PutStructure(Level::Shallow, ref name, ref arity, ref r) =>
write!(f, "put_structure {}/{}, A{}", name, arity, r.reg_num()),
&QueryInstruction::PutUnsafeValue(y, a) =>
write!(f, "put_unsafe_value Y{}, A{}", y, a),
&QueryInstruction::PutValue(ref x, ref a) =>
write!(f, "put_value {}, A{}", x, a),
&QueryInstruction::PutVariable(ref x, ref a) =>
write!(f, "put_variable {}, A{}", x, a),
&QueryInstruction::SetConstant(ref constant) =>
write!(f, "set_constant {}", constant),
&QueryInstruction::SetLocalValue(ref r) =>
write!(f, "set_local_value {}", r),
&QueryInstruction::SetVariable(ref r) =>
write!(f, "set_variable {}", r),
&QueryInstruction::SetValue(ref r) =>
@@ -213,6 +219,7 @@ pub fn eval(wam: &mut Machine, buffer: &str) -> EvalResult
&Ok(TopLevel::Predicate(ref clauses)) => {
if is_consistent(clauses) {
let compiled_pred = cg.compile_predicate(clauses);
print_code(&compiled_pred);
wam.add_predicate(clauses, compiled_pred);
EvalResult::EntrySuccess
@@ -226,11 +233,13 @@ Each predicate must have the same name and arity.";
},
&Ok(TopLevel::Fact(ref fact)) => {
let compiled_fact = cg.compile_fact(&fact);
print_code(&compiled_fact);
wam.add_fact(fact, compiled_fact);
EvalResult::EntrySuccess
},
&Ok(TopLevel::Rule(ref rule)) => {
let compiled_rule = cg.compile_rule(&rule);
print_code(&compiled_rule);
wam.add_rule(rule, compiled_rule);
EvalResult::EntrySuccess
},

View File

@@ -463,6 +463,7 @@ impl MachineState {
_ => self.fail = true
};
},
&FactInstruction::GetList(_, reg) => {
let addr = self.deref(self[reg].clone());
@@ -536,10 +537,14 @@ impl MachineState {
let addr = self.deref(Addr::HeapCell(self.s));
match self.store(addr) {
Addr::HeapCell(hc) =>
self.heap[hc] = HeapCellValue::Con(c.clone()),
Addr::StackCell(fr, sc) =>
self.and_stack[fr][sc] = Addr::Con(c.clone()),
Addr::HeapCell(hc) => {
self.heap[hc] = HeapCellValue::Con(c.clone());
self.trail(Ref::HeapCell(hc));
},
Addr::StackCell(fr, sc) => {
self.and_stack[fr][sc] = Addr::Con(c.clone());
self.trail(Ref::StackCell(fr, sc));
},
Addr::Con(c1) => {
if c1 != *c {
self.fail = true;
@@ -569,6 +574,39 @@ impl MachineState {
self.s += 1;
},
&FactInstruction::UnifyLocalValue(reg) => {
let s = self.s;
match self.mode {
MachineMode::Read => {
let reg_addr = self[reg].clone();
self.unify(reg_addr, Addr::HeapCell(s));
},
MachineMode::Write => {
let addr = self.deref(self[reg].clone());
let h = self.h;
if let Addr::HeapCell(hc) = addr {
if hc < h {
let val = self.heap[hc].clone();
self.heap.push(val);
self.h += 1;
self.s += 1;
return;
}
}
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
self.bind(Ref::HeapCell(h), addr);
self.h += 1;
}
};
self.s += 1;
},
&FactInstruction::UnifyValue(reg) => {
let s = self.s;
@@ -615,21 +653,63 @@ impl MachineState {
self[reg] = Addr::Str(self.h);
self.h += 1;
},
&QueryInstruction::PutUnsafeValue(n, arg) => {
let e = self.e;
let addr = self.deref(Addr::StackCell(e, n));
if addr.is_protected(e) {
self.registers[arg] = self.store(addr);
} else {
let h = self.h;
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
self.bind(Ref::HeapCell(h), addr);
self.registers[arg] = self.heap[h].as_addr(h);
self.h += 1;
}
},
&QueryInstruction::PutValue(norm, arg) =>
self.registers[arg] = self[norm].clone(),
&QueryInstruction::PutVariable(norm, arg) => {
let h = self.h;
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
match norm {
RegType::Perm(n) => {
let e = self.e;
self[norm] = Addr::StackCell(e, n);
self.registers[arg] = self[norm].clone();
},
RegType::Temp(_) => {
let h = self.h;
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
self[norm] = Addr::HeapCell(h);
self.registers[arg] = Addr::HeapCell(h);
self[norm] = Addr::HeapCell(h);
self.registers[arg] = Addr::HeapCell(h);
self.h += 1;
self.h += 1;
}
};
},
&QueryInstruction::SetConstant(ref constant) => {
self.heap.push(HeapCellValue::Con(constant.clone()));
self.h += 1;
},
&QueryInstruction::SetLocalValue(reg) => {
let addr = self.deref(self[reg].clone());
let h = self.h;
if let Addr::HeapCell(hc) = addr {
if hc < h {
self.heap.push(HeapCellValue::from(addr));
self.h += 1;
return;
}
}
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));
self.bind(Ref::HeapCell(h), addr);
self.h += 1;
},
&QueryInstruction::SetVariable(reg) => {
let h = self.h;
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(h)));