mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-29 16:24:43 +00:00
Estimated hours taken: 8
Record in which predicate an assertion is used.
compiler/accumulator.m:
compiler/lambda.m:
compiler/magic.m:
Initialise the assertions field in the new pred_info.
compiler/assertion.m:
An abstract interface to the assertion table (hopefully).
compiler/hlds_data.m:
Modify assertion_table_add_assertion to return the assert_id of the
inserted assertion.
compiler/hlds_pred.m:
Record in the pred_info the set of assertions that mention the pred.
compiler/post_typecheck.m:
Now record which predicates are used in assertions.
compiler/notes/compiler_design.html:
Document assertion.m
1911 lines
63 KiB
Mathematica
1911 lines
63 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1999 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 transformation is described more fully in the paper
|
|
% "Making Mercury programs tail recursive", which will be available
|
|
% from the Mercury web site.
|
|
%
|
|
% Basically the transformation is to locate predicates in the following
|
|
% form. The transformation also handles if-then-elses since they can
|
|
% conceptually be treated like a switch, we just need to be a little bit
|
|
% more careful about what happens in the condition.
|
|
%
|
|
% p(X,Ys) :-
|
|
% minimal(X),
|
|
% base(Ys).
|
|
% p(X, Ys) :-
|
|
% decompose(X, Xhead, Xrest),
|
|
% process(Xhead, Hs),
|
|
% p(Xrest, Y0s),
|
|
% compose(Hs, Y0s, Ys).
|
|
%
|
|
% and transform them into this form
|
|
%
|
|
% p(X,Ys) :-
|
|
% minimal(X),
|
|
% base(Ys).
|
|
% p(X, Ys) :-
|
|
% decompose(X, Xhead, Xrest),
|
|
% process(Xhead, Hs),
|
|
% p'(Xrest, Ys, Hs).
|
|
%
|
|
% p'(X, Ys, As) :-
|
|
% minimal(X),
|
|
% base(Y0s),
|
|
% compose(As, Y0s, Ys).
|
|
% p'(X, Ys, As) :-
|
|
% decompose(X, Xhead, Xrest),
|
|
% process(Xhead, Hs),
|
|
% compose(As, Hs, A1s),
|
|
% p'(Xrest, Ys, A1s).
|
|
%
|
|
% Any variable that ends with an 's' represents a set of variables.
|
|
% The constraint on the transformation is that the compose goal must
|
|
% obey the following law
|
|
%
|
|
% some [BC] (compose(I, B, C, BC), compose(I, A, BC, ABC))
|
|
% <=> some [AB] (compose(I, A, B, AB), compose(I, AB, C, ABC))
|
|
%
|
|
% The above law denotes that the compose goal must be associative, or
|
|
% maybe more intuitively the compose goal can construct an answer processing
|
|
% in a right to left manner or a left to right manner.
|
|
%
|
|
% Currently the knowledge of which goals are associative is hard-wired
|
|
% into this module, at a later date we should add the ability to add
|
|
% pragmas to supply this information.
|
|
%
|
|
% Another subtlety is that making the code tail recursive doesn't
|
|
% necessarily improve the efficiency of code. Note that the call
|
|
% to compose in the accumulator version of the code has Hs located
|
|
% in a different position. For append(in, in, out) it is the first
|
|
% argument which controls the complexity of append. So if the compose
|
|
% goal was append, the complexity of the predicate as a whole will
|
|
% change. This problem is dealt with by ensuring that only a variable
|
|
% that is a member of Hs ends up in the first position of append,
|
|
% because in general the variables in Hs are smaller then those in As.
|
|
%
|
|
% Note that the transformation will leave construction unifications
|
|
% after the recursive call if '--optimize-constructor-last-call' is
|
|
% enabled.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- 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 goal_util, globals, hlds_data, hlds_goal, hlds_out.
|
|
:- import_module inst_match, instmap, mode_util, options, prog_data, prog_util.
|
|
|
|
:- import_module assoc_list, bool, list, map, multi_map.
|
|
:- import_module require, set, std_util, term, varset.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type base_goal
|
|
---> base(
|
|
hlds_goals
|
|
).
|
|
|
|
:- type rec_goal
|
|
---> recursive(
|
|
a_goals, % Goals inside the condition of an
|
|
% if/then/else
|
|
a_goals, % Decompose, Process
|
|
a_goal, % Recursive call
|
|
a_goals % Compose calls
|
|
).
|
|
|
|
:- type split_result
|
|
---> recursive(
|
|
hlds_goals, % Decompose, Process
|
|
hlds_goal, % Recursive call
|
|
hlds_goals % Compose calls
|
|
).
|
|
|
|
:- type a_goal == goal(hlds_goal).
|
|
:- type a_goals == goal(hlds_goals).
|
|
|
|
:- type goal(T)
|
|
---> goal(
|
|
T, % goal/s
|
|
instmap % instmap at the start of the goal
|
|
).
|
|
|
|
:- type top_level
|
|
---> switch_base_rec
|
|
; switch_rec_base
|
|
; disj_base_rec
|
|
; disj_rec_base
|
|
; ite_base_rec
|
|
; ite_rec_base.
|
|
|
|
:- type subst == map(prog_var, prog_var).
|
|
|
|
:- type assoc_info
|
|
---> assoc_info(
|
|
set(prog_var), % Static set
|
|
% A static variable is one whose
|
|
% value is set before the
|
|
% recursive call.
|
|
|
|
set(prog_var), % Dynamic set
|
|
% The dynamic set is initialised
|
|
% to Y0s. At the end of the
|
|
% process it will contain all
|
|
% the variables that are
|
|
% constructed using another
|
|
% dynamic variable.
|
|
module_info,
|
|
prev_call_map,
|
|
orig_dynvar_map,
|
|
subst, % Y0s -> As
|
|
subst, % Hs -> As
|
|
set(prog_var) % Ys
|
|
).
|
|
|
|
% is the pred commutative?
|
|
:- type commutative == bool.
|
|
|
|
:- type prev_call
|
|
---> prev_call(
|
|
pred_id,
|
|
proc_id,
|
|
commutative
|
|
).
|
|
|
|
% If a variable is constructed from a chain of calls, what are
|
|
% the details of the previous call in the chain.
|
|
:- type prev_call_map == map(prog_var, prev_call).
|
|
|
|
% Given a dynamic variable, from which dynamic variable was it
|
|
% descended (ie which variable in Y0s).
|
|
%
|
|
% For the following call
|
|
%
|
|
% append(R0, ListH, R)
|
|
%
|
|
% The variable ListH is static and R0 is dynamic, therefore R is
|
|
% descended from R0.
|
|
%
|
|
:- type orig_dynvar_map == map(prog_var, prog_var).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
|
|
%
|
|
% accumulator__process_proc
|
|
%
|
|
% Attempt to transform one procedure into a accumulator
|
|
% recursive form.
|
|
%
|
|
accumulator__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) },
|
|
{ accumulator__attempt_transform(ProcId, ProcInfo0, PredId,
|
|
PredInfo, DoLCO, FullyStrict, ModuleInfo0,
|
|
ProcInfo1, ModuleInfo1) }
|
|
->
|
|
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")
|
|
;
|
|
[]
|
|
),
|
|
|
|
{ ProcInfo = ProcInfo1 },
|
|
{ ModuleInfo = ModuleInfo1 }
|
|
;
|
|
{ ProcInfo = ProcInfo0 },
|
|
{ ModuleInfo = ModuleInfo0 }
|
|
).
|
|
|
|
%
|
|
% accumulator__attempt_transform is only true if the current
|
|
% proc has been transformed to call the newly created
|
|
% accumulator proc.
|
|
%
|
|
:- pred accumulator__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) is semidet.
|
|
|
|
accumulator__attempt_transform(ProcId, ProcInfo0, PredId, PredInfo0, DoLCO,
|
|
FullyStrict, ModuleInfo0, ProcInfo, ModuleInfo) :-
|
|
proc_info_goal(ProcInfo0, Goal0),
|
|
proc_info_headvars(ProcInfo0, HeadVars),
|
|
proc_info_get_initial_instmap(ProcInfo0, ModuleInfo0, InitialInstMap),
|
|
|
|
accumulator__simplify(Goal0, Goal),
|
|
accumulator__rearrange_goal(PredId, ProcId, Goal, InitialInstMap,
|
|
ModuleInfo0, FullyStrict, GoalType, Base, Rec),
|
|
|
|
accumulator__create_accumulator_pred(Rec, PredInfo0, ProcInfo0,
|
|
HstoAs_Subst, NewPredId, NewProcId, NewPredName,
|
|
ModuleInfo0, ModuleInfo1),
|
|
|
|
accumulator__transform(GoalType, Base, Rec, Goal, DoLCO, FullyStrict,
|
|
ModuleInfo1, HeadVars, HstoAs_Subst, NewPredId,
|
|
NewProcId, NewPredName, OrigGoal, AccGoal),
|
|
|
|
accumulator__update_accumulator_pred(NewPredId, NewProcId, AccGoal,
|
|
ModuleInfo1, ModuleInfo),
|
|
|
|
proc_info_set_goal(ProcInfo0, OrigGoal, ProcInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__simplify
|
|
%
|
|
% Simplify the goal to make it more 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 accumulator__simplify(hlds_goal, hlds_goal).
|
|
:- mode accumulator__simplify(in, out) is det.
|
|
|
|
accumulator__simplify(Goal0, Goal) :-
|
|
(
|
|
(
|
|
Goal0 = conj([Goal1]) - _
|
|
;
|
|
Goal0 = disj([Goal1], _) - _
|
|
)
|
|
->
|
|
accumulator__simplify(Goal1, Goal)
|
|
;
|
|
Goal = Goal0
|
|
).
|
|
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% This predicate is meant to take the original goal and
|
|
% rearrange it into a standard form that can be used in the rest
|
|
% of module.
|
|
%
|
|
:- pred accumulator__rearrange_goal(pred_id::in, proc_id::in,
|
|
hlds_goal::in, instmap::in, module_info::in, bool::in,
|
|
top_level::out, base_goal::out, rec_goal::out) is semidet.
|
|
|
|
accumulator__rearrange_goal(PredId, ProcId, Goal, InitialInstMap, ModuleInfo,
|
|
FullyStrict, 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)
|
|
->
|
|
(
|
|
accumulator__split_recursive_case(PredId, ProcId,
|
|
InitialInstMap, [],
|
|
InitialInstMap, ModuleInfo, FullyStrict,
|
|
GoalAList, Rec0),
|
|
|
|
% Make sure that the base case doesn't
|
|
% contain a recursive call.
|
|
\+ accumulator__split_recursive_case(PredId, ProcId,
|
|
InitialInstMap, [],
|
|
InitialInstMap, ModuleInfo, FullyStrict,
|
|
GoalBList, _)
|
|
->
|
|
Type = switch_rec_base,
|
|
Base = base(GoalBList),
|
|
Rec = Rec0
|
|
;
|
|
accumulator__split_recursive_case(PredId, ProcId,
|
|
InitialInstMap, [],
|
|
InitialInstMap, ModuleInfo, FullyStrict,
|
|
GoalBList, Rec0),
|
|
|
|
% Make sure that the base case doesn't
|
|
% contain a recursive call.
|
|
\+ accumulator__split_recursive_case(PredId, ProcId,
|
|
InitialInstMap, [],
|
|
InitialInstMap, ModuleInfo, FullyStrict,
|
|
GoalAList, _)
|
|
->
|
|
Type = switch_base_rec,
|
|
Base = base(GoalAList),
|
|
Rec = Rec0
|
|
;
|
|
fail
|
|
)
|
|
;
|
|
Goal = disj(Goals, _SM) - _GoalInfo,
|
|
Goals = [GoalA, GoalB],
|
|
goal_to_conj_list(GoalA, GoalAList),
|
|
goal_to_conj_list(GoalB, GoalBList)
|
|
->
|
|
(
|
|
accumulator__split_recursive_case(PredId, ProcId,
|
|
InitialInstMap, [],
|
|
InitialInstMap, ModuleInfo, FullyStrict,
|
|
GoalAList, Rec0),
|
|
|
|
% Make sure that the base case doesn't
|
|
% contain a recursive call.
|
|
\+ accumulator__split_recursive_case(PredId, ProcId,
|
|
InitialInstMap, [],
|
|
InitialInstMap, ModuleInfo, FullyStrict,
|
|
GoalBList, _)
|
|
->
|
|
Type = disj_rec_base,
|
|
Base = base(GoalBList),
|
|
Rec = Rec0
|
|
;
|
|
accumulator__split_recursive_case(PredId, ProcId,
|
|
InitialInstMap, [],
|
|
InitialInstMap, ModuleInfo, FullyStrict,
|
|
GoalBList, Rec0),
|
|
|
|
% Make sure that the base case doesn't
|
|
% contain a recursive call.
|
|
\+ accumulator__split_recursive_case(PredId, ProcId,
|
|
InitialInstMap, [],
|
|
InitialInstMap, ModuleInfo, FullyStrict,
|
|
GoalAList, _)
|
|
->
|
|
Type = disj_base_rec,
|
|
Base = base(GoalAList),
|
|
Rec = Rec0
|
|
;
|
|
fail
|
|
)
|
|
;
|
|
Goal = if_then_else(_Vars, If, Then, Else, _SM) - _GoalInfo,
|
|
|
|
If = _ - IfGoalInfo,
|
|
goal_info_get_instmap_delta(IfGoalInfo, IMDelta),
|
|
instmap__apply_instmap_delta(InitialInstMap, IMDelta,
|
|
BeforeThenInstMap),
|
|
|
|
goal_to_conj_list(If, IfList),
|
|
goal_to_conj_list(Then, ThenList),
|
|
goal_to_conj_list(Else, ElseList)
|
|
->
|
|
(
|
|
accumulator__split_recursive_case(PredId, ProcId,
|
|
InitialInstMap, IfList,
|
|
BeforeThenInstMap, ModuleInfo,
|
|
FullyStrict, ThenList, Rec0),
|
|
|
|
% Make sure that the base case doesn't
|
|
% contain a recursive call.
|
|
\+ accumulator__split_recursive_case(PredId, ProcId,
|
|
InitialInstMap, [],
|
|
InitialInstMap, ModuleInfo, FullyStrict,
|
|
ElseList, _)
|
|
->
|
|
Type = ite_rec_base,
|
|
Base = base(ElseList),
|
|
Rec = Rec0
|
|
;
|
|
accumulator__split_recursive_case(PredId, ProcId,
|
|
InitialInstMap, [],
|
|
InitialInstMap, ModuleInfo, FullyStrict,
|
|
ElseList, Rec0),
|
|
|
|
% Make sure that the base case doesn't
|
|
% contain a recursive call.
|
|
\+ accumulator__split_recursive_case(PredId, ProcId,
|
|
InitialInstMap, [],
|
|
InitialInstMap, ModuleInfo, FullyStrict,
|
|
ThenList, _)
|
|
->
|
|
Type = ite_base_rec,
|
|
Base = base(ThenList),
|
|
Rec = Rec0
|
|
;
|
|
fail
|
|
)
|
|
|
|
;
|
|
fail
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__create_accumulator_pred
|
|
%
|
|
% Create a new predicate which is an accumulator version of the
|
|
% current proc being looked at.
|
|
%
|
|
:- pred accumulator__create_accumulator_pred(rec_goal::in, pred_info::in,
|
|
proc_info::in, subst::out, pred_id::out, proc_id::out,
|
|
sym_name::out, module_info::in, module_info::out) is det.
|
|
|
|
accumulator__create_accumulator_pred(RecGoal, PredInfo, ProcInfo, HstoAs_Subst,
|
|
NewPredId, NewProcId, NewPredName, ModuleInfo0, ModuleInfo) :-
|
|
|
|
proc_info_get_initial_instmap(ProcInfo, ModuleInfo0, InstMap0),
|
|
|
|
accumulator__acc_proc_info(RecGoal, InstMap0, ProcInfo, HstoAs_Subst,
|
|
NewTypes, NewProcInfo),
|
|
accumulator__acc_pred_info(NewTypes, NewProcInfo, PredInfo, NewProcId,
|
|
NewPredInfo),
|
|
|
|
pred_info_name(NewPredInfo, NewPredName0),
|
|
NewPredName = unqualified(NewPredName0),
|
|
|
|
module_info_get_predicate_table(ModuleInfo0, PredTable0),
|
|
predicate_table_insert(PredTable0, NewPredInfo, NewPredId, PredTable),
|
|
module_info_set_predicate_table(ModuleInfo0, PredTable, ModuleInfo).
|
|
|
|
|
|
%
|
|
% accumulator__acc_proc_info
|
|
%
|
|
% Construct a proc_info for the introduced predicate, it also
|
|
% creates the substitutions that maps between each variable that
|
|
% is a member of Hs and those in As.
|
|
%
|
|
:- pred accumulator__acc_proc_info(rec_goal::in, instmap::in, proc_info::in,
|
|
subst::out, list(type)::out, proc_info::out) is det.
|
|
|
|
accumulator__acc_proc_info(recursive(PreDP, DP, _R, C), InstMap0,
|
|
ProcInfo, HstoAs_Subst, NewTypes, NewProcInfo) :-
|
|
|
|
% ProcInfo Stuff that must change.
|
|
proc_info_varset(ProcInfo, VarSet0),
|
|
proc_info_vartypes(ProcInfo, VarTypes0),
|
|
proc_info_headvars(ProcInfo, HeadVars0),
|
|
proc_info_argmodes(ProcInfo, HeadModes0),
|
|
|
|
proc_info_inferred_determinism(ProcInfo, Detism),
|
|
proc_info_goal(ProcInfo, Goal),
|
|
proc_info_context(ProcInfo, Context),
|
|
proc_info_typeinfo_varmap(ProcInfo, TVarMap),
|
|
proc_info_typeclass_info_varmap(ProcInfo, TCVarsMap),
|
|
proc_info_is_address_taken(ProcInfo, IsAddressTaken),
|
|
|
|
accumulator__extra_vars_for_recursive_call(PreDP, DP, C, Vars),
|
|
|
|
DP = goal(DPGoals, _DPInstMap),
|
|
goal_list_instmap_delta(DPGoals, InstMapDelta),
|
|
instmap__apply_instmap_delta(InstMap0, InstMapDelta, InstMap),
|
|
|
|
accumulator__new_acc_var(Vars, InstMap, VarSet0, VarTypes0,
|
|
HstoAs_Subst, NewHeadVars, VarSet,
|
|
VarTypes, NewHeadModes),
|
|
|
|
list__append(HeadVars0, NewHeadVars, HeadVars),
|
|
list__append(HeadModes0, NewHeadModes, HeadModes),
|
|
|
|
list__map(map__lookup(VarTypes), NewHeadVars, NewTypes),
|
|
|
|
proc_info_create(VarSet, VarTypes, HeadVars, HeadModes, Detism, Goal,
|
|
Context, TVarMap, TCVarsMap, IsAddressTaken,
|
|
NewProcInfo).
|
|
|
|
%
|
|
% accumulator__new_acc_var(Hs, IM, VS0, VT0, HstoAs0, HstoAs,
|
|
% As, VS, VT, Ms)
|
|
%
|
|
% For each variable, Hs, that needs to be accumulated create a
|
|
% corresponding variable in As updating the varset, VS, var-type
|
|
% mapping, VT, and recording the mode in Ms. Also record the
|
|
% mapping from each H to A in HstoAs.
|
|
%
|
|
:- pred accumulator__new_acc_var(prog_vars::in, instmap::in,
|
|
prog_varset::in, map(prog_var, type)::in,
|
|
map(prog_var, prog_var)::out, prog_vars::out, prog_varset::out,
|
|
map(prog_var, type)::out, list(mode)::out) is det.
|
|
|
|
accumulator__new_acc_var([], _InstMap, VarSet, VarTypes,
|
|
Subst, [], VarSet, VarTypes, []) :-
|
|
map__init(Subst).
|
|
accumulator__new_acc_var([Var | Vars], InstMap, VarSet0, VarTypes0,
|
|
Subst, HeadVars, VarSet, VarTypes, Modes) :-
|
|
accumulator__new_acc_var(Vars, InstMap, VarSet0, VarTypes0,
|
|
Subst0, HeadVars0, VarSet1, VarTypes1, Modes0),
|
|
|
|
varset__new_var(VarSet1, NewVar, VarSet),
|
|
|
|
map__det_insert(Subst0, Var, NewVar, Subst),
|
|
|
|
HeadVars = [NewVar | HeadVars0],
|
|
|
|
map__lookup(VarTypes0, Var, Type),
|
|
map__det_insert(VarTypes1, NewVar, Type, VarTypes),
|
|
|
|
instmap__lookup_var(InstMap, Var, Inst),
|
|
inst_lists_to_mode_list([Inst], [Inst], Mode),
|
|
list__append(Mode, Modes0, Modes).
|
|
|
|
%
|
|
% accumulator__acc_pred_info
|
|
%
|
|
% Construct the pred_info for the introduced predicate
|
|
%
|
|
:- pred accumulator__acc_pred_info(list(type)::in, proc_info::in, pred_info::in,
|
|
proc_id::out, pred_info::out) is det.
|
|
|
|
accumulator__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).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__transform
|
|
%
|
|
% Actually do the transformation. This predicate may fail if
|
|
% for some reason the transformation cannot be completed.
|
|
%
|
|
:- pred accumulator__transform(top_level::in, base_goal::in, rec_goal::in,
|
|
hlds_goal::in, bool::in, bool::in, module_info::in,
|
|
prog_vars::in, subst::in, pred_id::in,
|
|
proc_id::in, sym_name::in,
|
|
hlds_goal::out, hlds_goal::out) is semidet.
|
|
|
|
accumulator__transform(TopLevel, base(BaseGoalList), recursive(PreDP, DP, R, C),
|
|
Goal, DoLCO, FullyStrict, ModuleInfo, HeadVars, HstoAs_Subst,
|
|
NewPredId, NewProcId, NewPredName, OrigGoal, NewGoal) :-
|
|
|
|
accumulator__Ys_descended_from_Y0s(HeadVars, DP, ModuleInfo),
|
|
|
|
accumulator__orig_base_case(BaseGoalList, OrigBaseGoal),
|
|
|
|
accumulator__extra_vars_for_recursive_call(PreDP, DP, C, Vars),
|
|
accumulator__orig_recursive_case(DP, R, HeadVars, NewPredId, NewProcId,
|
|
NewPredName, Vars, Y0stoYs_Subst, OrigRecGoal),
|
|
|
|
accumulator__new_base_case(BaseGoalList, C,
|
|
Y0stoYs_Subst, HstoAs_Subst, NewBaseGoal),
|
|
accumulator__new_recursive_case(DP, C, R, DoLCO, FullyStrict,
|
|
ModuleInfo, NewPredId, NewProcId, NewPredName,
|
|
Vars, HeadVars,
|
|
Y0stoYs_Subst, HstoAs_Subst, NewRecGoal),
|
|
|
|
accumulator__top_level(TopLevel, Goal, OrigBaseGoal, OrigRecGoal,
|
|
NewBaseGoal, NewRecGoal, OrigGoal, NewGoal).
|
|
|
|
|
|
:- pred accumulator__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.
|
|
|
|
accumulator__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("accumulator__top_level: not the correct top level")
|
|
).
|
|
accumulator__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("accumulator__top_level: not the correct top level")
|
|
).
|
|
accumulator__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("accumulator__top_level: not the correct top level")
|
|
).
|
|
accumulator__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("accumulator__top_level: not the correct top level")
|
|
).
|
|
accumulator__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("accumulator__top_level: not the correct top level")
|
|
).
|
|
accumulator__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("accumulator__top_level: not the correct top level")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__update_accumulator_pred
|
|
%
|
|
% Place the accumulator version of the predicate in the
|
|
% module_info structure.
|
|
%
|
|
:- pred accumulator__update_accumulator_pred(pred_id::in, proc_id::in,
|
|
hlds_goal::in, module_info::in, module_info::out) is det.
|
|
|
|
accumulator__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).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__split_recursive_case(PredId, ProcId, IM0, MI, FS, Gs, R)
|
|
%
|
|
% Split the goals, Gs, which make up the recursive case into the
|
|
% decompose and process list of goals, the compose goals and the
|
|
% recursive goal and place the components in the rec_goal
|
|
% structure, R.
|
|
%
|
|
:- pred accumulator__split_recursive_case(pred_id::in, proc_id::in,
|
|
instmap::in, hlds_goals::in,
|
|
instmap::in, module_info::in, bool::in,
|
|
hlds_goals::in, rec_goal::out) is semidet.
|
|
|
|
accumulator__split_recursive_case(PredId, ProcId,
|
|
PreInstMap, PreGoals,
|
|
InitialInstMap, ModuleInfo, FullyStrict, Goals, RecGoal) :-
|
|
solutions(accumulator__split_goals(Goals, PredId, ProcId), Solns),
|
|
Solns = [recursive(DP0, R, C0)],
|
|
calculate_instmap(DP0, InitialInstMap, InitialInstMapBeforeR),
|
|
|
|
% Any goal which doesn't depend on the recursive call is
|
|
% moved before the recursive call, because this means
|
|
% that only goals which contain dynamic variables are
|
|
% left after the recursive call, simplifying the latter
|
|
% stages.
|
|
move_goals(R, InitialInstMapBeforeR, ModuleInfo, FullyStrict,
|
|
C0, PreC0, PostC0),
|
|
list__append(DP0, PreC0, DP),
|
|
C = PostC0,
|
|
|
|
calculate_instmap(DP, InitialInstMap, InstMapBeforeR),
|
|
calculate_instmap([R], InstMapBeforeR, InstMapBeforeC),
|
|
|
|
Pre = goal(PreGoals, PreInstMap),
|
|
DecomposeProcess = goal(DP, InitialInstMap),
|
|
Recursive = goal(R, InstMapBeforeR),
|
|
Compose = goal(C, InstMapBeforeC),
|
|
|
|
RecGoal = recursive(Pre, DecomposeProcess, Recursive, Compose).
|
|
|
|
:- pred accumulator__split_goals(hlds_goals::in, pred_id::in, proc_id::in,
|
|
split_result::out) is nondet.
|
|
|
|
accumulator__split_goals(Goals, PredId, ProcId, RecGoal) :-
|
|
list__append(DecomposeProcess, [RecursiveCall | Compose], Goals),
|
|
RecursiveCall = call(PredId, ProcId, _, _, _, _) - _,
|
|
|
|
% An empty compose means the predicate is already tail
|
|
% recursive.
|
|
Compose \= [],
|
|
RecGoal = recursive(DecomposeProcess, RecursiveCall, Compose).
|
|
|
|
|
|
%
|
|
% Given a list of goals and an instmap before the list of goals,
|
|
% work out what the instmap at the end of the goals is.
|
|
%
|
|
:- pred calculate_instmap(hlds_goals::in, instmap::in, instmap::out) is det.
|
|
|
|
calculate_instmap(Goals, InstMap0, InstMap) :-
|
|
goal_list_instmap_delta(Goals, InstMapDelta),
|
|
instmap__apply_instmap_delta(InstMap0, InstMapDelta, InstMap).
|
|
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% move_goals(G, IM, MI, FS, Gs, BGs, AGs)
|
|
%
|
|
% Seperate the list of Goals, Gs, into the goals, BGs, that can be
|
|
% placed before the goal, G, and the goals, AGs, which must be
|
|
% placed after the goal, G. IM is the instmap before the goal
|
|
% G.
|
|
%
|
|
% XXX This should be able to be transformed to accumulator
|
|
% recursive form, much harder to do though. Look into this
|
|
% later. NB you need LCO for the else case.
|
|
%
|
|
:- pred move_goals(hlds_goal::in, instmap::in, module_info::in, bool::in,
|
|
hlds_goals::in, hlds_goals::out, hlds_goals::out) is det.
|
|
|
|
move_goals(_StartGoal, _IMBeforeStartGoal, _MI, _FullyStrict, [], [], []).
|
|
move_goals(StartGoal, InstMapBeforeStartGoal, ModuleInfo, FullyStrict,
|
|
[Goal|Goals], PreGoals, PostGoals) :-
|
|
|
|
StartGoal = _GoalExpr - GoalInfo,
|
|
goal_info_get_instmap_delta(GoalInfo, InstMapDelta),
|
|
instmap__apply_instmap_delta(InstMapBeforeStartGoal,
|
|
InstMapDelta, InstMapBeforeGoal),
|
|
(
|
|
goal_util__can_reorder_goals(ModuleInfo, FullyStrict,
|
|
InstMapBeforeStartGoal, StartGoal,
|
|
InstMapBeforeGoal, Goal)
|
|
->
|
|
move_goals(StartGoal, InstMapBeforeStartGoal, ModuleInfo,
|
|
FullyStrict, Goals, PreGoals0, PostGoals),
|
|
PreGoals = [Goal | PreGoals0]
|
|
;
|
|
move_goals(Goal, InstMapBeforeGoal, ModuleInfo, FullyStrict,
|
|
Goals, PreGoalsForGoal, PostGoalsForGoal),
|
|
move_goals(StartGoal, InstMapBeforeStartGoal,
|
|
ModuleInfo, FullyStrict, PreGoalsForGoal,
|
|
PreGoals, PostGoalsForStartGoal),
|
|
|
|
list__append(PostGoalsForStartGoal, [Goal | PostGoalsForGoal],
|
|
PostGoals)
|
|
).
|
|
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__Ys_descended_from_Y0s(HV, DP)
|
|
%
|
|
% If any head variable is calculated in the decompose/process,
|
|
% DP, list of goals then it cannot be descended from the Y0s.
|
|
%
|
|
:- pred accumulator__Ys_descended_from_Y0s(prog_vars::in,
|
|
a_goals::in, module_info::in) is semidet.
|
|
|
|
accumulator__Ys_descended_from_Y0s(HeadVars, DecomposeProcess, ModuleInfo) :-
|
|
accumulator__vars_to_accumulate(HeadVars, DecomposeProcess,
|
|
ModuleInfo, ChangedHeadVars),
|
|
|
|
ChangedHeadVars = [].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__vars_to_accumulate(HV, C, VA)
|
|
%
|
|
% Given the list of goals, C, which represent the compose
|
|
% goal and the list of the head variables, HV, determine the
|
|
% head variables that will need to be accumulated, VA.
|
|
%
|
|
% The variables that will need to be accumulated are simply
|
|
% those whose instantiatedness change in the compose goals and
|
|
% that are headvars.
|
|
%
|
|
:- pred accumulator__vars_to_accumulate(prog_vars::in, a_goals::in,
|
|
module_info::in, prog_vars::out) is det.
|
|
|
|
accumulator__vars_to_accumulate(HeadVars, C, ModuleInfo, VarsToAccumulate) :-
|
|
C = goal(ComposeGoals, InstMapBeforeCompose),
|
|
|
|
goal_list_instmap_delta(ComposeGoals, InstMapDelta),
|
|
instmap__apply_instmap_delta(InstMapBeforeCompose,
|
|
InstMapDelta,InstMapAfterCompose),
|
|
|
|
instmap_changed_vars(InstMapBeforeCompose,
|
|
InstMapAfterCompose, ModuleInfo, ChangedVars),
|
|
|
|
Member = (pred(M::in) is semidet :- set__member(M, ChangedVars)),
|
|
list__filter(Member, HeadVars, VarsToAccumulate).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__extra_vars_for_recursive_call(DP, C, Hs)
|
|
%
|
|
% If the decompose/process list of goals, DP, produce a value
|
|
% that is needed in the compose list of goals, C, then that
|
|
% value will need to be passed via the introduced recursive
|
|
% call. This predicate identifies these variables, Hs.
|
|
%
|
|
:- pred accumulator__extra_vars_for_recursive_call(a_goals::in, a_goals::in,
|
|
a_goals::in, prog_vars::out) is det.
|
|
|
|
accumulator__extra_vars_for_recursive_call(
|
|
goal(PreDecomposeProcess, _InstMapBeforePreDecomposeProcess),
|
|
goal(DecomposeProcess, _InstMapBeforeDecomposeProcess),
|
|
goal(Compose, _InstMapBeforeCompose), Vars) :-
|
|
|
|
goal_list_nonlocals(PreDecomposeProcess, PreDPNonLocalsSet),
|
|
goal_list_nonlocals(DecomposeProcess, DPNonLocalsSet),
|
|
set__union(PreDPNonLocalsSet, DPNonLocalsSet, NonLocals),
|
|
|
|
goal_list_nonlocals(Compose, CNonLocalsSet),
|
|
|
|
set__intersect(NonLocals, CNonLocalsSet, VarsSet),
|
|
set__to_sorted_list(VarsSet, Vars).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__static_vars_in_recursive_call
|
|
%
|
|
% Identify the variables which are static and only appear in the
|
|
% recursive call.
|
|
%
|
|
% This predicate is currently unused.
|
|
%
|
|
:- pred accumulator__static_vars_in_recursive_call(a_goal::in,
|
|
a_goals::in, module_info::in, prog_vars::out) is det.
|
|
|
|
accumulator__static_vars_in_recursive_call(Recursive, Compose,
|
|
ModuleInfo, Vars) :-
|
|
|
|
Recursive = goal(_RecGoalExpr - RecGoalInfo, InstMapBeforeRec),
|
|
Compose = goal(ComposeGoals, _InstMapBeforeCompose),
|
|
|
|
goal_info_get_instmap_delta(RecGoalInfo, RecInstMapDelta),
|
|
goal_info_get_nonlocals(RecGoalInfo, RecNonLocals),
|
|
instmap__apply_instmap_delta(InstMapBeforeRec, RecInstMapDelta,
|
|
InstMapAfterRec),
|
|
instmap_changed_vars(InstMapBeforeRec, InstMapAfterRec,
|
|
ModuleInfo, ChangedVars),
|
|
|
|
set__difference(RecNonLocals, ChangedVars, PossibleStaticVars),
|
|
|
|
goal_list_nonlocals(ComposeGoals, CNonLocalsSet),
|
|
|
|
set__intersect(CNonLocalsSet, PossibleStaticVars, VarsSet),
|
|
set__to_sorted_list(VarsSet, Vars).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__orig_base_case
|
|
%
|
|
% Determine the base case of the original goal.
|
|
%
|
|
:- pred accumulator__orig_base_case(hlds_goals::in, hlds_goal::out) is det.
|
|
|
|
accumulator__orig_base_case(BaseGoalList, BaseGoal) :-
|
|
|
|
% Note the the goal_info constructed should be identical
|
|
% to the original goal_info. It is just that it is no
|
|
% longer easily accessible.
|
|
calculate_goal_info(conj(BaseGoalList), BaseGoal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__orig_recursive_case
|
|
%
|
|
% Work out a new recursive case for the original predicate
|
|
% which replaces the recursive call with a call to the new
|
|
% accumulator predicate.
|
|
%
|
|
:- pred accumulator__orig_recursive_case(a_goals::in, a_goal::in,
|
|
prog_vars::in, pred_id::in, proc_id::in, sym_name::in,
|
|
prog_vars::in, subst::out, hlds_goal::out) is det.
|
|
|
|
accumulator__orig_recursive_case(DP, R0, HeadVars, PredId, ProcId, Name,
|
|
ExtraVars, Y0stoYs_Subst, Goal) :-
|
|
DP = goal(DecomposeProcess, _InstMapBeforeDecomposeProcess),
|
|
R0 = goal(Recursive, _InstMapBeforeRecursive),
|
|
(
|
|
Recursive = GoalExpr0 - GoalInfo,
|
|
GoalExpr0 = call(_, _, Vars, Builtin, Context, _)
|
|
->
|
|
% Calculate what vars the new call should use.
|
|
goal_list_nonlocals(DecomposeProcess, NonLocals),
|
|
assoc_list__from_corresponding_lists(HeadVars, Vars, Pairs),
|
|
list__map(accumulator__which_var(NonLocals), Pairs, CallVars0),
|
|
list__append(CallVars0, ExtraVars, CallVars),
|
|
GoalExpr = call(PredId, ProcId, CallVars,
|
|
Builtin, Context, Name),
|
|
|
|
% Rename the variables in the goal.
|
|
map__init(Subst0),
|
|
map__det_insert_from_corresponding_lists(Subst0, Vars,
|
|
CallVars0, Y0stoYs_Subst),
|
|
goal_util__rename_vars_in_goal(GoalExpr - GoalInfo,
|
|
Y0stoYs_Subst, R),
|
|
|
|
|
|
list__append(DecomposeProcess, [R], GoalList),
|
|
calculate_goal_info(conj(GoalList), Goal)
|
|
;
|
|
error("accumulator__orig_recursive_case: Not a call.")
|
|
).
|
|
|
|
|
|
%
|
|
% If a var is a member of the nonlocal set of variables then it
|
|
% must be that variable we are to use in the call, otherwise we
|
|
% use the corresponding head variable.
|
|
%
|
|
:- pred accumulator__which_var(set(prog_var)::in,
|
|
pair(prog_var, prog_var)::in, prog_var::out) is det.
|
|
|
|
accumulator__which_var(NonLocals, HeadVar - CallVar, Var) :-
|
|
(
|
|
set__member(CallVar, NonLocals)
|
|
->
|
|
Var = CallVar
|
|
;
|
|
Var = HeadVar
|
|
).
|
|
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__new_base_case
|
|
%
|
|
% Determine the base case of the introduced predicate.
|
|
%
|
|
:- pred accumulator__new_base_case(hlds_goals::in, a_goals::in,
|
|
subst::in, subst::in, hlds_goal::out) is det.
|
|
|
|
accumulator__new_base_case(Base, C, Y0stoYs_Subst, HstoAs_Subst, Goal) :-
|
|
C = goal(Compose, _InstMapBeforeCompose),
|
|
|
|
reverse_subst(Y0stoYs_Subst, YstoY0s_Subst0),
|
|
|
|
goal_list_nonlocals(Compose, NonLocals),
|
|
map__select(YstoY0s_Subst0, NonLocals, YstoY0s_Subst),
|
|
|
|
goal_util__rename_vars_in_goals(Base, no, YstoY0s_Subst, NewBase),
|
|
goal_util__rename_vars_in_goals(Compose, no, HstoAs_Subst, NewCompose),
|
|
|
|
list__append(NewBase, NewCompose, GoalList),
|
|
calculate_goal_info(conj(GoalList), Goal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__new_recursive_case
|
|
%
|
|
% Determine the recursive case of the introduced predicate.
|
|
%
|
|
:- pred accumulator__new_recursive_case(a_goals::in, a_goals::in,
|
|
a_goal::in, bool::in, bool::in,
|
|
module_info::in, pred_id::in, proc_id::in,
|
|
sym_name::in, prog_vars::in, prog_vars::in, subst::in,
|
|
subst::in, hlds_goal::out) is semidet.
|
|
|
|
accumulator__new_recursive_case(DP, C, R0, DoLCO, FullyStrict,
|
|
ModuleInfo, PredId, ProcId, Name, Hs, HeadVars,
|
|
Y0stoYs_Subst, HstoAs_Subst, Goal) :-
|
|
DP = goal(DecomposeProcess, _InstMapBeforeDecomposeProcess),
|
|
C = goal(Compose, InstMapBeforeCompose),
|
|
R0 = goal(Recursive0, _InstMapBeforeRecursive0),
|
|
|
|
assoc_info_init(ModuleInfo, HeadVars, DP, C, R0,
|
|
Y0stoYs_Subst, HstoAs_Subst, AssocInfo0),
|
|
accumulator__check_assoc(Compose, InstMapBeforeCompose, ModuleInfo,
|
|
FullyStrict, PreRecGoal0, PostRecGoal0,
|
|
AssocInfo0, AssocInfo),
|
|
|
|
(
|
|
DoLCO = yes,
|
|
|
|
% If there are no goals that can be moved before
|
|
% the recursive call, then there is nothing to
|
|
% accumulate so fail.
|
|
PreRecGoal0 \= [],
|
|
|
|
map__init(LCO_Subst0),
|
|
assoc_info_dynamic_set(DynamicSet, AssocInfo, _),
|
|
accumulator__check_post_rec_goals(PostRecGoal0, DynamicSet,
|
|
LCO_Subst0, LCO_Subst)
|
|
;
|
|
DoLCO = no,
|
|
map__init(LCO_Subst),
|
|
PostRecGoal0 = []
|
|
),
|
|
|
|
assoc_info_Y0stoAs(Y0stoAs_Subst, AssocInfo, _),
|
|
|
|
accumulator__rename_prerec_goals(PreRecGoal0, Y0stoAs_Subst,
|
|
Y0stoYs_Subst, LCO_Subst, HstoAs_Subst, PreRecGoal),
|
|
|
|
accumulator__rename_recursive_goal(Recursive0, Hs, PredId, ProcId,
|
|
Name, HstoAs_Subst, Y0stoAs_Subst,
|
|
Y0stoYs_Subst, LCO_Subst, Recursive),
|
|
|
|
accumulator__rename_postrec_goals(PostRecGoal0, HstoAs_Subst,
|
|
PostRecGoal),
|
|
|
|
list__append(PreRecGoal, [Recursive | PostRecGoal], GoalList0),
|
|
list__append(DecomposeProcess, GoalList0, GoalList),
|
|
calculate_goal_info(conj(GoalList), Goal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
|
|
%
|
|
% accumulator__check_post_rec_goals
|
|
%
|
|
% Ensure that each goal which is to be placed after the
|
|
% recursive goal is a construction unification.
|
|
%
|
|
% Also create a substition which records from which dynamic var
|
|
% each headvar is descended.
|
|
%
|
|
:- pred accumulator__check_post_rec_goals(hlds_goals::in, set(prog_var)::in,
|
|
subst::in, subst::out) is semidet.
|
|
|
|
accumulator__check_post_rec_goals([], _DynamicSet, Subst, Subst).
|
|
accumulator__check_post_rec_goals([Goal | Goals], DynamicSet, Subst0, Subst) :-
|
|
Goal = unify(_TermL, _TermR, _Mode, Unify, _Context) - _GoalInfo,
|
|
Unify = construct(Var, _ConsId, Vars, _Modes, _, _, _),
|
|
set__list_to_set(Vars, VarsSet),
|
|
set__intersect(VarsSet, DynamicSet, DynamicVarsSet),
|
|
set__singleton_set(DynamicVarsSet, DynamicVar),
|
|
(
|
|
map__search(Subst0, DynamicVar, DescendedFrom)
|
|
->
|
|
map__delete(Subst0, DynamicVar, Subst1),
|
|
map__insert(Subst1, Var, DescendedFrom, Subst2)
|
|
;
|
|
map__insert(Subst0, Var, DynamicVar, Subst2)
|
|
),
|
|
accumulator__check_post_rec_goals(Goals, DynamicSet, Subst2, Subst).
|
|
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred accumulator__rename_prerec_goals(hlds_goals::in, subst::in,
|
|
subst::in, subst::in, subst::in, hlds_goals::out) is det.
|
|
|
|
accumulator__rename_prerec_goals(Goal0, Y0stoAs_Subst, Y0stoYs_Subst,
|
|
LCO_Subst, HstoAs_Subst0, Goal) :-
|
|
reverse_subst(Y0stoYs_Subst, YstoY0s_Subst),
|
|
reverse_subst(LCO_Subst, LCOtoYs_Subst),
|
|
chain_subst(LCOtoYs_Subst, YstoY0s_Subst, LCOtoY0s_Subst),
|
|
|
|
delete_used_as(HstoAs_Subst0, Y0stoAs_Subst, HstoAs_Subst),
|
|
|
|
goal_util__rename_vars_in_goals(Goal0, no, Y0stoAs_Subst, Goal1),
|
|
goal_util__rename_vars_in_goals(Goal1, no, LCOtoY0s_Subst, Goal2),
|
|
goal_util__rename_vars_in_goals(Goal2, no, YstoY0s_Subst, Goal3),
|
|
goal_util__rename_vars_in_goals(Goal3, no, HstoAs_Subst, Goal).
|
|
|
|
:- pred delete_used_as(subst::in, subst::in, subst::out) is det.
|
|
delete_used_as(HstoAs_Subst0, Y0stoAs_Subst, HstoAs_Subst) :-
|
|
reverse_subst(HstoAs_Subst0, AstoHs_Subst0),
|
|
reverse_subst(Y0stoAs_Subst, AstoY0s_Subst),
|
|
|
|
map__keys(AstoY0s_Subst, AstoDelete),
|
|
map__delete_list(AstoHs_Subst0, AstoDelete, AstoHs_Subst),
|
|
|
|
reverse_subst(AstoHs_Subst, HstoAs_Subst).
|
|
|
|
:- pred accumulator__rename_recursive_goal(hlds_goal::in, prog_vars::in,
|
|
pred_id::in, proc_id::in, sym_name::in, subst::in, subst::in,
|
|
subst::in, subst::in, hlds_goal::out) is det.
|
|
|
|
accumulator__rename_recursive_goal(Goal0, Hs, PredId, ProcId, Name,
|
|
HstoAs_Subst, Y0stoAs_Subst, Y0stoYs_Subst, LCO_Subst, Goal) :-
|
|
|
|
Goal0 = GoalExpr0 - GoalInfo0,
|
|
(
|
|
GoalExpr0 = call(_, _, Args0, Builtin, Context, _)
|
|
->
|
|
reverse_subst(Y0stoAs_Subst, AstoY0s_Subst),
|
|
reverse_subst(HstoAs_Subst, AstoHs_Subst),
|
|
|
|
list__append(Args0, Hs, Args),
|
|
GoalExpr1 = call(PredId, ProcId, Args, Builtin, Context, Name),
|
|
|
|
goal_info_get_nonlocals(GoalInfo0, NonLocals0),
|
|
set__list_to_set(Args, ArgsSet),
|
|
set__union(ArgsSet, NonLocals0, NonLocals),
|
|
goal_info_set_nonlocals(GoalInfo0, NonLocals, GoalInfo1),
|
|
|
|
Goal1 = GoalExpr1 - GoalInfo1,
|
|
|
|
goal_util__rename_vars_in_goal(Goal1, Y0stoYs_Subst, Goal2),
|
|
goal_util__rename_vars_in_goal(Goal2, HstoAs_Subst, Goal3),
|
|
goal_util__rename_vars_in_goal(Goal3, AstoY0s_Subst, Goal4),
|
|
goal_util__rename_vars_in_goal(Goal4, LCO_Subst, Goal5),
|
|
goal_util__rename_vars_in_goal(Goal5, AstoHs_Subst, Goal)
|
|
;
|
|
error("accumulator__rename_recursive_goal: to make det.")
|
|
).
|
|
|
|
:- pred accumulator__rename_postrec_goals(hlds_goals::in, subst::in,
|
|
hlds_goals::out) is det.
|
|
|
|
accumulator__rename_postrec_goals(Goals0, HstoAs_Subst, Goals) :-
|
|
goal_util__rename_vars_in_goals(Goals0, no, HstoAs_Subst, 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.")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__check_assoc(Gs, MGs, AGs)
|
|
%
|
|
% Given a list of goals, Gs, check to ensure that the list of
|
|
% goals Gs is associative rearranging a goal if necessary to
|
|
% ensure associativity. These goals are placed into MGs.
|
|
%
|
|
% It is not possible for construction unfications to be
|
|
% associative. However if only construction unfications remain
|
|
% after the recursive call, it is possible to do the lco (see
|
|
% lco.m) optimisation. So whenever the predicate encounters a
|
|
% construction unfication, it places it any goals that depend on
|
|
% that goal into AGs.
|
|
%
|
|
:- pred accumulator__check_assoc(hlds_goals::in, instmap::in,
|
|
module_info::in, bool::in, hlds_goals::out,
|
|
hlds_goals::out, assoc_info::in, assoc_info::out) is semidet.
|
|
|
|
accumulator__check_assoc([], _InstMap, _MI, _FullyStrict, [], []) --> [].
|
|
accumulator__check_assoc([Goal0 | Goal0s], InstMapBeforeGoal0,
|
|
ModuleInfo, FullyStrict, MovedGoals, AfterGoals) -->
|
|
(
|
|
{ goal_is_construction_unification(Goal0) }
|
|
->
|
|
{ move_goals(Goal0, InstMapBeforeGoal0, ModuleInfo, FullyStrict,
|
|
Goal0s, PreGoal0s, PostGoal0s) },
|
|
accumulator__check_assoc(PreGoal0s, InstMapBeforeGoal0,
|
|
ModuleInfo, FullyStrict,
|
|
MovedGoals, AfterGoal0s),
|
|
{ list__append(AfterGoal0s, [Goal0 | PostGoal0s], AfterGoals) }
|
|
;
|
|
{ Goal0 = _ - GoalInfo0 },
|
|
{ goal_info_get_instmap_delta(GoalInfo0, InstMapDelta) },
|
|
{ instmap__apply_instmap_delta(InstMapBeforeGoal0 ,
|
|
InstMapDelta, InstMapBeforeGoal0s) },
|
|
|
|
|
|
accumulator__check_goal(Goal0, Goal),
|
|
accumulator__check_assoc(Goal0s, InstMapBeforeGoal0s,
|
|
ModuleInfo, FullyStrict,
|
|
MovedGoal0s, AfterGoals),
|
|
{ MovedGoals = [Goal | MovedGoal0s] }
|
|
).
|
|
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% Is the goal a construction unification?
|
|
%
|
|
:- pred goal_is_construction_unification(hlds_goal::in) is semidet.
|
|
|
|
goal_is_construction_unification(Goal - _GoalInfo) :-
|
|
Goal = unify(_, _, _, Unification, _),
|
|
Unification = construct(_, _, _, _, _, _, _).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% Is the current goal associative?
|
|
%
|
|
:- pred accumulator__check_goal(hlds_goal::in, hlds_goal::out,
|
|
assoc_info::in, assoc_info::out) is semidet.
|
|
|
|
accumulator__check_goal(conj(Goals0) - GoalInfo, conj(Goals) - GoalInfo) -->
|
|
accumulator__check_goallist(Goals0, Goals).
|
|
|
|
accumulator__check_goal(par_conj(_, _) - _, _) -->
|
|
{ fail }.
|
|
|
|
% Branched Goals
|
|
%
|
|
% XXX the previous version of accumulator ensured the condition
|
|
% that each arm of the disjunct only updated static variables.
|
|
% However this will not handle the vectorising case.
|
|
accumulator__check_goal(disj(_, _) - _, _) --> { fail }.
|
|
accumulator__check_goal(switch(_, _, _, _) - _, _) --> { fail }.
|
|
accumulator__check_goal(if_then_else(_, _, _, _, _) - _, _) --> { fail }.
|
|
|
|
accumulator__check_goal(not(_) - _, _) --> { fail }.
|
|
|
|
accumulator__check_goal(some(Vars, CanRemove, Goal0) - GoalInfo,
|
|
some(Vars, CanRemove, Goal) - GoalInfo) -->
|
|
accumulator__check_goal(Goal0, Goal).
|
|
|
|
% All of these must fail because there is no way we can know
|
|
% whether or not these calls are associative.
|
|
% XXX this may not be true for class_method_call, it may be
|
|
% possible to make it a condition that this method is always
|
|
% associative.
|
|
accumulator__check_goal(generic_call(_,_,_,_) - _, _) --> { fail }.
|
|
accumulator__check_goal(pragma_c_code(_,_,_,_,_,_,_) - _, _) --> { fail }.
|
|
|
|
accumulator__check_goal(unify(TermL, TermR, Mode, Unify, Context) - GoalInfo,
|
|
unify(TermL, TermR, Mode, Unify, Context) - GoalInfo) -->
|
|
{ accumulator__check_assoc_unify_rhs(TermR) },
|
|
accumulator__check_assoc_unify(Unify).
|
|
|
|
accumulator__check_goal(call(PredId, ProcId, Arg0s, Builtin, Context, Sym)
|
|
- GoalInfo,
|
|
call(PredId, ProcId, Args, Builtin, Context, Sym)
|
|
- GoalInfo) -->
|
|
assoc_info_module_info(ModuleInfo),
|
|
accumulator__call_dynamic_var(Arg0s, DynamicCallVar),
|
|
|
|
{ accumulator__is_associative(PredId, ProcId, ModuleInfo, Arg0s, Args,
|
|
PossibleStaticVars, Commutative) },
|
|
|
|
(
|
|
% Make sure that after rearrangement of the
|
|
% arguments the new proc will be at least as
|
|
% efficient as the old pred.
|
|
{ Commutative = no },
|
|
assoc_info_static_set(StaticSet),
|
|
{ accumulator__obey_heuristic(PredId, ModuleInfo,
|
|
Args, StaticSet) }
|
|
;
|
|
{ Commutative = yes }
|
|
),
|
|
|
|
accumulator__check_previous_calls(DynamicCallVar,
|
|
PredId, ProcId, Commutative),
|
|
accumulator__new_dynamic_var(Arg0s, DynamicCallVar, NewDynamicVar),
|
|
accumulator__update_orig_var_map(DynamicCallVar, NewDynamicVar),
|
|
accumulator__update_Y0stoAs_subst(DynamicCallVar, [PossibleStaticVars]).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred accumulator__check_goallist(hlds_goals::in, hlds_goals::out,
|
|
assoc_info::in, assoc_info::out) is semidet.
|
|
|
|
accumulator__check_goallist([], []) --> [].
|
|
accumulator__check_goallist([Goal0 | Goals0], [Goal | Goals]) -->
|
|
accumulator__check_goal(Goal0, Goal),
|
|
accumulator__check_goallist(Goals0, Goals).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% This predicate is meant to fail if the rhs of the unification
|
|
% is a lambda goal, because I am not convinced that I know how
|
|
% to handle this correctly.
|
|
%
|
|
:- pred accumulator__check_assoc_unify_rhs(unify_rhs::in) is semidet.
|
|
|
|
accumulator__check_assoc_unify_rhs(var(_)).
|
|
accumulator__check_assoc_unify_rhs(functor(_, _)).
|
|
accumulator__check_assoc_unify_rhs(lambda_goal(_, _, _, _, _, _, _, _)) :-
|
|
%
|
|
% For the moment just fail, as I am not sure how to
|
|
% handle this.
|
|
%
|
|
fail.
|
|
|
|
%
|
|
% accumulator__check_assoc_unify_rhs
|
|
%
|
|
% The only safe unifications are assignment unifications.
|
|
%
|
|
:- pred accumulator__check_assoc_unify(unification::in,
|
|
assoc_info::in, assoc_info::out) is semidet.
|
|
|
|
accumulator__check_assoc_unify(
|
|
construct(_Var, _ConsId, _Vars, _Modes, _, _, _)) -->
|
|
%
|
|
% We shouldn't fail if we have this case as all the
|
|
% construction unification does is put a wrapper
|
|
% around the dynamic variables. What we need to
|
|
% recognise is that the construction/deconstruction
|
|
% pair do nothing.
|
|
%
|
|
% f(X,Y) :-
|
|
% decompose(X,Xh,Xr),
|
|
% f(Xr,Y0),
|
|
% Y0 = c(A0, B0),
|
|
% composeA(Xh,A0,A),
|
|
% composeB(Xh,B0,B),
|
|
% Y = c(A, B).
|
|
%
|
|
% I think that the way to recognise these
|
|
% situations is when the type of Y is flat
|
|
% (non-recursive).
|
|
%
|
|
{ fail }.
|
|
accumulator__check_assoc_unify(
|
|
deconstruct(_Var, _ConsId, _Vars, _Modes, _Cat)) -->
|
|
% see comment in construct
|
|
{ fail }.
|
|
accumulator__check_assoc_unify(assign(L, _R)) -->
|
|
assoc_info_dynamic_set(DynamicSet0),
|
|
{ set__insert(DynamicSet0, L, DynamicSet) },
|
|
assoc_info_set_dynamic_set(DynamicSet).
|
|
accumulator__check_assoc_unify(simple_test(_L, _R)) -->
|
|
{ fail }.
|
|
accumulator__check_assoc_unify(complicated_unify(_Modes, _Cat, _)) -->
|
|
{ fail }. % XXX not sure what this should be.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__call_dynamic_var(As, DV)
|
|
%
|
|
% Given the list of arguments to a call try and determine which
|
|
% of the arguments is the current dynamic variable.
|
|
%
|
|
% Fails if there is more then one dynamic variable in a call.
|
|
% This is because in general more then one dynamic variable
|
|
% being used in a call prevents the goals from being
|
|
% associative.
|
|
%
|
|
:- pred accumulator__call_dynamic_var(prog_vars::in, prog_var::out,
|
|
assoc_info::in, assoc_info::out) is semidet.
|
|
|
|
accumulator__call_dynamic_var(Args, DynamicCallVar) -->
|
|
assoc_info_dynamic_set(DynamicSet),
|
|
{ set__list_to_set(Args, ArgSet) },
|
|
{ set__intersect(ArgSet, DynamicSet, DynamicCallArgs) },
|
|
{ set__singleton_set(DynamicCallArgs, DynamicCallVar) }.
|
|
|
|
|
|
%
|
|
% accumulator__new_dynamic_var(As, DV, O)
|
|
%
|
|
% Given the original arguments to a call, As, and the dynamic
|
|
% var, DV, determine the new dynamic var, O, also update the
|
|
% dynamic set at the same time.
|
|
%
|
|
:- pred accumulator__new_dynamic_var(prog_vars::in, prog_var::in,
|
|
prog_var::out, assoc_info::in, assoc_info::out) is semidet.
|
|
|
|
accumulator__new_dynamic_var(Args0, DynamicCallVar, NewDynamicVar) -->
|
|
assoc_info_static_set(StaticSet0),
|
|
assoc_info_dynamic_set(DynamicSet0),
|
|
|
|
{ set__list_to_set(Args0, ArgSet) },
|
|
{ set__difference(ArgSet, StaticSet0, ArgDynamicSet) },
|
|
{ set__union(ArgDynamicSet, DynamicSet0, DynamicSet) },
|
|
|
|
assoc_info_set_dynamic_set(DynamicSet),
|
|
|
|
{ set__delete(ArgDynamicSet, DynamicCallVar, OutputDynamicCallArgs) },
|
|
{ set__singleton_set(OutputDynamicCallArgs, NewDynamicVar) }.
|
|
|
|
%
|
|
% accumulator__check_previous_calls
|
|
%
|
|
% Ensure that the previous calls have been commutative and calling
|
|
% the same procedure, or if it is associative that there has
|
|
% been no previous calls.
|
|
%
|
|
:- pred accumulator__check_previous_calls(prog_var::in,
|
|
pred_id::in, proc_id::in, commutative::in,
|
|
assoc_info::in, assoc_info::out) is semidet.
|
|
|
|
accumulator__check_previous_calls(DynamicCallVar,
|
|
PredId, ProcId, Commutative) -->
|
|
assoc_info_orig_dynvar_map(OrigDynMap),
|
|
assoc_info_prev_call_map(PrevCallMap0),
|
|
|
|
{ map__lookup(OrigDynMap, DynamicCallVar, OrigVar) },
|
|
{ accumulator__check_prevcalls(OrigVar, PredId, ProcId, Commutative,
|
|
PrevCallMap0, PrevCallMap) },
|
|
|
|
assoc_info_set_prev_call_map(PrevCallMap).
|
|
|
|
%
|
|
% accumulator__update_orig_var_map(DV, Ns)
|
|
%
|
|
% Record for each variable Ns which variable that variable
|
|
% is descended from.
|
|
%
|
|
:- pred accumulator__update_orig_var_map(prog_var::in, prog_var::in,
|
|
assoc_info::in, assoc_info::out) is det.
|
|
|
|
accumulator__update_orig_var_map(DynamicCallVar, NewDynamicVar) -->
|
|
assoc_info_orig_dynvar_map(OrigDynMap0),
|
|
|
|
{ map__lookup(OrigDynMap0, DynamicCallVar, OrigVar) },
|
|
{ map__det_insert(OrigDynMap0, NewDynamicVar, OrigVar, OrigDynMap) },
|
|
|
|
assoc_info_set_orig_dynvar_map(OrigDynMap).
|
|
|
|
%
|
|
% accumulator__update_Y0stoAs_subst
|
|
%
|
|
% Update the Y0s -> As Substitution.
|
|
%
|
|
:- pred accumulator__update_Y0stoAs_subst(prog_var::in, list(set(prog_var))::in,
|
|
assoc_info::in, assoc_info::out) is semidet.
|
|
|
|
accumulator__update_Y0stoAs_subst(DynamicCallVar, PossibleStaticVarsList) -->
|
|
assoc_info_orig_dynvar_map(OrigDynVarMap),
|
|
assoc_info_static_set(StaticSet),
|
|
assoc_info_HstoAs(HstoAs_Subst),
|
|
|
|
{ map__lookup(OrigDynVarMap, DynamicCallVar, OrigDynVar) },
|
|
|
|
assoc_info_Y0stoAs(Y0stoAs_Subst0),
|
|
{ accumulator__update_Y0stoAs_subst_2(PossibleStaticVarsList,
|
|
OrigDynVar, StaticSet, HstoAs_Subst,
|
|
Y0stoAs_Subst0, Y0stoAs_Subst) },
|
|
assoc_info_set_Y0stoAs(Y0stoAs_Subst).
|
|
|
|
|
|
:- pred accumulator__update_Y0stoAs_subst_2(list(set(prog_var))::in,
|
|
prog_var::in, set(prog_var)::in,
|
|
subst::in, subst::in, subst::out) is semidet.
|
|
|
|
accumulator__update_Y0stoAs_subst_2([], _, _, _, Y0stoAs_Subst, Y0stoAs_Subst).
|
|
accumulator__update_Y0stoAs_subst_2([PossibleStaticVars | T], OrigDynVar,
|
|
StaticSet, HstoAs_Subst, Y0stoAs_Subst0, Y0stoAs_Subst) :-
|
|
|
|
set__intersect(PossibleStaticVars, StaticSet, StaticVarSet),
|
|
set__to_sorted_list(StaticVarSet, StaticVar),
|
|
list__map(map__lookup(HstoAs_Subst), StaticVar, As),
|
|
|
|
% There should only be one variable that is being
|
|
% accumulated.
|
|
As = [AccVar],
|
|
|
|
%
|
|
% If you have a chain of commutative calls,
|
|
% only the first one will need to have the
|
|
% accumulator placed in the call.
|
|
%
|
|
(
|
|
map__insert(Y0stoAs_Subst0, OrigDynVar, AccVar,
|
|
Y0stoAs_Subst1)
|
|
->
|
|
Y0stoAs_Subst2 = Y0stoAs_Subst1
|
|
;
|
|
Y0stoAs_Subst2 = Y0stoAs_Subst0
|
|
),
|
|
|
|
accumulator__update_Y0stoAs_subst_2(T, OrigDynVar, StaticSet,
|
|
HstoAs_Subst, Y0stoAs_Subst2, Y0stoAs_Subst).
|
|
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__check_prevcalls
|
|
%
|
|
% We must ensure that if the dynamic variables which the current
|
|
% variable is descended from have been used in any other
|
|
% calls previously that all these calls have been commutative
|
|
% including the current call.
|
|
%
|
|
% If this is true update the prev call map.
|
|
%
|
|
:- pred accumulator__check_prevcalls(prog_var::in,
|
|
pred_id::in, proc_id::in, commutative::in,
|
|
prev_call_map::in, prev_call_map::out) is semidet.
|
|
|
|
accumulator__check_prevcalls(OrigVar, PredId, ProcId, Commutative,
|
|
PrevCallMap0, PrevCallMap) :-
|
|
(
|
|
map__search(PrevCallMap0, OrigVar, PrevCall)
|
|
->
|
|
%
|
|
% Only succeed if the current call is
|
|
% commutative and all the previous calls are
|
|
% commutative and to the same procedure.
|
|
%
|
|
PrevCall = prev_call(PredId, ProcId, yes),
|
|
PrevCallMap = PrevCallMap0
|
|
;
|
|
%
|
|
% There are no previous calls so set whether or
|
|
% not the current call is commutative for all
|
|
% the original variables.
|
|
%
|
|
map__det_insert(PrevCallMap0, OrigVar,
|
|
prev_call(PredId, ProcId, Commutative),
|
|
PrevCallMap)
|
|
).
|
|
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% If accumulator_is_associative is true, it returns a reordering
|
|
% of the args to make it associative when executed left to right
|
|
% and an indicator of whether or not the predicate is
|
|
% commutative.
|
|
%
|
|
:- pred accumulator__is_associative(pred_id::in, proc_id::in, module_info::in,
|
|
prog_vars::in, prog_vars::out, set(prog_var)::out,
|
|
commutative::out) is semidet.
|
|
|
|
accumulator__is_associative(PredId, ProcId, ModuleInfo,
|
|
Args0, Args, PossibleStaticVars, Commutative):-
|
|
module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
|
|
PredInfo, ProcInfo),
|
|
pred_info_module(PredInfo, ModuleName),
|
|
pred_info_name(PredInfo, PredName),
|
|
pred_info_arity(PredInfo, Arity),
|
|
proc_info_argmodes(ProcInfo, Modes),
|
|
assoc_fact(ModuleName, PredName, Arity, Modes, ModuleInfo, Args0, Args,
|
|
PossibleStaticVars, Reordered),
|
|
bool__not(Reordered, Commutative).
|
|
|
|
%
|
|
% XXX this fact table is only a temporary solution to whether or
|
|
% not a particular procedure is associative. In the long term
|
|
% the user should be able to annotate their code to indicate
|
|
% which predicates are associative.
|
|
%
|
|
% The set is simply the set of vars that must be static. It is
|
|
% a simple heuristic to ensure that the O() behaviour only
|
|
% improves. ie for append after swapping the arguments the
|
|
% static variable must be in the first location.
|
|
%
|
|
:- pred assoc_fact(module_name::in, string::in, arity::in,
|
|
list(mode)::in, module_info::in, prog_vars::in,
|
|
prog_vars::out, set(prog_var)::out, bool::out) is semidet.
|
|
|
|
assoc_fact(unqualified("int"), "+", 3, [In, In, Out], ModuleInfo,
|
|
[A, B, C], [A, B, C], PossibleStaticVars, no) :-
|
|
set__list_to_set([A, B], PossibleStaticVars),
|
|
mode_is_input(ModuleInfo, In),
|
|
mode_is_output(ModuleInfo, Out).
|
|
|
|
assoc_fact(unqualified("int"), "*", 3, [In, In, Out], ModuleInfo,
|
|
[A, B, C], [A, B, C], PossibleStaticVars, no) :-
|
|
set__list_to_set([A, B], PossibleStaticVars),
|
|
mode_is_input(ModuleInfo, In),
|
|
mode_is_output(ModuleInfo, Out).
|
|
|
|
assoc_fact(unqualified("list"), "append", 3, [TypeInfoIn, In, In, Out],
|
|
ModuleInfo, [TypeInfo, A, B, C],
|
|
[TypeInfo, B, A, C], PossibleStaticVars, yes) :-
|
|
set__list_to_set([A, B], PossibleStaticVars),
|
|
mode_is_input(ModuleInfo, TypeInfoIn),
|
|
mode_is_input(ModuleInfo, In),
|
|
mode_is_output(ModuleInfo, Out).
|
|
|
|
|
|
|
|
/* XXX introducing accumulators for floating point numbers can be bad.
|
|
assoc_fact(unqualified("float"), "+", 3, [In, In, Out], ModuleInfo,
|
|
[A, B, C], [A, B, C], PossibleStaticVars, no) :-
|
|
set__list_to_set([A, B], PossibleStaticVars),
|
|
mode_is_input(ModuleInfo, In),
|
|
mode_is_output(ModuleInfo, Out).
|
|
|
|
assoc_fact(unqualified("float"), "*", 3, [In, In, Out], ModuleInfo,
|
|
[A, B, C], [A, B, C], PossibleStaticVars, no) :-
|
|
set__list_to_set([A, B], PossibleStaticVars),
|
|
mode_is_input(ModuleInfo, In),
|
|
mode_is_output(ModuleInfo, Out).
|
|
*/
|
|
/*
|
|
XXX this no longer works, because set__insert isn't associative.
|
|
|
|
However set__insert obeys the following axiom, providing that you
|
|
use user-defined equality (set__equals), not structural equality
|
|
for S.
|
|
|
|
some [SA] (p(A, S0, SA), p(B, SA, S)) <=>
|
|
some [SB] (p(B, S0, SB), p(A, SB, S))
|
|
|
|
My previous attempt at this transformation handled this case
|
|
and I thought the current one did as well. I was wrong. I need
|
|
to reintegrate my old code.
|
|
|
|
assoc_fact(unqualified("set"), "insert", 3, [TypeInfoIn, In, In, Out],
|
|
Moduleinfo, [TypeInfo, A, B, C],
|
|
[TypeInfo, A, B, C], PossibleStaticVars, no) :-
|
|
set__list_to_set([A, B], PossibleStaticVars),
|
|
mode_is_input(Moduleinfo, TypeInfoIn),
|
|
mode_is_input(Moduleinfo, In),
|
|
mode_is_output(Moduleinfo, Out).
|
|
*/
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%
|
|
% accumulator__obey_heuristic
|
|
%
|
|
% for calls which rearrange the order of variables ensure that
|
|
% the call obeys the heuristic that the static variables are in
|
|
% certain positions.
|
|
%
|
|
% For example, a call to append in the forward mode will have
|
|
% the following types of variables: (static, dynamic, dynamic).
|
|
% After rearrangment that order will be (dynamic, static, dynamic).
|
|
% Having a dynamic variable in the first position will probably
|
|
% take O(N) time to process while having a static variable will
|
|
% probably take O(1) time. Therefore the complexity of the
|
|
% predicate as a whole will change, we must ensure that it
|
|
% changes for the better.
|
|
%
|
|
:- pred accumulator__obey_heuristic(pred_id::in, module_info::in,
|
|
prog_vars::in, set(prog_var)::in) is semidet.
|
|
|
|
accumulator__obey_heuristic(Predid, ModuleInfo, Args, StaticSet) :-
|
|
module_info_pred_info(ModuleInfo, Predid, PredInfo),
|
|
pred_info_module(PredInfo, ModuleName),
|
|
pred_info_name(PredInfo, PredName),
|
|
pred_info_arity(PredInfo, Arity),
|
|
heuristic(ModuleName, PredName, Arity, Args, MustBeStaticVars),
|
|
set__intersect(StaticSet, MustBeStaticVars, Intersection),
|
|
set__equal(MustBeStaticVars, Intersection).
|
|
|
|
:- 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).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred assoc_info_init(module_info::in, prog_vars::in, a_goals::in,
|
|
a_goals::in, a_goal::in,
|
|
subst::in, subst::in, assoc_info::out) is det.
|
|
|
|
assoc_info_init(ModuleInfo, HeadVars, DP, Compose, R, Y0stoYs_Subst,
|
|
HstoAs_Subst, AssocInfo) :-
|
|
DP = goal(Decompose, _InstMapBeforeDecomposeProcess),
|
|
R = goal(_RecGoalExpr - RecGoalInfo, _InstMapBeforeRecursive),
|
|
|
|
map__init(PrevCallMap),
|
|
|
|
%
|
|
% Set up the OrigDynVarMap
|
|
%
|
|
reverse_subst(Y0stoYs_Subst, YstoY0s_Subst),
|
|
accumulator__vars_to_accumulate(HeadVars, Compose, ModuleInfo, Ys),
|
|
list__map(map__lookup(YstoY0s_Subst), Ys, Y0s),
|
|
|
|
assoc_list__from_corresponding_lists(Y0s, Y0s, AssocDynamicList),
|
|
map__from_assoc_list(AssocDynamicList, OrigDynVarMap),
|
|
|
|
%
|
|
% Dynamic Set
|
|
%
|
|
set__list_to_set(Y0s, DynamicSet),
|
|
|
|
%
|
|
% Static Set
|
|
%
|
|
GetGoalInfos = (pred(Goal::in, GoalInfo::out) is det :-
|
|
Goal = _ - GoalInfo),
|
|
list__map(GetGoalInfos, Decompose, DPGoalInfos),
|
|
list__map(goal_info_get_nonlocals, DPGoalInfos, DPNonLocals),
|
|
set__list_to_set(DPNonLocals, DPNonLocalsPowerSet),
|
|
set__power_union(DPNonLocalsPowerSet, StaticSet0),
|
|
|
|
goal_info_get_nonlocals(RecGoalInfo, RecNonLocals),
|
|
set__delete_list(RecNonLocals, Y0s, StaticSet1),
|
|
|
|
set__union(StaticSet0, StaticSet1, StaticSet),
|
|
|
|
%
|
|
% Y0stoAs
|
|
%
|
|
map__init(Y0stoAs_Subst),
|
|
|
|
%
|
|
% Ys
|
|
%
|
|
set__list_to_set(Ys, YsSet),
|
|
|
|
AssocInfo = assoc_info(StaticSet, DynamicSet,
|
|
ModuleInfo, PrevCallMap, OrigDynVarMap,
|
|
Y0stoAs_Subst, HstoAs_Subst, YsSet).
|
|
|
|
:- pred assoc_info_static_set(set(prog_var)::out,
|
|
assoc_info::in, assoc_info::out) is det.
|
|
assoc_info_static_set(StaticSet, AssocInfo, AssocInfo) :-
|
|
AssocInfo = assoc_info(StaticSet, _, _, _, _, _, _, _).
|
|
|
|
:- pred assoc_info_dynamic_set(set(prog_var)::out,
|
|
assoc_info::in, assoc_info::out) is det.
|
|
assoc_info_dynamic_set(DynamicSet, AssocInfo, AssocInfo) :-
|
|
AssocInfo = assoc_info(_, DynamicSet, _, _, _, _, _, _).
|
|
|
|
:- pred assoc_info_module_info(module_info::out,
|
|
assoc_info::in, assoc_info::out) is det.
|
|
assoc_info_module_info(ModuleInfo, AssocInfo, AssocInfo) :-
|
|
AssocInfo = assoc_info(_, _, ModuleInfo, _, _, _, _, _).
|
|
|
|
:- pred assoc_info_prev_call_map(prev_call_map::out,
|
|
assoc_info::in, assoc_info::out) is det.
|
|
assoc_info_prev_call_map(PrevCallMap, AssocInfo, AssocInfo) :-
|
|
AssocInfo = assoc_info(_, _, _, PrevCallMap, _, _, _, _).
|
|
|
|
:- pred assoc_info_orig_dynvar_map(orig_dynvar_map::out,
|
|
assoc_info::in, assoc_info::out) is det.
|
|
assoc_info_orig_dynvar_map(OrigDynVarMap, AssocInfo, AssocInfo) :-
|
|
AssocInfo = assoc_info(_, _, _, _, OrigDynVarMap, _, _, _).
|
|
|
|
:- pred assoc_info_Y0stoAs(subst::out,
|
|
assoc_info::in, assoc_info::out) is det.
|
|
assoc_info_Y0stoAs(Y0stoAs_Subst, AssocInfo, AssocInfo) :-
|
|
AssocInfo = assoc_info(_, _, _, _, _, Y0stoAs_Subst, _, _).
|
|
|
|
:- pred assoc_info_HstoAs(subst::out,
|
|
assoc_info::in, assoc_info::out) is det.
|
|
assoc_info_HstoAs(HstoAs_Subst, AssocInfo, AssocInfo) :-
|
|
AssocInfo = assoc_info(_, _, _, _, _, _, HstoAs_Subst, _).
|
|
|
|
:- pred assoc_info_Ys(set(prog_var)::out,
|
|
assoc_info::in, assoc_info::out) is det.
|
|
assoc_info_Ys(Ys, AssocInfo, AssocInfo) :-
|
|
AssocInfo = assoc_info(_, _, _, _, _, _, _, Ys).
|
|
|
|
/*
|
|
:- pred assoc_info_set_static_set(set(prog_var)::in, assoc_info::in,
|
|
assoc_info::out) is det.
|
|
assoc_info_set_static_set(StaticSet, assoc_info(_, B, C, D, E, F, G, H),
|
|
assoc_info(StaticSet, B, C, D, E, F, G, H)).
|
|
*/
|
|
|
|
:- pred assoc_info_set_dynamic_set(set(prog_var)::in, assoc_info::in,
|
|
assoc_info::out) is det.
|
|
assoc_info_set_dynamic_set(DynamicSet, assoc_info(A, _, C, D, E, F, G, H),
|
|
assoc_info(A, DynamicSet, C, D, E, F, G, H)).
|
|
|
|
:- pred assoc_info_set_prev_call_map(prev_call_map::in, assoc_info::in,
|
|
assoc_info::out) is det.
|
|
assoc_info_set_prev_call_map(PrevCallMap, assoc_info(A, B, C, _, E, F, G, H),
|
|
assoc_info(A, B, C, PrevCallMap, E, F, G, H)).
|
|
|
|
:- pred assoc_info_set_orig_dynvar_map(orig_dynvar_map::in, assoc_info::in,
|
|
assoc_info::out) is det.
|
|
assoc_info_set_orig_dynvar_map(OrigDynMap, assoc_info(A, B, C, D, _, F, G, H),
|
|
assoc_info(A, B, C, D, OrigDynMap, F, G, H)).
|
|
|
|
:- pred assoc_info_set_Y0stoAs(subst::in, assoc_info::in,
|
|
assoc_info::out) is det.
|
|
assoc_info_set_Y0stoAs(Y0stoAs_Subst, assoc_info(A, B, C, D, E, _, G, H),
|
|
assoc_info(A, B, C, D, E, Y0stoAs_Subst, G, H)).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred reverse_subst(subst::in, subst::out) is det.
|
|
|
|
reverse_subst(Subst0, Subst) :-
|
|
map__to_assoc_list(Subst0, List0),
|
|
assoc_list__reverse_members(List0, List),
|
|
map__from_assoc_list(List, Subst).
|
|
|
|
:- pred chain_subst(subst::in, subst::in, subst::out) is det.
|
|
|
|
chain_subst(AtoB, BtoC, AtoC) :-
|
|
map__keys(AtoB, Keys),
|
|
chain_subst_2(Keys, AtoB, BtoC, AtoC).
|
|
|
|
:- pred chain_subst_2(list(A)::in, map(A, B)::in, map(B, C)::in,
|
|
map(A, C)::out) is det.
|
|
|
|
chain_subst_2([], _, _, AtoC) :-
|
|
map__init(AtoC).
|
|
chain_subst_2([A|As], AtoB, BtoC, AtoC) :-
|
|
chain_subst_2(As, AtoB, BtoC, AtoC0),
|
|
map__lookup(AtoB, A, B),
|
|
map__lookup(BtoC, B, C),
|
|
map__det_insert(AtoC0, A, C, AtoC).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|