Files
mercury/compiler/interval.m
Zoltan Somogyi 672f77c4ec Add a new compiler option. --inform-ite-instead-of-switch.
Estimated hours taken: 20
Branches: main

Add a new compiler option. --inform-ite-instead-of-switch. If this is enabled,
the compiler will generate informational messages about if-then-elses that
it thinks should be converted to switches for the sake of program reliability.

Act on the output generated by this option.

compiler/simplify.m:
	Implement the new option.

	Fix an old bug that could cause us to generate warnings about code
	that was OK in one duplicated copy but not in another (where a switch
	arm's code is duplicated due to the case being selected for more than
	one cons_id).

compiler/options.m:
	Add the new option.

	Add a way to test for the bug fix in simplify.

doc/user_guide.texi:
	Document the new option.

NEWS:
	Mention the new option.

library/*.m:
mdbcomp/*.m:
browser/*.m:
compiler/*.m:
deep_profiler/*.m:
	Convert if-then-elses to switches at most of the sites suggested by the
	new option. At the remaining sites, switching to switches would have
	nontrivial downsides. This typically happens with the switched-on type
	has many functors, and we treat one or two specially (e.g. cons/2 in
	the cons_id type).

	Perform misc cleanups in the vicinity of the if-then-else to switch
	conversions.

	In a few cases, improve the error messages generated.

compiler/accumulator.m:
compiler/hlds_goal.m:
	(Rename and) move insts for particular kinds of goal from
	accumulator.m to hlds_goal.m, to allow them to be used in other
	modules. Using these insts allowed us to eliminate some if-then-elses
	entirely.

compiler/exprn_aux.m:
	Instead of fixing some if-then-elses, delete the predicates containing
	them, since they aren't used, and (as pointed out by the new option)
	would need considerable other fixing if they were ever needed again.

compiler/lp_rational.m:
	Add prefixes to the names of the function symbols on some types,
	since without those prefixes, it was hard to figure out what type
	the switch corresponding to an old if-then-else was switching on.

tests/invalid/reserve_tag.err_exp:
	Expect a new, improved error message.
2007-11-23 07:36:01 +00:00

1327 lines
54 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2002-2007 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: interval.m.
% Author: zs.
%
% This module contains a predicate to build up interval information for a
% procedure; in particular the start and end points of intervals and the set
% of variables needed in that interval. It also contains a procedure to
% insert deconstruction unifications into a goal, given a map of insertions
% to make after particular anchors. More detailed information is in
% stack_opt.m, from where this code was extracted.
%
% A description of intervals is in the paper "Using the heap to eliminate
% stack accesses" by Zoltan Somogyi and Peter Stuckey:
%
% Definition 3: An interval is a sequence of atomic goals delimited by a
% left-right pair of anchors, satisfying the property that if forward
% execution starts at the left anchor and continues without encountering
% failure (which would initiate backtracking, i.e. backward execution),
% the next anchor it reaches is the right anchor of the pair. We
% consider a call to be part of the atomic goals of the interval only if
% the call site is the right anchor of the interval, not the left anchor.
%
%-----------------------------------------------------------------------------%
:- module backend_libs.interval.
:- interface.
:- import_module hlds.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_module.
:- import_module mdbcomp.
:- import_module mdbcomp.program_representation.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module bool.
:- import_module counter.
:- import_module io.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module set.
%-----------------------------------------------------------------------------%
:- type save_point_type
---> save_point_call_site
; save_point_resume_point.
:- type save_point
---> save_point(
save_point_type,
goal_path
).
:- type branch_construct
---> branch_ite
; branch_disj
; branch_switch
; branch_neg
; branch_par_conj.
:- type resume_save_status
---> has_resume_save
; has_no_resume_save.
:- type anchor
---> anchor_proc_start
; anchor_proc_end
; anchor_branch_start(branch_construct, goal_path)
; anchor_cond_then(goal_path)
; anchor_branch_end(branch_construct, goal_path)
; anchor_call_site(goal_path).
:- type interval_id
---> interval_id(int).
:- type branch_end_info
---> branch_end_info(
flushed_after_branch :: set(prog_var),
accessed_after_branch :: set(prog_var),
interval_after_branch :: interval_id
).
:- type insert_spec
---> insert_spec(
hlds_goal,
set(prog_var)
).
:- type insert_map == map(anchor, list(insert_spec)).
:- type anchor_follow_info
---> anchor_follow_info(
set(prog_var),
set(interval_id)
).
:- type interval_params
---> interval_params(
module_info :: module_info,
var_types :: vartypes,
at_most_zero_calls :: bool
).
:- type interval_info
---> interval_info(
interval_params :: interval_params,
flushed_later :: set(prog_var),
accessed_later :: set(prog_var),
branch_resume_map :: map(goal_path, resume_save_status),
branch_end_map :: map(goal_path, branch_end_info),
cond_end_map :: map(goal_path, interval_id),
cur_interval :: interval_id,
interval_counter :: counter,
open_intervals :: set(interval_id),
anchor_follow_map :: map(anchor, anchor_follow_info),
model_non_anchors :: set(anchor),
interval_start :: map(interval_id, anchor),
interval_end :: map(interval_id, anchor),
interval_succ :: map(interval_id, list(interval_id)),
interval_vars :: map(interval_id, set(prog_var)),
interval_delvars :: map(interval_id, list(set(prog_var)))
).
:- type maybe_needs_flush
---> needs_flush
; doesnt_need_flush.
:- typeclass build_interval_info_acc(T) where [
pred use_cell(prog_var::in, list(prog_var)::in, cons_id::in, hlds_goal::in,
interval_info::in, interval_info::out, T::in, T::out) is det
].
:- pred build_interval_info_in_goal(hlds_goal::in, interval_info::in,
interval_info::out, T::in, T::out) is det <= build_interval_info_acc(T).
:- pred record_interval_vars(interval_id::in, list(prog_var)::in,
interval_info::in, interval_info::out) is det.
:- pred delete_interval_vars(interval_id::in, set(prog_var)::in,
set(prog_var)::out, interval_info::in, interval_info::out) is det.
:- type rename_map == map(prog_var, prog_var).
:- pred record_decisions_in_goal(hlds_goal::in, hlds_goal::out,
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
rename_map::in, rename_map::out, insert_map::in,
maybe(goal_feature)::in) is det.
:- pred make_inserted_goal(prog_varset::in, prog_varset::out,
vartypes::in, vartypes::out, rename_map::in, rename_map::out,
insert_spec::in, maybe(goal_feature)::in, hlds_goal::out) is det.
% The final RenameMap may ask for some of the head variables to be renamed.
% Doing so is inconvenient, e.g. because the debugger wants head variables
% to have names of a fixed form. Instead, we exploit the fact that the
% transformation does not care about actual variable names or even numbers;
% all it cares about wrt renaming is that the variables it has renamed
% apart should stay renamed apart. We therefore swap the roles of the
% original and the renamed variable in the goal representing the procedure
% body. The resulting procedure definition will be isomorphic to the one
% we would have get by applying the original renaming to the headvars.
%
:- pred apply_headvar_correction(set(prog_var)::in, rename_map::in,
hlds_goal::in, hlds_goal::out) is det.
:- pred dump_interval_info(interval_info::in, io::di, io::uo) is det.
:- pred write_int_list(list(int)::in, io::di, io::uo) is det.
:- func interval_id_to_int(interval_id) = int.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module check_hlds. % needed for type_util, mode_util
:- import_module check_hlds.inst_match.
:- import_module check_hlds.mode_util.
:- import_module hlds.arg_info.
:- import_module hlds.code_model.
:- import_module hlds.goal_util.
:- import_module hlds.hlds_llds.
:- import_module hlds.instmap.
:- import_module libs.
:- import_module libs.compiler_util.
:- import_module ll_backend.
:- import_module ll_backend.call_gen.
:- import_module assoc_list.
:- import_module pair.
:- import_module svmap.
:- import_module svset.
:- import_module svvarset.
:- import_module term.
:- import_module varset.
%-----------------------------------------------------------------------------%
build_interval_info_in_goal(hlds_goal(GoalExpr, GoalInfo), !IntervalInfo,
!Acc) :-
(
GoalExpr = conj(ConjType, Goals),
build_interval_info_in_conj(Goals, ConjType, !IntervalInfo, !Acc)
;
GoalExpr = disj(Goals),
(
Goals = [FirstDisjunct | _],
reached_branch_end(GoalInfo, yes(FirstDisjunct), branch_disj,
StartAnchor, EndAnchor, BeforeId, AfterId,
MaybeResumeVars, !IntervalInfo, !Acc),
build_interval_info_in_disj(Goals, doesnt_need_flush,
StartAnchor, EndAnchor, BeforeId, AfterId,
OpenIntervals, !IntervalInfo, !Acc),
leave_branch_start(branch_disj, StartAnchor, BeforeId,
MaybeResumeVars, OpenIntervals, !IntervalInfo)
;
Goals = [],
% We could reset the set of variables in the current interval
% to the empty set, since any variable accesses after a fail
% goal (which is what an empty disjunction represent) will not
% be executed at runtime. However, simplify should have removed
% any goals in the current branch from after the fail, so the
% set of variables in the current interval will already be
% the empty set.
no_open_intervals(!IntervalInfo)
)
;
GoalExpr = switch(Var, _Det, Cases),
reached_branch_end(GoalInfo, no, branch_switch,
StartAnchor, EndAnchor, BeforeId, AfterId, MaybeResumeVars,
!IntervalInfo, !Acc),
build_interval_info_in_cases(Cases, StartAnchor, EndAnchor,
BeforeId, AfterId, OpenIntervalsList, !IntervalInfo, !Acc),
OpenIntervals = set.union_list(OpenIntervalsList),
leave_branch_start(branch_switch, StartAnchor, BeforeId,
MaybeResumeVars, OpenIntervals, !IntervalInfo),
require_in_regs([Var], !IntervalInfo),
require_access([Var], !IntervalInfo)
;
GoalExpr = negation(SubGoal),
reached_branch_end(GoalInfo, yes(SubGoal), branch_neg,
StartAnchor, EndAnchor, BeforeId, AfterId, MaybeResumeVars,
!IntervalInfo, !Acc),
enter_branch_tail(EndAnchor, AfterId, !IntervalInfo),
build_interval_info_in_goal(SubGoal, !IntervalInfo, !Acc),
reached_branch_start(needs_flush, StartAnchor, BeforeId,
OpenIntervals, !IntervalInfo, !Acc),
leave_branch_start(branch_neg, StartAnchor, BeforeId, MaybeResumeVars,
OpenIntervals, !IntervalInfo)
;
GoalExpr = if_then_else(_, Cond, Then, Else),
reached_branch_end(GoalInfo, yes(Cond), branch_ite,
StartAnchor, EndAnchor, BeforeId, AfterId, MaybeResumeVars,
!IntervalInfo, !Acc),
enter_branch_tail(EndAnchor, AfterId, !IntervalInfo),
build_interval_info_in_goal(Then, !IntervalInfo, !Acc),
reached_cond_then(GoalInfo, !IntervalInfo),
build_interval_info_in_goal(Cond, !IntervalInfo, !Acc),
reached_branch_start(doesnt_need_flush, StartAnchor, BeforeId,
CondOpenIntervals, !IntervalInfo, !Acc),
enter_branch_tail(EndAnchor, AfterId, !IntervalInfo),
build_interval_info_in_goal(Else, !IntervalInfo, !Acc),
reached_branch_start(needs_flush, StartAnchor, BeforeId,
_ElseOpenIntervals, !IntervalInfo, !Acc),
leave_branch_start(branch_ite, StartAnchor, BeforeId, MaybeResumeVars,
CondOpenIntervals, !IntervalInfo)
;
GoalExpr = scope(_Reason, SubGoal),
build_interval_info_in_goal(SubGoal, !IntervalInfo, !Acc)
;
GoalExpr = generic_call(GenericCall, ArgVars, ArgModes, _Detism),
goal_info_get_maybe_need_across_call(GoalInfo, MaybeNeedAcrossCall),
IntParams = !.IntervalInfo ^ interval_params,
VarTypes = IntParams ^ var_types,
list.map(map.lookup(VarTypes), ArgVars, ArgTypes),
ModuleInfo = IntParams ^ module_info,
arg_info.compute_in_and_out_vars(ModuleInfo, ArgVars,
ArgModes, ArgTypes, InputArgs, _OutputArgs),
% Casts are generated inline.
(
GenericCall = cast(_),
require_in_regs(InputArgs, !IntervalInfo),
require_access(InputArgs, !IntervalInfo)
;
( GenericCall = higher_order(_, _, _, _)
; GenericCall = class_method(_, _, _, _)
; GenericCall = event_call(_)
),
module_info_get_globals(ModuleInfo, Globals),
call_gen.generic_call_info(Globals, GenericCall,
length(InputArgs), _, GenericVarsArgInfos, _, _),
assoc_list.keys(GenericVarsArgInfos, GenericVars),
list.append(GenericVars, InputArgs, Inputs),
build_interval_info_at_call(Inputs, MaybeNeedAcrossCall, GoalInfo,
!IntervalInfo, !Acc)
)
;
GoalExpr = plain_call(PredId, ProcId, ArgVars, Builtin, _, _),
IntParams = !.IntervalInfo ^ interval_params,
ModuleInfo = IntParams ^ module_info,
module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
_PredInfo, ProcInfo),
VarTypes = IntParams ^ var_types,
arg_info.partition_proc_call_args(ProcInfo, VarTypes,
ModuleInfo, ArgVars, InputArgs, _, _),
set.to_sorted_list(InputArgs, Inputs),
(
Builtin = inline_builtin,
require_in_regs(Inputs, !IntervalInfo),
require_access(Inputs, !IntervalInfo)
;
( Builtin = out_of_line_builtin
; Builtin = not_builtin
),
goal_info_get_maybe_need_across_call(GoalInfo,
MaybeNeedAcrossCall),
build_interval_info_at_call(Inputs, MaybeNeedAcrossCall, GoalInfo,
!IntervalInfo, !Acc)
)
;
GoalExpr = call_foreign_proc(_Attributes, PredId, ProcId,
Args, ExtraArgs, _MaybeTraceRuntimeCond, _PragmaCode),
IntParams = !.IntervalInfo ^ interval_params,
ModuleInfo = IntParams ^ module_info,
module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
_PredInfo, ProcInfo),
VarTypes = IntParams ^ var_types,
ArgVars = list.map(foreign_arg_var, Args),
ExtraVars = list.map(foreign_arg_var, ExtraArgs),
arg_info.partition_proc_call_args(ProcInfo, VarTypes,
ModuleInfo, ArgVars, InputArgVarSet, _, _),
set.to_sorted_list(InputArgVarSet, InputArgVars),
list.append(InputArgVars, ExtraVars, InputVars),
(
goal_info_maybe_get_maybe_need_across_call(GoalInfo,
MaybeNeedAcrossCall),
MaybeNeedAcrossCall = yes(_)
->
build_interval_info_at_call(InputVars, MaybeNeedAcrossCall,
GoalInfo, !IntervalInfo, !Acc)
;
require_in_regs(InputVars, !IntervalInfo),
require_access(InputVars, !IntervalInfo)
)
;
GoalExpr = unify(_, _, _, Unification, _),
(
Unification = construct(CellVar, _ConsId, ArgVars, _,
HowToConstruct, _, _),
(
HowToConstruct = reuse_cell(_),
unexpected(this_file, "build_interval_info_in_goal: reuse")
;
% XXX Temporary for the time being.
HowToConstruct = construct_in_region(_),
unexpected(this_file,
"build_interval_info_in_goal: construct in region")
;
( HowToConstruct = construct_statically(_)
; HowToConstruct = construct_dynamically
)
),
require_in_regs(ArgVars, !IntervalInfo),
require_access([CellVar | ArgVars], !IntervalInfo)
% use_cell(CellVar, ArgVars, ConsId, GoalExpr - GoalInfo,
% !IntervalInfo)
% We cannot use such cells, because some of the ArgVars
% may need to be saved on the stack before this construction.
;
Unification = deconstruct(CellVar, ConsId, ArgVars, ArgModes,
_, _),
IntParams = !.IntervalInfo ^ interval_params,
ModuleInfo = IntParams ^ module_info,
( shared_left_to_right_deconstruct(ModuleInfo, ArgModes) ->
Goal = hlds_goal(GoalExpr, GoalInfo),
use_cell(CellVar, ArgVars, ConsId, Goal, !IntervalInfo, !Acc)
;
true
),
require_in_regs([CellVar], !IntervalInfo),
require_access([CellVar | ArgVars], !IntervalInfo)
;
Unification = assign(ToVar, FromVar),
require_in_regs([FromVar], !IntervalInfo),
require_access([FromVar, ToVar], !IntervalInfo)
;
Unification = simple_test(Var1, Var2),
require_in_regs([Var1, Var2], !IntervalInfo),
require_access([Var1, Var2], !IntervalInfo)
;
Unification = complicated_unify(_, _, _),
unexpected(this_file,
"build_interval_info_in_goal: complicated_unify")
)
;
GoalExpr = shorthand(_),
unexpected(this_file, "shorthand in build_interval_info_in_goal")
).
:- pred shared_left_to_right_deconstruct(module_info::in, list(uni_mode)::in)
is semidet.
shared_left_to_right_deconstruct(_, []).
shared_left_to_right_deconstruct(ModuleInfo, [ArgMode | ArgsModes]) :-
ArgMode = ((InitCell - InitArg) -> (FinalCell - FinalArg)),
mode_is_fully_input(ModuleInfo, InitCell -> FinalCell),
mode_is_output(ModuleInfo, InitArg -> FinalArg),
inst_is_not_partly_unique(ModuleInfo, FinalCell),
inst_is_not_partly_unique(ModuleInfo, FinalArg),
shared_left_to_right_deconstruct(ModuleInfo, ArgsModes).
%-----------------------------------------------------------------------------%
:- pred build_interval_info_at_call(list(prog_var)::in,
maybe(need_across_call)::in, hlds_goal_info::in,
interval_info::in, interval_info::out, T::in, T::out) is det
<= build_interval_info_acc(T).
build_interval_info_at_call(Inputs, MaybeNeedAcrossCall, GoalInfo,
!IntervalInfo, !Acc) :-
(
MaybeNeedAcrossCall = yes(NeedAcrossCall),
NeedAcrossCall = need_across_call(ForwardVars, ResumeVars,
NondetLiveVars),
VarsOnStack0 = set.union_list([ForwardVars, ResumeVars,
NondetLiveVars]),
GoalPath = goal_info_get_goal_path(GoalInfo),
CallAnchor = anchor_call_site(GoalPath),
get_cur_interval(AfterCallId, !.IntervalInfo),
new_interval_id(BeforeCallId, !IntervalInfo),
record_interval_start(AfterCallId, CallAnchor, !IntervalInfo),
record_interval_end(BeforeCallId, CallAnchor, !IntervalInfo),
InstMapDelta = goal_info_get_instmap_delta(GoalInfo),
IntParams = !.IntervalInfo ^ interval_params,
(
( instmap_delta_is_reachable(InstMapDelta)
; IntParams ^ at_most_zero_calls = no
)
->
record_interval_succ(BeforeCallId, AfterCallId, !IntervalInfo),
VarsOnStack = VarsOnStack0
;
% If the call cannot succeed, then execution cannot
% get from BeforeCallId to AfterCallId.
record_interval_no_succ(BeforeCallId, !IntervalInfo),
VarsOnStack = set.init
),
set_cur_interval(BeforeCallId, !IntervalInfo),
assign_open_intervals_to_anchor(CallAnchor, !IntervalInfo),
CodeModel = goal_info_get_code_model(GoalInfo),
(
CodeModel = model_non,
record_model_non_anchor(CallAnchor, !IntervalInfo)
;
( CodeModel = model_det
; CodeModel = model_semi
)
),
one_open_interval(BeforeCallId, !IntervalInfo),
require_flushed(VarsOnStack, !IntervalInfo),
require_in_regs(Inputs, !IntervalInfo),
require_access(Inputs, !IntervalInfo)
;
MaybeNeedAcrossCall = no,
unexpected(this_file,
"build_interval_info_at_call: no need across call")
).
%-----------------------------------------------------------------------------%
:- pred build_interval_info_in_conj(list(hlds_goal)::in, conj_type::in,
interval_info::in, interval_info::out, T::in, T::out) is det
<= build_interval_info_acc(T).
build_interval_info_in_conj([], _, !IntervalInfo, !Acc).
build_interval_info_in_conj([Goal | Goals], ConjType, !IntervalInfo, !Acc) :-
% XXX zs: I am not sure that passing interval_info from the first goal
% to the rest is OK when ConjType = parallel_conj. Maybe we should pass
% the initial interval_info to all the conjuncts, and then merge the
% resulting interval_infos.
build_interval_info_in_conj(Goals, ConjType, !IntervalInfo, !Acc),
build_interval_info_in_goal(Goal, !IntervalInfo, !Acc).
:- pred build_interval_info_in_disj(list(hlds_goal)::in, maybe_needs_flush::in,
anchor::in, anchor::in, interval_id::in, interval_id::in,
set(interval_id)::out, interval_info::in, interval_info::out,
T::in, T::out) is det <= build_interval_info_acc(T).
build_interval_info_in_disj([], _, _, _, _, _, set.init, !IntervalInfo, !Acc).
build_interval_info_in_disj([Goal | Goals], MaybeNeedsFlush,
StartAnchor, EndAnchor, BeforeId, AfterId, OpenIntervals,
!IntervalInfo, !Acc) :-
enter_branch_tail(EndAnchor, AfterId, !IntervalInfo),
build_interval_info_in_goal(Goal, !IntervalInfo, !Acc),
reached_branch_start(MaybeNeedsFlush, StartAnchor, BeforeId,
OpenIntervals, !IntervalInfo, !Acc),
build_interval_info_in_disj(Goals, needs_flush, StartAnchor, EndAnchor,
BeforeId, AfterId, _OpenIntervals, !IntervalInfo, !Acc).
:- pred build_interval_info_in_cases(list(case)::in,
anchor::in, anchor::in, interval_id::in, interval_id::in,
list(set(interval_id))::out, interval_info::in, interval_info::out,
T::in, T::out) is det <= build_interval_info_acc(T).
build_interval_info_in_cases([], _, _, _, _, [], !IntervalInfo, !Acc).
build_interval_info_in_cases([case(_Var, Goal) | Cases],
StartAnchor, EndAnchor, BeforeId, AfterId,
[OpenIntervals | OpenIntervalsList], !IntervalInfo, !Acc) :-
enter_branch_tail(EndAnchor, AfterId, !IntervalInfo),
build_interval_info_in_goal(Goal, !IntervalInfo, !Acc),
reached_branch_start(doesnt_need_flush, StartAnchor, BeforeId,
OpenIntervals, !IntervalInfo, !Acc),
build_interval_info_in_cases(Cases, StartAnchor, EndAnchor,
BeforeId, AfterId, OpenIntervalsList, !IntervalInfo, !Acc).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- pred reached_branch_end(hlds_goal_info::in, maybe(hlds_goal)::in,
branch_construct::in, anchor::out, anchor::out,
interval_id::out, interval_id::out, maybe(set(prog_var))::out,
interval_info::in, interval_info::out, T::in, T::out) is det
<= build_interval_info_acc(T).
reached_branch_end(GoalInfo, MaybeResumeGoal, Construct,
StartAnchor, EndAnchor, BeforeIntervalId, AfterIntervalId,
MaybeResumeVars, !IntervalInfo, !Acc) :-
GoalPath = goal_info_get_goal_path(GoalInfo),
record_branch_end_info(GoalPath, !IntervalInfo),
(
MaybeResumeGoal = yes(hlds_goal(_ResumeGoalExpr, ResumeGoalInfo)),
goal_info_maybe_get_resume_point(ResumeGoalInfo, ResumePoint),
ResumePoint = resume_point(ResumeVars, ResumeLocs),
ResumeLocs \= resume_locs_orig_only
->
HasResumeSave = has_resume_save,
MaybeResumeVars = yes(ResumeVars)
;
HasResumeSave = has_no_resume_save,
MaybeResumeVars = no
),
record_branch_resume(GoalPath, HasResumeSave, !IntervalInfo),
( goal_info_maybe_get_store_map(GoalInfo, StoreMap) ->
map.sorted_keys(StoreMap, StoreMapVarList),
set.sorted_list_to_set(StoreMapVarList, StoreMapVars),
require_flushed(StoreMapVars, !IntervalInfo)
;
unexpected(this_file, "reached_branch_end: no store map")
),
EndAnchor = anchor_branch_end(Construct, GoalPath),
StartAnchor = anchor_branch_start(Construct, GoalPath),
assign_open_intervals_to_anchor(EndAnchor, !IntervalInfo),
CodeModel = goal_info_get_code_model(GoalInfo),
(
CodeModel = model_non,
record_model_non_anchor(EndAnchor, !IntervalInfo)
;
( CodeModel = model_det
; CodeModel = model_semi
)
),
no_open_intervals(!IntervalInfo),
get_cur_interval(AfterIntervalId, !.IntervalInfo),
record_interval_start(AfterIntervalId, EndAnchor, !IntervalInfo),
new_interval_id(BeforeIntervalId, !IntervalInfo).
:- pred enter_branch_tail(anchor::in, interval_id::in,
interval_info::in, interval_info::out) is det.
enter_branch_tail(EndAnchor, AfterId, !IntervalInfo) :-
new_interval_id(BranchTailId, !IntervalInfo),
record_interval_end(BranchTailId, EndAnchor, !IntervalInfo),
record_interval_succ(BranchTailId, AfterId, !IntervalInfo),
set_cur_interval(BranchTailId, !IntervalInfo),
one_open_interval(BranchTailId, !IntervalInfo).
:- pred reached_branch_start(maybe_needs_flush::in, anchor::in,
interval_id::in, set(interval_id)::out, interval_info::in,
interval_info::out, T::in, T::out) is det <= build_interval_info_acc(T).
reached_branch_start(MaybeNeedsFlush, StartAnchor, BeforeId, OpenIntervals,
!IntervalInfo, !Acc) :-
get_cur_interval(BranchStartId, !.IntervalInfo),
record_interval_start(BranchStartId, StartAnchor, !IntervalInfo),
record_interval_succ(BeforeId, BranchStartId, !IntervalInfo),
get_open_intervals(!.IntervalInfo, OpenIntervals),
(
MaybeNeedsFlush = doesnt_need_flush
;
MaybeNeedsFlush = needs_flush,
assign_open_intervals_to_anchor(StartAnchor, !IntervalInfo)
).
:- pred reached_cond_then(hlds_goal_info::in, interval_info::in,
interval_info::out) is det.
reached_cond_then(GoalInfo, !IntervalInfo) :-
GoalPath = goal_info_get_goal_path(GoalInfo),
record_cond_end(GoalPath, !IntervalInfo),
get_cur_interval(ThenStartId, !.IntervalInfo),
record_interval_start(ThenStartId, CondThenAnchor, !IntervalInfo),
new_interval_id(CondTailId, !IntervalInfo),
CondThenAnchor = anchor_cond_then(GoalPath),
record_interval_end(CondTailId, CondThenAnchor, !IntervalInfo),
record_interval_succ(CondTailId, ThenStartId, !IntervalInfo),
set_cur_interval(CondTailId, !IntervalInfo),
get_open_intervals(!.IntervalInfo, OpenIntervals0),
svset.insert(CondTailId, OpenIntervals0, OpenIntervals),
set_open_intervals(OpenIntervals, !IntervalInfo).
:- pred leave_branch_start(branch_construct::in, anchor::in, interval_id::in,
maybe(set(prog_var))::in, set(interval_id)::in,
interval_info::in, interval_info::out) is det.
leave_branch_start(_BranchConstruct, StartArchor, BeforeId, MaybeResumeVars,
OpenIntervals, !IntervalInfo) :-
record_interval_end(BeforeId, StartArchor, !IntervalInfo),
(
MaybeResumeVars = yes(ResumeVars),
require_flushed(ResumeVars, !IntervalInfo)
;
MaybeResumeVars = no
),
set_cur_interval(BeforeId, !IntervalInfo),
set_open_intervals(OpenIntervals, !IntervalInfo).
:- pred get_open_intervals(interval_info::in, set(interval_id)::out) is det.
get_open_intervals(IntervalInfo, OpenIntervals) :-
OpenIntervals = IntervalInfo ^ open_intervals.
:- pred set_open_intervals(set(interval_id)::in,
interval_info::in, interval_info::out) is det.
set_open_intervals(OpenIntervals, !IntervalInfo) :-
!:IntervalInfo = !.IntervalInfo ^ open_intervals := OpenIntervals.
:- pred no_open_intervals(interval_info::in, interval_info::out) is det.
no_open_intervals(!IntervalInfo) :-
!:IntervalInfo = !.IntervalInfo ^ open_intervals := set.init.
:- pred one_open_interval(interval_id::in, interval_info::in,
interval_info::out) is det.
one_open_interval(IntervalId, !IntervalInfo) :-
!:IntervalInfo = !.IntervalInfo ^ open_intervals :=
set.make_singleton_set(IntervalId).
:- pred assign_open_intervals_to_anchor(anchor::in,
interval_info::in, interval_info::out) is det.
assign_open_intervals_to_anchor(Anchor, !IntervalInfo) :-
AnchorFollowMap0 = !.IntervalInfo ^ anchor_follow_map,
IntervalVarMap = !.IntervalInfo ^ interval_vars,
CurOpenIntervals = !.IntervalInfo ^ open_intervals,
set.fold(gather_interval_vars(IntervalVarMap), CurOpenIntervals,
set.init, CurOpenIntervalVars),
( map.search(AnchorFollowMap0, Anchor, AnchorFollowInfo0) ->
AnchorFollowInfo0 =
anchor_follow_info(OpenIntervalVars0, OpenIntervals0),
OpenIntervalVars = set.union(OpenIntervalVars0, CurOpenIntervalVars),
OpenIntervals = set.union(OpenIntervals0, CurOpenIntervals),
AnchorFollowInfo =
anchor_follow_info(OpenIntervalVars, OpenIntervals),
svmap.det_update(Anchor, AnchorFollowInfo,
AnchorFollowMap0, AnchorFollowMap)
;
AnchorFollowInfo =
anchor_follow_info(CurOpenIntervalVars, CurOpenIntervals),
svmap.det_insert(Anchor, AnchorFollowInfo,
AnchorFollowMap0, AnchorFollowMap)
),
!:IntervalInfo = !.IntervalInfo ^ anchor_follow_map := AnchorFollowMap.
:- pred gather_interval_vars(map(interval_id, set(prog_var))::in,
interval_id::in, set(prog_var)::in, set(prog_var)::out) is det.
gather_interval_vars(IntervalVarMap, IntervalId, !OpenIntervalVars) :-
map.lookup(IntervalVarMap, IntervalId, IntervalVars),
!:OpenIntervalVars = set.union(!.OpenIntervalVars, IntervalVars).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- pred get_cur_interval(interval_id::out, interval_info::in) is det.
get_cur_interval(IntervalInfo ^ cur_interval, IntervalInfo).
:- pred set_cur_interval(interval_id::in, interval_info::in,
interval_info::out) is det.
set_cur_interval(CurInterval, IntervalInfo,
IntervalInfo ^ cur_interval := CurInterval).
:- pred new_interval_id(interval_id::out, interval_info::in,
interval_info::out) is det.
new_interval_id(Id, !IntervalInfo) :-
Counter0 = !.IntervalInfo ^ interval_counter,
IntervalVars0 = !.IntervalInfo ^ interval_vars,
counter.allocate(Num, Counter0, Counter),
Id = interval_id(Num),
svmap.det_insert(Id, set.init, IntervalVars0, IntervalVars),
!:IntervalInfo = !.IntervalInfo ^ interval_counter := Counter,
!:IntervalInfo = !.IntervalInfo ^ interval_vars := IntervalVars.
:- pred record_branch_end_info(goal_path::in,
interval_info::in, interval_info::out) is det.
record_branch_end_info(GoalPath, !IntervalInfo) :-
FlushedLater = !.IntervalInfo ^ flushed_later,
AccessedLater = !.IntervalInfo ^ accessed_later,
CurInterval = !.IntervalInfo ^ cur_interval,
BranchEndMap0 = !.IntervalInfo ^ branch_end_map,
BranchEndInfo = branch_end_info(FlushedLater, AccessedLater, CurInterval),
svmap.det_insert(GoalPath, BranchEndInfo, BranchEndMap0, BranchEndMap),
!:IntervalInfo = !.IntervalInfo ^ branch_end_map := BranchEndMap.
:- pred record_cond_end(goal_path::in, interval_info::in, interval_info::out)
is det.
record_cond_end(GoalPath, !IntervalInfo) :-
CurInterval = !.IntervalInfo ^ cur_interval,
CondEndMap0 = !.IntervalInfo ^ cond_end_map,
svmap.det_insert(GoalPath, CurInterval, CondEndMap0, CondEndMap),
!:IntervalInfo = !.IntervalInfo ^ cond_end_map := CondEndMap.
:- pred record_interval_end(interval_id::in, anchor::in,
interval_info::in, interval_info::out) is det.
record_interval_end(Id, End, !IntervalInfo) :-
EndMap0 = !.IntervalInfo ^ interval_end,
svmap.det_insert(Id, End, EndMap0, EndMap),
!:IntervalInfo = !.IntervalInfo ^ interval_end := EndMap.
:- pred record_interval_start(interval_id::in, anchor::in,
interval_info::in, interval_info::out) is det.
record_interval_start(Id, Start, !IntervalInfo) :-
StartMap0 = !.IntervalInfo ^ interval_start,
svmap.det_insert(Id, Start, StartMap0, StartMap),
!:IntervalInfo = !.IntervalInfo ^ interval_start := StartMap.
:- pred record_interval_succ(interval_id::in, interval_id::in,
interval_info::in, interval_info::out) is det.
record_interval_succ(Id, Succ, !IntervalInfo) :-
SuccMap0 = !.IntervalInfo ^ interval_succ,
( map.search(SuccMap0, Id, Succ0) ->
svmap.det_update(Id, [Succ | Succ0], SuccMap0, SuccMap)
;
svmap.det_insert(Id, [Succ], SuccMap0, SuccMap)
),
!:IntervalInfo = !.IntervalInfo ^ interval_succ := SuccMap.
:- pred record_interval_no_succ(interval_id::in,
interval_info::in, interval_info::out) is det.
record_interval_no_succ(Id, !IntervalInfo) :-
SuccMap0 = !.IntervalInfo ^ interval_succ,
( map.search(SuccMap0, Id, _Succ0) ->
unexpected(this_file, "record_interval_no_succ: already in succ map")
;
svmap.det_insert(Id, [], SuccMap0, SuccMap)
),
!:IntervalInfo = !.IntervalInfo ^ interval_succ := SuccMap.
record_interval_vars(Id, NewVars, !IntervalInfo) :-
VarsMap0 = !.IntervalInfo ^ interval_vars,
( map.search(VarsMap0, Id, Vars0) ->
svset.insert_list(NewVars, Vars0, Vars),
svmap.det_update(Id, Vars, VarsMap0, VarsMap)
;
set.list_to_set(NewVars, Vars),
svmap.det_insert(Id, Vars, VarsMap0, VarsMap)
),
!:IntervalInfo = !.IntervalInfo ^ interval_vars := VarsMap.
delete_interval_vars(Id, ToDeleteVars, DeletedVars, !IntervalInfo) :-
VarsMap0 = !.IntervalInfo ^ interval_vars,
map.lookup(VarsMap0, Id, Vars0),
DeletedVars = set.intersect(Vars0, ToDeleteVars),
Vars = set.difference(Vars0, DeletedVars),
svmap.det_update(Id, Vars, VarsMap0, VarsMap),
!:IntervalInfo = !.IntervalInfo ^ interval_vars := VarsMap,
% The deletions are recorded only for debugging. The algorithm itself
% does not need this information to be recorded.
DeleteMap0 = !.IntervalInfo ^ interval_delvars,
( map.search(DeleteMap0, Id, Deletions0) ->
Deletions = [DeletedVars | Deletions0],
svmap.det_update(Id, Deletions, DeleteMap0, DeleteMap)
;
Deletions = [DeletedVars],
svmap.det_insert(Id, Deletions, DeleteMap0, DeleteMap)
),
!:IntervalInfo = !.IntervalInfo ^ interval_delvars := DeleteMap.
:- pred require_in_regs(list(prog_var)::in, interval_info::in,
interval_info::out) is det.
require_in_regs(Vars, !IntervalInfo) :-
CurIntervalId = !.IntervalInfo ^ cur_interval,
record_interval_vars(CurIntervalId, Vars, !IntervalInfo).
:- pred require_flushed(set(prog_var)::in,
interval_info::in, interval_info::out) is det.
require_flushed(Vars, !IntervalInfo) :-
FlushedLater0 = !.IntervalInfo ^ flushed_later,
FlushedLater = set.union(FlushedLater0, Vars),
!:IntervalInfo = !.IntervalInfo ^ flushed_later := FlushedLater.
:- pred require_access(list(prog_var)::in,
interval_info::in, interval_info::out) is det.
require_access(Vars, !IntervalInfo) :-
AccessedLater0 = !.IntervalInfo ^ accessed_later,
svset.insert_list(Vars, AccessedLater0, AccessedLater),
!:IntervalInfo = !.IntervalInfo ^ accessed_later := AccessedLater.
:- pred record_branch_resume(goal_path::in, resume_save_status::in,
interval_info::in, interval_info::out) is det.
record_branch_resume(GoalPath, ResumeSaveStatus, !IntervalInfo) :-
BranchResumeMap0 = !.IntervalInfo ^ branch_resume_map,
svmap.det_insert(GoalPath, ResumeSaveStatus,
BranchResumeMap0, BranchResumeMap),
!:IntervalInfo = !.IntervalInfo ^ branch_resume_map := BranchResumeMap.
:- pred record_model_non_anchor(anchor::in, interval_info::in,
interval_info::out) is det.
record_model_non_anchor(Anchor, !IntervalInfo) :-
ModelNonAnchors0 = !.IntervalInfo ^ model_non_anchors,
svset.insert(Anchor, ModelNonAnchors0, ModelNonAnchors),
!:IntervalInfo = !.IntervalInfo ^ model_non_anchors := ModelNonAnchors.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- type var_info
---> var_info(
varset :: prog_varset,
vartypes :: vartypes
).
record_decisions_in_goal(!Goal, VarSet0, VarSet, VarTypes0, VarTypes,
!VarRename, InsertMap, MaybeFeature) :-
record_decisions_in_goal(!Goal, var_info(VarSet0, VarTypes0),
var_info(VarSet, VarTypes), !VarRename, InsertMap, MaybeFeature).
:- pred record_decisions_in_goal(hlds_goal::in, hlds_goal::out,
var_info::in, var_info::out, rename_map::in, rename_map::out,
insert_map::in, maybe(goal_feature)::in) is det.
record_decisions_in_goal(Goal0, Goal, !VarInfo, !VarRename, InsertMap,
MaybeFeature) :-
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
(
GoalExpr0 = conj(ConjType, Goals0),
record_decisions_in_conj(Goals0, Goals, !VarInfo, !VarRename,
ConjType, InsertMap, MaybeFeature),
GoalExpr = conj(ConjType, Goals),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = disj(Goals0),
construct_anchors(branch_disj, Goal0, StartAnchor, EndAnchor),
(
Goals0 = [FirstGoal0 | LaterGoals0],
record_decisions_in_goal(FirstGoal0, FirstGoal, !VarInfo,
!.VarRename, _, InsertMap, MaybeFeature),
lookup_inserts(InsertMap, StartAnchor, StartInserts),
record_decisions_in_disj(LaterGoals0, LaterGoals,
!VarInfo, !.VarRename, StartInserts, InsertMap, MaybeFeature),
Goals = [FirstGoal | LaterGoals],
Goal1 = hlds_goal(disj(Goals), GoalInfo0),
lookup_inserts(InsertMap, EndAnchor, Inserts),
insert_goals_after(Goal1, Goal, !VarInfo, !:VarRename, Inserts,
MaybeFeature)
;
Goals0 = [],
GoalExpr = disj(Goals0),
Goal = hlds_goal(GoalExpr, GoalInfo0)
)
;
GoalExpr0 = switch(Var0, Det, Cases0),
record_decisions_in_cases(Cases0, Cases, !VarInfo, !.VarRename,
InsertMap, MaybeFeature),
rename_var(need_not_rename, !.VarRename, Var0, Var),
Goal1 = hlds_goal(switch(Var, Det, Cases), GoalInfo0),
construct_anchors(branch_switch, Goal0, _StartAnchor, EndAnchor),
lookup_inserts(InsertMap, EndAnchor, Inserts),
insert_goals_after(Goal1, Goal, !VarInfo, !:VarRename, Inserts,
MaybeFeature)
;
GoalExpr0 = negation(NegGoal0),
record_decisions_in_goal(NegGoal0, NegGoal, !VarInfo, !.VarRename, _,
InsertMap, MaybeFeature),
Goal1 = hlds_goal(negation(NegGoal), GoalInfo0),
construct_anchors(branch_neg, Goal0, _StartAnchor, EndAnchor),
lookup_inserts(InsertMap, EndAnchor, Inserts),
% XXX
insert_goals_after(Goal1, Goal, !VarInfo, !:VarRename, Inserts,
MaybeFeature)
;
GoalExpr0 = if_then_else(Vars0, Cond0, Then0, Else0),
construct_anchors(branch_ite, Goal0, StartAnchor, EndAnchor),
rename_var_list(need_not_rename, !.VarRename, Vars0, Vars),
record_decisions_in_goal(Cond0, Cond, !VarInfo, !VarRename, InsertMap,
MaybeFeature),
record_decisions_in_goal(Then0, Then, !VarInfo, !.VarRename, _,
InsertMap, MaybeFeature),
lookup_inserts(InsertMap, StartAnchor, StartInserts),
make_inserted_goals(!VarInfo, map.init, VarRenameElse,
StartInserts, MaybeFeature, StartInsertGoals),
record_decisions_in_goal(Else0, Else1, !VarInfo, VarRenameElse, _,
InsertMap, MaybeFeature),
Else0 = hlds_goal(_, ElseGoalInfo0),
conj_list_to_goal(list.append(StartInsertGoals, [Else1]),
ElseGoalInfo0, Else),
Goal1 = hlds_goal(if_then_else(Vars, Cond, Then, Else), GoalInfo0),
lookup_inserts(InsertMap, EndAnchor, EndInserts),
insert_goals_after(Goal1, Goal, !VarInfo, !:VarRename, EndInserts,
MaybeFeature)
;
GoalExpr0 = scope(Reason0, SubGoal0),
(
Reason0 = exist_quant(Vars0),
rename_var_list(need_not_rename, !.VarRename, Vars0, Vars),
Reason = exist_quant(Vars)
;
Reason0 = promise_purity(_, _),
Reason = Reason0
;
Reason0 = promise_solutions(_, _),
Reason = Reason0
;
Reason0 = commit(_),
Reason = Reason0
;
Reason0 = barrier(_),
Reason = Reason0
;
Reason0 = from_ground_term(Var0),
rename_var(need_not_rename, !.VarRename, Var0, Var),
Reason = from_ground_term(Var)
;
Reason0 = trace_goal(_, _, _, _, _),
Reason = Reason0
),
record_decisions_in_goal(SubGoal0, SubGoal, !VarInfo, !VarRename,
InsertMap, MaybeFeature),
GoalExpr = scope(Reason, SubGoal),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = generic_call(GenericCall, _, _, _),
% Casts are generated inline.
(
GenericCall = cast(_),
MustHaveMap = no
;
( GenericCall = higher_order(_, _, _, _)
; GenericCall = class_method(_, _, _, _)
; GenericCall = event_call(_)
),
MustHaveMap = yes
),
record_decisions_at_call_site(Goal0, Goal, !VarInfo, !VarRename,
MustHaveMap, InsertMap, MaybeFeature)
;
GoalExpr0 = plain_call(_, _, _, Builtin, _, _),
(
Builtin = inline_builtin,
MustHaveMap = no
;
( Builtin = out_of_line_builtin
; Builtin = not_builtin
),
MustHaveMap = yes
),
record_decisions_at_call_site(Goal0, Goal, !VarInfo, !VarRename,
MustHaveMap, InsertMap, MaybeFeature)
;
GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _),
record_decisions_at_call_site(Goal0, Goal, !VarInfo,
!VarRename, no, InsertMap, MaybeFeature)
;
GoalExpr0 = unify(_, _, _, _, _),
rename_some_vars_in_goal(!.VarRename, Goal0, Goal)
;
GoalExpr0 = shorthand(_),
unexpected(this_file, "shorthand in record_decisions_in_goal")
).
%-----------------------------------------------------------------------------%
:- pred lookup_inserts(insert_map::in, anchor::in, list(insert_spec)::out)
is det.
lookup_inserts(InsertMap, Anchor, Inserts) :-
( map.search(InsertMap, Anchor, InsertsPrime) ->
Inserts = InsertsPrime
;
Inserts = []
).
:- pred insert_goals_after(hlds_goal::in, hlds_goal::out,
var_info::in, var_info::out, rename_map::out,
list(insert_spec)::in, maybe(goal_feature)::in) is det.
insert_goals_after(BranchesGoal, Goal, !VarInfo, VarRename, Inserts,
MaybeFeature) :-
make_inserted_goals(!VarInfo, map.init, VarRename, Inserts, MaybeFeature,
InsertGoals),
BranchesGoal = hlds_goal(_, BranchesGoalInfo),
conj_list_to_goal([BranchesGoal | InsertGoals], BranchesGoalInfo, Goal).
:- pred make_inserted_goals(var_info::in, var_info::out,
rename_map::in, rename_map::out, list(insert_spec)::in,
maybe(goal_feature)::in, list(hlds_goal)::out) is det.
make_inserted_goals(!VarInfo, !VarRename, [], _MaybeFeature, []).
make_inserted_goals(!VarInfo, !VarRename, [Spec | Specs], MaybeFeature,
[Goal | Goals]) :-
make_inserted_goal(!VarInfo, !VarRename, Spec, MaybeFeature, Goal),
make_inserted_goals(!VarInfo, !VarRename, Specs, MaybeFeature, Goals).
:- pred make_inserted_goal(var_info::in, var_info::out,
rename_map::in, rename_map::out, insert_spec::in,
maybe(goal_feature)::in, hlds_goal::out) is det.
make_inserted_goal(!VarInfo, !VarRename, Spec, MaybeFeature, Goal) :-
Spec = insert_spec(Goal0, VarsToExtract),
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
(
GoalExpr0 = unify(_, _, _, Unification0, _),
Unification0 = deconstruct(_, _, ArgVars, _, _, _)
->
Unification1 = Unification0 ^ deconstruct_can_fail := cannot_fail,
GoalExpr1 = GoalExpr0 ^ unify_kind := Unification1,
goal_info_set_determinism(detism_det, GoalInfo0, GoalInfo1),
(
MaybeFeature = yes(Feature),
goal_info_add_feature(Feature, GoalInfo1, GoalInfo2)
;
MaybeFeature = no,
GoalInfo2 = GoalInfo1
),
Goal2 = hlds_goal(GoalExpr1, GoalInfo2),
!.VarInfo = var_info(VarSet0, VarTypes0),
create_shadow_vars(ArgVars, VarsToExtract, VarSet0, VarSet,
VarTypes0, VarTypes, map.init, NewRename, map.init, VoidRename),
!:VarInfo = var_info(VarSet, VarTypes),
map.old_merge(!.VarRename, NewRename, !:VarRename),
% We rename the original goal.
rename_some_vars_in_goal(!.VarRename, Goal2, Goal3),
rename_some_vars_in_goal(VoidRename, Goal3, Goal)
;
unexpected(this_file, "make_inserted_goal: not a deconstruct")
).
make_inserted_goal(VarSet0, VarSet, VarTypes0, VarTypes, !RenameMap,
InsertSpec, MaybeFeature, Goal) :-
make_inserted_goal(var_info(VarSet0, VarTypes0),
var_info(VarSet, VarTypes), !RenameMap, InsertSpec,
MaybeFeature, Goal).
:- pred create_shadow_vars(list(prog_var)::in, set(prog_var)::in,
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
rename_map::in, rename_map::out, rename_map::in, rename_map::out)
is det.
create_shadow_vars([], _, !VarSet, !VarTypes, !VarRename, !VoidRename).
create_shadow_vars([Arg | Args], VarsToExtract, !VarSet, !VarTypes,
!VarRename, !VoidRename) :-
create_shadow_var(Arg, VarsToExtract, !VarSet, !VarTypes,
!VarRename, !VoidRename),
create_shadow_vars(Args, VarsToExtract, !VarSet, !VarTypes,
!VarRename, !VoidRename).
:- pred create_shadow_var(prog_var::in, set(prog_var)::in,
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
rename_map::in, rename_map::out, rename_map::in, rename_map::out) is det.
create_shadow_var(Arg, VarsToExtract, !VarSet, !VarTypes,
!VarRename, !VoidRename) :-
varset.lookup_name(!.VarSet, Arg, Name),
svvarset.new_named_var(Name, Shadow, !VarSet),
map.lookup(!.VarTypes, Arg, Type),
svmap.det_insert(Shadow, Type, !VarTypes),
( set.member(Arg, VarsToExtract) ->
svmap.det_insert(Arg, Shadow, !VarRename)
;
svmap.det_insert(Arg, Shadow, !VoidRename)
).
%-----------------------------------------------------------------------------%
:- pred record_decisions_at_call_site(hlds_goal::in, hlds_goal::out,
var_info::in, var_info::out, rename_map::in, rename_map::out,
bool::in, insert_map::in, maybe(goal_feature)::in) is det.
record_decisions_at_call_site(Goal0, Goal, !VarInfo, !VarRename,
MustHaveMap, InsertMap, MaybeFeature) :-
Goal0 = hlds_goal(_, GoalInfo0),
rename_some_vars_in_goal(!.VarRename, Goal0, Goal1),
(
goal_info_maybe_get_maybe_need_across_call(GoalInfo0,
MaybeNeedAcrossCall),
MaybeNeedAcrossCall = yes(_NeedAcrossCall)
->
GoalPath = goal_info_get_goal_path(GoalInfo0),
Anchor = anchor_call_site(GoalPath),
lookup_inserts(InsertMap, Anchor, Inserts),
insert_goals_after(Goal1, Goal, !VarInfo, !:VarRename, Inserts,
MaybeFeature)
;
(
MustHaveMap = no,
Goal = Goal1
;
MustHaveMap = yes,
unexpected(this_file, "record_decisions_at_call_site: no save map")
)
).
%-----------------------------------------------------------------------------%
:- pred record_decisions_in_conj(list(hlds_goal)::in, list(hlds_goal)::out,
var_info::in, var_info::out, rename_map::in, rename_map::out,
conj_type::in, insert_map::in, maybe(goal_feature)::in) is det.
record_decisions_in_conj([], [], !VarInfo, !VarRename, _, _, _).
record_decisions_in_conj([Goal0 | Goals0], Goals, !VarInfo, !VarRename,
ConjType, InsertMap, MaybeFeature) :-
record_decisions_in_goal(Goal0, Goal, !VarInfo, !VarRename,
InsertMap, MaybeFeature),
record_decisions_in_conj(Goals0, TailGoals, !VarInfo, !VarRename,
ConjType, InsertMap, MaybeFeature),
(
Goal = hlds_goal(conj(InnerConjType, SubGoals), _),
ConjType = InnerConjType
->
Goals = SubGoals ++ TailGoals
;
Goals = [Goal | TailGoals]
).
:- pred record_decisions_in_disj(list(hlds_goal)::in, list(hlds_goal)::out,
var_info::in, var_info::out, rename_map::in, list(insert_spec)::in,
insert_map::in, maybe(goal_feature)::in) is det.
record_decisions_in_disj([], [], !VarInfo, _, _, _, _).
record_decisions_in_disj([Goal0 | Goals0], [Goal | Goals], !VarInfo,
VarRename0, Inserts, InsertMap, MaybeFeature) :-
make_inserted_goals(!VarInfo, map.init, VarRename1,
Inserts, MaybeFeature, InsertGoals),
Goal0 = hlds_goal(_, GoalInfo0),
record_decisions_in_goal(Goal0, Goal1, !VarInfo, VarRename1, _,
InsertMap, MaybeFeature),
conj_list_to_goal(list.append(InsertGoals, [Goal1]), GoalInfo0, Goal),
record_decisions_in_disj(Goals0, Goals, !VarInfo, VarRename0,
Inserts, InsertMap, MaybeFeature).
:- pred record_decisions_in_cases(list(case)::in, list(case)::out,
var_info::in, var_info::out, rename_map::in, insert_map::in,
maybe(goal_feature)::in) is det.
record_decisions_in_cases([], [], !VarInfo, _, _, _).
record_decisions_in_cases([case(Var, Goal0) | Cases0],
[case(Var, Goal) | Cases], !VarInfo, VarRename0, InsertMap,
MaybeFeature) :-
record_decisions_in_goal(Goal0, Goal, !VarInfo, VarRename0, _,
InsertMap, MaybeFeature),
record_decisions_in_cases(Cases0, Cases, !VarInfo, VarRename0,
InsertMap, MaybeFeature).
%-----------------------------------------------------------------------------%
apply_headvar_correction(HeadVarSet, RenameMap, Goal0, Goal) :-
set.to_sorted_list(HeadVarSet, HeadVars),
build_headvar_subst(HeadVars, RenameMap, map.init, Subst),
( map.is_empty(Subst) ->
Goal = Goal0
;
rename_some_vars_in_goal(Subst, Goal0, Goal)
).
:- pred build_headvar_subst(list(prog_var)::in, rename_map::in,
map(prog_var, prog_var)::in, map(prog_var, prog_var)::out) is det.
build_headvar_subst([], _RenameMap, !Subst).
build_headvar_subst([HeadVar | HeadVars], RenameMap, !Subst) :-
( map.search(RenameMap, HeadVar, Replacement) ->
svmap.det_insert(Replacement, HeadVar, !Subst),
svmap.det_insert(HeadVar, Replacement, !Subst)
;
true
),
build_headvar_subst(HeadVars, RenameMap, !Subst).
%-----------------------------------------------------------------------------%
:- pred construct_anchors(branch_construct::in, hlds_goal::in,
anchor::out, anchor::out) is det.
construct_anchors(Construct, Goal, StartAnchor, EndAnchor) :-
Goal = hlds_goal(_, GoalInfo),
GoalPath = goal_info_get_goal_path(GoalInfo),
StartAnchor = anchor_branch_start(Construct, GoalPath),
EndAnchor = anchor_branch_end(Construct, GoalPath).
%-----------------------------------------------------------------------------%
% For debugging purposes.
dump_interval_info(IntervalInfo, !IO) :-
map.keys(IntervalInfo ^ interval_start, StartIds),
map.keys(IntervalInfo ^ interval_end, EndIds),
map.keys(IntervalInfo ^ interval_vars, VarsIds),
map.keys(IntervalInfo ^ interval_succ, SuccIds),
list.condense([StartIds, EndIds, VarsIds, SuccIds], IntervalIds0),
list.sort_and_remove_dups(IntervalIds0, IntervalIds),
io.write_string("INTERVALS:\n", !IO),
list.foldl(dump_interval_info_id(IntervalInfo), IntervalIds, !IO),
map.to_assoc_list(IntervalInfo ^ anchor_follow_map, AnchorFollows),
io.write_string("\nANCHOR FOLLOW:\n", !IO),
list.foldl(dump_anchor_follow, AnchorFollows, !IO).
:- pred dump_interval_info_id(interval_info::in, interval_id::in,
io::di, io::uo) is det.
dump_interval_info_id(IntervalInfo, IntervalId, !IO) :-
io.write_string("\ninterval ", !IO),
io.write_int(interval_id_to_int(IntervalId), !IO),
io.write_string(": ", !IO),
( map.search(IntervalInfo ^ interval_succ, IntervalId, SuccIds) ->
SuccNums = list.map(interval_id_to_int, SuccIds),
io.write_string("succ [", !IO),
write_int_list(SuccNums, !IO),
io.write_string("]\n", !IO)
;
io.write_string("no succ\n", !IO)
),
( map.search(IntervalInfo ^ interval_start, IntervalId, Start) ->
io.write_string("start ", !IO),
io.write(Start, !IO),
io.write_string("\n", !IO)
;
io.write_string("no start\n", !IO)
),
( map.search(IntervalInfo ^ interval_end, IntervalId, End) ->
io.write_string("end ", !IO),
io.write(End, !IO),
io.write_string("\n", !IO)
;
io.write_string("no end\n", !IO)
),
( map.search(IntervalInfo ^ interval_vars, IntervalId, Vars) ->
list.map(term.var_to_int, set.to_sorted_list(Vars), VarNums),
io.write_string("vars [", !IO),
write_int_list(VarNums, !IO),
io.write_string("]\n", !IO)
;
io.write_string("no vars\n", !IO)
),
( map.search(IntervalInfo ^ interval_delvars, IntervalId, Deletions) ->
io.write_string("deletions", !IO),
list.foldl(dump_deletion, Deletions, !IO),
io.write_string("\n", !IO)
;
true
).
:- pred dump_deletion(set(prog_var)::in, io::di, io::uo) is det.
dump_deletion(Vars, !IO) :-
list.map(term.var_to_int, set.to_sorted_list(Vars), VarNums),
io.write_string(" [", !IO),
write_int_list(VarNums, !IO),
io.write_string("]", !IO).
:- pred dump_anchor_follow(pair(anchor, anchor_follow_info)::in,
io::di, io::uo) is det.
dump_anchor_follow(Anchor - AnchorFollowInfo, !IO) :-
AnchorFollowInfo = anchor_follow_info(Vars, Intervals),
io.write_string("\n", !IO),
io.write(Anchor, !IO),
io.write_string(" =>\n", !IO),
list.map(term.var_to_int, set.to_sorted_list(Vars), VarNums),
io.write_string("vars [", !IO),
write_int_list(VarNums, !IO),
io.write_string("]\nintervals: ", !IO),
set.to_sorted_list(Intervals, IntervalList),
write_int_list(list.map(interval_id_to_int, IntervalList), !IO),
io.write_string("\n", !IO).
write_int_list(List, !IO) :-
io.write_list(List, ", ", io.write_int, !IO).
interval_id_to_int(interval_id(Num)) = Num.
%-----------------------------------------------------------------------------%
:- func this_file = string.
this_file = "interval.m".
%-----------------------------------------------------------------------------%
:- end_module interval.
%-----------------------------------------------------------------------------%