mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-18 23:35:25 +00:00
Estimated hours taken: _____ Branches: 0.5 Post-commit review of Paul's change introducing the loop_control scope reason. compiler/simplify.m: Fix a bug in the handling of the new scope kind. compiler/make_hlds_warn.m: compiler/modecheck_goal.m: Replace tentative documentation with firm documentation. compiler/saved_vars.m: Improve layout.
629 lines
26 KiB
Mathematica
629 lines
26 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1996-2011 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.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: saved_vars.m.
|
|
% Main author: zs.
|
|
%
|
|
% This module traverses the goal for each procedure, looking for and
|
|
% exploiting opportunities to reduce the number of variables that have
|
|
% to be saved across calls.
|
|
%
|
|
% At the moment the only opportunity we look for is the assignment of
|
|
% constants to variables. These assignments should be delayed until
|
|
% the value of the variable is needed. If the variable has several uses,
|
|
% we generate a copy for each use, renaming the using goal to refer to the
|
|
% variable by its new name.
|
|
%
|
|
% We thread the SlotInfo structure through the module; this allows us
|
|
% to define new variables.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend.saved_vars.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred saved_vars_proc(pred_proc_id::in,
|
|
proc_info::in, proc_info::out, module_info::in, module_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.mode_util.
|
|
:- import_module check_hlds.polymorphism.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_out.
|
|
:- import_module hlds.hlds_out.hlds_out_goal.
|
|
:- import_module hlds.hlds_out.hlds_out_util.
|
|
:- import_module hlds.hlds_rtti.
|
|
:- import_module hlds.passes_aux.
|
|
:- import_module hlds.quantification.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.set_of_var.
|
|
|
|
:- import_module bool.
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module term.
|
|
:- import_module varset.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
saved_vars_proc(proc(PredId, ProcId), !ProcInfo, !ModuleInfo) :-
|
|
trace [io(!IO)] (
|
|
write_proc_progress_message("% Minimizing saved vars in ",
|
|
PredId, ProcId, !.ModuleInfo, !IO)
|
|
),
|
|
|
|
module_info_get_globals(!.ModuleInfo, Globals),
|
|
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
|
|
body_should_use_typeinfo_liveness(PredInfo, Globals, TypeInfoLiveness),
|
|
|
|
proc_info_get_goal(!.ProcInfo, Goal0),
|
|
proc_info_get_varset(!.ProcInfo, Varset0),
|
|
proc_info_get_vartypes(!.ProcInfo, VarTypes0),
|
|
proc_info_get_rtti_varmaps(!.ProcInfo, RttiVarMaps0),
|
|
init_slot_info(Varset0, VarTypes0, RttiVarMaps0, TypeInfoLiveness,
|
|
SlotInfo0),
|
|
|
|
saved_vars_in_goal(Goal0, Goal1, SlotInfo0, SlotInfo),
|
|
|
|
final_slot_info(Varset1, VarTypes1, RttiVarMaps1, SlotInfo),
|
|
proc_info_get_headvars(!.ProcInfo, HeadVars),
|
|
|
|
% Recompute the nonlocals for each goal.
|
|
implicitly_quantify_clause_body_general(ordinary_nonlocals_no_lambda,
|
|
HeadVars, _Warnings, Goal1, Goal2,
|
|
Varset1, Varset, VarTypes1, VarTypes, RttiVarMaps1, RttiVarMaps),
|
|
proc_info_get_initial_instmap(!.ProcInfo, !.ModuleInfo, InstMap0),
|
|
proc_info_get_inst_varset(!.ProcInfo, InstVarSet),
|
|
recompute_instmap_delta(do_not_recompute_atomic_instmap_deltas,
|
|
Goal2, Goal, VarTypes, InstVarSet, InstMap0, !ModuleInfo),
|
|
|
|
trace [io(!IO), compile_time(flag("debug_saved_vars"))] (
|
|
OutInfo = hlds_out_util.init_hlds_out_info(Globals),
|
|
io.write_string("initial version:\n", !IO),
|
|
hlds_out_goal.write_goal(OutInfo, Goal0, !.ModuleInfo, Varset0,
|
|
yes, 0, "\n", !IO),
|
|
io.write_string("after transformation:\n", !IO),
|
|
hlds_out_goal.write_goal(OutInfo, Goal1, !.ModuleInfo, Varset1,
|
|
yes, 0, "\n", !IO),
|
|
io.write_string("final version:\n", !IO),
|
|
hlds_out_goal.write_goal(OutInfo, Goal, !.ModuleInfo, Varset,
|
|
yes, 0, "\n", !IO)
|
|
),
|
|
|
|
proc_info_set_goal(Goal, !ProcInfo),
|
|
proc_info_set_varset(Varset, !ProcInfo),
|
|
proc_info_set_vartypes(VarTypes, !ProcInfo),
|
|
proc_info_set_rtti_varmaps(RttiVarMaps, !ProcInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred saved_vars_in_goal(hlds_goal::in, hlds_goal::out,
|
|
slot_info::in, slot_info::out) is det.
|
|
|
|
saved_vars_in_goal(Goal0, Goal, !SlotInfo) :-
|
|
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
|
|
(
|
|
GoalExpr0 = conj(ConjType, Goals0),
|
|
(
|
|
ConjType = plain_conj,
|
|
NonLocals = goal_info_get_nonlocals(GoalInfo0),
|
|
saved_vars_in_conj(Goals0, Goals, NonLocals, !SlotInfo),
|
|
conj_list_to_goal(Goals, GoalInfo0, Goal)
|
|
;
|
|
ConjType = parallel_conj,
|
|
saved_vars_in_independent_goals(Goals0, Goals, !SlotInfo),
|
|
Goal = hlds_goal(conj(ConjType, Goals), GoalInfo0)
|
|
)
|
|
;
|
|
GoalExpr0 = disj(Goals0),
|
|
saved_vars_in_independent_goals(Goals0, Goals, !SlotInfo),
|
|
GoalExpr = disj(Goals),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = negation(NegGoal0),
|
|
saved_vars_in_goal(NegGoal0, NegGoal, !SlotInfo),
|
|
GoalExpr = negation(NegGoal),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = switch(Var, CanFail, Cases0),
|
|
saved_vars_in_switch(Cases0, Cases, !SlotInfo),
|
|
GoalExpr = switch(Var, CanFail, Cases),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
|
|
saved_vars_in_goal(Cond0, Cond, !SlotInfo),
|
|
saved_vars_in_goal(Then0, Then, !SlotInfo),
|
|
saved_vars_in_goal(Else0, Else, !SlotInfo),
|
|
GoalExpr = if_then_else(Vars, Cond, Then, Else),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = scope(Reason, SubGoal0),
|
|
( Reason = from_ground_term(_, from_ground_term_construct) ->
|
|
% Moving unifications around inside these scopes is
|
|
% (a) counterproductive, and (b) incorrect, since it would
|
|
% invalidate the invariants required of such scopes.
|
|
SubGoal = SubGoal0
|
|
;
|
|
saved_vars_in_goal(SubGoal0, SubGoal, !SlotInfo)
|
|
),
|
|
GoalExpr = scope(Reason, SubGoal),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
( GoalExpr0 = generic_call(_, _, _, _)
|
|
; GoalExpr0 = plain_call(_, _, _, _, _, _)
|
|
; GoalExpr0 = unify(_, _, _, _, _)
|
|
; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
|
|
),
|
|
Goal = Goal0
|
|
;
|
|
GoalExpr0 = shorthand(_),
|
|
% these should have been expanded out by now
|
|
unexpected($module, $pred, "shorthand")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% If we find a unification that assigns a constant to a variable,
|
|
% attempt to push it into the following code.
|
|
%
|
|
% We cannot push such a unification if the following goal needs the value
|
|
% of the variable. We also avoid attempting to push the unification into
|
|
% any following similar unifications, since the order of such unifications
|
|
% does not matter, and we would like to avoid "pushing contests" between
|
|
% several such unifications about which one should be closest to a
|
|
% following goal that uses all the variables they define.
|
|
%
|
|
:- pred saved_vars_in_conj(list(hlds_goal)::in, list(hlds_goal)::out,
|
|
set_of_progvar::in, slot_info::in, slot_info::out) is det.
|
|
|
|
saved_vars_in_conj([], [], _, !SlotInfo).
|
|
saved_vars_in_conj([Goal0 | Goals0], Goals, NonLocals, !SlotInfo) :-
|
|
(
|
|
Goal0 = hlds_goal(unify(_, _, _, Unif, _), GoalInfo),
|
|
Unif = construct(Var, _, [], _, _, _, _),
|
|
Features = goal_info_get_features(GoalInfo),
|
|
( all [Feature]
|
|
(
|
|
set.member(Feature, Features)
|
|
=>
|
|
ok_to_duplicate(Feature) = yes
|
|
)
|
|
),
|
|
\+ slot_info_do_not_duplicate_var(!.SlotInfo, Var),
|
|
skip_constant_constructs(Goals0, Constants, OtherGoals),
|
|
OtherGoals = [First | _Rest],
|
|
can_push(Var, First) = yes
|
|
->
|
|
set_of_var.is_member(NonLocals, Var, IsNonLocal),
|
|
saved_vars_delay_goal(OtherGoals, Goals1, Goal0, Var, IsNonLocal,
|
|
!SlotInfo),
|
|
list.append(Constants, Goals1, Goals2),
|
|
saved_vars_in_conj(Goals2, Goals, NonLocals, !SlotInfo)
|
|
;
|
|
saved_vars_in_goal(Goal0, Goal1, !SlotInfo),
|
|
saved_vars_in_conj(Goals0, Goals1, NonLocals, !SlotInfo),
|
|
Goals = [Goal1 | Goals1]
|
|
).
|
|
|
|
% ok_to_duplicate returns `no' for features which shouldn't be
|
|
% on construction unifications in the first place as well as for
|
|
% construction unifications that shouldn't be duplicated.
|
|
%
|
|
:- func ok_to_duplicate(goal_feature) = bool.
|
|
|
|
ok_to_duplicate(feature_constraint) = no.
|
|
ok_to_duplicate(feature_from_head) = yes.
|
|
ok_to_duplicate(feature_not_impure_for_determinism) = no.
|
|
ok_to_duplicate(feature_stack_opt) = no.
|
|
ok_to_duplicate(feature_tuple_opt) = no.
|
|
ok_to_duplicate(feature_call_table_gen) = no.
|
|
ok_to_duplicate(feature_preserve_backtrack_into) = no.
|
|
ok_to_duplicate(feature_hide_debug_event) = no.
|
|
ok_to_duplicate(feature_deep_tail_rec_call) = no.
|
|
ok_to_duplicate(feature_debug_tail_rec_call) = no.
|
|
ok_to_duplicate(feature_keep_constant_binding) = no.
|
|
ok_to_duplicate(feature_save_deep_excp_vars) = no.
|
|
ok_to_duplicate(feature_dont_warn_singleton) = yes.
|
|
ok_to_duplicate(feature_duplicated_for_switch) = yes.
|
|
ok_to_duplicate(feature_mode_check_clauses_goal) = yes.
|
|
ok_to_duplicate(feature_will_not_modify_trail) = yes.
|
|
ok_to_duplicate(feature_will_not_call_mm_tabled) = yes.
|
|
ok_to_duplicate(feature_contains_trace) = yes.
|
|
ok_to_duplicate(feature_pretest_equality) = yes.
|
|
ok_to_duplicate(feature_pretest_equality_condition) = yes.
|
|
ok_to_duplicate(feature_lambda_undetermined_mode) = yes.
|
|
ok_to_duplicate(feature_contains_stm_inner_outer) = yes.
|
|
|
|
% Divide a list of goals into an initial subsequence of goals
|
|
% that construct constants, and all other goals.
|
|
%
|
|
:- pred skip_constant_constructs(list(hlds_goal)::in, list(hlds_goal)::out,
|
|
list(hlds_goal)::out) is det.
|
|
|
|
skip_constant_constructs([], [], []).
|
|
skip_constant_constructs([Goal0 | Goals0], Constants, Others) :-
|
|
(
|
|
Goal0 = hlds_goal(unify(_, _, _, Unif, _), _),
|
|
Unif = construct(_, _, [], _, _, _, _)
|
|
->
|
|
skip_constant_constructs(Goals0, Constants1, Others),
|
|
Constants = [Goal0 | Constants1]
|
|
;
|
|
Constants = [],
|
|
Others = [Goal0 | Goals0]
|
|
).
|
|
|
|
% Decide whether the value of the given variable is needed immediately
|
|
% in the goal, or whether the unification that constructs a value for
|
|
% the variable can be usefully pushed into the goal.
|
|
%
|
|
% NOTE: the logic of this predicate must match the logic of
|
|
% saved_vars_delay_goal.
|
|
% XXX: I went to update saved_vars_delay_goal after changing this
|
|
% predicate and it already diverges from the algorithm here!
|
|
%
|
|
:- func can_push(prog_var, hlds_goal) = bool.
|
|
|
|
can_push(Var, Goal) = CanPush :-
|
|
Goal = hlds_goal(GoalExpr, GoalInfo),
|
|
NonLocals = goal_info_get_nonlocals(GoalInfo),
|
|
( set_of_var.member(NonLocals, Var) ->
|
|
(
|
|
( GoalExpr = if_then_else(_, _, _, _)
|
|
; GoalExpr = negation(_)
|
|
; GoalExpr = disj(_)
|
|
; GoalExpr = conj(plain_conj, _)
|
|
),
|
|
CanPush = yes
|
|
;
|
|
( GoalExpr = conj(parallel_conj, _)
|
|
; GoalExpr = unify(_, _, _, _, _)
|
|
; GoalExpr = plain_call(_, _, _, _, _, _)
|
|
; GoalExpr = generic_call(_, _, _, _)
|
|
; GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
|
|
),
|
|
CanPush = no
|
|
;
|
|
GoalExpr = scope(Reason, _),
|
|
(
|
|
( Reason = exist_quant(_)
|
|
; Reason = from_ground_term(_, from_ground_term_deconstruct)
|
|
; Reason = from_ground_term(_, from_ground_term_other)
|
|
),
|
|
CanPush = yes
|
|
;
|
|
( Reason = from_ground_term(_, from_ground_term_construct)
|
|
; Reason = promise_solutions(_, _)
|
|
; Reason = promise_purity(_)
|
|
; Reason = commit(_)
|
|
; Reason = barrier(_)
|
|
; Reason = trace_goal(_, _, _, _, _)
|
|
; Reason = loop_control(_, _)
|
|
),
|
|
CanPush = no
|
|
;
|
|
( Reason = require_detism(_)
|
|
; Reason = require_complete_switch(_)
|
|
; Reason = from_ground_term(_, from_ground_term_initial)
|
|
),
|
|
% These scopes should have been deleted by now.
|
|
unexpected($module, $pred, "unexpected scope")
|
|
)
|
|
;
|
|
GoalExpr = switch(SwitchVar, _, _),
|
|
( Var = SwitchVar ->
|
|
CanPush = no
|
|
;
|
|
CanPush = yes
|
|
)
|
|
;
|
|
GoalExpr = shorthand(_),
|
|
% These should have been expanded out by now.
|
|
unexpected($module, $pred, "shorthand")
|
|
)
|
|
;
|
|
CanPush = yes
|
|
).
|
|
|
|
% The main inputs of this predicate are a list of goals in a conjunction,
|
|
% and a goal Construct that assigns a constant to a variable Var.
|
|
%
|
|
% When we find an atomic goal in the conjunction that refers to Var,
|
|
% we create a new variable NewVar, rename both this goal and Construct
|
|
% to refer to NewVar instead of Var, and insert the new version
|
|
% of Construct before the new version of the goal.
|
|
%
|
|
% When we find a non-atomic goal in the conjunction that refers to Var,
|
|
% we push Construct into each of its components.
|
|
%
|
|
% If Var is exported from the conjunction, we include Construct
|
|
% at the end of the conjunction to give it its value.
|
|
%
|
|
:- pred saved_vars_delay_goal(list(hlds_goal)::in, list(hlds_goal)::out,
|
|
hlds_goal::in, prog_var::in, bool::in, slot_info::in, slot_info::out)
|
|
is det.
|
|
|
|
saved_vars_delay_goal([], Goals, Construct, _Var, IsNonLocal, !SlotInfo) :-
|
|
(
|
|
IsNonLocal = yes,
|
|
Goals = [Construct]
|
|
;
|
|
IsNonLocal = no,
|
|
Goals = []
|
|
).
|
|
saved_vars_delay_goal([Goal0 | Goals0], Goals, Construct, Var, IsNonLocal,
|
|
!SlotInfo) :-
|
|
Goal0 = hlds_goal(Goal0Expr, Goal0Info),
|
|
Goal0NonLocals = goal_info_get_nonlocals(Goal0Info),
|
|
( set_of_var.member(Goal0NonLocals, Var) ->
|
|
(
|
|
Goal0Expr = unify(_, _, _, _, _),
|
|
rename_var(Var, _NewVar, Subst, !SlotInfo),
|
|
rename_some_vars_in_goal(Subst, Construct, NewConstruct),
|
|
rename_some_vars_in_goal(Subst, Goal0, Goal1),
|
|
saved_vars_delay_goal(Goals0, Goals1, Construct, Var,
|
|
IsNonLocal, !SlotInfo),
|
|
Goals = [NewConstruct, Goal1 | Goals1]
|
|
;
|
|
Goal0Expr = plain_call(_, _, _, _, _, _),
|
|
rename_var(Var, _NewVar, Subst, !SlotInfo),
|
|
rename_some_vars_in_goal(Subst, Construct, NewConstruct),
|
|
rename_some_vars_in_goal(Subst, Goal0, Goal1),
|
|
saved_vars_delay_goal(Goals0, Goals1, Construct, Var,
|
|
IsNonLocal, !SlotInfo),
|
|
Goals = [NewConstruct, Goal1 | Goals1]
|
|
;
|
|
Goal0Expr = generic_call(_, _, _, _),
|
|
rename_var(Var, _NewVar, Subst, !SlotInfo),
|
|
rename_some_vars_in_goal(Subst, Construct, NewConstruct),
|
|
rename_some_vars_in_goal(Subst, Goal0, Goal1),
|
|
saved_vars_delay_goal(Goals0, Goals1, Construct, Var,
|
|
IsNonLocal, !SlotInfo),
|
|
Goals = [NewConstruct, Goal1 | Goals1]
|
|
;
|
|
Goal0Expr = call_foreign_proc(_, _, _, _, _, _, _),
|
|
rename_var(Var, _NewVar, Subst, !SlotInfo),
|
|
rename_some_vars_in_goal(Subst, Construct, NewConstruct),
|
|
rename_some_vars_in_goal(Subst, Goal0, Goal1),
|
|
saved_vars_delay_goal(Goals0, Goals1, Construct, Var,
|
|
IsNonLocal, !SlotInfo),
|
|
Goals = [NewConstruct, Goal1 | Goals1]
|
|
;
|
|
Goal0Expr = conj(ConjType, Conj0),
|
|
(
|
|
ConjType = plain_conj,
|
|
list.append(Conj0, Goals0, Goals1),
|
|
saved_vars_delay_goal(Goals1, Goals, Construct, Var,
|
|
IsNonLocal, !SlotInfo)
|
|
;
|
|
ConjType = parallel_conj,
|
|
push_into_goals_rename(Conj0, Conj, Construct, Var,
|
|
!SlotInfo),
|
|
Goal1 = hlds_goal(conj(ConjType, Conj), Goal0Info),
|
|
saved_vars_delay_goal(Goals0, Goals1, Construct, Var,
|
|
IsNonLocal, !SlotInfo),
|
|
Goals = [Goal1 | Goals1]
|
|
)
|
|
;
|
|
Goal0Expr = scope(Reason, SomeGoal0),
|
|
rename_var(Var, NewVar, Subst, !SlotInfo),
|
|
rename_some_vars_in_goal(Subst, Construct, NewConstruct),
|
|
rename_some_vars_in_goal(Subst, SomeGoal0, SomeGoal1),
|
|
push_into_goal(SomeGoal1, SomeGoal, NewConstruct, NewVar,
|
|
!SlotInfo),
|
|
Goal1 = hlds_goal(scope(Reason, SomeGoal), Goal0Info),
|
|
saved_vars_delay_goal(Goals0, Goals1, Construct, Var,
|
|
IsNonLocal, !SlotInfo),
|
|
Goals = [Goal1 | Goals1]
|
|
;
|
|
Goal0Expr = negation(NegGoal0),
|
|
rename_var(Var, NewVar, Subst, !SlotInfo),
|
|
rename_some_vars_in_goal(Subst, Construct, NewConstruct),
|
|
rename_some_vars_in_goal(Subst, NegGoal0, NegGoal1),
|
|
push_into_goal(NegGoal1, NegGoal, NewConstruct, NewVar,
|
|
!SlotInfo),
|
|
Goal1 = hlds_goal(negation(NegGoal), Goal0Info),
|
|
saved_vars_delay_goal(Goals0, Goals1, Construct, Var,
|
|
IsNonLocal, !SlotInfo),
|
|
Goals = [Goal1 | Goals1]
|
|
;
|
|
Goal0Expr = disj(Disjuncts0),
|
|
push_into_goals_rename(Disjuncts0, Disjuncts, Construct, Var,
|
|
!SlotInfo),
|
|
Goal1 = hlds_goal(disj(Disjuncts), Goal0Info),
|
|
saved_vars_delay_goal(Goals0, Goals1, Construct, Var,
|
|
IsNonLocal, !SlotInfo),
|
|
Goals = [Goal1 | Goals1]
|
|
;
|
|
Goal0Expr = switch(SwitchVar, CF, Cases0),
|
|
( SwitchVar = Var ->
|
|
saved_vars_delay_goal(Goals0, Goals1, Construct, Var,
|
|
IsNonLocal, !SlotInfo),
|
|
Goals = [Construct, Goal0 | Goals1]
|
|
;
|
|
push_into_cases_rename(Cases0, Cases, Construct, Var,
|
|
!SlotInfo),
|
|
Goal1 = hlds_goal(switch(SwitchVar, CF, Cases), Goal0Info),
|
|
saved_vars_delay_goal(Goals0, Goals1, Construct, Var,
|
|
IsNonLocal, !SlotInfo),
|
|
Goals = [Goal1 | Goals1]
|
|
)
|
|
;
|
|
Goal0Expr = if_then_else(V, Cond0, Then0, Else0),
|
|
push_into_goal_rename(Cond0, Cond, Construct, Var, !SlotInfo),
|
|
push_into_goal_rename(Then0, Then, Construct, Var, !SlotInfo),
|
|
push_into_goal_rename(Else0, Else, Construct, Var, !SlotInfo),
|
|
Goal1 = hlds_goal(if_then_else(V, Cond, Then, Else), Goal0Info),
|
|
saved_vars_delay_goal(Goals0, Goals1, Construct, Var,
|
|
IsNonLocal, !SlotInfo),
|
|
Goals = [Goal1 | Goals1]
|
|
;
|
|
Goal0Expr = shorthand(_),
|
|
% These should have been expanded out by now.
|
|
unexpected($module, $pred, "shorthand")
|
|
)
|
|
;
|
|
saved_vars_delay_goal(Goals0, Goals1, Construct, Var, IsNonLocal,
|
|
!SlotInfo),
|
|
Goals = [Goal0 | Goals1]
|
|
).
|
|
|
|
% Push a non-renamed version of the given construction into the given goal.
|
|
% Also traverse the goal looking for further opportunities.
|
|
%
|
|
:- pred push_into_goal(hlds_goal::in, hlds_goal::out, hlds_goal::in,
|
|
prog_var::in, slot_info::in, slot_info::out) is det.
|
|
|
|
push_into_goal(Goal0, Goal, Construct, Var, !SlotInfo) :-
|
|
saved_vars_in_goal(Goal0, Goal1, !SlotInfo),
|
|
Goal1 = hlds_goal(_, GoalInfo1),
|
|
goal_to_conj_list(Goal1, Conj1),
|
|
saved_vars_delay_goal(Conj1, Conj, Construct, Var, no, !SlotInfo),
|
|
conj_list_to_goal(Conj, GoalInfo1, Goal).
|
|
|
|
% Push a renamed version of the given construction into the given goal.
|
|
% If the goal does not refer to the variable bound by the construction,
|
|
% then this would have no effect, so we merely traverse the goal
|
|
% looking for other opportunities.
|
|
%
|
|
:- pred push_into_goal_rename(hlds_goal::in, hlds_goal::out, hlds_goal::in,
|
|
prog_var::in, slot_info::in, slot_info::out) is det.
|
|
|
|
push_into_goal_rename(Goal0, Goal, Construct, Var, !SlotInfo) :-
|
|
Goal0 = hlds_goal(_, GoalInfo0),
|
|
NonLocals = goal_info_get_nonlocals(GoalInfo0),
|
|
( set_of_var.member(NonLocals, Var) ->
|
|
rename_var(Var, NewVar, Subst, !SlotInfo),
|
|
rename_some_vars_in_goal(Subst, Construct, NewConstruct),
|
|
rename_some_vars_in_goal(Subst, Goal0, Goal1),
|
|
push_into_goal(Goal1, Goal, NewConstruct, NewVar, !SlotInfo)
|
|
;
|
|
saved_vars_in_goal(Goal0, Goal, !SlotInfo)
|
|
).
|
|
|
|
% Push renamed versions of the given construction into each of
|
|
% several goals.
|
|
%
|
|
:- pred push_into_goals_rename(list(hlds_goal)::in, list(hlds_goal)::out,
|
|
hlds_goal::in, prog_var::in, slot_info::in, slot_info::out) is det.
|
|
|
|
push_into_goals_rename([], [], _Construct, _Var, !SlotInfo).
|
|
push_into_goals_rename([Goal0 | Goals0], [Goal | Goals], Construct, Var,
|
|
!SlotInfo) :-
|
|
push_into_goal_rename(Goal0, Goal, Construct, Var, !SlotInfo),
|
|
push_into_goals_rename(Goals0, Goals, Construct, Var, !SlotInfo).
|
|
|
|
% Push renamed versions of the given construction into each of
|
|
% several cases.
|
|
%
|
|
:- pred push_into_cases_rename(list(case)::in, list(case)::out, hlds_goal::in,
|
|
prog_var::in, slot_info::in, slot_info::out) is det.
|
|
|
|
push_into_cases_rename([], [], _Construct, _Var, !SlotInfo).
|
|
push_into_cases_rename([Case0 | Cases0], [Case | Cases], Construct, Var,
|
|
!SlotInfo) :-
|
|
Case0 = case(MainConsId, OtherConsIds, Goal0),
|
|
push_into_goal_rename(Goal0, Goal, Construct, Var, !SlotInfo),
|
|
Case = case(MainConsId, OtherConsIds, Goal),
|
|
push_into_cases_rename(Cases0, Cases, Construct, Var, !SlotInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% saved_vars_in_goal does a saved_vars_in_goal on an list of
|
|
% independent goals, and is used to process disjunctions and
|
|
% parallel conjunctions.
|
|
%
|
|
:- pred saved_vars_in_independent_goals(list(hlds_goal)::in,
|
|
list(hlds_goal)::out, slot_info::in, slot_info::out) is det.
|
|
|
|
saved_vars_in_independent_goals([], [], !SlotInfo).
|
|
saved_vars_in_independent_goals([Goal0 | Goals0], [Goal | Goals],
|
|
!SlotInfo) :-
|
|
saved_vars_in_goal(Goal0, Goal, !SlotInfo),
|
|
saved_vars_in_independent_goals(Goals0, Goals, !SlotInfo).
|
|
|
|
:- pred saved_vars_in_switch(list(case)::in, list(case)::out,
|
|
slot_info::in, slot_info::out) is det.
|
|
|
|
saved_vars_in_switch([], [], !SlotInfo).
|
|
saved_vars_in_switch([Case0 | Cases0], [Case | Cases], !SlotInfo) :-
|
|
Case0 = case(MainConsId, OtherConsIds, Goal0),
|
|
saved_vars_in_goal(Goal0, Goal, !SlotInfo),
|
|
Case = case(MainConsId, OtherConsIds, Goal),
|
|
saved_vars_in_switch(Cases0, Cases, !SlotInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type slot_info
|
|
---> slot_info(
|
|
prog_varset,
|
|
vartypes,
|
|
rtti_varmaps,
|
|
bool % TypeInfoLiveness
|
|
).
|
|
|
|
:- pred init_slot_info(prog_varset::in, vartypes::in,
|
|
rtti_varmaps::in, bool::in, slot_info::out) is det.
|
|
|
|
init_slot_info(Varset, VarTypes, RttiVarMaps, TypeInfoLiveness, SlotInfo) :-
|
|
SlotInfo = slot_info(Varset, VarTypes, RttiVarMaps, TypeInfoLiveness).
|
|
|
|
:- pred final_slot_info(prog_varset::out, vartypes::out, rtti_varmaps::out,
|
|
slot_info::in) is det.
|
|
|
|
final_slot_info(Varset, VarTypes, RttiVarMaps, SlotInfo) :-
|
|
SlotInfo = slot_info(Varset, VarTypes, RttiVarMaps, _).
|
|
|
|
:- pred rename_var(prog_var::in, prog_var::out, map(prog_var, prog_var)::out,
|
|
slot_info::in, slot_info::out) is det.
|
|
|
|
rename_var(Var, NewVar, Substitution, !SlotInfo) :-
|
|
!.SlotInfo = slot_info(Varset0, VarTypes0, RttiVarMaps0, TypeInfoLiveness),
|
|
varset.new_var(NewVar, Varset0, Varset),
|
|
map.from_assoc_list([Var - NewVar], Substitution),
|
|
map.lookup(VarTypes0, Var, Type),
|
|
map.det_insert(NewVar, Type, VarTypes0, VarTypes),
|
|
rtti_var_info_duplicate(Var, NewVar, RttiVarMaps0, RttiVarMaps),
|
|
!:SlotInfo = slot_info(Varset, VarTypes, RttiVarMaps,
|
|
TypeInfoLiveness).
|
|
|
|
% Check whether it is ok to duplicate a given variable according
|
|
% to the information in the slot_info. If TypeInfoLiveness is set,
|
|
% it is possible that liveness.m will want to refer to the
|
|
% rtti_varmaps to calculate which type_infos are live (see the
|
|
% comments at the top of liveness.m). If we duplicated any
|
|
% type_info variables here then this could cause problems because
|
|
% the rtti_varmaps would not be able to be kept consistent.
|
|
% Therefore we don't allow type_infos to be duplicated when
|
|
% TypeInfoLiveness is set.
|
|
%
|
|
:- pred slot_info_do_not_duplicate_var(slot_info::in, prog_var::in) is semidet.
|
|
|
|
slot_info_do_not_duplicate_var(SlotInfo, Var) :-
|
|
SlotInfo = slot_info(_, VarTypes, _, TypeInfoLiveness),
|
|
TypeInfoLiveness = yes,
|
|
map.lookup(VarTypes, Var, Type),
|
|
polymorphism.type_is_type_info_or_ctor_type(Type).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module ll_backend.saved_vars.
|
|
%-----------------------------------------------------------------------------%
|