From: Mark Thom Date: Sun, 30 Apr 2017 19:53:15 +0000 (-0600) Subject: transition to debray allocation X-Git-Tag: v0.8.110~740 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=316e5c5c1a9bda4e4798ff4f553ca0ab7e5a47a0;p=scryer-prolog.git transition to debray allocation --- diff --git a/Cargo.lock b/Cargo.lock index 3d66e845..ea28ff07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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)", diff --git a/Cargo.toml b/Cargo.toml index a750dbc3..ae6ad2cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rusty-wam" -version = "0.5.7" +version = "0.6.0" authors = ["Mark Thom"] build = "build.rs" diff --git a/README.md b/README.md index 834bfb01..5364911a 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/prolog/ast.rs b/src/prolog/ast.rs index ee81b3f0..e8b17a41 100644 --- a/src/prolog/ast.rs +++ b/src/prolog/ast.rs @@ -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)) diff --git a/src/prolog/codegen.rs b/src/prolog/codegen.rs index aaf95b71..bfef6a2e 100644 --- a/src/prolog/codegen.rs +++ b/src/prolog/codegen.rs @@ -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(&mut self, lvl: Level, cell: &'a Cell, + term_loc: GenContext, name: &'a Atom, - arity: usize) + arity: usize, + target: &mut Vec) -> 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(&mut self, lvl: Level, cell: &'a Cell, - constant: &'a Constant) + term_loc: GenContext, + constant: &'a Constant, + target: &mut Vec) -> 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(&mut self, lvl: Level, cell: &'a Cell) -> Target + fn to_list(&mut self, + lvl: Level, + term_loc: GenContext, + cell: &'a Cell, + target: &mut Vec) + -> 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(&mut self, lvl: Level, target: &mut Vec) + fn non_var_subterm(&mut self, + lvl: Level, + term_loc: GenContext, + cell: &'a Cell, + target: &mut Vec) + -> 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(&mut self, - lvl: Level, - cell: &'a Cell, - var: &'a Var, - _: GenContext, - target: &mut Vec) - 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(&mut self, cell: &'a Cell) -> 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() { diff --git a/src/prolog/debray_allocator.rs b/src/prolog/debray_allocator.rs new file mode 100644 index 00000000..c5dbd4c1 --- /dev/null +++ b/src/prolog/debray_allocator.rs @@ -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, + in_use: BTreeSet, +} + +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(®) { + return reg; + } + } + + let mut result = 0; + + for reg in self.temp_lb .. { + if !self.in_use.contains(®) { + if !tvd.no_use_set.contains(®) { + 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(®) { + return reg; + } + } + + let mut result = 0; + + for reg in self.temp_lb .. { + if !self.in_use.contains(®) { + if !tvd.no_use_set.contains(®) { + if !tvd.conflict_set.contains(®) { + 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(&mut self, chunk_num: usize, target: &mut Vec) + 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(&mut self, + var: &'a Var, + lvl: Level, + term_loc: GenContext, + target: &mut Vec) + -> 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(&mut self, lvl: Level, target: &mut Vec) + 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(&mut self, + lvl: Level, + term_loc: GenContext, + cell: &Cell, + target: &mut Vec) + 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(&mut self, + var: &'a Var, + lvl: Level, + cell: &'a Cell, + term_loc: GenContext, + target: &mut Vec) + 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(); + } +} diff --git a/src/prolog/fixtures.rs b/src/prolog/fixtures.rs index e5637683..beebd27b 100644 --- a/src/prolog/fixtures.rs +++ b/src/prolog/fixtures.rs @@ -10,14 +10,9 @@ pub type OccurrenceSet = BTreeSet<(GenContext, usize)>; pub struct TempVarData { last_term_arity: usize, - use_set: OccurrenceSet, - no_use_set: BTreeSet, - conflict_set: BTreeSet -} - -// 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, + pub conflict_set: BTreeSet } // 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 { diff --git a/src/prolog/io.rs b/src/prolog/io.rs index 5c0727db..9a3c8207 100644 --- a/src/prolog/io.rs +++ b/src/prolog/io.rs @@ -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) => diff --git a/src/prolog/machine.rs b/src/prolog/machine.rs index 8509083b..3abb42f9 100644 --- a/src/prolog/machine.rs +++ b/src/prolog/machine.rs @@ -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(); }, diff --git a/src/prolog/mod.rs b/src/prolog/mod.rs index 7cfe7cd9..dd2d771e 100644 --- a/src/prolog/mod.rs +++ b/src/prolog/mod.rs @@ -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; diff --git a/src/prolog/targets.rs b/src/prolog/targets.rs index bef3610d..be041c87 100644 --- a/src/prolog/targets.rs +++ b/src/prolog/targets.rs @@ -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)