Files
scryer-prolog/src/machine/compile.rs

1702 lines
58 KiB
Rust

use crate::codegen::*;
use crate::debray_allocator::*;
use crate::indexing::{merge_clause_index, remove_index};
use crate::machine::load_state::set_code_index;
use crate::machine::loader::*;
use crate::machine::load_state::LoadState;
use crate::machine::*;
use crate::machine::term_stream::*;
use slice_deque::sdeq;
use std::cell::Cell;
use std::collections::VecDeque;
use std::ops::Range;
struct StandaloneCompileResult {
clause_code: Code,
standalone_skeleton: PredicateSkeleton,
}
pub(super)
fn bootstrapping_compile(
stream: Stream,
wam: &mut Machine,
listing_src: ListingSource,
) -> Result<(), SessionError> {
let stream = &mut parsing_stream(stream)?;
let term_stream =
BootstrappingTermStream::from_prolog_stream(
stream,
wam.machine_st.atom_tbl.clone(),
wam.machine_st.flags,
listing_src,
);
let loader = Loader::new(term_stream, wam);
loader.load()?;
Ok(())
}
// throw errors if declaration or query found.
pub(super)
fn compile_relation(
cg: &mut CodeGenerator<DebrayAllocator>,
tl: &TopLevel
) -> Result<Code, CompilationError> {
match tl {
&TopLevel::Declaration(_) | &TopLevel::Query(_) =>
Err(CompilationError::ExpectedRel),
&TopLevel::Predicate(ref clauses) =>
cg.compile_predicate(&clauses),
&TopLevel::Fact(ref fact, ..) =>
Ok(cg.compile_fact(fact)),
&TopLevel::Rule(ref rule, ..) =>
cg.compile_rule(rule),
}
}
pub(super)
fn compile_appendix(
code: &mut Code,
queue: &VecDeque<TopLevel>,
jmp_by_locs: Vec<usize>,
non_counted_bt: bool,
atom_tbl: TabledData<Atom>,
) -> Result<(), CompilationError> {
let mut jmp_by_locs = VecDeque::from(jmp_by_locs);
for tl in queue.iter() {
let code_len = code.len();
let jmp_by_offset = jmp_by_locs.pop_front().unwrap();
match &mut code[jmp_by_offset] {
&mut Line::Control(ControlInstruction::JmpBy(_, ref mut offset, ..)) => {
*offset = code_len - jmp_by_offset;
}
_ => {
unreachable!()
}
}
// false because the inner predicate is a one-off, hence not extensible.
let settings = CodeGenSettings::new(false, non_counted_bt);
let mut cg = CodeGenerator::<DebrayAllocator>::new(atom_tbl.clone(), settings);
let decl_code = compile_relation(&mut cg, tl)?;
jmp_by_locs.extend(cg.jmp_by_locs.into_iter().map(|offset| offset + code.len()));
code.extend(decl_code.into_iter());
}
Ok(())
}
fn lower_bound_of_target_clause(skeleton: &PredicateSkeleton, target_pos: usize) -> usize {
if target_pos == 0 {
return 0;
}
let arg_num = skeleton.clauses[target_pos - 1].opt_arg_index_key.arg_num();
if arg_num == 0 {
return target_pos - 1;
}
for index in (0 .. target_pos - 1).rev() {
let current_arg_num = skeleton.clauses[index].opt_arg_index_key.arg_num();
if current_arg_num == 0 || current_arg_num != arg_num {
return index + 1;
}
}
0
}
fn compile_standalone_clause(
clause: PredicateClause,
queue: VecDeque<TopLevel>,
settings: CodeGenSettings,
atom_tbl: TabledData<Atom>,
) -> Result<StandaloneCompileResult, SessionError> {
let mut cg = CodeGenerator::<DebrayAllocator>::new(atom_tbl.clone(), settings);
let mut clause_code = cg.compile_predicate(&vec![clause])?;
compile_appendix(
&mut clause_code,
&queue,
cg.jmp_by_locs,
settings.non_counted_bt,
atom_tbl,
)?;
Ok(StandaloneCompileResult {
clause_code,
standalone_skeleton: cg.skeleton,
})
}
fn derelictize_try_me_else(
code: &mut Code,
index: usize,
retraction_info: &mut RetractionInfo,
) -> Option<usize> {
match &mut code[index] {
Line::Choice(ChoiceInstruction::TryMeElse(0)) => {
None
}
Line::Choice(ChoiceInstruction::TryMeElse(ref mut offset)) => {
retraction_info.push_record(
RetractionRecord::ModifiedTryMeElse(index, *offset),
);
Some(mem::replace(offset, 0))
}
_ => {
unreachable!()
}
}
}
fn merge_indices(
code: &mut Code,
target_index_loc: usize,
index_range: Range<usize>,
skeleton: &mut [ClauseIndexInfo],
retraction_info: &mut RetractionInfo,
) {
for clause_index in index_range {
if let Some(index_loc) = skeleton[clause_index].opt_arg_index_key.switch_on_term_loc() {
let clause_loc =
find_inner_choice_instr(code, skeleton[clause_index].clause_start, index_loc);
let target_indexing_line =
to_indexing_line_mut(&mut code[target_index_loc]).unwrap();
skeleton[clause_index].opt_arg_index_key.set_switch_on_term_loc(target_index_loc);
merge_clause_index(
target_indexing_line,
&mut skeleton[0 .. clause_index + 1],
clause_loc,
AppendOrPrepend::Append,
);
retraction_info.push_record(
RetractionRecord::AddedIndex(
skeleton[clause_index].opt_arg_index_key.clone(),
clause_loc,
),
);
} else {
break;
}
}
}
fn find_inner_choice_instr(
code: &Code,
mut index: usize,
index_loc: usize,
) -> usize {
loop {
match &code[index] {
Line::Choice(ChoiceInstruction::TryMeElse(o)) |
Line::Choice(ChoiceInstruction::RetryMeElse(o)) => {
if *o > 0 {
return index;
} else {
index = index_loc;
}
}
Line::Choice(ChoiceInstruction::TrustMe(_)) => {
return index;
}
Line::IndexingCode(indexing_code) => {
match &indexing_code[0] {
IndexingLine::Indexing(
IndexingInstruction::SwitchOnTerm(_, v, ..)
) => {
index += v;
}
_ => {
unreachable!();
}
}
}
Line::Control(ControlInstruction::RevJmpBy(offset)) => {
index -= offset;
}
_ => {
/* Here we land at the line after a TryMeElse(0),
* which happens iff a single clause belongs to the
* indexed subsequence. So, end the search by pointing
* to the original derelict TryMeElse.
*/
return index - 1;
}
}
}
}
fn remove_index_from_subsequence(
code: &mut Code,
opt_arg_index_key: &OptArgIndexKey,
clause_start: usize,
retraction_info: &mut RetractionInfo,
) {
if let Some(index_loc) = opt_arg_index_key.switch_on_term_loc() {
let clause_start = find_inner_choice_instr(code, clause_start, index_loc);
let target_indexing_line =
to_indexing_line_mut(&mut code[index_loc]).unwrap();
let offset = clause_start - index_loc + 1;
remove_index(opt_arg_index_key, target_indexing_line, offset);
// TODO: this isn't sufficiently precise. The removed offset could
// appear anywhere inside an Internal record.
retraction_info.push_record(
RetractionRecord::RemovedIndex(
index_loc,
opt_arg_index_key.clone(),
offset,
),
);
}
}
fn merge_indexed_subsequences(
code: &mut Code,
skeleton: &mut PredicateSkeleton,
lower_upper_bound: usize,
upper_lower_bound: usize,
retraction_info: &mut RetractionInfo,
) -> Option<IndexPtr> {
// patch the inner-threaded choice instructions to link the
// two sequences, patch lower_bound's outer-threaded choice
// instruction to TrustMe (or RetryMeElse), and derelict-ize
// target_pos + 1's inner TryMeElse.
let inner_trust_me_loc =
skeleton.clauses[upper_lower_bound - 2].clause_start;
let inner_try_me_else_loc = find_inner_choice_instr(
code,
skeleton.clauses[upper_lower_bound].clause_start,
skeleton.clauses[upper_lower_bound].opt_arg_index_key
.switch_on_term_loc().unwrap(),
);
match &mut code[inner_try_me_else_loc] {
Line::Choice(ChoiceInstruction::TryMeElse(ref mut o)) => {
retraction_info.push_record(
RetractionRecord::ModifiedTryMeElse(
skeleton.clauses[upper_lower_bound].clause_start,
*o,
),
);
match *o {
0 => {
code[inner_try_me_else_loc] =
Line::Choice(ChoiceInstruction::TrustMe(0));
}
o => {
match &code[inner_try_me_else_loc + o] {
Line::Control(ControlInstruction::RevJmpBy(0)) => {
code[inner_try_me_else_loc] =
Line::Choice(ChoiceInstruction::TrustMe(o));
}
_ => {
code[inner_try_me_else_loc] =
Line::Choice(ChoiceInstruction::RetryMeElse(o));
}
}
}
}
}
_ => {
}
}
thread_choice_instr_at_to(
code,
inner_trust_me_loc,
inner_try_me_else_loc,
retraction_info,
);
let mut end_of_upper_lower_bound = None;
for index in upper_lower_bound .. skeleton.clauses.len() {
if !skeleton.clauses[index].opt_arg_index_key.is_some() {
end_of_upper_lower_bound = Some(index);
break;
}
}
let outer_threaded_choice_instr_loc =
skeleton.clauses[lower_upper_bound].clause_start - 2;
match end_of_upper_lower_bound {
Some(outer_threaded_clause_index) => {
thread_choice_instr_at_to(
code,
outer_threaded_choice_instr_loc,
skeleton.clauses[outer_threaded_clause_index].clause_start,
retraction_info,
);
}
None => {
match &mut code[outer_threaded_choice_instr_loc] {
Line::Choice(ChoiceInstruction::TryMeElse(ref mut o)) => {
retraction_info.push_record(
RetractionRecord::ModifiedTryMeElse(inner_trust_me_loc, *o),
);
*o = 0;
return Some(IndexPtr::Index(outer_threaded_choice_instr_loc + 1));
}
_ => {
}
}
}
}
None
}
fn delete_from_dynamic_skeleton(
compilation_target: CompilationTarget,
key: PredicateKey,
skeleton: &mut PredicateSkeleton,
target_pos: usize,
retraction_info: &mut RetractionInfo,
) -> usize {
let clause_clause_loc = skeleton.clause_clause_locs.remove(target_pos);
let clause_index_info = skeleton.clauses.remove(target_pos);
retraction_info.push_record(
RetractionRecord::RemovedDynamicSkeletonClause(
compilation_target,
key,
target_pos,
clause_index_info,
clause_clause_loc,
),
);
clause_clause_loc
}
fn blunt_leading_choice_instr(
code: &mut Code,
mut instr_loc: usize,
retraction_info: &mut RetractionInfo,
) -> usize {
loop {
match &mut code[instr_loc] {
Line::Choice(ChoiceInstruction::RetryMeElse(o)) => {
retraction_info.push_record(
RetractionRecord::ModifiedRetryMeElse(instr_loc, *o),
);
code[instr_loc] = Line::Choice(ChoiceInstruction::TryMeElse(*o));
return instr_loc;
}
Line::Choice(ChoiceInstruction::TrustMe(offset)) => {
retraction_info.push_record(
RetractionRecord::AppendedTrustMe(instr_loc, *offset, false),
);
code[instr_loc] = Line::Choice(ChoiceInstruction::TryMeElse(0));
return instr_loc + 1;
}
Line::Choice(ChoiceInstruction::TryMeElse(0)) => {
return instr_loc + 1;
}
Line::Choice(ChoiceInstruction::TryMeElse(o)) => {
instr_loc += *o;
}
Line::Control(ControlInstruction::RevJmpBy(o)) => {
instr_loc -= *o;
}
_ => {
unreachable!()
}
}
}
}
fn set_switch_var_offset_to_choice_instr_(
code: &mut Code,
index_loc: usize,
offset: usize,
retraction_info: &mut RetractionInfo,
) {
let target_indexing_line =
to_indexing_line_mut(&mut code[index_loc]).unwrap();
let v =
match &mut target_indexing_line[0] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, v, ..)) => {
*v
}
_ => {
unreachable!();
}
};
match &code[index_loc + v] {
Line::Choice(ChoiceInstruction::TryMeElse(_)) => {
}
_ => {
set_switch_var_offset(
code,
index_loc,
offset,
retraction_info,
);
}
}
}
#[inline]
fn set_switch_var_offset(
code: &mut Code,
index_loc: usize,
offset: usize,
retraction_info: &mut RetractionInfo,
) {
let target_indexing_line =
to_indexing_line_mut(&mut code[index_loc]).unwrap();
let old_v =
match &mut target_indexing_line[0] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, ref mut v, ..)) => {
mem::replace(v, offset)
}
_ => {
unreachable!()
}
};
retraction_info.push_record(
RetractionRecord::ReplacedSwitchOnTermVarIndex(index_loc, old_v),
);
}
fn internalize_choice_instr_at(
code: &mut Code,
instr_loc: usize,
retraction_info: &mut RetractionInfo,
) {
match &mut code[instr_loc] {
Line::Choice(ChoiceInstruction::TryMeElse(0)) => {
retraction_info.push_record(
RetractionRecord::ModifiedTryMeElse(instr_loc, 0),
);
code[instr_loc] = Line::Choice(ChoiceInstruction::TrustMe(0));
}
Line::Choice(ChoiceInstruction::TryMeElse(o)) => {
let o = *o;
retraction_info.push_record(
RetractionRecord::ModifiedTryMeElse(instr_loc, o),
);
match &mut code[instr_loc + o] {
Line::Control(ControlInstruction::RevJmpBy(p))
if *p == 0 => {
code[instr_loc] = Line::Choice(ChoiceInstruction::TrustMe(o));
}
_ => {
code[instr_loc] = Line::Choice(ChoiceInstruction::RetryMeElse(o));
}
}
}
_ => {
unreachable!();
}
}
}
fn thread_choice_instr_at_to(
code: &mut Code,
mut instr_loc: usize,
target_loc: usize,
retraction_info: &mut RetractionInfo,
) {
loop {
match &mut code[instr_loc] {
Line::Choice(ChoiceInstruction::TryMeElse(ref mut o)) |
Line::Choice(ChoiceInstruction::RetryMeElse(ref mut o))
if target_loc >= instr_loc => {
retraction_info.push_record(
RetractionRecord::ReplacedChoiceOffset(instr_loc, *o),
);
*o = target_loc - instr_loc;
return;
}
Line::Choice(ChoiceInstruction::TryMeElse(ref mut o)) |
Line::Choice(ChoiceInstruction::RetryMeElse(ref mut o)) => {
instr_loc += *o;
}
Line::Control(ControlInstruction::RevJmpBy(ref mut o))
if instr_loc >= target_loc => {
retraction_info.push_record(
RetractionRecord::ModifiedRevJmpBy(instr_loc, *o),
);
*o = instr_loc - target_loc;
return;
}
Line::Choice(ChoiceInstruction::TrustMe(ref mut o))
if target_loc >= instr_loc => {
retraction_info.push_record(
RetractionRecord::AppendedTrustMe(instr_loc, *o, false),
//choice_instr.is_default()),
);
code[instr_loc] =
Line::Choice(ChoiceInstruction::RetryMeElse(target_loc - instr_loc));
return;
}
Line::Choice(ChoiceInstruction::TrustMe(o))
if *o > 0 => {
instr_loc += *o;
}
_ => {
unreachable!()
}
}
}
}
fn remove_non_leading_clause(
code: &mut Code,
preceding_choice_instr_loc: usize,
non_indexed_choice_instr_loc: usize,
retraction_info: &mut RetractionInfo,
) -> Option<IndexPtr> {
match &mut code[non_indexed_choice_instr_loc] {
Line::Choice(ChoiceInstruction::RetryMeElse(ref mut o)) => {
let o = *o;
thread_choice_instr_at_to(
code,
preceding_choice_instr_loc,
non_indexed_choice_instr_loc + o,
retraction_info,
);
None
}
Line::Choice(ChoiceInstruction::TrustMe(_)) => {
match &mut code[preceding_choice_instr_loc] {
Line::Choice(ChoiceInstruction::RetryMeElse(o)) => {
retraction_info.push_record(
RetractionRecord::ModifiedRetryMeElse(
preceding_choice_instr_loc,
*o,
),
);
code[preceding_choice_instr_loc] =
Line::Choice(ChoiceInstruction::TrustMe(0));
None
}
Line::Choice(ChoiceInstruction::TryMeElse(ref mut o)) => {
retraction_info.push_record(
RetractionRecord::ModifiedTryMeElse(
preceding_choice_instr_loc,
*o,
)
);
*o = 0;
Some(IndexPtr::Index(preceding_choice_instr_loc + 1))
}
_ => {
unreachable!();
}
}
}
_ => {
unreachable!();
}
}
}
fn finalize_retract(
key: PredicateKey,
compilation_target: CompilationTarget,
skeleton: &mut PredicateSkeleton,
code_index: CodeIndex,
target_pos: usize,
index_ptr_opt: Option<IndexPtr>,
retraction_info: &mut RetractionInfo,
) -> usize {
let clause_clause_loc =
delete_from_dynamic_skeleton(
compilation_target.clone(),
key.clone(),
skeleton,
target_pos,
retraction_info,
);
if let Some(index_ptr) = index_ptr_opt {
set_code_index(
retraction_info,
&compilation_target,
key,
&code_index,
index_ptr,
);
}
clause_clause_loc
}
fn remove_leading_unindexed_clause(
code: &mut Code,
non_indexed_choice_instr_loc: usize,
retraction_info: &mut RetractionInfo,
) -> Option<IndexPtr> {
match &mut code[non_indexed_choice_instr_loc] {
Line::Choice(ChoiceInstruction::TryMeElse(ref mut o)) => {
if *o > 0 {
retraction_info.push_record(
RetractionRecord::ModifiedTryMeElse(
non_indexed_choice_instr_loc,
*o,
)
);
let o = mem::replace(o, 0);
let index_ptr = blunt_leading_choice_instr(
code,
non_indexed_choice_instr_loc + o,
retraction_info,
);
Some(IndexPtr::Index(index_ptr))
} else {
Some(IndexPtr::DynamicUndefined)
}
}
_ => {
unreachable!();
}
}
}
fn prepend_compiled_clause(
code: &mut Code,
compilation_target: CompilationTarget,
key: PredicateKey,
mut clause_code: Code,
skeleton: &mut PredicateSkeleton,
retraction_info: &mut RetractionInfo,
) -> usize {
let clause_loc = code.len();
let mut prepend_queue = sdeq![];
let target_arg_num = skeleton.clauses[0].opt_arg_index_key.arg_num();
let head_arg_num = skeleton.clauses[1].opt_arg_index_key.arg_num();
if skeleton.clauses[0].opt_arg_index_key.switch_on_term_loc().is_some() {
match skeleton.clauses[1].opt_arg_index_key.switch_on_term_loc() {
Some(index_loc) if target_arg_num == head_arg_num => {
prepend_queue.extend(clause_code.drain(3 ..));
skeleton.clauses[0].opt_arg_index_key += index_loc - 1;
skeleton.clauses[0].clause_start = clause_loc + 2;
retraction_info.push_record(
RetractionRecord::AddedIndex(
skeleton.clauses[0].opt_arg_index_key.clone(),
skeleton.clauses[0].clause_start,
),
);
let outer_thread_choice_loc = skeleton.clauses[1].clause_start - 2;
retraction_info.push_record(
RetractionRecord::SkeletonClauseStartReplaced(
compilation_target,
key.clone(),
1,
skeleton.clauses[1].clause_start,
),
);
skeleton.clauses[1].clause_start =
find_inner_choice_instr(
code,
skeleton.clauses[1].clause_start,
index_loc,
);
let inner_thread_rev_offset =
3 + prepend_queue.len() + clause_loc - skeleton.clauses[1].clause_start;
prepend_queue.push_back(
Line::Control(ControlInstruction::RevJmpBy(inner_thread_rev_offset)),
);
prepend_queue.push_front(
Line::Choice(ChoiceInstruction::TryMeElse(prepend_queue.len())),
);
// prepend_queue is now:
// | TryMeElse N_2
// | (clause_code)
// +N_2 | RevJmpBy (RetryMeElse(M_1) or TryMeElse(0) at index_loc + 1)
prepend_queue.push_front(
Line::Control(ControlInstruction::RevJmpBy(
1 + clause_loc - index_loc
)),
);
let outer_thread_choice_offset = // outer_thread_choice_loc WAS index_loc - 1..
match derelictize_try_me_else(code, outer_thread_choice_loc, retraction_info) {
Some(next_subseq_offset) => {
// skeleton.clauses[1] has a non-stub TryMeElse.
let outer_thread_rev_offset =
prepend_queue.len() + 1 + clause_loc - outer_thread_choice_loc -
next_subseq_offset;
prepend_queue.push_back(
Line::Control(ControlInstruction::RevJmpBy(outer_thread_rev_offset))
);
prepend_queue.len()
}
None => {
// This case occurs when the clauses of
// the host predicate, up to and including
// the prepending of this clause, are
// indexed.
// The outer TryMeElse / RevJmpBy pushed
// in this case are stub instructions
// awaiting the addition of unindexed
// clauses.
prepend_queue.push_back(
Line::Control(ControlInstruction::RevJmpBy(0)),
);
0
}
};
prepend_queue.push_front(
Line::Choice(ChoiceInstruction::TryMeElse(outer_thread_choice_offset)),
);
// prepend_queue is now:
// | TryMeElse N_3
// | RevJmpBy (SwitchOnTerm at index_loc)
// | TryMeElse N_2
// | (clause_code)
// N_2 | RevJmpBy (RetryMeElse(M_1) or TryMeElse(0) at index_loc + 1)
// N_3 | RevJmpBy (TryMeElse(N_1) at index_loc - 1 or TrustMe if N_1 == 0)
let target_indexing_line = to_indexing_line_mut(&mut code[index_loc]).unwrap();
merge_clause_index(
target_indexing_line,
&mut skeleton.clauses,
clause_loc + 2, // == skeleton.clauses[0].clause_start
AppendOrPrepend::Prepend,
);
set_switch_var_offset(
code,
index_loc,
clause_loc - index_loc + 2,
retraction_info,
);
internalize_choice_instr_at(code, skeleton.clauses[1].clause_start, retraction_info);
code.extend(prepend_queue.into_iter());
clause_loc + (outer_thread_choice_offset == 0) as usize
}
_ => {
prepend_queue.extend(clause_code.drain(1 ..));
skeleton.clauses[0].opt_arg_index_key += clause_loc;
skeleton.clauses[0].clause_start = clause_loc + 2;
let old_clause_start = skeleton.clauses[1].clause_start;
let inner_thread_rev_offset =
2 + prepend_queue.len() + clause_loc - old_clause_start;
// this is a stub for chaining inner-threaded choice
// instructions.
prepend_queue.push_back(
Line::Control(ControlInstruction::RevJmpBy(0))
);
let prepend_queue_len = prepend_queue.len();
match &mut prepend_queue[1] {
Line::Choice(ChoiceInstruction::TryMeElse(ref mut o))
if *o == 0 => {
*o = prepend_queue_len - 2;
}
_ => {
unreachable!();
}
}
prepend_queue.push_back(
Line::Control(ControlInstruction::RevJmpBy(inner_thread_rev_offset)),
);
prepend_queue.push_front(
Line::Choice(ChoiceInstruction::TryMeElse(prepend_queue.len())),
);
// prepend_queue is now:
// | TryMeElse(N_2)
// | SwitchOnTerm 2, ...
// | TryMeElse(0)
// | (clause_code)
// +N_2 | RevJmpBy (RetryMeElse(M_1))
internalize_choice_instr_at(code, old_clause_start, retraction_info);
code.extend(prepend_queue.into_iter());
clause_loc // + (outer_thread_choice_offset == 0 as usize)
}
}
} else {
match skeleton.clauses[1].opt_arg_index_key.switch_on_term_loc() {
Some(_) => {
prepend_queue.extend(clause_code.drain(1 ..));
let old_clause_start = skeleton.clauses[1].clause_start - 2;
let inner_thread_rev_offset =
1 + prepend_queue.len() + clause_loc - old_clause_start;
prepend_queue.push_back(
Line::Control(ControlInstruction::RevJmpBy(inner_thread_rev_offset)),
);
prepend_queue.push_front(
Line::Choice(ChoiceInstruction::TryMeElse(prepend_queue.len())),
);
// prepend_queue is now:
// | TryMeElse(N_2)
// | (clause_code)
// +N_2 | RevJmpBy (RetryMeElse(M_1))
internalize_choice_instr_at(code, old_clause_start, retraction_info);
code.extend(prepend_queue.into_iter());
// skeleton.clauses[0].opt_arg_index_key += clause_loc;
skeleton.clauses[0].clause_start = clause_loc;
clause_loc // + (outer_thread_choice_offset == 0 as usize)
}
None => {
prepend_queue.extend(clause_code.drain(1 ..));
let old_clause_start = skeleton.clauses[1].clause_start;
let inner_thread_rev_offset =
1 + prepend_queue.len() + clause_loc - old_clause_start;
prepend_queue.push_back(
Line::Control(ControlInstruction::RevJmpBy(inner_thread_rev_offset)),
);
prepend_queue.push_front(
Line::Choice(ChoiceInstruction::TryMeElse(prepend_queue.len())),
);
// prepend_queue is now:
// | TryMeElse(N_2)
// | (clause_code)
// +N_2 | RevJmpBy (RetryMeElse(M_1))
internalize_choice_instr_at(code, old_clause_start, retraction_info);
code.extend(prepend_queue.into_iter());
// skeleton.clauses[0].opt_arg_index_key += clause_loc;
skeleton.clauses[0].clause_start = clause_loc;
clause_loc // + (outer_thread_choice_offset == 0 as usize)
}
}
}
}
fn append_compiled_clause(
code: &mut Code,
mut clause_code: Code,
skeleton: &mut PredicateSkeleton,
retraction_info: &mut RetractionInfo,
) -> Option<usize> {
let clause_loc = code.len();
let target_pos = skeleton.clauses.len() - 1;
let lower_bound = lower_bound_of_target_clause(skeleton, target_pos);
code.push(Line::Choice(ChoiceInstruction::TrustMe(0)));
skeleton.clauses[target_pos].clause_start = clause_loc;
let mut code_ptr_opt = None;
let lower_bound_arg_num = skeleton.clauses[lower_bound].opt_arg_index_key.arg_num();
let target_arg_num = skeleton.clauses[target_pos].opt_arg_index_key.arg_num();
let threaded_choice_instr_loc =
match skeleton.clauses[lower_bound].opt_arg_index_key.switch_on_term_loc() {
Some(index_loc) if lower_bound_arg_num == target_arg_num => {
code.extend(clause_code.drain(3 ..)); // skip the indexing code
// set skeleton[target_pos].opt_arg_index_key to
// index_loc. its original value is always 1.
skeleton.clauses[target_pos].opt_arg_index_key += index_loc - 1;
retraction_info.push_record(
RetractionRecord::AddedIndex(
skeleton.clauses[target_pos].opt_arg_index_key.clone(),
skeleton.clauses[target_pos].clause_start,
),
);
let target_indexing_line = to_indexing_line_mut(&mut code[index_loc]).unwrap();
merge_clause_index(
target_indexing_line,
&mut skeleton.clauses[lower_bound ..],
clause_loc,
AppendOrPrepend::Append,
);
if lower_bound + 1 == target_pos {
let lower_bound_clause_start = find_inner_choice_instr(
code,
skeleton.clauses[lower_bound].clause_start,
index_loc,
);
set_switch_var_offset(
code,
index_loc,
lower_bound_clause_start - index_loc,
retraction_info,
);
thread_choice_instr_at_to(
code,
lower_bound_clause_start,
skeleton.clauses[target_pos].clause_start,
retraction_info,
);
}
skeleton.clauses[target_pos - 1].clause_start
}
_ => {
skeleton.clauses[target_pos].opt_arg_index_key += clause_loc;
code.extend(clause_code.drain(1 ..));
match skeleton.clauses[lower_bound].opt_arg_index_key.switch_on_term_loc() {
Some(_) => {
if lower_bound == 0 {
code_ptr_opt = Some(skeleton.clauses[lower_bound].clause_start - 2);
}
skeleton.clauses[lower_bound].clause_start - 2
}
_ => {
if lower_bound == 0 {
code_ptr_opt = Some(skeleton.clauses[lower_bound].clause_start);
}
skeleton.clauses[lower_bound].clause_start
}
}
}
};
thread_choice_instr_at_to(code, threaded_choice_instr_loc, clause_loc, retraction_info);
code_ptr_opt
}
#[inline]
fn mergeable_indexed_subsequences(
lower_bound: usize,
target_pos: usize,
skeleton: &PredicateSkeleton,
) -> bool {
let lower_bound_arg_num = skeleton.clauses[lower_bound].opt_arg_index_key.arg_num();
if target_pos + 1 < skeleton.clauses.len() {
let succ_arg_num = skeleton.clauses[target_pos + 1].opt_arg_index_key.arg_num();
let target_arg_num = skeleton.clauses[target_pos].opt_arg_index_key.arg_num();
return target_arg_num != succ_arg_num && lower_bound_arg_num == succ_arg_num;
}
false
}
impl<'a> LoadState<'a> {
fn compile(
&mut self,
key: PredicateKey,
predicates: &Vec<PredicateClause>,
queue: &VecDeque<TopLevel>,
settings: CodeGenSettings,
) -> Result<IndexPtr, SessionError> {
let code_index = self.get_or_insert_code_index(key.clone());
let code_len = self.wam.code_repo.code.len();
let mut code_ptr = code_len;
let mut cg = CodeGenerator::<DebrayAllocator>::new(
self.wam.machine_st.atom_tbl.clone(),
settings,
);
let mut code = cg.compile_predicate(predicates)?;
compile_appendix(
&mut code,
queue,
cg.jmp_by_locs,
settings.non_counted_bt,
self.wam.machine_st.atom_tbl.clone(),
)?;
if settings.is_extensible {
for clause_index_info in cg.skeleton.clauses.iter_mut() {
clause_index_info.clause_start += code_len;
clause_index_info.opt_arg_index_key += code_len;
}
match &mut code[0] {
Line::Choice(ChoiceInstruction::TryMeElse(0)) => {
code_ptr += 1;
}
_ => {
}
}
match self.wam.indices.get_predicate_skeleton(
&self.compilation_target,
&key,
) {
Some(skeleton) => {
self.retraction_info.push_record(
RetractionRecord::SkeletonClauseTruncateBack(
self.compilation_target.clone(),
key.clone(),
skeleton.clauses.len(),
),
);
skeleton.clauses.extend(cg.skeleton.clauses.into_iter());
}
None => {
self.add_extensible_predicate(key.clone(), cg.skeleton);
}
}
}
set_code_index(
&mut self.retraction_info,
&self.compilation_target,
key,
&code_index,
IndexPtr::Index(code_ptr),
);
self.wam.code_repo.code.extend(code.into_iter());
Ok(code_index.get())
}
fn record_incremental_compile(&mut self, key: PredicateKey, append_or_prepend: AppendOrPrepend)
{
self.retraction_info.push_record(
match &self.compilation_target {
CompilationTarget::User => {
match append_or_prepend {
AppendOrPrepend::Append => {
RetractionRecord::AppendedUserExtensiblePredicate(
key,
)
}
AppendOrPrepend::Prepend => {
RetractionRecord::PrependedUserExtensiblePredicate(
key,
)
}
}
}
CompilationTarget::Module(ref module_name) => {
match append_or_prepend {
AppendOrPrepend::Append => {
RetractionRecord::AppendedModuleExtensiblePredicate(
module_name.clone(), key,
)
}
AppendOrPrepend::Prepend => {
RetractionRecord::PrependedModuleExtensiblePredicate(
module_name.clone(), key,
)
}
}
}
}
);
}
pub(super)
fn incremental_compile_clause(
&mut self,
key: PredicateKey,
clause: PredicateClause,
queue: VecDeque<TopLevel>,
non_counted_bt: bool,
append_or_prepend: AppendOrPrepend,
) -> Result<IndexPtr, SessionError> {
self.record_incremental_compile(key.clone(), append_or_prepend);
let skeleton =
match self.wam.indices.get_predicate_skeleton(
&self.compilation_target,
&key,
) {
Some(skeleton) if !skeleton.clauses.is_empty() => {
skeleton
}
_ => {
// true because this predicate is extensible.
let settings = CodeGenSettings::new(true, non_counted_bt);
return self.compile(key, &vec![clause], &queue, settings);
}
};
let settings = CodeGenSettings::new(true, non_counted_bt);
let atom_tbl = self.wam.machine_st.atom_tbl.clone();
let StandaloneCompileResult { clause_code, mut standalone_skeleton } =
compile_standalone_clause(clause, queue, settings, atom_tbl)?;
match append_or_prepend {
AppendOrPrepend::Append => {
skeleton.clauses.push_back(standalone_skeleton.clauses.pop_back().unwrap());
self.retraction_info.push_record(
RetractionRecord::SkeletonClausePopBack(
self.compilation_target.clone(),
key.clone(),
),
);
let result =
append_compiled_clause(
&mut self.wam.code_repo.code,
clause_code,
skeleton,
&mut self.retraction_info,
);
let code_index = self.get_or_insert_code_index(key.clone());
if let Some(new_code_index) = result {
set_code_index(
&mut self.retraction_info,
&self.compilation_target,
key,
&code_index,
IndexPtr::Index(new_code_index),
);
}
Ok(code_index.get())
}
AppendOrPrepend::Prepend => {
skeleton.clauses.push_front(standalone_skeleton.clauses.pop_back().unwrap());
self.retraction_info.push_record(
RetractionRecord::SkeletonClausePopFront(
self.compilation_target.clone(),
key.clone(),
),
);
let threaded_choice_instr_loc =
prepend_compiled_clause(
&mut self.wam.code_repo.code,
self.compilation_target.clone(),
key.clone(),
clause_code,
skeleton,
&mut self.retraction_info,
);
let code_index = self.get_or_insert_code_index(key.clone());
set_code_index(
&mut self.retraction_info,
&self.compilation_target,
key,
&code_index,
IndexPtr::Index(threaded_choice_instr_loc),
);
Ok(IndexPtr::Index(threaded_choice_instr_loc))
}
}
}
pub(super)
fn retract_clause(&mut self, key: PredicateKey, target_pos: usize) -> usize {
let code_index = self.get_or_insert_code_index(key.clone());
let skeleton =
match self.wam.indices.get_predicate_skeleton(
&self.compilation_target,
&key,
) {
Some(skeleton) => {
skeleton
}
None => {
unreachable!();
}
};
let code = &mut self.wam.code_repo.code;
let lower_bound = lower_bound_of_target_clause(skeleton, target_pos);
let lower_bound_is_unindexed = !skeleton.clauses[lower_bound].opt_arg_index_key.is_some();
if target_pos == 0 || (lower_bound + 1 == target_pos && lower_bound_is_unindexed) {
// the clause preceding target_pos, if there is one, is of key type
// OptArgIndexKey::None.
match skeleton.clauses[target_pos].opt_arg_index_key.switch_on_term_loc() {
Some(index_loc) => {
let inner_clause_start = find_inner_choice_instr(
code,
skeleton.clauses[target_pos].clause_start,
index_loc,
);
remove_index_from_subsequence(
code,
&skeleton.clauses[target_pos].opt_arg_index_key,
inner_clause_start,
&mut self.retraction_info,
);
match derelictize_try_me_else(
code,
inner_clause_start,
&mut self.retraction_info,
) {
Some(offset) => {
let instr_loc = find_inner_choice_instr(
code,
inner_clause_start + offset,
index_loc,
);
let clause_loc = blunt_leading_choice_instr(
code,
instr_loc,
&mut self.retraction_info,
);
set_switch_var_offset(
code,
index_loc,
clause_loc - index_loc,
&mut self.retraction_info,
);
self.retraction_info.push_record(
RetractionRecord::SkeletonClauseStartReplaced(
self.compilation_target.clone(),
key.clone(),
target_pos + 1,
skeleton.clauses[target_pos + 1].clause_start,
),
);
skeleton.clauses[target_pos + 1].clause_start =
skeleton.clauses[target_pos].clause_start;
return delete_from_dynamic_skeleton(
self.compilation_target.clone(),
key,
skeleton,
target_pos,
&mut self.retraction_info,
);
}
None => {
let index_ptr_opt =
if target_pos > 0 {
let preceding_choice_instr_loc =
skeleton.clauses[target_pos - 1].clause_start;
remove_non_leading_clause(
code,
preceding_choice_instr_loc,
skeleton.clauses[target_pos].clause_start - 2,
&mut self.retraction_info,
)
} else {
remove_leading_unindexed_clause(
code,
skeleton.clauses[target_pos].clause_start - 2,
&mut self.retraction_info,
)
};
return finalize_retract(
key,
self.compilation_target.clone(),
skeleton,
code_index,
target_pos,
index_ptr_opt,
&mut self.retraction_info,
);
}
}
}
None => {
}
}
}
let index_ptr_opt =
match skeleton.clauses[lower_bound].opt_arg_index_key.switch_on_term_loc() {
Some(target_indexing_loc)
if mergeable_indexed_subsequences(lower_bound, target_pos, skeleton) =>
{
let lower_bound_clause_start = find_inner_choice_instr(
code,
skeleton.clauses[lower_bound].clause_start,
target_indexing_loc,
);
let result;
match skeleton.clauses[target_pos + 1].opt_arg_index_key.switch_on_term_loc() {
Some(later_indexing_loc) if later_indexing_loc < target_indexing_loc => {
let target_indexing_line = mem::replace(
&mut code[target_indexing_loc],
Line::Control(ControlInstruction::RevJmpBy(
target_indexing_loc - later_indexing_loc
)),
);
match target_indexing_line {
Line::IndexingCode(indexing_code) => {
self.retraction_info.push_record(
RetractionRecord::ReplacedIndexingLine(
target_indexing_loc,
indexing_code,
),
);
}
_ => {
}
}
set_switch_var_offset(
code,
later_indexing_loc,
lower_bound_clause_start - later_indexing_loc, // target_indexing_loc - later_indexing_loc + 1,
&mut self.retraction_info,
);
result = merge_indexed_subsequences(
code,
skeleton,
lower_bound,
target_pos + 1,
&mut self.retraction_info,
);
merge_indices(
code,
later_indexing_loc,
0 .. target_pos - lower_bound,
&mut skeleton.clauses[lower_bound ..],
&mut self.retraction_info,
);
}
_ => {
set_switch_var_offset_to_choice_instr_(
code,
target_indexing_loc,
lower_bound_clause_start - target_indexing_loc, //1,
&mut self.retraction_info,
);
result = merge_indexed_subsequences(
code,
skeleton,
lower_bound,
target_pos + 1,
&mut self.retraction_info,
);
merge_indices(
code,
target_indexing_loc,
target_pos + 1 - lower_bound .. skeleton.clauses.len() - lower_bound,
&mut skeleton.clauses[lower_bound ..],
&mut self.retraction_info,
);
}
};
result
}
_ => {
if target_pos > 0 {
remove_index_from_subsequence(
code,
&skeleton.clauses[target_pos].opt_arg_index_key,
skeleton.clauses[target_pos].clause_start,
&mut self.retraction_info,
);
match skeleton.clauses[target_pos].opt_arg_index_key.switch_on_term_loc() {
Some(index_loc) => {
let preceding_choice_instr_loc = find_inner_choice_instr(
code,
skeleton.clauses[target_pos - 1].clause_start,
index_loc,
);
remove_non_leading_clause(
code,
preceding_choice_instr_loc,
skeleton.clauses[target_pos].clause_start,
&mut self.retraction_info,
);
match &mut code[preceding_choice_instr_loc] {
Line::Choice(ChoiceInstruction::TryMeElse(0)) => {
set_switch_var_offset(
code,
index_loc,
preceding_choice_instr_loc + 1 - index_loc,
&mut self.retraction_info,
);
}
_ => {
}
}
None
}
None => {
let preceding_choice_instr_loc =
if skeleton.clauses[lower_bound].opt_arg_index_key.is_some() {
skeleton.clauses[lower_bound].clause_start - 2
} else {
skeleton.clauses[lower_bound].clause_start
};
remove_non_leading_clause(
code,
preceding_choice_instr_loc,
skeleton.clauses[target_pos].clause_start,
&mut self.retraction_info,
)
}
}
} else {
remove_leading_unindexed_clause(
code,
skeleton.clauses[target_pos].clause_start,
&mut self.retraction_info,
)
}
}
};
finalize_retract(
key,
self.compilation_target.clone(),
skeleton,
code_index,
target_pos,
index_ptr_opt,
&mut self.retraction_info,
)
}
}
impl<'a, TS: TermStream> Loader<'a, TS> {
pub(super)
fn compile_clause_clauses<ClauseIter: Iterator<Item=(Term, Term)>>(
&mut self,
key: PredicateKey,
clause_clauses: ClauseIter,
append_or_prepend: AppendOrPrepend,
) -> Result<(), SessionError> {
let clause_predicates =
clause_clauses.map(|(head, body)| {
PredicateClause::Fact(
Term::Clause(
Cell::default(),
clause_name!("$clause"),
vec![Box::new(head), Box::new(body)],
None,
)
)
});
let compilation_target = mem::replace(
&mut self.load_state.compilation_target,
CompilationTarget::Module(clause_name!("builtins")),
);
let mut clause_clause_locs = sdeq![];
for clause_predicate in clause_predicates {
clause_clause_locs.push_back(self.load_state.wam.code_repo.code.len());
let result = self.load_state.incremental_compile_clause(
(clause_name!("$clause"), 2),
clause_predicate,
VecDeque::new(),
false, // non_counted_bt is false.
append_or_prepend,
);
if let Err(e) = result {
self.load_state.compilation_target = compilation_target;
return Err(e);
}
}
match self.load_state.wam.indices.get_predicate_skeleton(
&self.load_state.compilation_target,
&(clause_name!("$clause"), 2),
) {
Some(skeleton) if append_or_prepend.is_append() => {
skeleton.clause_clause_locs.extend_from_slice(&clause_clause_locs[0 ..]);
}
Some(skeleton) => {
for loc in clause_clause_locs.iter() {
skeleton.clause_clause_locs.push_front(*loc);
}
}
None => {
unreachable!();
}
}
match self.load_state.wam.indices.get_predicate_skeleton(
&compilation_target,
&key,
) {
Some(skeleton) if append_or_prepend.is_append() => {
self.load_state.retraction_info.push_record(
RetractionRecord::SkeletonClauseClausesTruncateBack(
compilation_target.clone(),
key.clone(),
skeleton.clause_clause_locs.len(),
),
);
skeleton.clause_clause_locs.append(
&mut clause_clause_locs
);
}
Some(skeleton) => {
self.load_state.retraction_info.push_record(
RetractionRecord::SkeletonClauseClausesTruncateFront(
compilation_target.clone(),
key.clone(),
skeleton.clause_clause_locs.len(),
),
);
for loc in clause_clause_locs.iter() {
skeleton.clause_clause_locs.push_front(*loc);
}
self.load_state.increment_clause_assert_margin(
clause_clause_locs.len()
);
}
None => {
unreachable!();
}
}
self.load_state.compilation_target = compilation_target;
Ok(())
}
pub(super)
fn compile_and_submit(&mut self) -> Result<(), SessionError> {
let queue = self.preprocessor.parse_queue(&mut self.load_state)?;
let key = self.predicates
.first()
.and_then(|cl| {
let arity = cl.arity();
cl.name().map(|name| (name, arity))
})
.ok_or(SessionError::NamelessEntry)?;
let (is_dynamic, is_extensible) = self.load_state.wam.indices
.get_predicate_skeleton(&self.load_state.compilation_target, &key)
.map(|skeleton| (skeleton.is_dynamic, true))
.unwrap_or((false, false));
let non_counted_bt = self.non_counted_bt_preds.contains(&key);
let settings = CodeGenSettings::new(is_extensible, non_counted_bt);
self.load_state.compile(key.clone(), &self.predicates, &queue, settings)?;
if is_dynamic {
let iter = mem::replace(&mut self.clause_clauses, vec![]).into_iter();
self.compile_clause_clauses(key, iter, AppendOrPrepend::Append)?;
}
Ok(self.predicates.clear())
}
}