%---------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 2006, 2010-2011 The University of Melbourne. % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %---------------------------------------------------------------------------% % % File: ctgc.fixpoint_table.m. % Main author: nancy. % % This module defines a generic table to be used for fixpoint computations. % The purpose of this table is mainly to map pred_proc_ids onto abstract % substitutions representing either structure sharing or structure reuse. % %---------------------------------------------------------------------------% :- module transform_hlds.ctgc.fixpoint_table. :- interface. :- import_module list. :- type fixpoint_table(K, E). % Initialise the table. % The first parameter is a function that produces the initial value for % each of the keys that are to be inserted into the table. % :- func init_fixpoint_table(func(K) = E, list(K)) = fixpoint_table(K, E). % Inform the table that a new run has begun. % :- pred new_run(fixpoint_table(K, E)::in, fixpoint_table(K, E)::out) is det. % Which run of the fix point are we up to? % :- func which_run(fixpoint_table(K, E)) = int. % Check whether the entries are recursive. % :- pred is_recursive(fixpoint_table(K, E)::in) is semidet. % Check whether a fixpoint has been reached. % :- pred fixpoint_reached(fixpoint_table(K, E)::in) is semidet. % Return a short description of the state of the fixpoint table. % :- func description(fixpoint_table(K, E)) = string. % add_to_fixpoint_table(EqualityTest, Key, Element, !Table): % % Add a new element (E) associated with key (K) to the table. % % - If an element is already recorded with that key: % * if the new value is subsumed by the existing value, then % a fixpoint is obtained as far as this key is concerned; % * if the values are different, fixpoint is not reached yet, % and the new value is recorded instead of the old one. % % - If the key has not yet any value associated to it, add it % to the table (which does not change the stability of the table) % :- pred add_to_fixpoint_table(pred(E, E)::in(pred(in, in) is semidet), K::in, E::in, fixpoint_table(K, E)::in, fixpoint_table(K, E)::out) is det. % Retrieve an element E associated with key K from the table. % This operation changes the state of the table if the % element _is_ present in the table. This means we are facing % a recursive calltree. If the key is not an element of the % allowed keys, then the procedure fails. % :- pred get_from_fixpoint_table(K::in, E::out, fixpoint_table(K, E)::in, fixpoint_table(K, E)::out) is semidet. % Retrieve an element E associated with key K from the table. % The operation reports a software error when the element is not present. % :- func get_from_fixpoint_table_final(K, fixpoint_table(K, E)) = E. % Same as get_final, but the predicate fails instead of aborting when % the element is not present. % :- pred get_from_fixpoint_table_final_semidet(K::in, fixpoint_table(K, E)::in, E::out) is semidet. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. :- import_module int. :- import_module map. :- import_module require. %---------------------------------------------------------------------------% :- type fixpoint_table(K, E) ---> fixpoint_table( keys :: list(K), % list of allowed keys run :: int, % number of runs recursive :: is_recursive, mapping :: map(K, fp_entry(E)) ). :- type fp_entry(E) ---> entry( entry_stable :: is_stable, entry_elem :: E ). :- type is_recursive ---> is_recursive ; is_not_recursive. :- type is_stable ---> is_stable ; is_unstable. %---------------------------------------------------------------------------% :- func fp_entry_init(E) = fp_entry(E). :- func fp_entry_init_with_stability(is_stable, E) = fp_entry(E). fp_entry_init(Elem) = entry(is_unstable, Elem). fp_entry_init_with_stability(IsStable, Elem) = entry(IsStable, Elem). init_fixpoint_table(InitFunction, Ks) = FT :- InsertElement = ( pred(K::in, !.Map::in, !:Map::out) is det :- E = InitFunction(K), map.det_insert(K, fp_entry_init(E), !Map) ), list.foldl(InsertElement, Ks, map.init, Map), Run = 0, FT = fixpoint_table(Ks, Run, is_not_recursive, Map). new_run(T0, T0 ^ run := T0 ^ run + 1). which_run(T0) = T0 ^ run. is_recursive(T) :- T ^ recursive = is_recursive. fixpoint_reached(T) :- IsRecursive = T ^ recursive, ( IsRecursive = is_recursive, map.foldl(accumulate_instability, T ^ mapping, is_stable, FinalStability), FinalStability = is_stable ; IsRecursive = is_not_recursive ). :- pred accumulate_instability(K::in, fp_entry(E)::in, is_stable::in, is_stable::out) is det. accumulate_instability(_Key, Entry, S0, S) :- ( S0 = is_unstable, S = is_unstable ; S0 = is_stable, S = Entry ^ entry_stable ). description(T) = ( if fixpoint_reached(T) then "stable" else "unstable" ). add_to_fixpoint_table(IsLessOrEqualTest, Index, Elem, !T) :- Map0 = !.T ^ mapping, map.lookup(Map0, Index, Entry), TabledElem = Entry ^ entry_elem, ( if IsLessOrEqualTest(Elem, TabledElem) then IsStable = is_stable else IsStable = is_unstable ), % Whether or not the tabled element is equal to the new element, the final % tabled element will always be set to the new one. This is handy for % performing the following trick: equality can be checked on some partial % piece of the elements (for deciding stability), but some other part % might have changed, a change that should become visible in the table % too. (in fact this is necessary for the reuse-fixpoint table where not % only the reuses are kept (the abstract substitution), but also the goal % that might have changed. FinalTabledElem = fp_entry_init_with_stability(IsStable, Elem), map.det_update(Index, FinalTabledElem, Map0, Map), !T ^ mapping := Map. get_from_fixpoint_table(Index, Elem, !T) :- List = !.T ^ keys, list.member(Index, List), % can fail Map = !.T ^ mapping, map.lookup(Map, Index, Entry), Elem = Entry ^ entry_elem, !T ^ recursive := is_recursive. get_from_fixpoint_table_final(Index, T) = Elem :- ( if get_from_fixpoint_table_final_semidet(Index, T, TabledElem) then Elem = TabledElem else unexpected($pred, "key not in map.") ). get_from_fixpoint_table_final_semidet(Index, T, Elem) :- map.search(T ^ mapping, Index, Entry), Elem = Entry ^ entry_elem. %---------------------------------------------------------------------------% :- end_module transform_hlds.ctgc.fixpoint_table. %---------------------------------------------------------------------------%