improve efficiency of call/N, replace '$call_with_default_policy' with

'$call_with_inference_counting'
This commit is contained in:
Mark Thom
2022-06-12 22:17:44 -06:00
parent a68394b6f2
commit 987bbdecf5
12 changed files with 1116 additions and 621 deletions

View File

@@ -1339,7 +1339,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
term: Term,
settings: CodeGenSettings,
) -> Result<StandaloneCompileResult, SessionError> {
let mut preprocessor = Preprocessor::new();
let mut preprocessor = Preprocessor::new(settings);
let clause = self.try_term_to_tl(term, &mut preprocessor)?;
let queue = preprocessor.parse_queue(self)?;
@@ -1379,7 +1379,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
let mut code_ptr = code_len;
let mut clauses = vec![];
let mut preprocessor = Preprocessor::new();
let mut preprocessor = Preprocessor::new(settings);
for term in predicates.predicates.drain(0..) {
clauses.push(self.try_term_to_tl(term, &mut preprocessor)?);

View File

@@ -4692,11 +4692,11 @@ impl Machine {
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallLoadContextModule(_) => {
self.load_context_module();
self.load_context_module(self.machine_st.registers[1]);
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecuteLoadContextModule(_) => {
self.load_context_module();
self.load_context_module(self.machine_st.registers[1]);
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallLoadContextStream(_) => {
@@ -4859,6 +4859,62 @@ impl Machine {
self.machine_st.fail = !self.predicate_defined();
self.machine_st.p = self.machine_st.cp;
}
&Instruction::CallStripModule(_) => {
let (module_loc, qualified_goal) = self.machine_st.strip_module(
self.machine_st.registers[1],
self.machine_st.registers[2],
);
let target_module_loc = self.machine_st.registers[2];
unify_fn!(
&mut self.machine_st,
module_loc,
target_module_loc
);
let target_qualified_goal = self.machine_st.registers[3];
unify_fn!(
&mut self.machine_st,
qualified_goal,
target_qualified_goal
);
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecuteStripModule(_) => {
let (module_loc, qualified_goal) = self.machine_st.strip_module(
self.machine_st.registers[1],
self.machine_st.registers[2]
);
let target_module_loc = self.machine_st.registers[2];
unify_fn!(
&mut self.machine_st,
module_loc,
target_module_loc
);
let target_qualified_goal = self.machine_st.registers[3];
unify_fn!(
&mut self.machine_st,
qualified_goal,
target_qualified_goal
);
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallPrepareCallClause(arity, _) => {
try_or_throw!(self.machine_st, self.prepare_call_clause(arity));
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecutePrepareCallClause(arity, _) => {
try_or_throw!(self.machine_st, self.prepare_call_clause(arity));
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
}
}

View File

@@ -1844,7 +1844,7 @@ impl Machine {
return;
}
_ => {
return self.load_context_module();
return self.load_context_module(self.machine_st.registers[1]);
}
}
}
@@ -1866,9 +1866,9 @@ impl Machine {
self.machine_st.fail = true;
}
pub(crate) fn load_context_module(&mut self) {
pub(crate) fn load_context_module(&mut self, target: HeapCellValue) {
if let Some(load_context) = self.load_contexts.last() {
self.machine_st.unify_atom(load_context.module, self.machine_st.registers[1]);
self.machine_st.unify_atom(load_context.module, target);
} else {
self.machine_st.fail = true;
}

View File

@@ -2039,12 +2039,15 @@ impl MachineState {
)
}
pub(super) fn setup_call_n(&mut self, arity: usize) -> Result<PredicateKey, MachineStub> {
let addr = self.store(self.deref(self.registers[arity]));
let (name, narity) = read_heap_cell!(addr,
pub(crate) fn setup_call_n_init_goal_info(
&mut self,
goal: HeapCellValue,
arity: usize,
) -> Result<(Atom, usize, usize), MachineStub> {
Ok(read_heap_cell!(goal,
(HeapCellValueTag::Str, s) => {
let (name, narity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity();
let (name, narity) = cell_as_atom_cell!(self.heap[s])
.get_name_and_arity();
if narity + arity > MAX_ARITY {
let stub = functor_stub(atom!("call"), arity + 1);
@@ -2052,34 +2055,48 @@ impl MachineState {
return Err(self.error_form(err, stub));
}
for i in (1..arity).rev() {
self.registers[i + narity] = self.registers[i];
}
for i in 1..narity + 1 {
self.registers[i] = self.heap[s + i];
}
(name, narity)
(name, narity, s)
}
(HeapCellValueTag::Atom, (name, arity)) => {
debug_assert_eq!(arity, 0);
(name, 0)
if name == atom!("[]") {
let stub = functor_stub(atom!("call"), arity + 1);
let err = self.type_error(ValidType::Callable, goal);
return Err(self.error_form(err, stub));
}
(name, 0, 0)
}
(HeapCellValueTag::Char, c) => {
(self.atom_tbl.build_with(&c.to_string()), 0)
(self.atom_tbl.build_with(&c.to_string()), 0, 0)
}
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar, _h) => {
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
let stub = functor_stub(atom!("call"), arity + 1);
let err = self.instantiation_error();
return Err(self.error_form(err, stub));
}
_ => {
let stub = functor_stub(atom!("call"), arity + 1);
let err = self.type_error(ValidType::Callable, addr);
let err = self.type_error(ValidType::Callable, goal);
return Err(self.error_form(err, stub));
}
);
))
}
pub(crate) fn setup_call_n(&mut self, arity: usize) -> Result<PredicateKey, MachineStub> {
let addr = self.store(self.deref(self.registers[arity]));
let (name, narity, s) = self.setup_call_n_init_goal_info(addr, arity)?;
if narity > 0 {
for i in (1..arity).rev() {
self.registers[i + narity] = self.registers[i];
}
for i in 1..narity + 1 {
self.registers[i] = self.heap[s + i];
}
}
Ok((name, arity + narity - 1))
}

View File

@@ -1,4 +1,5 @@
use crate::atom_table::*;
use crate::codegen::CodeGenSettings;
use crate::forms::*;
use crate::instructions::*;
use crate::iterators::*;
@@ -474,9 +475,10 @@ fn clause_to_query_term<'a, LS: LoadState<'a>>(
loader: &mut Loader<'a, LS>,
name: Atom,
terms: Vec<Term>,
call_policy: CallPolicy,
) -> QueryTerm {
let ct = loader.get_clause_type(name, terms.len());
QueryTerm::Clause(Cell::default(), ct, terms, false)
QueryTerm::Clause(Cell::default(), ct, terms, call_policy)
}
#[inline]
@@ -485,20 +487,23 @@ fn qualified_clause_to_query_term<'a, LS: LoadState<'a>>(
module_name: Atom,
name: Atom,
terms: Vec<Term>,
call_policy: CallPolicy,
) -> QueryTerm {
let ct = loader.get_qualified_clause_type(module_name, name, terms.len());
QueryTerm::Clause(Cell::default(), ct, terms, false)
QueryTerm::Clause(Cell::default(), ct, terms, call_policy)
}
#[derive(Debug)]
pub(crate) struct Preprocessor {
queue: VecDeque<VecDeque<Term>>,
settings: CodeGenSettings,
}
impl Preprocessor {
pub(super) fn new() -> Self {
pub(super) fn new(settings: CodeGenSettings) -> Self {
Preprocessor {
queue: VecDeque::new(),
settings,
}
}
@@ -599,7 +604,10 @@ impl Preprocessor {
if name == atom!("!") || name == atom!("blocked_!") {
Ok(QueryTerm::BlockedCut)
} else {
Ok(clause_to_query_term(loader, name, vec![]))
Ok(clause_to_query_term(
loader, name, vec![],
self.settings.default_call_policy(),
))
}
}
Term::Literal(_, Literal::Char('!')) => Ok(QueryTerm::BlockedCut),
@@ -663,6 +671,7 @@ impl Preprocessor {
module_name,
predicate_name,
vec![],
self.settings.default_call_policy(),
)),
(
Term::Literal(_, Literal::Atom(module_name)),
@@ -672,22 +681,29 @@ impl Preprocessor {
module_name,
name,
terms,
self.settings.default_call_policy()
)),
(module_name, predicate_name) => {
terms.push(module_name);
terms.push(predicate_name);
Ok(clause_to_query_term(loader, name, terms))
Ok(clause_to_query_term(
loader,
name,
terms,
self.settings.default_call_policy(),
))
}
}
}
_ => Ok(clause_to_query_term(loader, name, terms)),
_ => Ok(clause_to_query_term(loader, name, terms,
self.settings.default_call_policy())),
},
Term::Var(..) => Ok(QueryTerm::Clause(
Cell::default(),
ClauseType::CallN(1),
vec![term],
false,
self.settings.default_call_policy(),
)),
_ => Err(CompilationError::InadmissibleQueryTerm),
}
@@ -700,10 +716,10 @@ impl Preprocessor {
) -> Result<QueryTerm, CompilationError> {
match term {
Term::Clause(r, name, mut subterms) => {
if subterms.len() == 1 && name == atom!("$call_with_default_policy") {
if subterms.len() == 1 && name == atom!("$call_with_inference_counting") {
self.to_query_term(loader, subterms.pop().unwrap())
.map(|mut query_term| {
query_term.set_default_caller();
query_term.set_call_policy(CallPolicy::Counted);
query_term
})
} else {

View File

@@ -887,9 +887,117 @@ impl MachineState {
Ok(string)
}
pub(crate) fn strip_module(
&self,
mut qualified_goal: HeapCellValue,
mut module_loc: HeapCellValue,
) -> (HeapCellValue, HeapCellValue) {
loop {
read_heap_cell!(qualified_goal,
(HeapCellValueTag::Str, s) => {
let (name, arity) = cell_as_atom_cell!(self.heap[s])
.get_name_and_arity();
if name == atom!(":") && arity == 2 {
module_loc = self.heap[s+1];
qualified_goal = self.heap[s+2];
} else {
break;
}
}
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
if qualified_goal != self.heap[h] {
qualified_goal = self.heap[h];
} else {
break;
}
}
_ => {
break;
}
);
}
(module_loc, qualified_goal)
}
}
impl Machine {
#[inline(always)]
pub(crate) fn prepare_call_clause(&mut self, arity: usize) -> CallResult {
let (module_loc, qualified_goal) = self.machine_st.strip_module(
self.machine_st.registers[3],
self.machine_st.registers[2],
);
// the first three arguments don't belong to the containing call/N.
let arity = arity - 3;
let (name, narity, s) = self.machine_st.setup_call_n_init_goal_info(
qualified_goal,
arity,
)?;
let module_loc = self.machine_st.store(self.machine_st.deref(module_loc));
if module_loc.is_var() {
self.load_context_module(module_loc);
if self.machine_st.fail {
self.machine_st.fail = false;
self.machine_st.unify_atom(atom!("user"), module_loc);
if self.machine_st.fail {
return Ok(());
}
}
}
let target_module_loc = self.machine_st.registers[2];
unify_fn!(
&mut self.machine_st,
module_loc,
target_module_loc
);
if self.machine_st.fail {
return Ok(());
}
// assemble goal from pre-loaded (narity) and supplementary
// (arity) arguments.
let h = self.machine_st.heap.len();
self.machine_st.heap.push(atom_as_cell!(name, narity + arity));
let target_goal = if narity + arity > 0 {
for idx in 1 .. narity + 1 {
self.machine_st.heap.push(self.machine_st.heap[s + idx]);
}
for idx in 1 .. arity + 1 {
self.machine_st.heap.push(self.machine_st.registers[3 + idx]);
}
str_loc_as_cell!(h)
} else {
heap_loc_as_cell!(h)
};
let target_qualified_goal = self.machine_st.registers[1];
unify_fn!(
&mut self.machine_st,
target_goal,
target_qualified_goal
);
Ok(())
}
#[inline(always)]
pub(crate) fn is_reset_cont_marker(&self, p: usize) -> bool {
match &self.code[p] {