Files
mercury/compiler/rbmm.live_variable_analysis.m
Zoltan Somogyi bbb2535eb5 Break up mercury_to_mercury.m.
With almost 6000 lines, mercury_to_mercury.m was one of the biggest modules
of compiler, but it was far from cohesive. This diff carves seven new modules
out of it, each of which is much more cohesive. The stuff remaining in
mercury_to_mercury.m is still not as cohesive as one would like, but it is
now small enough that moving its individually-cohesive parts into modules
of their own would be overkill.

Three consequences of the old mercury_to_mercury.m's lack of cohesion
were that

- the order of predicate declarations often did not match the order of
  their implementation;
- related predicates were not grouped together;
- even when they were grouped together, the order of those groups
  was often random.

This diff fixes all three of these problems for all eight successor modules
of mercury_to_mercury.m: the seven new modules, and the new
mercury_to_mercury.m itself.

In some cases, this diff adds or improves the documentation of the predicates
in mercury_to_mercury.m's successor modules. In some other cases, it just
documents the lack of documentation :-(. In yet other cases, it removes
"documentation" that says nothing that isn't obvious from the predicate's name.

There are some algorithmic changes, but they are all trivial.

compiler/parse_tree_out.m:
    New module containing the code to print out the top levels of parse trees,
    including most sorts of items.

compiler/parse_tree_out_clause.m:
    New module containing the code to print out clauses and goals.

compiler/parse_tree_out_pragma.m:
    New module containing the code to print out pragmas.

compiler/parse_tree_out_pred_decl.m:
    New module containing the code to print out predicate, function and
    mode declarations. It is separate from parse_tree_out.m because a
    significant number of compiler modules need only its functionality,
    and not parse_tree_out.m's functionality.

compiler/parse_tree_out_inst.m:
    New module containing the code to print out insts and modes.

compiler/parse_tree_out_term.m:
    New module containing the code to print out variables and terms.

compiler/parse_tree_out_info.m:
    New module containing the infrastructure of both mercury_to_mercury.m
    and the other new modules.

compiler/parse_tree.m:
    Include the new modules.

compiler/notes/compiler_design.html:
    Document the new modules.

compiler/Mercury.options:
    Transfer an option from mercury_to_mercury.m to the successor module
    that needs it.

compiler/*.m:
    Import one of the new modules either as well as, or instead of,
    mercury_to_mercury.m. In most cases, we need to import only one
    or two of mercury_to_mercury.m's successor modules; nowhere do we
    need to import all eight.

    Clean up some code in termination.m around a call to one of the
    new modules.

tools/speedtest:
    Replace mercury_to_mercury.m on the list of the ten largest modules
    of the compiler.
2015-09-06 21:01:11 +10:00

397 lines
16 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2005-2007, 2010-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 rbmm.live_variable_analysis.m.
% Main author: Quan Phan.
%
% This module implements the live variable analysis.
%
%-----------------------------------------------------------------------------%
:- module transform_hlds.rbmm.live_variable_analysis.
:- interface.
:- import_module hlds.
:- import_module hlds.hlds_module.
:- import_module transform_hlds.rbmm.region_liveness_info.
% Collects live variable sets.
%
:- pred live_variable_analysis(module_info::in, execution_path_table::in,
proc_pp_varset_table::out, proc_pp_varset_table::out,
proc_pp_varset_table::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_pred.
:- import_module parse_tree.
:- import_module parse_tree.parse_tree_out_term.
:- import_module parse_tree.prog_data.
:- import_module transform_hlds.smm_common.
:- import_module assoc_list.
:- import_module bool.
:- import_module list.
:- import_module map.
:- import_module pair.
:- import_module require.
:- import_module set.
:- import_module string.
:- import_module term.
:- import_module varset.
%-----------------------------------------------------------------------------%
%
% Live variable analysis
%
% For each procedure, compute the sets of live variables before and after
% each program point.
% Currently, it also calculates set of void variables (i.e., ones whose names
% start with "_") after each program point. Those variables are considered
% dead at that point.
%
live_variable_analysis(ModuleInfo, ExecPathTable, LVBeforeTable,
LVAfterTable, VoidVarTable) :-
module_info_get_valid_pred_ids(ModuleInfo, PredIds),
map.init(LVBeforeTable0),
map.init(LVAfterTable0),
map.init(VoidVarTable0),
list.foldl3(live_variable_analysis_pred(ModuleInfo, ExecPathTable),
PredIds, LVBeforeTable0, LVBeforeTable, LVAfterTable0, LVAfterTable,
VoidVarTable0, VoidVarTable).
:- pred live_variable_analysis_pred(module_info::in, execution_path_table::in,
pred_id::in, proc_pp_varset_table::in, proc_pp_varset_table::out,
proc_pp_varset_table::in, proc_pp_varset_table::out,
proc_pp_varset_table::in, proc_pp_varset_table::out) is det.
live_variable_analysis_pred(ModuleInfo, ExecPathTable, PredId,
!LVBeforeTable, !LVAfterTable, !VoidVarTable) :-
module_info_pred_info(ModuleInfo, PredId, PredInfo),
pred_info_non_imported_procids(PredInfo) = ProcIds,
list.foldl3(
live_variable_analysis_proc(ModuleInfo, ExecPathTable, PredId),
ProcIds, !LVBeforeTable, !LVAfterTable, !VoidVarTable).
:- pred live_variable_analysis_proc(module_info::in,
execution_path_table::in, pred_id::in, proc_id::in,
proc_pp_varset_table::in, proc_pp_varset_table::out,
proc_pp_varset_table::in, proc_pp_varset_table::out,
proc_pp_varset_table::in, proc_pp_varset_table::out) is det.
live_variable_analysis_proc(ModuleInfo, ExecPathTable, PredId, ProcId,
!LVBeforeTable, !LVAfterTable, !VoidVarTable) :-
PPId = proc(PredId, ProcId),
( some_are_special_preds([PPId], ModuleInfo) ->
true
;
module_info_proc_info(ModuleInfo, PPId, ProcInfo),
find_input_output_args(ModuleInfo, ProcInfo, Inputs, Outputs),
map.lookup(ExecPathTable, PPId, ExecPaths),
live_variable_analysis_exec_paths(ExecPaths, Inputs, Outputs,
ModuleInfo, ProcInfo, map.init, ProcLVBefore,
map.init, ProcLVAfter, map.init, ProcVoidVar),
map.set(PPId, ProcLVBefore, !LVBeforeTable),
map.set(PPId, ProcLVAfter, !LVAfterTable),
map.set(PPId, ProcVoidVar, !VoidVarTable)
).
:- pred live_variable_analysis_exec_paths(list(execution_path)::in,
list(prog_var)::in, list(prog_var)::in, module_info::in, proc_info::in,
pp_varset_table::in, pp_varset_table::out, pp_varset_table::in,
pp_varset_table::out, pp_varset_table::in, pp_varset_table::out) is det.
% Live variable analysis is backward, so we reverse the execution path
% before starting. We have specific treatment for execution paths with
% only one program point, which means the last program point is also the
% first one.
%
live_variable_analysis_exec_paths([], _, _, _, _, !ProcLVBefore,
!ProcLVAfter, !ProcVoidVar).
live_variable_analysis_exec_paths([ExecPath0 | ExecPaths], Inputs, Outputs,
ModuleInfo, ProcInfo, !ProcLVBefore, !ProcLVAfter, !ProcVoidVar) :-
list.reverse(ExecPath0, ExecPath),
( list.length(ExecPath) = 1 ->
live_variable_analysis_singleton_exec_path(ExecPath, Inputs, Outputs,
ModuleInfo, ProcInfo, !ProcLVBefore, !ProcLVAfter, !ProcVoidVar)
;
% Start with the last program point.
live_variable_analysis_exec_path(ExecPath, Inputs, Outputs,
ModuleInfo, ProcInfo, yes, set.init, !ProcLVBefore, !ProcLVAfter,
!ProcVoidVar)
),
live_variable_analysis_exec_paths(ExecPaths, Inputs, Outputs,
ModuleInfo, ProcInfo, !ProcLVBefore, !ProcLVAfter, !ProcVoidVar).
:- pred live_variable_analysis_exec_path(execution_path::in,
list(prog_var)::in, list(prog_var)::in, module_info::in, proc_info::in,
bool::in, set(prog_var)::in, pp_varset_table::in, pp_varset_table::out,
pp_varset_table::in, pp_varset_table::out,
pp_varset_table::in, pp_varset_table::out) is det.
live_variable_analysis_exec_path([], _, _, _, _,_, _, !ProcLVBefore,
!ProcLVAfter, !ProcVoidVar).
% XXX Exactly what piece of code does this comment apply to?
% Process the last program point in an execution path. The live variable
% set of the last program point is always the set of output variables
% of the procedure.
%
live_variable_analysis_exec_path([(LastProgPoint - Goal) | ProgPointGoals],
Inputs, Outputs, ModuleInfo, ProcInfo, yes, _LVBeforeNext,
!ProcLVBefore, !ProcLVAfter, !ProcVoidVar) :-
( map.search(!.ProcLVAfter, LastProgPoint, LVAfterLast0) ->
LVAfterLast = LVAfterLast0
;
LVAfterLast = set.list_to_set(Outputs),
map.set(LastProgPoint, LVAfterLast, !ProcLVAfter)
),
% Compute live variable before this last program point.
compute_useds_produceds(ModuleInfo, Goal, UsedSet, ProducedSet),
set.union(set.difference(LVAfterLast, ProducedSet), UsedSet,
LVBeforeLastInThisExecPath),
record_live_vars_at_prog_point(LastProgPoint, LVBeforeLastInThisExecPath,
!ProcLVBefore),
% Collect void variables after this program point.
collect_void_vars(LastProgPoint, ProducedSet, ProcInfo, !ProcVoidVar),
live_variable_analysis_exec_path(ProgPointGoals, Inputs, Outputs,
ModuleInfo, ProcInfo, no, LVBeforeLastInThisExecPath, !ProcLVBefore,
!ProcLVAfter, !ProcVoidVar).
% Process a middle program point.
%
live_variable_analysis_exec_path(
[(ProgPoint - Goal), ProgPointGoal | ProgPointGoals], Inputs,
Outputs, ModuleInfo, ProcInfo, no, LVBeforeNext, !ProcLVBefore,
!ProcLVAfter, !ProcVoidVar) :-
% The live variable set after this program point is the union of the
% live variable sets after it in all execution paths to which it belongs.
record_live_vars_at_prog_point(ProgPoint, LVBeforeNext, !ProcLVAfter),
% Compute LV before this program point.
compute_useds_produceds(ModuleInfo, Goal, UsedSet, ProducedSet),
set.union(set.difference(LVBeforeNext, ProducedSet), UsedSet,
LVBeforeInThisExecPath),
record_live_vars_at_prog_point(ProgPoint, LVBeforeInThisExecPath,
!ProcLVBefore),
% Collect void variables after this program point.
collect_void_vars(ProgPoint, ProducedSet, ProcInfo, !ProcVoidVar),
live_variable_analysis_exec_path([ProgPointGoal | ProgPointGoals],
Inputs, Outputs, ModuleInfo, ProcInfo, no, LVBeforeInThisExecPath,
!ProcLVBefore, !ProcLVAfter, !ProcVoidVar).
% The live variable set before the first program point is ALWAYS
% Inputs.
%
live_variable_analysis_exec_path([FirstProgPoint - Goal], Inputs, _Outputs,
ModuleInfo, ProcInfo, no, LVBeforeNext, !ProcLVBefore,
!ProcLVAfter, !ProcVoidVar) :-
( map.search(!.ProcLVBefore, FirstProgPoint, _LVBeforeFirst) ->
true
;
LVBeforeFirst = set.list_to_set(Inputs),
map.set(FirstProgPoint, LVBeforeFirst, !ProcLVBefore)
),
% Live variable set after the first program point.
record_live_vars_at_prog_point(FirstProgPoint, LVBeforeNext, !ProcLVAfter),
% Collect void vars after this program point.
compute_useds_produceds(ModuleInfo, Goal, _UsedSet, ProducedSet),
collect_void_vars(FirstProgPoint, ProducedSet, ProcInfo, !ProcVoidVar).
% This predicate analyses execution paths with only one program point.
% So it must be called in a context that matches that condition.
%
:- pred live_variable_analysis_singleton_exec_path(execution_path::in,
list(prog_var)::in, list(prog_var)::in, module_info::in, proc_info::in,
pp_varset_table::in, pp_varset_table::out, pp_varset_table::in,
pp_varset_table::out, pp_varset_table::in, pp_varset_table::out) is det.
live_variable_analysis_singleton_exec_path([ProgPoint - Goal | _], Inputs,
Outputs, ModuleInfo, ProcInfo, !ProcLVBefore, !ProcLVAfter,
!ProcVoidVar) :-
LVBefore = set.list_to_set(Inputs),
map.set(ProgPoint, LVBefore, !ProcLVBefore),
LVAfter = set.list_to_set(Outputs),
map.set(ProgPoint, LVAfter, !ProcLVAfter),
% Collect void vars after this program point.
compute_useds_produceds(ModuleInfo, Goal, _UsedSet, ProducedSet),
collect_void_vars(ProgPoint, ProducedSet, ProcInfo, !ProcVoidVar).
live_variable_analysis_singleton_exec_path([], _, _, _, _,
!ProcLVBefore, !ProcLVAfter, !ProcVoidVar) :-
unexpected($module, $pred, "empty list").
% A variable is live at a program point if it is live in one of
% the execution paths that covers the program point.
% Therefore we need to union the existing live variable set at a program
% point with the newly found.
%
:- pred record_live_vars_at_prog_point(program_point::in, variable_set::in,
pp_varset_table::in, pp_varset_table::out) is det.
record_live_vars_at_prog_point(ProgPoint, LV, !ProcLV) :-
( map.search(!.ProcLV, ProgPoint, ExistingLV) ->
map.set(ProgPoint, set.union(ExistingLV, LV), !ProcLV)
;
map.set(ProgPoint, LV, !ProcLV)
).
% Compute used and produced variables in an atomic goal, which
% has been recorded alongside a program point in an execution_path.
% A variable is used in an atomic goal if it is input to the goal.
% It is produced in the atomic goal if it is output of the goal.
%
:- pred compute_useds_produceds(module_info::in, hlds_goal::in,
variable_set::out, variable_set::out) is det.
compute_useds_produceds(ModuleInfo, Goal, UsedSet, ProducedSet) :-
( if
% a removed switch
Goal = hlds_goal(switch(Var, _, _), _SwitchInfo)
then
Useds = [Var],
Produceds = []
else
Goal = hlds_goal(Expr, _Info),
(
Expr = plain_call(CalleePredId, CalleeProcId, Args,
_BuiltIn, _Context, _Name)
->
get_inputs_outputs_proc_call(Args,
proc(CalleePredId, CalleeProcId), ModuleInfo,
Useds, Produceds)
;
Expr = unify(_, _, _, Unification, _)
->
get_inputs_outputs_unification(Unification, Useds,
Produceds)
;
( Expr = conj(_, [])
; Expr = disj([])
)
->
Useds = [],
Produceds = []
;
unexpected($module, $pred,
"the expression must be either call, unify, true, or fail")
)
),
set.list_to_set(Useds, UsedSet),
set.list_to_set(Produceds, ProducedSet).
% Divide the variables appearing in a unification into lists of input
% variables and output variables.
%
:- pred get_inputs_outputs_unification(unification::in,
list(prog_var)::out, list(prog_var)::out) is det.
get_inputs_outputs_unification(construct(LVar, _, Args, _, _, _, _),
Args, [LVar]).
get_inputs_outputs_unification(deconstruct(LVar, _, Args, _, _, _),
[LVar], Args).
get_inputs_outputs_unification(assign(LVar, RVar), [RVar], [LVar]).
get_inputs_outputs_unification(simple_test(LVar, RVar), [LVar, RVar], []).
get_inputs_outputs_unification(complicated_unify(_, _, _), [], []).
% Divide the arguments in a procedure call into lists of input
% variables and output variables.
%
:- pred get_inputs_outputs_proc_call(list(prog_var)::in, pred_proc_id::in,
module_info::in, list(prog_var)::out, list(prog_var)::out) is det.
get_inputs_outputs_proc_call(ActualArgs, CalleeId, ModuleInfo,
ActualInputs, ActualOutputs) :-
module_info_pred_proc_info(ModuleInfo, CalleeId, _PredInfo, CalleeInfo),
find_input_output_args(ModuleInfo, CalleeInfo, Inputs, Outputs),
proc_info_get_headvars(CalleeInfo, FormalArgs),
get_inputs_outputs_proc_call_2(FormalArgs, ActualArgs,
Inputs, Outputs, [], ActualInputs, [], ActualOutputs).
:- pred get_inputs_outputs_proc_call_2(list(prog_var)::in,
list(prog_var)::in, list(prog_var)::in, list(prog_var)::in,
list(prog_var)::in, list(prog_var)::out, list(prog_var)::in,
list(prog_var)::out) is det.
get_inputs_outputs_proc_call_2([], [], _, _, !ActualInputs, !ActualOutputs).
get_inputs_outputs_proc_call_2([], [_ | _], _, _, !ActualInputs,
!ActualOutputs) :-
unexpected($module, $pred, "mismatched lists").
get_inputs_outputs_proc_call_2([_ | _], [], _, _, !ActualInputs,
!ActualOutputs) :-
unexpected($module, $pred, "mismatched lists").
get_inputs_outputs_proc_call_2([FormalArg | FormalArgs],
[ActualArg | ActualArgs], Inputs, Outputs, !ActualInputs,
!ActualOutputs) :-
( list.member(FormalArg, Inputs) ->
% This formal argument is an input, so the correspondig argument
% is an actual input argument.
ActualInputs1 = [ActualArg | !.ActualInputs],
ActualOutputs1 = !.ActualOutputs
; list.member(FormalArg, Outputs) ->
% This formal argument is an output, so the corresponding argument
% is an actual output argument.
ActualOutputs1 = [ActualArg | !.ActualOutputs],
ActualInputs1 = !.ActualInputs
;
% This formal param is neither an output nor an input, so ignore
% the corresponding arg.
ActualInputs1 = !.ActualInputs,
ActualOutputs1 = !.ActualOutputs
),
get_inputs_outputs_proc_call_2(FormalArgs, ActualArgs, Inputs, Outputs,
ActualInputs1, !:ActualInputs, ActualOutputs1, !:ActualOutputs).
% Collect variables whose names start with _, i.e., void variables.
% I am considering those variables dead right after created in the live
% variable and region analyses.
%
:- pred collect_void_vars(program_point::in, variable_set::in, proc_info::in,
pp_varset_table::in, pp_varset_table::out) is det.
collect_void_vars(ProgPoint, ProducedSet, ProcInfo, !ProcVoidVar) :-
( map.search(!.ProcVoidVar, ProgPoint, _DeadVars) ->
true
;
proc_info_get_varset(ProcInfo, VarSet),
set.fold(void_var(VarSet), ProducedSet, set.init, VoidVars),
map.set(ProgPoint, VoidVars, !ProcVoidVar)
).
% To be used with the fold above: if Var is a void variable,
% add it to VoidVars set.
%
:- pred void_var(prog_varset::in, prog_var::in,
variable_set::in, variable_set::out) is det.
void_var(VarSet, Var, !VoidVars) :-
VarName = mercury_var_to_name_only(VarSet, Var),
( string.index(VarName, 0, '_') ->
set.insert(Var, !VoidVars)
;
true
).
%----------------------------------------------------------------------------%
:- end_module transform_hlds.rbmm.live_variable_analysis.
%----------------------------------------------------------------------------%