simplify and optimize attributed variables (#1590, #1634, #1730)

This commit is contained in:
Mark Thom
2023-02-10 00:09:22 -07:00
parent 9454d670c7
commit 359619e035
6 changed files with 289 additions and 243 deletions

View File

@@ -272,16 +272,10 @@ enum SystemClauseType {
PathCanonical,
#[strum_discriminants(strum(props(Arity = "3", Name = "$file_time")))]
FileTime,
#[strum_discriminants(strum(props(Arity = "1", Name = "$del_attr_non_head")))]
DeleteAttribute,
#[strum_discriminants(strum(props(Arity = "1", Name = "$del_attr_head")))]
DeleteHeadAttribute,
#[strum_discriminants(strum(props(Arity = "arity", Name = "$module_call")))]
DynamicModuleResolution(usize),
#[strum_discriminants(strum(props(Arity = "arity", Name = "$prepare_call_clause")))]
PrepareCallClause(usize),
#[strum_discriminants(strum(props(Arity = "1", Name = "$enqueue_attr_var")))]
EnqueueAttributedVar,
#[strum_discriminants(strum(props(Arity = "2", Name = "$fetch_global_var")))]
FetchGlobalVar,
#[strum_discriminants(strum(props(Arity = "1", Name = "$first_stream")))]
@@ -566,8 +560,12 @@ enum SystemClauseType {
GetClauseP,
#[strum_discriminants(strum(props(Arity = "6", Name = "$invoke_clause_at_p")))]
InvokeClauseAtP,
#[strum_discriminants(strum(props(Arity = "2", Name = "$get_from_attr_list")))]
#[strum_discriminants(strum(props(Arity = "3", Name = "$get_from_attr_list")))]
GetFromAttributedVarList,
#[strum_discriminants(strum(props(Arity = "3", Name = "$put_to_attr_list")))]
PutToAttributedVarList,
#[strum_discriminants(strum(props(Arity = "3", Name = "$del_from_attr_list")))]
DeleteFromAttributedVarList,
REPL(REPLCodePtr),
}
@@ -1620,8 +1618,6 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::CallDeleteDirectory(_) |
&Instruction::CallPathCanonical(_) |
&Instruction::CallFileTime(_) |
&Instruction::CallDeleteAttribute(_) |
&Instruction::CallDeleteHeadAttribute(_) |
&Instruction::CallDynamicModuleResolution(..) |
&Instruction::CallPrepareCallClause(..) |
&Instruction::CallCompileInlineOrExpandedGoal(..) |
@@ -1629,7 +1625,8 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::CallGetClauseP(_) |
&Instruction::CallInvokeClauseAtP(_) |
&Instruction::CallGetFromAttributedVarList(_) |
&Instruction::CallEnqueueAttributedVar(_) |
&Instruction::CallPutToAttributedVarList(_) |
&Instruction::CallDeleteFromAttributedVarList(_) |
&Instruction::CallFetchGlobalVar(_) |
&Instruction::CallFirstStream(_) |
&Instruction::CallFlushOutput(_) |
@@ -1836,8 +1833,6 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::ExecuteDeleteDirectory(_) |
&Instruction::ExecutePathCanonical(_) |
&Instruction::ExecuteFileTime(_) |
&Instruction::ExecuteDeleteAttribute(_) |
&Instruction::ExecuteDeleteHeadAttribute(_) |
&Instruction::ExecuteDynamicModuleResolution(..) |
&Instruction::ExecutePrepareCallClause(..) |
&Instruction::ExecuteCompileInlineOrExpandedGoal(..) |
@@ -1845,7 +1840,8 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::ExecuteGetClauseP(_) |
&Instruction::ExecuteInvokeClauseAtP(_) |
&Instruction::ExecuteGetFromAttributedVarList(_) |
&Instruction::ExecuteEnqueueAttributedVar(_) |
&Instruction::ExecutePutToAttributedVarList(_) |
&Instruction::ExecuteDeleteFromAttributedVarList(_) |
&Instruction::ExecuteFetchGlobalVar(_) |
&Instruction::ExecuteFirstStream(_) |
&Instruction::ExecuteFlushOutput(_) |

View File

@@ -19,76 +19,12 @@
'$default_attr_list'(PGs, Module, AttrVar).
'$default_attr_list'([], _, _) --> [].
'$absent_attr'(V, Attr) :-
'$get_attr_list'(V, Ls),
'$absent_from_list'(Ls, Attr).
'$absent_from_list'(X, Attr) :-
( var(X) ->
true
; X = [L|Ls],
L \= Attr ->
'$absent_from_list'(Ls, Attr)
).
'$get_attr'(V, Attr) :-
'$get_attr_list'(V, Ls),
nonvar(Ls),
'$get_from_attr_list'(Ls, Attr).
/*
'$get_from_list'([L|Ls], V, Attr) :-
nonvar(L),
( L \= Attr ->
nonvar(Ls),
'$get_from_list'(Ls, V, Attr)
; L = Attr
).
*/
'$put_attr'(V, Attr) :-
'$get_attr_list'(V, Ls),
'$add_to_list'(Ls, V, Attr).
'$add_to_list'(Ls, V, Attr) :-
( var(Ls) ->
Ls = [Attr | _],
'$enqueue_attr_var'(V)
; Ls = [_ | Ls0],
'$add_to_list'(Ls0, V, Attr)
).
'$del_attr'(Ls0, _, _) :-
var(Ls0),
!.
'$del_attr'(Ls0, V, Attr) :-
Ls0 = [Att | Ls1],
nonvar(Att),
( Att \= Attr ->
'$del_attr_buried'(Ls0, Ls1, V, Attr)
; '$del_attr_head'(V),
'$del_attr'(Ls1, V, Attr)
).
'$del_attr_step'(Ls1, V, Attr) :-
( nonvar(Ls1) ->
Ls1 = [_ | Ls2],
'$del_attr_buried'(Ls1, Ls2, V, Attr)
'$absent_attr'(V, Module, Attr) :-
( '$get_from_attr_list'(V, Module, Attr) ->
false
; true
).
%% assumptions: Ls0 is a list, Ls1 is its tail;
%% the head of Ls0 can be ignored.
'$del_attr_buried'(Ls0, Ls1, V, Attr) :-
( var(Ls1) -> true
; Ls1 = [Att | Ls2] ->
( Att \= Attr ->
'$del_attr_buried'(Ls1, Ls2, V, Attr)
; '$del_attr_non_head'(Ls0), %% set tail of Ls0 = tail of Ls1. can be undone by backtracking.
'$del_attr_step'(Ls1, V, Attr)
)
).
'$copy_attr_list'(L, _Module, []) :- var(L), !.
'$copy_attr_list'([Module0:Att|Atts], Module, CopiedAtts) :-
( Module0 == Module ->
@@ -144,38 +80,28 @@ put_attr(Name, Arity, Module) -->
{ functor(Attr, Name, Arity) },
[(put_atts(V, +Attr) :-
!,
functor(Attr, Head, Arity),
functor(AttrForm, Head, Arity),
'$get_attr_list'(V, Ls),
atts:'$del_attr'(Ls, V, Module:AttrForm),
atts:'$put_attr'(V, Module:Attr)),
'$put_to_attr_list'(V, Module, Attr)),
(put_atts(V, Attr) :-
!,
functor(Attr, Head, Arity),
functor(AttrForm, Head, Arity),
'$get_attr_list'(V, Ls),
atts:'$del_attr'(Ls, V, Module:AttrForm),
atts:'$put_attr'(V, Module:Attr)),
'$put_to_attr_list'(V, Module, Attr)),
(put_atts(V, -Attr) :-
!,
functor(Attr, _, _),
'$get_attr_list'(V, Ls),
atts:'$del_attr'(Ls, V, Module:Attr))].
'$del_from_attr_list'(V, Module, Attr))].
get_attr(Name, Arity, Module) -->
{ functor(Attr, Name, Arity) },
[(get_atts(V, +Attr) :-
!,
functor(Attr, _, _),
atts:'$get_attr'(V, Module:Attr)),
atts:'$get_from_attr_list'(V, Module, Attr)),
(get_atts(V, Attr) :-
!,
functor(Attr, _, _),
atts:'$get_attr'(V, Module:Attr)),
atts:'$get_from_attr_list'(V, Module, Attr)),
(get_atts(V, -Attr) :-
!,
functor(Attr, _, _),
atts:'$absent_attr'(V, Module:Attr))].
atts:'$absent_attr'(V, Module, Attr))].
user:goal_expansion(Term, M:put_atts(Var, Attr)) :-
nonvar(Term),

