move to a predicate-based module system, move to loader.[rs|pl]-based compilation, add support for incremental clause compilation

This commit is contained in:
Mark Thom
2021-01-30 14:32:47 -07:00
parent b33158b92e
commit a4d15bfb88
55 changed files with 10501 additions and 7601 deletions

View File

@@ -1,5 +1,6 @@
/// Code generation to WAM-like instructions.
use crate::prolog_parser::ast::*;
use crate::prolog_parser::tabled_rc::TabledData;
use crate::allocator::*;
use crate::arithmetic::*;
@@ -9,20 +10,32 @@ use crate::forms::*;
use crate::indexing::*;
use crate::instructions::*;
use crate::iterators::*;
use crate::machine::machine_indices::*;
use crate::targets::*;
use crate::machine::machine_errors::*;
use crate::indexmap::{IndexMap, IndexSet};
use std::cell::Cell;
use std::collections::VecDeque;
use std::rc::Rc;
use std::vec::Vec;
#[derive(Debug)]
pub struct CodeGenerator<TermMarker> {
marker: TermMarker,
pub var_count: IndexMap<Rc<Var>, usize>,
non_counted_bt: bool,
#[inline]
pub fn trust_me(non_counted_bt: bool) -> ChoiceInstruction {
if non_counted_bt {
ChoiceInstruction::DefaultTrustMe(0)
} else {
ChoiceInstruction::TrustMe(0)
}
}
#[inline]
pub fn retry_me_else(offset: usize, non_counted_bt: bool) -> ChoiceInstruction {
if non_counted_bt {
ChoiceInstruction::DefaultRetryMeElse(offset)
} else {
ChoiceInstruction::RetryMeElse(offset)
}
}
#[derive(Debug)]
@@ -97,17 +110,46 @@ impl<'a> ConjunctInfo<'a> {
}
}
impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
pub fn new(non_counted_bt: bool) -> Self {
CodeGenerator {
marker: Allocator::new(),
var_count: IndexMap::new(),
#[derive(Clone, Copy, Debug)]
pub struct CodeGenSettings {
pub is_extensible: bool,
pub non_counted_bt: bool,
}
impl CodeGenSettings {
#[inline]
pub fn new(is_extensible: bool, non_counted_bt: bool) -> Self {
CodeGenSettings {
is_extensible,
non_counted_bt,
}
}
}
pub fn take_vars(self) -> AllocVarDict {
self.marker.take_bindings()
#[derive(Debug)]
pub struct CodeGenerator<TermMarker> {
atom_tbl: TabledData<Atom>,
marker: TermMarker,
pub var_count: IndexMap<Rc<Var>, usize>,
non_counted_bt: bool,
is_extensible: bool,
pub skeleton: PredicateSkeleton,
pub jmp_by_locs: Vec<usize>,
global_jmp_by_locs_offset: usize,
}
impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
pub fn new(atom_tbl: TabledData<Atom>, settings: CodeGenSettings) -> Self {
CodeGenerator {
atom_tbl,
marker: Allocator::new(),
var_count: IndexMap::new(),
non_counted_bt: settings.non_counted_bt,
is_extensible: settings.is_extensible,
skeleton: PredicateSkeleton::new(),
jmp_by_locs: vec![],
global_jmp_by_locs_offset: 0,
}
}
fn update_var_count<Iter: Iterator<Item = TermRef<'a>>>(&mut self, iter: Iter) {
@@ -337,16 +379,20 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
ConjunctInfo::new(vs, num_of_chunks, has_deep_cut)
}
fn add_conditional_call(code: &mut Code, qt: &QueryTerm, pvs: usize) {
fn add_conditional_call(&mut self, code: &mut Code, qt: &QueryTerm, pvs: usize) {
match qt {
&QueryTerm::Jump(ref vars) => code.push(jmp_call!(vars.len(), 0, pvs)),
&QueryTerm::Jump(ref vars) => {
self.jmp_by_locs.push(code.len());
code.push(jmp_call!(vars.len(), 0, pvs));
}
&QueryTerm::Clause(_, ref ct, ref terms, true) => {
code.push(call_clause_by_default!(ct.clone(), terms.len(), pvs))
code.push(call_clause_by_default!(ct.clone(), terms.len(), pvs));
}
&QueryTerm::Clause(_, ref ct, ref terms, false) => {
code.push(call_clause!(ct.clone(), terms.len(), pvs))
code.push(call_clause!(ct.clone(), terms.len(), pvs));
}
_ => {
}
_ => {}
}
}
@@ -356,16 +402,22 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
match code.last_mut() {
Some(&mut Line::Control(ref mut ctrl)) => match ctrl {
&mut ControlInstruction::CallClause(_, _, _, ref mut last_call, _) => {
*last_call = true
*last_call = true;
}
&mut ControlInstruction::JmpBy(_, _, _, ref mut last_call) => {
*last_call = true
*last_call = true;
}
&mut ControlInstruction::Proceed => {
}
_ => {
dealloc_index += 1;
}
&mut ControlInstruction::Proceed => {}
_ => dealloc_index += 1,
},
Some(&mut Line::Cut(CutInstruction::Cut(_))) => dealloc_index += 1,
_ => {}
Some(&mut Line::Cut(CutInstruction::Cut(_))) => {
dealloc_index += 1;
}
_ => {
}
};
dealloc_index
@@ -377,7 +429,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
terms: &'a Vec<Box<Term>>,
term_loc: GenContext,
code: &mut Code,
) -> Result<(), ParserError> {
) -> Result<(), CompilationError> {
match ct {
&InlinedClauseType::CompareNumber(cmp, ..) => {
self.marker.reset_arg(2);
@@ -553,7 +605,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
code: &mut Code,
term_loc: GenContext,
use_default_call_policy: bool,
) -> Result<(), ParserError> {
) -> Result<(), CompilationError> {
let (mut acode, at) = self.call_arith_eval(terms[1].as_ref(), 1)?;
code.append(&mut acode);
@@ -656,7 +708,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
conjunct_info: &ConjunctInfo<'a>,
code: &mut Code,
is_exposed: bool,
) -> Result<(), ParserError> {
) -> Result<(), CompilationError> {
for (chunk_num, _, terms) in iter.rule_body_iter() {
for (i, term) in terms.iter().enumerate() {
let term_loc = if i + 1 < terms.len() {
@@ -669,18 +721,24 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
&QueryTerm::GetLevelAndUnify(ref cell, ref var) => {
self.compile_get_level_and_unify(code, cell, var.clone(), term_loc)
}
&QueryTerm::UnblockedCut(ref cell) => self.compile_unblocked_cut(code, cell),
&QueryTerm::BlockedCut => code.push(if chunk_num == 0 {
Line::Cut(CutInstruction::NeckCut)
} else {
Line::Cut(CutInstruction::Cut(perm_v!(1)))
}),
&QueryTerm::UnblockedCut(ref cell) => {
self.compile_unblocked_cut(code, cell)
}
&QueryTerm::BlockedCut => {
code.push(if chunk_num == 0 {
Line::Cut(CutInstruction::NeckCut)
} else {
Line::Cut(CutInstruction::Cut(perm_v!(1)))
})
}
&QueryTerm::Clause(
_,
ClauseType::BuiltIn(BuiltInClauseType::Is(..)),
ref terms,
use_default_call_policy,
) => self.compile_is_call(terms, code, term_loc, use_default_call_policy)?,
) => {
self.compile_is_call(terms, code, term_loc, use_default_call_policy)?
}
&QueryTerm::Clause(_, ClauseType::Inlined(ref ct), ref terms, _) => {
self.compile_inlined(ct, terms, term_loc, code)?
}
@@ -714,7 +772,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
}
}
fn compile_cleanup(code: &mut Code, conjunct_info: &ConjunctInfo, toc: &'a QueryTerm) {
fn compile_cleanup(&mut self, code: &mut Code, conjunct_info: &ConjunctInfo, toc: &'a QueryTerm) {
// add a proceed to bookend any trailing cuts.
match toc {
&QueryTerm::BlockedCut | &QueryTerm::UnblockedCut(..) => code.push(proceed!()),
@@ -726,11 +784,19 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
let dealloc_index = Self::lco(code);
if conjunct_info.allocates() {
let offset = self.global_jmp_by_locs_offset;
if let Some(jmp_by_offset) = self.jmp_by_locs[offset ..].last_mut() {
if *jmp_by_offset == dealloc_index {
*jmp_by_offset += 1;
}
}
code.insert(dealloc_index, Line::Control(ControlInstruction::Deallocate));
}
}
pub fn compile_rule<'b: 'a>(&mut self, rule: &'b Rule) -> Result<Code, ParserError> {
pub fn compile_rule<'b: 'a>(&mut self, rule: &'b Rule) -> Result<Code, CompilationError> {
let iter = ChunkedIterator::from_rule(rule);
let conjunct_info = self.collect_var_data(iter);
@@ -739,7 +805,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
ref clauses,
} = rule;
let mut code = Vec::new();
let mut code = Code::new();
self.marker.reset_at_head(args);
self.compile_seq_prelude(&conjunct_info, &mut code);
@@ -761,8 +827,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
self.compile_seq(iter, &conjunct_info, &mut code, false)?;
conjunct_info.mark_unsafe_vars(unsafe_var_marker, &mut code);
self.compile_cleanup(&mut code, &conjunct_info, clauses.last().unwrap_or(p1));
Self::compile_cleanup(&mut code, &conjunct_info, clauses.last().unwrap_or(p1));
Ok(code)
}
@@ -836,10 +902,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
}
}
Self::add_conditional_call(code, term, num_perm_vars_left);
self.add_conditional_call(code, term, num_perm_vars_left);
}
pub fn compile_query(&mut self, query: &'a Vec<QueryTerm>) -> Result<Code, ParserError> {
/*
pub fn compile_query(&mut self, query: &'a Vec<QueryTerm>) -> Result<Code, CompilationError> {
let iter = ChunkedIterator::from_term_sequence(query);
let conjunct_info = self.collect_var_data(iter);
@@ -857,6 +924,16 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
Ok(code)
}
*/
#[inline]
fn increment_jmp_by_locs_by(&mut self, incr: usize) {
let offset = self.global_jmp_by_locs_offset;
for loc in &mut self.jmp_by_locs[offset ..] {
*loc += incr;
}
}
/// Returns the index of the first instantiated argument.
fn first_instantiated_index(clauses: &[PredicateClause]) -> Option<usize> {
@@ -925,48 +1002,50 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
subseqs
}
fn trust_me(&self) -> ChoiceInstruction {
if self.non_counted_bt {
ChoiceInstruction::DefaultTrustMe
} else {
ChoiceInstruction::TrustMe
}
}
fn retry_me_else(&self, offset: usize) -> ChoiceInstruction {
if self.non_counted_bt {
ChoiceInstruction::DefaultRetryMeElse(offset)
} else {
ChoiceInstruction::RetryMeElse(offset)
}
}
fn compile_pred_subseq<'b: 'a>(
&mut self,
clauses: &'b [PredicateClause],
optimal_index: usize,
) -> Result<Code, ParserError> {
let mut code_body = Vec::new();
let mut code_offsets = CodeOffsets::new();
) -> Result<Code, CompilationError> {
let mut code = VecDeque::new();
let mut code_offsets = CodeOffsets::new(self.atom_tbl.clone(), optimal_index + 1);
let mut skip_stub_try_me_else = false;
let num_clauses = clauses.len();
let jmp_by_locs_len = self.jmp_by_locs.len();
for (i, clause) in clauses.iter().enumerate() {
self.marker.reset();
let mut clause_code = match *clause {
PredicateClause::Fact(ref fact, ..) => self.compile_fact(fact),
PredicateClause::Rule(ref rule, ..) => self.compile_rule(rule)?,
let mut clause_index_info = ClauseIndexInfo::new(code.len());
self.global_jmp_by_locs_offset = self.jmp_by_locs.len();
let clause_code = match clause {
&PredicateClause::Fact(ref fact, ..) => self.compile_fact(fact),
&PredicateClause::Rule(ref rule, ..) => self.compile_rule(rule)?,
};
if num_clauses > 1 {
if clauses.len() > 1 {
let choice = match i {
0 => ChoiceInstruction::TryMeElse(clause_code.len() + 1),
_ if i == num_clauses - 1 => self.trust_me(),
_ => self.retry_me_else(clause_code.len() + 1),
_ if i == clauses.len() - 1 => trust_me(self.non_counted_bt),
_ => retry_me_else(clause_code.len() + 1, self.non_counted_bt),
};
code_body.push(Line::Choice(choice));
code.push_back(Line::Choice(choice));
} else if self.is_extensible {
/*
generate stub choice instructions for extensible
predicates. if predicates are added to either the
inner or outer thread of choice instructions,
these stubs will be used, and surrounding indexing
instructions modified accordingly.
until then, the v offset of SwitchOnTerm will skip
over them.
*/
code.push_front(Line::Choice(ChoiceInstruction::TryMeElse(0)));
skip_stub_try_me_else = true;
}
let arg = match clause.args() {
@@ -976,47 +1055,86 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
},
None => None,
};
if let Some(arg) = arg {
let index = code_body.len();
code_offsets.index_term(arg, index);
let index = code.len();
code_offsets.index_term(arg, index, &mut clause_index_info);
}
code_body.append(&mut clause_code);
if !skip_stub_try_me_else {
self.increment_jmp_by_locs_by(code.len());
}
self.skeleton.clauses.push_back(clause_index_info);
code.extend(clause_code.into_iter());
}
let mut code = Vec::new();
code_offsets.add_indices(&mut code, code_body, optimal_index + 1);
let index_code = code_offsets.compute_indices(skip_stub_try_me_else);
self.global_jmp_by_locs_offset = jmp_by_locs_len;
Ok(code)
if !index_code.is_empty() {
code.push_front(Line::IndexingCode(index_code));
if skip_stub_try_me_else {
// skip the TryMeElse(0) also.
self.increment_jmp_by_locs_by(2);
} else {
self.increment_jmp_by_locs_by(1);
}
} else if skip_stub_try_me_else {
// remove the TryMeElse(0).
code.pop_front();
}
Ok(Vec::from(code))
}
pub fn compile_predicate<'b: 'a>(
&mut self,
clauses: &'b Vec<PredicateClause>,
) -> Result<Code, ParserError> {
let mut code = Vec::new();
) -> Result<Code, CompilationError> {
let mut code = Code::new();
let optimal_index = match Self::first_instantiated_index(&clauses) {
Some(index) => index,
None => 0, // Default to first argument indexing.
};
let split_pred = Self::split_predicate(&clauses, optimal_index);
let multi_seq = split_pred.len() > 1;
for (l, r) in split_pred {
let mut code_segment =
self.compile_pred_subseq(&clauses[l..r], optimal_index)?;
let skel_lower_bound = self.skeleton.clauses.len();
let code_segment = self.compile_pred_subseq(&clauses[l .. r], optimal_index)?;
let clause_start_offset = code.len();
if multi_seq {
let choice = match l {
0 => ChoiceInstruction::TryMeElse(code_segment.len() + 1),
_ if r == clauses.len() => self.trust_me(),
_ => self.retry_me_else(code_segment.len() + 1),
_ if r == clauses.len() => trust_me(self.non_counted_bt),
_ => retry_me_else(code_segment.len() + 1, self.non_counted_bt),
};
code.push(Line::Choice(choice));
} else if self.is_extensible {
code.push(Line::Choice(ChoiceInstruction::TryMeElse(0)));
}
code.append(&mut code_segment);
if self.is_extensible {
let segment_is_indexed = to_indexing_line(&code_segment[0]).is_some();
for clause_index_info in self.skeleton.clauses[skel_lower_bound ..].iter_mut() {
clause_index_info.clause_start +=
clause_start_offset + 2 * (segment_is_indexed as usize);
clause_index_info.opt_arg_index_key +=
clause_start_offset + 1;
}
}
self.increment_jmp_by_locs_by(code.len());
self.global_jmp_by_locs_offset = self.jmp_by_locs.len();
code.extend(code_segment.into_iter());
}
Ok(code)