Files
mercury/compiler/stack_opt.m
Zoltan Somogyi 885fd4a387 Remove almost all dependencies by the modules of parse_tree.m on the modules
Estimated hours taken: 12
Branches: main

Remove almost all dependencies by the modules of parse_tree.m on the modules
of hlds.m. The only such dependencies remaining now are on type_util.m.

compiler/hlds_data.m:
compiler/prog_data.m:
	Move the cons_id type from hlds_data to prog_data, since several parts
	of the parse tree data structure depend on it (particularly insts).
	Remove the need to import HLDS modules in prog_data.m by making the
	cons_ids that refer to procedure ids refer to them via a new type
	that contains shrouded pred_ids and proc_ids. Since pred_ids and
	proc_ids are abstract types in hlds_data, add predicates to hlds_data
	to shroud and unshroud them.

	Also move some other types, e.g. mode_id and class_id, from hlds_data
	to prog_data.

compiler/hlds_data.m:
compiler/prog_util.m:
	Move predicates for manipulating cons_ids from hlds_data to prog_util.

compiler/inst.m:
compiler/prog_data.m:
	Move the contents of inst.m to prog_data.m, since that is where it
	belongs, and since doing so eliminates a circular dependency.
	The separation doesn't serve any purpose any more, since we don't
	need to import hlds_data.m anymore to get access to the cons_id type.

compiler/mode_util.m:
compiler/prog_mode.m:
compiler/parse_tree.m:
	Move the predicates in mode_util that don't depend on the HLDS to a new
	module prog_mode, which is part of parse_tree.m.

compiler/notes/compiler_design.m:
	Mention prog_mode.m, and delete the mention of inst.m.

compiler/mercury_to_mercury.m:
compiler/hlds_out.m:
	Move the predicates that depend on HLDS out of mercury_to_mercury.m
	to hlds_out.m. Export from mercury_to_mercury.m the predicates needed
	by the moved predicates.

compiler/hlds_out.m:
compiler/prog_out.m:
	Move predicates for printing parts of the parse tree out of hlds_out.m
	to prog_out.m, since mercury_to_mercury.m needs to use them.

compiler/purity.m:
compiler/prog_out.m:
	Move predicates for printing purities from purity.m, which is part
	of check_hlds.m, to prog_out.m, since mercury_to_mercury.m needs to use
	them.

compiler/passes_aux.m:
compiler/prog_out.m:
	Move some utility predicates (e.g. for printing progress messages) from
	passes_aux.m to prog_out.m, since some predicates in submodules of
	parse_tree.m need to use them.

compiler/foreign.m:
compiler/prog_data.m:
	Move some types from foreign.m to prog_data.m to allow the elimination
	of some dependencies on foreign.m from submodules of parse_tree.m.

