improve efficiency of call/N, replace '$call_with_default_policy' with
'$call_with_inference_counting'
This commit is contained in:
@@ -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)?);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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] {
|
||||
|
||||
Reference in New Issue
Block a user