improve call/N implementation (#1829)

This commit is contained in:
Mark
2023-06-10 01:25:47 -06:00
parent 4d982d22c1
commit d7f5675727
8 changed files with 596 additions and 951 deletions

View File

@@ -560,8 +560,8 @@ enum SystemClauseType {
StripModule, StripModule,
#[strum_discriminants(strum(props(Arity = "4", Name = "$compile_inline_or_expanded_goal")))] #[strum_discriminants(strum(props(Arity = "4", Name = "$compile_inline_or_expanded_goal")))]
CompileInlineOrExpandedGoal, CompileInlineOrExpandedGoal,
#[strum_discriminants(strum(props(Arity = "arity", Name = "$call_inline")))] #[strum_discriminants(strum(props(Arity = "arity", Name = "$fast_call")))]
InlineCallN(usize), FastCallN(usize),
#[strum_discriminants(strum(props(Arity = "1", Name = "$is_expanded_or_inlined")))] #[strum_discriminants(strum(props(Arity = "1", Name = "$is_expanded_or_inlined")))]
IsExpandedOrInlined, IsExpandedOrInlined,
#[strum_discriminants(strum(props(Arity = "3", Name = "$get_clause_p")))] #[strum_discriminants(strum(props(Arity = "3", Name = "$get_clause_p")))]
@@ -1472,11 +1472,11 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::DefaultExecuteN(arity) => { &Instruction::DefaultExecuteN(arity) => {
functor!(atom!("execute_default_n"), [fixnum(arity)]) functor!(atom!("execute_default_n"), [fixnum(arity)])
} }
&Instruction::CallInlineCallN(arity) => { &Instruction::CallFastCallN(arity) => {
functor!(atom!("call_n_inline"), [fixnum(arity)]) functor!(atom!("call_fast_call_n"), [fixnum(arity)])
} }
&Instruction::ExecuteInlineCallN(arity) => { &Instruction::ExecuteFastCallN(arity) => {
functor!(atom!("call_n_inline"), [fixnum(arity)]) functor!(atom!("execute_fast_call_n"), [fixnum(arity)])
} }
&Instruction::CallTermGreaterThan | &Instruction::CallTermGreaterThan |
&Instruction::CallTermLessThan | &Instruction::CallTermLessThan |

File diff suppressed because it is too large Load Diff

View File

@@ -5058,12 +5058,12 @@ impl Machine {
self.machine_st.fail = !self.is_expanded_or_inlined(); self.machine_st.fail = !self.is_expanded_or_inlined();
step_or_fail!(self, self.machine_st.p = self.machine_st.cp); step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
} }
&Instruction::CallInlineCallN(arity) => { &Instruction::CallFastCallN(arity) => {
let call_at_index = |wam: &mut Machine, name, arity, ptr| { let call_at_index = |wam: &mut Machine, name, arity, ptr| {
wam.try_call(name, arity, ptr) wam.try_call(name, arity, ptr)
}; };
try_or_throw!(self.machine_st, self.call_inline(arity, call_at_index)); try_or_throw!(self.machine_st, self.fast_call(arity, call_at_index));
if self.machine_st.fail { if self.machine_st.fail {
self.machine_st.backtrack(); self.machine_st.backtrack();
@@ -5074,12 +5074,12 @@ impl Machine {
); );
} }
} }
&Instruction::ExecuteInlineCallN(arity) => { &Instruction::ExecuteFastCallN(arity) => {
let call_at_index = |wam: &mut Machine, name, arity, ptr| { let call_at_index = |wam: &mut Machine, name, arity, ptr| {
wam.try_execute(name, arity, ptr) wam.try_execute(name, arity, ptr)
}; };
try_or_throw!(self.machine_st, self.call_inline(arity, call_at_index)); try_or_throw!(self.machine_st, self.fast_call(arity, call_at_index));
if self.machine_st.fail { if self.machine_st.fail {
self.machine_st.backtrack(); self.machine_st.backtrack();

View File

@@ -1698,6 +1698,21 @@ impl Machine {
let add_clause = || { let add_clause = || {
let term = loader.read_term_from_heap(temp_v!(2))?; let term = loader.read_term_from_heap(temp_v!(2))?;
let indexing_arg = match term.name() {
Some(atom!(":-")) => term.first_arg().and_then(Term::first_arg),
Some(_) => term.first_arg(),
None => None,
};
if let Some(indexing_term) = indexing_arg {
if let Some(indexing_name) = indexing_term.name() {
loader.wam_prelude
.indices
.goal_expansion_indices
.insert((indexing_name, indexing_term.arity()));
}
}
loader.incremental_compile_clause( loader.incremental_compile_clause(
(atom!("goal_expansion"), 2), (atom!("goal_expansion"), 2),
term, term,

View File

@@ -8,7 +8,7 @@ use crate::machine::machine_state::*;
use crate::machine::streams::Stream; use crate::machine::streams::Stream;
use fxhash::FxBuildHasher; use fxhash::FxBuildHasher;
use indexmap::IndexMap; use indexmap::{IndexMap, IndexSet};
use modular_bitfield::{BitfieldSpecifier, bitfield}; use modular_bitfield::{BitfieldSpecifier, bitfield};
use modular_bitfield::specifiers::*; use modular_bitfield::specifiers::*;
@@ -243,12 +243,15 @@ pub(crate) type LocalExtensiblePredicates =
pub(crate) type CodeDir = IndexMap<PredicateKey, CodeIndex, FxBuildHasher>; pub(crate) type CodeDir = IndexMap<PredicateKey, CodeIndex, FxBuildHasher>;
pub(crate) type GoalExpansionIndices = IndexSet<PredicateKey, FxBuildHasher>;
#[derive(Debug)] #[derive(Debug)]
pub struct IndexStore { pub struct IndexStore {
pub(super) code_dir: CodeDir, pub(super) code_dir: CodeDir,
pub(super) extensible_predicates: ExtensiblePredicates, pub(super) extensible_predicates: ExtensiblePredicates,
pub(super) local_extensible_predicates: LocalExtensiblePredicates, pub(super) local_extensible_predicates: LocalExtensiblePredicates,
pub(super) global_variables: GlobalVarDir, pub(super) global_variables: GlobalVarDir,
pub(super) goal_expansion_indices: GoalExpansionIndices,
pub(super) meta_predicates: MetaPredicateDir, pub(super) meta_predicates: MetaPredicateDir,
pub(super) modules: ModuleDir, pub(super) modules: ModuleDir,
pub(super) op_dir: OpDir, pub(super) op_dir: OpDir,
@@ -257,6 +260,11 @@ pub struct IndexStore {
} }
impl IndexStore { impl IndexStore {
#[inline(always)]
pub(crate) fn goal_expansion_defined(&self, key: PredicateKey) -> bool {
self.goal_expansion_indices.contains(&key)
}
pub(crate) fn get_predicate_skeleton_mut( pub(crate) fn get_predicate_skeleton_mut(
&mut self, &mut self,
compilation_target: &CompilationTarget, compilation_target: &CompilationTarget,

View File

@@ -1202,29 +1202,29 @@ impl Machine {
#[inline(always)] #[inline(always)]
pub(crate) fn deref_register(&mut self, i: usize) -> HeapCellValue { pub(crate) fn deref_register(&mut self, i: usize) -> HeapCellValue {
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[i])) self.machine_st.store(self.machine_st.deref(self.machine_st.registers[i]))
} }
#[inline(always)] #[inline(always)]
pub(crate) fn call_inline( pub(crate) fn fast_call(
&mut self, &mut self,
arity: usize, arity: usize,
call_at_index: impl Fn(&mut Machine, Atom, usize, IndexPtr) -> CallResult, call_at_index: impl Fn(&mut Machine, Atom, usize, IndexPtr) -> CallResult,
) -> CallResult { ) -> CallResult {
let arity = arity - 1; let arity = arity - 1;
let goal = self.deref_register(1); let (mut module_name, mut goal) = self.machine_st.strip_module(
self.machine_st.registers[1],
heap_loc_as_cell!(0),
);
let load_registers = |machine_st: &mut MachineState, goal: HeapCellValue| -> Option<PredicateKey> { let load_registers = |machine_st: &mut MachineState, goal: HeapCellValue, goal_arity: usize| {
read_heap_cell!(goal, read_heap_cell!(goal,
(HeapCellValueTag::Str, s) => { (HeapCellValueTag::Str | HeapCellValueTag::Atom, s) => {
let (name, goal_arity) = cell_as_atom_cell!(machine_st.heap[s]) if goal_arity > 1 {
.get_name_and_arity();
if goal_arity > 0 {
for idx in (1 .. arity + 1).rev() { for idx in (1 .. arity + 1).rev() {
machine_st.registers[idx + goal_arity] = machine_st.registers[idx + 1]; machine_st.registers[idx + goal_arity] = machine_st.registers[idx + 1];
} }
} else { } else if goal_arity == 0 {
for idx in 1 .. arity + 1 { for idx in 1 .. arity + 1 {
machine_st.registers[idx] = machine_st.registers[idx + 1]; machine_st.registers[idx] = machine_st.registers[idx + 1];
} }
@@ -1233,8 +1233,6 @@ impl Machine {
for idx in 1 .. goal_arity + 1 { for idx in 1 .. goal_arity + 1 {
machine_st.registers[idx] = machine_st.heap[s+idx]; machine_st.registers[idx] = machine_st.heap[s+idx];
} }
Some((name, goal_arity))
} }
_ => { _ => {
unreachable!() unreachable!()
@@ -1242,35 +1240,70 @@ impl Machine {
) )
}; };
read_heap_cell!(goal, let (mut name, mut goal_arity, index_cell_opt) = read_heap_cell!(goal,
(HeapCellValueTag::Str, s) => { (HeapCellValueTag::Str, s) => {
let goal_arity = cell_as_atom_cell!(self.machine_st.heap[s]).get_arity(); let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity();
if self.machine_st.heap.len() > s + goal_arity + 1 { (name, arity, if self.machine_st.heap.len() > s + arity + 1 {
let index_cell = self.machine_st.heap[s+goal_arity+1]; get_structure_index(self.machine_st.heap[s + arity + 1])
} else {
if let Some(code_index) = get_structure_index(index_cell) { None
if code_index.is_undefined() { })
self.machine_st.fail = true; }
return Ok(()); (HeapCellValueTag::Atom, (name, arity)) => {
} debug_assert_eq!(arity, 0);
(name, arity, None)
match load_registers(&mut self.machine_st, goal) {
Some((name, goal_arity)) => {
let arity = goal_arity + arity;
self.machine_st.neck_cut();
return call_at_index(self, name, arity, code_index.get());
}
None => {
}
}
}
}
} }
_ => { _ => {
self.machine_st.fail = true;
return Ok(());
} }
); );
let mut arity = arity + goal_arity;
let index_cell = index_cell_opt.or_else(|| {
let is_internal_call = name == atom!("$call") && goal_arity > 0;
if !is_internal_call && self.indices.goal_expansion_defined((name, arity)) {
None
} else {
if is_internal_call {
debug_assert_eq!(goal.get_tag(), HeapCellValueTag::Str);
goal = self.machine_st.heap[goal.get_value()+1];
(module_name, goal) = self.machine_st.strip_module(goal, module_name);
if let Some((inner_name, inner_arity)) = self.machine_st.name_and_arity_from_heap(goal) {
arity -= goal_arity;
(name, goal_arity) = (inner_name, inner_arity);
arity += goal_arity;
} else {
return None;
}
}
let module_name = if module_name.get_tag() != HeapCellValueTag::Atom {
if let Some(load_context) = self.load_contexts.last() {
load_context.module
} else {
atom!("user")
}
} else {
cell_as_atom!(module_name)
};
self.indices.get_predicate_code_index(name, arity, module_name)
}
});
if let Some(code_index) = index_cell {
if !code_index.is_undefined() {
load_registers(&mut self.machine_st, goal, goal_arity);
self.machine_st.neck_cut();
return call_at_index(self, name, arity, code_index.get());
}
}
self.machine_st.fail = true; self.machine_st.fail = true;
Ok(()) Ok(())
} }
@@ -1489,35 +1522,12 @@ impl Machine {
} }
#[inline(always)] #[inline(always)]
pub(crate) fn prepare_call_clause(&mut self, arity: usize) -> CallResult { pub(crate) fn strip_module(&mut self) {
let (module_loc, qualified_goal) = self.machine_st.strip_module( let (module_loc, qualified_goal) = self.machine_st.strip_module(
self.machine_st.registers[3], self.machine_st.registers[1],
self.machine_st.registers[2], 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]; let target_module_loc = self.machine_st.registers[2];
unify_fn!( unify_fn!(
@@ -1526,9 +1536,26 @@ impl Machine {
target_module_loc target_module_loc
); );
if self.machine_st.fail { let target_qualified_goal = self.machine_st.registers[3];
return Ok(());
} unify_fn!(
&mut self.machine_st,
qualified_goal,
target_qualified_goal
);
}
#[inline(always)]
pub(crate) fn prepare_call_clause(&mut self, arity: usize) -> CallResult {
let qualified_goal = self.deref_register(2);
// the first two arguments don't belong to the containing call/N.
let arity = arity - 2;
let (name, narity, s) = self.machine_st.setup_call_n_init_goal_info(
qualified_goal,
arity,
)?;
// assemble goal from pre-loaded (narity) and supplementary // assemble goal from pre-loaded (narity) and supplementary
// (arity) arguments. // (arity) arguments.
@@ -1544,15 +1571,10 @@ impl Machine {
} }
for idx in 1 .. arity + 1 { for idx in 1 .. arity + 1 {
self.machine_st.heap.push(self.machine_st.registers[3 + idx]); self.machine_st.heap.push(self.machine_st.registers[2 + idx]);
} }
let index_cell = self.machine_st.heap[s + narity + 1]; if narity + arity > 0 {
if get_structure_index(index_cell).is_some() {
self.machine_st.heap.push(index_cell);
str_loc_as_cell!(h)
} else if narity + arity > 0 {
str_loc_as_cell!(h) str_loc_as_cell!(h)
} else { } else {
heap_loc_as_cell!(h) heap_loc_as_cell!(h)
@@ -1570,6 +1592,65 @@ impl Machine {
Ok(()) Ok(())
} }
#[inline(always)]
pub(crate) fn dynamic_module_resolution(
&mut self,
narity: usize,
) -> Result<(Atom, PredicateKey), MachineStub> {
let module_name = self.deref_register(1);
let module_name = read_heap_cell!(module_name,
(HeapCellValueTag::Atom, (name, _arity)) => {
debug_assert_eq!(_arity, 0);
name
}
(HeapCellValueTag::Str, s) => {
let (module_name, _arity) = cell_as_atom_cell!(self.machine_st.heap[s])
.get_name_and_arity();
debug_assert_eq!(_arity, 0);
module_name
}
_ if module_name.is_var() => {
if let Some(load_context) = self.load_contexts.last() {
load_context.module
} else {
atom!("user")
}
}
_ => {
unreachable!()
}
);
let goal = self.deref_register(2);
let (name, arity, s) = self.machine_st.setup_call_n_init_goal_info(goal, narity)?;
// TODO: think we just need the 'Greater' branch here.
match arity.cmp(&2) {
Ordering::Less => {
for i in arity + 1..arity + narity + 1 {
self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity];
}
}
Ordering::Greater => {
for i in (arity + 1..arity + narity + 1).rev() {
self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity];
}
}
Ordering::Equal => {}
}
let key = (name, arity + narity);
for i in 1..arity + 1 {
self.machine_st.registers[i] = self.machine_st.heap[s + i];
}
Ok((module_name, key))
}
#[inline(always)] #[inline(always)]
pub(crate) fn is_reset_cont_marker(&self, p: usize) -> bool { pub(crate) fn is_reset_cont_marker(&self, p: usize) -> bool {
match &self.code[p] { match &self.code[p] {
@@ -3606,60 +3687,6 @@ impl Machine {
} }
} }
#[inline(always)]
pub(crate) fn dynamic_module_resolution(
&mut self,
narity: usize,
) -> Result<(Atom, PredicateKey), MachineStub> {
let module_name = self.deref_register(1);
let module_name = read_heap_cell!(module_name,
(HeapCellValueTag::Atom, (name, _arity)) => {
debug_assert_eq!(_arity, 0);
name
}
(HeapCellValueTag::Str, s) => {
let (module_name, _arity) = cell_as_atom_cell!(self.machine_st.heap[s])
.get_name_and_arity();
debug_assert_eq!(_arity, 0);
module_name
}
_ if module_name.is_var() => {
atom!("user")
}
_ => {
unreachable!()
}
);
let goal = self.deref_register(2);
let (name, arity, s) = self.machine_st.setup_call_n_init_goal_info(goal, narity)?;
match arity.cmp(&2) {
Ordering::Less => {
for i in arity + 1..arity + narity + 1 {
self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity];
}
}
Ordering::Greater => {
for i in (arity + 1..arity + narity + 1).rev() {
self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity];
}
}
Ordering::Equal => {}
}
let key = (name, arity + narity);
for i in 1..arity + 1 {
self.machine_st.registers[i] = self.machine_st.heap[s + i];
}
Ok((module_name, key))
}
#[inline(always)] #[inline(always)]
pub(crate) fn lookup_db_ref(&mut self) { pub(crate) fn lookup_db_ref(&mut self) {
let name = cell_as_atom!(self.deref_register(1)); let name = cell_as_atom!(self.deref_register(1));

View File

@@ -574,6 +574,7 @@ macro_rules! index_store {
extensible_predicates: ExtensiblePredicates::with_hasher(FxBuildHasher::default()), extensible_predicates: ExtensiblePredicates::with_hasher(FxBuildHasher::default()),
local_extensible_predicates: LocalExtensiblePredicates::with_hasher(FxBuildHasher::default()), local_extensible_predicates: LocalExtensiblePredicates::with_hasher(FxBuildHasher::default()),
global_variables: GlobalVarDir::with_hasher(FxBuildHasher::default()), global_variables: GlobalVarDir::with_hasher(FxBuildHasher::default()),
goal_expansion_indices: GoalExpansionIndices::with_hasher(FxBuildHasher::default()),
meta_predicates: MetaPredicateDir::with_hasher(FxBuildHasher::default()), meta_predicates: MetaPredicateDir::with_hasher(FxBuildHasher::default()),
modules: $modules, modules: $modules,
op_dir: $op_dir, op_dir: $op_dir,

View File

@@ -181,7 +181,8 @@ submit_query_and_print_results_(Term, VarList) :-
'$get_b_value'(B), '$get_b_value'(B),
bb_put('$report_all', false), bb_put('$report_all', false),
bb_put('$report_n_more', 0), bb_put('$report_n_more', 0),
atts:call_residue_vars(user:Term, AttrVars), expand_goal(Term, user, Term0),
atts:call_residue_vars(user:Term0, AttrVars),
write_eqs_and_read_input(B, VarList, AttrVars), write_eqs_and_read_input(B, VarList, AttrVars),
!. !.
submit_query_and_print_results_(_, _) :- submit_query_and_print_results_(_, _) :-