[m-dev.] for review: new version of accumulator introduction algorithm
Peter Ross
petdr at cs.mu.OZ.AU
Tue Jan 18 14:35:05 AEDT 2000
New version of accumulator.m and revised log message, as per zs's
sit-down review.
===================================================================
Estimated hours taken: 40
Rewrite the accumulator introduction algorithm so that it syncs up with
the paper "Making Mercury Programs Tail Recursive".
compiler/accumulator.m:
Rewrite the accumulator introduction algorithm so that it syncs up
with the paper "Making Mercury Programs Tail Recursive".
The major enhancement over the old version is that construction
unifications which depend on associative calls are handled correctly
for arbitrary associative calls (the 0.9 version only handles
the associative call, list__append, so will not have problems).
The other advantage is that my next transformation will be easier to
code up.
compiler/assertion.m:
assertion__is_associativity_assertion now reports the output
variable which is constructed from the two associative input
variables.
compiler/goal_store.m:
A module to associate arbitrary Ids with a hlds_goal and the instmap
before the goal.
library/list.m:
Add the law
:- promise all [L,H,T] ( L = [H|T] <=> list__append([H], T, L)).
Index: compiler/accumulator.m
===================================================================
%-----------------------------------------------------------------------------%
% Copyright (C) 1999-2000 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.
%-----------------------------------------------------------------------------%
%
% Module: accumulator
% Main authors: petdr
%
% Attempts to transform a single proc to a tail recursive form by
% introducing accumlators.
%
% The algorithm implemented is the one from "Making Mercury Programs
% Tail Recursive", which can be found at
% <http://www.cs.mu.oz.au/research/mercury/information/papers.html>.
%
% The code uses the same variable names as the paper, so a pre-reading
% of the paper is probably useful in understanding the code.
%
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- module accumulator.
:- interface.
:- import_module hlds_module, hlds_pred, io.
:- pred accumulator__process_proc(pred_id::in, proc_id::in, proc_info::in,
proc_info::out, module_info::in, module_info::out,
io__state::di, io__state::uo) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module (assertion), error_util, goal_store, goal_util, globals.
:- import_module hlds_data, hlds_goal, hlds_out, (inst).
:- import_module inst_match, instmap, mode_util, options, prog_data, prog_util.
:- import_module assoc_list, bool, int, list, map, multi_map.
:- import_module require, set, std_util, string, term, varset.
%-----------------------------------------------------------------------------%
% The form of the goal around the base and recursive cases.
:- type top_level
---> switch_base_rec
; switch_rec_base
; disj_base_rec
; disj_rec_base
; ite_base_rec
; ite_rec_base.
% A goal is represented by two integers, the first integer
% stores which conjunction the goal came from (base or
% recursive), and the second stores the location of the goal in
% the conjunction.
:- type goal_id == pair(int).
% The goal_store associates a goal with each goal_id.
:- type goal_store == goal_store(goal_id).
% A substition from one first variable name to the second.
:- type subst == map(prog_var, prog_var).
:- type warning
% warn that two prog_vars in call to pred_id
% at prog_context were swapped.
---> w(prog_context, pred_id, prog_var, prog_var).
:- type warnings == list(warning).
%-----------------------------------------------------------------------------%
%
% process_proc
%
% Attempt to transform one procedure into a accumulator
% recursive form.
%
process_proc(PredId, ProcId, ProcInfo0, ProcInfo,
ModuleInfo0, ModuleInfo) -->
globals__io_lookup_bool_option(optimize_constructor_last_call, DoLCO),
globals__io_lookup_bool_option(fully_strict, FullyStrict),
(
{ module_info_pred_info(ModuleInfo0, PredId, PredInfo) },
{ attempt_transform(ProcId, ProcInfo0, PredId,
PredInfo, DoLCO, FullyStrict, ModuleInfo0,
ProcInfo1, ModuleInfo1, Warnings) }
->
globals__io_lookup_bool_option(very_verbose, VeryVerbose),
(
{ VeryVerbose = yes }
->
io__write_string("% Accumulators introduced into "),
hlds_out__write_pred_id(ModuleInfo1, PredId),
io__write_string("\n")
;
[]
),
globals__io_lookup_bool_option(inhibit_accumulator_warnings,
InhibitWarnings),
(
{ not (InhibitWarnings = no, Warnings \= []) }
->
{ ModuleInfo = ModuleInfo1 }
;
{ error_util__describe_one_pred_name(ModuleInfo1,
PredId, PredName) },
{ pred_info_context(PredInfo, Context) },
error_util__write_error_pieces(Context, 0,
[words("In"), words(PredName)]),
{ proc_info_varset(ProcInfo, VarSet) },
output_warnings(Warnings, VarSet, ModuleInfo1),
error_util__write_error_pieces(Context, 2,
[
words("Please ensure that these"),
words("argument rearrangements"),
words("do not introduce"),
words("performance problems."),
words("These warnings can"),
words("be suppressed by"),
words("`--inhibit-accumulator-warnings'.")
]),
globals__io_lookup_bool_option(verbose_errors,
VerboseErrors),
(
{ VerboseErrors = yes }
->
error_util__write_error_pieces(Context, 2,
[
words("If a predicate has been"),
words("declared associative via"),
words("a promise declaration,"),
words("the compiler will"),
words("rearrange the order"),
words("of the arguments"),
words("in calls to that"),
words("predicate, if by so doing"),
words("it makes the containing"),
words("predicate tail recursive."),
words("In such situations, the"),
words("compiler will issue"),
words("this warning."),
words("If this reordering changes the"),
words("performance characteristics"),
words("of the call to the"),
words("predicate, use"),
words("`--no-accumulator-introduction'"),
words("to turn the optimization"),
words("off, or"),
words("`--inhibit-accumulator-warnings'"),
words("to turn off the warnings.")
])
;
[]
),
globals__io_lookup_bool_option(halt_at_warn,
HaltAtWarn),
(
{ HaltAtWarn = yes }
->
io__set_exit_status(1),
{ module_info_incr_errors(ModuleInfo1,
ModuleInfo) }
;
{ ModuleInfo = ModuleInfo1 }
)
),
{ ProcInfo = ProcInfo1 }
;
{ ProcInfo = ProcInfo0 },
{ ModuleInfo = ModuleInfo0 }
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- pred output_warnings(list(warning)::in, prog_varset::in,
module_info::in, io__state::di, io__state::uo) is det.
output_warnings([], _, _) --> [].
output_warnings([W | Ws], VarSet, ModuleInfo) -->
{ output_warning(W, VarSet, ModuleInfo, Context, Format) },
error_util__write_error_pieces(Context, 2, Format),
output_warnings(Ws, VarSet, ModuleInfo).
:- pred output_warning(warning::in, prog_varset::in, module_info::in,
prog_context::out, list(format_component)::out) is det.
output_warning(w(Context, PredId, VarA, VarB), VarSet, ModuleInfo,
Context, Formats) :-
error_util__describe_one_pred_name(ModuleInfo, PredId, PredStr),
varset__lookup_name(VarSet, VarA, VarAStr),
varset__lookup_name(VarSet, VarB, VarBStr),
Formats = [words("warning: the call to"), words(PredStr),
words("has had the location of the variables"),
words(VarAStr), words("and"), words(VarBStr),
words("swapped to allow accumulator introduction.")
].
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
%
% attempt_transform is only true if the current
% proc has been transformed to call the newly created
% accumulator proc.
%
:- pred attempt_transform(proc_id::in, proc_info::in,
pred_id::in, pred_info::in, bool::in, bool::in, module_info::in,
proc_info::out, module_info::out, warnings::out) is semidet.
attempt_transform(ProcId, ProcInfo0, PredId, PredInfo, DoLCO,
FullyStrict, ModuleInfo0, ProcInfo, ModuleInfo, Warnings) :-
proc_info_goal(ProcInfo0, Goal0),
proc_info_headvars(ProcInfo0, HeadVars),
proc_info_get_initial_instmap(ProcInfo0, ModuleInfo0, InitialInstMap),
simplify(Goal0, Goal),
identify_goal_type(PredId, ProcId, Goal, TopLevel, Base, Rec),
C = initialise_goal_store(Rec, Base, InitialInstMap),
identify_recursive_calls(PredId, ProcId, C, RecCallIds),
M = list__length(Rec),
attempt_transform_2(RecCallIds, C, M, Rec, HeadVars, InitialInstMap,
TopLevel, DoLCO, FullyStrict, PredInfo,
ProcInfo0, ModuleInfo0, Warnings, ProcInfo, ModuleInfo).
%
% attempt_transform_2 takes a list of locations of the recursive
% calls, and attempts to introduce accumulator into each of the
% recursive calls, stopping at the first one that succeeds.
% This catches the following case, as selecting the first
% recursive call allows the second recursive call to be moved
% before it, and OutA is in the correct spot in list__append.
%
% p(InA, OutA),
% p(InB, OutB),
% list__append(OutB, OutA, Out)
%
:- pred attempt_transform_2(list(goal_id)::in, goal_store::in, int::in,
hlds_goals::in, prog_vars::in, instmap::in, top_level::in,
bool::in, bool::in, pred_info::in, proc_info::in,
module_info::in, warnings::out,
proc_info::out, module_info::out) is semidet.
attempt_transform_2([Id | Ids], C, M, Rec, HeadVars, InitialInstMap, TopLevel,
DoLCO, FullyStrict, PredInfo, ProcInfo0, ModuleInfo0,
Warnings, ProcInfo, ModuleInfo) :-
(
identify_out_and_out_prime(Id, Rec, HeadVars,
InitialInstMap, ModuleInfo0, _Out, OutPrime,
HeadToCallSubst, CallToHeadSubst),
stage1(Id, M, C, DoLCO, FullyStrict, ModuleInfo0, Sets),
stage2(Id, C, Sets, OutPrime, ModuleInfo0, ProcInfo0,
VarSet - VarTypes, InitAccs, Substs, CS,
Warnings0),
stage3(Id, InitAccs, VarSet, VarTypes, C, CS, Substs,
HeadToCallSubst, CallToHeadSubst, Sets,
TopLevel, PredInfo, ProcInfo0, ModuleInfo0,
ProcInfo1, ModuleInfo1)
->
Warnings = Warnings0,
ProcInfo = ProcInfo1,
ModuleInfo = ModuleInfo1
;
attempt_transform_2(Ids, C, M, Rec, HeadVars, InitialInstMap,
TopLevel, DoLCO, FullyStrict,
PredInfo, ProcInfo0, ModuleInfo0,
Warnings, ProcInfo, ModuleInfo)
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
%
% standardise
%
% Transform the goal into a standard form that is amenable to
% introducing accumulators.
%
% At the moment all this does is remove any extra disj/conj
% wrappers around the top level goal.
%
% Future work is for this code to rearrange code with multiple
% base and recursive cases into a single base and recursive
% case.
%
:- pred standardise(hlds_goal, hlds_goal).
:- mode standardise(in, out) is det.
standardise(Goal0, Goal) :-
(
(
Goal0 = conj([Goal1]) - _
;
Goal0 = disj([Goal1], _) - _
)
->
standardise(Goal1, Goal)
;
Goal = Goal0
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
%
% This predicate takes the original goal and identifies the
% `shape' of the goal around the recursive and base cases.
% Note that the base case can contain a recursive call, as the
% transformation doesn't depend on what is in the base case.
%
:- pred identify_goal_type(pred_id::in, proc_id::in, hlds_goal::in,
top_level::out, hlds_goals::out, hlds_goals::out) is semidet.
identify_goal_type(PredId, ProcId, Goal, Type, Base, Rec) :-
(
Goal = switch(_Var, _CanFail, Cases, _StoreMap) - _GoalInfo,
Cases = [case(_IdA, GoalA), case(_IdB, GoalB)],
goal_to_conj_list(GoalA, GoalAList),
goal_to_conj_list(GoalB, GoalBList)
->
(
is_recursive_case(GoalAList, proc(PredId, ProcId))
->
Type = switch_rec_base,
Base = GoalBList,
Rec = GoalAList
;
is_recursive_case(GoalBList, proc(PredId, ProcId))
->
Type = switch_base_rec,
Base = GoalAList,
Rec = GoalBList
;
fail
)
;
fail
).
%
% is_recursive_case(Gs, Id) is true iff the list of goals, Gs,
% contains a call to the procedure specified by Id, where the
% call is located in a position that can be used by the
% transformation (ie not hidden in a compound goal).
%
:- pred is_recursive_case(list(hlds_goal)::in, pred_proc_id::in) is semidet.
is_recursive_case(Goals, proc(PredId, ProcId)) :-
list__append(_Initial, [RecursiveCall | _Final], Goals),
RecursiveCall = call(PredId, ProcId, _, _, _, _) - _.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- type store_info
---> store_info(
int,
instmap,
goal_store
).
%
% Initialise the goal_store, which will hold the C_{a,b} goals.
%
:- func initialise_goal_store(hlds_goals, hlds_goals, instmap) = goal_store.
initialise_goal_store(Rec, Base, InitialInstMap) = C :-
goal_store__init(C0),
list__foldl(store(rec), Rec, store_info(1, InitialInstMap, C0),
store_info(_, _, C1)),
list__foldl(store(base), Base,
store_info(1, InitialInstMap, C1),
store_info(_, _, C)).
%
% store(Id, G, SI0, SI) is true iff the goal G is stored inside
% the goal_store (which is part of SI) with the correct
% identifier and instmap associatied with the goal.
%
:- pred store(int::in, hlds_goal::in, store_info::in, store_info::out) is det.
store(Identifier, Goal, store_info(N, IM0, GS0), store_info(N+1, IM, GS)) :-
Goal = _ - GoalInfo,
goal_info_get_instmap_delta(GoalInfo, InstMapDelta),
instmap__apply_instmap_delta(IM0, InstMapDelta, IM),
goal_store__det_insert(GS0, Identifier - N, Goal - IM0, GS).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% Determine the k's which are recursive calls
% Note that this doesn't find recursive calls which are `hidden'
% in compound goals, this is not a problem as currently we can't
% use these to do transformation.
:- pred identify_recursive_calls(pred_id::in, proc_id::in,
goal_store::in, list(goal_id)::out) is det.
identify_recursive_calls(PredId, ProcId, GoalStore, Ids) :-
P = (pred(Key::out) is nondet :-
goal_store__member(GoalStore, Key, Goal - _InstMap),
Goal = call(PredId, ProcId, _, _, _, _) - _
),
solutions(P, Ids).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
%
% identify_out_and_out_prime
%
% Determine the variables which are members of the sets Out and
% Out', and initialise the substitutions between the two sets.
%
% This is done by identifing those variables whose
% instantiatedness change in the goals after the recursive call
% and are headvars.
%
% Note that we are only identifying the output variables which
% will need to be accumulated, as there may be other output
% variables which are produced prior to the recursive call.
%
:- pred identify_out_and_out_prime(goal_id::in, hlds_goals::in, prog_vars::in,
instmap::in, module_info::in, prog_vars::out,
prog_vars::out, subst::out, subst::out) is det.
identify_out_and_out_prime(_N - K, Rec, HeadVars, InitialInstMap, ModuleInfo,
Out, OutPrime, HeadToCallSubst, CallToHeadSubst) :-
(
list__take(K, Rec, InitialGoals),
list__drop(K-1, Rec, FinalGoals),
FinalGoals = [call(_, _, Args, _, _, _) - _ | Rest]
->
goal_list_instmap_delta(InitialGoals, InitInstMapDelta),
instmap__apply_instmap_delta(InitialInstMap,
InitInstMapDelta, InstMapBeforeRest),
goal_list_instmap_delta(Rest, InstMapDelta),
instmap__apply_instmap_delta(InstMapBeforeRest,
InstMapDelta, InstMapAfterRest),
instmap_changed_vars(InstMapBeforeRest,
InstMapAfterRest, ModuleInfo, ChangedVars),
assoc_list__from_corresponding_lists(HeadVars, Args, HeadArg0),
Member = (pred(M::in) is semidet :-
M = HeadVar - _,
set__member(HeadVar, ChangedVars)
),
list__filter(Member, HeadArg0, HeadArg),
list__map(fst, HeadArg, Out),
list__map(snd, HeadArg, OutPrime),
map__from_assoc_list(HeadArg, HeadToCallSubst),
list__map((pred(X-Y::in, Y-X::out) is det), HeadArg, ArgHead),
map__from_assoc_list(ArgHead, CallToHeadSubst)
;
error("accumulator__identify_out_and_out_prime")
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- type sets
---> sets(
set(goal_id), % before
set(goal_id), % assoc
set(goal_id), % construct_assoc
set(goal_id), % construct
set(goal_id) % reject
).
%
% Stage 1 is responsible for identifying, which goals are
% associative, which can be moved before the recursive call and
% so on.
%
:- pred stage1(goal_id::in, int::in, goal_store::in, bool::in, bool::in,
module_info::in, sets::out) is semidet.
stage1(N - K, M, GoalStore, DoLCO, FullyStrict, ModuleInfo, Sets) :-
sets_init(Sets0),
stage1_2(N - (K+1), K, M, GoalStore, FullyStrict, ModuleInfo,
Sets0, Sets1),
Sets1 = sets(Before, Assoc, ConstructAssoc, Construct, Reject),
Sets = sets(Before `union` set_upto(N, (K-1)), Assoc,
ConstructAssoc, Construct, Reject),
% Continue the transformation only if the set reject is
% empty and the set assoc contains something that needs
% to be moved before the recursive call.
set__empty(Reject),
not set__empty(Assoc),
(
DoLCO = no
->
% If LCMC is not turned on then there must be no
% construction unifications after the recursive
% call.
set__empty(Construct `union` ConstructAssoc)
;
true
).
%
% For each goal after the recursive call decide which set the
% goal belongs to.
%
:- pred stage1_2(goal_id::in, int::in, int::in, goal_store::in,
bool::in, module_info::in, sets::in, sets::out) is det.
stage1_2(N - I, K, M, GoalStore, FullyStrict, ModuleInfo, Sets0, Sets) :-
(
I > M
->
Sets0 = Sets
;
(
before(N - I, K, GoalStore, Sets0,
FullyStrict, ModuleInfo)
->
stage1_2(N - (I+1), K, M, GoalStore,
FullyStrict, ModuleInfo,
insert_before(N - I, Sets0), Sets)
;
assoc(N - I, K, GoalStore, Sets0,
FullyStrict, ModuleInfo)
->
stage1_2(N - (I+1), K, M, GoalStore,
FullyStrict, ModuleInfo,
insert_assoc(N - I, Sets0), Sets)
;
construct(N - I, K, GoalStore, Sets0,
FullyStrict, ModuleInfo)
->
stage1_2(N - (I+1), K, M, GoalStore,
FullyStrict, ModuleInfo,
insert_construct(N - I, Sets0), Sets)
;
construct_assoc(N - I, K, GoalStore, Sets0,
FullyStrict, ModuleInfo)
->
stage1_2(N - (I+1), K, M, GoalStore,
FullyStrict, ModuleInfo,
insert_construct_assoc(N - I, Sets0),
Sets)
;
Sets = insert_reject(N - I, Sets0)
)
).
%-----------------------------------------------------------------------------%
:- pred sets_init(sets::out) is det.
sets_init(Sets) :-
set__init(Set),
Before = Set,
Assoc = Set,
ConstructAssoc = Set,
Construct = Set,
Reject = Set,
Sets = sets(Before, Assoc, ConstructAssoc, Construct, Reject).
% This function is called repeatedly with the same arguments so
% is an ideal canditate for tabling.
:- pragma memo(set_upto/2).
%
% set_upto(N, K) returns the set {(N,1)...(N,K)}
%
:- func set_upto(int, int) = set(goal_id).
set_upto(N, K) = Set :-
(
K =< 0
->
set__init(Set)
;
Set0 = set_upto(N, K-1),
set__insert(Set0, N-K, Set)
).
:- func insert_before(goal_id, sets) = sets.
insert_before(GoalId, sets(Before, Assoc, ConstructAssoc, Construct, Reject))
= sets(set__insert(Before, GoalId), Assoc, ConstructAssoc, Construct,
Reject).
:- func insert_assoc(goal_id, sets) = sets.
insert_assoc(GoalId, sets(Before, Assoc, ConstructAssoc, Construct, Reject))
= sets(Before, set__insert(Assoc, GoalId), ConstructAssoc, Construct,
Reject).
:- func insert_construct_assoc(goal_id, sets) = sets.
insert_construct_assoc(GoalId, sets(Before, Assoc, ConstructAssoc, Construct,
Reject))
= sets(Before, Assoc, set__insert(ConstructAssoc, GoalId), Construct,
Reject).
:- func insert_construct(goal_id, sets) = sets.
insert_construct(GoalId, sets(Before, Assoc, ConstructAssoc, Construct, Reject))
= sets(Before, Assoc, ConstructAssoc, set__insert(Construct, GoalId),
Reject).
:- func insert_reject(goal_id, sets) = sets.
insert_reject(GoalId, sets(Before, Assoc, ConstructAssoc, Construct, Reject))
= sets(Before, Assoc, ConstructAssoc, Construct,
set__insert(Reject, GoalId)).
%-----------------------------------------------------------------------------%
%
% A goal is a member of the before set iff the goal only depends on
% goals which are before the recursive call or can be moved
% before the recursive call (member of the before set).
%
:- pred before(goal_id::in, int::in, goal_store::in, sets::in,
bool::in, module_info::in) is semidet.
before(N - I, K, GoalStore, sets(Before, _, _, _, _),
FullyStrict, ModuleInfo) :-
goal_store__lookup(GoalStore, N - I, LaterGoal - LaterInstMap),
(
member_lessthan_goalid(GoalStore, N - I, N - J,
EarlierGoal - EarlierInstMap),
not goal_util__can_reorder_goals(ModuleInfo, FullyStrict,
EarlierInstMap, EarlierGoal,
LaterInstMap, LaterGoal)
)
=>
(
set__member(N - J, set_upto(N, K-1) `union` Before)
).
%
% A goal is a member of the assoc set iff the goal only depends
% on goals upto and including the recursive call and goals which
% can be moved before the recursive call (member of the before
% set) AND the goal is associative.
%
:- pred assoc(goal_id::in, int::in, goal_store::in, sets::in, bool::in,
module_info::in) is semidet.
assoc(N - I, K, GoalStore, sets(Before, _, _, _, _),
FullyStrict, ModuleInfo) :-
goal_store__lookup(GoalStore, N - I, LaterGoal - LaterInstMap),
LaterGoal = call(PredId, ProcId, Args, _, _, _) - _,
is_associative(PredId, ProcId, ModuleInfo, Args, _),
(
member_lessthan_goalid(GoalStore, N - I, _N - J,
EarlierGoal - EarlierInstMap),
not goal_util__can_reorder_goals(ModuleInfo, FullyStrict,
EarlierInstMap, EarlierGoal,
LaterInstMap, LaterGoal)
)
=>
(
set__member(N - J, set_upto(N, K) `union` Before)
).
%
% A goal is a member of the construct set iff the goal only depends
% on goals upto and including the recursive call and goals which
% can be moved before the recursive call (member of the before
% set) AND the goal is construction unification.
%
:- pred construct(goal_id::in, int::in, goal_store::in, sets::in,
bool::in, module_info::in) is semidet.
construct(N - I, K, GoalStore, sets(Before, _, _, Construct, _),
FullyStrict, ModuleInfo) :-
goal_store__lookup(GoalStore, N - I, LaterGoal - LaterInstMap),
LaterGoal = unify(_, _, _, Unify, _) - _GoalInfo,
Unify = construct(_, _, _, _, _, _, _),
(
member_lessthan_goalid(GoalStore, N - I, _N - J,
EarlierGoal - EarlierInstMap),
not goal_util__can_reorder_goals(ModuleInfo, FullyStrict,
EarlierInstMap, EarlierGoal,
LaterInstMap, LaterGoal)
)
=>
(
set__member(N - J, set_upto(N, K) `union` Before `union`
Construct)
).
%
% A goal is a member of the construct_assoc set iff the goal
% only depends on goals upto and including the recursive call
% and goals which can be moved before the recursive call (member
% of the before set) and goals which are associative AND the
% goal is construction unification AND the there is only one
% member of the assoc set which the construction unification
% depends on AND the construction unification can be expressed
% as a call to the member of the assoc set which the
% construction unification depends on.
%
:- pred construct_assoc(goal_id::in, int::in, goal_store::in, sets::in,
bool::in, module_info::in) is semidet.
construct_assoc(N - I, K, GoalStore, sets(Before, Assoc, ConstructAssoc, _, _),
FullyStrict, ModuleInfo) :-
goal_store__lookup(GoalStore, N - I, LaterGoal - LaterInstMap),
LaterGoal = unify(_, _, _, Unify, _) - _GoalInfo,
Unify = construct(_, ConsId, _, _, _, _, _),
goal_store__all_ancestors(GoalStore, N - I, ModuleInfo, FullyStrict,
Ancestors),
set__singleton_set(Assoc `intersect` Ancestors, AssocId),
goal_store__lookup(GoalStore, AssocId, AssocGoal - _AssocInstMap),
AssocGoal = call(PredId, _, _, _, _, _) - _,
is_associative_construction(ConsId, PredId, ModuleInfo),
(
member_lessthan_goalid(GoalStore, N - I, _N - J,
EarlierGoal - EarlierInstMap),
not goal_util__can_reorder_goals(ModuleInfo, FullyStrict,
EarlierInstMap, EarlierGoal,
LaterInstMap, LaterGoal)
)
=>
(
set__member(N - J, set_upto(N, K) `union` Before `union`
Assoc `union` ConstructAssoc)
).
%
% member_lessthan_goalid(GS, IdA, IdB, GB) is true iff the
% goal_id, IdB, and its associated goal, GB, is a member of the
% goal_store, GS, and IdB is less than IdA.
%
:- pred member_lessthan_goalid(goal_store::in, goal_id::in,
goal_id::out, goal_store__goal::out) is nondet.
member_lessthan_goalid(GoalStore, N - I, N - J, Goal) :-
goal_store__member(GoalStore, N - J, Goal),
J < I.
%-----------------------------------------------------------------------------%
:- type assoc
---> assoc(
set(prog_var), % the associative input args
prog_var, % the corresponding output arg
bool % is the predicate commutative?
).
%
% If accumulator_is_associative is true, it returns the two
% arguments which are associative and the variable which depends
% on those two arguments, and an indicator of whether or not
% the predicate is commutative.
%
:- pred is_associative(pred_id::in, proc_id::in, module_info::in,
prog_vars::in, assoc::out) is semidet.
is_associative(PredId, ProcId, ModuleInfo, Args, Result) :-
module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
PredInfo, ProcInfo),
pred_info_get_assertions(PredInfo, Assertions),
(
commutativity_assertion(set__to_sorted_list(Assertions),
ModuleInfo, Args, CommutativeVars)
->
% Currently we can only have one output
% argument, so we know which arg is generated
% directly by the two commutative args.
proc_info_argmodes(ProcInfo, ArgModes),
P = (pred(Pair::in, Var::out) is semidet :-
Pair = Var - Mode,
mode_get_insts(ModuleInfo, Mode,
InitialInst, FinalInst),
not inst_matches_final(InitialInst, FinalInst,
ModuleInfo)
),
assoc_list__from_corresponding_lists(Args, ArgModes, Pairs),
list__filter_map(P, Pairs, [OutputVar]),
IsCommutative = yes,
Result = assoc(CommutativeVars, OutputVar, IsCommutative)
;
associativity_assertion(set__to_sorted_list(Assertions),
ModuleInfo, Args, AssociativeVars, OutputVar),
IsCommutative = no,
Result = assoc(AssociativeVars, OutputVar, IsCommutative)
).
%
% commutativity_assertion
%
% Does there exist one (and only one) commutativity assertion for the
% current predicate.
% The 'and only one condition' is required because we currently
% don't handle the case of predicates which have individual
% parts which are commutative, because then we don't know which
% variable is descended from which.
%
:- pred commutativity_assertion(list(assert_id)::in, module_info::in,
prog_vars::in, set(prog_var)::out) is semidet.
commutativity_assertion([AssertId | AssertIds], ModuleInfo, Args0,
PossibleStaticVars) :-
(
assertion__is_commutativity_assertion(AssertId, ModuleInfo,
Args0, StaticVarA - StaticVarB)
->
\+ commutativity_assertion(AssertIds, ModuleInfo, Args0, _),
PossibleStaticVars = set__list_to_set([StaticVarA, StaticVarB])
;
commutativity_assertion(AssertIds, ModuleInfo, Args0,
PossibleStaticVars)
).
%
% associativity_assertion
%
% Does there exist one (and only one) associativity assertion for the
% current predicate.
% The 'and only one condition' is required because we currently
% don't handle the case of predicates which have individual
% parts which are associative, because then we don't know which
% variable is descended from which.
%
:- pred associativity_assertion(list(assert_id)::in, module_info::in,
prog_vars::in, set(prog_var)::out, prog_var::out) is semidet.
associativity_assertion([AssertId | AssertIds], ModuleInfo, Args0, VarAB,
OutputVar) :-
(
assertion__is_associativity_assertion(AssertId, ModuleInfo,
Args0, VarA - VarB, OutputVar0)
->
\+ associativity_assertion(AssertIds, ModuleInfo, Args0, _, _),
VarAB = set__list_to_set([VarA, VarB]),
OutputVar = OutputVar0
;
associativity_assertion(AssertIds, ModuleInfo, Args0,
VarAB, OutputVar)
).
%-----------------------------------------------------------------------------%
%
% Can the construction unification be expressed as a call to the
% specified predicate.
%
:- pred is_associative_construction(cons_id::in, pred_id::in,
module_info::in) is semidet.
is_associative_construction(ConsId, PredId, ModuleInfo) :-
module_info_pred_info(ModuleInfo, PredId, PredInfo),
pred_info_get_assertions(PredInfo, Assertions),
list__filter((pred(AssertId::in) is semidet :-
assertion__is_construction_equivalence_assertion(
AssertId, ModuleInfo, ConsId, PredId)
),
set__to_sorted_list(Assertions),
Result),
Result \= [].
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- type substs
---> substs(
subst, % acc_var_subst
subst, % rec_call_subst
subst % assoc_call_subst
).
%
% Stage 2 is responsible for identifying the substitutions which
% are needed to mimic the unfold/fold process that was used as
% the justification of the algorithm in the paper.
% It is also responsible for ensuring that the reordering of
% arguments doesn't worsen the big-O complexity of the
% procedure.
%
:- pred stage2(goal_id::in, goal_store::in, sets::in,
prog_vars::in, module_info::in, proc_info::in,
pair(prog_varset, vartypes)::out, prog_vars::out,
substs::out, goal_store::out, warnings::out) is semidet.
stage2(N - K, GoalStore, Sets, OutPrime, ModuleInfo, ProcInfo0,
VarSetVarTypesPair, set__to_sorted_list(InitAccs),
Substs, CS, Warnings) :-
Sets = sets(Before0, Assoc, ConstructAssoc, Construct, _Reject),
Before = Before0 `union` set_upto(N, K-1),
After = Assoc `union` ConstructAssoc `union` Construct,
P = (pred(Id::in, Set0::in, Set::out) is det :-
goal_store__lookup(GoalStore, Id, Goal - _InstMap),
Goal = _GoalExpr - GoalInfo,
goal_info_get_nonlocals(GoalInfo, NonLocals),
Set = NonLocals `union` Set0
),
list__foldl(P, set__to_sorted_list(Before), set__init, BeforeNonLocals),
list__foldl(P, set__to_sorted_list(After), set__init, AfterNonLocals),
InitAccs = BeforeNonLocals `intersect` AfterNonLocals,
proc_info_varset(ProcInfo0, VarSet0),
proc_info_vartypes(ProcInfo0, VarTypes0),
substs_init(set__to_sorted_list(InitAccs), VarSet0, VarTypes0,
VarSet1, VarTypes1, Substs0),
process_assoc_set(set__to_sorted_list(Assoc), GoalStore,
set__list_to_set(OutPrime), ModuleInfo, Substs0,
VarSet1 - VarTypes1, Substs, VarSetVarTypesPair, CS,
Warnings).
%-----------------------------------------------------------------------------%
:- pred substs_init(prog_vars::in, prog_varset::in, vartypes::in,
prog_varset::out, vartypes::out, substs::out) is det.
substs_init(InitAccs, VarSet0, VarTypes0, VarSet, VarTypes, Substs) :-
map__init(Subst),
acc_var_subst_init(InitAccs, VarSet0, VarTypes0,
VarSet, VarTypes, AccVarSubst),
RecCallSubst = Subst,
AssocCallSubst = Subst,
Substs = substs(AccVarSubst, RecCallSubst, AssocCallSubst).
%
% Initialise the acc_var_subst to be from Var to A_Var where
% Var is a member of InitAccs and A_Var is a fresh variable of
% the same type of Var.
%
:- pred acc_var_subst_init(prog_vars::in, prog_varset::in, vartypes::in,
prog_varset::out, vartypes::out, subst::out) is det.
acc_var_subst_init([], VarSet, VarTypes, VarSet, VarTypes, Subst) :-
map__init(Subst).
acc_var_subst_init([Var | Vars], VarSet0, VarTypes0,
VarSet, VarTypes, Subst) :-
create_new_var(Var, "A_", AccVar, VarSet0 - VarTypes0,
VarSet1 - VarTypes1),
acc_var_subst_init(Vars, VarSet1, VarTypes1, VarSet, VarTypes, Subst0),
map__det_insert(Subst0, Var, AccVar, Subst).
%
% Create a fresh variable which is the same type as the old
% variable and has the same name except that it begins with the
% prefix.
%
:- pred create_new_var(prog_var::in, string::in, prog_var::out,
pair(prog_varset, vartypes)::in,
pair(prog_varset, vartypes)::out) is det.
create_new_var(OldVar, Prefix, NewVar, VarSet0 - VarTypes0,
VarSet - VarTypes) :-
varset__lookup_name(VarSet0, OldVar, OldName),
string__append(Prefix, OldName, NewName),
varset__new_named_var(VarSet0, NewName, NewVar, VarSet),
map__lookup(VarTypes0, OldVar, Type),
map__det_insert(VarTypes0, NewVar, Type, VarTypes).
%-----------------------------------------------------------------------------%
%
% For each member of the assoc set determine the substitutions
% needed, and also check the efficiency of the procedure isn't
% worsened by swapping reordering the arguments to a call.
%
:- pred process_assoc_set(list(goal_id)::in, goal_store::in, set(prog_var)::in,
module_info::in, substs::in, pair(prog_varset, vartypes)::in,
substs::out, pair(prog_varset, vartypes)::out,
goal_store::out, warnings::out) is semidet.
process_assoc_set([], _GS, _OutPrime, _ModuleInfo, Substs, Types,
Substs, Types, CS, []) :-
goal_store__init(CS).
process_assoc_set([Id | Ids], GS, OutPrime, ModuleInfo, Substs0, Types0,
Substs, Types, CS, Warnings) :-
Substs0 = substs(AccVarSubst, RecCallSubst0, AssocCallSubst0),
lookup_call(GS, Id, Goal - InstMap),
Goal = call(PredId, ProcId, Args, _, _, _) - GoalInfo,
is_associative(PredId, ProcId, ModuleInfo, Args, AssocInfo),
AssocInfo = assoc(Vars, AssocOutput, IsCommutative),
set__singleton_set(Vars `intersect` OutPrime, DuringAssocVar),
set__singleton_set(Vars `difference` (Vars `intersect` OutPrime),
BeforeAssocVar),
map__lookup(AccVarSubst, BeforeAssocVar, AccVar),
create_new_var(BeforeAssocVar, "NewAcc_", NewAcc, Types0, Types1),
map__det_insert(AssocCallSubst0, DuringAssocVar, AccVar,
AssocCallSubst1),
map__det_insert(AssocCallSubst1, AssocOutput, NewAcc,
AssocCallSubst),
map__det_insert(RecCallSubst0, DuringAssocVar, AssocOutput,
RecCallSubst1),
map__det_insert(RecCallSubst1, BeforeAssocVar, NewAcc, RecCallSubst),
Substs1 = substs(AccVarSubst, RecCallSubst, AssocCallSubst),
% ONLY swap the order of the variables if the goal is
% associative.
(
IsCommutative = yes,
CSGoal = Goal - InstMap,
Warning = []
;
IsCommutative = no,
% Ensure that the reordering doesn't cause a
% efficiency problem
module_info_pred_info(ModuleInfo, PredId, PredInfo),
pred_info_module(PredInfo, ModuleName),
pred_info_name(PredInfo, PredName),
pred_info_arity(PredInfo, Arity),
(
has_heuristic(ModuleName, PredName, Arity)
->
heuristic(ModuleName, PredName, Arity, Args,
PossibleDuringAssocVars),
set__member(DuringAssocVar, PossibleDuringAssocVars),
Warning = []
;
goal_info_get_context(GoalInfo, ProgContext),
Warning = [w(ProgContext, PredId,
BeforeAssocVar, DuringAssocVar)]
),
% Swap the arguments.
[A,B] = set__to_sorted_list(Vars),
map__from_assoc_list([A-B, B-A], Subst),
goal_util__rename_vars_in_goal(Goal, Subst, SwappedGoal),
CSGoal = SwappedGoal - InstMap
),
process_assoc_set(Ids, GS, OutPrime, ModuleInfo, Substs1, Types1,
Substs, Types, CS0, Warnings0),
goal_store__det_insert(CS0, Id, CSGoal, CS),
list__append(Warnings0, Warning, Warnings).
:- pred has_heuristic(module_name::in, string::in, arity::in) is semidet.
has_heuristic(unqualified("list"), "append", 3).
:- pred heuristic(module_name::in, string::in, arity::in, prog_vars::in,
set(prog_var)::out) is semidet.
heuristic(unqualified("list"), "append", 3, [_Typeinfo, A, _B, _C], Set) :-
set__list_to_set([A], Set).
%-----------------------------------------------------------------------------%
:- inst call = bound(call(ground, ground, ground, ground, ground, ground)).
:- inst hlds_call = bound('-'(call, ground)).
:- inst call_goal = bound('-'(hlds_call, ground)).
% Do a goal_store__lookup where the result is known to be a
% call.
:- pred lookup_call(goal_store, goal_id, goal_store__goal) is det.
:- mode lookup_call(in, in, out(call_goal)) is det.
lookup_call(GoalStore, Id, Call - InstMap) :-
goal_store__lookup(GoalStore, Id, Goal - InstMap),
(
Goal = call(_, _, _, _, _, _) - _
->
Call = Goal
;
error("accumulator__lookup_call: not a call.")
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
%
% stage3 creates the accumulator version of the predicate using
% the substitutions determined in stage2, it also redefines the
% original procedure to call the accumulator version of the
% procedure.
%
:- pred stage3(goal_id::in, prog_vars::in, prog_varset::in, vartypes::in,
goal_store::in, goal_store::in, substs::in, subst::in,
subst::in, sets::in, top_level::in, pred_info::in,
proc_info::in, module_info::in,
proc_info::out, module_info::out) is det.
stage3(RecCallId, InitAccs, VarSet, VarTypes, C, CS, Substs,
HeadToCallSubst, CallToHeadSubst, Sets, TopLevel,
OrigPredInfo, OrigProcInfo0, ModuleInfo0,
OrigProcInfo, ModuleInfo) :-
acc_proc_info(InitAccs, VarSet, VarTypes, Substs,
OrigProcInfo0, AccTypes, AccProcInfo),
acc_pred_info(AccTypes, AccProcInfo, OrigPredInfo, AccProcId,
AccPredInfo),
pred_info_name(AccPredInfo, AccPredName),
AccName = unqualified(AccPredName),
module_info_get_predicate_table(ModuleInfo0, PredTable0),
predicate_table_insert(PredTable0, AccPredInfo, AccPredId, PredTable),
module_info_set_predicate_table(ModuleInfo0, PredTable, ModuleInfo1),
create_goal(RecCallId, InitAccs, AccPredId, AccProcId, AccName, Substs,
HeadToCallSubst, CallToHeadSubst, Sets, C, CS,
OrigBaseGoal, OrigRecGoal, AccBaseGoal, AccRecGoal),
proc_info_goal(OrigProcInfo0, OrigGoal0),
top_level(TopLevel, OrigGoal0, OrigBaseGoal, OrigRecGoal,
AccBaseGoal, AccRecGoal, OrigGoal, AccGoal),
proc_info_set_goal(OrigProcInfo0, OrigGoal, OrigProcInfo),
update_accumulator_pred(AccPredId, AccProcId, AccGoal,
ModuleInfo1, ModuleInfo).
%-----------------------------------------------------------------------------%
%
% acc_proc_info
%
% Construct a proc_info for the introduced predicate.
%
:- pred acc_proc_info(prog_vars::in, prog_varset::in, vartypes::in,
substs::in, proc_info::in,
list(type)::out, proc_info::out) is det.
acc_proc_info(InitAccs, VarSet, VarTypes, Substs,
OrigProcInfo, AccTypes, AccProcInfo) :-
% ProcInfo Stuff that must change.
proc_info_headvars(OrigProcInfo, HeadVars0),
proc_info_argmodes(OrigProcInfo, HeadModes0),
proc_info_inferred_determinism(OrigProcInfo, Detism),
proc_info_goal(OrigProcInfo, Goal),
proc_info_context(OrigProcInfo, Context),
proc_info_typeinfo_varmap(OrigProcInfo, TVarMap),
proc_info_typeclass_info_varmap(OrigProcInfo, TCVarsMap),
proc_info_is_address_taken(OrigProcInfo, IsAddressTaken),
Substs = substs(AccVarSubst, _RecCallSubst, _AssocCallSubst),
list__map(map__lookup(AccVarSubst), InitAccs, Accs),
HeadVars = HeadVars0 `append` Accs,
% XXX we don't want to use the inst of the var as it can
% be more specific than it should be. ie int_const(1)
% when it should be any integer.
% However this will no longer handle partially
% instantiated data structures.
Inst = ground(shared, no),
inst_lists_to_mode_list([Inst], [Inst], Mode),
list__duplicate(list__length(Accs), list__det_head(Mode), AccModes),
HeadModes = HeadModes0 `append` AccModes,
list__map(map__lookup(VarTypes), Accs, AccTypes),
proc_info_create(VarSet, VarTypes, HeadVars, HeadModes, Detism, Goal,
Context, TVarMap, TCVarsMap, IsAddressTaken,
AccProcInfo).
%-----------------------------------------------------------------------------%
%
% acc_pred_info
%
% Construct the pred_info for the introduced predicate
%
:- pred acc_pred_info(list(type)::in, proc_info::in, pred_info::in,
proc_id::out, pred_info::out) is det.
acc_pred_info(NewTypes, NewProcInfo, PredInfo, NewProcId, NewPredInfo) :-
% PredInfo stuff that must change.
pred_info_arg_types(PredInfo, TypeVarSet, ExistQVars, Types0),
pred_info_module(PredInfo, ModuleName),
pred_info_name(PredInfo, Name),
Cond = true,
pred_info_context(PredInfo, PredContext),
pred_info_get_markers(PredInfo, Markers),
pred_info_get_is_pred_or_func(PredInfo, PredOrFunc),
pred_info_get_class_context(PredInfo, ClassContext),
pred_info_get_aditi_owner(PredInfo, Owner),
set__init(Assertions),
proc_info_context(NewProcInfo, Context),
term__context_line(Context, Line),
Counter = 0,
list__append(Types0, NewTypes, Types),
make_pred_name_with_context(ModuleName, "AccFrom", PredOrFunc, Name,
Line, Counter, SymName),
pred_info_create(ModuleName, SymName, TypeVarSet, ExistQVars, Types,
Cond, PredContext, local, Markers, PredOrFunc,
ClassContext, Owner, Assertions, NewProcInfo, NewProcId,
NewPredInfo).
%-----------------------------------------------------------------------------%
%
% create_goal creates the new base and recursive case of the
% original procedure (OrigBaseGoal and OrigRecGoal) and the base
% and recursive cases of accumulator version (AccBaseGoal and
% AccRecGoal).
%
:- pred create_goal(goal_id, prog_vars, pred_id, proc_id, sym_name, substs,
subst, subst, sets, goal_store, goal_store,
hlds_goal, hlds_goal, hlds_goal, hlds_goal).
:- mode create_goal(in, in, in, in, in,
in, in, in, in, in, in, out, out, out, out) is det.
create_goal(RecCallId, InitAccs, AccPredId, AccProcId, AccName, Substs,
HeadToCallSubst, CallToHeadSubst, Sets, C, CS,
OrigBaseGoal, OrigRecGoal, AccBaseGoal, AccRecGoal) :-
lookup_call(C, RecCallId, OrigCall - _InstMap),
Substs = substs(AccVarSubst, RecCallSubst, AssocCallSubst),
Sets = sets(Before, Assoc, ConstructAssoc, Construct, _Reject),
Call = create_acc_call(OrigCall, InitAccs, AccPredId,
AccProcId, AccName),
goal_util__rename_vars_in_goal(Call, CallToHeadSubst, BaseCall),
goal_util__rename_vars_in_goal(Call, RecCallSubst, RecCall),
Cbefore = goal_list(set__to_sorted_list(Before), C),
Cbase = goal_list(base_case_ids(C), C),
calculate_goal_info(conj(Cbefore `append` [BaseCall]), OrigRecGoal),
calculate_goal_info(conj(Cbase), OrigBaseGoal),
% Create the goals which will be used in the new
% recursive case.
R = create_new_recursive_goals(Assoc, Construct `union` ConstructAssoc,
AssocCallSubst, AccVarSubst, C, CS),
Rassoc = goal_list(set__to_sorted_list(Assoc), R),
Rconstruct = goal_list(set__to_sorted_list(Construct `union`
ConstructAssoc), R),
% Create the goals which will be used in the new
% base case.
B = create_new_base_goals(Assoc `union` Construct `union`
ConstructAssoc, C, AccVarSubst, HeadToCallSubst),
Bafter = base_case_ids(B) `append` set__to_sorted_list(Assoc `union`
Construct `union` ConstructAssoc),
BaseCase = goal_list(Bafter, B),
calculate_goal_info(conj(Cbefore `append` Rassoc `append`
[RecCall] `append` Rconstruct), AccRecGoal),
calculate_goal_info(conj(BaseCase), AccBaseGoal).
%
% create_acc_call takes the original call and generates a call
% to the accumulator version of the call, which can have the
% substitutions applied to it easily.
%
:- func create_acc_call(hlds_goal, prog_vars, pred_id, proc_id,
sym_name) = hlds_goal.
:- mode create_acc_call(in(hlds_call), in, in, in, in) = out(hlds_call) is det.
create_acc_call(OrigCall, InitAccs, AccPredId, AccProcId, AccName) = Call :-
OrigCall = call(_PredId, _ProcId, Args, Builtin, Context, _Name) - GI,
Call = call(AccPredId, AccProcId, Args `append` InitAccs,
Builtin, Context, AccName) - GI.
%
% Create the R set of goals (those that will be used in the new
% recursive case) by renaming all the members of assoc in CS
% using assoc_call_subst and all the members of (construct U
% construct_assoc) in C with acc_var_subst.
%
:- func create_new_recursive_goals(set(goal_id), set(goal_id), subst, subst,
goal_store, goal_store) = goal_store.
create_new_recursive_goals(Assoc, Constructs, AssocCallSubst, AccVarSubst,
C, CS)
= rename(set__to_sorted_list(Constructs), AccVarSubst, C, RBase) :-
RBase = rename(set__to_sorted_list(Assoc), AssocCallSubst, CS,
goal_store__init).
%
% Create the B set of goals (those that will be used in the new
% base case) by renaming all the base case goals of C with
% head_to_call and all the members of (assoc U construct U
% construct_assoc) of C with acc_var_subst.
%
:- func create_new_base_goals(set(goal_id), goal_store, subst, subst) =
goal_store.
create_new_base_goals(Ids, C, AccVarSubst, HeadToCallSubst)
= rename(set__to_sorted_list(Ids), AccVarSubst, C, Bbase) :-
Bbase = rename(base_case_ids(C), HeadToCallSubst, C, goal_store__init).
%-----------------------------------------------------------------------------%
%
% Given the top level structure of the goal create new version
% with new base and recursive cases plugged in.
%
:- pred top_level(top_level::in, hlds_goal::in,
hlds_goal::in, hlds_goal::in, hlds_goal::in,
hlds_goal::in, hlds_goal::out, hlds_goal::out) is det.
top_level(switch_base_rec, Goal, OrigBaseGoal, OrigRecGoal,
NewBaseGoal, NewRecGoal, OrigGoal, NewGoal) :-
(
Goal = switch(Var, CanFail, Cases0, StoreMap) - GoalInfo,
Cases0 = [case(IdA, _), case(IdB, _)]
->
OrigCases = [case(IdA, OrigBaseGoal), case(IdB, OrigRecGoal)],
OrigGoal = switch(Var, CanFail, OrigCases, StoreMap) - GoalInfo,
NewCases = [case(IdA, NewBaseGoal), case(IdB, NewRecGoal)],
NewGoal = switch(Var, CanFail, NewCases, StoreMap) - GoalInfo
;
error("top_level: not the correct top level")
).
top_level(switch_rec_base, Goal, OrigBaseGoal, OrigRecGoal,
NewBaseGoal, NewRecGoal, OrigGoal, NewGoal) :-
(
Goal = switch(Var, CanFail, Cases0, StoreMap) - GoalInfo,
Cases0 = [case(IdA, _), case(IdB, _)]
->
OrigCases = [case(IdA, OrigRecGoal), case(IdB, OrigBaseGoal)],
OrigGoal = switch(Var, CanFail, OrigCases, StoreMap) - GoalInfo,
NewCases = [case(IdA, NewRecGoal), case(IdB, NewBaseGoal)],
NewGoal = switch(Var, CanFail, NewCases, StoreMap) - GoalInfo
;
error("top_level: not the correct top level")
).
top_level(disj_base_rec, Goal, OrigBaseGoal,
OrigRecGoal, NewBaseGoal, NewRecGoal, OrigGoal, NewGoal) :-
(
Goal = disj(Goals, StoreMap) - GoalInfo,
Goals = [_, _]
->
OrigGoals = [OrigBaseGoal, OrigRecGoal],
OrigGoal = disj(OrigGoals, StoreMap) - GoalInfo,
NewGoals = [NewBaseGoal, NewRecGoal],
NewGoal = disj(NewGoals, StoreMap) - GoalInfo
;
error("top_level: not the correct top level")
).
top_level(disj_rec_base, Goal, OrigBaseGoal,
OrigRecGoal, NewBaseGoal, NewRecGoal, OrigGoal, NewGoal) :-
(
Goal = disj(Goals, StoreMap) - GoalInfo,
Goals = [_, _]
->
OrigGoals = [OrigRecGoal, OrigBaseGoal],
OrigGoal = disj(OrigGoals, StoreMap) - GoalInfo,
NewGoals = [NewRecGoal, NewBaseGoal],
NewGoal = disj(NewGoals, StoreMap) - GoalInfo
;
error("top_level: not the correct top level")
).
top_level(ite_base_rec, Goal, OrigBaseGoal,
OrigRecGoal, NewBaseGoal, NewRecGoal, OrigGoal, NewGoal) :-
(
Goal = if_then_else(Vars, If, _, _, StoreMap) - GoalInfo
->
OrigGoal = if_then_else(Vars, If,
OrigBaseGoal, OrigRecGoal, StoreMap) - GoalInfo,
NewGoal = if_then_else(Vars, If,
NewBaseGoal, NewRecGoal, StoreMap) - GoalInfo
;
error("top_level: not the correct top level")
).
top_level(ite_rec_base, Goal, OrigBaseGoal,
OrigRecGoal, NewBaseGoal, NewRecGoal, OrigGoal, NewGoal) :-
(
Goal = if_then_else(Vars, If, _, _, StoreMap) - GoalInfo
->
OrigGoal = if_then_else(Vars, If,
OrigRecGoal, OrigBaseGoal, StoreMap) - GoalInfo,
NewGoal = if_then_else(Vars, If,
NewRecGoal, NewBaseGoal, StoreMap) - GoalInfo
;
error("top_level: not the correct top level")
).
%-----------------------------------------------------------------------------%
%
% update_accumulator_pred
%
% Place the accumulator version of the predicate in the
% module_info structure.
%
:- pred update_accumulator_pred(pred_id::in, proc_id::in,
hlds_goal::in, module_info::in, module_info::out) is det.
update_accumulator_pred(NewPredId, NewProcId, AccGoal,
ModuleInfo0, ModuleInfo) :-
module_info_pred_proc_info(ModuleInfo0, NewPredId, NewProcId,
PredInfo, ProcInfo0),
proc_info_set_goal(ProcInfo0, AccGoal, ProcInfo),
module_info_set_pred_proc_info(ModuleInfo0, NewPredId, NewProcId,
PredInfo, ProcInfo, ModuleInfo).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
%
% rename(Ids, Subst, From, Initial)
%
% return a goal_store, Final, which is the result of looking up each
% member of set of goal_ids, Ids, in the goal_store, From,
% applying the substitution and then storing the goal into
% the goal_store, Initial.
%
:- func rename(list(goal_id), subst, goal_store, goal_store) = goal_store.
rename(Ids, Subst, From, Initial) = Final :-
list__foldl((pred(Id::in, GS0::in, GS::out) is det :-
goal_store__lookup(From, Id, Goal0 - InstMap),
goal_util__rename_vars_in_goal(Goal0, Subst, Goal),
goal_store__det_insert(GS0, Id, Goal - InstMap, GS)
), Ids, Initial, Final).
%
% Return all the goal_ids which belong in the base case.
%
:- func base_case_ids(goal_store) = list(goal_id).
base_case_ids(GS) = Base :-
solutions((pred(Key::out) is nondet :-
goal_store__member(GS, Key, _Goal),
Key = base - _
), Base).
%
% Given a list of goal_ids, return the list of hlds_goals from
% the goal_store.
%
:- func goal_list(list(goal_id), goal_store) = hlds_goals.
goal_list(Ids, GS) = Goals :-
list__map((pred(Key::in, G::out) is det :-
goal_store__lookup(GS, Key, G - _)
), Ids, Goals).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- pred calculate_goal_info(hlds_goal_expr::in, hlds_goal::out) is det.
calculate_goal_info(GoalExpr, GoalExpr - GoalInfo) :-
(
GoalExpr = conj(GoalList)
->
goal_list_nonlocals(GoalList, NonLocals),
goal_list_instmap_delta(GoalList, InstMapDelta),
goal_list_determinism(GoalList, Determinism),
goal_info_init(NonLocals, InstMapDelta, Determinism, GoalInfo)
;
error("calculate_goal_info: not a conj.")
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% The number which indicates the base case.
:- func base = int.
base = 2.
% The number which indicates the recursive case.
:- func rec = int.
rec = 1.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to: mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions: mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------
More information about the developers
mailing list