transition to debray allocation

This commit is contained in:
Mark Thom
2017-04-30 13:53:15 -06:00
parent 1bdc539c25
commit 316e5c5c1a
11 changed files with 440 additions and 100 deletions

2
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
[root]
name = "rusty-wam"
version = "0.5.7"
version = "0.6.0"
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.6.0"
authors = ["Mark Thom"]
build = "build.rs"

View File

@@ -10,14 +10,9 @@ pure Prolog.
Prolog is implemented as a simple REPL. It is without without meta- or
extra-logical operators, or side effects of any kind, with the lone
exception of cut. In terms of the tutorial pacing, the work has
progressed to the end of section 5.11, 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
fine-grained control over the alignment and allocation of stack
frames.
exception of cut. In terms of the tutorial pacing, the work covers in
some form all of the WAM book, including lists, cuts, Debray
allocation, and indexing.
## Tutorial
To enter a multi-clause predicate, the brackets ":{" and "}:" are used

View File

@@ -9,7 +9,7 @@ pub type Atom = String;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum GenContext {
Head, Mid(usize), Last(usize) // Mid/Last: chunk_num
Head, Mid(usize), Last(usize) // Mid & Last: chunk_num
}
pub enum PredicateClause {
@@ -70,7 +70,7 @@ impl RegType {
RegType::Perm(reg_num) | RegType::Temp(reg_num) => reg_num
}
}
pub fn is_perm(self) -> bool {
match self {
RegType::Perm(_) => true,
@@ -91,7 +91,7 @@ impl VarReg {
VarReg::ArgAndNorm(reg, _) | VarReg::Norm(reg) => reg
}
}
pub fn is_temp(self) -> bool {
!self.norm().is_perm()
}
@@ -242,6 +242,7 @@ pub enum FactInstruction {
}
pub enum QueryInstruction {
GetVariable(RegType, usize),
PutConstant(Level, Constant, RegType),
PutList(Level, RegType),
PutStructure(Level, Atom, usize, RegType),
@@ -433,13 +434,6 @@ impl Term {
}
}
pub fn subterms(&self) -> usize {
match self {
&Term::Clause(_, _, ref terms) => terms.len(),
_ => 1
}
}
pub fn name(&self) -> Option<&Atom> {
match self {
&Term::Constant(_, Constant::Atom(ref atom))

View File

@@ -1,8 +1,8 @@
use prolog::ast::*;
use prolog::debray_allocator::*;
use prolog::fixtures::*;
use prolog::indexing::*;
use prolog::iterators::*;
use prolog::naive_allocator::*;
use prolog::targets::*;
use std::cell::Cell;
@@ -69,30 +69,39 @@ impl<'a> CodeGenerator<'a> {
fn to_structure<Target>(&mut self,
lvl: Level,
cell: &'a Cell<RegType>,
term_loc: GenContext,
name: &'a Atom,
arity: usize)
arity: usize,
target: &mut Vec<Target>)
-> Target
where Target: CompilationTarget<'a>
{
self.marker.mark_non_var(lvl, cell);
self.marker.mark_non_var(lvl, term_loc, cell, target);
Target::to_structure(lvl, name.clone(), arity, cell.get())
}
fn to_constant<Target>(&mut self,
lvl: Level,
cell: &'a Cell<RegType>,
constant: &'a Constant)
term_loc: GenContext,
constant: &'a Constant,
target: &mut Vec<Target>)
-> Target
where Target: CompilationTarget<'a>
{
self.marker.mark_non_var(lvl, cell);
self.marker.mark_non_var(lvl, term_loc, cell, target);
Target::to_constant(lvl, constant.clone(), cell.get())
}
fn to_list<Target>(&mut self, lvl: Level, cell: &'a Cell<RegType>) -> Target
fn to_list<Target>(&mut self,
lvl: Level,
term_loc: GenContext,
cell: &'a Cell<RegType>,
target: &mut Vec<Target>)
-> Target
where Target: CompilationTarget<'a>
{
self.marker.mark_non_var(lvl, cell);
self.marker.mark_non_var(lvl, term_loc, cell, target);
Target::to_list(lvl, cell.get())
}
@@ -102,56 +111,15 @@ impl<'a> CodeGenerator<'a> {
Target::constant_subterm(constant.clone())
}
fn anon_var_term<Target>(&mut self, lvl: Level, target: &mut Vec<Target>)
fn non_var_subterm<Target>(&mut self,
lvl: Level,
term_loc: GenContext,
cell: &'a Cell<RegType>,
target: &mut Vec<Target>)
-> Target
where Target: CompilationTarget<'a>
{
let reg = self.marker.mark_anon_var(lvl);
let instr = match reg {
VarReg::ArgAndNorm(arg, norm) =>
Target::argument_to_variable(arg, norm),
VarReg::Norm(norm) =>
Target::subterm_to_variable(norm)
};
target.push(instr);
}
fn var_term<Target>(&mut self,
lvl: Level,
cell: &'a Cell<VarReg>,
var: &'a Var,
_: GenContext,
target: &mut Vec<Target>)
where Target: CompilationTarget<'a>
{
if !self.marker.marked_var(var) {
let reg = self.marker.mark_new_var(lvl, var, cell.get().norm());
cell.set(reg);
match reg {
VarReg::ArgAndNorm(arg, norm) => // if arg.reg_num() != norm =>
target.push(Target::argument_to_variable(arg, norm)),
VarReg::Norm(norm) =>
target.push(Target::subterm_to_variable(norm)),
};
} else {
let reg = self.marker.mark_old_var(lvl, var);
cell.set(reg);
match reg {
VarReg::ArgAndNorm(arg, norm) => // if arg.reg_num() != norm =>
target.push(Target::argument_to_value(arg, norm)),
VarReg::Norm(norm) =>
target.push(Target::subterm_to_value(norm)),
};
}
}
fn non_var_subterm<Target>(&mut self, cell: &'a Cell<RegType>) -> Target
where Target: CompilationTarget<'a>
{
self.marker.mark_non_var(Level::Deep, cell);
self.marker.mark_non_var(lvl, term_loc, cell, target);
Target::clause_arg_to_instr(cell.get())
}
@@ -163,13 +131,15 @@ impl<'a> CodeGenerator<'a> {
{
match subterm {
&Term::AnonVar =>
self.anon_var_term(Level::Deep, target),
&Term::Cons(ref cell, _, _) | &Term::Clause(ref cell, _, _) =>
target.push(self.non_var_subterm(cell)),
self.marker.mark_anon_var(Level::Deep, target),
&Term::Cons(ref cell, _, _) | &Term::Clause(ref cell, _, _) => {
let instr = self.non_var_subterm(Level::Deep, term_loc, cell, target);
target.push(instr);
},
&Term::Constant(_, ref constant) =>
target.push(self.constant_subterm(constant)),
&Term::Var(ref cell, ref var) =>
self.var_term(Level::Deep, cell, var, term_loc, target)
self.marker.mark_var(var, Level::Deep, cell, term_loc, target)
};
}
@@ -186,7 +156,14 @@ impl<'a> CodeGenerator<'a> {
for term in iter {
match term {
TermRef::Clause(lvl, cell, atom, terms) => {
target.push(self.to_structure(lvl, cell, atom, terms.len()));
let str_instr = self.to_structure(lvl,
cell,
term_loc,
atom,
terms.len(),
&mut target);
target.push(str_instr);
if !has_exposed_vars {
if self.all_singleton_vars(terms) {
@@ -200,21 +177,24 @@ impl<'a> CodeGenerator<'a> {
}
},
TermRef::Cons(lvl, cell, head, tail) => {
target.push(self.to_list(lvl, cell));
let list_instr = self.to_list(lvl, term_loc, cell, &mut target);
target.push(list_instr);
self.subterm_to_instr(head, term_loc, &mut target);
self.subterm_to_instr(tail, term_loc, &mut target);
},
TermRef::Constant(lvl @ Level::Shallow, cell, constant) =>
target.push(self.to_constant(lvl, cell, constant)),
TermRef::Constant(lvl @ Level::Shallow, cell, constant) => {
let const_instr = self.to_constant(lvl, cell, term_loc, constant, &mut target);
target.push(const_instr);
},
TermRef::AnonVar(lvl @ Level::Shallow) =>
if has_exposed_vars {
self.anon_var_term(lvl, &mut target);
self.marker.mark_anon_var(lvl, &mut target);
} else {
self.marker.advance_arg();
},
TermRef::Var(lvl @ Level::Shallow, ref cell, ref var) =>
self.var_term(lvl, cell, var, term_loc, &mut target),
self.marker.mark_var(var, lvl, cell, term_loc, &mut target),
_ => {}
};
}
@@ -332,7 +312,7 @@ impl<'a> CodeGenerator<'a> {
body.push(Line::Cut(CutInstruction::NeckCut(term)));
},
&TermOrCut::Term(ref p1) => {
self.marker.advance_at_head(p1);
self.marker.advance(p1);
if p1.is_clause() {
let term_loc = if p1.is_callable() {

View File

@@ -0,0 +1,355 @@
use prolog::ast::*;
use prolog::fixtures::*;
use prolog::targets::*;
use std::cell::Cell;
use std::collections::{BTreeSet, HashMap};
pub struct TermMarker<'a> {
pub bindings: HashMap<&'a Var, VarData>,
arg_c: usize,
temp_lb: usize,
contents: HashMap<usize, &'a Var>,
in_use: BTreeSet<usize>,
}
impl<'a> TermMarker<'a> {
pub fn new() -> TermMarker<'a> {
TermMarker {
arg_c: 1,
temp_lb: 1,
bindings: HashMap::new(),
contents: HashMap::new(),
in_use: BTreeSet::new()
}
}
fn occurs_shallowly_in_head(&self, var: &'a Var, term_loc: GenContext, r: usize) -> bool
{
match (term_loc, self.bindings.get(var).unwrap()) {
(GenContext::Head, &VarData::Temp(_, _, ref tvd)) =>
tvd.use_set.contains(&(GenContext::Head, r)),
_ => false
}
}
pub fn drain_var_data(&mut self, vs: VariableFixtures<'a>) -> VariableFixtures<'a>
{
let mut perm_vs = VariableFixtures::new();
for (var, (var_status, cells)) in vs.into_iter() {
match var_status {
VarStatus::Temp(chunk_num, tvd) => {
self.bindings.insert(var, VarData::Temp(chunk_num, 0, tvd));
},
VarStatus::Perm(_) => {
self.bindings.insert(var, VarData::Perm(0));
perm_vs.insert(var, (var_status, cells));
}
};
}
perm_vs
}
fn alloc_with_cr(&self, var: &'a Var) -> usize
{
match self.bindings.get(var) {
Some(&VarData::Temp(_, _, ref tvd)) => {
for &(_, reg) in tvd.use_set.iter() {
if !self.in_use.contains(&reg) {
return reg;
}
}
let mut result = 0;
for reg in self.temp_lb .. {
if !self.in_use.contains(&reg) {
if !tvd.no_use_set.contains(&reg) {
result = reg;
break;
}
}
}
result
},
_ => 0
}
}
fn alloc_with_ca(&self, var: &'a Var) -> usize
{
match self.bindings.get(var) {
Some(&VarData::Temp(_, _, ref tvd)) => {
for &(_, reg) in tvd.use_set.iter() {
if !self.in_use.contains(&reg) {
return reg;
}
}
let mut result = 0;
for reg in self.temp_lb .. {
if !self.in_use.contains(&reg) {
if !tvd.no_use_set.contains(&reg) {
if !tvd.conflict_set.contains(&reg) {
result = reg;
break;
}
}
}
}
result
},
_ => 0
}
}
fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(&'a Var, usize)>
{
// we want to allocate a register to the k^{th} parameter, par_k.
// par_k may not be a temporary variable.
let k = self.arg_c;
match self.contents.get(&k) {
Some(t_var) => {
// suppose this branch fires. then t_var is a
// temp. var. belonging to the current chunk.
// consider its use set. T == par_k iff
// (GenContext::Last(_), k) is in t_var.use_set.
let tvd = self.bindings.get(t_var).unwrap();
if let &VarData::Temp(_, _, ref tvd) = tvd {
if !tvd.use_set.contains(&(GenContext::Last(chunk_num), k)) {
return Some((t_var, self.alloc_with_ca(t_var)));
}
}
None
},
_ => None
}
}
fn evacuate_arg<Target>(&mut self, chunk_num: usize, target: &mut Vec<Target>)
where Target: CompilationTarget<'a>
{
match self.alloc_in_last_goal_hint(chunk_num) {
Some((var, r)) => {
let k = self.arg_c;
if r != k {
let r = RegType::Temp(r);
if r.reg_num() != k {
target.push(Target::move_to_register(r, k));
self.contents.remove(&k);
self.contents.insert(r.reg_num(), var);
self.record_register(var, r);
self.in_use.insert(r.reg_num());
}
}
},
_ => {}
};
}
fn alloc_reg_to_var<Target>(&mut self,
var: &'a Var,
lvl: Level,
term_loc: GenContext,
target: &mut Vec<Target>)
-> usize
where Target: CompilationTarget<'a>
{
match term_loc {
GenContext::Head =>
if let Level::Shallow = lvl {
self.alloc_with_cr(var)
} else {
self.alloc_with_ca(var)
},
GenContext::Mid(_) =>
self.alloc_with_ca(var),
GenContext::Last(chunk_num) =>
if let Level::Shallow = lvl {
self.evacuate_arg(chunk_num, target);
self.alloc_with_cr(var)
} else {
self.alloc_with_ca(var)
}
}
}
fn get(&self, var: &'a Var) -> RegType {
self.bindings.get(var).unwrap().as_reg_type()
}
fn in_place(&self, var: &'a Var, term_loc: GenContext, r: RegType, k: usize) -> bool
{
match term_loc {
GenContext::Head if !r.is_perm() => r.reg_num() == k,
_ => match self.bindings.get(var).unwrap() {
&VarData::Temp(_, o, _) if r.reg_num() == k => o == k,
_ => false
}
}
}
// delete after anon_vars are handled by means of *_void stuff.
pub fn mark_anon_var<Target>(&mut self, lvl: Level, target: &mut Vec<Target>)
where Target: CompilationTarget<'a>
{
let r = RegType::Temp(self.alloc_reg_to_non_var());
match lvl {
Level::Shallow => {
let k = self.arg_c;
self.arg_c += 1;
target.push(Target::argument_to_variable(r, k));
},
Level::Deep =>
target.push(Target::subterm_to_variable(r))
};
}
fn alloc_reg_to_non_var(&mut self) -> usize
{
let mut final_index = 0;
for index in self.temp_lb .. {
if !self.in_use.contains(&index) {
final_index = index;
break;
}
}
self.temp_lb = final_index + 1;
final_index
}
pub fn mark_non_var<Target>(&mut self,
lvl: Level,
term_loc: GenContext,
cell: &Cell<RegType>,
target: &mut Vec<Target>)
where Target: CompilationTarget<'a>
{
let r = cell.get();
if r.reg_num() == 0 {
let r = match lvl {
Level::Shallow => {
let k = self.arg_c;
if let GenContext::Last(chunk_num) = term_loc {
self.evacuate_arg(chunk_num, target);
}
self.arg_c += 1;
RegType::Temp(k)
},
_ => RegType::Temp(self.alloc_reg_to_non_var())
};
cell.set(r);
}
}
pub fn mark_var<Target>(&mut self,
var: &'a Var,
lvl: Level,
cell: &'a Cell<VarReg>,
term_loc: GenContext,
target: &mut Vec<Target>)
where Target: CompilationTarget<'a>
{
let (r, is_new_var) = match self.get(var) {
RegType::Temp(0) => {
// here, r is temporary *and* unassigned.
let o = self.alloc_reg_to_var(var, lvl, term_loc, target);
cell.set(VarReg::Norm(RegType::Temp(o)));
(RegType::Temp(o), true)
},
RegType::Perm(0) => {
let pr = cell.get().norm();
self.record_register(var, pr);
(pr, true)
},
r => (r, false)
};
match lvl {
Level::Shallow => {
let k = self.arg_c;
self.arg_c += 1;
cell.set(VarReg::ArgAndNorm(r, k));
if !self.in_place(var, term_loc, r, k) {
if is_new_var {
target.push(Target::argument_to_variable(r, k));
} else {
target.push(Target::argument_to_value(r, k));
}
}
},
Level::Deep if is_new_var =>
if self.occurs_shallowly_in_head(var, term_loc, r.reg_num()) {
target.push(Target::subterm_to_value(r));
} else {
target.push(Target::subterm_to_variable(r));
},
Level::Deep =>
target.push(Target::subterm_to_value(r))
};
if !r.is_perm() {
let o = r.reg_num();
self.contents.insert(o, var);
self.record_register(var, r);
self.in_use.insert(o);
}
}
pub fn contains_var(&self, var: &'a Var) -> bool {
self.bindings.contains_key(var)
}
fn record_register(&mut self, var: &'a Var, r: RegType) {
match self.bindings.get_mut(var).unwrap() {
&mut VarData::Temp(_, ref mut s, _) => *s = r.reg_num(),
&mut VarData::Perm(ref mut s) => *s = r.reg_num()
}
}
pub fn advance_arg(&mut self) {
self.arg_c += 1;
}
pub fn advance(&mut self, term: &'a Term) {
self.arg_c = 1;
self.temp_lb = term.arity() + 1;
}
pub fn reset(&mut self) {
self.bindings.clear();
self.contents.clear();
self.in_use.clear();
}
pub fn reset_contents(&mut self) {
self.contents.clear();
self.in_use.clear();
}
}

View File

@@ -10,14 +10,9 @@ pub type OccurrenceSet = BTreeSet<(GenContext, usize)>;
pub struct TempVarData {
last_term_arity: usize,
use_set: OccurrenceSet,
no_use_set: BTreeSet<usize>,
conflict_set: BTreeSet<usize>
}
// labeled with chunk_num and temp offset (unassigned if 0).
pub enum VarData {
Perm(usize), Temp(usize, usize, TempVarData)
pub use_set: OccurrenceSet,
pub no_use_set: BTreeSet<usize>,
pub conflict_set: BTreeSet<usize>
}
// labeled with chunk numbers.
@@ -25,6 +20,12 @@ pub enum VarStatus {
Perm(usize), Temp(usize, TempVarData) // Perm(chunk_num) | Temp(chunk_num, _)
}
// Perm: 0 initially, a stack register once processed.
// Temp: labeled with chunk_num and temp offset (unassigned if 0), arg (0 if unassigned).
pub enum VarData {
Perm(usize), Temp(usize, usize, TempVarData)
}
impl VarData {
pub fn as_reg_type(&self) -> RegType {
match self {

View File

@@ -57,6 +57,8 @@ impl fmt::Display for FactInstruction {
impl fmt::Display for QueryInstruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&QueryInstruction::GetVariable(ref x, ref a) =>
write!(f, "get_variable {}, A{}", x, a),
&QueryInstruction::PutConstant(Level::Shallow, ref constant, ref r) =>
write!(f, "put_constant {}, A{}", constant, r.reg_num()),
&QueryInstruction::PutConstant(Level::Deep, ref constant, ref r) =>

View File

@@ -111,7 +111,7 @@ impl Machine {
self.code_dir.insert((name, arity), p);
}
fn execute_instr<'b>(&mut self, instr_src: LineOrCodeOffset<'b>) -> bool
fn execute_instr<'a>(&mut self, instr_src: LineOrCodeOffset<'a>) -> bool
{
let mut instr = match instr_src {
LineOrCodeOffset::Instruction(instr) => instr,
@@ -251,9 +251,10 @@ impl Machine {
}
if succeeded {
for (var, var_status) in cg.vars() {
let r = var_status.as_reg_type().reg_num();
let addr = self.ms.registers[r].clone();
for (var, var_data) in cg.vars() {
let r = var_data.as_reg_type();
let addr = self.ms[r].clone();
heap_locs.insert((*var).clone(), addr);
}
@@ -536,7 +537,6 @@ impl MachineState {
_ => self.fail = true
};
},
&FactInstruction::GetList(_, reg) => {
let addr = self.deref(self[reg].clone());
@@ -782,6 +782,8 @@ impl MachineState {
fn execute_query_instr(&mut self, instr: &QueryInstruction) {
match instr {
&QueryInstruction::GetVariable(norm, arg) =>
self[norm] = self.registers[arg].clone(),
&QueryInstruction::PutConstant(_, ref constant, reg) =>
self[reg] = Addr::Con(constant.clone()),
&QueryInstruction::PutList(_, reg) =>
@@ -813,6 +815,7 @@ impl MachineState {
match norm {
RegType::Perm(n) => {
let e = self.e;
self[norm] = Addr::StackCell(e, n);
self.registers[arg] = self[norm].clone();
},

View File

@@ -1,12 +1,12 @@
pub mod and_stack;
pub mod ast;
pub mod codegen;
pub mod debray_allocator;
pub mod fixtures;
pub mod heapview;
pub mod indexing;
pub mod io;
pub mod iterators;
pub mod naive_allocator;
pub mod prolog_parser;
pub mod machine;
pub mod or_stack;

View File

@@ -16,6 +16,8 @@ pub trait CompilationTarget<'a> {
fn argument_to_variable(RegType, usize) -> Self;
fn argument_to_value(RegType, usize) -> Self;
fn move_to_register(RegType, usize) -> Self;
fn subterm_to_variable(RegType) -> Self;
fn subterm_to_value(RegType) -> Self;
@@ -53,6 +55,10 @@ impl<'a> CompilationTarget<'a> for FactInstruction {
FactInstruction::GetVariable(arg, val)
}
fn move_to_register(arg: RegType, val: usize) -> Self {
FactInstruction::GetVariable(arg, val)
}
fn argument_to_value(arg: RegType, val: usize) -> Self {
FactInstruction::GetValue(arg, val)
}
@@ -100,6 +106,10 @@ impl<'a> CompilationTarget<'a> for QueryInstruction {
fn argument_to_variable(arg: RegType, val: usize) -> Self {
QueryInstruction::PutVariable(arg, val)
}
fn move_to_register(arg: RegType, val: usize) -> Self {
QueryInstruction::GetVariable(arg, val)
}
fn argument_to_value(arg: RegType, val: usize) -> Self {
QueryInstruction::PutValue(arg, val)