Files
mercury/library/solutions.m
Julien Fischer 8ee103b308 Replace references to the IL backend.
library/solutions.m:
     The comments here should refer to the C# backend, not the IL or
     .NET backend.
2015-12-14 15:02:04 +11:00

1020 lines
37 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et wm=0 tw=0
%---------------------------------------------------------------------------%
% Copyright (C) 1994-2007 The University of Melbourne.
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING.LIB in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: solutions.m.
% Main author: fjh.
% Stability: medium.
%
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- module solutions.
:- interface.
:- import_module bool.
:- import_module list.
:- import_module set.
%---------------------------------------------------------------------------%
% solutions/2 collects all the solutions to a predicate and returns
% them as a list in sorted order, with duplicates removed.
% solutions_set/2 returns them as a set. unsorted_solutions/2 returns
% them as an unsorted list with possible duplicates; since there are
% an infinite number of such lists, this must be called from a context
% in which only a single solution is required.
%
:- pred solutions(pred(T), list(T)).
:- mode solutions(pred(out) is multi, out(non_empty_list)) is det.
:- mode solutions(pred(out) is nondet, out) is det.
:- func solutions(pred(T)) = list(T).
:- mode solutions(pred(out) is multi) = out(non_empty_list) is det.
:- mode solutions(pred(out) is nondet) = out is det.
:- func solutions_set(pred(T)) = set(T).
:- mode solutions_set(pred(out) is multi) = out is det.
:- mode solutions_set(pred(out) is nondet) = out is det.
:- pred solutions_set(pred(T), set(T)).
:- mode solutions_set(pred(out) is multi, out) is det.
:- mode solutions_set(pred(out) is nondet, out) is det.
:- pred unsorted_solutions(pred(T), list(T)).
:- mode unsorted_solutions(pred(out) is multi, out(non_empty_list))
is cc_multi.
:- mode unsorted_solutions(pred(out) is nondet, out)
is cc_multi.
:- func aggregate(pred(T), func(T, U) = U, U) = U.
:- mode aggregate(pred(out) is multi, func(in, in) = out is det, in)
= out is det.
:- mode aggregate(pred(out) is nondet, func(in, in) = out is det, in)
= out is det.
% aggregate/4 generates all the solutions to a predicate,
% sorts them and removes duplicates, then applies an accumulator
% predicate to each solution in turn:
%
% aggregate(Generator, AccumulatorPred, Acc0, Acc) <=>
% solutions(Generator, Solutions),
% list.foldl(AccumulatorPred, Solutions, Acc0, Acc).
%
:- pred aggregate(pred(T), pred(T, U, U), U, U).
:- mode aggregate(pred(out) is multi, pred(in, in, out) is det,
in, out) is det.
:- mode aggregate(pred(out) is multi, pred(in, di, uo) is det,
di, uo) is det.
:- mode aggregate(pred(out) is nondet, pred(in, di, uo) is det,
di, uo) is det.
:- mode aggregate(pred(out) is nondet, pred(in, in, out) is det,
in, out) is det.
% aggregate2/6 generates all the solutions to a predicate,
% sorts them and removes duplicates, then applies an accumulator
% predicate to each solution in turn:
%
% aggregate2(Generator, AccumulatorPred, AccA0, AccA, AccB0, AccB) <=>
% solutions(Generator, Solutions),
% list.foldl2(AccumulatorPred, Solutions, AccA0, AccA, AccB0, AccB).
%
:- pred aggregate2(pred(T), pred(T, U, U, V, V), U, U, V, V).
:- mode aggregate2(pred(out) is multi, pred(in, in, out, in, out) is det,
in, out, in, out) is det.
:- mode aggregate2(pred(out) is multi, pred(in, in, out, di, uo) is det,
in, out, di, uo) is det.
:- mode aggregate2(pred(out) is nondet, pred(in, in, out, di, uo) is det,
in, out, di, uo) is det.
:- mode aggregate2(pred(out) is nondet, pred(in, in, out, in, out) is det,
in, out, in, out) is det.
% unsorted_aggregate/4 generates all the solutions to a predicate
% and applies an accumulator predicate to each solution in turn.
% Declaratively, the specification is as follows:
%
% unsorted_aggregate(Generator, AccumulatorPred, Acc0, Acc) <=>
% unsorted_solutions(Generator, Solutions),
% list.foldl(AccumulatorPred, Solutions, Acc0, Acc).
%
% Operationally, however, unsorted_aggregate/4 will call the
% AccumulatorPred for each solution as it is obtained, rather than
% first building a list of all the solutions.
%
:- pred unsorted_aggregate(pred(T), pred(T, U, U), U, U).
:- mode unsorted_aggregate(pred(out) is multi, pred(in, in, out) is det,
in, out) is cc_multi.
:- mode unsorted_aggregate(pred(out) is multi, pred(in, in, out) is cc_multi,
in, out) is cc_multi.
:- mode unsorted_aggregate(pred(out) is multi, pred(in, di, uo) is det,
di, uo) is cc_multi.
:- mode unsorted_aggregate(pred(out) is multi, pred(in, di, uo) is cc_multi,
di, uo) is cc_multi.
:- mode unsorted_aggregate(pred(muo) is multi, pred(mdi, di, uo) is det,
di, uo) is cc_multi.
:- mode unsorted_aggregate(pred(out) is nondet, pred(in, di, uo) is det,
di, uo) is cc_multi.
:- mode unsorted_aggregate(pred(out) is nondet, pred(in, di, uo) is cc_multi,
di, uo) is cc_multi.
:- mode unsorted_aggregate(pred(out) is nondet, pred(in, in, out) is det,
in, out) is cc_multi.
:- mode unsorted_aggregate(pred(out) is nondet, pred(in, in, out) is cc_multi,
in, out) is cc_multi.
:- mode unsorted_aggregate(pred(muo) is nondet, pred(mdi, di, uo) is det,
di, uo) is cc_multi.
% unsorted_aggregate2/6 generates all the solutions to a predicate
% and applies an accumulator predicate to each solution in turn.
% Declaratively, the specification is as follows:
%
% unsorted_aggregate2(Generator, AccumulatorPred, !Acc1, !Acc2) <=>
% unsorted_solutions(Generator, Solutions),
% list.foldl2(AccumulatorPred, Solutions, !Acc1, !Acc2).
%
% Operationally, however, unsorted_aggregate2/6 will call the
% AccumulatorPred for each solution as it is obtained, rather than
% first building a list of all the solutions.
%
:- pred unsorted_aggregate2(pred(T), pred(T, U, U, V, V), U, U, V, V).
:- mode unsorted_aggregate2(pred(out) is multi,
pred(in, in, out, in, out) is det, in, out, in, out) is cc_multi.
:- mode unsorted_aggregate2(pred(out) is multi,
pred(in, in, out, in, out) is cc_multi, in, out, in, out) is cc_multi.
:- mode unsorted_aggregate2(pred(out) is multi,
pred(in, in, out, di, uo) is det, in, out, di, uo) is cc_multi.
:- mode unsorted_aggregate2(pred(out) is multi,
pred(in, in, out, di, uo) is cc_multi, in, out, di, uo) is cc_multi.
:- mode unsorted_aggregate2(pred(out) is nondet,
pred(in, in, out, in, out) is det, in, out, in, out) is cc_multi.
:- mode unsorted_aggregate2(pred(out) is nondet,
pred(in, in, out, in, out) is cc_multi, in, out, in, out) is cc_multi.
:- mode unsorted_aggregate2(pred(out) is nondet,
pred(in, in, out, di, uo) is det, in, out, di, uo) is cc_multi.
:- mode unsorted_aggregate2(pred(out) is nondet,
pred(in, in, out, di, uo) is cc_multi, in, out, di, uo) is cc_multi.
% This is a generalization of unsorted_aggregate which allows the
% iteration to stop before all solutions have been found.
% Declaratively, the specification is as follows:
%
% do_while(Generator, Filter, !Acc) :-
% unsorted_solutions(Generator, Solutions),
% do_while_2(Solutions, Filter, !Acc).
%
% do_while_2([], _, !Acc).
% do_while_2([X | Xs], Filter, !Acc) :-
% Filter(X, More, !Acc),
% (
% More = yes,
% do_while_2(Xs, Filter, !Acc)
% ;
% More = no
% ).
%
% Operationally, however, do_while/4 will call the Filter
% predicate for each solution as it is obtained, rather than
% first building a list of all the solutions.
%
:- pred do_while(pred(T), pred(T, bool, T2, T2), T2, T2).
:- mode do_while(pred(out) is multi, pred(in, out, in, out) is det, in, out)
is cc_multi.
:- mode do_while(pred(out) is multi, pred(in, out, di, uo) is det, di, uo)
is cc_multi.
:- mode do_while(pred(out) is multi, pred(in, out, di, uo) is cc_multi, di, uo)
is cc_multi.
:- mode do_while(pred(out) is nondet, pred(in, out, in, out) is det, in, out)
is cc_multi.
:- mode do_while(pred(out) is nondet, pred(in, out, di, uo) is det, di, uo)
is cc_multi.
:- mode do_while(pred(out) is nondet, pred(in, out, di, uo) is cc_multi, di, uo)
is cc_multi.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module mutvar.
:- import_module require.
%---------------------------------------------------------------------------%
% NOTE: the all-solutions predicates are handled specially in
% browser/declarative_tree.m. Any changes here may need to be reflected
% there.
%---------------------------------------------------------------------------%
solutions(Pred, List) :-
builtin_solutions(Pred, UnsortedList),
list.sort_and_remove_dups(UnsortedList, List0),
assert_num_solutions(Pred, List0, List).
solutions(P) = S :-
solutions(P, S).
solutions_set(P) = S :-
solutions_set(P, S).
solutions_set(Pred, Set) :-
builtin_solutions(Pred, List),
set.list_to_set(List, Set).
unsorted_solutions(Pred, List) :-
builtin_solutions(Pred, UnsortedList),
( List = UnsortedList
; List = UnsortedList
).
aggregate(P, F, Acc0) = Acc :-
aggregate(P, (pred(X::in, A0::in, A::out) is det :- A = F(X, A0)),
Acc0, Acc).
aggregate(Generator, AccumulatorPred, !Acc) :-
solutions(Generator, Solutions),
list.foldl(AccumulatorPred, Solutions, !Acc).
aggregate2(Generator, AccumulatorPred, !Acc1, !Acc2) :-
solutions(Generator, Solutions),
list.foldl2(AccumulatorPred, Solutions, !Acc1, !Acc2).
unsorted_aggregate(Generator, AccumulatorPred, !Acc) :-
builtin_aggregate(Generator, AccumulatorPred, !Acc),
cc_multi_equal(!Acc).
unsorted_aggregate2(Generator, AccumulatorPred, !Acc1, !Acc2) :-
builtin_aggregate2(Generator, AccumulatorPred, !Acc1, !Acc2),
cc_multi_equal(!Acc1),
cc_multi_equal(!Acc2).
%---------------------------------------------------------------------------%
:- pred builtin_solutions(pred(T), list(T)).
:- mode builtin_solutions(pred(out) is multi, out(non_empty_list))
is det. % really cc_multi
:- mode builtin_solutions(pred(out) is nondet, out)
is det. % really cc_multi
builtin_solutions(Generator, UnsortedList) :-
builtin_aggregate(Generator, list.cons, [], UnsortedList0),
assert_num_solutions(Generator, UnsortedList0, UnsortedList).
:- pred assert_num_solutions(pred(T), list(T), list(T)).
:- mode assert_num_solutions(pred(out) is multi,
in, out(non_empty_list)) is det.
:- mode assert_num_solutions(pred(out) is nondet,
in, out) is det.
:- pragma promise_equivalent_clauses(assert_num_solutions/3).
assert_num_solutions(_Pred::pred(out) is multi,
List0::in, List::out(non_empty_list)) :-
(
List0 = [],
unexpected($module, $pred, "no solutions")
;
List0 = [_ | _],
List = List0
).
assert_num_solutions(_Pred::pred(out) is nondet, List::in, List::out).
%---------------------------------------------------------------------------%
%
% This section defines builtin_aggregate/4 which takes a closure of type
% pred(T) in which the remaining argument is output, and backtracks over
% solutions for this, using the second argument to aggregate them however the
% user wishes. This is basically a generalization of solutions/2.
:- pred builtin_aggregate(pred(T), pred(T, U, U), U, U).
:- mode builtin_aggregate(pred(out) is multi, pred(in, in, out) is det,
in, out) is det. % really cc_multi
:- mode builtin_aggregate(pred(out) is multi, pred(in, di, uo) is det,
di, uo) is det. % really cc_multi
:- mode builtin_aggregate(pred(out) is multi, pred(in, in, out) is cc_multi,
in, out) is det. % really cc_multi
:- mode builtin_aggregate(pred(out) is multi, pred(in, di, uo) is cc_multi,
di, uo) is det. % really cc_multi
:- mode builtin_aggregate(pred(muo) is multi, pred(mdi, di, uo) is det,
di, uo) is det. % really cc_multi
:- mode builtin_aggregate(pred(out) is nondet, pred(in, di, uo) is det,
di, uo) is det. % really cc_multi
:- mode builtin_aggregate(pred(out) is nondet, pred(in, di, uo) is cc_multi,
di, uo) is det. % really cc_multi
:- mode builtin_aggregate(pred(out) is nondet, pred(in, in, out) is det,
in, out) is det. % really cc_multi
:- mode builtin_aggregate(pred(out) is nondet, pred(in, in, out) is cc_multi,
in, out) is det. % really cc_multi
:- mode builtin_aggregate(pred(muo) is nondet, pred(mdi, di, uo) is det,
di, uo) is det. % really cc_multi
% If we're doing heap reclamation on failure, then in order to implement any
% sort of code that requires terms to survive backtracking, we need to
% (deeply) copy them out of the heap and into some other area before
% backtracking. The obvious thing to do then is just call the generator
% predicate, let it run to completion, and copy its result into another memory
% area (call it the solutions heap) before forcing backtracking. When we get
% the next solution, we do the same, this time passing the previous collection
% (which is still on the solutions heap) to the collector predicate. If the
% result of this operation contains the old collection as a part, then the
% deep copy operation is smart enough not to copy again. So this could be
% pretty efficient.
%
% But what if the collector predicate does something that copies the previous
% collection? Then on each solution, we'll copy the previous collection to
% the heap, and then deep copy it back to the solution heap. This means
% copying solutions order N**2 times, where N is the number of solutions.
% So this isn't as efficient as we hoped.
%
% So we use a slightly different approach. When we find a solution, we deep
% copy it to the solution heap. Then, before calling the collector code, we
% sneakily swap the runtime system's notion of which is the heap and which is
% the solutions heap. This ensures that any terms are constructed on the
% solutions heap. When this is complete, we swap them back, and force the
% engine to backtrack to get the next solution. And so on. After we've
% gotten the last solution, we do another deep copy to move the solution back
% to the 'real' heap, and reset the solutions heap pointer (which of course
% reclaims all the garbage of the collection process).
%
% Note that this will work with recursive calls to builtin_aggregate as
% well. If the recursive invocation occurs in the generator pred, there can
% be no problem because by the time the generator succeeds, the inner
% do_ call will have completed, copied its result from the solutions heap,
% and reset the solutions heap pointer. If the recursive invocation happens
% in the collector pred, then it will happen when the heap and solutions heap
% are 'swapped.' This will work out fine, because the real heap isn't needed
% while the collector pred is executing, and by the time the nested do_ is
% completed, the 'real' heap pointer will have been reset.
%
% If the collector predicate throws an exception while they are swapped,
% then the code for builtin_throw/1 will unswap the heaps.
% So we don't need to create our own exception handlers to here to
% cover that case.
%
% If we're not doing heap reclamation on failure (as is currently the
% case when using conservative GC), then all of the heap-swapping
% and copying operations are no-ops, so we get a "zero-copy" solution.
% Note that the code for builtin_aggregate is very similar to the code
% for do_while (below).
:- pragma promise_pure(builtin_aggregate/4).
builtin_aggregate(GeneratorPred, CollectorPred, !Acc) :-
% Save some of the Mercury virtual machine registers
impure get_registers(HeapPtr, SolutionsHeapPtr, TrailPtr),
impure start_all_soln_neg_context,
% Initialize the accumulator
% /* Mutvar := !.Acc */
impure new_mutvar(!.Acc, Mutvar),
(
% Get a solution.
GeneratorPred(Answer0),
% Check that the generator didn't leave any delayed goals outstanding.
impure check_for_floundering(TrailPtr),
% Update the accumulator.
% /* Mutvar := CollectorPred(MutVar) */
impure swap_heap_and_solutions_heap,
impure partial_deep_copy(HeapPtr, Answer0, Answer),
impure get_mutvar(Mutvar, Acc0),
impure non_cc_call(CollectorPred, Answer, Acc0, Acc1),
impure set_mutvar(Mutvar, Acc1),
impure swap_heap_and_solutions_heap,
% Force backtracking, so that we get the next solution.
% This will automatically reset the heap and trail.
fail
;
% There are no more solutions.
impure end_all_soln_neg_context_no_more,
% So now we just need to copy the final value of the accumulator
% from the solutions heap back onto the ordinary heap, and then we can
% reset the solutions heap pointer. We also need to discard the trail
% ticket created by get_registers/3.
% /* !:Acc := Mutvar */
impure get_mutvar(Mutvar, !:Acc),
impure partial_deep_copy(SolutionsHeapPtr, !Acc),
impure reset_solutions_heap(SolutionsHeapPtr),
impure discard_trail_ticket
).
%---------------------------------------------------------------------------%
:- pragma promise_pure(builtin_aggregate2/6).
:- pred builtin_aggregate2(pred(T), pred(T, U, U, V, V), U, U, V, V).
:- mode builtin_aggregate2(pred(out) is multi,
pred(in, in, out, in, out) is det,
in, out, in, out) is det. % really cc_multi
:- mode builtin_aggregate2(pred(out) is multi,
pred(in, in, out, in, out) is cc_multi,
in, out, in, out) is det. % really cc_multi
:- mode builtin_aggregate2(pred(out) is multi,
pred(in, in, out, di, uo) is det,
in, out, di, uo) is det. % really cc_multi
:- mode builtin_aggregate2(pred(out) is multi,
pred(in, in, out, di, uo) is cc_multi,
in, out, di, uo) is det. % really cc_multi
:- mode builtin_aggregate2(pred(out) is nondet,
pred(in, in, out, in, out) is det,
in, out, in, out) is det. % really cc_multi
:- mode builtin_aggregate2(pred(out) is nondet,
pred(in, in, out, in, out) is cc_multi,
in, out, in, out) is det. % really cc_multi
:- mode builtin_aggregate2(pred(out) is nondet,
pred(in, in, out, di, uo) is det,
in, out, di, uo) is det. % really cc_multi
:- mode builtin_aggregate2(pred(out) is nondet,
pred(in, in, out, di, uo) is cc_multi,
in, out, di, uo) is det. % really cc_multi
builtin_aggregate2(GeneratorPred, CollectorPred, !Acc1, !Acc2) :-
% Save some of the Mercury virtual machine registers
impure get_registers(HeapPtr, SolutionsHeapPtr, TrailPtr),
impure start_all_soln_neg_context,
% Initialize the accumulator
impure new_mutvar(!.Acc1, Mutvar1),
impure new_mutvar(!.Acc2, Mutvar2),
(
% Get a solution.
GeneratorPred(Answer0),
% Check that the generator didn't leave any delayed goals outstanding.
impure check_for_floundering(TrailPtr),
% Update the accumulators.
impure swap_heap_and_solutions_heap,
impure partial_deep_copy(HeapPtr, Answer0, Answer),
some [!SubAcc1, !SubAcc2] (
impure get_mutvar(Mutvar1, !:SubAcc1),
impure get_mutvar(Mutvar2, !:SubAcc2),
impure non_cc_call(CollectorPred, Answer, !SubAcc1, !SubAcc2),
impure set_mutvar(Mutvar1, !.SubAcc1),
impure set_mutvar(Mutvar2, !.SubAcc2)
),
impure swap_heap_and_solutions_heap,
% Force backtracking, so that we get the next solution.
% This will automatically reset the heap and trail.
fail
;
% There are no more solutions.
impure end_all_soln_neg_context_no_more,
% So now we just need to copy the final value of the accumulators
% from the solutions heap back onto the ordinary heap, and then we can
% reset the solutions heap pointer. We also need to discard the trail
% ticket created by get_registers/3.
impure get_mutvar(Mutvar1, !:Acc1),
impure get_mutvar(Mutvar2, !:Acc2),
impure partial_deep_copy(SolutionsHeapPtr, !Acc1),
impure partial_deep_copy(SolutionsHeapPtr, !Acc2),
impure reset_solutions_heap(SolutionsHeapPtr),
impure discard_trail_ticket
).
%---------------------------------------------------------------------------%
% The code for do_while/4 is essentially the same as the code for
% builtin_aggregate (above). See the detailed comments above.
%
% XXX It would be nice to avoid the code duplication here,
% but it is a bit tricky -- we can't just use a lambda expression,
% because we'd need to specify the mode, but we want it to work
% for multiple modes. An alternative would be to use a typeclass,
% but typeclasses still don't work in `jump' or `fast' grades.
:- pragma promise_pure(do_while/4).
do_while(GeneratorPred, CollectorPred, !Acc) :-
impure get_registers(HeapPtr, SolutionsHeapPtr, TrailPtr),
impure new_mutvar(!.Acc, Mutvar),
impure start_all_soln_neg_context,
(
GeneratorPred(Answer0),
impure check_for_floundering(TrailPtr),
impure swap_heap_and_solutions_heap,
impure partial_deep_copy(HeapPtr, Answer0, Answer),
impure get_mutvar(Mutvar, Acc0),
impure non_cc_call(CollectorPred, Answer, More, Acc0, Acc1),
impure set_mutvar(Mutvar, Acc1),
impure swap_heap_and_solutions_heap,
% If More = yes, then backtrack for the next solution.
% If More = no, then we're done.
More = no,
impure end_all_soln_neg_context_more
;
impure end_all_soln_neg_context_no_more
),
impure get_mutvar(Mutvar, !:Acc),
impure partial_deep_copy(SolutionsHeapPtr, !Acc),
impure reset_solutions_heap(SolutionsHeapPtr),
impure discard_trail_ticket.
% This is the same as call/4, except that it is not cc_multi
% even when the called predicate is cc_multi.
:- impure pred non_cc_call(pred(T, Acc, Acc), T, Acc, Acc).
:- mode non_cc_call(pred(in, in, out) is det, in, in, out) is det.
:- mode non_cc_call(pred(in, in, out) is cc_multi, in, in, out) is det.
:- mode non_cc_call(pred(in, di, uo) is det, in, di, uo) is det.
:- mode non_cc_call(pred(in, di, uo) is cc_multi, in, di, uo) is det.
:- mode non_cc_call(pred(mdi, di, uo) is det, mdi, di, uo) is det.
non_cc_call(P::pred(in, in, out) is det, X::in, !.Acc::in, !:Acc::out) :-
P(X, !Acc).
non_cc_call(P::pred(in, in, out) is cc_multi, X::in, !.Acc::in, !:Acc::out) :-
promise_equivalent_solutions [!:Acc] (
P(X, !Acc),
impure impure_true
).
non_cc_call(P::pred(in, di, uo) is cc_multi, X::in, !.Acc::di, !:Acc::uo) :-
promise_equivalent_solutions [!:Acc] (
P(X, !Acc),
impure impure_true
).
non_cc_call(P::pred(in, di, uo) is det, X::in, !.Acc::di, !:Acc::uo) :-
P(X, !Acc).
non_cc_call(P::pred(mdi, di, uo) is det, X::mdi, !.Acc::di, !:Acc::uo) :-
P(X, !Acc).
% This is the same as call/5, except that it is not cc_multi
% even when the called predicate is cc_multi.
:- impure pred non_cc_call(pred(T1, T2, Acc, Acc), T1, T2, Acc, Acc).
:- mode non_cc_call(pred(in, out, in, out) is det, in, out, in, out) is det.
:- mode non_cc_call(pred(in, out, di, uo) is det, in, out, di, uo) is det.
:- mode non_cc_call(pred(in, out, di, uo) is cc_multi, in, out, di, uo) is det.
non_cc_call(P::pred(in, out, di, uo) is det, X::in, More::out,
!.Acc::di, !:Acc::uo) :-
P(X, More, !Acc).
non_cc_call(P::pred(in, out, in, out) is det, X::in, More::out,
!.Acc::in, !:Acc::out) :-
P(X, More, !Acc).
non_cc_call(P::pred(in, out, di, uo) is cc_multi, X::in, More::out,
!.Acc::di, !:Acc::uo) :-
promise_equivalent_solutions [More, !:Acc] (
P(X, More, !Acc),
impure impure_true
).
:- impure pred non_cc_call(pred(T1, Acc1, Acc1, Acc2, Acc2), T1,
Acc1, Acc1, Acc2, Acc2).
:- mode non_cc_call(pred(in, in, out, in, out) is det,
in, in, out, in, out) is det.
:- mode non_cc_call(pred(in, in, out, in, out) is cc_multi,
in, in, out, in, out) is det.
:- mode non_cc_call(pred(in, in, out, di, uo) is det,
in, in, out, di, uo) is det.
:- mode non_cc_call(pred(in, in, out, di, uo) is cc_multi,
in, in, out, di, uo) is det.
non_cc_call(P::pred(in, in, out, in, out) is det, X::in,
!.Acc1::in, !:Acc1::out, !.Acc2::in, !:Acc2::out) :-
P(X, !Acc1, !Acc2).
non_cc_call(P::pred(in, in, out, in, out) is cc_multi, X::in,
!.Acc1::in, !:Acc1::out, !.Acc2::in, !:Acc2::out) :-
promise_equivalent_solutions [!:Acc1, !:Acc2] (
P(X, !Acc1, !Acc2),
impure impure_true
).
non_cc_call(P::pred(in, in, out, di, uo) is det, X::in,
!.Acc1::in, !:Acc1::out, !.Acc2::di, !:Acc2::uo) :-
P(X, !Acc1, !Acc2).
non_cc_call(P::pred(in, in, out, di, uo) is cc_multi, X::in,
!.Acc1::in, !:Acc1::out, !.Acc2::di, !:Acc2::uo) :-
promise_equivalent_solutions [!:Acc1, !:Acc2] (
P(X, !Acc1, !Acc2),
impure impure_true
).
:- type heap_ptr == private_builtin.heap_pointer.
:- type trail_ptr ---> trail_ptr(c_pointer).
% Save the state of the Mercury heap and trail registers,
% for later use in partial_deep_copy/3 and reset_solutions_heap/1.
% Note that this allocates a trail ticket;
% you need to dispose of it properly when you're finished with it,
% e.g. by calling discard_trail_ticket/0.
:- impure pred get_registers(heap_ptr::out, heap_ptr::out, trail_ptr::out)
is det.
:- pragma foreign_proc("C",
get_registers(HeapPtr::out, SolutionsHeapPtr::out, TrailPtr::out),
[will_not_call_mercury, thread_safe],
"
/* save heap states */
#ifdef MR_RECLAIM_HP_ON_FAILURE
HeapPtr = (MR_Word) MR_hp;
SolutionsHeapPtr = (MR_Word) MR_sol_hp;
#else
HeapPtr = SolutionsHeapPtr = 0;
#endif
/* save trail state */
#ifdef MR_USE_TRAIL
MR_store_ticket(TrailPtr);
#else
TrailPtr = 0;
#endif
").
:- pragma foreign_proc("C#",
get_registers(HeapPtr::out, SolutionsHeapPtr::out, TrailPtr::out),
[will_not_call_mercury, thread_safe],
"
/*
** For .NET we always use the MS garbage collector,
** so we don't have to worry here about heap reclamation on failure.
*/
HeapPtr = null;
SolutionsHeapPtr = null;
#if MR_USE_TRAIL
/* XXX trailing not yet implemented for the MLDS back-end */
mercury.runtime.Errors.SORRY(""foreign code for get_registers"");
#else
TrailPtr = null;
#endif
").
:- pragma foreign_proc("Java",
get_registers(HeapPtr::out, SolutionsHeapPtr::out, TrailPtr::out),
[will_not_call_mercury, thread_safe],
"
/*
** Java has a builtin garbage collector,
** so we don't have to worry here about heap reclamation on failure.
*/
HeapPtr = null;
SolutionsHeapPtr = null;
/* XXX No trailing for the Java back-end. */
TrailPtr = null;
").
:- pragma foreign_proc("Erlang",
get_registers(HeapPtr::out, SolutionsHeapPtr::out, TrailPtr::out),
[will_not_call_mercury, thread_safe],
"
% Erlang has a builtin garbage collector,
% so we don't have to worry here about heap reclamation on failure.
HeapPtr = null,
SolutionsHeapPtr = null,
TrailPtr = null
").
:- impure pred check_for_floundering(trail_ptr::in) is det.
:- pragma foreign_proc("C",
check_for_floundering(TrailPtr::in),
[will_not_call_mercury, thread_safe],
"
#ifdef MR_USE_TRAIL
/* check for outstanding delayed goals (``floundering'') */
MR_reset_ticket(TrailPtr, MR_solve);
#endif
").
:- pragma foreign_proc("C#",
check_for_floundering(_TrailPtr::in),
[will_not_call_mercury, thread_safe],
"
#if MR_USE_TRAIL
mercury.runtime.Errors.SORRY(""foreign code for check_for_floundering"");
#endif
").
:- pragma foreign_proc("Java",
check_for_floundering(_TrailPtr::in),
[will_not_call_mercury, thread_safe],
"
/* XXX No trailing for the Java back-end, so take no action. */
").
:- pragma foreign_proc("Erlang",
check_for_floundering(_TrailPtr::in),
[will_not_call_mercury, thread_safe],
"
% XXX No trailing for the Erlang back-end, so take no action.
void
").
% Discard the topmost trail ticket.
%
:- impure pred discard_trail_ticket is det.
:- pragma foreign_proc("C",
discard_trail_ticket,
[will_not_call_mercury, thread_safe],
"
#ifdef MR_USE_TRAIL
MR_discard_ticket();
#endif
").
:- pragma foreign_proc("C#",
discard_trail_ticket,
[will_not_call_mercury, thread_safe],
"
#if MR_USE_TRAIL
mercury.runtime.Errors.SORRY(""foreign code for discard_trail_ticket"");
#endif
").
:- pragma foreign_proc("Java",
discard_trail_ticket,
[will_not_call_mercury, thread_safe],
"
/* XXX No trailing for the Java back-end, so take no action. */
").
:- pragma foreign_proc("Erlang",
discard_trail_ticket,
[will_not_call_mercury, thread_safe],
"
% XXX No trailing for the Erlang back-end, so take no action.
void
").
% Swap the heap with the solutions heap
%
:- impure pred swap_heap_and_solutions_heap is det.
:- pragma foreign_proc("C",
swap_heap_and_solutions_heap,
[will_not_call_mercury, thread_safe],
"{
#ifdef MR_RECLAIM_HP_ON_FAILURE
MR_MemoryZone *temp_zone;
MR_Word *temp_hp;
temp_zone = MR_ENGINE(MR_eng_heap_zone);
MR_ENGINE(MR_eng_heap_zone) = MR_ENGINE(MR_eng_solutions_heap_zone);
MR_ENGINE(MR_eng_solutions_heap_zone) = temp_zone;
temp_hp = MR_hp;
MR_hp_word = (MR_Word) MR_sol_hp;
MR_sol_hp = temp_hp;
#endif
}").
:- pragma foreign_proc("C#",
swap_heap_and_solutions_heap,
[will_not_call_mercury, thread_safe],
"
//
// For the .NET back-end, we use the system heap, rather
// than defining our own heaps. So we don't need to
// worry about swapping them. Hence do nothing here.
//
").
:- pragma foreign_proc("Java",
swap_heap_and_solutions_heap,
[will_not_call_mercury, thread_safe],
"
/*
** For the Java back-end, as for the .NET back-end, we don't define
** our own heaps. So take no action here.
*/
").
:- pragma foreign_proc("Erlang",
swap_heap_and_solutions_heap,
[will_not_call_mercury, thread_safe],
"
% For the Erlang back-end, as for the .NET back-end, we don't define
% our own heaps. So take no action here.
void
").
% partial_deep_copy(SolutionsHeapPtr, OldVal, NewVal):
% Make a copy of all of the parts of OldVar that occur between
% SolutionsHeapPtr and the top of the current solutions heap.
%
:- impure pred partial_deep_copy(heap_ptr, T, T) is det.
:- mode partial_deep_copy(in, di, uo) is det.
:- mode partial_deep_copy(in, mdi, muo) is det.
:- mode partial_deep_copy(in, in, out) is det.
:- pragma foreign_decl("C", "
#include ""mercury_deep_copy.h""
#ifndef MR_RECLAIM_HP_ON_FAILURE
/* for conservative GC, shallow copies suffice */
#define MR_PARTIAL_DEEP_COPY(SolutionsHeapPtr, \\
OldVar, NewVal, TypeInfo_for_T) \\
do { \\
NewVal = OldVal; \\
} while (0)
#else
/*
** Note that we need to save/restore the MR_hp register, if it
** is transient, before/after calling MR_deep_copy().
*/
#define MR_PARTIAL_DEEP_COPY(SolutionsHeapPtr, \\
OldVar, NewVal, TypeInfo_for_T) \\
do { \\
MR_save_transient_hp(); \\
NewVal = MR_deep_copy(OldVal, (MR_TypeInfo) TypeInfo_for_T, \\
(const MR_Word *) SolutionsHeapPtr, \\
MR_ENGINE(MR_eng_solutions_heap_zone)->MR_zone_top); \\
MR_restore_transient_hp(); \\
} while (0)
#endif
").
:- pragma foreign_proc("C",
partial_deep_copy(SolutionsHeapPtr::in, OldVal::in, NewVal::out),
[will_not_call_mercury, thread_safe],
"
MR_PARTIAL_DEEP_COPY(SolutionsHeapPtr, OldVal, NewVal, TypeInfo_for_T);
").
:- pragma foreign_proc("C",
partial_deep_copy(SolutionsHeapPtr::in, OldVal::mdi, NewVal::muo),
[will_not_call_mercury, thread_safe],
"
MR_PARTIAL_DEEP_COPY(SolutionsHeapPtr, OldVal, NewVal, TypeInfo_for_T);
").
:- pragma foreign_proc("C",
partial_deep_copy(SolutionsHeapPtr::in, OldVal::di, NewVal::uo),
[will_not_call_mercury, thread_safe],
"
MR_PARTIAL_DEEP_COPY(SolutionsHeapPtr, OldVal, NewVal, TypeInfo_for_T);
").
:- pragma foreign_proc("C#",
partial_deep_copy(_SolutionsHeapPtr::in, OldVal::in, NewVal::out),
[will_not_call_mercury, thread_safe],
"
//
// For the C# back-end, we don't do heap reclamation on failure,
// so we don't need to worry about making deep copies here.
// Shallow copies will suffice.
//
NewVal = OldVal;
").
:- pragma foreign_proc("C#",
partial_deep_copy(_SolutionsHeapPtr::in, OldVal::mdi, NewVal::muo),
[will_not_call_mercury, thread_safe],
"
NewVal = OldVal;
").
:- pragma foreign_proc("C#",
partial_deep_copy(_SolutionsHeapPtr::in, OldVal::di, NewVal::uo),
[will_not_call_mercury, thread_safe],
"
NewVal = OldVal;
").
:- pragma foreign_proc("Java",
partial_deep_copy(_SolutionsHeapPtr::in, OldVal::in, NewVal::out),
[will_not_call_mercury, thread_safe],
"
/*
** For the Java back-end, as for the C# back-end,
** we don't do heap reclamation on failure,
** so we don't need to worry about making deep copies here.
** Shallow copies will suffice.
*/
NewVal = OldVal;
").
:- pragma foreign_proc("Java",
partial_deep_copy(_SolutionsHeapPtr::in, OldVal::mdi, NewVal::muo),
[will_not_call_mercury, thread_safe],
"
NewVal = OldVal;
").
:- pragma foreign_proc("Java",
partial_deep_copy(_SolutionsHeapPtr::in, OldVal::di, NewVal::uo),
[will_not_call_mercury, thread_safe],
"
NewVal = OldVal;
").
:- pragma foreign_proc("Erlang",
partial_deep_copy(_SolutionsHeapPtr::in, OldVal::in, NewVal::out),
[will_not_call_mercury, thread_safe],
"
% For the Erlang back-end, as for the .NET implementation,
% we don't do heap reclamation on failure,
% so we don't need to worry about making deep copies here.
% Shallow copies will suffice.
NewVal = OldVal
").
:- pragma foreign_proc("Erlang",
partial_deep_copy(_SolutionsHeapPtr::in, OldVal::mdi, NewVal::muo),
[will_not_call_mercury, thread_safe],
"
NewVal = OldVal
").
:- pragma foreign_proc("Erlang",
partial_deep_copy(_SolutionsHeapPtr::in, OldVal::di, NewVal::uo),
[will_not_call_mercury, thread_safe],
"
NewVal = OldVal
").
% reset_solutions_heap(SolutionsHeapPtr):
%
% Reset the solutions heap pointer to the specified value,
% thus deallocating everything allocated on the solutions
% heap since that value was obtained via get_registers/3.
%
:- impure pred reset_solutions_heap(heap_ptr::in) is det.
:- pragma foreign_proc("C",
reset_solutions_heap(SolutionsHeapPtr::in),
[will_not_call_mercury, thread_safe],
"
#ifdef MR_RECLAIM_HP_ON_FAILURE
MR_sol_hp = (MR_Word *) SolutionsHeapPtr;
#endif
").
:- pragma foreign_proc("C#",
reset_solutions_heap(_SolutionsHeapPtr::in),
[will_not_call_mercury, thread_safe],
"
// For the C# back-end, we don't have a separate `solutions heap'.
// Hence this operation is a NOP.
").
:- pragma foreign_proc("Java",
reset_solutions_heap(_SolutionsHeapPtr::in),
[will_not_call_mercury, thread_safe],
"
/* As above, we take no action. */
").
:- pragma foreign_proc("Erlang",
reset_solutions_heap(_SolutionsHeapPtr::in),
[will_not_call_mercury, thread_safe],
"
% As above, we take no action.
void
").
:- impure pred start_all_soln_neg_context is det.
:- pragma foreign_proc("C",
start_all_soln_neg_context,
% In minimal model tabling grades, there are no threads.
% In all other grades, this predicate is a noop.
[will_not_call_mercury, thread_safe],
"
#ifdef MR_USE_MINIMAL_MODEL_STACK_COPY
MR_pneg_enter_cond();
#endif
").
start_all_soln_neg_context.
:- impure pred end_all_soln_neg_context_more is det.
:- pragma foreign_proc("C",
end_all_soln_neg_context_more,
% In minimal model tabling grades, there are no threads.
% In all other grades, this predicate is a noop.
[will_not_call_mercury, thread_safe],
"
#ifdef MR_USE_MINIMAL_MODEL_STACK_COPY
MR_pneg_enter_then();
#endif
").
end_all_soln_neg_context_more.
:- impure pred end_all_soln_neg_context_no_more is det.
:- pragma foreign_proc("C",
end_all_soln_neg_context_no_more,
% In minimal model tabling grades, there are no threads.
% In all other grades, this predicate is a noop.
[will_not_call_mercury, thread_safe],
"
#ifdef MR_USE_MINIMAL_MODEL_STACK_COPY
MR_pneg_enter_else(""end_all_soln_neg_context"");
#endif
").
end_all_soln_neg_context_no_more.
%---------------------------------------------------------------------------%
:- end_module solutions.
%---------------------------------------------------------------------------%