Files
mercury/compiler/structure_reuse.domain.m
Nancy Mazur d3e5a8eda4 Provide the direct reuse analysis part of the structure reuse analysis (which
Estimated hours taken: 25
Branches: main

Provide the direct reuse analysis part of the structure reuse analysis (which
itself is part of the CTGC system).

compiler/ctgc.datastruct.m:
compiler/ctgc.util.m:
	Additional predicates.

compiler/ctgc.m:
	Add structure reuse module.

compiler/handle_options.m:
compiler/options.m:
	Add new options "structure_reuse_analysis" and related ones.

compiler/handle_options.m:
compiler/hlds_out.m:
	Add dump option "R" to dump structure reuse related information
	in the hlds_dump files.

compiler/hlds_goal.m:
	Types to record structure reuse information at the level of each
	goal.
	Additional "case_get_goal" function to extract the goal from an case.

compiler/mercury_compile.m:
	Add structure reuse analysis as a new compiler stage.

compiler/structure_reuse.analysis.m:
	The top level analysis predicates.

compiler/structure_reuse.direct.m:
compiler/structure_reuse.direct.choose_reuse.m:
compiler/structure_reuse.direct.detect_garbage.m:
	Direct reuse analysis is split into 2 steps: determining when and how
	data structures become garbage, and then choosing how these dead
	data structures might best be reused.

compiler/structure_reuse.domain.m:
	The abstract domain for keeping track of reuse conditions, the main
	domain in the structure reuse analysis.

compiler/structure_reuse.lbu.m:
compiler/structure_reuse.lfu.m:
	To determine whether data structures become dead or not, one needs to
	know which variables in a goal are needed with respect to forward
	execution (lfu = local forward use), and backward execution, i.e.
	backtracking (lbu = local backward use). These two modules provide
	the necessary functionality to pre-annotate the goals with lfu and
	lbu information.

compiler/structure_sharing.analysis.m:
compiler/structure_sharing.domain.m:
	Remove the structure sharing table from the interface of the analysis
	predicate in structure_sharing.analysis.m;
	Move predicates to structure_sharing.domain.m so that they become
	more easily accessible for the structure_reuse modules.

compiler/prog_data.m:
	New types "dead_var", "live_var" and alike.
2006-05-10 10:56:57 +00:00

438 lines
16 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2006 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: structure_reuse.domain.m
% Main authors: nancy
%
% Definition of the abstract domain for keeping track of opportunities for
% structure reuse.
%
%-----------------------------------------------------------------------------%
:- module transform_hlds.ctgc.structure_reuse.domain.
:- interface.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module parse_tree.prog_data.
:- import_module transform_hlds.ctgc.structure_sharing.domain.
:- import_module map.
:- import_module set.
:- import_module list.
% A reuse condition stores all the necessary information to check if
% a procedure call is safe w.r.t. a structure reuse opportunity within
% the body of the called procedure.
%
:- type reuse_condition.
:- type reuse_conditions == list(reuse_condition).
% Abstract representation for a set of reuse conditions.
%
:- type reuse_as.
%-----------------------------------------------------------------------------%
% reuse_condition
%
% reuse_condition_init(ModuleInfo, ProcInfo, DeadVar, LocalForwardUse,
% LocalBackwardUse, SharingAs) = NewReuseCondition.
%
% Create a reuse condition for DeadVar, knowing the set of variables in
% local forward and backward use, as well as the local structure sharing.
%
:- func reuse_condition_init(module_info, proc_info, dead_var,
set(live_var), set(live_var), sharing_as) = reuse_condition.
:- pred reuse_condition_is_conditional(reuse_condition::in) is semidet.
% Renaming operation.
% This operation renames all occurrences of program variables and
% type variables according to a program and type variable mapping.
%
:- pred reuse_condition_rename(map(prog_var, prog_var)::in, tsubst::in,
reuse_condition::in, reuse_condition::out) is det.
% Succeeds if the first condition is subsumed by the second one, i.e.,
% if a procedure call verifies the second condition, then it also
% verifies the first condition.
%
:- pred reuse_condition_subsumed_by(module_info::in, proc_info::in,
reuse_condition::in, reuse_condition::in) is semidet.
% Translate a reuse condition to its new environment.
% E.g. Consider a procedure definition: p :- ..., q, ... ,
% where q contains a potential reuse expressed by a condition C, then this
% condition needs to be translated to calls to p, such that calls to p
% can be checked w.r.t. reuses that are possible in q.
%
% In order to translate a condition to its new environment, the same kind
% of information is needed as when creating an initial condition, i.e.:
% set of variables in local forward and backward use, as well as the
% structure sharing existing at that place.
%
% XXX To be implemented. Used at indirect-reuse-analysis stage.
% :- pred reuse_condition_translate(module_info::in, proc_info::in,
% set(prog_var)::in, set(prog_var)::in, sharing_as::in, reuse_condition::in,
% reuse_condition::out) is det.
%-----------------------------------------------------------------------------%
% reuse_as
%
% XXX The implementation of this type has changed wrt. its counterpart in the
% reuse branch (called memo_reuse). While memo_reuse's didn't always keep a
% minimal representation, reuse_as does.
%
% Create an initial set of reuse descriptions.
%
:- func reuse_as_init = reuse_as.
:- func reuse_as_init_with_one_condition(reuse_condition) = reuse_as.
% Succeeds if the first reuses description is subsumed by the second
% description, i.e., if a procedure call satisfies all the conditions
% expressed by the second reuses description, then it also satisfies all
% the conditions expressed by the first reuses description.
%
:- pred reuse_as_subsumed_by(module_info::in, proc_info::in, reuse_as::in,
reuse_as::in) is semidet.
% Tests to see whether the reuses description describes no reuses at all,
% only unconditional reuses, or conditional reuses resp.
%
:- pred reuse_as_no_reuses(reuse_as::in) is semidet.
:- pred reuse_as_all_unconditional_reuses(reuse_as::in) is semidet.
:- pred reuse_as_conditional_reuses(reuse_as::in) is semidet.
% Given a variable and type variable mapping, rename the reuses
% conditions accordingly.
%
:- pred reuse_as_rename(map(prog_var, prog_var)::in, tsubst::in, reuse_as::in,
reuse_as::out) is det.
% Add a reuse condition to the reuses description. The information of
% module_info and proc_info are needed to verify subsumption before adding
% the new condition.
%
:- pred reuse_as_add_condition(module_info::in, proc_info::in,
reuse_condition::in, reuse_as::in, reuse_as::out) is det.
% Compute the least upper bound of two reuses descriptions. Module_info
% and proc_info are needed for verifying subsumption.
%
:- pred reuse_as_least_upper_bound(module_info::in, proc_info::in,
reuse_as::in, reuse_as::in, reuse_as::out) is det.
% XXX TO DO!
% :- func from_reuse_domain(reuse_domain) = reuse_as.
% :- func to_reuse_domain(reuse_as) = reuse_domain.
%-----------------------------------------------------------------------------%
%
% reuse_as_table
%
% Intermediate storage of the reuse results for individual procedures.
%
:- type reuse_as_table == map(pred_proc_id, reuse_as).
:- func reuse_as_table_init = reuse_as_table.
:- func reuse_as_table_search(pred_proc_id, reuse_as_table)
= reuse_as is semidet.
:- pred reuse_as_table_set(pred_proc_id::in, reuse_as::in,
reuse_as_table::in, reuse_as_table::out) is det.
% XXX TO DO!
% :- func load_structure_reuse_table(module_info) = reuse_as_table.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module transform_hlds.ctgc.datastruct.
:- import_module parse_tree.prog_ctgc.
:- import_module set.
:- type reuse_condition
---> always % The reuse is always safe and does not actually
% have a condition.
; condition(
reuseable_nodes :: dead_datastructs,
% Description of the datastructures pointing to the
% memory that can be reused within a procedure.
local_use_headvars :: live_datastructs,
% Set of (headvar-related) datastructures that are
% inherently live at the place where the reuse is
% decided.
local_sharing_headvars :: sharing_as
% Description of the (headvar-related) structure sharing
% that exists at the place where the reuse is decided.
).
:- type reuse_as
---> no_reuse
% = fictive bottom element representing the fact that no
% reuse has been detected so far.
; unconditional
% no_reuse < unconditional.
% = element representing the fact that all reuses detected
% so far are unconditional.
% Semantically equivalent to "conditional(Cs)" where every C in
% Cs is "always".
; conditional(reuse_conditions).
% no_reuse < unconditional < conditional(List)
% = element representing the collection of reuse conditions
% collected for the reuses detected so far.
%-----------------------------------------------------------------------------%
%
% reuse_condition.
%
reuse_condition_init(ModuleInfo, ProcInfo, DeadVar, LFU, LBU,
Sharing) = Condition :-
proc_info_get_headvars(ProcInfo, HeadVars),
% First determine the nodes to which the reuse is related.
% There are two cases:
% 1. Var is a headvar, then it is sufficient to keep the top cell of that
% Var as only node. HeadVar-datastructures shared with that node will
% still be retraceable at the moment of verifying the condition
% 2. Var is a local var, then we must compute all the headvar-
% datastructures sharing the same memory representation as the top cell
% of this var (note that the datastructures that share with some
% substructure of this var are not relevant for the nodes). All the
% obtained datastructures are kept as the nodes for our condition.
TopCell = ctgc.datastruct.datastruct_init(DeadVar),
(
list__member(DeadVar, HeadVars)
->
Nodes = [TopCell]
;
SharedDatastructs = extend_datastruct(ModuleInfo, ProcInfo,
Sharing, TopCell),
Nodes = datastructs_project(HeadVars, SharedDatastructs)
),
% It is possible that the obtained set of nodes is empty, in that
% case the condition is always satisfied, independent of any calling
% environment.
(
Nodes = []
->
Condition = always
;
set__union(LFU, LBU, LU),
% XXX the old implementation did not bother about extending at
% this place, which was contrary to the theory. Check the effect
% of this change!
SharedLU = list.condense(
list.map(extend_datastruct(ModuleInfo, ProcInfo, Sharing),
list.map(datastruct_init, set.to_sorted_list(LU)))),
HeadVarSharedLU = datastructs_project(HeadVars, SharedLU),
structure_sharing.domain.sharing_as_project(HeadVars, Sharing,
HeadVarSharing),
Condition = condition(Nodes, HeadVarSharedLU, HeadVarSharing)
).
reuse_condition_is_conditional(condition(_, _, _)).
reuse_condition_subsumed_by(ModuleInfo, ProcInfo, Cond1, Cond2) :-
(
Cond1 = always
;
Cond1 = condition(Nodes1, LocalUse1, LocalSharing1),
Cond2 = condition(Nodes2, LocalUse2, LocalSharing2),
datastructs_subsumed_by_list(ModuleInfo, ProcInfo, Nodes1, Nodes2),
datastructs_subsumed_by_list(ModuleInfo, ProcInfo, LocalUse1,
LocalUse2),
sharing_as_is_subsumed_by(ModuleInfo,
ProcInfo, LocalSharing1, LocalSharing2)
).
:- pred reuse_condition_subsumed_by_list(module_info::in, proc_info::in,
reuse_condition::in, reuse_conditions::in) is semidet.
reuse_condition_subsumed_by_list(ModuleInfo, ProcInfo, Cond, [Cond1|Rest]) :-
(
reuse_condition_subsumed_by(ModuleInfo, ProcInfo, Cond, Cond1)
;
reuse_condition_subsumed_by_list(ModuleInfo, ProcInfo, Cond, Rest)
).
:- pred reuse_conditions_subsume_reuse_condition(module_info::in,
proc_info::in, reuse_conditions::in, reuse_condition::in) is semidet.
reuse_conditions_subsume_reuse_condition(ModuleInfo, ProcInfo, Conds, Cond):-
reuse_condition_subsumed_by_list(ModuleInfo, ProcInfo, Cond, Conds).
reuse_condition_rename(MapVar, TypeSubst, Condition, RenamedCondition):-
(
Condition = always,
RenamedCondition = always
;
Condition = condition(DeadNodes, InUseNodes, LocalSharing),
RenamedDeadNodes = list.map(rename_datastruct(MapVar, TypeSubst),
DeadNodes),
RenamedInUseNodes = list.map(rename_datastruct(MapVar, TypeSubst),
InUseNodes),
sharing_as_rename(MapVar, TypeSubst, LocalSharing,
RenamedLocalSharing),
RenamedCondition = condition(RenamedDeadNodes, RenamedInUseNodes,
RenamedLocalSharing)
).
%-----------------------------------------------------------------------------%
%
% reuse_as
%
reuse_as_init = no_reuse.
reuse_as_init_with_one_condition(ReuseCondition) = ReuseAs :-
(
reuse_condition_is_conditional(ReuseCondition)
->
ReuseAs = conditional([ReuseCondition])
;
ReuseAs = unconditional
).
reuse_as_subsumed_by(ModuleInfo, ProcInfo, FirstReuseAs, SecondReuseAs) :-
(
FirstReuseAs = no_reuse
;
FirstReuseAs = unconditional,
SecondReuseAs = conditional(_)
% Every calling environment satisfies the reuse conditions as all
% reuse is unconditional, hence also the calling environments that
% satisfy the conditions expressed by SecondReuseAs.
;
FirstReuseAs = conditional(ReuseConditionsFirst),
SecondReuseAs = conditional(ReuseConditionsSecond),
list.takewhile(reuse_conditions_subsume_reuse_condition(ModuleInfo,
ProcInfo, ReuseConditionsSecond), ReuseConditionsFirst, _,
NotSubsumed),
NotSubsumed = []
).
reuse_as_no_reuses(no_reuse).
reuse_as_all_unconditional_reuses(unconditional).
reuse_as_conditional_reuses(conditional(_)).
reuse_as_rename(MapVar, TypeSubst, ReuseAs, RenamedReuseAs) :-
(
ReuseAs = no_reuse,
RenamedReuseAs = no_reuse
;
ReuseAs = unconditional,
RenamedReuseAs = unconditional
;
ReuseAs = conditional(ReuseConditions),
list.map(reuse_condition_rename(MapVar, TypeSubst),
ReuseConditions, RenamedReuseConditions),
RenamedReuseAs = conditional(RenamedReuseConditions)
).
reuse_as_add_condition(ModuleInfo, ProcInfo, Condition, !ReuseAs) :-
(
Condition = always,
(
!.ReuseAs = no_reuse
->
!:ReuseAs = unconditional
;
true
)
;
Condition = condition(_, _, _),
(
!.ReuseAs = no_reuse,
!:ReuseAs = conditional([Condition])
;
!.ReuseAs = unconditional,
!:ReuseAs = conditional([Condition])
;
!.ReuseAs = conditional(Conditions),
reuse_conditions_add_condition(ModuleInfo, ProcInfo,
Condition, Conditions, NewConditions),
!:ReuseAs = conditional(NewConditions)
)
).
:- pred reuse_conditions_add_condition(module_info::in, proc_info::in,
reuse_condition::in, reuse_conditions::in, reuse_conditions::out) is det.
reuse_conditions_add_condition(ModuleInfo, ProcInfo, Condition, !Conds):-
(
reuse_condition_subsumed_by_list(ModuleInfo, ProcInfo,
Condition, !.Conds)
->
true
;
!:Conds = [Condition|!.Conds]
).
:- pred reuse_conditions_add_conditions(module_info::in, proc_info::in,
reuse_conditions::in, reuse_conditions::in, reuse_conditions::out) is det.
reuse_conditions_add_conditions(ModuleInfo, ProcInfo, NewConds, !Conds):-
(
NewConds = [Cond|RemainingConds],
reuse_conditions_add_condition(ModuleInfo, ProcInfo, Cond, !Conds),
reuse_conditions_add_conditions(ModuleInfo, ProcInfo,
RemainingConds, !Conds)
;
NewConds = []
).
reuse_as_least_upper_bound(ModuleInfo, ProcInfo, NewReuseAs, !ReuseAs) :-
(
NewReuseAs = no_reuse
;
NewReuseAs = unconditional,
(
!.ReuseAs = no_reuse
->
!:ReuseAs = unconditional
;
true
)
;
NewReuseAs = conditional(NewConditions),
(
!.ReuseAs = no_reuse,
!:ReuseAs = NewReuseAs
;
!.ReuseAs = unconditional,
!:ReuseAs = NewReuseAs
;
!.ReuseAs = conditional(Conditions),
reuse_conditions_add_conditions(ModuleInfo, ProcInfo,
NewConditions, Conditions, AllConditions),
!:ReuseAs = conditional(AllConditions)
)
).
%-----------------------------------------------------------------------------%
%
% reuse_as_table
%
reuse_as_table_init = map.init.
reuse_as_table_search(PPId, Table) = Table ^ elem(PPId).
reuse_as_table_set(PPId, ReuseAs, !Table) :-
!:Table = !.Table ^ elem(PPId) := ReuseAs.
%-----------------------------------------------------------------------------%
:- func this_file = string.
this_file = "structure_reuse.domain.m".
:- end_module transform_hlds.ctgc.structure_reuse.domain.