Files
mercury/compiler/simplify.m
Simon Taylor 3a0770e7b9 Clean up the handling of commits and goals which cannot succeed.
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.
1998-09-10 06:38:28 +00:00

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).