View File

@@ -52,6 +52,7 @@ impl MachineState {
self.cp = INSTALL_VERIFY_ATTR_INTERRUPT;
}
debug_assert_eq!(self.heap[h].get_tag(), HeapCellValueTag::AttrVar);
self.attr_var_init.bindings.push((h, addr));
}
@@ -63,10 +64,9 @@ impl MachineState {
.map(|(ref h, _)| attr_var_as_cell!(*h));
let var_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter));
let iter = self.attr_var_init.bindings.drain(0..).map(|(_, ref v)| *v);
let value_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter));
(var_list_addr, value_list_addr)
}

View File

@@ -3570,22 +3570,6 @@ impl Machine {
self.file_time();
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallDeleteAttribute(_) => {
self.delete_attribute();
self.machine_st.p += 1;
}
&Instruction::ExecuteDeleteAttribute(_) => {
self.delete_attribute();
self.machine_st.p = self.machine_st.cp;
}
&Instruction::CallDeleteHeadAttribute(_) => {
self.delete_head_attribute();
self.machine_st.p += 1;
}
&Instruction::ExecuteDeleteHeadAttribute(_) => {
self.delete_head_attribute();
self.machine_st.p = self.machine_st.cp;
}
&Instruction::CallDynamicModuleResolution(arity, _) => {
let (module_name, key) = try_or_throw!(
self.machine_st,
@@ -3616,14 +3600,6 @@ impl Machine {
self.machine_st.backtrack();
}
}
&Instruction::CallEnqueueAttributedVar(_) => {
self.enqueue_attributed_var();
self.machine_st.p += 1;
}
&Instruction::ExecuteEnqueueAttributedVar(_) => {
self.enqueue_attributed_var();
self.machine_st.p = self.machine_st.cp;
}
&Instruction::CallFetchGlobalVar(_) => {
self.fetch_global_var();
step_or_fail!(self, self.machine_st.p += 1);
@@ -5215,6 +5191,22 @@ impl Machine {
self.get_from_attributed_variable_list();
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallPutToAttributedVarList(_) => {
self.put_to_attributed_variable_list();
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecutePutToAttributedVarList(_) => {
self.put_to_attributed_variable_list();
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallDeleteFromAttributedVarList(_) => {
self.delete_from_attributed_variable_list();
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecuteDeleteFromAttributedVarList(_) => {
self.delete_from_attributed_variable_list();
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
}
}

View File

@@ -870,7 +870,18 @@ impl Machine {
let l = self.machine_st.trail[i + 1].get_value() as usize;
if l < self.machine_st.hb {
if h == l {
self.machine_st.heap[h] = heap_loc_as_cell!(h);
} else {
read_heap_cell!(self.machine_st.heap[l],
(HeapCellValueTag::Var) => {
self.machine_st.heap[h] = list_loc_as_cell!(l);
}
_ => {
self.machine_st.heap[h] = heap_loc_as_cell!(l);
}
);
}
} else {
self.machine_st.heap[h] = heap_loc_as_cell!(h);
}

View File

@@ -456,7 +456,41 @@ impl BrentAlgState {
}
}
#[derive(Debug)]
enum MatchSite {
NoMatchVarTail(usize), // no match, we refer to the location of the uninstantiated tail instead.
Match(usize), // a match
}
#[derive(Debug)]
struct AttrListMatch {
match_site: MatchSite,
prev_tail: Option<usize>,
}
impl MachineState {
pub(crate) fn get_attr_var_list(&mut self, attr_var: HeapCellValue) -> Option<usize> {
read_heap_cell!(attr_var,
(HeapCellValueTag::AttrVar, h) => {
Some(h + 1)
}
(HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
// create an AttrVar in the heap.
let h = self.heap.len();
self.heap.push(attr_var_as_cell!(h));
self.heap.push(heap_loc_as_cell!(h+1));
self.bind(Ref::attr_var(h), attr_var);
Some(h + 1)
}
_ => {
None
}
)
}
pub(crate) fn name_and_arity_from_heap(&self, cell: HeapCellValue) -> Option<PredicateKey> {
read_heap_cell!(self.store(self.deref(cell)),
(HeapCellValueTag::Str, s) => {
@@ -4308,15 +4342,8 @@ impl Machine {
(HeapCellValueTag::AttrVar, h) => {
h+1
}
(HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
// create an AttrVar in the heap.
let h = self.machine_st.heap.len();
self.machine_st.heap.push(attr_var_as_cell!(h));
self.machine_st.heap.push(heap_loc_as_cell!(h+1));
self.machine_st.bind(Ref::attr_var(h), attr_var);
h + 1
(HeapCellValueTag::Var, h) => {
h
}
_ => {
self.machine_st.fail = true;
@@ -4330,67 +4357,40 @@ impl Machine {
#[inline(always)]
pub(crate) fn get_from_attributed_variable_list(&mut self) {
let mut attrs_list = self.deref_register(1);
let attr = self.deref_register(2);
let (name, arity) = match self.machine_st.name_and_arity_from_heap(attr) {
Some(key) => key,
None => {
self.machine_st.fail = true;
return;
}
};
while let HeapCellValueTag::Lis = attrs_list.get_tag() {
let mut list_head = self.machine_st.heap[attrs_list.get_value()];
loop {
read_heap_cell!(list_head,
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
if list_head != self.machine_st.heap[h] {
list_head = self.machine_st.heap[h];
} else {
self.machine_st.fail = true;
return;
}
}
(HeapCellValueTag::Str | HeapCellValueTag::Atom) => {
let (t_name, t_arity) = self.machine_st
.name_and_arity_from_heap(list_head)
.unwrap();
if name == t_name && arity == t_arity {
let old_tr = self.machine_st.tr;
unify!(self.machine_st, list_head, attr);
if self.machine_st.fail {
let curr_tr = self.machine_st.trail.len();
self.unwind_trail(old_tr, curr_tr);
self.machine_st.tr = old_tr;
self.machine_st.pdl.clear();
self.machine_st.fail = false;
} else {
return;
}
}
break;
let attr_var = self.deref_register(1);
let attr = self.deref_register(3);
let attr_var_list = read_heap_cell!(attr_var,
(HeapCellValueTag::AttrVar, h) => {
self.machine_st.heap[h+1]
}
_ => {
break;
}
);
}
attrs_list = self.machine_st.store(
self.machine_st.deref(self.machine_st.heap[attrs_list.get_value()+1])
);
}
self.machine_st.fail = true;
return;
}
);
let module = self.deref_register(2);
match self.match_attribute(attr_var_list, module, attr) {
Some(AttrListMatch { match_site: MatchSite::Match(match_site), .. }) => {
let list_head = self.machine_st.heap[match_site];
if list_head.get_value() == match_site {
// at the end of the list, no match found in this case.
self.machine_st.fail = true;
} else {
let (_, qualified_goal) = self.machine_st.strip_module(
list_head,
empty_list_as_cell!(),
);
unify!(self.machine_st, qualified_goal, attr);
}
}
_ => {
self.machine_st.fail = true;
}
}
}
#[inline(always)]
@@ -4427,81 +4427,202 @@ impl Machine {
}
#[inline(always)]
pub(crate) fn enqueue_attributed_var(&mut self) {
let addr = self.deref_register(1);
read_heap_cell!(addr,
pub(crate) fn delete_from_attributed_variable_list(&mut self) {
let attr_var = self.deref_register(1);
let attr = self.deref_register(3);
let attr_var_list = read_heap_cell!(attr_var,
(HeapCellValueTag::AttrVar, h) => {
self.machine_st.attr_var_init.attr_var_queue.push(h);
h + 1
}
_ => {
return;
}
);
let module = self.deref_register(2);
match self.match_attribute(self.machine_st.heap[attr_var_list], module, attr) {
Some(AttrListMatch { prev_tail, match_site: MatchSite::Match(match_site) }) => {
let prev_tail = if let Some(prev_tail) = prev_tail {
// not at the head.
prev_tail
} else {
if self.machine_st.heap[match_site + 1].is_var() {
let h = attr_var.get_value();
self.machine_st.heap[h] = heap_loc_as_cell!(h);
self.machine_st.trail(TrailRef::Ref(Ref::attr_var(h)));
}
// at the head.
attr_var_list
};
if self.machine_st.heap[match_site + 1].get_tag() == HeapCellValueTag::Lis {
let prev_tail_value = self.machine_st.heap[match_site + 1].get_value();
self.machine_st.heap[prev_tail].set_value(prev_tail_value);
} else {
self.machine_st.heap[prev_tail] = heap_loc_as_cell!(prev_tail);
}
self.machine_st.trail(TrailRef::AttrVarListLink(prev_tail, match_site));
}
_ => {
}
}
}
#[inline(always)]
pub(crate) fn put_to_attributed_variable_list(&mut self) {
let attr_var = self.deref_register(1);
let attr = self.deref_register(3);
let attr_var_list = match self.machine_st.get_attr_var_list(attr_var) {
Some(h) => h,
None => {
self.machine_st.fail = true;
return;
}
};
let module = self.deref_register(2);
/*
* How to handle attribute trailing using just AttrVarListLink (which
* should be re-named to something more general) in unwind_trail:
*
* Given AttrVarListLink(h, l):
*
* 1. Check cell at offset l.
* 2. If h == l, set heap[h] = heap_loc_as_cell!(h).
* 3. If cell is a Var, set heap[h] = list_loc_as_cell!(l).
* 4. Otherwise, cell points to an element of the list which is therefore
* an atom or str. Set heap[h] accordingly.
*
* For this to work, all elements of attributed variable lists must be
* heap cell locs pointing to later elements in the heap, either atoms (0-arity)
* or str cells (> 0-arity).
*/
let h = self.machine_st.heap.len();
self.machine_st.heap.push(str_loc_as_cell!(h+1));
self.machine_st.heap.extend(functor!(atom!(":"), [cell(module), cell(attr)]));
match self.match_attribute(self.machine_st.heap[attr_var_list], module, attr) {
Some(AttrListMatch { match_site, .. }) => {
let (match_site, l) = match match_site {
MatchSite::NoMatchVarTail(match_site) => {
let l = self.machine_st.heap[match_site].get_value();
// at the end of the (non-empty) list here.
self.machine_st.heap[match_site] = list_loc_as_cell!(h+4);
self.machine_st.heap.push(heap_loc_as_cell!(h));
self.machine_st.heap.push(heap_loc_as_cell!(h+5));
(match_site, l)
}
MatchSite::Match(match_site) => {
let l = self.machine_st.heap[match_site].get_value();
self.machine_st.heap[match_site].set_value(h);
(match_site, l)
}
};
self.machine_st.trail(TrailRef::AttrVarListLink(match_site, l));
}
None => {
// the list is empty.
self.machine_st.heap[attr_var_list] = list_loc_as_cell!(h+4);
self.machine_st.heap.push(heap_loc_as_cell!(h));
self.machine_st.heap.push(heap_loc_as_cell!(h+5));
self.machine_st.attr_var_init.attr_var_queue.push(attr_var_list - 1);
self.machine_st.trail(TrailRef::AttrVarListLink(attr_var_list, attr_var_list));
}
}
}
fn match_attribute(
&self,
mut attrs_list: HeapCellValue,
module: HeapCellValue,
attr: HeapCellValue,
) -> Option<AttrListMatch> {
let (name, arity) = match self.machine_st.name_and_arity_from_heap(attr) {
Some(key) => key,
None => {
return None;
}
};
let mut prev_tail = None;
while let HeapCellValueTag::Lis = attrs_list.get_tag() {
let mut list_head = self.machine_st.heap[attrs_list.get_value()];
loop {
read_heap_cell!(list_head,
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
debug_assert!(list_head != self.machine_st.heap[h]);
list_head = self.machine_st.heap[h];
}
(HeapCellValueTag::Str | HeapCellValueTag::Atom) => {
let (module_loc, qualified_goal) = self.machine_st.strip_module(
list_head,
empty_list_as_cell!(),
);
let (t_name, t_arity) = self.machine_st
.name_and_arity_from_heap(qualified_goal)
.unwrap();
if module == module_loc && name == t_name && arity == t_arity {
return Some(AttrListMatch {
match_site: MatchSite::Match(attrs_list.get_value()),
prev_tail,
});
}
break;
}
_ => {
break;
}
);
}
#[inline(always)]
pub(crate) fn delete_attribute(&mut self) {
let ls0 = self.deref_register(1);
let tail_loc = attrs_list.get_value() + 1;
prev_tail = Some(tail_loc);
if let HeapCellValueTag::Lis = ls0.get_tag() {
let l1 = ls0.get_value();
let ls1 = self.machine_st.store(self.machine_st.deref(heap_loc_as_cell!(l1 + 1)));
// do the work of self.store(self.deref(...)) but inline it
// for speed and simplify it.
let mut list_tail = self.machine_st.heap[tail_loc];
if let HeapCellValueTag::Lis = ls1.get_tag() {
let l2 = ls1.get_value();
let old_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[l1+1]));
let tail = self.machine_st.store(self.machine_st.deref(heap_loc_as_cell!(l2 + 1)));
let tail = if tail.is_var() {
heap_loc_as_cell!(l1 + 1)
loop {
read_heap_cell!(list_tail,
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
if list_tail != self.machine_st.heap[h] {
list_tail = self.machine_st.heap[h];
} else {
tail
};
let trail_ref = read_heap_cell!(old_addr,
(HeapCellValueTag::Var, h) => {
TrailRef::AttrVarHeapLink(h)
return Some(AttrListMatch {
match_site: MatchSite::NoMatchVarTail(h),
prev_tail,
});
}
(HeapCellValueTag::Lis, l) => {
TrailRef::AttrVarListLink(l1 + 1, l)
}
(HeapCellValueTag::Lis) => {
attrs_list = list_tail;
break;
}
_ => {
unreachable!()
}
);
self.machine_st.heap[l1 + 1] = tail;
self.machine_st.trail(trail_ref);
}
}
}
#[inline(always)]
pub(crate) fn delete_head_attribute(&mut self) {
let addr = self.deref_register(1);
debug_assert_eq!(addr.get_tag(), HeapCellValueTag::AttrVar);
let h = addr.get_value();
let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[h + 1]));
debug_assert_eq!(addr.get_tag(), HeapCellValueTag::Lis);
let l = addr.get_value();
let tail = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[l + 1]));
let tail = if tail.is_var() {
self.machine_st.heap[h] = heap_loc_as_cell!(h);
self.machine_st.trail(TrailRef::Ref(Ref::attr_var(h)));
heap_loc_as_cell!(h + 1)
} else {
tail
};
self.machine_st.heap[h + 1] = tail;
self.machine_st.trail(TrailRef::AttrVarListLink(h + 1, l));
None
}
#[inline(always)]