mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-17 14:57:03 +00:00
Estimated hours taken: 20 Branches: main Fix a problem with from_ground_term scopes. When they are built, the scopes are tentantively marked as from_ground_term_construct scopes, and the unifications inside them are in a top down order. Mode analysis therefore expected the unifications inside from_ground_term_construct scopes to have that order. The problem was that mode analysis, when it confirmed that a from_ground_term scope is indeed a from_ground_term_construct scope, itself reversed the order of the unifications, putting them in a bottom up order. When mode analysis is reinvoked, either for unique mode checking, or after cse_detection finds common subexpressions, this meant that mode analysis found the unifications in the "wrong" order, and therefore disregarded the scope, discarding all its performance benefits. This diff separates out the two notions that we previously conflated. The scope kind from_ground_term_construct now refers only to scopes which are definitely known to construct ground terms. We can know that only after mode analysis. Until then, from_ground_term scopes are now marked as from_ground_term_initial. The two kinds have different though overlapping sets of invariants; in particular, they promise different orderings of the unifications in the scope. This diff reduces the time needed to compile mas_objects.data.m from about 221 seconds to about 8. compiler/hlds_goal.m: Add the from_ground_term_initial kind. Document the invariants that each kind of from_ground_term scope promises. compiler/superhomogeneous.m: Mark from_ground_term scopes initially as from_ground_term_initial, not from_ground_term_construct. compiler/post_typecheck.m: Make the predicate that converts function calls that look like unifications (such as X = int.min) into actual function calls say whether it performed such a conversion. compiler/purity.m: Use the new functionality in post_typecheck.m to convert from_ground_term_initial scopes into from_ground_term_other scopes if the conversion of a unification into a call means that we have to break an invariant expected of from_ground_term_initial scopes. compiler/cse_detection.m: compiler/switch_detection.m: Maintain the invariants we now expect of from_ground_term_deconstruct scopes. compiler/modecheck_goal.m: Maintain the invariants we now expect of the different from_ground_term scopes. Avoid traversing such scopes if a previous invocation of mode analysis says we can. Optimize away from_ground_term_construct scopes if the variable being constructed is not needed later. compiler/quantification.m: If the variable named in a from_ground_term_initial or from_ground_term_construct scope is not referred to outside the scope, set the nonlocals set of the scope to empty, which allows later compiler passes to optimize it away. Avoid some unnecessary work by the compiler. compiler/add_trail_ops.m: compiler/closure_analysis.m: compiler/constraint.m: compiler/dead_proc_elim.m: compiler/deep_profile.m: compiler/deforest.m: compiler/delay_construct.m: compiler/delay_partial_inst.m: compiler/dep_par_conj.m: compiler/dependency_graph.m: compiler/exception_analysis.m: compiler/follow_code.m: compiler/follow_vars.m: compiler/goal_form.m: compiler/goal_util.m: compiler/granularity.m: compiler/inlining.m: compiler/interval.m: compiler/lambda.m: compiler/lco.m: compiler/middle_rec.m: compiler/mode_util.m: compiler/parallel_to_plain.m: compiler/simplify.m: compiler/stm_expand.m: compiler/stratify.m: compiler/tabling_analysis.m: compiler/term_pass1.m: compiler/try_expand.m: compiler/tupling.m: compiler/untupling.m: compiler/unused_args.m: Avoid traversing from_ground_term_deconstruct scopes in cases where the invariants that now hold (mainly the absence of anything but deconstruct unifications) make such traversals unnecessary. compiler/live_vars.m: compiler/liveness.m: compiler/structure_reuse.lbu.m: Add comments about exploiting from_ground_term_deconstruct scopes. compiler/det_analysis.m: compiler/hlds_out_goal.m: compiler/polymorphism.m: compiler/saved_vars.m: compiler/unique_modes.m: Handle from_ground_term_initial scopes. compiler/handle_options.m: Add a dump verbosity option that is useful for comparing HLDS dumps created by two different compilers. compiler/type_util.m: Minor speedup. compiler/mode_info.m: compiler/modecheck_conj.m: compiler/prog_data.m: compiler/rbmm.region_transformation.m: compiler/typecheck.m: Improve documentation.
507 lines
20 KiB
Mathematica
507 lines
20 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2005-2011 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: closure_analysis.m
|
|
% Main author: juliensf
|
|
%
|
|
% Perform local closure analysis on procedures. This involves tracking
|
|
% the possible values that a higher-order variable can take within a
|
|
% procedure. We attach this information to places where knowing the
|
|
% possible values of a higher-order call may be useful.
|
|
%
|
|
% This is similar to the analysis done by higher-order specialization, except
|
|
% that here, we do care if a higher-order variable can take multiple values.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module transform_hlds.closure_analysis.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module io.
|
|
|
|
:- pred process_module(module_info::in, module_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
%----------------------------------------------------------------------------%
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.mode_util.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.passes_aux.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_out.
|
|
:- import_module parse_tree.prog_type.
|
|
:- import_module parse_tree.set_of_var.
|
|
:- import_module transform_hlds.dependency_graph.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
:- import_module varset.
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
process_module(!ModuleInfo, !IO) :-
|
|
% XXX At the moment it is not necessary to do this on a per-SCC basis,
|
|
% since the analysis is only procedure-local, but we would eventually
|
|
% like to extend it.
|
|
|
|
module_info_get_globals(!.ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, debug_closure, Debug),
|
|
module_info_ensure_dependency_info(!ModuleInfo),
|
|
module_info_dependency_info(!.ModuleInfo, DepInfo),
|
|
hlds_dependency_info_get_dependency_ordering(DepInfo, SCCs),
|
|
list.foldl2(process_scc(Debug), SCCs, !ModuleInfo, !IO).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
%
|
|
% Perform closure analysis on an SCC.
|
|
%
|
|
|
|
:- pred process_scc(bool::in, list(pred_proc_id)::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
process_scc(Debug, SCC, !ModuleInfo, !IO) :-
|
|
list.foldl2(process_proc(Debug), SCC, !ModuleInfo, !IO).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
% This type represents the possible values of a higher-order valued
|
|
% variable.
|
|
%
|
|
:- type closure_values
|
|
---> unknown
|
|
% The higher-order variable may be bound to something
|
|
% but we don't know what it is.
|
|
|
|
; partial(set(pred_proc_id))
|
|
% The higher-order variable may be bound to these
|
|
% values, or it may be bound to something else we don't
|
|
% know about. (This is intended to be useful in producing
|
|
% error messages for the termination analysis; if one
|
|
% of the higher-order values is definitely non-terminating
|
|
% we can certainly let the user know about it.)
|
|
|
|
; exclusive(set(pred_proc_id)).
|
|
% The higher-order variable will be exclusively bound
|
|
% to this set of values.
|
|
|
|
% We attach a closure_info to each goal where it may be of interest;
|
|
% at the moment calls and generic_calls.
|
|
%
|
|
:- type closure_info == map(prog_var, closure_values).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- func closure_info_init(module_info, vartypes, prog_vars, list(mer_mode))
|
|
= closure_info.
|
|
|
|
closure_info_init(ModuleInfo, VarTypes, HeadVars, ArgModes) = ClosureInfo :-
|
|
partition_arguments(ModuleInfo, VarTypes, HeadVars, ArgModes,
|
|
set_of_var.init, Inputs0, set_of_var.init, _Outputs),
|
|
Inputs = set_of_var.filter(var_has_ho_type(VarTypes), Inputs0),
|
|
set_of_var.fold(insert_unknown, Inputs, map.init, ClosureInfo).
|
|
|
|
% Succeeds iff the given variable has a higher-order type.
|
|
%
|
|
:- pred var_has_ho_type(vartypes::in, prog_var::in) is semidet.
|
|
|
|
var_has_ho_type(VarTypes, Var) :-
|
|
Type = map.lookup(VarTypes, Var),
|
|
type_is_higher_order(Type).
|
|
|
|
% Insert the given prog_var into the closure_info and set the
|
|
% possible values to unknown.
|
|
%
|
|
:- pred insert_unknown(prog_var::in, closure_info::in, closure_info::out)
|
|
is det.
|
|
|
|
insert_unknown(Var, !ClosureInfo) :-
|
|
map.det_insert(Var, unknown, !ClosureInfo).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
%
|
|
% Perform local closure analysis on a procedure.
|
|
%
|
|
|
|
:- pred process_proc(bool::in, pred_proc_id::in,
|
|
module_info::in, module_info::out, io::di, io::uo) is det.
|
|
|
|
process_proc(Debug, PPId, !ModuleInfo, !IO) :-
|
|
module_info_pred_proc_info(!.ModuleInfo, PPId, PredInfo, ProcInfo0),
|
|
proc_info_get_headvars(ProcInfo0, HeadVars),
|
|
proc_info_get_vartypes(ProcInfo0, VarTypes),
|
|
proc_info_get_argmodes(ProcInfo0, ArgModes),
|
|
ClosureInfo0 = closure_info_init(!.ModuleInfo, VarTypes, HeadVars,
|
|
ArgModes),
|
|
write_proc_progress_message("% Analysing closures in ", PPId, !.ModuleInfo,
|
|
!IO),
|
|
proc_info_get_goal(ProcInfo0, Body0),
|
|
process_goal(VarTypes, !.ModuleInfo, Body0, Body,
|
|
ClosureInfo0, _ClosureInfo),
|
|
(
|
|
Debug = yes,
|
|
proc_info_get_varset(ProcInfo, Varset),
|
|
dump_closure_info(Varset, Body, !IO),
|
|
io.flush_output(!IO)
|
|
;
|
|
Debug = no
|
|
),
|
|
proc_info_set_goal(Body, ProcInfo0, ProcInfo),
|
|
module_info_set_pred_proc_info(PPId, PredInfo, ProcInfo, !ModuleInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Track higher-order values through goals.
|
|
%
|
|
|
|
:- pred process_goal(vartypes::in, module_info::in,
|
|
hlds_goal::in, hlds_goal::out, closure_info::in, closure_info::out) is det.
|
|
|
|
process_goal(VarTypes, ModuleInfo, Goal0, Goal, !ClosureInfo) :-
|
|
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
|
|
(
|
|
GoalExpr0 = conj(ConjType, Goals0),
|
|
list.map_foldl(process_goal(VarTypes, ModuleInfo), Goals0, Goals,
|
|
!ClosureInfo),
|
|
GoalExpr = conj(ConjType, Goals),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = plain_call(CallPredId, CallProcId, CallArgs, _, _, _),
|
|
|
|
% Look for any higher-order arguments and divide them
|
|
% into sets of input and output arguments.
|
|
module_info_pred_proc_info(ModuleInfo, CallPredId, CallProcId,
|
|
_CallPredInfo, CallProcInfo),
|
|
proc_info_get_argmodes(CallProcInfo, CallArgModes),
|
|
|
|
% NOTE: We construct sets of arguments, rather than lists,
|
|
% in case there are duplicate arguments.
|
|
|
|
partition_arguments(ModuleInfo, VarTypes, CallArgs, CallArgModes,
|
|
set_of_var.init, InputArgs, set_of_var.init, OutputArgs),
|
|
|
|
% Update the goal_info to include any information about the
|
|
% values of higher-order valued variables.
|
|
|
|
AddValues = (pred(Var::in, !.ValueMap::in, !:ValueMap::out) is det :-
|
|
% The closure_info won't yet contain any information about
|
|
% higher-order outputs from this call.
|
|
|
|
( map.search(!.ClosureInfo, Var, PossibleValues) ->
|
|
(
|
|
PossibleValues = unknown
|
|
;
|
|
PossibleValues = partial(_)
|
|
;
|
|
PossibleValues = exclusive(KnownValues),
|
|
map.det_insert(Var, KnownValues, !ValueMap)
|
|
)
|
|
;
|
|
true
|
|
)
|
|
),
|
|
set_of_var.fold(AddValues, InputArgs, map.init, Values),
|
|
goal_info_set_ho_values(Values, GoalInfo0, GoalInfo),
|
|
|
|
% Insert any information about higher-order outputs from this call
|
|
% into the closure_info.
|
|
set_of_var.fold(insert_unknown, OutputArgs, !ClosureInfo),
|
|
Goal = hlds_goal(GoalExpr0, GoalInfo)
|
|
;
|
|
GoalExpr0 = generic_call(Details, GCallArgs, GCallModes, _),
|
|
partition_arguments(ModuleInfo, VarTypes, GCallArgs, GCallModes,
|
|
set_of_var.init, InputArgs0, set_of_var.init, OutputArgs),
|
|
|
|
% For higher-order calls we need to make sure that the actual
|
|
% higher-order variable being called is also considered (it will
|
|
% typically be the variable of interest). This variable is not included
|
|
% in 'GCallArgs' so we need to include in the set of input argument
|
|
% separately.
|
|
|
|
( Details = higher_order(CalledClosure0, _, _, _) ->
|
|
set_of_var.insert(CalledClosure0, InputArgs0, InputArgs)
|
|
;
|
|
InputArgs = InputArgs0
|
|
),
|
|
AddValues = (pred(Var::in, !.ValueMap::in, !:ValueMap::out) is det :-
|
|
% The closure_info won't yet contain any information about
|
|
% higher-order outputs from this call.
|
|
|
|
( map.search(!.ClosureInfo, Var, PossibleValues) ->
|
|
(
|
|
PossibleValues = unknown
|
|
;
|
|
PossibleValues = partial(_)
|
|
;
|
|
PossibleValues = exclusive(KnownValues),
|
|
map.det_insert(Var, KnownValues, !ValueMap)
|
|
)
|
|
;
|
|
true
|
|
)
|
|
),
|
|
set_of_var.fold(AddValues, InputArgs, map.init, Values),
|
|
goal_info_set_ho_values(Values, GoalInfo0, GoalInfo),
|
|
|
|
% Insert any information about higher-order outputs from this call
|
|
% into the closure_info
|
|
set_of_var.fold(insert_unknown, OutputArgs, !ClosureInfo),
|
|
Goal = hlds_goal(GoalExpr0, GoalInfo)
|
|
;
|
|
GoalExpr0 = switch(SwitchVar, SwitchCanFail, Cases0),
|
|
ProcessCase = (func(Case0) = Case - CaseInfo :-
|
|
Case0 = case(MainConsId, OtherConsIds, CaseGoal0),
|
|
process_goal(VarTypes, ModuleInfo, CaseGoal0, CaseGoal,
|
|
!.ClosureInfo, CaseInfo),
|
|
Case = case(MainConsId, OtherConsIds, CaseGoal)
|
|
),
|
|
CasesAndInfos = list.map(ProcessCase, Cases0),
|
|
assoc_list.keys_and_values(CasesAndInfos, Cases, CasesInfo),
|
|
list.foldl(merge_closure_infos, CasesInfo, map.init, !:ClosureInfo),
|
|
GoalExpr = switch(SwitchVar, SwitchCanFail, Cases),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = unify(_, _, _, Unification, _),
|
|
(
|
|
Unification = construct(LHS, RHS, _, _, _, _, _),
|
|
(
|
|
RHS = closure_cons(ShroudedPPId, EvalMethod),
|
|
EvalMethod = lambda_normal
|
|
->
|
|
PPId = unshroud_pred_proc_id(ShroudedPPId),
|
|
HO_Value = set.make_singleton_set(PPId),
|
|
map.det_insert(LHS, exclusive(HO_Value), !ClosureInfo)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
Unification = deconstruct(_, _, Args, _, _, _),
|
|
|
|
% XXX We don't currently support tracking the values of
|
|
% closures that are stored in data structures.
|
|
|
|
HO_Args = list.filter(var_has_ho_type(VarTypes), Args),
|
|
list.foldl(insert_unknown, HO_Args, !ClosureInfo)
|
|
;
|
|
Unification = assign(LHS, RHS),
|
|
( var_has_ho_type(VarTypes, LHS) ->
|
|
% Sanity check: make sure the rhs is also a higher-order
|
|
% variable.
|
|
|
|
( var_has_ho_type(VarTypes, RHS) ->
|
|
true
|
|
;
|
|
unexpected($module, $pred, "not a higher-order var")
|
|
),
|
|
Values = map.lookup(!.ClosureInfo, RHS),
|
|
map.det_insert(LHS, Values, !ClosureInfo)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
Unification = simple_test(_, _)
|
|
;
|
|
Unification = complicated_unify(_, _, _)
|
|
),
|
|
Goal = Goal0
|
|
;
|
|
GoalExpr0 = disj(Goals0),
|
|
ProcessDisjunct = (func(Disjunct0) = DisjunctResult :-
|
|
process_goal(VarTypes, ModuleInfo, Disjunct0, Disjunct,
|
|
!.ClosureInfo, ClosureInfoForDisjunct),
|
|
DisjunctResult = Disjunct - ClosureInfoForDisjunct
|
|
),
|
|
DisjunctsAndInfos = list.map(ProcessDisjunct, Goals0),
|
|
assoc_list.keys_and_values(DisjunctsAndInfos, Goals, DisjunctsInfo),
|
|
list.foldl(merge_closure_infos, DisjunctsInfo, map.init, !:ClosureInfo),
|
|
GoalExpr = disj(Goals),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = negation(NegatedGoal0),
|
|
process_goal(VarTypes, ModuleInfo, NegatedGoal0, NegatedGoal,
|
|
!.ClosureInfo, _),
|
|
GoalExpr = negation(NegatedGoal),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = scope(Reason, SubGoal0),
|
|
(
|
|
Reason = from_ground_term(_, FGT),
|
|
( FGT = from_ground_term_construct
|
|
; FGT = from_ground_term_deconstruct
|
|
)
|
|
->
|
|
SubGoal = SubGoal0
|
|
;
|
|
process_goal(VarTypes, ModuleInfo, SubGoal0, SubGoal, !ClosureInfo)
|
|
),
|
|
GoalExpr = scope(Reason, SubGoal),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = if_then_else(ExistQVars, Cond0, Then0, Else0),
|
|
process_goal(VarTypes, ModuleInfo, Cond0, Cond,
|
|
!.ClosureInfo, CondInfo),
|
|
process_goal(VarTypes, ModuleInfo, Then0, Then,
|
|
CondInfo, CondThenInfo),
|
|
process_goal(VarTypes, ModuleInfo, Else0, Else,
|
|
!.ClosureInfo, ElseInfo),
|
|
map.union(merge_closure_values, CondThenInfo, ElseInfo, !:ClosureInfo),
|
|
GoalExpr = if_then_else(ExistQVars, Cond, Then, Else),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = call_foreign_proc(_, _, _, Args, _ExtraArgs, _, _),
|
|
% XXX 'ExtraArgs' should probably be ignored here since it is only
|
|
% used by the tabling transformation.
|
|
%
|
|
% XXX We may eventually want to annotate foreign_procs with
|
|
% clousure_infos as well. It isn't useful at the moment however.
|
|
|
|
ForeignHOArgs = (pred(Arg::in, Out::out) is semidet :-
|
|
Arg = foreign_arg(Var, NameMode, Type, _BoxPolicy),
|
|
%
|
|
% A 'no' here means that the foreign argument is unused.
|
|
%
|
|
NameMode = yes(_ - Mode),
|
|
mode_util.mode_is_output(ModuleInfo, Mode),
|
|
type_is_higher_order(Type),
|
|
Out = Var - unknown
|
|
),
|
|
list.filter_map(ForeignHOArgs, Args, OutputForeignHOArgs),
|
|
map.det_insert_from_assoc_list(OutputForeignHOArgs, !ClosureInfo),
|
|
Goal = Goal0
|
|
;
|
|
GoalExpr0 = shorthand(_),
|
|
unexpected($module, $pred, "shorthand")
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- pred partition_arguments(module_info::in, vartypes::in,
|
|
prog_vars::in, list(mer_mode)::in,
|
|
set_of_progvar::in, set_of_progvar::out,
|
|
set_of_progvar::in, set_of_progvar::out) is det.
|
|
|
|
partition_arguments(_, _, [], [], !Inputs, !Outputs).
|
|
partition_arguments(_, _, [_|_], [], _, _, _, _) :-
|
|
unexpected($module, $pred, "unequal length lists.").
|
|
partition_arguments(_, _, [], [_|_], _, _, _, _) :-
|
|
unexpected($module, $pred, "unequal length lists.").
|
|
partition_arguments(ModuleInfo, VarTypes, [ Var | Vars ], [ Mode | Modes ],
|
|
!Inputs, !Outputs) :-
|
|
( var_has_ho_type(VarTypes, Var) ->
|
|
( mode_is_input(ModuleInfo, Mode) ->
|
|
set_of_var.insert(Var, !Inputs)
|
|
; mode_is_output(ModuleInfo, Mode) ->
|
|
set_of_var.insert(Var, !Outputs)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
true
|
|
),
|
|
partition_arguments(ModuleInfo, VarTypes, Vars, Modes, !Inputs, !Outputs).
|
|
|
|
:- pred merge_closure_infos(closure_info::in, closure_info::in,
|
|
closure_info::out) is det.
|
|
|
|
merge_closure_infos(A, B, C) :-
|
|
map.union(merge_closure_values, A, B, C).
|
|
|
|
:- pred merge_closure_values(closure_values::in, closure_values::in,
|
|
closure_values::out) is det.
|
|
|
|
merge_closure_values(unknown, unknown, unknown).
|
|
merge_closure_values(unknown, partial(A), partial(A)).
|
|
merge_closure_values(unknown, exclusive(A), partial(A)).
|
|
merge_closure_values(partial(A), unknown, partial(A)).
|
|
merge_closure_values(partial(A), partial(B), partial(A `set.union` B)).
|
|
merge_closure_values(partial(A), exclusive(B), partial(A `set.union` B)).
|
|
merge_closure_values(exclusive(A), unknown, partial(A)).
|
|
merge_closure_values(exclusive(A), partial(B), partial(A `set.union` B)).
|
|
merge_closure_values(exclusive(A), exclusive(B), exclusive(A `set.union` B)).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
%
|
|
% Debugging code (used by '--debug-closure' option)
|
|
%
|
|
|
|
:- pred dump_closure_info(prog_varset::in, hlds_goal::in,
|
|
io::di, io::uo) is det.
|
|
|
|
dump_closure_info(Varset, Goal, !IO) :-
|
|
Goal = hlds_goal(GoalExpr, GoalInfo),
|
|
dump_closure_info_expr(Varset, GoalExpr, GoalInfo, !IO).
|
|
|
|
:- pred dump_closure_info_expr(prog_varset::in, hlds_goal_expr::in,
|
|
hlds_goal_info::in, io::di, io::uo) is det.
|
|
|
|
dump_closure_info_expr(Varset, conj(_ConjType, Goals), _, !IO) :-
|
|
list.foldl(dump_closure_info(Varset), Goals, !IO).
|
|
dump_closure_info_expr(Varset, plain_call(_,_,_,_,_,_), GoalInfo, !IO) :-
|
|
dump_ho_values(GoalInfo, Varset, !IO).
|
|
dump_closure_info_expr(Varset, generic_call(_,_,_,_), GoalInfo, !IO) :-
|
|
dump_ho_values(GoalInfo, Varset, !IO).
|
|
dump_closure_info_expr(Varset, scope(_, Goal), _, !IO) :-
|
|
dump_closure_info(Varset, Goal, !IO).
|
|
dump_closure_info_expr(Varset, switch(_, _, Cases), _, !IO) :-
|
|
CaseToGoal = (func(case(_, _, Goal)) = Goal),
|
|
Goals = list.map(CaseToGoal, Cases),
|
|
list.foldl(dump_closure_info(Varset), Goals, !IO).
|
|
dump_closure_info_expr(Varset, if_then_else(_, Cond, Then, Else), _, !IO) :-
|
|
list.foldl(dump_closure_info(Varset), [Cond, Then, Else], !IO).
|
|
dump_closure_info_expr(_, unify(_,_,_,_,_), _, !IO).
|
|
dump_closure_info_expr(Varset, negation(Goal), _, !IO) :-
|
|
dump_closure_info(Varset, Goal, !IO).
|
|
dump_closure_info_expr(_, call_foreign_proc(_, _, _, _, _, _, _), _, !IO).
|
|
dump_closure_info_expr(Varset, disj(Goals), _, !IO) :-
|
|
list.foldl(dump_closure_info(Varset), Goals, !IO).
|
|
dump_closure_info_expr(_, shorthand(_), _, _, _) :-
|
|
unexpected($module, $pred, "shorthand").
|
|
|
|
:- pred dump_ho_values(hlds_goal_info::in, prog_varset::in,
|
|
io::di, io::uo) is det.
|
|
|
|
dump_ho_values(GoalInfo, Varset, !IO) :-
|
|
HO_Values = goal_info_get_ho_values(GoalInfo),
|
|
( not map.is_empty(HO_Values) ->
|
|
prog_out.write_context(goal_info_get_context(GoalInfo), !IO),
|
|
io.nl(!IO),
|
|
map.foldl(dump_ho_value(Varset), HO_Values, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred dump_ho_value(prog_varset::in, prog_var::in, set(pred_proc_id)::in,
|
|
io::di, io::uo) is det.
|
|
|
|
dump_ho_value(Varset, ProgVar, Values, !IO) :-
|
|
VarName = varset.lookup_name(Varset, ProgVar),
|
|
io.format("%s =\n", [s(VarName)], !IO),
|
|
WritePPIds = (pred(PPId::in, !.IO::di, !:IO::uo) is det :-
|
|
io.write_string("\t", !IO),
|
|
io.write(PPId, !IO),
|
|
io.nl(!IO)
|
|
),
|
|
set.fold(WritePPIds, Values, !IO).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
:- end_module transform_hlds.closure_analysis.
|
|
%----------------------------------------------------------------------------%
|