optimizations up to section 5.8
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1,6 +1,6 @@
|
|||||||
[root]
|
[root]
|
||||||
name = "rusty-wam"
|
name = "rusty-wam"
|
||||||
version = "0.5.5"
|
version = "0.5.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lalrpop 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"lalrpop-util 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rusty-wam"
|
name = "rusty-wam"
|
||||||
version = "0.5.6"
|
version = "0.5.7"
|
||||||
authors = ["Mark Thom"]
|
authors = ["Mark Thom"]
|
||||||
|
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|||||||
@@ -11,9 +11,14 @@ pure Prolog.
|
|||||||
Pure Prolog is implemented as a simple REPL. "Pure Prolog" is 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
|
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
|
kind. In terms of the tutorial pacing, the work has progressed to the
|
||||||
to the end of section 5.6, skipping past 5.4. Atoms and lists are the
|
to the end of section 5.7, skipping past 5.4. Atoms and lists are the
|
||||||
only two data types currently supported.
|
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
|
||||||
|
fine-grained control over the alignment and allocation of stack
|
||||||
|
frames.
|
||||||
|
|
||||||
## Tutorial
|
## Tutorial
|
||||||
To enter a multi-clause predicate, the brackets ":{" and "}:" are used
|
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 entirely contained with their own lines.
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ pub enum ChoiceInstruction {
|
|||||||
|
|
||||||
pub enum ControlInstruction {
|
pub enum ControlInstruction {
|
||||||
Allocate(usize),
|
Allocate(usize),
|
||||||
Call(Atom, usize),
|
Call(Atom, usize, usize),
|
||||||
Deallocate,
|
Deallocate,
|
||||||
Execute(Atom, usize),
|
Execute(Atom, usize),
|
||||||
Proceed
|
Proceed
|
||||||
|
|||||||
@@ -126,7 +126,6 @@ impl<'a> CompilationTarget<'a> for QueryInstruction {
|
|||||||
struct TermMarker<'a> {
|
struct TermMarker<'a> {
|
||||||
bindings: HashMap<&'a Var, VarReg>,
|
bindings: HashMap<&'a Var, VarReg>,
|
||||||
arg_c: usize,
|
arg_c: usize,
|
||||||
perm_c: usize,
|
|
||||||
temp_c: usize
|
temp_c: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,13 +133,11 @@ impl<'a> TermMarker<'a> {
|
|||||||
fn new() -> TermMarker<'a> {
|
fn new() -> TermMarker<'a> {
|
||||||
TermMarker { bindings: HashMap::new(),
|
TermMarker { bindings: HashMap::new(),
|
||||||
arg_c: 1,
|
arg_c: 1,
|
||||||
perm_c: 1,
|
|
||||||
temp_c: 1 }
|
temp_c: 1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
self.bindings.clear();
|
self.bindings.clear();
|
||||||
self.perm_c = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contains_var(&self, var: &'a Var) -> bool {
|
fn contains_var(&self, var: &'a Var) -> bool {
|
||||||
@@ -160,26 +157,17 @@ impl<'a> TermMarker<'a> {
|
|||||||
|
|
||||||
if reg_type.reg_num() == 0 {
|
if reg_type.reg_num() == 0 {
|
||||||
match lvl {
|
match lvl {
|
||||||
Level::Deep if reg_type.is_perm() => {
|
Level::Deep if !reg_type.is_perm() => {
|
||||||
let perm = self.perm_c;
|
|
||||||
self.perm_c += 1;
|
|
||||||
cell.set(RegType::Perm(perm));
|
|
||||||
},
|
|
||||||
Level::Deep => {
|
|
||||||
let temp = self.temp_c;
|
let temp = self.temp_c;
|
||||||
self.temp_c += 1;
|
self.temp_c += 1;
|
||||||
cell.set(RegType::Temp(temp));
|
cell.set(RegType::Temp(temp));
|
||||||
},
|
},
|
||||||
Level::Shallow if reg_type.is_perm() => {
|
Level::Shallow if !reg_type.is_perm() => {
|
||||||
let arg = self.arg_c;
|
|
||||||
self.arg_c += 1;
|
|
||||||
cell.set(RegType::Perm(arg));
|
|
||||||
},
|
|
||||||
Level::Shallow => {
|
|
||||||
let arg = self.arg_c;
|
let arg = self.arg_c;
|
||||||
self.arg_c += 1;
|
self.arg_c += 1;
|
||||||
cell.set(RegType::Temp(arg));
|
cell.set(RegType::Temp(arg));
|
||||||
}
|
},
|
||||||
|
_ => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,14 +191,12 @@ impl<'a> TermMarker<'a> {
|
|||||||
|
|
||||||
fn mark_new_var(&mut self, lvl: Level, var: &'a Var, reg: RegType) -> VarReg
|
fn mark_new_var(&mut self, lvl: Level, var: &'a Var, reg: RegType) -> VarReg
|
||||||
{
|
{
|
||||||
let inner_reg = if reg.is_perm() {
|
let inner_reg = if !reg.is_perm() {
|
||||||
let perm = self.perm_c;
|
|
||||||
self.perm_c += 1;
|
|
||||||
RegType::Perm(perm)
|
|
||||||
} else {
|
|
||||||
let temp = self.temp_c;
|
let temp = self.temp_c;
|
||||||
self.temp_c += 1;
|
self.temp_c += 1;
|
||||||
RegType::Temp(temp)
|
RegType::Temp(temp)
|
||||||
|
} else {
|
||||||
|
reg
|
||||||
};
|
};
|
||||||
|
|
||||||
let reg = match lvl {
|
let reg = match lvl {
|
||||||
@@ -259,15 +245,15 @@ impl<'a> TermMarker<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
enum TermStatus {
|
enum VarStatus {
|
||||||
New, Old, Recurrent
|
New, Old, Permanent(usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CodeGenerator<'a> {
|
pub struct CodeGenerator<'a> {
|
||||||
marker: TermMarker<'a>
|
marker: TermMarker<'a>
|
||||||
}
|
}
|
||||||
|
|
||||||
type VariableFixture<'a> = (TermStatus, Vec<&'a Cell<VarReg>>);
|
type VariableFixture<'a> = (VarStatus, Vec<&'a Cell<VarReg>>);
|
||||||
type VariableFixtures<'a> = HashMap<&'a Var, VariableFixture<'a>>;
|
type VariableFixtures<'a> = HashMap<&'a Var, VariableFixture<'a>>;
|
||||||
|
|
||||||
impl<'a> CodeGenerator<'a> {
|
impl<'a> CodeGenerator<'a> {
|
||||||
@@ -472,77 +458,109 @@ impl<'a> CodeGenerator<'a> {
|
|||||||
target
|
target
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_vars_in_term<Iter>(iter: Iter, vs: &mut VariableFixtures<'a>)
|
fn mark_vars_in_term<Iter>(iter: Iter, vs: &mut VariableFixtures<'a>, i: usize)
|
||||||
where Iter : Iterator<Item=TermRef<'a>>
|
where Iter : Iterator<Item=TermRef<'a>>
|
||||||
{
|
{
|
||||||
for term in iter {
|
for term in iter {
|
||||||
if let TermRef::Var(_, reg_cell, var) = term {
|
if let TermRef::Var(_, reg_cell, var) = term {
|
||||||
let mut status = vs.entry(var)
|
let mut status = vs.entry(var)
|
||||||
.or_insert((TermStatus::New, Vec::new()));
|
.or_insert((VarStatus::New, Vec::new()));
|
||||||
|
|
||||||
status.1.push(reg_cell);
|
status.1.push(reg_cell);
|
||||||
|
|
||||||
match status.0 {
|
match status.0 {
|
||||||
TermStatus::Old => status.0 = TermStatus::Recurrent,
|
VarStatus::Old =>
|
||||||
|
status.0 = VarStatus::Permanent(i),
|
||||||
|
VarStatus::Permanent(_) =>
|
||||||
|
status.0 = VarStatus::Permanent(i),
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for &mut (ref mut term_status, ref mut cb) in vs.values_mut() {
|
for &mut (ref mut term_status, _) in vs.values_mut() {
|
||||||
match *term_status {
|
match *term_status {
|
||||||
TermStatus::New => *term_status = TermStatus::Old,
|
VarStatus::New =>
|
||||||
TermStatus::Recurrent => {
|
*term_status = VarStatus::Old,
|
||||||
for cell_reg in cb.drain(0..) {
|
|
||||||
cell_reg.set(VarReg::Norm(RegType::Perm(0)));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_perm_vals(vs: &VariableFixtures) {
|
||||||
|
let mut values_vec : Vec<_> = vs.values()
|
||||||
|
.map(|ref v| (v.0, &v.1))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
values_vec.sort_by_key(|ref v| {
|
||||||
|
match v.0 {
|
||||||
|
VarStatus::Permanent(i) => i,
|
||||||
|
_ => usize::min_value()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (i, v) in values_vec.into_iter().rev().enumerate() {
|
||||||
|
if let VarStatus::Permanent(_) = v.0 {
|
||||||
|
for cell in v.1 {
|
||||||
|
cell.set(VarReg::Norm(RegType::Perm(i + 1)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn mark_perm_vars(rule: &'a Rule) -> VariableFixtures {
|
fn mark_perm_vars(rule: &'a Rule) -> VariableFixtures {
|
||||||
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
||||||
let mut vfs = HashMap::new();
|
let mut vs = HashMap::new();
|
||||||
|
|
||||||
let iter = p0.breadth_first_iter().chain(p1.breadth_first_iter());
|
let iter = p0.breadth_first_iter().chain(p1.breadth_first_iter());
|
||||||
|
|
||||||
Self::mark_vars_in_term(iter, &mut vfs);
|
Self::mark_vars_in_term(iter, &mut vs, 0);
|
||||||
|
|
||||||
for term in clauses {
|
for (i, term) in clauses.iter().enumerate() {
|
||||||
Self::mark_vars_in_term(term.breadth_first_iter(), &mut vfs);
|
Self::mark_vars_in_term(term.breadth_first_iter(), &mut vs, i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
vfs
|
Self::set_perm_vals(&vs);
|
||||||
|
|
||||||
|
vs
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_conditional_call(compiled_query: &mut Code, term: &Term)
|
fn add_conditional_call(compiled_query: &mut Code, term: &Term, pvs: usize)
|
||||||
{
|
{
|
||||||
match term {
|
match term {
|
||||||
&Term::Constant(_, Constant::Atom(ref atom)) => {
|
&Term::Constant(_, Constant::Atom(ref atom)) => {
|
||||||
let call = ControlInstruction::Call(atom.clone(), 0);
|
let call = ControlInstruction::Call(atom.clone(), 0, pvs);
|
||||||
compiled_query.push(Line::Control(call));
|
compiled_query.push(Line::Control(call));
|
||||||
},
|
},
|
||||||
&Term::Clause(_, ref atom, ref terms) => {
|
&Term::Clause(_, ref atom, ref terms) => {
|
||||||
let call = ControlInstruction::Call(atom.clone(), terms.len());
|
let call = ControlInstruction::Call(atom.clone(), terms.len(), pvs);
|
||||||
compiled_query.push(Line::Control(call));
|
compiled_query.push(Line::Control(call));
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn vars_above_threshold(vs: &VariableFixtures, index: usize) -> usize {
|
||||||
|
let mut var_count = 0;
|
||||||
|
|
||||||
|
for &(term_status, _) in vs.values() {
|
||||||
|
if let VarStatus::Permanent(i) = term_status {
|
||||||
|
if i >= index {
|
||||||
|
var_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var_count
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compile_rule(&mut self, rule: &'a Rule) -> Code {
|
pub fn compile_rule(&mut self, rule: &'a Rule) -> Code {
|
||||||
let vfs = Self::mark_perm_vars(&rule);
|
let vs = Self::mark_perm_vars(&rule);
|
||||||
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
||||||
let mut perm_vars = 0;
|
|
||||||
|
|
||||||
for &(term_status, _) in vfs.values() {
|
|
||||||
if let TermStatus::Recurrent = term_status {
|
|
||||||
perm_vars += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let perm_vars = Self::vars_above_threshold(&vs, 1);
|
||||||
let mut body = Vec::new();
|
let mut body = Vec::new();
|
||||||
|
|
||||||
if clauses.len() > 0 {
|
if clauses.len() > 0 {
|
||||||
@@ -555,15 +573,19 @@ impl<'a> CodeGenerator<'a> {
|
|||||||
self.marker.advance_at_head(p1);
|
self.marker.advance_at_head(p1);
|
||||||
body.push(Line::Query(self.compile_target(p1, false)));
|
body.push(Line::Query(self.compile_target(p1, false)));
|
||||||
|
|
||||||
Self::add_conditional_call(&mut body, p1);
|
Self::add_conditional_call(&mut body, p1, perm_vars);
|
||||||
|
|
||||||
body = clauses.iter()
|
body = clauses.iter().enumerate()
|
||||||
.map(|ref term| self.compile_internal_query(term))
|
.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| {
|
.fold(body, |mut body, ref mut cqs| {
|
||||||
body.append(cqs);
|
body.append(cqs);
|
||||||
body
|
body
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// now perform LCO.
|
||||||
let last_arity = rule.last_clause().arity();
|
let last_arity = rule.last_clause().arity();
|
||||||
let mut dealloc_index = body.len() - 1;
|
let mut dealloc_index = body.len() - 1;
|
||||||
|
|
||||||
@@ -594,11 +616,11 @@ impl<'a> CodeGenerator<'a> {
|
|||||||
compiled_fact
|
compiled_fact
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_internal_query(&mut self, term: &'a Term) -> Code {
|
fn compile_internal_query(&mut self, term: &'a Term, index: usize) -> Code {
|
||||||
self.marker.advance(term);
|
self.marker.advance(term);
|
||||||
|
|
||||||
let mut compiled_query = vec![Line::Query(self.compile_target(term, false))];
|
let mut compiled_query = vec![Line::Query(self.compile_target(term, false))];
|
||||||
Self::add_conditional_call(&mut compiled_query, term);
|
Self::add_conditional_call(&mut compiled_query, term, index);
|
||||||
|
|
||||||
compiled_query
|
compiled_query
|
||||||
}
|
}
|
||||||
@@ -607,7 +629,7 @@ impl<'a> CodeGenerator<'a> {
|
|||||||
self.marker.advance(term);
|
self.marker.advance(term);
|
||||||
|
|
||||||
let mut compiled_query = vec![Line::Query(self.compile_target(term, true))];
|
let mut compiled_query = vec![Line::Query(self.compile_target(term, true))];
|
||||||
Self::add_conditional_call(&mut compiled_query, term);
|
Self::add_conditional_call(&mut compiled_query, term, 1);
|
||||||
|
|
||||||
compiled_query
|
compiled_query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ impl fmt::Display for ControlInstruction {
|
|||||||
match self {
|
match self {
|
||||||
&ControlInstruction::Allocate(num_cells) =>
|
&ControlInstruction::Allocate(num_cells) =>
|
||||||
write!(f, "allocate {}", num_cells),
|
write!(f, "allocate {}", num_cells),
|
||||||
&ControlInstruction::Call(ref name, ref arity) =>
|
&ControlInstruction::Call(ref name, arity, pvs) =>
|
||||||
write!(f, "call {}/{}", name, arity),
|
write!(f, "call {}/{}, {}", name, arity, pvs),
|
||||||
&ControlInstruction::Deallocate =>
|
&ControlInstruction::Deallocate =>
|
||||||
write!(f, "deallocate"),
|
write!(f, "deallocate"),
|
||||||
&ControlInstruction::Execute(ref name, arity) =>
|
&ControlInstruction::Execute(ref name, arity) =>
|
||||||
|
|||||||
@@ -666,7 +666,7 @@ impl MachineState {
|
|||||||
self.e = self.and_stack.len() - 1;
|
self.e = self.and_stack.len() - 1;
|
||||||
self.p += 1;
|
self.p += 1;
|
||||||
},
|
},
|
||||||
&ControlInstruction::Call(ref name, arity) => {
|
&ControlInstruction::Call(ref name, arity, _) => {
|
||||||
let compiled_tl_index = code_dir.get(&(name.clone(), arity))
|
let compiled_tl_index = code_dir.get(&(name.clone(), arity))
|
||||||
.map(|index| *index);
|
.map(|index| *index);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user