compiler/*.m:
	Conform to the changes above, mostly by updating lists of imported
	modules and module qualifications. In some cases, also do some local
	cleanups such as converting predicate declarations to predmode syntax
	and fixing white space.
2004-06-14 04:17:03 +00:00

2278 lines
80 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 2002-2004 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 stack_opt.
%
% Author: zs.
%
% The input to this module is a HLDS structure with annotations on three kinds
% of goals:
%
% - calls, including generic calls and foreign_proc goals which may
% call back to Mercury, should have need_across_call annotations;
%
% - goals that have resume points before them (the conditions of if-then-elses
% and the non-last disjuncts of disjunction) should have need_in_resume
% annotations on them, provided that the resume point has a label that
% expects its variables to be on the stack;
%
% - parallel conjunctions should have need_in_par_conj annotations.
%
% The code in this module puts stack_save_map annotations on goals that have
% need_across_call annotations, on if-then-else goals whose condition has a
% need_in_resume annotation, and on disjunction goals whose first disjunct has
% a need_in_resume annotation. The stack_save map annotation tells the
% code generator which of the relevant variables need to be saved in their own
% stack slots, and which can be accessed through other variables on the stack.
%
% The code in this module processes procedures one by one. It makes two passes
% over each procedure.
%
% The first pass traverses the procedure body backward, building a graph
% structure as it goes along. The nodes of the graphs are *anchors*. Points
% at which stack flushes may be required are anchors, and so are the beginnings
% and ends of branched control structures and of the procedure body itself.
% The graph associates with the edge between two anchors the set of variables
% accessed by the program fragment between those two anchors.
%
% When the traversal reaches a deconstruction unification, we sweep forward
% over the graph. During this sweep, we build a set of *paths*, with the
% intention that this set should contain an element for each path that control
% can take from the starting unification to the end of the procedure body.
% Each path is a sequence of *intervals*. An interval starts either at the
% starting unification or at a stack flush point; it ends at a stack flush
% point or the end of the procedure body. An interval is associated with one
% or more edges in the graph; the first of these associated edges will not
% have a left anchor yet.
%
% We give each path to the matching algorithm one by one. The matching
% algorithm finds out which set of variables should be accessed via
% the cell variable on that path. Since the decisions made for different
% paths are not independent, we have to apply a fixpoint iteration until
% we get a consistent set of answers.
%
% The first pass (whose main predicate is optimize_live_sets_in_goal) records
% its results in the var_save_info field of the opt_info data structure it
% passes around. This field then becomes the main input to the second pass
% (whose main predicate is record_decisions_in_goal), which performs the
% source-to-source transformation that makes each segment access via the cell
% variable the field variables that have been selected to be so accessed
% by the first pass.
%
% The principles of this optimization are documented in the paper "Using the
% heap to eliminate stack accesses" by Zoltan Somogyi and Peter Stuckey.
%
%-----------------------------------------------------------------------------%
:- module ll_backend__stack_opt.
:- interface.
:- import_module hlds__hlds_module.
:- import_module hlds__hlds_pred.
:- import_module io.
:- pred stack_opt_cell(pred_id::in, proc_id::in, proc_info::in, proc_info::out,
module_info::in, module_info::out, io::di, io::uo) is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs__matching.
:- import_module check_hlds__goal_path.
:- import_module check_hlds__inst_match.
:- import_module check_hlds__mode_util.
:- import_module check_hlds__type_util.
:- import_module hlds__arg_info.
:- import_module hlds__code_model.
:- import_module hlds__goal_util.
:- import_module hlds__hlds_data.
:- import_module hlds__hlds_goal.
:- import_module hlds__hlds_llds.
:- import_module hlds__hlds_out.
:- import_module hlds__instmap.
:- import_module hlds__quantification.
:- import_module libs__globals.
:- import_module libs__options.
:- import_module ll_backend__call_gen.
:- import_module ll_backend__live_vars.
:- import_module ll_backend__liveness.
:- import_module ll_backend__store_alloc.
:- import_module parse_tree__mercury_to_mercury.
:- import_module parse_tree__prog_data.
:- import_module counter, bool, int, list, assoc_list.
:- import_module map, set, std_util, require, term, varset.
% The opt_stack_alloc structure is constructed by live_vars.m. It contains
% the set of vars that definitely need their own stack slots, and which this
% optimization should not try to make reachable from a heap cell. At the
% moment, the only variables we treat this way are those that are required to
% be on the stack by a parallel conjunction.
:- type opt_stack_alloc --->
opt_stack_alloc(
par_conj_own_slots :: set(prog_var)
).
:- type save_point_type
---> call_site
; resume_point.
:- type save_point --->
save_point(
save_point_type,
goal_path
).
:- type branch_construct
---> ite
; disj
; switch
; neg
; par_conj.
:- type resume_save_status
---> has_resume_save
; has_no_resume_save.
:- type anchor
---> proc_start
; proc_end
; branch_start(branch_construct, goal_path)
; cond_then(goal_path)
; branch_end(branch_construct, goal_path)
; 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 == pair(set(prog_var), set(interval_id)).
:- type opt_params --->
opt_params(
module_info :: module_info,
var_types :: vartypes,
matching_params :: matching_params,
all_path_node_ratio :: int,
fixpoint_loop :: bool,
full_path :: bool,
on_stack :: bool,
opt_at_most_zero_calls :: bool,
non_candidate_vars :: set(prog_var)
).
:- type matching_result --->
matching_result(
prog_var,
cons_id,
list(prog_var),
set(prog_var),
goal_path,
set(interval_id),
set(interval_id),
set(anchor),
set(anchor)
).
:- type opt_info --->
opt_info(
opt_params :: opt_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),
left_anchor_inserts :: insert_map,
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))),
matching_results :: list(matching_result)
).
:- type maybe_needs_flush
---> needs_flush
; doesnt_need_flush.
stack_opt_cell(PredId, ProcId, !ProcInfo, !ModuleInfo, !IO) :-
detect_liveness_proc(PredId, ProcId, !.ModuleInfo, !ProcInfo, !IO),
initial_liveness(!.ProcInfo, PredId, !.ModuleInfo, Liveness0),
module_info_globals(!.ModuleInfo, Globals),
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
body_should_use_typeinfo_liveness(PredInfo, Globals, TypeInfoLiveness),
globals__lookup_bool_option(Globals, opt_no_return_calls,
OptNoReturnCalls),
AllocData = alloc_data(!.ModuleInfo, !.ProcInfo, TypeInfoLiveness,
OptNoReturnCalls),
goal_path__fill_slots(!.ModuleInfo, !ProcInfo),
proc_info_goal(!.ProcInfo, Goal2),
OptStackAlloc0 = init_opt_stack_alloc,
set__init(FailVars),
set__init(NondetLiveness0),
build_live_sets_in_goal(Goal2, Goal, FailVars, AllocData,
OptStackAlloc0, OptStackAlloc, Liveness0, _Liveness,
NondetLiveness0, _NondetLiveness),
proc_info_set_goal(Goal, !ProcInfo),
allocate_store_maps(for_stack_opt, PredId, !.ModuleInfo, !ProcInfo),
globals__lookup_int_option(Globals, debug_stack_opt, DebugStackOpt),
pred_id_to_int(PredId, PredIdInt),
maybe_write_progress_message("\nbefore stack opt cell",
DebugStackOpt, PredIdInt, !.ProcInfo, !.ModuleInfo, !IO),
optimize_live_sets(!.ModuleInfo, OptStackAlloc, !ProcInfo,
Changed, DebugStackOpt, PredIdInt, !IO),
(
Changed = yes,
maybe_write_progress_message(
"\nafter stack opt transformation",
DebugStackOpt, PredIdInt, !.ProcInfo, !.ModuleInfo,
!IO),
requantify_proc(!ProcInfo),
maybe_write_progress_message(
"\nafter stack opt requantify",
DebugStackOpt, PredIdInt, !.ProcInfo, !.ModuleInfo,
!IO),
recompute_instmap_delta_proc(yes, !ProcInfo, !ModuleInfo),
maybe_write_progress_message(
"\nafter stack opt recompute instmaps",
DebugStackOpt, PredIdInt, !.ProcInfo, !.ModuleInfo,
!IO)
;
Changed = no
).
:- func init_opt_stack_alloc = opt_stack_alloc.
init_opt_stack_alloc = opt_stack_alloc(set__init).
:- pred optimize_live_sets(module_info::in, opt_stack_alloc::in,
proc_info::in, proc_info::out, bool::out, int::in, int::in,
io::di, io::uo) is det.
optimize_live_sets(ModuleInfo, OptAlloc, !ProcInfo, Changed, DebugStackOpt,
PredIdInt, !IO) :-
proc_info_goal(!.ProcInfo, Goal0),
proc_info_vartypes(!.ProcInfo, VarTypes0),
proc_info_varset(!.ProcInfo, VarSet0),
OptAlloc = opt_stack_alloc(ParConjOwnSlot),
arg_info__partition_proc_args(!.ProcInfo, ModuleInfo,
InputArgs, OutputArgs, UnusedArgs),
HeadVars = set__union_list([InputArgs, OutputArgs, UnusedArgs]),
module_info_globals(ModuleInfo, Globals),
globals__lookup_bool_option(Globals,
optimize_saved_vars_cell_candidate_headvars, CandHeadvars),
(
CandHeadvars = no,
set__union(HeadVars, ParConjOwnSlot, NonCandidateVars)
;
CandHeadvars = yes,
NonCandidateVars = ParConjOwnSlot
),
Counter0 = counter__init(1),
counter__allocate(CurInterval, Counter0, Counter1),
CurIntervalId = interval_id(CurInterval),
EndMap0 = map__det_insert(map__init, CurIntervalId, proc_end),
InsertMap0 = map__init,
StartMap0 = map__init,
SuccMap0 = map__det_insert(map__init, CurIntervalId, []),
VarsMap0 = map__det_insert(map__init, CurIntervalId, OutputArgs),
globals__lookup_int_option(Globals,
optimize_saved_vars_cell_cv_store_cost, CellVarStoreCost),
globals__lookup_int_option(Globals,
optimize_saved_vars_cell_cv_load_cost, CellVarLoadCost),
globals__lookup_int_option(Globals,
optimize_saved_vars_cell_fv_store_cost, FieldVarStoreCost),
globals__lookup_int_option(Globals,
optimize_saved_vars_cell_fv_load_cost, FieldVarLoadCost),
globals__lookup_int_option(Globals,
optimize_saved_vars_cell_op_ratio, OpRatio),
globals__lookup_int_option(Globals,
optimize_saved_vars_cell_node_ratio, NodeRatio),
globals__lookup_bool_option(Globals,
optimize_saved_vars_cell_include_all_candidates, InclAllCand),
MatchingParams = matching_params(CellVarStoreCost, CellVarLoadCost,
FieldVarStoreCost, FieldVarLoadCost, OpRatio, NodeRatio,
InclAllCand),
globals__lookup_int_option(Globals,
optimize_saved_vars_cell_all_path_node_ratio,
AllPathNodeRatio),
globals__lookup_bool_option(Globals,
optimize_saved_vars_cell_loop, FixpointLoop),
globals__lookup_bool_option(Globals,
optimize_saved_vars_cell_full_path, FullPath),
globals__lookup_bool_option(Globals,
optimize_saved_vars_cell_on_stack, OnStack),
globals__lookup_bool_option(Globals,
opt_no_return_calls, OptNoReturnCalls),
OptParams = opt_params(ModuleInfo, VarTypes0, MatchingParams,
AllPathNodeRatio, FixpointLoop, FullPath, OnStack,
OptNoReturnCalls, NonCandidateVars),
OptInfo0 = opt_info(OptParams, set__init, OutputArgs, map__init,
map__init, map__init, CurIntervalId, Counter1,
set__make_singleton_set(CurIntervalId),
map__init, set__init, InsertMap0, StartMap0, EndMap0,
SuccMap0, VarsMap0, map__init, []),
optimize_live_sets_in_goal(Goal0, OptInfo0, OptInfo),
( DebugStackOpt = PredIdInt ->
dump_opt_info(OptInfo, !IO)
;
true
),
InsertMap = OptInfo ^ left_anchor_inserts,
( map__is_empty(InsertMap) ->
Changed = no
;
VarInfo0 = var_info(VarSet0, VarTypes0),
record_decisions_in_goal(Goal0, Goal1, VarInfo0, VarInfo,
map__init, RenameMap, InsertMap),
apply_headvar_correction(HeadVars, RenameMap, Goal1, Goal),
VarInfo = var_info(VarSet, VarTypes),
proc_info_set_goal(Goal, !ProcInfo),
proc_info_set_varset(VarSet, !ProcInfo),
proc_info_set_vartypes(VarTypes, !ProcInfo),
Changed = yes
).
%-----------------------------------------------------------------------------%
:- pred optimize_live_sets_in_goal(hlds_goal::in,
opt_info::in, opt_info::out) is det.
optimize_live_sets_in_goal(conj(Goals) - _GoalInfo, !OptInfo) :-
optimize_live_sets_in_conj(Goals, !OptInfo).
optimize_live_sets_in_goal(par_conj(Goals) - _GoalInfo, !OptInfo) :-
optimize_live_sets_in_par_conj(Goals, !OptInfo).
optimize_live_sets_in_goal(disj(Goals) - GoalInfo, !OptInfo) :-
( Goals = [FirstDisjunct | _] ->
reached_branch_end(GoalInfo, yes(FirstDisjunct), disj,
StartAnchor, EndAnchor, BeforeId, AfterId,
MaybeResumeVars, !OptInfo),
optimize_live_sets_in_disj(Goals, doesnt_need_flush,
StartAnchor, EndAnchor, BeforeId, AfterId,
OpenIntervals, !OptInfo),
leave_branch_start(disj, StartAnchor, BeforeId,
MaybeResumeVars, OpenIntervals, !OptInfo)
;
% 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(!OptInfo)
).
optimize_live_sets_in_goal(switch(Var, _Det, Cases) - GoalInfo, !OptInfo) :-
reached_branch_end(GoalInfo, no, switch,
StartAnchor, EndAnchor, BeforeId, AfterId, MaybeResumeVars,
!OptInfo),
optimize_live_sets_in_cases(Cases, StartAnchor, EndAnchor,
BeforeId, AfterId, OpenIntervalsList, !OptInfo),
OpenIntervals = set__union_list(OpenIntervalsList),
leave_branch_start(switch, StartAnchor, BeforeId, MaybeResumeVars,
OpenIntervals, !OptInfo),
require_in_regs([Var], !OptInfo),
require_access([Var], !OptInfo).
optimize_live_sets_in_goal(not(Goal) - GoalInfo, !OptInfo) :-
reached_branch_end(GoalInfo, yes(Goal), neg,
StartAnchor, EndAnchor, BeforeId, AfterId, MaybeResumeVars,
!OptInfo),
enter_branch_tail(EndAnchor, AfterId, !OptInfo),
optimize_live_sets_in_goal(Goal, !OptInfo),
reached_branch_start(needs_flush, StartAnchor, BeforeId,
OpenIntervals, !OptInfo),
leave_branch_start(neg, StartAnchor, BeforeId, MaybeResumeVars,
OpenIntervals, !OptInfo).
optimize_live_sets_in_goal(if_then_else(_, Cond, Then, Else) - GoalInfo,
!OptInfo) :-
reached_branch_end(GoalInfo, yes(Cond), ite, StartAnchor, EndAnchor,
BeforeId, AfterId, MaybeResumeVars, !OptInfo),
enter_branch_tail(EndAnchor, AfterId, !OptInfo),
optimize_live_sets_in_goal(Then, !OptInfo),
reached_cond_then(GoalInfo, !OptInfo),
optimize_live_sets_in_goal(Cond, !OptInfo),
reached_branch_start(doesnt_need_flush, StartAnchor, BeforeId,
CondOpenIntervals, !OptInfo),
enter_branch_tail(EndAnchor, AfterId, !OptInfo),
optimize_live_sets_in_goal(Else, !OptInfo),
reached_branch_start(needs_flush, StartAnchor, BeforeId,
_ElseOpenIntervals, !OptInfo),
leave_branch_start(ite, StartAnchor, BeforeId, MaybeResumeVars,
CondOpenIntervals, !OptInfo).
optimize_live_sets_in_goal(some(_Vars, _CanRemove, Goal) - _GoalInfo,
!OptInfo) :-
optimize_live_sets_in_goal(Goal, !OptInfo).
optimize_live_sets_in_goal(Goal - GoalInfo, !OptInfo) :-
OptParams = !.OptInfo ^ opt_params,
Goal = generic_call(GenericCall, ArgVars, ArgModes, Detism),
goal_info_get_maybe_need_across_call(GoalInfo, MaybeNeedAcrossCall),
VarTypes = OptParams ^ var_types,
list__map(map__lookup(VarTypes), ArgVars, ArgTypes),
ModuleInfo = OptParams ^ module_info,
arg_info__compute_in_and_out_vars(ModuleInfo, ArgVars,
ArgModes, ArgTypes, InputArgs, _OutputArgs),
determinism_to_code_model(Detism, CodeModel),
% unsafe_casts are generated inline.
( GenericCall = unsafe_cast ->
require_in_regs(InputArgs, !OptInfo),
require_access(InputArgs, !OptInfo)
;
call_gen__generic_call_info(CodeModel, GenericCall, _,
GenericVarsArgInfos, _),
assoc_list__keys(GenericVarsArgInfos, GenericVars),
list__append(GenericVars, InputArgs, Inputs),
optimize_live_sets_at_call(Inputs,
MaybeNeedAcrossCall, GoalInfo, !OptInfo)
).
optimize_live_sets_in_goal(Goal - GoalInfo, !OptInfo) :-
Goal = call(PredId, ProcId, ArgVars, Builtin, _, _),
OptParams = !.OptInfo ^ opt_params,
ModuleInfo = OptParams ^ module_info,
module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
_PredInfo, ProcInfo),
VarTypes = OptParams ^ 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, !OptInfo),
require_access(Inputs, !OptInfo)
;
goal_info_get_maybe_need_across_call(GoalInfo,
MaybeNeedAcrossCall),
optimize_live_sets_at_call(Inputs, MaybeNeedAcrossCall,
GoalInfo, !OptInfo)
).
optimize_live_sets_in_goal(Goal - GoalInfo, !OptInfo) :-
Goal = foreign_proc(_Attributes, PredId, ProcId, Args, ExtraArgs,
_PragmaCode),
OptParams = !.OptInfo ^ opt_params,
ModuleInfo = OptParams ^ module_info,
module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
_PredInfo, ProcInfo),
VarTypes = OptParams ^ 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(_)
->
optimize_live_sets_at_call(InputVars, MaybeNeedAcrossCall,
GoalInfo, !OptInfo)
;
require_in_regs(InputVars, !OptInfo),
require_access(InputVars, !OptInfo)
).
optimize_live_sets_in_goal(Goal - GoalInfo, !OptInfo) :-
Goal = unify(_, _, _, Unification, _),
(
Unification = construct(CellVar, _ConsId, ArgVars, _,
HowToConstruct, _, _),
( HowToConstruct = reuse_cell(_) ->
error("optimize_live_sets_in_goal: reuse")
;
true
),
require_in_regs(ArgVars, !OptInfo),
require_access([CellVar | ArgVars], !OptInfo)
% use_cell(CellVar, ArgVars, ConsId, Goal - GoalInfo, !OptInfo)
% 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, _, _),
OptParams = !.OptInfo ^ opt_params,
ModuleInfo = OptParams ^ module_info,
( shared_left_to_right_deconstruct(ModuleInfo, ArgModes) ->
use_cell(CellVar, ArgVars, ConsId, Goal - GoalInfo,
!OptInfo)
;
true
),
require_in_regs([CellVar], !OptInfo),
require_access([CellVar | ArgVars], !OptInfo)
;
Unification = assign(ToVar, FromVar),
require_in_regs([FromVar], !OptInfo),
require_access([FromVar, ToVar], !OptInfo)
;
Unification = simple_test(Var1, Var2),
require_in_regs([Var1, Var2], !OptInfo),
require_access([Var1, Var2], !OptInfo)
;
Unification = complicated_unify(_, _, _),
error("optimize_live_sets_in_goal: complicated_unify")
).
optimize_live_sets_in_goal(shorthand(_) - _, !OptInfo) :-
error("shorthand in optimize_live_sets_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 optimize_live_sets_at_call(list(prog_var)::in,
maybe(need_across_call)::in, hlds_goal_info::in,
opt_info::in, opt_info::out) is det.
optimize_live_sets_at_call(Inputs, MaybeNeedAcrossCall, GoalInfo, !OptInfo) :-
(
MaybeNeedAcrossCall = yes(NeedAcrossCall),
NeedAcrossCall = need_across_call(ForwardVars,
ResumeVars, NondetLiveVars),
VarsOnStack0 = set__union_list([ForwardVars, ResumeVars,
NondetLiveVars]),
goal_info_get_goal_path(GoalInfo, GoalPath),
CallAnchor = call_site(GoalPath),
get_cur_interval(AfterCallId, !OptInfo),
new_interval_id(BeforeCallId, !OptInfo),
record_interval_start(AfterCallId, CallAnchor, !OptInfo),
record_interval_end(BeforeCallId, CallAnchor, !OptInfo),
goal_info_get_instmap_delta(GoalInfo, InstMapDelta),
OptParams = !.OptInfo ^ opt_params,
(
( instmap_delta_is_reachable(InstMapDelta)
; OptParams ^ opt_at_most_zero_calls = no
)
->
record_interval_succ(BeforeCallId, AfterCallId, !OptInfo),
VarsOnStack = VarsOnStack0
;
% If the call cannot succeed, then execution cannot
% get from BeforeCallId to AfterCallId.
record_interval_no_succ(BeforeCallId, !OptInfo),
VarsOnStack = set__init
),
set_cur_interval(BeforeCallId, !OptInfo),
assign_open_intervals_to_anchor(CallAnchor, !OptInfo),
goal_info_get_code_model(GoalInfo, CodeModel),
( CodeModel = model_non ->
record_model_non_anchor(CallAnchor, !OptInfo)
;
true
),
one_open_interval(BeforeCallId, !OptInfo),
require_flushed(VarsOnStack, !OptInfo),
require_in_regs(Inputs, !OptInfo),
require_access(Inputs, !OptInfo)
;
MaybeNeedAcrossCall = no,
error("optimize_live_sets_at_call: no need across call")
).
%-----------------------------------------------------------------------------%
:- pred optimize_live_sets_in_conj(list(hlds_goal)::in,
opt_info::in, opt_info::out) is det.
optimize_live_sets_in_conj([], !OptInfo).
optimize_live_sets_in_conj([Goal | Goals], !OptInfo) :-
optimize_live_sets_in_conj(Goals, !OptInfo),
optimize_live_sets_in_goal(Goal, !OptInfo).
:- pred optimize_live_sets_in_par_conj(list(hlds_goal)::in,
opt_info::in, opt_info::out) is det.
optimize_live_sets_in_par_conj([], !OptInfo).
optimize_live_sets_in_par_conj([Goal | Goals], !OptInfo) :-
% XXX zs: I am not sure that passing opt_info from the first goal to
% the rest is OK. Maybe we should pass the initial opt_info to all the
% conjuncts, and then merge the resulting opt_infos.
optimize_live_sets_in_par_conj(Goals, !OptInfo),
optimize_live_sets_in_goal(Goal, !OptInfo).
:- pred optimize_live_sets_in_disj(list(hlds_goal)::in, maybe_needs_flush::in,
anchor::in, anchor::in, interval_id::in, interval_id::in,
set(interval_id)::out, opt_info::in, opt_info::out) is det.
optimize_live_sets_in_disj([], _, _, _, _, _, set__init, !OptInfo).
optimize_live_sets_in_disj([Goal | Goals], MaybeNeedsFlush,
StartAnchor, EndAnchor, BeforeId, AfterId, OpenIntervals,
!OptInfo) :-
enter_branch_tail(EndAnchor, AfterId, !OptInfo),
optimize_live_sets_in_goal(Goal, !OptInfo),
reached_branch_start(MaybeNeedsFlush, StartAnchor, BeforeId,
OpenIntervals, !OptInfo),
optimize_live_sets_in_disj(Goals, needs_flush, StartAnchor, EndAnchor,
BeforeId, AfterId, _OpenIntervals, !OptInfo).
:- pred optimize_live_sets_in_cases(list(case)::in,
anchor::in, anchor::in, interval_id::in, interval_id::in,
list(set(interval_id))::out, opt_info::in, opt_info::out) is det.
optimize_live_sets_in_cases([], _, _, _, _, [], !OptInfo).
optimize_live_sets_in_cases([case(_Var, Goal) | Cases], StartAnchor, EndAnchor,
BeforeId, AfterId, [OpenIntervals | OpenIntervalsList],
!OptInfo) :-
enter_branch_tail(EndAnchor, AfterId, !OptInfo),
optimize_live_sets_in_goal(Goal, !OptInfo),
reached_branch_start(doesnt_need_flush, StartAnchor, BeforeId,
OpenIntervals, !OptInfo),
optimize_live_sets_in_cases(Cases, StartAnchor, EndAnchor,
BeforeId, AfterId, OpenIntervalsList, !OptInfo).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- instance stack_alloc_info(opt_stack_alloc) where [
pred(at_call_site/4) is opt_at_call_site,
pred(at_resume_site/4) is opt_at_resume_site,
pred(at_par_conj/4) is opt_at_par_conj
].
:- pred opt_at_call_site(need_across_call::in, hlds_goal_info::in,
opt_stack_alloc::in, opt_stack_alloc::out) is det.
opt_at_call_site(_NeedAtCall, _GoalInfo, StackAlloc, StackAlloc).
:- pred opt_at_resume_site(need_in_resume::in, hlds_goal_info::in,
opt_stack_alloc::in, opt_stack_alloc::out) is det.
opt_at_resume_site(_NeedAtResume, _GoalInfo, StackAlloc, StackAlloc).
:- pred opt_at_par_conj(need_in_par_conj::in, hlds_goal_info::in,
opt_stack_alloc::in, opt_stack_alloc::out) is det.
opt_at_par_conj(NeedParConj, _GoalInfo, StackAlloc0, StackAlloc) :-
NeedParConj = need_in_par_conj(StackVars),
ParConjOwnSlots0 = StackAlloc0 ^ par_conj_own_slots,
ParConjOwnSlots = set__union(StackVars, ParConjOwnSlots0),
StackAlloc = StackAlloc0 ^ par_conj_own_slots := ParConjOwnSlots.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- 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,
opt_info::in, opt_info::out) is det.
reached_branch_end(GoalInfo, MaybeResumeGoal, Construct,
StartAnchor, EndAnchor, BeforeIntervalId, AfterIntervalId,
MaybeResumeVars, !OptInfo) :-
goal_info_get_goal_path(GoalInfo, GoalPath),
record_branch_end_info(GoalPath, !OptInfo),
(
MaybeResumeGoal = yes(_ResumeGoalExpr - ResumeGoalInfo),
goal_info_maybe_get_resume_point(ResumeGoalInfo, ResumePoint),
ResumePoint = resume_point(ResumeVars, ResumeLocs),
ResumeLocs \= orig_only
->
HasResumeSave = has_resume_save,
MaybeResumeVars = yes(ResumeVars)
;
HasResumeSave = has_no_resume_save,
MaybeResumeVars = no
),
record_branch_resume(GoalPath, HasResumeSave, !OptInfo),
( goal_info_maybe_get_store_map(GoalInfo, StoreMap) ->
map__sorted_keys(StoreMap, StoreMapVarList),
set__sorted_list_to_set(StoreMapVarList, StoreMapVars),
require_flushed(StoreMapVars, !OptInfo)
;
error("reached_branch_end: no store map")
),
EndAnchor = branch_end(Construct, GoalPath),
StartAnchor = branch_start(Construct, GoalPath),
assign_open_intervals_to_anchor(EndAnchor, !OptInfo),
goal_info_get_code_model(GoalInfo, CodeModel),
( CodeModel = model_non ->
record_model_non_anchor(EndAnchor, !OptInfo)
;
true
),
no_open_intervals(!OptInfo),
get_cur_interval(AfterIntervalId, !OptInfo),
record_interval_start(AfterIntervalId, EndAnchor, !OptInfo),
new_interval_id(BeforeIntervalId, !OptInfo).
:- pred enter_branch_tail(anchor::in, interval_id::in,
opt_info::in, opt_info::out) is det.
enter_branch_tail(EndAnchor, AfterId, !OptInfo) :-
new_interval_id(BranchTailId, !OptInfo),
record_interval_end(BranchTailId, EndAnchor, !OptInfo),
record_interval_succ(BranchTailId, AfterId, !OptInfo),
set_cur_interval(BranchTailId, !OptInfo),
one_open_interval(BranchTailId, !OptInfo).
:- pred reached_branch_start(maybe_needs_flush::in, anchor::in,
interval_id::in, set(interval_id)::out, opt_info::in, opt_info::out)
is det.
reached_branch_start(MaybeNeedsFlush, StartAnchor, BeforeId, OpenIntervals,
!OptInfo) :-
get_cur_interval(BranchStartId, !OptInfo),
record_interval_start(BranchStartId, StartAnchor, !OptInfo),
record_interval_succ(BeforeId, BranchStartId, !OptInfo),
get_open_intervals(!.OptInfo, OpenIntervals),
(
MaybeNeedsFlush = doesnt_need_flush
;
MaybeNeedsFlush = needs_flush,
assign_open_intervals_to_anchor(StartAnchor, !OptInfo)
).
:- pred reached_cond_then(hlds_goal_info::in, opt_info::in, opt_info::out)
is det.
reached_cond_then(GoalInfo, !OptInfo) :-
goal_info_get_goal_path(GoalInfo, GoalPath),
record_cond_end(GoalPath, !OptInfo),
get_cur_interval(ThenStartId, !OptInfo),
record_interval_start(ThenStartId, CondThenAnchor, !OptInfo),
new_interval_id(CondTailId, !OptInfo),
CondThenAnchor = cond_then(GoalPath),
record_interval_end(CondTailId, CondThenAnchor, !OptInfo),
record_interval_succ(CondTailId, ThenStartId, !OptInfo),
set_cur_interval(CondTailId, !OptInfo),
get_open_intervals(!.OptInfo, OpenIntervals0),
OpenIntervals = set__insert(OpenIntervals0, CondTailId),
set_open_intervals(OpenIntervals, !OptInfo).
:- pred leave_branch_start(branch_construct::in, anchor::in, interval_id::in,
maybe(set(prog_var))::in, set(interval_id)::in,
opt_info::in, opt_info::out) is det.
leave_branch_start(_BranchConstruct, StartArchor, BeforeId, MaybeResumeVars,
OpenIntervals, !OptInfo) :-
record_interval_end(BeforeId, StartArchor, !OptInfo),
(
MaybeResumeVars = yes(ResumeVars),
require_flushed(ResumeVars, !OptInfo)
;
MaybeResumeVars = no
),
set_cur_interval(BeforeId, !OptInfo),
set_open_intervals(OpenIntervals, !OptInfo).
:- pred get_open_intervals(opt_info::in, set(interval_id)::out) is det.
get_open_intervals(OptInfo, OpenIntervals) :-
OpenIntervals = OptInfo ^ open_intervals.
:- pred set_open_intervals(set(interval_id)::in,
opt_info::in, opt_info::out) is det.
set_open_intervals(OpenIntervals, OptInfo0, OptInfo) :-
OptInfo = OptInfo0 ^ open_intervals := OpenIntervals.
:- pred no_open_intervals(opt_info::in, opt_info::out) is det.
no_open_intervals(OptInfo0, OptInfo) :-
OptInfo = OptInfo0 ^ open_intervals := set__init.
:- pred one_open_interval(interval_id::in, opt_info::in, opt_info::out) is det.
one_open_interval(IntervalId, OptInfo0, OptInfo) :-
OptInfo = OptInfo0 ^ open_intervals :=
set__make_singleton_set(IntervalId).
:- pred assign_open_intervals_to_anchor(anchor::in,
opt_info::in, opt_info::out) is det.
assign_open_intervals_to_anchor(Anchor, OptInfo0, OptInfo) :-
AnchorFollowMap0 = OptInfo0 ^ anchor_follow_map,
IntervalVarMap = OptInfo0 ^ interval_vars,
CurOpenIntervals = OptInfo0 ^ open_intervals,
set__fold(gather_interval_vars(IntervalVarMap), CurOpenIntervals,
set__init, CurOpenIntervalVars),
( map__search(AnchorFollowMap0, Anchor, AnchorFollowInfo0) ->
AnchorFollowInfo0 = OpenIntervalVars0 - OpenIntervals0,
OpenIntervalVars =
set__union(OpenIntervalVars0, CurOpenIntervalVars),
OpenIntervals =
set__union(OpenIntervals0, CurOpenIntervals),
AnchorFollowInfo = OpenIntervalVars - OpenIntervals,
map__det_update(AnchorFollowMap0, Anchor, AnchorFollowInfo,
AnchorFollowMap)
;
AnchorFollowInfo = CurOpenIntervalVars - CurOpenIntervals,
map__det_insert(AnchorFollowMap0, Anchor, AnchorFollowInfo,
AnchorFollowMap)
),
OptInfo = OptInfo0 ^ 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,
OpenIntervalVars0, OpenIntervalVars) :-
map__lookup(IntervalVarMap, IntervalId, IntervalVars),
OpenIntervalVars = set__union(OpenIntervalVars0, IntervalVars).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- pred get_cur_interval(interval_id::out, opt_info::in, opt_info::out)
is det.
get_cur_interval(OptInfo ^ cur_interval, OptInfo, OptInfo).
:- pred set_cur_interval(interval_id::in, opt_info::in, opt_info::out) is det.
set_cur_interval(CurInterval, OptInfo,
OptInfo ^ cur_interval := CurInterval).
:- pred new_interval_id(interval_id::out, opt_info::in, opt_info::out) is det.
new_interval_id(Id, OptInfo0, OptInfo) :-
Counter0 = OptInfo0 ^ interval_counter,
IntervalVars0 = OptInfo0 ^ interval_vars,
counter__allocate(Num, Counter0, Counter),
Id = interval_id(Num),
map__det_insert(IntervalVars0, Id, set__init, IntervalVars),
OptInfo = (OptInfo0 ^ interval_counter := Counter)
^ interval_vars := IntervalVars.
:- pred record_branch_end_info(goal_path::in,
opt_info::in, opt_info::out) is det.
record_branch_end_info(GoalPath, OptInfo0, OptInfo) :-
FlushedLater = OptInfo0 ^ flushed_later,
AccessedLater = OptInfo0 ^ accessed_later,
CurInterval = OptInfo0 ^ cur_interval,
BranchEndMap0 = OptInfo0 ^ branch_end_map,
BranchEndInfo = branch_end_info(FlushedLater, AccessedLater,
CurInterval),
map__det_insert(BranchEndMap0, GoalPath, BranchEndInfo, BranchEndMap),
OptInfo = OptInfo0 ^ branch_end_map := BranchEndMap.
:- pred record_cond_end(goal_path::in, opt_info::in, opt_info::out) is det.
record_cond_end(GoalPath, OptInfo0, OptInfo) :-
CurInterval = OptInfo0 ^ cur_interval,
CondEndMap0 = OptInfo0 ^ cond_end_map,
map__det_insert(CondEndMap0, GoalPath, CurInterval, CondEndMap),
OptInfo = OptInfo0 ^ cond_end_map := CondEndMap.
:- pred record_interval_end(interval_id::in, anchor::in,
opt_info::in, opt_info::out) is det.
record_interval_end(Id, End, OptInfo0, OptInfo) :-
EndMap0 = OptInfo0 ^ interval_end,
map__det_insert(EndMap0, Id, End, EndMap),
OptInfo = OptInfo0 ^ interval_end := EndMap.
:- pred record_interval_start(interval_id::in, anchor::in,
opt_info::in, opt_info::out) is det.
record_interval_start(Id, Start, OptInfo0, OptInfo) :-
StartMap0 = OptInfo0 ^ interval_start,
map__det_insert(StartMap0, Id, Start, StartMap),
OptInfo = OptInfo0 ^ interval_start := StartMap.
:- pred record_interval_succ(interval_id::in, interval_id::in,
opt_info::in, opt_info::out) is det.
record_interval_succ(Id, Succ, OptInfo0, OptInfo) :-
SuccMap0 = OptInfo0 ^ interval_succ,
( map__search(SuccMap0, Id, Succ0) ->
map__det_update(SuccMap0, Id, [Succ | Succ0], SuccMap)
;
map__det_insert(SuccMap0, Id, [Succ], SuccMap)
),
OptInfo = OptInfo0 ^ interval_succ := SuccMap.
:- pred record_interval_no_succ(interval_id::in,
opt_info::in, opt_info::out) is det.
record_interval_no_succ(Id, OptInfo0, OptInfo) :-
SuccMap0 = OptInfo0 ^ interval_succ,
( map__search(SuccMap0, Id, _Succ0) ->
error("record_interval_no_succ: already in succ map")
;
map__det_insert(SuccMap0, Id, [], SuccMap)
),
OptInfo = OptInfo0 ^ interval_succ := SuccMap.
:- pred record_interval_vars(interval_id::in, list(prog_var)::in,
opt_info::in, opt_info::out) is det.
record_interval_vars(Id, NewVars, OptInfo0, OptInfo) :-
VarsMap0 = OptInfo0 ^ interval_vars,
( map__search(VarsMap0, Id, Vars0) ->
Vars = set__insert_list(Vars0, NewVars),
map__det_update(VarsMap0, Id, Vars, VarsMap)
;
set__list_to_set(NewVars, Vars),
map__det_insert(VarsMap0, Id, Vars, VarsMap)
),
OptInfo = OptInfo0 ^ interval_vars := VarsMap.
:- pred delete_interval_vars(interval_id::in, set(prog_var)::in,
set(prog_var)::out, opt_info::in, opt_info::out) is det.
delete_interval_vars(Id, ToDeleteVars, DeletedVars, OptInfo0, OptInfo) :-
VarsMap0 = OptInfo0 ^ interval_vars,
map__lookup(VarsMap0, Id, Vars0),
DeletedVars = set__intersect(Vars0, ToDeleteVars),
Vars = set__difference(Vars0, DeletedVars),
map__det_update(VarsMap0, Id, Vars, VarsMap),
OptInfo1 = OptInfo0 ^ interval_vars := VarsMap,
% The deletions are recorded only for debugging. The algorithm itself
% does not need this information to be recorded.
DeleteMap0 = OptInfo1 ^ interval_delvars,
( map__search(DeleteMap0, Id, Deletions0) ->
Deletions = [DeletedVars | Deletions0],
map__det_update(DeleteMap0, Id, Deletions, DeleteMap)
;
Deletions = [DeletedVars],
map__det_insert(DeleteMap0, Id, Deletions, DeleteMap)
),
OptInfo = OptInfo1 ^ interval_delvars := DeleteMap.
:- pred lookup_interval_end(interval_id::in, anchor::out,
opt_info::in, opt_info::out) is det.
lookup_interval_end(Id, End, OptInfo, OptInfo) :-
EndMap = OptInfo ^ interval_end,
map__lookup(EndMap, Id, End).
:- pred lookup_interval_succ(interval_id::in, list(interval_id)::out,
opt_info::in, opt_info::out) is det.
lookup_interval_succ(Id, Succ, OptInfo, OptInfo) :-
SuccMap = OptInfo ^ interval_succ,
map__lookup(SuccMap, Id, Succ).
:- pred lookup_interval_vars(interval_id::in, set(prog_var)::out,
opt_info::in, opt_info::out) is det.
lookup_interval_vars(Id, Vars, OptInfo, OptInfo) :-
VarsMap = OptInfo ^ interval_vars,
map__lookup(VarsMap, Id, Vars).
:- pred require_in_regs(list(prog_var)::in, opt_info::in, opt_info::out)
is det.
require_in_regs(Vars, OptInfo0, OptInfo) :-
CurIntervalId = OptInfo0 ^ cur_interval,
record_interval_vars(CurIntervalId, Vars, OptInfo0, OptInfo).
:- pred require_flushed(set(prog_var)::in,
opt_info::in, opt_info::out) is det.
require_flushed(Vars, OptInfo0, OptInfo) :-
FlushedLater0 = OptInfo0 ^ flushed_later,
FlushedLater = set__union(FlushedLater0, Vars),
OptInfo = OptInfo0 ^ flushed_later := FlushedLater.
:- pred require_access(list(prog_var)::in,
opt_info::in, opt_info::out) is det.
require_access(Vars, OptInfo0, OptInfo) :-
AccessedLater0 = OptInfo0 ^ accessed_later,
AccessedLater = set__insert_list(AccessedLater0, Vars),
OptInfo = OptInfo0 ^ accessed_later := AccessedLater.
:- pred record_branch_resume(goal_path::in, resume_save_status::in,
opt_info::in, opt_info::out) is det.
record_branch_resume(GoalPath, ResumeSaveStatus, OptInfo0, OptInfo) :-
BranchResumeMap0 = OptInfo0 ^ branch_resume_map,
map__det_insert(BranchResumeMap0, GoalPath, ResumeSaveStatus,
BranchResumeMap),
OptInfo = OptInfo0 ^ branch_resume_map := BranchResumeMap.
:- pred record_model_non_anchor(anchor::in, opt_info::in, opt_info::out)
is det.
record_model_non_anchor(Anchor, OptInfo0, OptInfo) :-
ModelNonAnchors0 = OptInfo0 ^ model_non_anchors,
ModelNonAnchors = set__insert(ModelNonAnchors0, Anchor),
OptInfo = OptInfo0 ^ model_non_anchors := ModelNonAnchors.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- type match_path_info
---> match_path_info(
set(prog_var), % The set of vars referenced in
% the first interval, before
% the first flush point.
list(set(prog_var)) % The set of vars referenced in
% later intervals, after the
% first flush point.
).
:- type match_info
---> match_info(
list(match_path_info), % Information about the
% variables used along each
% path.
set(prog_var), % The variables used after the
% deconstruction goes out of
% scope.
bool, % Have we stepped over a
% model_non goal?
set(anchor), % The set of save points
% to which the results of the
% matching applies.
set(interval_id)
).
:- pred use_cell(prog_var::in, list(prog_var)::in, cons_id::in, hlds_goal::in,
opt_info::in, opt_info::out) is det.
use_cell(CellVar, FieldVarList, ConsId, Goal, !OptInfo) :-
FlushedLater = !.OptInfo ^ flushed_later,
OptParams = !.OptInfo ^ opt_params,
NonCandidateVars = OptParams ^ non_candidate_vars,
set__list_to_set(FieldVarList, FieldVars),
set__intersect(FieldVars, FlushedLater, FlushedLaterFieldVars),
set__difference(FlushedLaterFieldVars, NonCandidateVars,
CandidateArgVars0),
(
set__empty(CandidateArgVars0)
->
true
;
ConsId = cons(_Name, _Arity),
VarTypes = OptParams ^ var_types,
map__lookup(VarTypes, CellVar, Type),
(
type_is_tuple(Type, _)
->
FreeOfCost = no
;
type_to_ctor_and_args(Type, TypeCtor, _),
ModuleInfo = OptParams ^ module_info,
module_info_types(ModuleInfo, TypeTable),
map__lookup(TypeTable, TypeCtor, TypeDefn),
hlds_data__get_type_defn_body(TypeDefn, TypeBody),
ConsTable = TypeBody ^ du_type_cons_tag_values
->
map__lookup(ConsTable, ConsId, ConsTag),
( ConsTag = no_tag ->
FreeOfCost = yes
;
FreeOfCost = no
)
;
fail
)
->
RelevantVars = set__insert(FieldVars, CellVar),
find_all_branches_from_cur_interval(RelevantVars, MatchInfo,
!OptInfo),
MatchInfo = match_info(PathsInfo, RelevantAfterVars,
AfterModelNon, InsertAnchors, InsertIntervals),
(
FreeOfCost = yes,
set__difference(CandidateArgVars0, RelevantAfterVars,
ViaCellVars),
record_matching_result(CellVar, ConsId,
FieldVarList, ViaCellVars, Goal,
InsertAnchors, InsertIntervals, !OptInfo)
;
FreeOfCost = no,
(
AfterModelNon = no,
OnStack = OptParams ^ on_stack,
set__difference(CandidateArgVars0,
RelevantAfterVars, CandidateArgVars),
(
OnStack = yes,
( set__member(CellVar, FlushedLater) ->
CellVarFlushedLater = yes
;
CellVarFlushedLater = no
)
;
OnStack = no,
(
list__member(PathInfo,
PathsInfo),
PathInfo = match_path_info(_,
Segments),
list__member(Segment,
Segments),
set__member(CellVar, Segment)
->
CellVarFlushedLater = yes
;
CellVarFlushedLater = no
)
),
apply_matching(CellVar, CellVarFlushedLater,
OptParams, PathsInfo, CandidateArgVars,
ViaCellVars),
record_matching_result(CellVar, ConsId,
FieldVarList, ViaCellVars, Goal,
InsertAnchors, InsertIntervals,
!OptInfo)
;
AfterModelNon = yes
)
)
;
true
).
:- pred apply_matching(prog_var::in, bool::in, opt_params::in,
list(match_path_info)::in, set(prog_var)::in, set(prog_var)::out)
is det.
apply_matching(CellVar, CellVarFlushedLater, OptParams, PathInfos,
CandidateArgVars0, ViaCellVars) :-
apply_matching_loop(CellVar, CellVarFlushedLater, OptParams, PathInfos,
CandidateArgVars0, BenefitNodeSets, CostNodeSets,
ViaCellVars0),
BenefitNodes = set__union_list(BenefitNodeSets),
CostNodes = set__union_list(CostNodeSets),
set__count(BenefitNodes, NumBenefitNodes),
set__count(CostNodes, NumCostNodes),
AllPathNodeRatio = OptParams ^ all_path_node_ratio,
( NumBenefitNodes * 100 >= NumCostNodes * AllPathNodeRatio ->
ViaCellVars = ViaCellVars0
;
ViaCellVars = set__init
).
:- pred apply_matching_loop(prog_var::in, bool::in, opt_params::in,
list(match_path_info)::in, set(prog_var)::in,
list(set(benefit_node))::out, list(set(cost_node))::out,
set(prog_var)::out) is det.
apply_matching_loop(CellVar, CellVarFlushedLater, OptParams, PathInfos,
CandidateArgVars0, BenefitNodeSets, CostNodeSets,
ViaCellVars) :-
list__map3(apply_matching_for_path(CellVar, CellVarFlushedLater,
OptParams, CandidateArgVars0), PathInfos,
BenefitNodeSets0, CostNodeSets0, PathViaCellVars),
( list__all_same(PathViaCellVars) ->
BenefitNodeSets = BenefitNodeSets0,
CostNodeSets = CostNodeSets0,
( PathViaCellVars = [ViaCellVarsPrime | _] ->
ViaCellVars = ViaCellVarsPrime
;
ViaCellVars = set__init
)
;
CandidateArgVars1 = set__intersect_list(PathViaCellVars),
FixpointLoop = OptParams ^ fixpoint_loop,
(
FixpointLoop = no,
BenefitNodeSets = BenefitNodeSets0,
CostNodeSets = CostNodeSets0,
ViaCellVars = CandidateArgVars1
;
FixpointLoop = yes,
apply_matching_loop(CellVar, CellVarFlushedLater,
OptParams, PathInfos, CandidateArgVars1,
BenefitNodeSets, CostNodeSets, ViaCellVars)
)
).
:- pred apply_matching_for_path(prog_var::in, bool::in, opt_params::in,
set(prog_var)::in, match_path_info::in,
set(benefit_node)::out, set(cost_node)::out, set(prog_var)::out)
is det.
apply_matching_for_path(CellVar, CellVarFlushedLater, OptParams,
CandidateArgVars, PathInfo, BenefitNodes, CostNodes,
ViaCellVars) :-
( set__empty(CandidateArgVars) ->
BenefitNodes = set__init,
CostNodes = set__init,
ViaCellVars = set__init
;
PathInfo = match_path_info(FirstSegment, LaterSegments),
MatchingParams = OptParams ^ matching_params,
find_via_cell_vars(CellVar, CandidateArgVars,
CellVarFlushedLater, FirstSegment, LaterSegments,
MatchingParams, BenefitNodes, CostNodes, ViaCellVars)
).
:- pred record_matching_result(prog_var::in, cons_id::in, list(prog_var)::in,
set(prog_var)::in, hlds_goal::in, set(anchor)::in,
set(interval_id)::in, opt_info::in, opt_info::out) is det.
record_matching_result(CellVar, ConsId, ArgVars, ViaCellVars, Goal,
PotentialAnchors, PotentialIntervals, OptInfo0, OptInfo) :-
( set__empty(ViaCellVars) ->
OptInfo = OptInfo0
;
set__to_sorted_list(PotentialIntervals, PotentialIntervalList),
set__to_sorted_list(PotentialAnchors, PotentialAnchorList),
list__foldl2(
record_cell_var_for_interval(CellVar, ViaCellVars),
PotentialIntervalList, OptInfo0, OptInfo1,
set__init, InsertIntervals),
list__foldl2(
add_anchor_inserts(Goal, ViaCellVars, InsertIntervals),
PotentialAnchorList, OptInfo1, OptInfo2,
set__init, InsertAnchors),
Goal = _ - GoalInfo,
goal_info_get_goal_path(GoalInfo, GoalPath),
MatchingResult = matching_result(CellVar, ConsId,
ArgVars, ViaCellVars, GoalPath,
PotentialIntervals, InsertIntervals,
PotentialAnchors, InsertAnchors),
MatchingResults0 = OptInfo2 ^ matching_results,
MatchingResults = [MatchingResult | MatchingResults0],
OptInfo = OptInfo2 ^ matching_results := MatchingResults
).
:- pred record_cell_var_for_interval(prog_var::in, set(prog_var)::in,
interval_id::in, opt_info::in, opt_info::out,
set(interval_id)::in, set(interval_id)::out) is det.
record_cell_var_for_interval(CellVar, ViaCellVars, IntervalId,
OptInfo0, OptInfo, InsertIntervals0, InsertIntervals) :-
record_interval_vars(IntervalId, [CellVar], OptInfo0, OptInfo1),
delete_interval_vars(IntervalId, ViaCellVars, DeletedVars,
OptInfo1, OptInfo),
( set__non_empty(DeletedVars) ->
set__insert(InsertIntervals0, IntervalId, InsertIntervals)
;
InsertIntervals = InsertIntervals0
).
:- pred add_anchor_inserts(hlds_goal::in, set(prog_var)::in,
set(interval_id)::in, anchor::in, opt_info::in, opt_info::out,
set(anchor)::in, set(anchor)::out) is det.
add_anchor_inserts(Goal, ArgVarsViaCellVar, InsertIntervals, Anchor,
OptInfo0, OptInfo, InsertAnchors0, InsertAnchors) :-
map__lookup(OptInfo0 ^ anchor_follow_map, Anchor, AnchorFollow),
AnchorFollow = _ - AnchorIntervals,
set__intersect(AnchorIntervals, InsertIntervals,
AnchorInsertIntervals),
( set__non_empty(AnchorInsertIntervals) ->
Insert = insert_spec(Goal, ArgVarsViaCellVar),
InsertMap0 = OptInfo0 ^ left_anchor_inserts,
( map__search(InsertMap0, Anchor, Inserts0) ->
Inserts = [Insert | Inserts0],
map__det_update(InsertMap0, Anchor, Inserts, InsertMap)
;
Inserts = [Insert],
map__det_insert(InsertMap0, Anchor, Inserts, InsertMap)
),
OptInfo = OptInfo0 ^ left_anchor_inserts := InsertMap,
set__insert(InsertAnchors0, Anchor, InsertAnchors)
;
OptInfo = OptInfo0,
InsertAnchors = InsertAnchors0
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- type current_segment_first_flush
---> current_is_before_first_flush
; current_is_after_first_flush.
:- type path
---> path(
flush_state :: current_segment_first_flush,
current_segment :: set(prog_var),
first_segment :: set(prog_var),
other_segments :: list(set(prog_var)),
flush_anchors :: set(anchor),
occurring_intervals :: set(interval_id)
).
:- type all_paths
---> all_paths(
set(path), % The set of all paths so far.
bool, % Have we stepped over model_non goals?
set(prog_var) % The vars which are known to be used
% after the deconstruction goes out of
% scope.
).
:- pred extract_match_and_save_info(path::in, match_path_info::out,
set(anchor)::out, set(interval_id)::out) is det.
extract_match_and_save_info(Path0, MatchPathInfo, Anchors, Intervals) :-
Path = close_path(Path0),
FirstSegment = Path ^ first_segment,
OtherSegments = Path ^ other_segments,
MatchPathInfo = match_path_info(FirstSegment, OtherSegments),
Anchors = Path ^ flush_anchors,
Intervals = Path ^ occurring_intervals.
:- func close_path(path) = path.
close_path(Path0) = Path :-
Path0 = path(FlushState, CurSegment, FirstSegment0, OtherSegments0,
FlushAnchors, IntervalIds),
( FlushState = current_is_before_first_flush ->
require(set__empty(FirstSegment0),
"close_path: FirstSegment0 not empty"),
FirstSegment = CurSegment,
OtherSegments = OtherSegments0
; set__empty(CurSegment) ->
FirstSegment = FirstSegment0,
OtherSegments = OtherSegments0
;
FirstSegment = FirstSegment0,
OtherSegments = [CurSegment | OtherSegments0]
),
Path = path(current_is_after_first_flush, set__init,
FirstSegment, OtherSegments, FlushAnchors, IntervalIds).
:- func add_interval_to_path(interval_id, set(prog_var), path) = path.
add_interval_to_path(IntervalId, Vars, Path0) = Path :-
( set__empty(Vars) ->
Path = Path0
;
CurSegment0 = Path0 ^ current_segment,
CurSegment = set__union(Vars, CurSegment0),
OccurringIntervals0 = Path0 ^ occurring_intervals,
OccurringIntervals = set__insert(OccurringIntervals0,
IntervalId),
Path = (Path0 ^ current_segment := CurSegment)
^ occurring_intervals := OccurringIntervals
).
:- func add_anchor_to_path(anchor, path) = path.
add_anchor_to_path(Anchor, Path0) = Path :-
Anchors0 = Path0 ^ flush_anchors,
Anchors = set__insert(Anchors0, Anchor),
Path = Path0 ^ flush_anchors := Anchors.
:- func anchor_requires_close(opt_info, anchor) = bool.
anchor_requires_close(_, proc_start) = no.
anchor_requires_close(_, proc_end) = yes.
anchor_requires_close(OptInfo, branch_start(_, GoalPath)) =
resume_save_status_requires_close(ResumeSaveStatus) :-
map__lookup(OptInfo ^ branch_resume_map, GoalPath, ResumeSaveStatus).
anchor_requires_close(_, cond_then(_)) = no.
anchor_requires_close(_, branch_end(BranchConstruct, _)) =
( BranchConstruct = neg ->
no
;
yes
).
anchor_requires_close(_, call_site(_)) = yes.
:- func resume_save_status_requires_close(resume_save_status) = bool.
resume_save_status_requires_close(has_resume_save) = yes.
resume_save_status_requires_close(has_no_resume_save) = no.
:- pred may_have_no_successor(anchor::in) is semidet.
may_have_no_successor(Anchor) :-
may_have_no_successor(Anchor, yes).
:- pred may_have_no_successor(anchor::in, bool::out) is det.
may_have_no_successor(proc_start, no).
may_have_no_successor(proc_end, yes).
may_have_no_successor(branch_start(_, _), no).
may_have_no_successor(cond_then(_), no).
may_have_no_successor(branch_end(_, _), no).
may_have_no_successor(call_site(_), yes). % if the call cannot succeed
:- pred may_have_one_successor(anchor::in) is semidet.
may_have_one_successor(Anchor) :-
may_have_one_successor(Anchor, yes).
:- pred may_have_one_successor(anchor::in, bool::out) is det.
may_have_one_successor(proc_start, yes).
may_have_one_successor(proc_end, no).
may_have_one_successor(branch_start(_, _), yes).
may_have_one_successor(cond_then(_), yes).
may_have_one_successor(branch_end(_, _), yes).
may_have_one_successor(call_site(_), yes).
:- pred may_have_more_successors(anchor::in) is semidet.
may_have_more_successors(Anchor) :-
may_have_more_successors(Anchor, yes).
:- pred may_have_more_successors(anchor::in, bool::out) is det.
may_have_more_successors(proc_start, no).
may_have_more_successors(proc_end, no).
may_have_more_successors(branch_start(Type, _), MayHave) :-
( Type = neg ->
MayHave = no
;
MayHave = yes
).
may_have_more_successors(cond_then(_), no).
may_have_more_successors(branch_end(_, _), no).
may_have_more_successors(call_site(_), no).
:- 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 find_all_branches_from_cur_interval(set(prog_var)::in,
match_info::out, opt_info::in, opt_info::out) is det.
find_all_branches_from_cur_interval(RelevantVars, MatchInfo,
OptInfo, OptInfo) :-
IntervalId = OptInfo ^ cur_interval,
map__lookup(OptInfo ^ interval_vars, IntervalId, IntervalVars),
IntervalRelevantVars = set__intersect(RelevantVars, IntervalVars),
Path0 = path(current_is_before_first_flush, IntervalRelevantVars,
set__init, [], set__init, set__init),
AllPaths0 = all_paths(set__make_singleton_set(Path0), no, set__init),
find_all_branches(RelevantVars, IntervalId, no, OptInfo,
AllPaths0, AllPaths),
AllPaths = all_paths(Paths, AfterModelNon, RelevantAfter),
set__to_sorted_list(Paths, PathList),
list__map3(extract_match_and_save_info, PathList,
MatchInputs, FlushAnchorSets, OccurringIntervalSets),
FlushAnchors = set__union_list(FlushAnchorSets),
OccurringIntervals = set__union_list(OccurringIntervalSets),
MatchInfo = match_info(MatchInputs, RelevantAfter, AfterModelNon,
FlushAnchors, OccurringIntervals).
:- pred find_all_branches(set(prog_var)::in, interval_id::in,
maybe(anchor)::in, opt_info::in,
all_paths::in, all_paths::out) is det.
find_all_branches(RelevantVars, IntervalId, MaybeSearchAnchor0,
OptInfo, AllPaths0, AllPaths) :-
map__lookup(OptInfo ^ interval_end, IntervalId, End),
map__lookup(OptInfo ^ interval_succ, IntervalId, SuccessorIds),
(
SuccessorIds = [],
require(may_have_no_successor(End),
"find_all_branches: unexpected no successor"),
% require(unify(MaybeSearchAnchor0, no),
% "find_all_branches: no successor while in search"),
% that test may fail if we come to a call that cannot succeed
AllPaths = AllPaths0
;
SuccessorIds = [SuccessorId | MoreSuccessorIds],
(
MoreSuccessorIds = [],
require(may_have_one_successor(End),
"find_all_branches: unexpected one successor")
;
MoreSuccessorIds = [_ | _],
require(may_have_more_successors(End),
"find_all_branches: unexpected more successors")
),
(
MaybeSearchAnchor0 = yes(SearchAnchor0),
End = SearchAnchor0
->
AllPaths0 = all_paths(Paths0, AfterModelNon, _),
AllPaths = all_paths(Paths0, AfterModelNon, set__init)
;
End = branch_end(_, EndGoalPath),
map__lookup(OptInfo ^ branch_end_map, EndGoalPath,
BranchEndInfo),
OnStackAfterBranch =
BranchEndInfo ^ flushed_after_branch,
AccessedAfterBranch =
BranchEndInfo ^ accessed_after_branch,
NeededAfterBranch = set__union(OnStackAfterBranch,
AccessedAfterBranch),
RelevantAfter = set__intersect(RelevantVars,
NeededAfterBranch),
set__non_empty(RelevantAfter)
->
AllPaths0 = all_paths(Paths0, AfterModelNon, _),
AllPaths = all_paths(Paths0, AfterModelNon,
RelevantAfter)
;
find_all_branches_from(End, RelevantVars,
MaybeSearchAnchor0, OptInfo,
[SuccessorId | MoreSuccessorIds],
AllPaths0, AllPaths)
)
).
:- pred find_all_branches_from(anchor::in, set(prog_var)::in,
maybe(anchor)::in, opt_info::in, list(interval_id)::in,
all_paths::in, all_paths::out) is det.
find_all_branches_from(End, RelevantVars, MaybeSearchAnchor0, OptInfo,
SuccessorIds, AllPaths0, AllPaths) :-
( anchor_requires_close(OptInfo, End) = yes ->
AllPaths0 = all_paths(Paths0, AfterModelNon,
RelevantAfter),
Paths1 = set__map(close_path, Paths0),
AllPaths1 = all_paths(Paths1, AfterModelNon,
RelevantAfter)
;
AllPaths1 = AllPaths0
),
OptParams = OptInfo ^ opt_params,
FullPath = OptParams ^ full_path,
(
FullPath = yes,
End = branch_start(disj, EndGoalPath)
->
MaybeSearchAnchor1 = yes(branch_end(disj, EndGoalPath)),
one_after_another(RelevantVars, MaybeSearchAnchor1,
OptInfo, SuccessorIds, AllPaths1, AllPaths2),
map__lookup(OptInfo ^ branch_end_map, EndGoalPath,
BranchEndInfo),
ContinueId = BranchEndInfo ^ interval_after_branch,
apply_interval_find_all_branches(RelevantVars,
MaybeSearchAnchor0, OptInfo,
AllPaths2, ContinueId, AllPaths)
;
FullPath = yes,
End = branch_start(ite, EndGoalPath)
->
( SuccessorIds = [ElseStartIdPrime, CondStartIdPrime] ->
ElseStartId = ElseStartIdPrime,
CondStartId = CondStartIdPrime
;
error("find_all_branches_from: ite not else, cond")
),
MaybeSearchAnchorCond = yes(cond_then(EndGoalPath)),
apply_interval_find_all_branches(RelevantVars,
MaybeSearchAnchorCond, OptInfo, AllPaths1,
CondStartId, AllPaths2),
MaybeSearchAnchorEnd = yes(branch_end(ite, EndGoalPath)),
CondEndMap = OptInfo ^ cond_end_map,
map__lookup(CondEndMap, EndGoalPath, ThenStartId),
one_after_another(RelevantVars, MaybeSearchAnchorEnd, OptInfo,
[ThenStartId, ElseStartId], AllPaths2, AllPaths3),
map__lookup(OptInfo ^ branch_end_map, EndGoalPath,
BranchEndInfo),
ContinueId = BranchEndInfo ^ interval_after_branch,
apply_interval_find_all_branches(RelevantVars,
MaybeSearchAnchor0, OptInfo,
AllPaths3, ContinueId, AllPaths)
;
End = branch_start(BranchType, EndGoalPath)
->
MaybeSearchAnchor1 = yes(branch_end(BranchType, EndGoalPath)),
list__map(apply_interval_find_all_branches(RelevantVars,
MaybeSearchAnchor1, OptInfo, AllPaths1),
SuccessorIds, AllPathsList),
consolidate_after_join(AllPathsList, AllPaths2),
map__lookup(OptInfo ^ branch_end_map, EndGoalPath,
BranchEndInfo),
ContinueId = BranchEndInfo ^ interval_after_branch,
apply_interval_find_all_branches(RelevantVars,
MaybeSearchAnchor0, OptInfo,
AllPaths2, ContinueId, AllPaths)
;
( SuccessorIds = [SuccessorId] ->
apply_interval_find_all_branches(RelevantVars,
MaybeSearchAnchor0, OptInfo,
AllPaths1, SuccessorId, AllPaths)
;
error("more successor ids")
)
).
:- pred one_after_another(set(prog_var)::in, maybe(anchor)::in, opt_info::in,
list(interval_id)::in, all_paths::in, all_paths::out) is det.
one_after_another(_, _, _, [], AllPaths, AllPaths).
one_after_another(RelevantVars, MaybeSearchAnchor1, OptInfo,
[SuccessorId | MoreSuccessorIds], AllPaths0, AllPaths) :-
apply_interval_find_all_branches(RelevantVars, MaybeSearchAnchor1,
OptInfo, AllPaths0, SuccessorId, AllPaths1),
one_after_another(RelevantVars, MaybeSearchAnchor1, OptInfo,
MoreSuccessorIds, AllPaths1, AllPaths).
:- pred apply_interval_find_all_branches(set(prog_var)::in,
maybe(anchor)::in, opt_info::in, all_paths::in,
interval_id::in, all_paths::out) is det.
apply_interval_find_all_branches(RelevantVars, MaybeSearchAnchor0,
OptInfo, AllPaths0, IntervalId, AllPaths) :-
map__lookup(OptInfo ^ interval_vars, IntervalId, IntervalVars),
RelevantIntervalVars = set__intersect(RelevantVars, IntervalVars),
AllPaths0 = all_paths(Paths0, AfterModelNon0, RelevantAfter),
Paths1 = set__map(
add_interval_to_path(IntervalId, RelevantIntervalVars),
Paths0),
map__lookup(OptInfo ^ interval_start, IntervalId, Start),
(
% Check if intervals starting at Start use any RelevantVars.
( Start = call_site(_)
; Start = branch_end(_, _)
; Start = branch_start(_, _)
),
map__search(OptInfo ^ anchor_follow_map, Start, StartInfo),
StartInfo = AnchorFollowVars - _,
set__intersect(RelevantVars, AnchorFollowVars, NeededVars),
set__non_empty(NeededVars)
->
Paths2 = set__map(
add_anchor_to_path(Start),
Paths1)
;
Paths2 = Paths1
),
( set__member(Start, OptInfo ^ model_non_anchors) ->
AfterModelNon = yes
;
AfterModelNon = AfterModelNon0
),
AllPaths2 = all_paths(Paths2, AfterModelNon, RelevantAfter),
find_all_branches(RelevantVars, IntervalId,
MaybeSearchAnchor0, OptInfo, AllPaths2, AllPaths).
:- pred consolidate_after_join(list(all_paths)::in,
all_paths::out) is det.
consolidate_after_join([], _) :-
error("consolidate_after_join: no paths to join").
consolidate_after_join([First | Rest], AllPaths) :-
PathsList = list__map(project_paths_from_all_paths, [First | Rest]),
Paths0 = set__union_list(PathsList),
Paths = compress_paths(Paths0),
AfterModelNonList = list__map(project_after_model_non_from_all_paths,
[First | Rest]),
bool__or_list(AfterModelNonList, AfterModelNon),
AllPaths = all_paths(Paths, AfterModelNon, set__init).
:- func project_paths_from_all_paths(all_paths) = set(path).
project_paths_from_all_paths(all_paths(Paths, _, _)) = Paths.
:- func project_after_model_non_from_all_paths(all_paths) = bool.
project_after_model_non_from_all_paths(all_paths(_, AfterModelNon, _)) =
AfterModelNon.
:- func compress_paths(set(path)) = set(path).
compress_paths(Paths) = Paths.
% XXX should reduce the cardinality of Paths below a threshold.
% XXX should try to preserve the current segment.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- type var_info
---> var_info(
varset :: prog_varset,
vartypes :: vartypes
).
:- type rename_map == map(prog_var, prog_var).
:- 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) is det.
record_decisions_in_goal(conj(Goals0) - GoalInfo, conj(Goals) - GoalInfo,
VarInfo0, VarInfo, VarRename0, VarRename, InsertMap) :-
record_decisions_in_conj(Goals0, Goals, VarInfo0, VarInfo,
VarRename0, VarRename, InsertMap).
record_decisions_in_goal(par_conj(Goals0) - GoalInfo,
par_conj(Goals) - GoalInfo, VarInfo0, VarInfo,
VarRename0, map__init, InsertMap) :-
record_decisions_in_par_conj(Goals0, Goals, VarInfo0, VarInfo,
VarRename0, InsertMap).
record_decisions_in_goal(Goal0, Goal, VarInfo0, VarInfo,
VarRename0, VarRename, InsertMap) :-
Goal0 = disj(Goals0) - GoalInfo0,
construct_anchors(disj, Goal0, StartAnchor, EndAnchor),
( Goals0 = [FirstGoal0 | LaterGoals0] ->
record_decisions_in_goal(FirstGoal0, FirstGoal,
VarInfo0, VarInfo1, VarRename0, _, InsertMap),
lookup_inserts(InsertMap, StartAnchor, StartInserts),
record_decisions_in_disj(LaterGoals0, LaterGoals,
VarInfo1, VarInfo2, VarRename0, StartInserts,
InsertMap),
Goals = [FirstGoal | LaterGoals],
Goal1 = disj(Goals) - GoalInfo0,
lookup_inserts(InsertMap, EndAnchor, Inserts),
insert_goals_after(Goal1, Goal, VarInfo2, VarInfo,
VarRename, Inserts)
;
Goal = disj(Goals0) - GoalInfo0,
VarInfo = VarInfo0,
VarRename = VarRename0
).
record_decisions_in_goal(Goal0, Goal, VarInfo0, VarInfo, VarRename0, VarRename,
InsertMap) :-
Goal0 = switch(Var0, Det, Cases0) - GoalInfo0,
record_decisions_in_cases(Cases0, Cases, VarInfo0, VarInfo1,
VarRename0, InsertMap),
rename_var(Var0, no, VarRename0, Var),
Goal1 = switch(Var, Det, Cases) - GoalInfo0,
construct_anchors(switch, Goal0, _StartAnchor, EndAnchor),
lookup_inserts(InsertMap, EndAnchor, Inserts),
insert_goals_after(Goal1, Goal, VarInfo1, VarInfo,
VarRename, Inserts).
record_decisions_in_goal(Goal0, Goal, VarInfo0, VarInfo, VarRename0, VarRename,
InsertMap) :-
Goal0 = not(NegGoal0) - GoalInfo0,
record_decisions_in_goal(NegGoal0, NegGoal, VarInfo0, VarInfo1,
VarRename0, _, InsertMap),
Goal1 = not(NegGoal) - GoalInfo0,
construct_anchors(neg, Goal0, _StartAnchor, EndAnchor),
lookup_inserts(InsertMap, EndAnchor, Inserts),
% XXX
insert_goals_after(Goal1, Goal, VarInfo1, VarInfo,
VarRename, Inserts).
record_decisions_in_goal(Goal0, Goal, VarInfo0, VarInfo, VarRename0, VarRename,
InsertMap) :-
Goal0 = if_then_else(Vars0, Cond0, Then0, Else0) - GoalInfo0,
construct_anchors(ite, Goal0, StartAnchor, EndAnchor),
rename_var_list(Vars0, no, VarRename0, Vars),
record_decisions_in_goal(Cond0, Cond, VarInfo0, VarInfo1,
VarRename0, VarRename1, InsertMap),
record_decisions_in_goal(Then0, Then, VarInfo1, VarInfo2,
VarRename1, _, InsertMap),
lookup_inserts(InsertMap, StartAnchor, StartInserts),
make_inserted_goals(VarInfo2, VarInfo3, map__init, VarRenameElse,
StartInserts, StartInsertGoals),
record_decisions_in_goal(Else0, Else1, VarInfo3, VarInfo4,
VarRenameElse, _, InsertMap),
Else0 = _ - ElseGoalInfo0,
conj_list_to_goal(list__append(StartInsertGoals, [Else1]),
ElseGoalInfo0, Else),
Goal1 = if_then_else(Vars, Cond, Then, Else) - GoalInfo0,
lookup_inserts(InsertMap, EndAnchor, EndInserts),
insert_goals_after(Goal1, Goal, VarInfo4, VarInfo,
VarRename, EndInserts).
record_decisions_in_goal(some(Vars0, CanRemove, Goal0) - GoalInfo,
some(Vars, CanRemove, Goal) - GoalInfo, VarInfo0, VarInfo,
VarRename0, VarRename, InsertMap) :-
rename_var_list(Vars0, no, VarRename0, Vars),
record_decisions_in_goal(Goal0, Goal, VarInfo0, VarInfo,
VarRename0, VarRename, InsertMap).
record_decisions_in_goal(Goal0, Goal, VarInfo0, VarInfo,
VarRename0, VarRename, InsertMap) :-
Goal0 = generic_call(_,_,_,_) - _,
record_decisions_at_call_site(Goal0, Goal, VarInfo0, VarInfo,
VarRename0, VarRename, yes, InsertMap).
record_decisions_in_goal(Goal0, Goal, VarInfo0, VarInfo, VarRename0, VarRename,
InsertMap) :-
Goal0 = call(_, _, _, Builtin, _, _) - _,
( Builtin = inline_builtin ->
MustHaveMap = no
;
MustHaveMap = yes
),
record_decisions_at_call_site(Goal0, Goal, VarInfo0, VarInfo,
VarRename0, VarRename, MustHaveMap, InsertMap).
record_decisions_in_goal(Goal0, Goal, VarInfo0, VarInfo, VarRename0, VarRename,
InsertMap) :-
Goal0 = foreign_proc(_, _, _, _, _, _) - _,
record_decisions_at_call_site(Goal0, Goal, VarInfo0, VarInfo,
VarRename0, VarRename, no, InsertMap).
record_decisions_in_goal(Goal0 - GoalInfo0, Goal - GoalInfo, VarInfo, VarInfo,
VarRename, VarRename, _InsertMap) :-
Goal0 = unify(_,_,_,_,_),
rename_vars_in_goal(Goal0 - GoalInfo0, VarRename, Goal - GoalInfo).
record_decisions_in_goal(shorthand(_) - _, _, _, _, _, _, _) :-
error("shorthand in record_decisions_in_goal").
%-----------------------------------------------------------------------------%
:- pred insert_goals_after(hlds_goal::in, hlds_goal::out,
var_info::in, var_info::out, rename_map::out,
list(insert_spec)::in) is det.
insert_goals_after(BranchesGoal, Goal, VarInfo0, VarInfo, VarRename,
Inserts) :-
make_inserted_goals(VarInfo0, VarInfo, map__init, VarRename,
Inserts, InsertGoals),
BranchesGoal = _ - 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,
list(hlds_goal)::out) is det.
make_inserted_goals(VarInfo, VarInfo, VarRename, VarRename, [], []).
make_inserted_goals(VarInfo0, VarInfo, VarRename0, VarRename,
[Spec | Specs], [Goal | Goals]) :-
make_inserted_goal(VarInfo0, VarInfo1, VarRename0, VarRename1,
Spec, Goal),
make_inserted_goals(VarInfo1, VarInfo, VarRename1, VarRename,
Specs, Goals).
:- pred make_inserted_goal(var_info::in, var_info::out,
rename_map::in, rename_map::out, insert_spec::in,
hlds_goal::out) is det.
make_inserted_goal(VarInfo0, VarInfo, VarRename0, VarRename, Spec, Goal) :-
Spec = insert_spec(Goal0, VarsToExtract),
Goal0 = GoalExpr0 - GoalInfo0,
(
GoalExpr0 = unify(_, _, _, Unification0, _),
Unification0 = deconstruct(_, _, ArgVars, _, _, _)
->
Unification1 = Unification0 ^ deconstruct_can_fail
:= cannot_fail,
GoalExpr1 = GoalExpr0 ^ unify_kind := Unification1,
goal_info_set_determinism(GoalInfo0, det, GoalInfo1),
goal_info_add_feature(GoalInfo1, stack_opt, GoalInfo2),
Goal2 = GoalExpr1 - GoalInfo2,
VarInfo0 = 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__merge(VarRename0, NewRename, VarRename),
% We rename the original goal with the
rename_vars_in_goal(Goal2, VarRename, Goal3),
rename_vars_in_goal(Goal3, VoidRename, Goal)
;
error("make_inserted_goal: not a deconstruct")
).
:- 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, VarSet, VarTypes, VarTypes,
VarRename, VarRename, VoidRename, VoidRename).
create_shadow_vars([Arg | Args], VarsToExtract, VarSet0, VarSet,
VarTypes0, VarTypes, VarRename0, VarRename,
VoidRename0, VoidRename) :-
create_shadow_var(Arg, VarsToExtract, VarSet0, VarSet1,
VarTypes0, VarTypes1, VarRename0, VarRename1,
VoidRename0, VoidRename1),
create_shadow_vars(Args, VarsToExtract, VarSet1, VarSet,
VarTypes1, VarTypes, VarRename1, VarRename,
VoidRename1, 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, VarSet0, VarSet, VarTypes0, VarTypes,
VarRename0, VarRename, VoidRename0, VoidRename) :-
varset__lookup_name(VarSet0, Arg, Name),
varset__new_named_var(VarSet0, Name, Shadow, VarSet),
map__lookup(VarTypes0, Arg, Type),
map__det_insert(VarTypes0, Shadow, Type, VarTypes),
( set__member(Arg, VarsToExtract) ->
map__det_insert(VarRename0, Arg, Shadow, VarRename),
VoidRename = VoidRename0
;
VarRename = VarRename0,
map__det_insert(VoidRename0, 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) is det.
record_decisions_at_call_site(Goal0, Goal, VarInfo0, VarInfo,
VarRename0, VarRename, MustHaveMap, InsertMap) :-
Goal0 = _ - GoalInfo0,
rename_vars_in_goal(Goal0, VarRename0, Goal1),
(
goal_info_maybe_get_maybe_need_across_call(GoalInfo0,
MaybeNeedAcrossCall),
MaybeNeedAcrossCall = yes(_NeedAcrossCall)
->
goal_info_get_goal_path(GoalInfo0, GoalPath),
Anchor = call_site(GoalPath),
lookup_inserts(InsertMap, Anchor, Inserts),
insert_goals_after(Goal1, Goal, VarInfo0, VarInfo,
VarRename, Inserts)
;
(
MustHaveMap = no,
Goal = Goal1,
VarInfo = VarInfo0,
VarRename = VarRename0
;
MustHaveMap = yes,
error("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,
insert_map::in) is det.
record_decisions_in_conj([], [], VarInfo, VarInfo, VarRename, VarRename, _).
record_decisions_in_conj([Goal0 | Goals0], Goals, VarInfo0, VarInfo,
VarRename0, VarRename, InsertMap) :-
record_decisions_in_goal(Goal0, Goal1, VarInfo0, VarInfo1,
VarRename0, VarRename1, InsertMap),
record_decisions_in_conj(Goals0, Goals1, VarInfo1, VarInfo,
VarRename1, VarRename, InsertMap),
( Goal1 = conj(SubGoals) - _ ->
Goals = list__append(SubGoals, Goals1)
;
Goals = [Goal1 | Goals1]
).
:- pred record_decisions_in_par_conj(list(hlds_goal)::in, list(hlds_goal)::out,
var_info::in, var_info::out, rename_map::in, insert_map::in) is det.
record_decisions_in_par_conj([], [], VarInfo, VarInfo, _, _).
record_decisions_in_par_conj([Goal0 | Goals0], [Goal | Goals],
VarInfo0, VarInfo, VarRename0, InsertMap) :-
record_decisions_in_goal(Goal0, Goal, VarInfo0, VarInfo1,
VarRename0, _, InsertMap),
record_decisions_in_par_conj(Goals0, Goals, VarInfo1, VarInfo,
VarRename0, InsertMap).
:- 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) is det.
record_decisions_in_disj([], [], VarInfo, VarInfo, _, _, _).
record_decisions_in_disj([Goal0 | Goals0], [Goal | Goals], VarInfo0, VarInfo,
VarRename0, Inserts, InsertMap) :-
make_inserted_goals(VarInfo0, VarInfo1, map__init, VarRename1,
Inserts, InsertGoals),
Goal0 = _ - GoalInfo0,
record_decisions_in_goal(Goal0, Goal1, VarInfo1, VarInfo2,
VarRename1, _, InsertMap),
conj_list_to_goal(list__append(InsertGoals, [Goal1]),
GoalInfo0, Goal),
record_decisions_in_disj(Goals0, Goals, VarInfo2, VarInfo,
VarRename0, Inserts, InsertMap).
:- pred record_decisions_in_cases(list(case)::in, list(case)::out,
var_info::in, var_info::out, rename_map::in, insert_map::in) is det.
record_decisions_in_cases([], [], VarInfo, VarInfo, _, _).
record_decisions_in_cases([case(Var, Goal0) | Cases0],
[case(Var, Goal) | Cases], VarInfo0, VarInfo,
VarRename0, InsertMap) :-
record_decisions_in_goal(Goal0, Goal, VarInfo0, VarInfo1,
VarRename0, _, InsertMap),
record_decisions_in_cases(Cases0, Cases, VarInfo1, VarInfo,
VarRename0, InsertMap).
%-----------------------------------------------------------------------------%
% 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.
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
;
goal_util__rename_vars_in_goal(Goal0, Subst, 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, Subst).
build_headvar_subst([HeadVar | HeadVars], RenameMap, Subst0, Subst) :-
( map__search(RenameMap, HeadVar, Replacement) ->
map__det_insert(Subst0, Replacement, HeadVar, Subst1),
map__det_insert(Subst1, HeadVar, Replacement, Subst2)
;
Subst2 = Subst0
),
build_headvar_subst(HeadVars, RenameMap, Subst2, Subst).
%-----------------------------------------------------------------------------%
:- pred construct_anchors(branch_construct::in, hlds_goal::in,
anchor::out, anchor::out) is det.
construct_anchors(Construct, Goal, StartAnchor, EndAnchor) :-
Goal = _ - GoalInfo,
goal_info_get_goal_path(GoalInfo, GoalPath),
StartAnchor = branch_start(Construct, GoalPath),
EndAnchor = branch_end(Construct, GoalPath).
%-----------------------------------------------------------------------------%
% This predicate can help debug the correctness of the transformation.
:- pred maybe_write_progress_message(string::in, int::in, int::in,
proc_info::in, module_info::in, io::di, io::uo) is det.
maybe_write_progress_message(Message, DebugStackOpt, PredIdInt, ProcInfo,
ModuleInfo, !IO) :-
( DebugStackOpt = PredIdInt ->
io__write_string(Message, !IO),
io__write_string(":\n", !IO),
proc_info_goal(ProcInfo, Goal),
proc_info_varset(ProcInfo, VarSet),
hlds_out__write_goal(Goal, ModuleInfo, VarSet, yes, 0, "\n",
!IO),
io__write_string("\n", !IO)
;
true
).
%-----------------------------------------------------------------------------%
% These predicates can help debug the performance of the transformation.
:- pred dump_opt_info(opt_info::in, io::di, io::uo) is det.
dump_opt_info(OptInfo, !IO) :-
map__keys(OptInfo ^ interval_start, StartIds),
map__keys(OptInfo ^ interval_end, EndIds),
map__keys(OptInfo ^ interval_vars, VarsIds),
map__keys(OptInfo ^ 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(OptInfo), IntervalIds, !IO),
map__to_assoc_list(OptInfo ^ anchor_follow_map, AnchorFollows),
io__write_string("\nANCHOR FOLLOW:\n", !IO),
list__foldl(dump_anchor_follow, AnchorFollows, !IO),
map__to_assoc_list(OptInfo ^ left_anchor_inserts, Inserts),
io__write_string("\nANCHOR INSERT:\n", !IO),
list__foldl(dump_anchor_inserts, Inserts, !IO),
io__write_string("\nMATCHING RESULTS:\n", !IO),
list__foldl(dump_matching_result, OptInfo ^ matching_results, !IO),
io__write_string("\n", !IO).
:- pred dump_interval_info(opt_info::in, interval_id::in, io::di, io::uo)
is det.
dump_interval_info(OptInfo, IntervalId, !IO) :-
io__write_string("\ninterval ", !IO),
io__write_int(interval_id_to_int(IntervalId), !IO),
io__write_string(": ", !IO),
( map__search(OptInfo ^ 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(OptInfo ^ 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(OptInfo ^ 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(OptInfo ^ 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(OptInfo ^ 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 = 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).
:- pred dump_anchor_inserts(pair(anchor, list(insert_spec))::in,
io::di, io::uo) is det.
dump_anchor_inserts(Anchor - InsertSpecs, !IO) :-
io__write_string("\ninsertions after ", !IO),
io__write(Anchor, !IO),
io__write_string(":\n", !IO),
list__foldl(dump_insert, InsertSpecs, !IO).
:- pred dump_insert(insert_spec::in, io::di, io::uo) is det.
dump_insert(insert_spec(Goal, Vars), !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("]: ", !IO),
(
Goal = unify(_, _, _, Unification, _) - _,
Unification = deconstruct(CellVar, ConsId, ArgVars, _,_,_)
->
term__var_to_int(CellVar, CellVarNum),
io__write_int(CellVarNum, !IO),
io__write_string(" => ", !IO),
mercury_output_cons_id(ConsId, does_not_need_brackets, !IO),
io__write_string("(", !IO),
list__map(term__var_to_int, ArgVars, ArgVarNums),
write_int_list(ArgVarNums, !IO),
io__write_string(")\n", !IO)
;
io__write_string("BAD INSERT GOAL\n", !IO)
).
:- pred dump_matching_result(matching_result::in,
io::di, io::uo) is det.
dump_matching_result(MatchingResult, !IO) :-
MatchingResult = matching_result(CellVar, ConsId,
ArgVars, ViaCellVars, GoalPath,
PotentialIntervals, InsertIntervals,
PotentialAnchors, InsertAnchors),
io__write_string("\nmatching result at ", !IO),
io__write(GoalPath, !IO),
io__write_string("\n", !IO),
term__var_to_int(CellVar, CellVarNum),
list__map(term__var_to_int, ArgVars, ArgVarNums),
list__map(term__var_to_int, set__to_sorted_list(ViaCellVars),
ViaCellVarNums),
io__write_int(CellVarNum, !IO),
io__write_string(" => ", !IO),
mercury_output_cons_id(ConsId, does_not_need_brackets, !IO),
io__write_string("(", !IO),
write_int_list(ArgVarNums, !IO),
io__write_string("): via cell ", !IO),
write_int_list(ViaCellVarNums, !IO),
io__write_string("\n", !IO),
io__write_string("potential intervals: ", !IO),
PotentialIntervalNums = list__map(interval_id_to_int,
set__to_sorted_list(PotentialIntervals)),
write_int_list(PotentialIntervalNums, !IO),
io__write_string("\n", !IO),
io__write_string("insert intervals: ", !IO),
InsertIntervalNums = list__map(interval_id_to_int,
set__to_sorted_list(InsertIntervals)),
write_int_list(InsertIntervalNums, !IO),
io__write_string("\n", !IO),
io__write_string("potential anchors: ", !IO),
io__write_list(set__to_sorted_list(PotentialAnchors), " ", io__write,
!IO),
io__write_string("\n", !IO),
io__write_string("insert anchors: ", !IO),
io__write_list(set__to_sorted_list(InsertAnchors), " ", io__write, !IO),
io__write_string("\n", !IO).
:- pred write_int_list(list(int)::in, io::di, io::uo) is det.
write_int_list(List, !IO) :-
io__write_list(List, ", ", io__write_int, !IO).
:- func interval_id_to_int(interval_id) = int.
interval_id_to_int(interval_id(Num)) = Num.
%-----------------------------------------------------------------------------%