mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-15 13:55:07 +00:00
Estimated hours taken: 10 Clean up the handling of commits and goals which cannot succeed. This fixes problems with tests/hard_coded/cut_test.m. compiler/simplify.m: Be more careful about using `true_goal/1' and `fail_goal/1' instead of `conj([])' and `disj([], _)'. When removing unnecessary disjunctions or if-then-elses around goals be sure to wrap the goal if the inner and outer determinisms are not equal. Rerun determinism analysis where required. Remove `some's where the inner and outer determinisms are the same. compiler/mode_util.m: Enforce the invariant that if the initial instmap is unreachable, the instmap_delta is unreachable. compiler/ite_gen.m: Don't barf if the `then' part of an if-then-else is unreachable. compiler/common.m: Check for constructions where the assigned variable is not output. compiler/deforest.m: Requantify before rerunning mode analysis when inlining. compiler/det_analysis.m: Export predicates to run determinism analysis on a goal, not an entire procedure.
1786 lines
62 KiB
Mathematica
1786 lines
62 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1996-1998 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.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Main authors: zs, stayl.
|
|
%
|
|
% The two jobs of the simplification module are
|
|
%
|
|
% to find and exploit opportunities for simplifying the internal form
|
|
% of the program, both to optimize the code and to massage the code
|
|
% into a form the code generator will accept, and
|
|
%
|
|
% to warn the programmer about any constructs that are so simple that
|
|
% they should not have been included in the program in the first place.
|
|
%
|
|
% Simplification is done in two passes. The first pass performs common
|
|
% structure and duplicate call elimination. The second pass performs
|
|
% excess assignment elimination and cleans up the code after the first pass.
|
|
% Two passes are required because the goal must be requantified after the
|
|
% optimizations in common.m are run so that excess assignment elimination
|
|
% works properly.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module simplify.
|
|
|
|
:- interface.
|
|
|
|
:- import_module hlds_goal, hlds_module, hlds_pred, det_report, det_util.
|
|
:- import_module common, instmap, globals.
|
|
:- import_module io, bool, list, map, term, varset.
|
|
|
|
:- pred simplify__pred(list(simplification), pred_id, module_info, module_info,
|
|
pred_info, pred_info, int, int, io__state, io__state).
|
|
:- mode simplify__pred(in, in, in, out, in, out, out, out, di, uo) is det.
|
|
|
|
:- pred simplify__proc(list(simplification), pred_id, proc_id,
|
|
module_info, module_info, proc_info, proc_info, io__state, io__state).
|
|
:- mode simplify__proc(in, in, in, in, out, in, out, di, uo) is det.
|
|
|
|
:- pred simplify__process_goal(hlds_goal, hlds_goal,
|
|
simplify_info, simplify_info).
|
|
:- mode simplify__process_goal(in, out, in, out) is det.
|
|
|
|
% Find out which simplifications should be run from the options table
|
|
% stored in the globals. The first argument states whether warnings
|
|
% should be issued during this pass of simplification.
|
|
:- pred simplify__find_simplifications(bool, globals, list(simplification)).
|
|
:- mode simplify__find_simplifications(in, in, out) is det.
|
|
|
|
:- type simplification
|
|
---> warn_simple_code % --warn-simple-code
|
|
; warn_duplicate_calls % --warn-duplicate-calls
|
|
; do_once % run things that should be done once
|
|
; excess_assigns % remove excess assignment unifications
|
|
; duplicate_calls % optimize duplicate calls
|
|
; constant_prop % partially evaluate calls
|
|
; common_struct % common structure elimination
|
|
; extra_common_struct % do common structure elimination
|
|
% even when it might increase stack
|
|
% usage (used by deforestation).
|
|
.
|
|
|
|
:- type simplify_info.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module code_aux, det_analysis, follow_code, goal_util, const_prop.
|
|
:- import_module hlds_module, hlds_data, (inst), inst_match.
|
|
:- import_module options, passes_aux, prog_data, mode_util, type_util.
|
|
:- import_module code_util, quantification, modes, purity, pd_cost.
|
|
:- import_module set, require, std_util, int.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
simplify__pred(Simplifications0, PredId, ModuleInfo0, ModuleInfo,
|
|
PredInfo0, PredInfo, WarnCnt, ErrCnt) -->
|
|
write_pred_progress_message("% Simplifying ", PredId, ModuleInfo0),
|
|
{ pred_info_non_imported_procids(PredInfo0, ProcIds) },
|
|
{ MaybeMsgs0 = no },
|
|
{
|
|
% Don't warn for compiler-generated procedures.
|
|
list__member(warn_simple_code, Simplifications0),
|
|
module_info_pred_info(ModuleInfo0, PredId, PredInfo0),
|
|
code_util__compiler_generated(PredInfo0)
|
|
->
|
|
list__delete_all(Simplifications0, warn_simple_code,
|
|
Simplifications)
|
|
;
|
|
Simplifications = Simplifications0
|
|
},
|
|
simplify__procs(Simplifications, PredId, ProcIds, ModuleInfo0,
|
|
ModuleInfo, PredInfo0, PredInfo, MaybeMsgs0, MaybeMsgs),
|
|
( { MaybeMsgs = yes(Msgs0 - Msgs1) } ->
|
|
{ set__union(Msgs0, Msgs1, Msgs2) },
|
|
{ set__to_sorted_list(Msgs2, Msgs) },
|
|
det_report_msgs(Msgs, ModuleInfo, WarnCnt, ErrCnt)
|
|
;
|
|
{ WarnCnt = 0 },
|
|
{ ErrCnt = 0 }
|
|
).
|
|
|
|
:- pred simplify__procs(list(simplification), pred_id, list(proc_id),
|
|
module_info, module_info, pred_info, pred_info,
|
|
maybe(pair(set(det_msg))), maybe(pair(set(det_msg))),
|
|
io__state, io__state).
|
|
:- mode simplify__procs(in, in, in, in, out, in, out,
|
|
in, out, di, uo) is det.
|
|
|
|
simplify__procs(_, _, [], ModuleInfo, ModuleInfo, PredInfo, PredInfo,
|
|
Msgs, Msgs) --> [].
|
|
simplify__procs(Simplifications, PredId, [ProcId | ProcIds], ModuleInfo0,
|
|
ModuleInfo, PredInfo0, PredInfo, MaybeMsgs0, MaybeMsgs) -->
|
|
{ pred_info_procedures(PredInfo0, Procs0) },
|
|
{ map__lookup(Procs0, ProcId, Proc0) },
|
|
simplify__proc_2(Simplifications, PredId, ProcId, ModuleInfo0,
|
|
ModuleInfo1, Proc0, Proc, Msgs1),
|
|
{ map__det_update(Procs0, ProcId, Proc, Procs) },
|
|
{ pred_info_set_procedures(PredInfo0, Procs, PredInfo1) },
|
|
{ set__to_sorted_list(Msgs1, Msgs2) },
|
|
{ list__filter(lambda([Msg::in] is semidet,
|
|
det_msg_is_any_mode_msg(Msg, any_mode)),
|
|
Msgs2, AnyModeMsgs1, AllModeMsgs1) },
|
|
{ set__sorted_list_to_set(AnyModeMsgs1, AnyModeMsgs2) },
|
|
{ set__sorted_list_to_set(AllModeMsgs1, AllModeMsgs2) },
|
|
{ MaybeMsgs0 = yes(AnyModeMsgs0 - AllModeMsgs0) ->
|
|
set__union(AnyModeMsgs0, AnyModeMsgs2, AnyModeMsgs),
|
|
set__intersect(AllModeMsgs0, AllModeMsgs2, AllModeMsgs),
|
|
MaybeMsgs1 = yes(AllModeMsgs - AnyModeMsgs)
|
|
;
|
|
MaybeMsgs1 = yes(AnyModeMsgs2 - AllModeMsgs2)
|
|
},
|
|
simplify__procs(Simplifications, PredId, ProcIds, ModuleInfo1,
|
|
ModuleInfo, PredInfo1, PredInfo, MaybeMsgs1, MaybeMsgs).
|
|
|
|
simplify__proc(Simplifications, PredId, ProcId, ModuleInfo0, ModuleInfo,
|
|
Proc0, Proc) -->
|
|
write_pred_progress_message("% Simplifying ", PredId, ModuleInfo0),
|
|
simplify__proc_2(Simplifications, PredId, ProcId, ModuleInfo0,
|
|
ModuleInfo, Proc0, Proc, _).
|
|
|
|
:- pred simplify__proc_2(list(simplification), pred_id, proc_id, module_info,
|
|
module_info, proc_info, proc_info, set(det_msg),
|
|
io__state, io__state).
|
|
:- mode simplify__proc_2(in, in, in, in, out, in, out,
|
|
out, di, uo) is det.
|
|
|
|
simplify__proc_2(Simplifications, PredId, ProcId, ModuleInfo0, ModuleInfo,
|
|
ProcInfo0, ProcInfo, Msgs, State0, State) :-
|
|
|
|
globals__io_get_globals(Globals, State0, State),
|
|
det_info_init(ModuleInfo0, PredId, ProcId, Globals, DetInfo0),
|
|
proc_info_get_initial_instmap(ProcInfo0, ModuleInfo0, InstMap0),
|
|
proc_info_varset(ProcInfo0, VarSet0),
|
|
proc_info_vartypes(ProcInfo0, VarTypes0),
|
|
proc_info_goal(ProcInfo0, Goal0),
|
|
|
|
simplify_info_init(DetInfo0, Simplifications, InstMap0,
|
|
VarSet0, VarTypes0, Info0),
|
|
simplify__process_goal(Goal0, Goal, Info0, Info),
|
|
|
|
simplify_info_get_varset(Info, VarSet),
|
|
simplify_info_get_var_types(Info, VarTypes),
|
|
proc_info_set_varset(ProcInfo0, VarSet, ProcInfo1),
|
|
proc_info_set_vartypes(ProcInfo1, VarTypes, ProcInfo2),
|
|
proc_info_set_goal(ProcInfo2, Goal, ProcInfo),
|
|
simplify_info_get_module_info(Info, ModuleInfo),
|
|
simplify_info_get_msgs(Info, Msgs).
|
|
|
|
simplify__process_goal(Goal0, Goal, Info0, Info) :-
|
|
simplify_info_get_simplifications(Info0, Simplifications0),
|
|
simplify_info_get_instmap(Info0, InstMap0),
|
|
|
|
( (simplify_do_common(Info0); simplify_do_calls(Info0)) ->
|
|
% On the first pass do common structure and call elimination.
|
|
NotOnFirstPass = [do_once, excess_assigns],
|
|
|
|
set__delete_list(Simplifications0, NotOnFirstPass,
|
|
Simplifications1),
|
|
simplify_info_set_simplifications(Info0, Simplifications1,
|
|
Info1),
|
|
|
|
simplify__do_process_goal(Goal0, Goal1, Info1, Info2),
|
|
|
|
NotOnSecondPass = [warn_simple_code, warn_duplicate_calls,
|
|
common_struct, duplicate_calls],
|
|
set__delete_list(Simplifications0, NotOnSecondPass,
|
|
Simplifications2),
|
|
simplify_info_reinit(Simplifications2, InstMap0, Info2, Info3)
|
|
;
|
|
Info3 = Info0,
|
|
Goal1 = Goal0
|
|
),
|
|
% On the second pass do excess assignment elimination and
|
|
% some cleaning up after the common structure pass.
|
|
simplify__do_process_goal(Goal1, Goal, Info3, Info).
|
|
|
|
:- pred simplify__do_process_goal(hlds_goal::in, hlds_goal::out,
|
|
simplify_info::in, simplify_info::out) is det.
|
|
|
|
simplify__do_process_goal(Goal0, Goal, Info0, Info) :-
|
|
simplify_info_get_instmap(Info0, InstMap0),
|
|
simplify__goal(Goal0, Goal1, Info0, Info1),
|
|
simplify_info_get_varset(Info1, VarSet0),
|
|
simplify_info_get_var_types(Info1, VarTypes0),
|
|
( simplify_info_requantify(Info1) ->
|
|
Goal1 = _ - GoalInfo1,
|
|
goal_info_get_nonlocals(GoalInfo1, NonLocals),
|
|
|
|
implicitly_quantify_goal(Goal1, VarSet0, VarTypes0, NonLocals,
|
|
Goal2, VarSet, VarTypes, _),
|
|
|
|
simplify_info_set_varset(Info1, VarSet, Info2),
|
|
simplify_info_set_var_types(Info2, VarTypes, Info3),
|
|
|
|
% Always recompute instmap_deltas for atomic goals - this
|
|
% is safer in the case where unused variables should no
|
|
% longer be included in the instmap_delta for a goal.
|
|
% In the alias branch this is necessary anyway.
|
|
RecomputeAtomic = yes,
|
|
|
|
simplify_info_get_module_info(Info3, ModuleInfo1),
|
|
recompute_instmap_delta(RecomputeAtomic, Goal2, Goal3,
|
|
InstMap0, ModuleInfo1, ModuleInfo),
|
|
simplify_info_set_module_info(Info3, ModuleInfo, Info)
|
|
;
|
|
Goal3 = Goal1,
|
|
Info = Info1
|
|
),
|
|
( simplify_info_rerun_det(Info) ->
|
|
Goal0 = _ - GoalInfo0,
|
|
goal_info_get_determinism(GoalInfo0, Det),
|
|
det_get_soln_context(Det, SolnContext),
|
|
simplify_info_get_det_info(Info, DetInfo),
|
|
det_infer_goal(Goal3, InstMap0, SolnContext,
|
|
DetInfo, Goal, _, _)
|
|
;
|
|
Goal = Goal3
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
simplify__find_simplifications(WarnThisPass, Globals, S) :-
|
|
simplify__find_simplifications_2(WarnThisPass, Globals, [], S).
|
|
|
|
:- pred simplify__find_simplifications_2(bool, globals,
|
|
list(simplification), list(simplification)).
|
|
:- mode simplify__find_simplifications_2(in, in, in, out) is det.
|
|
|
|
simplify__find_simplifications_2(WarnThisPass, Globals) -->
|
|
( { WarnThisPass = yes } ->
|
|
simplify__lookup_option(Globals, warn_duplicate_calls,
|
|
warn_duplicate_calls),
|
|
simplify__lookup_option(Globals, warn_simple_code,
|
|
warn_simple_code)
|
|
;
|
|
[]
|
|
),
|
|
simplify__lookup_option(Globals, excess_assign, excess_assigns),
|
|
simplify__lookup_option(Globals, common_struct, common_struct),
|
|
simplify__lookup_option(Globals, optimize_duplicate_calls,
|
|
duplicate_calls),
|
|
simplify__lookup_option(Globals, constant_propagation,
|
|
constant_prop).
|
|
|
|
:- pred simplify__lookup_option(globals::in, option::in, simplification::in,
|
|
list(simplification)::in, list(simplification)::out) is det.
|
|
|
|
simplify__lookup_option(Globals, Option, Simplification,
|
|
Simplifications0, Simplifications) :-
|
|
globals__lookup_bool_option(Globals, Option, Result),
|
|
( Result = yes ->
|
|
Simplifications = [Simplification | Simplifications0]
|
|
;
|
|
Simplifications = Simplifications0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred simplify__goal(hlds_goal, hlds_goal, simplify_info, simplify_info).
|
|
:- mode simplify__goal(in, out, in, out) is det.
|
|
|
|
simplify__goal(Goal0, Goal - GoalInfo, Info0, Info) :-
|
|
Goal0 = _ - GoalInfo0,
|
|
goal_info_get_determinism(GoalInfo0, Detism),
|
|
simplify_info_get_det_info(Info0, DetInfo),
|
|
simplify_info_get_module_info(Info0, ModuleInfo),
|
|
(
|
|
%
|
|
% if --no-fully-strict,
|
|
% replace goals with determinism failure with `fail'.
|
|
% XXX we should warn about this (if the goal wasn't `fail')
|
|
%
|
|
Detism = failure,
|
|
% ensure goal is pure or semipure
|
|
\+ goal_info_is_impure(GoalInfo0),
|
|
( det_info_get_fully_strict(DetInfo, no)
|
|
; code_aux__goal_cannot_loop(ModuleInfo, Goal0)
|
|
)
|
|
->
|
|
% If the goal had any non-locals we should requantify.
|
|
goal_info_get_nonlocals(GoalInfo0, NonLocals0),
|
|
( set__empty(NonLocals0) ->
|
|
Info1 = Info0
|
|
;
|
|
simplify_info_set_requantify(Info0, Info1)
|
|
),
|
|
pd_cost__goal(Goal0, CostDelta),
|
|
simplify_info_incr_cost_delta(Info1, CostDelta, Info2),
|
|
goal_info_get_context(GoalInfo0, Context),
|
|
fail_goal(Context, Goal1)
|
|
;
|
|
%
|
|
% if --no-fully-strict,
|
|
% replace goals which cannot fail and have no
|
|
% output variables with `true'.
|
|
% However, we don't do this for erroneous goals,
|
|
% since these may occur in conjunctions where there
|
|
% are no producers for some variables, and the
|
|
% code generator would fail for these.
|
|
% XXX we should warn about this (if the goal wasn't `true')
|
|
%
|
|
|
|
% XXX this optimization is currently disabled for anything
|
|
% other than unifications, since it mishandles calls to
|
|
% existentially typed predicates.
|
|
% The fix for this is to run polymorphism.m before simplify.m.
|
|
% When that is done, we can re-enable this optimization.
|
|
Goal0 = unify(_, _, _, _, _) - _,
|
|
|
|
determinism_components(Detism, cannot_fail, MaxSoln),
|
|
MaxSoln \= at_most_zero,
|
|
goal_info_get_instmap_delta(GoalInfo0, InstMapDelta),
|
|
goal_info_get_nonlocals(GoalInfo0, NonLocalVars),
|
|
simplify_info_get_instmap(Info0, InstMap0),
|
|
det_no_output_vars(NonLocalVars, InstMap0, InstMapDelta,
|
|
DetInfo),
|
|
% ensure goal is pure or semipure
|
|
\+ goal_info_is_impure(GoalInfo0),
|
|
( det_info_get_fully_strict(DetInfo, no)
|
|
; code_aux__goal_cannot_loop(ModuleInfo, Goal0)
|
|
)
|
|
->
|
|
% If the goal had any non-locals we should requantify.
|
|
goal_info_get_nonlocals(GoalInfo0, NonLocals0),
|
|
( set__empty(NonLocals0) ->
|
|
Info1 = Info0
|
|
;
|
|
simplify_info_set_requantify(Info0, Info1)
|
|
),
|
|
pd_cost__goal(Goal0, CostDelta),
|
|
simplify_info_incr_cost_delta(Info1, CostDelta, Info2),
|
|
goal_info_get_context(GoalInfo0, Context),
|
|
true_goal(Context, Goal1)
|
|
;
|
|
Goal1 = Goal0,
|
|
Info2 = Info0
|
|
),
|
|
simplify_info_maybe_clear_structs(before, Goal1, Info2, Info3),
|
|
Goal1 = GoalExpr1 - GoalInfo1,
|
|
simplify__goal_2(GoalExpr1, GoalInfo1, Goal, GoalInfo2, Info3, Info4),
|
|
simplify_info_maybe_clear_structs(after, Goal - GoalInfo2,
|
|
Info4, Info5),
|
|
simplify__enforce_invariant(GoalInfo2, GoalInfo, Info5, Info).
|
|
|
|
:- pred simplify__enforce_invariant(hlds_goal_info, hlds_goal_info,
|
|
simplify_info, simplify_info).
|
|
:- mode simplify__enforce_invariant(in, out, in, out) is det.
|
|
%
|
|
% Ensure that the mode information and the determinism
|
|
% information say consistent things about unreachability.
|
|
%
|
|
simplify__enforce_invariant(GoalInfo0, GoalInfo, Info0, Info) :-
|
|
goal_info_get_determinism(GoalInfo0, Determinism0),
|
|
goal_info_get_instmap_delta(GoalInfo0, DeltaInstmap0),
|
|
determinism_components(Determinism0, CanFail0, NumSolns0),
|
|
(
|
|
NumSolns0 = at_most_zero,
|
|
instmap_delta_is_reachable(DeltaInstmap0)
|
|
->
|
|
instmap_delta_init_unreachable(UnreachableInstMapDelta),
|
|
goal_info_set_instmap_delta(GoalInfo0, UnreachableInstMapDelta,
|
|
GoalInfo),
|
|
simplify_info_set_rerun_det(Info0, Info)
|
|
;
|
|
instmap_delta_is_unreachable(DeltaInstmap0),
|
|
NumSolns0 \= at_most_zero
|
|
->
|
|
determinism_components(Determinism, CanFail0, at_most_zero),
|
|
goal_info_set_determinism(GoalInfo0, Determinism, GoalInfo),
|
|
simplify_info_set_rerun_det(Info0, Info)
|
|
;
|
|
GoalInfo = GoalInfo0,
|
|
Info = Info0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred simplify__goal_2(hlds_goal_expr, hlds_goal_info, hlds_goal_expr,
|
|
hlds_goal_info, simplify_info, simplify_info).
|
|
:- mode simplify__goal_2(in, in, out, out, in, out) is det.
|
|
|
|
simplify__goal_2(conj(Goals0), GoalInfo0, Goal, GoalInfo, Info0, Info) :-
|
|
simplify_info_get_instmap(Info0, InstMap0),
|
|
simplify__conj(Goals0, [], Goals, GoalInfo0, Info0, Info1),
|
|
simplify_info_set_instmap(Info1, InstMap0, Info2),
|
|
( Goals = [] ->
|
|
goal_info_get_context(GoalInfo0, Context),
|
|
true_goal(Context, Goal - GoalInfo),
|
|
Info = Info2
|
|
; Goals = [SingleGoal - SingleGoalInfo] ->
|
|
% a singleton conjunction is equivalent to the goal itself
|
|
simplify__maybe_wrap_goal(GoalInfo0, SingleGoalInfo,
|
|
SingleGoal, Goal, GoalInfo, Info2, Info)
|
|
;
|
|
%
|
|
% Conjunctions that cannot produce solutions may nevertheless
|
|
% contain nondet and multidet goals. If this happens, the
|
|
% conjunction is put inside a `some' to appease the code
|
|
% generator.
|
|
%
|
|
Info = Info2,
|
|
goal_info_get_determinism(GoalInfo0, Detism),
|
|
(
|
|
simplify_do_once(Info),
|
|
determinism_components(Detism, CanFail, at_most_zero),
|
|
simplify__contains_multisoln_goal(Goals)
|
|
->
|
|
determinism_components(InnerDetism,
|
|
CanFail, at_most_many),
|
|
goal_info_set_determinism(GoalInfo0,
|
|
InnerDetism, InnerInfo),
|
|
InnerGoal = conj(Goals) - InnerInfo,
|
|
Goal = some([], InnerGoal)
|
|
;
|
|
Goal = conj(Goals)
|
|
),
|
|
GoalInfo = GoalInfo0
|
|
).
|
|
|
|
simplify__goal_2(par_conj(Goals0, SM), GoalInfo0, Goal,
|
|
GoalInfo, Info0, Info) :-
|
|
(
|
|
Goals0 = []
|
|
->
|
|
goal_info_get_context(GoalInfo0, Context),
|
|
true_goal(Context, Goal - GoalInfo),
|
|
Info = Info0
|
|
;
|
|
Goals0 = [SingleGoal0]
|
|
->
|
|
simplify__goal(SingleGoal0, SingleGoal - SingleGoalInfo,
|
|
Info0, Info1),
|
|
simplify__maybe_wrap_goal(GoalInfo0, SingleGoalInfo,
|
|
SingleGoal, Goal, GoalInfo, Info1, Info)
|
|
;
|
|
GoalInfo = GoalInfo0,
|
|
simplify__par_conj(Goals0, Goals, Info0, Info0, Info),
|
|
Goal = par_conj(Goals, SM)
|
|
).
|
|
|
|
simplify__goal_2(disj(Disjuncts0, SM), GoalInfo0,
|
|
Goal, GoalInfo, Info0, Info) :-
|
|
simplify_info_get_instmap(Info0, InstMap0),
|
|
simplify__disj(Disjuncts0, [], Disjuncts, [], InstMaps,
|
|
Info0, Info0, Info1),
|
|
( Disjuncts = [] ->
|
|
goal_info_get_context(GoalInfo0, Context),
|
|
fail_goal(Context, Goal - GoalInfo),
|
|
Info = Info1
|
|
; Disjuncts = [SingleGoal] ->
|
|
% a singleton disjunction is equivalent to the goal itself
|
|
SingleGoal = Goal1 - GoalInfo1,
|
|
simplify__maybe_wrap_goal(GoalInfo0, GoalInfo1,
|
|
Goal1, Goal, GoalInfo, Info1, Info)
|
|
;
|
|
Goal = disj(Disjuncts, SM),
|
|
simplify_info_get_module_info(Info1, ModuleInfo1),
|
|
goal_info_get_nonlocals(GoalInfo0, NonLocals),
|
|
merge_instmap_deltas(InstMap0, NonLocals, InstMaps,
|
|
NewDelta, ModuleInfo1, ModuleInfo2),
|
|
simplify_info_set_module_info(Info1, ModuleInfo2, Info),
|
|
goal_info_set_instmap_delta(GoalInfo0, NewDelta, GoalInfo)
|
|
).
|
|
|
|
simplify__goal_2(switch(Var, SwitchCanFail0, Cases0, SM),
|
|
GoalInfo0, Goal, GoalInfo, Info0, Info) :-
|
|
simplify_info_get_instmap(Info0, InstMap0),
|
|
simplify_info_get_module_info(Info0, ModuleInfo0),
|
|
instmap__lookup_var(InstMap0, Var, VarInst),
|
|
( inst_is_bound_to_functors(ModuleInfo0, VarInst, Functors) ->
|
|
functors_to_cons_ids(Functors, ConsIds0),
|
|
list__sort(ConsIds0, ConsIds),
|
|
delete_unreachable_cases(Cases0, ConsIds, Cases1),
|
|
MaybeConsIds = yes(ConsIds)
|
|
;
|
|
Cases1 = Cases0,
|
|
MaybeConsIds = no
|
|
),
|
|
simplify__switch(Var, Cases1, [], Cases, [], InstMaps,
|
|
SwitchCanFail0, SwitchCanFail, Info0, Info0, Info1),
|
|
( Cases = [] ->
|
|
% An empty switch always fails.
|
|
pd_cost__eliminate_switch(CostDelta),
|
|
simplify_info_incr_cost_delta(Info1, CostDelta, Info),
|
|
goal_info_get_context(GoalInfo0, Context),
|
|
fail_goal(Context, Goal - GoalInfo)
|
|
; Cases = [case(ConsId, SingleGoal)] ->
|
|
% a singleton switch is equivalent to the goal itself with
|
|
% a possibly can_fail unification with the functor on the front.
|
|
cons_id_arity(ConsId, Arity),
|
|
(
|
|
SwitchCanFail = can_fail,
|
|
MaybeConsIds \= yes([ConsId])
|
|
->
|
|
simplify__create_test_unification(Var, ConsId, Arity,
|
|
UnifyGoal, Info1, Info2),
|
|
|
|
% Conjoin the test and the rest of the case.
|
|
goal_to_conj_list(SingleGoal, SingleGoalConj),
|
|
GoalList = [UnifyGoal | SingleGoalConj],
|
|
|
|
% Work out the nonlocals, instmap_delta
|
|
% and determinism of the entire conjunction.
|
|
goal_info_get_nonlocals(GoalInfo0, NonLocals0),
|
|
set__insert(NonLocals0, Var, NonLocals),
|
|
goal_info_get_instmap_delta(GoalInfo0, InstMapDelta0),
|
|
simplify_info_get_instmap(Info2, InstMap),
|
|
instmap_delta_bind_var_to_functor(Var, ConsId,
|
|
InstMap, InstMapDelta0, InstMapDelta,
|
|
ModuleInfo0, ModuleInfo),
|
|
simplify_info_set_module_info(Info2,
|
|
ModuleInfo, Info3),
|
|
goal_info_get_determinism(GoalInfo0, CaseDetism),
|
|
det_conjunction_detism(semidet, CaseDetism, Detism),
|
|
goal_info_init(NonLocals, InstMapDelta, Detism,
|
|
CombinedGoalInfo),
|
|
|
|
simplify_info_set_requantify(Info3, Info4),
|
|
Goal = conj(GoalList),
|
|
GoalInfo = CombinedGoalInfo
|
|
;
|
|
% The var can only be bound to this cons_id, so
|
|
% a test is unnecessary.
|
|
SingleGoal = Goal - GoalInfo,
|
|
Info4 = Info1
|
|
),
|
|
pd_cost__eliminate_switch(CostDelta),
|
|
simplify_info_incr_cost_delta(Info4, CostDelta, Info)
|
|
;
|
|
Goal = switch(Var, SwitchCanFail, Cases, SM),
|
|
simplify_info_get_module_info(Info1, ModuleInfo1),
|
|
goal_info_get_nonlocals(GoalInfo0, NonLocals),
|
|
merge_instmap_deltas(InstMap0, NonLocals, InstMaps,
|
|
NewDelta, ModuleInfo1, ModuleInfo2),
|
|
simplify_info_set_module_info(Info1, ModuleInfo2, Info),
|
|
goal_info_set_instmap_delta(GoalInfo0, NewDelta, GoalInfo)
|
|
).
|
|
|
|
simplify__goal_2(Goal0, GoalInfo, Goal, GoalInfo, Info0, Info) :-
|
|
Goal0 = higher_order_call(Closure, Args, _, Modes, Det, _PredOrFunc),
|
|
( simplify_do_calls(Info0) ->
|
|
common__optimise_higher_order_call(Closure, Args, Modes, Det,
|
|
Goal0, GoalInfo, Goal, Info0, Info)
|
|
; simplify_do_warn_calls(Info0) ->
|
|
% we need to do the pass, for the warnings, but we ignore
|
|
% the optimized goal and instead use the original one
|
|
common__optimise_higher_order_call(Closure, Args, Modes, Det,
|
|
Goal0, GoalInfo, _Goal1, Info0, Info),
|
|
Goal = Goal0
|
|
;
|
|
Goal = Goal0,
|
|
Info = Info0
|
|
).
|
|
|
|
% XXX We ought to do duplicate call elimination for class
|
|
% XXX method calls here.
|
|
simplify__goal_2(Goal, GoalInfo, Goal, GoalInfo, Info, Info) :-
|
|
Goal = class_method_call(_, _, _, _, _, _).
|
|
|
|
simplify__goal_2(Goal0, GoalInfo0, Goal, GoalInfo, Info0, Info) :-
|
|
Goal0 = call(PredId, ProcId, Args, IsBuiltin, _, _),
|
|
simplify_info_get_module_info(Info0, ModuleInfo),
|
|
module_info_pred_proc_info(ModuleInfo, PredId, ProcId, PredInfo,
|
|
ProcInfo),
|
|
|
|
%
|
|
% check for calls to predicates with `pragma obsolete' declarations
|
|
%
|
|
(
|
|
simplify_do_warn(Info0),
|
|
pred_info_get_markers(PredInfo, Markers),
|
|
check_marker(Markers, obsolete),
|
|
%
|
|
% Don't warn about directly recursive calls.
|
|
% (That would cause spurious warnings, particularly
|
|
% with builtin predicates, or preds defined using
|
|
% pragma c_code.)
|
|
%
|
|
simplify_info_get_det_info(Info0, DetInfo0),
|
|
det_info_get_pred_id(DetInfo0, ThisPredId),
|
|
PredId \= ThisPredId
|
|
->
|
|
|
|
goal_info_get_context(GoalInfo0, Context1),
|
|
simplify_info_add_msg(Info0, warn_obsolete(PredId, Context1),
|
|
Info1)
|
|
;
|
|
Info1 = Info0
|
|
),
|
|
|
|
%
|
|
% Check for recursive calls with the same input arguments,
|
|
% and warn about them (since they will lead to infinite loops).
|
|
%
|
|
(
|
|
simplify_do_warn(Info1),
|
|
|
|
%
|
|
% Is this a (directly) recursive call,
|
|
% i.e. is the procedure being called the same as the
|
|
% procedure we're analyzing?
|
|
%
|
|
simplify_info_get_det_info(Info1, DetInfo),
|
|
det_info_get_pred_id(DetInfo, PredId),
|
|
det_info_get_proc_id(DetInfo, ProcId),
|
|
|
|
%
|
|
% Don't count inline builtins.
|
|
% (The compiler generates code for builtins that looks
|
|
% recursive, so that you can take their address, but since
|
|
% the recursive call actually expands into inline
|
|
% instructions, so it's not infinite recursion.)
|
|
%
|
|
IsBuiltin \= inline_builtin,
|
|
|
|
%
|
|
% Don't warn if we're inside a lambda goal, because the
|
|
% recursive call may not be executed.
|
|
%
|
|
\+ simplify_info_inside_lambda(Info1),
|
|
|
|
%
|
|
% Are the input arguments the same (or equivalent)?
|
|
%
|
|
simplify_info_get_module_info(Info1, ModuleInfo1),
|
|
module_info_pred_proc_info(ModuleInfo1, PredId, ProcId,
|
|
_PredInfo1, ProcInfo1),
|
|
proc_info_headvars(ProcInfo1, HeadVars),
|
|
proc_info_argmodes(ProcInfo1, ArgModes),
|
|
simplify_info_get_common_info(Info1, CommonInfo1),
|
|
simplify__input_args_are_equiv(Args, HeadVars, ArgModes,
|
|
CommonInfo1, ModuleInfo1),
|
|
|
|
%
|
|
% Don't count procs using minimal evaluation as they
|
|
% should always terminate if they have a finite number
|
|
% of answers.
|
|
%
|
|
\+ proc_info_eval_method(ProcInfo, eval_minimal)
|
|
->
|
|
goal_info_get_context(GoalInfo0, Context2),
|
|
simplify_info_add_msg(Info1, warn_infinite_recursion(Context2),
|
|
Info2)
|
|
;
|
|
Info2 = Info1
|
|
),
|
|
|
|
%
|
|
% check for duplicate calls to the same procedure
|
|
%
|
|
( simplify_do_calls(Info2),
|
|
goal_info_is_pure(GoalInfo0)
|
|
->
|
|
common__optimise_call(PredId, ProcId, Args, Goal0, GoalInfo0,
|
|
Goal1, Info2, Info3)
|
|
; simplify_do_warn_calls(Info0),
|
|
goal_info_is_pure(GoalInfo0)
|
|
->
|
|
% we need to do the pass, for the warnings, but we ignore
|
|
% the optimized goal and instead use the original one
|
|
common__optimise_call(PredId, ProcId, Args, Goal0, GoalInfo0,
|
|
_Goal1, Info2, Info3),
|
|
Goal1 = Goal0
|
|
;
|
|
Goal1 = Goal0,
|
|
Info3 = Info2
|
|
),
|
|
|
|
%
|
|
% Try to evaluate the call at compile-time.
|
|
%
|
|
|
|
( simplify_do_const_prop(Info3) ->
|
|
simplify_info_get_instmap(Info3, Instmap0),
|
|
simplify_info_get_module_info(Info3, ModuleInfo2),
|
|
(
|
|
Goal1 = call(_, _, _, _, _, _),
|
|
evaluate_builtin(PredId, ProcId, Args, GoalInfo0,
|
|
Goal2, GoalInfo2, Instmap0,
|
|
ModuleInfo2, ModuleInfo3)
|
|
->
|
|
Goal = Goal2,
|
|
GoalInfo = GoalInfo2,
|
|
simplify_info_set_module_info(Info3, ModuleInfo3, Info4),
|
|
simplify_info_set_requantify(Info4, Info)
|
|
;
|
|
Goal = Goal1,
|
|
GoalInfo = GoalInfo0,
|
|
Info = Info3
|
|
)
|
|
;
|
|
Goal = Goal1,
|
|
GoalInfo = GoalInfo0,
|
|
Info = Info3
|
|
).
|
|
|
|
simplify__goal_2(Goal0, GoalInfo0, Goal, GoalInfo, Info0, Info) :-
|
|
Goal0 = unify(LT0, RT0, M, U0, C),
|
|
(
|
|
% A unification of the form X = X can safely be
|
|
% optimised away.
|
|
|
|
RT0 = var(LT0)
|
|
->
|
|
goal_info_get_context(GoalInfo0, Context),
|
|
true_goal(Context, Goal - GoalInfo),
|
|
Info = Info0
|
|
;
|
|
RT0 = lambda_goal(PredOrFunc, NonLocals, Vars,
|
|
Modes, LambdaDeclaredDet, LambdaGoal0)
|
|
->
|
|
simplify_info_enter_lambda(Info0, Info1),
|
|
simplify_info_get_common_info(Info1, Common1),
|
|
simplify_info_get_module_info(Info1, ModuleInfo),
|
|
simplify_info_get_instmap(Info1, InstMap1),
|
|
instmap__pre_lambda_update(ModuleInfo, Vars, Modes,
|
|
InstMap1, InstMap2),
|
|
simplify_info_set_instmap(Info1, InstMap2, Info2),
|
|
|
|
% Don't attempt to pass structs into lambda_goals,
|
|
% since that could change the curried non-locals of the
|
|
% lambda_goal, and that would be difficult to fix up.
|
|
common_info_init(Common2),
|
|
simplify_info_set_common_info(Info2, Common2, Info3),
|
|
|
|
% Don't attempt to pass structs out of lambda_goals.
|
|
simplify__goal(LambdaGoal0, LambdaGoal, Info3, Info4),
|
|
simplify_info_set_common_info(Info4, Common1, Info5),
|
|
simplify_info_set_instmap(Info5, InstMap1, Info6),
|
|
RT = lambda_goal(PredOrFunc, NonLocals, Vars, Modes,
|
|
LambdaDeclaredDet, LambdaGoal),
|
|
simplify_info_leave_lambda(Info6, Info),
|
|
Goal = unify(LT0, RT, M, U0, C),
|
|
GoalInfo = GoalInfo0
|
|
;
|
|
simplify_do_common(Info0)
|
|
->
|
|
common__optimise_unification(U0, LT0, RT0, M, C,
|
|
Goal0, GoalInfo0, Goal, GoalInfo, Info0, Info)
|
|
;
|
|
( simplify_do_calls(Info0)
|
|
; simplify_do_warn_calls(Info0)
|
|
)
|
|
->
|
|
% We need to do the pass, to record the variable
|
|
% equivalences used for optimizing or warning about
|
|
% duplicate calls. But we don't want to perform
|
|
% the optimization, so we disregard the optimized goal
|
|
% and instead use the original one.
|
|
common__optimise_unification(U0, LT0, RT0, M, C,
|
|
Goal0, GoalInfo0, _Goal1, _GoalInfo1, Info0, Info),
|
|
Goal = Goal0,
|
|
GoalInfo = GoalInfo0
|
|
;
|
|
Goal = Goal0,
|
|
GoalInfo = GoalInfo0,
|
|
Info = Info0
|
|
).
|
|
|
|
% (A -> B ; C) is logically equivalent to (A, B ; ~A, C).
|
|
% If the determinism of A means that one of these disjuncts
|
|
% cannot succeed, then we replace the if-then-else with the
|
|
% other disjunct. (We could also eliminate A, but we leave
|
|
% that to the recursive invocations.)
|
|
%
|
|
% The conjunction operator in the remaining disjunct ought to be
|
|
% a sequential conjunction, because Mercury's if-then-else always
|
|
% guarantees sequentiality, whereas conjunction only guarantees
|
|
% sequentiality if the --no-reorder-conj option is enabled.
|
|
%
|
|
% However, currently reordering is only done in mode analysis,
|
|
% not in the code generator, so we don't yet need a sequential
|
|
% conjunction construct. This will change when constraint pushing
|
|
% is finished, or when we start doing coroutining.
|
|
|
|
simplify__goal_2(if_then_else(Vars, Cond0, Then0, Else0, SM),
|
|
GoalInfo0, Goal, GoalInfo, Info0, Info) :-
|
|
Cond0 = _ - CondInfo0,
|
|
|
|
goal_info_get_determinism(CondInfo0, CondDetism0),
|
|
determinism_components(CondDetism0, CondCanFail0, CondSolns0),
|
|
( CondCanFail0 = cannot_fail ->
|
|
goal_to_conj_list(Cond0, CondList),
|
|
goal_to_conj_list(Then0, ThenList),
|
|
list__append(CondList, ThenList, List),
|
|
simplify__goal(conj(List) - GoalInfo0, Goal - GoalInfo,
|
|
Info0, Info1),
|
|
goal_info_get_context(GoalInfo0, Context),
|
|
simplify_info_add_msg(Info1, ite_cond_cannot_fail(Context),
|
|
Info)
|
|
; CondSolns0 = at_most_zero ->
|
|
% Optimize away the condition and the `then' part.
|
|
det_negation_det(CondDetism0, MaybeNegDetism),
|
|
( Cond0 = not(NegCond) - _ ->
|
|
Cond = NegCond
|
|
;
|
|
(
|
|
MaybeNegDetism = yes(NegDetism1),
|
|
(
|
|
NegDetism1 = erroneous,
|
|
instmap_delta_init_unreachable(
|
|
NegInstMapDelta1)
|
|
;
|
|
NegDetism1 = det,
|
|
instmap_delta_init_reachable(
|
|
NegInstMapDelta1)
|
|
)
|
|
->
|
|
NegDetism = NegDetism1,
|
|
NegInstMapDelta = NegInstMapDelta1
|
|
;
|
|
error("simplify__goal_2: cannot get negated determinism")
|
|
),
|
|
goal_info_set_determinism(CondInfo0,
|
|
NegDetism, NegCondInfo0),
|
|
goal_info_set_instmap_delta(NegCondInfo0,
|
|
NegInstMapDelta, NegCondInfo),
|
|
Cond = not(Cond0) - NegCondInfo
|
|
),
|
|
goal_to_conj_list(Else0, ElseList),
|
|
List = [Cond | ElseList],
|
|
simplify__goal(conj(List) - GoalInfo0, Goal - GoalInfo,
|
|
Info0, Info1),
|
|
goal_info_get_context(GoalInfo0, Context),
|
|
simplify_info_add_msg(Info1, ite_cond_cannot_succeed(Context),
|
|
Info)
|
|
; Else0 = disj([], _) - _ ->
|
|
% (A -> C ; fail) is equivalent to (A, C)
|
|
goal_to_conj_list(Cond0, CondList),
|
|
goal_to_conj_list(Then0, ThenList),
|
|
list__append(CondList, ThenList, List),
|
|
simplify__goal(conj(List) - GoalInfo0, Goal - GoalInfo,
|
|
Info0, Info)
|
|
;
|
|
simplify_info_get_instmap(Info0, InstMap0),
|
|
simplify__goal(Cond0, Cond, Info0, Info1),
|
|
simplify_info_update_instmap(Info1, Cond, Info2),
|
|
simplify__goal(Then0, Then, Info2, Info3),
|
|
simplify_info_post_branch_update(Info0, Info3, Info4),
|
|
simplify__goal(Else0, Else, Info4, Info5),
|
|
simplify_info_post_branch_update(Info0, Info5, Info6),
|
|
Cond = _ - CondInfo,
|
|
goal_info_get_instmap_delta(CondInfo, CondDelta),
|
|
Then = _ - ThenInfo,
|
|
goal_info_get_instmap_delta(ThenInfo, ThenDelta),
|
|
instmap_delta_apply_instmap_delta(CondDelta, ThenDelta,
|
|
CondThenDelta),
|
|
Else = _ - ElseInfo,
|
|
goal_info_get_instmap_delta(ElseInfo, ElseDelta),
|
|
goal_info_get_nonlocals(GoalInfo0, NonLocals),
|
|
simplify_info_get_module_info(Info6, ModuleInfo0),
|
|
merge_instmap_deltas(InstMap0, NonLocals,
|
|
[CondThenDelta, ElseDelta], NewDelta,
|
|
ModuleInfo0, ModuleInfo1),
|
|
simplify_info_set_module_info(Info6, ModuleInfo1, Info),
|
|
goal_info_set_instmap_delta(GoalInfo0, NewDelta, GoalInfo1),
|
|
IfThenElse = if_then_else(Vars, Cond, Then, Else, SM),
|
|
%
|
|
% If-then-elses that are det or semidet may nevertheless
|
|
% contain nondet or multidet conditions. If this happens, the
|
|
% if-then-else must be put inside a `some' to appease the code
|
|
% generator.
|
|
%
|
|
goal_info_get_determinism(GoalInfo0, IfThenElseDetism0),
|
|
determinism_components(IfThenElseDetism0, IfThenElseCanFail,
|
|
IfThenElseNumSolns),
|
|
(
|
|
simplify_do_once(Info),
|
|
goal_info_get_determinism(CondInfo, CondDetism),
|
|
determinism_components(CondDetism, _, at_most_many),
|
|
IfThenElseNumSolns \= at_most_many
|
|
->
|
|
determinism_components(InnerDetism, IfThenElseCanFail,
|
|
at_most_many),
|
|
goal_info_set_determinism(GoalInfo1, InnerDetism,
|
|
InnerInfo),
|
|
Goal = some([], IfThenElse - InnerInfo)
|
|
;
|
|
Goal = IfThenElse
|
|
),
|
|
GoalInfo = GoalInfo1
|
|
).
|
|
|
|
simplify__goal_2(not(Goal0), GoalInfo0, Goal, GoalInfo, Info0, Info) :-
|
|
% Can't use calls or unifications seen within a negation,
|
|
% since non-local variables may not be bound within the negation.
|
|
simplify_info_get_common_info(Info0, Common),
|
|
simplify__goal(Goal0, Goal1, Info0, Info1),
|
|
simplify_info_set_common_info(Info1, Common, Info2),
|
|
Goal1 = _ - GoalInfo1,
|
|
goal_info_get_determinism(GoalInfo1, Detism),
|
|
determinism_components(Detism, CanFail, MaxSoln),
|
|
goal_info_get_context(GoalInfo0, Context),
|
|
( CanFail = cannot_fail ->
|
|
simplify_info_add_msg(Info2,
|
|
negated_goal_cannot_fail(Context), Info3)
|
|
; MaxSoln = at_most_zero ->
|
|
simplify_info_add_msg(Info2,
|
|
negated_goal_cannot_succeed(Context), Info3)
|
|
;
|
|
Info3 = Info2
|
|
),
|
|
(
|
|
% replace `not true' with `fail'
|
|
Goal1 = conj([]) - _GoalInfo
|
|
->
|
|
fail_goal(Context, Goal - GoalInfo),
|
|
Info = Info3
|
|
;
|
|
% replace `not fail' with `true'
|
|
Goal1 = disj([], _) - _GoalInfo2
|
|
->
|
|
true_goal(Context, Goal - GoalInfo),
|
|
Info = Info3
|
|
;
|
|
% remove double negation
|
|
Goal1 = not(SubGoal - SubGoalInfo) - _
|
|
->
|
|
simplify__maybe_wrap_goal(GoalInfo0, SubGoalInfo, SubGoal,
|
|
Goal, GoalInfo, Info3, Info)
|
|
;
|
|
Goal = not(Goal1),
|
|
GoalInfo = GoalInfo0,
|
|
Info = Info3
|
|
).
|
|
|
|
simplify__goal_2(some(Vars1, Goal1), SomeInfo, Goal, GoalInfo, Info0, Info) :-
|
|
simplify__goal(Goal1, Goal2, Info0, Info),
|
|
simplify__nested_somes(Vars1, Goal2, Vars, Goal3),
|
|
Goal3 = GoalExpr3 - GoalInfo3,
|
|
(
|
|
goal_info_get_determinism(GoalInfo3, Detism),
|
|
goal_info_get_determinism(SomeInfo, Detism)
|
|
->
|
|
% If the inner and outer detisms match the `some'
|
|
% is unnecessary.
|
|
Goal = GoalExpr3,
|
|
GoalInfo = GoalInfo3
|
|
;
|
|
Goal = some(Vars, Goal3),
|
|
GoalInfo = SomeInfo
|
|
).
|
|
|
|
simplify__goal_2(Goal0, GoalInfo, Goal, GoalInfo, Info0, Info) :-
|
|
Goal0 = pragma_c_code(_, PredId, ProcId, Args, _, _, _),
|
|
(
|
|
simplify_do_calls(Info0),
|
|
goal_info_is_pure(GoalInfo)
|
|
->
|
|
common__optimise_call(PredId, ProcId, Args, Goal0,
|
|
GoalInfo, Goal, Info0, Info)
|
|
;
|
|
Info = Info0,
|
|
Goal = Goal0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% simplify__input_args_are_equiv(Args, HeadVars, Modes,
|
|
% CommonInfo, ModuleInfo1):
|
|
% Succeeds if all the input arguments (determined by looking at
|
|
% `Modes') in `Args' are equivalent (according to the equivalence
|
|
% class specified by `CommonInfo') to the corresponding variables
|
|
% in HeadVars. HeadVars, Modes, and Args should all be lists of
|
|
% the same length.
|
|
|
|
:- pred simplify__input_args_are_equiv(list(var), list(var), list(mode),
|
|
common_info, module_info).
|
|
:- mode simplify__input_args_are_equiv(in, in, in, in, in) is semidet.
|
|
|
|
simplify__input_args_are_equiv([], [], _, _, _).
|
|
simplify__input_args_are_equiv([Arg|Args], [HeadVar|HeadVars], [Mode|Modes],
|
|
CommonInfo, ModuleInfo1) :-
|
|
( mode_is_input(ModuleInfo1, Mode) ->
|
|
common__vars_are_equivalent(Arg, HeadVar, CommonInfo)
|
|
;
|
|
true
|
|
),
|
|
simplify__input_args_are_equiv(Args, HeadVars, Modes,
|
|
CommonInfo, ModuleInfo1).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% replace nested `some's with a single `some',
|
|
:- pred simplify__nested_somes(list(var)::in, hlds_goal::in,
|
|
list(var)::out, hlds_goal::out) is det.
|
|
|
|
simplify__nested_somes(Vars0, Goal0, Vars, Goal) :-
|
|
( Goal0 = some(Vars1, Goal1) - _ ->
|
|
list__append(Vars0, Vars1, Vars2),
|
|
simplify__nested_somes(Vars2, Goal1, Vars, Goal)
|
|
;
|
|
Vars = Vars0,
|
|
Goal = Goal0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% When removing a level of wrapping around a goal,
|
|
% if the determinisms are not the same, we really
|
|
% need to rerun determinism analysis on the
|
|
% procedure. I think this is a similar situation
|
|
% to inlining of erroneous goals. The safe thing
|
|
% to do is to wrap a `some' around the inner goal if
|
|
% the inner and outer determinisms are not the same.
|
|
% It probably won't happen that often.
|
|
:- pred simplify__maybe_wrap_goal(hlds_goal_info::in, hlds_goal_info::in,
|
|
hlds_goal_expr::in, hlds_goal_expr::out, hlds_goal_info::out,
|
|
simplify_info::in, simplify_info::out) is det.
|
|
|
|
simplify__maybe_wrap_goal(OuterGoalInfo, InnerGoalInfo,
|
|
Goal1, Goal, GoalInfo, Info0, Info) :-
|
|
(
|
|
goal_info_get_determinism(InnerGoalInfo, Det),
|
|
goal_info_get_determinism(OuterGoalInfo, Det)
|
|
->
|
|
Goal = Goal1,
|
|
GoalInfo = InnerGoalInfo,
|
|
Info = Info0
|
|
;
|
|
Goal = some([], Goal1 - InnerGoalInfo),
|
|
GoalInfo = OuterGoalInfo,
|
|
simplify_info_set_rerun_det(Info0, Info)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred simplify__conj(list(hlds_goal), list(hlds_goal),
|
|
list(hlds_goal), hlds_goal_info,
|
|
simplify_info, simplify_info).
|
|
:- mode simplify__conj(in, in, out, in, in, out) is det.
|
|
|
|
simplify__conj([], RevGoals, Goals, _, Info, Info) :-
|
|
list__reverse(RevGoals, Goals).
|
|
simplify__conj([Goal0 | Goals0], RevGoals0, Goals, ConjInfo, Info0, Info) :-
|
|
% Flatten conjunctions.
|
|
( Goal0 = conj(SubGoals) - _ ->
|
|
list__append(SubGoals, Goals0, Goals1),
|
|
simplify__conj(Goals1, RevGoals0, Goals, ConjInfo, Info0, Info)
|
|
;
|
|
simplify__goal(Goal0, Goal1, Info0, Info1),
|
|
(
|
|
% Flatten conjunctions.
|
|
Goal1 = conj(SubGoals1) - _
|
|
->
|
|
simplify_info_undo_goal_updates(Info0, Info1, Info2),
|
|
list__append(SubGoals1, Goals0, Goals1),
|
|
simplify__conj(Goals1, RevGoals0, Goals, ConjInfo, Info2, Info)
|
|
;
|
|
% Delete unreachable goals.
|
|
(
|
|
simplify_info_get_instmap(Info1, InstMap1),
|
|
instmap__is_unreachable(InstMap1)
|
|
;
|
|
Goal1 = _ - GoalInfo1,
|
|
goal_info_get_determinism(GoalInfo1, Detism1),
|
|
determinism_components(Detism1, _, at_most_zero)
|
|
)
|
|
->
|
|
Info = Info1,
|
|
simplify__conjoin_goal_and_rev_goal_list(Goal1,
|
|
RevGoals0, RevGoals1),
|
|
|
|
( (Goal1 = disj([], _) - _ ; Goals0 = []) ->
|
|
RevGoals = RevGoals1
|
|
;
|
|
% We insert an explicit failure at the end
|
|
% of the non-succeeding conjunction. This
|
|
% is necessary, since the unreachability of
|
|
% the instmap could have been derived using
|
|
% inferred determinism information. Without the
|
|
% explicit fail goal, mode errors could result if mode
|
|
% analysis is rerun, since according to the language
|
|
% specification, mode analysis does not use inferred
|
|
% determinism information when deciding what can
|
|
% never succeed.
|
|
Goal0 = _ - GoalInfo0,
|
|
goal_info_get_context(GoalInfo0, Context),
|
|
fail_goal(Context, Fail),
|
|
simplify__conjoin_goal_and_rev_goal_list(Fail,
|
|
RevGoals1, RevGoals)
|
|
),
|
|
list__reverse(RevGoals, Goals)
|
|
;
|
|
simplify__excess_assigns(Goal1, ConjInfo,
|
|
Goals0, Goals1, RevGoals0, RevGoals1,
|
|
GoalNeeded, Info1, Info2),
|
|
( GoalNeeded = yes ->
|
|
simplify__conjoin_goal_and_rev_goal_list(Goal1,
|
|
RevGoals1, RevGoals2)
|
|
;
|
|
RevGoals2 = RevGoals1
|
|
),
|
|
simplify_info_update_instmap(Info2, Goal1, Info3),
|
|
simplify__conj(Goals1, RevGoals2, Goals,
|
|
ConjInfo, Info3, Info)
|
|
)
|
|
).
|
|
|
|
:- pred simplify__conjoin_goal_and_rev_goal_list(hlds_goal::in,
|
|
hlds_goals::in, hlds_goals::out) is det.
|
|
|
|
simplify__conjoin_goal_and_rev_goal_list(Goal, RevGoals0, RevGoals) :-
|
|
( Goal = conj(Goals) - _ ->
|
|
list__reverse(Goals, Goals1),
|
|
list__append(Goals1, RevGoals0, RevGoals)
|
|
;
|
|
RevGoals = [Goal | RevGoals0]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred simplify__par_conj(list(hlds_goal), list(hlds_goal),
|
|
simplify_info, simplify_info, simplify_info).
|
|
:- mode simplify__par_conj(in, out, in, in, out) is det.
|
|
|
|
simplify__par_conj([], [], _, Info, Info).
|
|
simplify__par_conj([Goal0 |Goals0], [Goal | Goals], Info0, Info1, Info) :-
|
|
simplify__goal(Goal0, Goal, Info1, Info2),
|
|
simplify_info_post_branch_update(Info0, Info2, Info3),
|
|
simplify__par_conj(Goals0, Goals, Info0, Info3, Info).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred simplify__excess_assigns(hlds_goal::in, hlds_goal_info::in,
|
|
hlds_goals::in, hlds_goals::out,
|
|
hlds_goals::in, hlds_goals::out, bool::out,
|
|
simplify_info::in, simplify_info::out) is det.
|
|
|
|
simplify__excess_assigns(Goal0, ConjInfo, Goals0, Goals,
|
|
RevGoals0, RevGoals, GoalNeeded, Info0, Info) :-
|
|
(
|
|
simplify_do_excess_assigns(Info0),
|
|
Goal0 = unify(_, _, _, Unif, _) - _,
|
|
goal_info_get_nonlocals(ConjInfo, NonLocals),
|
|
Unif = assign(LeftVar, RightVar),
|
|
( \+ set__member(LeftVar, NonLocals) ->
|
|
LocalVar = LeftVar, ReplacementVar = RightVar
|
|
; \+ set__member(RightVar, NonLocals) ->
|
|
LocalVar = RightVar, ReplacementVar = LeftVar
|
|
;
|
|
fail
|
|
)
|
|
->
|
|
GoalNeeded = no,
|
|
map__init(Subn0),
|
|
map__det_insert(Subn0, LocalVar, ReplacementVar, Subn),
|
|
goal_util__rename_vars_in_goals(Goals0, no,
|
|
Subn, Goals),
|
|
goal_util__rename_vars_in_goals(RevGoals0, no,
|
|
Subn, RevGoals),
|
|
simplify_info_get_varset(Info0, VarSet0),
|
|
varset__delete_var(VarSet0, LocalVar, VarSet),
|
|
simplify_info_set_varset(Info0, VarSet, Info)
|
|
;
|
|
GoalNeeded = yes,
|
|
Goals = Goals0,
|
|
RevGoals = RevGoals0,
|
|
Info = Info0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred simplify__switch(var, list(case), list(case), list(case),
|
|
list(instmap_delta), list(instmap_delta), can_fail, can_fail,
|
|
simplify_info, simplify_info, simplify_info).
|
|
:- mode simplify__switch(in, in, in, out, in, out, in, out,
|
|
in, in, out) is det.
|
|
|
|
simplify__switch(_, [], RevCases, Cases, InstMaps, InstMaps,
|
|
CanFail, CanFail, _, Info, Info) :-
|
|
list__reverse(RevCases, Cases).
|
|
simplify__switch(Var, [Case0 | Cases0], RevCases0, Cases, InstMaps0, InstMaps,
|
|
CanFail0, CanFail, Info0, Info1, Info) :-
|
|
simplify_info_get_instmap(Info0, InstMap0),
|
|
Case0 = case(ConsId, Goal0),
|
|
simplify_info_get_module_info(Info1, ModuleInfo0),
|
|
instmap__bind_var_to_functor(Var, ConsId,
|
|
InstMap0, InstMap1, ModuleInfo0, ModuleInfo1),
|
|
simplify_info_set_module_info(Info1, ModuleInfo1, Info2),
|
|
simplify_info_set_instmap(Info2, InstMap1, Info3),
|
|
simplify__goal(Goal0, Goal, Info3, Info4),
|
|
|
|
% Remove failing branches.
|
|
( Goal = disj([], _) - _ ->
|
|
RevCases = RevCases0,
|
|
InstMaps1 = InstMaps0,
|
|
CanFail1 = can_fail,
|
|
Info5 = Info4
|
|
;
|
|
Case = case(ConsId, Goal),
|
|
Goal = _ - GoalInfo,
|
|
|
|
%
|
|
% Make sure the switched on variable appears in the
|
|
% instmap delta. This avoids an abort in merge_instmap_delta
|
|
% if another branch further instantiates the switched-on
|
|
% variable. If the switched on variable does not appear in
|
|
% this branch's instmap_delta, the inst before the goal
|
|
% would be used, resulting in a mode error.
|
|
%
|
|
goal_info_get_instmap_delta(GoalInfo, InstMapDelta0),
|
|
simplify_info_get_module_info(Info4, ModuleInfo5),
|
|
instmap_delta_bind_var_to_functor(Var, ConsId,
|
|
InstMap0, InstMapDelta0, InstMapDelta,
|
|
ModuleInfo5, ModuleInfo),
|
|
simplify_info_set_module_info(Info4, ModuleInfo, Info5),
|
|
|
|
InstMaps1 = [InstMapDelta | InstMaps0],
|
|
RevCases = [Case | RevCases0],
|
|
CanFail1 = CanFail0
|
|
),
|
|
|
|
simplify_info_post_branch_update(Info0, Info5, Info6),
|
|
simplify__switch(Var, Cases0, RevCases, Cases, InstMaps1, InstMaps,
|
|
CanFail1, CanFail, Info0, Info6, Info).
|
|
|
|
% Create a semidet unification at the start of a singleton case
|
|
% in a can_fail switch.
|
|
:- pred simplify__create_test_unification(var::in, cons_id::in, int::in,
|
|
hlds_goal::out, simplify_info::in, simplify_info::out) is det.
|
|
|
|
simplify__create_test_unification(Var, ConsId, ConsArity,
|
|
ExtraGoal - ExtraGoalInfo, Info0, Info) :-
|
|
simplify_info_get_varset(Info0, VarSet0),
|
|
simplify_info_get_var_types(Info0, VarTypes0),
|
|
varset__new_vars(VarSet0, ConsArity, ArgVars, VarSet),
|
|
map__lookup(VarTypes0, Var, VarType),
|
|
simplify_info_get_module_info(Info0, ModuleInfo),
|
|
type_util__get_cons_id_arg_types(ModuleInfo,
|
|
VarType, ConsId, ArgTypes),
|
|
map__det_insert_from_corresponding_lists(VarTypes0, ArgVars,
|
|
ArgTypes, VarTypes),
|
|
simplify_info_set_varset(Info0, VarSet, Info1),
|
|
simplify_info_set_var_types(Info1, VarTypes, Info),
|
|
simplify_info_get_instmap(Info, InstMap),
|
|
instmap__lookup_var(InstMap, Var, Inst0),
|
|
(
|
|
inst_expand(ModuleInfo, Inst0, Inst1),
|
|
get_arg_insts(Inst1, ConsId, ConsArity, ArgInsts1)
|
|
->
|
|
ArgInsts = ArgInsts1
|
|
;
|
|
error("simplify__goal_2 - get_arg_insts failed")
|
|
),
|
|
InstToUniMode =
|
|
lambda([ArgInst::in, ArgUniMode::out] is det, (
|
|
ArgUniMode = ((ArgInst - free) -> (ArgInst - ArgInst))
|
|
)),
|
|
list__map(InstToUniMode, ArgInsts, UniModes),
|
|
UniMode = (Inst0 -> Inst0) - (Inst0 -> Inst0),
|
|
UnifyContext = unify_context(explicit, []),
|
|
Unification = deconstruct(Var, ConsId,
|
|
ArgVars, UniModes, can_fail),
|
|
ExtraGoal = unify(Var, functor(ConsId, ArgVars),
|
|
UniMode, Unification, UnifyContext),
|
|
set__singleton_set(NonLocals, Var),
|
|
|
|
% The test can't bind any variables, so the
|
|
% InstMapDelta should be empty.
|
|
instmap_delta_init_reachable(InstMapDelta),
|
|
goal_info_init(NonLocals, InstMapDelta, semidet, ExtraGoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred simplify__disj(list(hlds_goal), list(hlds_goal), list(hlds_goal),
|
|
list(instmap_delta), list(instmap_delta),
|
|
simplify_info, simplify_info, simplify_info).
|
|
:- mode simplify__disj(in, in, out, in, out, in, in, out) is det.
|
|
|
|
simplify__disj([], RevGoals, Goals, InstMaps, InstMaps, _, Info, Info) :-
|
|
list__reverse(RevGoals, Goals).
|
|
simplify__disj([Goal0 | Goals0], RevGoals0, Goals, PostBranchInstMaps0,
|
|
PostBranchInstMaps, Info0, Info1, Info) :-
|
|
simplify__goal(Goal0, Goal, Info1, Info2),
|
|
Goal = _ - GoalInfo,
|
|
|
|
(
|
|
% Don't prune or warn about impure disjuncts
|
|
% that can't succeed.
|
|
\+ goal_info_is_impure(GoalInfo),
|
|
goal_info_get_determinism(GoalInfo, Detism),
|
|
determinism_components(Detism, _CanFail, MaxSolns),
|
|
MaxSolns = at_most_zero
|
|
->
|
|
(
|
|
simplify_do_warn(Info2),
|
|
% Don't warn where the initial goal was fail,
|
|
% since that can result from mode analysis
|
|
% pruning away cases in a switch which cannot
|
|
% succeed due to sub-typing in the modes.
|
|
Goal0 \= disj([], _) - _
|
|
->
|
|
goal_info_get_context(GoalInfo, Context),
|
|
simplify_info_add_msg(Info2,
|
|
zero_soln_disjunct(Context), Info3)
|
|
;
|
|
Info3 = Info2
|
|
),
|
|
|
|
%
|
|
% Prune away non-succeeding disjuncts where possible.
|
|
%
|
|
|
|
(
|
|
(
|
|
Goal0 = disj([], _) - _
|
|
;
|
|
% Only remove disjuncts that might loop
|
|
% or call error/1 if --no-fully-strict.
|
|
simplify_info_get_det_info(Info3, DetInfo),
|
|
det_info_get_fully_strict(DetInfo, no)
|
|
)
|
|
->
|
|
RevGoals1 = RevGoals0,
|
|
PostBranchInstMaps1 = PostBranchInstMaps0
|
|
;
|
|
RevGoals1 = [Goal | RevGoals0],
|
|
goal_info_get_instmap_delta(GoalInfo, InstMapDelta),
|
|
PostBranchInstMaps1 =
|
|
[InstMapDelta | PostBranchInstMaps0]
|
|
)
|
|
;
|
|
Info3 = Info2,
|
|
RevGoals1 = [Goal | RevGoals0],
|
|
goal_info_get_instmap_delta(GoalInfo, InstMapDelta),
|
|
PostBranchInstMaps1 = [InstMapDelta | PostBranchInstMaps0]
|
|
),
|
|
|
|
simplify_info_post_branch_update(Info0, Info3, Info4),
|
|
simplify__disj(Goals0, RevGoals1, Goals, PostBranchInstMaps1,
|
|
PostBranchInstMaps, Info0, Info4, Info).
|
|
|
|
% Disjunctions that cannot succeed more than once when viewed from the
|
|
% outside generally need some fixing up, and/or some warnings to be
|
|
% issued.
|
|
|
|
% We previously converted them all to if-then-elses using the code
|
|
% below, however converting disjs that have output variables but
|
|
% that nevertheless cannot succeed more than one
|
|
% (e.g. cc_nondet or cc_multi disjs) into if-then-elses
|
|
% may cause problems with other parts of the compiler that
|
|
% assume that an if-then-else is mode-correct, i.e. that
|
|
% the condition doesn't bind variables.
|
|
|
|
/****
|
|
goal_info_get_determinism(GoalInfo, Detism),
|
|
determinism_components(Detism, _CanFail, MaxSoln),
|
|
MaxSoln \= at_most_many
|
|
->
|
|
goal_info_get_instmap_delta(GoalInfo, DeltaInstMap),
|
|
goal_info_get_nonlocals(GoalInfo, NonLocalVars),
|
|
(
|
|
det_no_output_vars(NonLocalVars, InstMap0,
|
|
DeltaInstMap, DetInfo)
|
|
->
|
|
OutputVars = no
|
|
;
|
|
OutputVars = yes
|
|
),
|
|
simplify__fixup_disj(Disjuncts, Detism, OutputVars,
|
|
GoalInfo, SM, InstMap0, DetInfo, Goal,
|
|
MsgsA, Msgs)
|
|
;
|
|
****/
|
|
|
|
:- pred simplify__fixup_disj(list(hlds_goal), determinism, bool,
|
|
hlds_goal_info, follow_vars, hlds_goal_expr,
|
|
simplify_info, simplify_info).
|
|
:- mode simplify__fixup_disj(in, in, in, in, in, out, in, out) is det.
|
|
|
|
simplify__fixup_disj(Disjuncts, _, _OutputVars, GoalInfo, SM,
|
|
Goal, Info0, Info) :-
|
|
det_disj_to_ite(Disjuncts, GoalInfo, SM, IfThenElse),
|
|
simplify__goal(IfThenElse, Simplified, Info0, Info),
|
|
Simplified = Goal - _.
|
|
|
|
% det_disj_to_ite is used to transform disjunctions that occur
|
|
% in prunable contexts into if-then-elses.
|
|
% For example, it would transform
|
|
%
|
|
% ( Disjunct1
|
|
% ; Disjunct2
|
|
% ; Disjunct3
|
|
% )
|
|
% into
|
|
% ( Disjunct1 ->
|
|
% true
|
|
% ; Disjunct2 ->
|
|
% true
|
|
% ;
|
|
% Disjunct3
|
|
% ).
|
|
|
|
:- pred det_disj_to_ite(list(hlds_goal), hlds_goal_info, follow_vars,
|
|
hlds_goal).
|
|
:- mode det_disj_to_ite(in, in, in, out) is det.
|
|
|
|
det_disj_to_ite([], _GoalInfo, _SM, _) :-
|
|
error("reached base case of det_disj_to_ite").
|
|
det_disj_to_ite([Disjunct | Disjuncts], GoalInfo, SM, Goal) :-
|
|
( Disjuncts = [] ->
|
|
Goal = Disjunct
|
|
;
|
|
Cond = Disjunct,
|
|
Cond = _CondGoal - CondGoalInfo,
|
|
|
|
true_goal(Then),
|
|
|
|
det_disj_to_ite(Disjuncts, GoalInfo, SM, Rest),
|
|
Rest = _RestGoal - RestGoalInfo,
|
|
|
|
goal_info_get_nonlocals(CondGoalInfo, CondNonLocals),
|
|
goal_info_get_nonlocals(RestGoalInfo, RestNonLocals),
|
|
set__union(CondNonLocals, RestNonLocals, NonLocals),
|
|
goal_info_set_nonlocals(GoalInfo, NonLocals, NewGoalInfo0),
|
|
|
|
goal_info_get_instmap_delta(GoalInfo, InstMapDelta0),
|
|
instmap_delta_restrict(InstMapDelta0, NonLocals, InstMapDelta),
|
|
goal_info_set_instmap_delta(NewGoalInfo0, InstMapDelta,
|
|
NewGoalInfo1),
|
|
|
|
goal_info_get_determinism(CondGoalInfo, CondDetism),
|
|
goal_info_get_determinism(RestGoalInfo, RestDetism),
|
|
determinism_components(CondDetism, CondCanFail, CondMaxSoln),
|
|
determinism_components(RestDetism, RestCanFail, RestMaxSoln),
|
|
det_disjunction_canfail(CondCanFail, RestCanFail, CanFail),
|
|
det_disjunction_maxsoln(CondMaxSoln, RestMaxSoln, MaxSoln0),
|
|
( MaxSoln0 = at_most_many ->
|
|
MaxSoln = at_most_one
|
|
;
|
|
MaxSoln = MaxSoln0
|
|
),
|
|
determinism_components(Detism, CanFail, MaxSoln),
|
|
goal_info_set_determinism(NewGoalInfo1, Detism, NewGoalInfo),
|
|
|
|
Goal = if_then_else([], Cond, Then, Rest, SM) - NewGoalInfo
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred simplify__contains_multisoln_goal(list(hlds_goal)::in) is semidet.
|
|
|
|
simplify__contains_multisoln_goal(Goals) :-
|
|
list__member(_Goal - GoalInfo, Goals),
|
|
goal_info_get_determinism(GoalInfo, Detism),
|
|
determinism_components(Detism, _, at_most_many).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type simplify_info
|
|
---> simplify_info(
|
|
det_info,
|
|
set(det_msg),
|
|
set(simplification),
|
|
common_info, % Info about common subexpressions.
|
|
instmap,
|
|
varset,
|
|
map(var, type),
|
|
bool, % Does the goal need requantification.
|
|
bool, % Do we need to recompute
|
|
% instmap_deltas for atomic goals
|
|
bool, % Does determinism analysis need to
|
|
% be rerun.
|
|
int, % Measure of the improvement in
|
|
% the goal from simplification.
|
|
int % Count of the number of lambdas
|
|
% which enclose the current goal.
|
|
).
|
|
|
|
simplify_info_init(DetInfo, Simplifications0, InstMap,
|
|
VarSet, VarTypes, Info) :-
|
|
common_info_init(CommonInfo),
|
|
set__init(Msgs),
|
|
set__list_to_set(Simplifications0, Simplifications),
|
|
Info = simplify_info(DetInfo, Msgs, Simplifications, CommonInfo,
|
|
InstMap, VarSet, VarTypes, no, no, no, 0, 0).
|
|
|
|
% Reinitialise the simplify_info before reprocessing a goal.
|
|
:- pred simplify_info_reinit(set(simplification)::in, instmap::in,
|
|
simplify_info::in, simplify_info::out) is det.
|
|
|
|
simplify_info_reinit(Simplifications, InstMap0, Info0, Info) :-
|
|
Info0 = simplify_info(DetInfo, Msgs, _, _, _,
|
|
VarSet, VarTypes, _, _, _, CostDelta, _),
|
|
common_info_init(Common),
|
|
Info = simplify_info(DetInfo, Msgs, Simplifications, Common, InstMap0,
|
|
VarSet, VarTypes, no, no, no, CostDelta, 0).
|
|
|
|
% exported for common.m
|
|
:- interface.
|
|
|
|
:- import_module prog_data.
|
|
:- import_module set.
|
|
|
|
:- pred simplify_info_init(det_info, list(simplification), instmap,
|
|
varset, map(var, type), simplify_info).
|
|
:- mode simplify_info_init(in, in, in, in, in, out) is det.
|
|
|
|
:- pred simplify_info_get_det_info(simplify_info::in, det_info::out) is det.
|
|
:- pred simplify_info_get_msgs(simplify_info::in, set(det_msg)::out) is det.
|
|
:- pred simplify_info_get_instmap(simplify_info::in, instmap::out) is det.
|
|
:- pred simplify_info_get_simplifications(simplify_info::in,
|
|
set(simplification)::out) is det.
|
|
:- pred simplify_info_get_common_info(simplify_info::in,
|
|
common_info::out) is det.
|
|
:- pred simplify_info_get_varset(simplify_info::in, varset::out) is det.
|
|
:- pred simplify_info_get_var_types(simplify_info::in,
|
|
map(var, type)::out) is det.
|
|
:- pred simplify_info_requantify(simplify_info::in) is semidet.
|
|
:- pred simplify_info_recompute_atomic(simplify_info::in) is semidet.
|
|
:- pred simplify_info_rerun_det(simplify_info::in) is semidet.
|
|
:- pred simplify_info_get_cost_delta(simplify_info::in, int::out) is det.
|
|
|
|
:- pred simplify_info_get_module_info(simplify_info::in,
|
|
module_info::out) is det.
|
|
|
|
:- implementation.
|
|
|
|
simplify_info_get_det_info(simplify_info(Det, _,_,_,_,_,_,_,_,_,_,_), Det).
|
|
simplify_info_get_msgs(simplify_info(_, Msgs, _,_,_,_,_,_,_,_,_,_), Msgs).
|
|
simplify_info_get_simplifications(simplify_info(_,_,Simplify,_,_,_,_,_,_,_,_,_),
|
|
Simplify).
|
|
simplify_info_get_common_info(simplify_info(_,_,_,Common, _,_,_,_,_,_,_,_),
|
|
Common).
|
|
simplify_info_get_instmap(simplify_info(_,_,_,_, InstMap,_,_,_,_,_,_,_),
|
|
InstMap).
|
|
simplify_info_get_varset(simplify_info(_,_,_,_,_, VarSet, _,_,_,_,_,_), VarSet).
|
|
simplify_info_get_var_types(simplify_info(_,_,_,_,_,_, VarTypes, _,_,_,_,_),
|
|
VarTypes).
|
|
simplify_info_requantify(simplify_info(_,_,_,_,_,_,_, yes, _,_,_,_)).
|
|
simplify_info_recompute_atomic(simplify_info(_,_,_,_,_,_,_,_, yes,_,_,_)).
|
|
simplify_info_rerun_det(simplify_info(_,_,_,_,_,_,_,_,_, yes,_,_)).
|
|
simplify_info_get_cost_delta(simplify_info(_,_,_,_,_,_,_,_,_,_,CostDelta, _),
|
|
CostDelta).
|
|
|
|
simplify_info_get_module_info(Info, ModuleInfo) :-
|
|
simplify_info_get_det_info(Info, DetInfo),
|
|
det_info_get_module_info(DetInfo, ModuleInfo).
|
|
|
|
:- interface.
|
|
|
|
:- pred simplify_info_set_det_info(simplify_info::in,
|
|
det_info::in, simplify_info::out) is det.
|
|
:- pred simplify_info_set_msgs(simplify_info::in,
|
|
set(det_msg)::in, simplify_info::out) is det.
|
|
:- pred simplify_info_set_simplifications(simplify_info::in,
|
|
set(simplification)::in, simplify_info::out) is det.
|
|
:- pred simplify_info_set_instmap(simplify_info::in,
|
|
instmap::in, simplify_info::out) is det.
|
|
:- pred simplify_info_set_common_info(simplify_info::in, common_info::in,
|
|
simplify_info::out) is det.
|
|
:- pred simplify_info_set_varset(simplify_info::in, varset::in,
|
|
simplify_info::out) is det.
|
|
:- pred simplify_info_set_var_types(simplify_info::in, map(var, type)::in,
|
|
simplify_info::out) is det.
|
|
:- pred simplify_info_set_requantify(simplify_info::in,
|
|
simplify_info::out) is det.
|
|
:- pred simplify_info_set_recompute_atomic(simplify_info::in,
|
|
simplify_info::out) is det.
|
|
:- pred simplify_info_set_rerun_det(simplify_info::in,
|
|
simplify_info::out) is det.
|
|
:- pred simplify_info_add_msg(simplify_info::in, det_msg::in,
|
|
simplify_info::out) is det.
|
|
:- pred simplify_info_do_add_msg(simplify_info::in, det_msg::in,
|
|
simplify_info::out) is det.
|
|
:- pred simplify_info_set_cost_delta(simplify_info::in, int::in,
|
|
simplify_info::out) is det.
|
|
:- pred simplify_info_incr_cost_delta(simplify_info::in,
|
|
int::in, simplify_info::out) is det.
|
|
|
|
:- pred simplify_info_enter_lambda(simplify_info::in, simplify_info::out)
|
|
is det.
|
|
:- pred simplify_info_leave_lambda(simplify_info::in, simplify_info::out)
|
|
is det.
|
|
:- pred simplify_info_inside_lambda(simplify_info::in) is semidet.
|
|
|
|
:- pred simplify_info_set_module_info(simplify_info::in,
|
|
module_info::in, simplify_info::out) is det.
|
|
|
|
:- implementation.
|
|
|
|
simplify_info_set_det_info(simplify_info(_, B, C, D, E, F, G, H, I, J, K, L),
|
|
Det, simplify_info(Det, B, C, D, E, F, G, H, I, J, K, L)).
|
|
simplify_info_set_msgs(simplify_info(A, _, C, D, E, F, G, H, I, J, K, L), Msgs,
|
|
simplify_info(A, Msgs, C, D, E, F, G, H, I, J, K, L)).
|
|
simplify_info_set_simplifications(
|
|
simplify_info(A, B, _, D, E, F, G, H, I, J, K, L),
|
|
Simp, simplify_info(A, B, Simp, D, E, F, G, H, I, J, K, L)).
|
|
simplify_info_set_instmap(simplify_info(A, B, C, D, _, F, G, H, I, J, K, L),
|
|
InstMap,
|
|
simplify_info(A, B, C, D, InstMap, F, G, H, I, J, K, L)).
|
|
simplify_info_set_common_info(simplify_info(A, B, C, _, E, F, G, H, I, J, K, L),
|
|
Common,
|
|
simplify_info(A, B, C, Common, E, F, G, H, I, J, K, L)).
|
|
simplify_info_set_varset(simplify_info(A, B, C, D, E, _, G, H, I, J, K, L),
|
|
VarSet,
|
|
simplify_info(A, B, C, D, E, VarSet, G, H, I, J, K, L)).
|
|
simplify_info_set_var_types(simplify_info(A, B, C, D, E, F, _, H, I, J, K, L),
|
|
VarTypes, simplify_info(A, B, C, D, E, F, VarTypes, H,I,J,K,L)).
|
|
simplify_info_set_requantify(simplify_info(A, B, C, D, E, F, G, _, I, J, K, L),
|
|
simplify_info(A, B, C, D, E, F, G, yes, I, J, K, L)).
|
|
simplify_info_set_recompute_atomic(simplify_info(A, B, C, D, E, F, G,H,_,J,K,L),
|
|
simplify_info(A, B, C, D, E, F, G, H, yes, J, K, L)).
|
|
simplify_info_set_rerun_det(simplify_info(A, B, C, D, E, F, G,H,I,_,K,L),
|
|
simplify_info(A, B, C, D, E, F, G, H, I, yes, K, L)).
|
|
simplify_info_set_cost_delta(simplify_info(A, B, C, D, E, F, G, H, I, J, _, L),
|
|
Delta, simplify_info(A, B, C, D, E, F, G, H, I, J, Delta, L)).
|
|
|
|
simplify_info_incr_cost_delta(
|
|
simplify_info(A, B, C, D, E, F,G,H,I,J, Delta0, L),
|
|
Incr, simplify_info(A, B, C, D, E, F, G, H, I, J, Delta, L)) :-
|
|
Delta is Delta0 + Incr.
|
|
|
|
simplify_info_add_msg(Info0, Msg, Info) :-
|
|
( simplify_do_warn(Info0) ->
|
|
simplify_info_do_add_msg(Info0, Msg, Info)
|
|
;
|
|
Info = Info0
|
|
).
|
|
|
|
simplify_info_do_add_msg(Info0, Msg, Info) :-
|
|
simplify_info_get_msgs(Info0, Msgs0),
|
|
set__insert(Msgs0, Msg, Msgs),
|
|
simplify_info_set_msgs(Info0, Msgs, Info).
|
|
|
|
simplify_info_enter_lambda(
|
|
simplify_info(A, B, C, D, E, F, G, H, I, J, K, LambdaCount0),
|
|
simplify_info(A, B, C, D, E, F, G, H, I, J, K, LambdaCount)) :-
|
|
LambdaCount is LambdaCount0 + 1.
|
|
simplify_info_leave_lambda(
|
|
simplify_info(A, B, C, D, E, F, G, H, I, J, K, LambdaCount0),
|
|
simplify_info(A, B, C, D, E, F, G, H, I, J, K, LambdaCount)) :-
|
|
LambdaCount1 is LambdaCount0 - 1,
|
|
(
|
|
LambdaCount1 >= 0
|
|
->
|
|
LambdaCount = LambdaCount1
|
|
;
|
|
error("simplify_info_leave_lambda: Left too many lambdas")
|
|
).
|
|
simplify_info_inside_lambda(
|
|
simplify_info(_,_,_,_,_,_,_,_,_,_,_,LambdaCount)) :-
|
|
LambdaCount > 0.
|
|
|
|
simplify_info_set_module_info(Info0, ModuleInfo, Info) :-
|
|
simplify_info_get_det_info(Info0, DetInfo0),
|
|
det_info_set_module_info(DetInfo0, ModuleInfo, DetInfo),
|
|
simplify_info_set_det_info(Info0, DetInfo, Info).
|
|
|
|
:- interface.
|
|
|
|
:- pred simplify_do_warn(simplify_info::in) is semidet.
|
|
:- pred simplify_do_warn_calls(simplify_info::in) is semidet.
|
|
:- pred simplify_do_once(simplify_info::in) is semidet.
|
|
:- pred simplify_do_common(simplify_info::in) is semidet.
|
|
:- pred simplify_do_excess_assigns(simplify_info::in) is semidet.
|
|
:- pred simplify_do_calls(simplify_info::in) is semidet.
|
|
:- pred simplify_do_const_prop(simplify_info::in) is semidet.
|
|
:- pred simplify_do_more_common(simplify_info::in) is semidet.
|
|
|
|
:- implementation.
|
|
|
|
simplify_do_warn(Info) :-
|
|
simplify_info_get_simplifications(Info, Simplifications),
|
|
set__member(warn_simple_code, Simplifications).
|
|
simplify_do_warn_calls(Info) :-
|
|
simplify_info_get_simplifications(Info, Simplifications),
|
|
set__member(warn_duplicate_calls, Simplifications).
|
|
simplify_do_once(Info) :-
|
|
simplify_info_get_simplifications(Info, Simplifications),
|
|
set__member(do_once, Simplifications).
|
|
simplify_do_excess_assigns(Info) :-
|
|
simplify_info_get_simplifications(Info, Simplifications),
|
|
set__member(excess_assigns, Simplifications).
|
|
simplify_do_calls(Info) :-
|
|
simplify_info_get_simplifications(Info, Simplifications),
|
|
set__member(duplicate_calls, Simplifications).
|
|
simplify_do_const_prop(Info) :-
|
|
simplify_info_get_simplifications(Info, Simplifications),
|
|
set__member(constant_prop, Simplifications).
|
|
simplify_do_common(Info) :-
|
|
simplify_info_get_simplifications(Info, Simplifications),
|
|
set__member(common_struct, Simplifications).
|
|
simplify_do_more_common(Info) :-
|
|
simplify_info_get_simplifications(Info, Simplifications),
|
|
set__member(extra_common_struct, Simplifications).
|
|
|
|
:- pred simplify_info_update_instmap(simplify_info::in, hlds_goal::in,
|
|
simplify_info::out) is det.
|
|
|
|
simplify_info_update_instmap(
|
|
simplify_info(A, B, C, D, InstMap0, F, G, H, I, J, K, L), Goal,
|
|
simplify_info(A, B, C, D, InstMap, F, G, H, I, J, K, L)) :-
|
|
update_instmap(Goal, InstMap0, InstMap).
|
|
|
|
:- type before_after
|
|
---> before
|
|
; after.
|
|
|
|
% Clear the common_info structs accumulated since the last goal that
|
|
% could cause a stack flush. This is done to avoid replacing a
|
|
% deconstruction with assignments to the arguments where this
|
|
% would cause more variables to be live across the stack flush.
|
|
% Calls and construction unifications are not treated in this
|
|
% way since it is nearly always better to optimize them away.
|
|
% When doing deforestation, it may be better to remove
|
|
% as many common structures as possible.
|
|
:- pred simplify_info_maybe_clear_structs(before_after::in, hlds_goal::in,
|
|
simplify_info::in, simplify_info::out) is det.
|
|
|
|
simplify_info_maybe_clear_structs(BeforeAfter, Goal, Info0, Info) :-
|
|
(
|
|
( code_util__cannot_stack_flush(Goal)
|
|
; simplify_do_more_common(Info0)
|
|
)
|
|
->
|
|
Info = Info0
|
|
;
|
|
% First check to see if a call is common and can be replaced
|
|
% by a series of unifications.
|
|
simplify_do_common(Info0),
|
|
(
|
|
BeforeAfter = after
|
|
;
|
|
BeforeAfter = before,
|
|
Goal = GoalExpr - _,
|
|
GoalExpr \= call(_, _, _, _, _, _),
|
|
GoalExpr \= higher_order_call(_, _, _, _, _, _),
|
|
GoalExpr \= class_method_call(_, _, _, _, _, _),
|
|
GoalExpr \= pragma_c_code(_, _, _, _, _, _, _)
|
|
)
|
|
->
|
|
simplify_info_get_common_info(Info0, CommonInfo0),
|
|
common_info_clear_structs(CommonInfo0, CommonInfo),
|
|
simplify_info_set_common_info(Info0, CommonInfo, Info)
|
|
;
|
|
Info = Info0
|
|
).
|
|
|
|
% Reset the instmap and seen calls for the next branch.
|
|
:- pred simplify_info_post_branch_update(simplify_info::in, simplify_info::in,
|
|
simplify_info::out) is det.
|
|
|
|
simplify_info_post_branch_update(PreBranchInfo, PostBranchInfo0, Info) :-
|
|
simplify_info_get_instmap(PreBranchInfo, InstMap),
|
|
simplify_info_set_instmap(PostBranchInfo0, InstMap, PostBranchInfo1),
|
|
simplify_info_get_common_info(PreBranchInfo, Common),
|
|
simplify_info_set_common_info(PostBranchInfo1, Common, Info).
|
|
|
|
% Undo updates to the simplify_info before redoing
|
|
% simplification on a goal.
|
|
:- pred simplify_info_undo_goal_updates(simplify_info::in, simplify_info::in,
|
|
simplify_info::out) is det.
|
|
|
|
simplify_info_undo_goal_updates(Info1, Info2, Info) :-
|
|
simplify_info_get_common_info(Info1, CommonInfo0),
|
|
simplify_info_set_common_info(Info2, CommonInfo0, Info3),
|
|
simplify_info_get_instmap(Info1, InstMap),
|
|
simplify_info_set_instmap(Info3, InstMap, Info).
|