From: Mark Thom Date: Fri, 10 Feb 2023 07:09:22 +0000 (-0700) Subject: simplify and optimize attributed variables (#1590, #1634, #1730) X-Git-Tag: v0.9.2~190 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=359619e0356831beba00e505daa26629b765c4a1;p=scryer-prolog.git simplify and optimize attributed variables (#1590, #1634, #1730) --- diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 23c96068..98295308 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -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(_) | diff --git a/src/lib/atts.pl b/src/lib/atts.pl index 752be478..d6ee47a3 100644 --- a/src/lib/atts.pl +++ b/src/lib/atts.pl @@ -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_atts(V, 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), diff --git a/src/machine/attributed_variables.rs b/src/machine/attributed_variables.rs index 2bab7451..57ea1c22 100644 --- a/src/machine/attributed_variables.rs +++ b/src/machine/attributed_variables.rs @@ -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) } diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 5ea18ba4..1cb1a8fa 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -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); + } } } diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 09883b99..2a498b02 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -870,7 +870,18 @@ impl Machine { let l = self.machine_st.trail[i + 1].get_value() as usize; if l < self.machine_st.hb { - self.machine_st.heap[h] = list_loc_as_cell!(l); + 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); } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index a41d4aef..0854a9f8 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -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, +} + impl MachineState { + pub(crate) fn get_attr_var_list(&mut self, attr_var: HeapCellValue) -> Option { + 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 { read_heap_cell!(self.store(self.deref(cell)), (HeapCellValueTag::Str, s) => { @@ -4306,17 +4340,10 @@ impl Machine { let attr_var = self.deref_register(1); let attr_var_list = read_heap_cell!(attr_var, (HeapCellValueTag::AttrVar, h) => { - h + 1 + 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 => { + 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] + } + _ => { 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(); + let module = self.deref_register(2); - self.unwind_trail(old_tr, curr_tr); - self.machine_st.tr = old_tr; + 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]; - self.machine_st.pdl.clear(); - self.machine_st.fail = false; - } else { - return; - } - } + 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!(), + ); - break; - } - _ => { - break; - } - ); + unify!(self.machine_st, qualified_goal, attr); + } + } + _ => { + self.machine_st.fail = true; } - - attrs_list = self.machine_st.store( - self.machine_st.deref(self.machine_st.heap[attrs_list.get_value()+1]) - ); } - - 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 delete_attribute(&mut self) { - let ls0 = self.deref_register(1); + 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; + } + }; - 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))); + 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). + */ - if let HeapCellValueTag::Lis = ls1.get_tag() { - let l2 = ls1.get_value(); + let h = self.machine_st.heap.len(); - 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))); + self.machine_st.heap.push(str_loc_as_cell!(h+1)); + self.machine_st.heap.extend(functor!(atom!(":"), [cell(module), cell(attr)])); - let tail = if tail.is_var() { - heap_loc_as_cell!(l1 + 1) - } else { - tail - }; + 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(); - let trail_ref = read_heap_cell!(old_addr, - (HeapCellValueTag::Var, h) => { - TrailRef::AttrVarHeapLink(h) - } - (HeapCellValueTag::Lis, l) => { - TrailRef::AttrVarListLink(l1 + 1, l) + // 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) } - _ => { - unreachable!() + 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.heap[l1 + 1] = tail; - self.machine_st.trail(trail_ref); + 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)); } } } - #[inline(always)] - pub(crate) fn delete_head_attribute(&mut self) { - let addr = self.deref_register(1); + fn match_attribute( + &self, + mut attrs_list: HeapCellValue, + module: HeapCellValue, + attr: HeapCellValue, + ) -> Option { + let (name, arity) = match self.machine_st.name_and_arity_from_heap(attr) { + Some(key) => key, + None => { + return None; + } + }; - debug_assert_eq!(addr.get_tag(), HeapCellValueTag::AttrVar); + let mut prev_tail = None; - let h = addr.get_value(); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[h + 1])); + while let HeapCellValueTag::Lis = attrs_list.get_tag() { + let mut list_head = self.machine_st.heap[attrs_list.get_value()]; - debug_assert_eq!(addr.get_tag(), HeapCellValueTag::Lis); + 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 l = addr.get_value(); - let tail = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[l + 1])); + let (t_name, t_arity) = self.machine_st + .name_and_arity_from_heap(qualified_goal) + .unwrap(); - 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))); + if module == module_loc && name == t_name && arity == t_arity { + return Some(AttrListMatch { + match_site: MatchSite::Match(attrs_list.get_value()), + prev_tail, + }); + } - heap_loc_as_cell!(h + 1) - } else { - tail - }; + break; + } + _ => { + break; + } + ); + } + + let tail_loc = attrs_list.get_value() + 1; + prev_tail = Some(tail_loc); + + // 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]; + + 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 { + return Some(AttrListMatch { + match_site: MatchSite::NoMatchVarTail(h), + prev_tail, + }); + } + } + (HeapCellValueTag::Lis) => { + attrs_list = list_tail; + break; + } + _ => { + unreachable!() + } + ); + } + } - self.machine_st.heap[h + 1] = tail; - self.machine_st.trail(TrailRef::AttrVarListLink(h + 1, l)); + None } #[inline(always)]