Files
mercury/compiler/stack_opt.m
Zoltan Somogyi 8693e293a2 This diff makes hlds_pred.m and many callers of its predicates easier to read
Estimated hours taken: 4
Branches: main

This diff makes hlds_pred.m and many callers of its predicates easier to read
and to maintain, but contains no changes in algorithms whatsoever.

compiler/hlds_pred.m:
	Bring this module into line with our current coding standards.
	Use predmode declarations, functions, and state variable syntax
	when appropriate.

	Reorder arguments of predicates where necessary for the use of state
	variable syntax, and where this improves readability.

	Replace old-style lambdas with new-style lambdas or with partially
	applied named procedures.

	Standardize indentation.

compiler/*.m:
	Conform to the changes in hlds_pred.m. This mostly means using the
	new argument orders of predicates exported by hlds_pred.m. Where this
	is now conveniently possible, change predicates to use state
	variable notation.

	In some modules, using state variable notation required changing the
	orders of arguments in the module's top predicate.

compiler/passes_aux.m:
	Change the order of arguments in the calls this module makes to
	allow the callees to use state variable notation.

	Convert this module to state variable notation too.
2003-10-24 06:17:51 +00:00

2264 lines
79 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 2002-2003 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__state::di, io__state::uo)
is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs__code_model.
:- 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__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__arg_info.
:- 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__state::di, io__state::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) -->
optimize_live_sets_in_conj(Goals).
optimize_live_sets_in_goal(par_conj(Goals) - _GoalInfo) -->
optimize_live_sets_in_par_conj(Goals).
optimize_live_sets_in_goal(disj(Goals) - GoalInfo) -->
( { Goals = [FirstDisjunct | _] } ->
reached_branch_end(GoalInfo, yes(FirstDisjunct), disj,
StartAnchor, EndAnchor, BeforeId, AfterId,
MaybeResumeVars),
optimize_live_sets_in_disj(Goals, doesnt_need_flush,
StartAnchor, EndAnchor, BeforeId, AfterId,
OpenIntervals),
leave_branch_start(disj, StartAnchor, BeforeId,
MaybeResumeVars, OpenIntervals)
;
% 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
).
optimize_live_sets_in_goal(switch(Var, _Det, Cases) - GoalInfo) -->
reached_branch_end(GoalInfo, no, switch,
StartAnchor, EndAnchor, BeforeId, AfterId, MaybeResumeVars),
optimize_live_sets_in_cases(Cases, StartAnchor, EndAnchor,
BeforeId, AfterId, OpenIntervalsList),
{ OpenIntervals = set__union_list(OpenIntervalsList) },
leave_branch_start(switch, StartAnchor, BeforeId, MaybeResumeVars,
OpenIntervals),
require_in_regs([Var]),
require_access([Var]).
optimize_live_sets_in_goal(not(Goal) - GoalInfo) -->
reached_branch_end(GoalInfo, yes(Goal), neg,
StartAnchor, EndAnchor, BeforeId, AfterId, MaybeResumeVars),
enter_branch_tail(EndAnchor, AfterId),
optimize_live_sets_in_goal(Goal),
reached_branch_start(needs_flush, StartAnchor, BeforeId,
OpenIntervals),
leave_branch_start(neg, StartAnchor, BeforeId, MaybeResumeVars,
OpenIntervals).
optimize_live_sets_in_goal(if_then_else(_, Cond, Then, Else) - GoalInfo) -->
reached_branch_end(GoalInfo, yes(Cond), ite, StartAnchor, EndAnchor,
BeforeId, AfterId, MaybeResumeVars),
enter_branch_tail(EndAnchor, AfterId),
optimize_live_sets_in_goal(Then),
reached_cond_then(GoalInfo),
optimize_live_sets_in_goal(Cond),
reached_branch_start(doesnt_need_flush, StartAnchor, BeforeId,
CondOpenIntervals),
enter_branch_tail(EndAnchor, AfterId),
optimize_live_sets_in_goal(Else),
reached_branch_start(needs_flush, StartAnchor, BeforeId,
_ElseOpenIntervals),
leave_branch_start(ite, StartAnchor, BeforeId, MaybeResumeVars,
CondOpenIntervals).
optimize_live_sets_in_goal(some(_Vars, _CanRemove, Goal) - _GoalInfo) -->
optimize_live_sets_in_goal(Goal).
optimize_live_sets_in_goal(Goal - GoalInfo) -->
OptParams =^ 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),
require_access(InputArgs)
;
{ 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)
).
optimize_live_sets_in_goal(Goal - GoalInfo) -->
{ Goal = call(PredId, ProcId, ArgVars, Builtin, _, _) },
OptParams =^ 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),
require_access(Inputs)
;
{ goal_info_get_maybe_need_across_call(GoalInfo,
MaybeNeedAcrossCall) },
optimize_live_sets_at_call(Inputs, MaybeNeedAcrossCall,
GoalInfo)
).
optimize_live_sets_in_goal(Goal - GoalInfo) -->
{ Goal = foreign_proc(_Attributes, PredId, ProcId,
ArgVars, _ArgNames, _OrigArgTypes, _PragmaCode) },
OptParams =^ 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) },
(
{ goal_info_maybe_get_maybe_need_across_call(GoalInfo,
MaybeNeedAcrossCall) },
{ MaybeNeedAcrossCall = yes(_) }
->
optimize_live_sets_at_call(Inputs, MaybeNeedAcrossCall,
GoalInfo)
;
require_in_regs(Inputs),
require_access(Inputs)
).
optimize_live_sets_in_goal(Goal - GoalInfo) -->
{ Goal = unify(_, _, _, Unification, _) },
(
{ Unification = construct(CellVar, _ConsId, ArgVars, _,
HowToConstruct, _, _) },
{ HowToConstruct = reuse_cell(_) ->
error("optimize_live_sets_in_goal: reuse")
;
true
},
require_in_regs(ArgVars),
require_access([CellVar | ArgVars])
% use_cell(CellVar, ArgVars, ConsId, Goal - GoalInfo)
% 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 =^ opt_params,
{ ModuleInfo = OptParams ^ module_info },
( { shared_left_to_right_deconstruct(ModuleInfo, ArgModes) } ->
use_cell(CellVar, ArgVars, ConsId, Goal - GoalInfo)
;
[]
),
require_in_regs([CellVar]),
require_access([CellVar | ArgVars])
;
{ Unification = assign(ToVar, FromVar) },
require_in_regs([FromVar]),
require_access([FromVar, ToVar])
;
{ Unification = simple_test(Var1, Var2) },
require_in_regs([Var1, Var2]),
require_access([Var1, Var2])
;
{ Unification = complicated_unify(_, _, _) },
{ error("optimize_live_sets_in_goal: complicated_unify") }
).
optimize_live_sets_in_goal(shorthand(_) - _) -->
{ 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) -->
(
{ 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),
new_interval_id(BeforeCallId),
record_interval_start(AfterCallId, CallAnchor),
record_interval_end(BeforeCallId, CallAnchor),
{ goal_info_get_instmap_delta(GoalInfo, InstMapDelta) },
OptParams =^ opt_params,
(
{ instmap_delta_is_reachable(InstMapDelta)
; OptParams ^ opt_at_most_zero_calls = no
}
->
record_interval_succ(BeforeCallId, AfterCallId),
{ VarsOnStack = VarsOnStack0 }
;
% If the call cannot succeed, then execution cannot
% get from BeforeCallId to AfterCallId.
record_interval_no_succ(BeforeCallId),
{ VarsOnStack = set__init }
),
set_cur_interval(BeforeCallId),
assign_open_intervals_to_anchor(CallAnchor),
{ goal_info_get_code_model(GoalInfo, CodeModel) },
( { CodeModel = model_non } ->
record_model_non_anchor(CallAnchor)
;
[]
),
one_open_interval(BeforeCallId),
require_flushed(VarsOnStack),
require_in_regs(Inputs),
require_access(Inputs)
;
{ 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([]) --> [].
optimize_live_sets_in_conj([Goal | Goals]) -->
optimize_live_sets_in_conj(Goals),
optimize_live_sets_in_goal(Goal).
:- 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([]) --> [].
optimize_live_sets_in_par_conj([Goal | Goals]) -->
% 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),
optimize_live_sets_in_goal(Goal).
:- 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) --> [].
optimize_live_sets_in_disj([Goal | Goals], MaybeNeedsFlush,
StartAnchor, EndAnchor, BeforeId, AfterId, OpenIntervals) -->
enter_branch_tail(EndAnchor, AfterId),
optimize_live_sets_in_goal(Goal),
reached_branch_start(MaybeNeedsFlush, StartAnchor, BeforeId,
OpenIntervals),
optimize_live_sets_in_disj(Goals, needs_flush, StartAnchor, EndAnchor,
BeforeId, AfterId, _OpenIntervals).
:- 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([], _, _, _, _, []) --> [].
optimize_live_sets_in_cases([case(_Var, Goal) | Cases], StartAnchor, EndAnchor,
BeforeId, AfterId, [OpenIntervals | OpenIntervalsList]) -->
enter_branch_tail(EndAnchor, AfterId),
optimize_live_sets_in_goal(Goal),
reached_branch_start(doesnt_need_flush, StartAnchor, BeforeId,
OpenIntervals),
optimize_live_sets_in_cases(Cases, StartAnchor, EndAnchor,
BeforeId, AfterId, OpenIntervalsList).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- 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) -->
{ goal_info_get_goal_path(GoalInfo, GoalPath) },
record_branch_end_info(GoalPath),
{
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),
( { goal_info_maybe_get_store_map(GoalInfo, StoreMap) } ->
{ map__sorted_keys(StoreMap, StoreMapVarList) },
{ set__sorted_list_to_set(StoreMapVarList, StoreMapVars) },
require_flushed(StoreMapVars)
;
{ error("reached_branch_end: no store map") }
),
{ EndAnchor = branch_end(Construct, GoalPath) },
{ StartAnchor = branch_start(Construct, GoalPath) },
assign_open_intervals_to_anchor(EndAnchor),
{ goal_info_get_code_model(GoalInfo, CodeModel) },
( { CodeModel = model_non } ->
record_model_non_anchor(EndAnchor)
;
[]
),
no_open_intervals,
get_cur_interval(AfterIntervalId),
record_interval_start(AfterIntervalId, EndAnchor),
new_interval_id(BeforeIntervalId).
:- pred enter_branch_tail(anchor::in, interval_id::in,
opt_info::in, opt_info::out) is det.
enter_branch_tail(EndAnchor, AfterId) -->
new_interval_id(BranchTailId),
record_interval_end(BranchTailId, EndAnchor),
record_interval_succ(BranchTailId, AfterId),
set_cur_interval(BranchTailId),
one_open_interval(BranchTailId).
:- 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) -->
get_cur_interval(BranchStartId),
record_interval_start(BranchStartId, StartAnchor),
record_interval_succ(BeforeId, BranchStartId),
get_open_intervals(OpenIntervals),
(
{ MaybeNeedsFlush = doesnt_need_flush }
;
{ MaybeNeedsFlush = needs_flush },
assign_open_intervals_to_anchor(StartAnchor)
).
:- pred reached_cond_then(hlds_goal_info::in, opt_info::in, opt_info::out)
is det.
reached_cond_then(GoalInfo) -->
{ goal_info_get_goal_path(GoalInfo, GoalPath) },
record_cond_end(GoalPath),
get_cur_interval(ThenStartId),
record_interval_start(ThenStartId, CondThenAnchor),
new_interval_id(CondTailId),
{ CondThenAnchor = cond_then(GoalPath) },
record_interval_end(CondTailId, CondThenAnchor),
record_interval_succ(CondTailId, ThenStartId),
set_cur_interval(CondTailId),
get_open_intervals(OpenIntervals0),
{ OpenIntervals = set__insert(OpenIntervals0, CondTailId) },
set_open_intervals(OpenIntervals).
:- 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) -->
record_interval_end(BeforeId, StartArchor),
(
{ MaybeResumeVars = yes(ResumeVars) },
require_flushed(ResumeVars)
;
{ MaybeResumeVars = no }
),
set_cur_interval(BeforeId),
set_open_intervals(OpenIntervals).
:- pred get_open_intervals(set(interval_id)::out,
opt_info::in, opt_info::out) is det.
get_open_intervals(OpenIntervals, OptInfo, OptInfo) :-
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) -->
FlushedLater =^ flushed_later,
OptParams =^ 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) }
->
[]
;
{ 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),
{ 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)
;
{ 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)
;
{ AfterModelNon = yes }
)
)
;
[]
).
:- 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__state::di, io__state::uo) is det.
maybe_write_progress_message(Message, DebugStackOpt, PredIdInt, ProcInfo,
ModuleInfo) -->
( { DebugStackOpt = PredIdInt } ->
io__write_string(Message),
io__write_string(":\n"),
{ proc_info_goal(ProcInfo, Goal) },
{ proc_info_varset(ProcInfo, VarSet) },
hlds_out__write_goal(Goal, ModuleInfo, VarSet, yes, 0, "\n"),
io__write_string("\n")
;
[]
).
%-----------------------------------------------------------------------------%
% These predicates can help debug the performance of the transformation.
:- pred dump_opt_info(opt_info::in, io__state::di, io__state::uo) is det.
dump_opt_info(OptInfo) -->
{ 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"),
list__foldl(dump_interval_info(OptInfo), IntervalIds),
{ map__to_assoc_list(OptInfo ^ anchor_follow_map, AnchorFollows) },
io__write_string("\nANCHOR FOLLOW:\n"),
list__foldl(dump_anchor_follow, AnchorFollows),
{ map__to_assoc_list(OptInfo ^ left_anchor_inserts, Inserts) },
io__write_string("\nANCHOR INSERT:\n"),
list__foldl(dump_anchor_inserts, Inserts),
io__write_string("\nMATCHING RESULTS:\n"),
list__foldl(dump_matching_result, OptInfo ^ matching_results),
io__write_string("\n").
:- pred dump_interval_info(opt_info::in, interval_id::in,
io__state::di, io__state::uo) is det.
dump_interval_info(OptInfo, IntervalId) -->
io__write_string("\ninterval "),
io__write_int(interval_id_to_int(IntervalId)),
io__write_string(": "),
( { map__search(OptInfo ^ interval_succ, IntervalId, SuccIds) } ->
{ SuccNums = list__map(interval_id_to_int, SuccIds) },
io__write_string("succ ["),
write_int_list(SuccNums),
io__write_string("]\n")
;
io__write_string("no succ\n")
),
( { map__search(OptInfo ^ interval_start, IntervalId, Start) } ->
io__write_string("start "),
io__write(Start),
io__write_string("\n")
;
io__write_string("no start\n")
),
( { map__search(OptInfo ^ interval_end, IntervalId, End) } ->
io__write_string("end "),
io__write(End),
io__write_string("\n")
;
io__write_string("no end\n")
),
( { map__search(OptInfo ^ interval_vars, IntervalId, Vars) } ->
{ list__map(term__var_to_int, set__to_sorted_list(Vars),
VarNums) },
io__write_string("vars ["),
write_int_list(VarNums),
io__write_string("]\n")
;
io__write_string("no vars\n")
),
( { map__search(OptInfo ^ interval_delvars, IntervalId, Deletions) } ->
io__write_string("deletions"),
list__foldl(dump_deletion, Deletions),
io__write_string("\n")
;
[]
).
:- pred dump_deletion(set(prog_var)::in, io__state::di, io__state::uo) is det.
dump_deletion(Vars) -->
{ list__map(term__var_to_int, set__to_sorted_list(Vars), VarNums) },
io__write_string(" ["),
write_int_list(VarNums),
io__write_string("]").
:- pred dump_anchor_follow(pair(anchor, anchor_follow_info)::in,
io__state::di, io__state::uo) is det.
dump_anchor_follow(Anchor - AnchorFollowInfo) -->
{ AnchorFollowInfo = Vars - Intervals },
io__write_string("\n"),
io__write(Anchor),
io__write_string(" =>\n"),
{ list__map(term__var_to_int, set__to_sorted_list(Vars), VarNums) },
io__write_string("vars ["),
write_int_list(VarNums),
io__write_string("]\nintervals: "),
{ set__to_sorted_list(Intervals, IntervalList) },
write_int_list(list__map(interval_id_to_int, IntervalList)),
io__write_string("\n").
:- pred dump_anchor_inserts(pair(anchor, list(insert_spec))::in,
io__state::di, io__state::uo) is det.
dump_anchor_inserts(Anchor - InsertSpecs) -->
io__write_string("\ninsertions after "),
io__write(Anchor),
io__write_string(":\n"),
list__foldl(dump_insert, InsertSpecs).
:- pred dump_insert(insert_spec::in, io__state::di, io__state::uo) is det.
dump_insert(insert_spec(Goal, Vars)) -->
{ list__map(term__var_to_int, set__to_sorted_list(Vars), VarNums) },
io__write_string("vars ["),
write_int_list(VarNums),
io__write_string("]: "),
(
{ Goal = unify(_, _, _, Unification, _) - _ },
{ Unification = deconstruct(CellVar, ConsId, ArgVars, _,_,_) }
->
{ term__var_to_int(CellVar, CellVarNum) },
io__write_int(CellVarNum),
io__write_string(" => "),
mercury_output_cons_id(ConsId, does_not_need_brackets),
io__write_string("("),
{ list__map(term__var_to_int, ArgVars, ArgVarNums) },
write_int_list(ArgVarNums),
io__write_string(")\n")
;
io__write_string("BAD INSERT GOAL\n")
).
:- pred dump_matching_result(matching_result::in,
io__state::di, io__state::uo) is det.
dump_matching_result(MatchingResult) -->
{ MatchingResult = matching_result(CellVar, ConsId,
ArgVars, ViaCellVars, GoalPath,
PotentialIntervals, InsertIntervals,
PotentialAnchors, InsertAnchors) },
io__write_string("\nmatching result at "),
io__write(GoalPath),
io__write_string("\n"),
{ 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__write_string(" => "),
mercury_output_cons_id(ConsId, does_not_need_brackets),
io__write_string("("),
write_int_list(ArgVarNums),
io__write_string("): via cell "),
write_int_list(ViaCellVarNums),
io__write_string("\n"),
io__write_string("potential intervals: "),
{ PotentialIntervalNums = list__map(interval_id_to_int,
set__to_sorted_list(PotentialIntervals)) },
write_int_list(PotentialIntervalNums),
io__write_string("\n"),
io__write_string("insert intervals: "),
{ InsertIntervalNums = list__map(interval_id_to_int,
set__to_sorted_list(InsertIntervals)) },
write_int_list(InsertIntervalNums),
io__write_string("\n"),
io__write_string("potential anchors: "),
io__write_list(set__to_sorted_list(PotentialAnchors), " ", io__write),
io__write_string("\n"),
io__write_string("insert anchors: "),
io__write_list(set__to_sorted_list(InsertAnchors), " ", io__write),
io__write_string("\n").
:- pred write_int_list(list(int)::in, io__state::di, io__state::uo) is det.
write_int_list(List) --> io__write_list(List, ", ", io__write_int).
:- func interval_id_to_int(interval_id) = int.
interval_id_to_int(interval_id(Num)) = Num.
%-----------------------------------------------------------------------------%