mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 05:12:33 +00:00
1120 lines
45 KiB
Mathematica
1120 lines
45 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2006-2012 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.analysis.m.
|
|
% Main authors: nancy, wangp.
|
|
%
|
|
% Implementation of the structure reuse analysis (compile-time garbage
|
|
% collection system): each procedure is analysed to see whether some
|
|
% of the terms it manipulates become garbage thus making it possible
|
|
% to reuse that garbage straight away for creating new terms.
|
|
%
|
|
% Structure reuse is broken up into three phases:
|
|
% * the direct reuse analysis (structure_reuse.direct.m)
|
|
% * the indirect analysis (structure_reuse.indirect.m)
|
|
% * and the generation of the optimised procedures.
|
|
%
|
|
% The following example shows instances of direct and indirect reuse:
|
|
%
|
|
% list.append(H1, H2, H3) :-
|
|
% (
|
|
% H1 => [],
|
|
% H3 := H2
|
|
% ;
|
|
% % Cell H1 dies provided some condition about the
|
|
% % structure sharing of H1 is true. A deconstruction
|
|
% % generating a dead cell, followed by a
|
|
% % construction reusing that cell, is called a direct
|
|
% % reuse.
|
|
% H1 => [X | Xs],
|
|
%
|
|
% % If the condition about the structure sharing of H1
|
|
% % is true then we can call the version of list.append
|
|
% % which does reuse. Calling the optimised version here leads
|
|
% % to a new condition to be met by the headvars of any
|
|
% % call to the resulting optimised version of append.
|
|
% % This is an indirect reuse.
|
|
% list.append(Xs, H2, Zs),
|
|
%
|
|
% % Reuse the dead cell H1. This is a direct reuse.
|
|
% H3 <= [X | Zs]
|
|
% ).
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module transform_hlds.ctgc.structure_reuse.analysis.
|
|
:- interface.
|
|
|
|
:- import_module analysis.
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module transform_hlds.ctgc.structure_reuse.domain.
|
|
|
|
:- import_module bool.
|
|
:- import_module io.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Perform structure reuse analysis on the procedures defined in the
|
|
% current module.
|
|
%
|
|
:- pred perform_structure_reuse_analysis(module_info::in, module_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type structure_reuse_call.
|
|
:- type structure_reuse_answer.
|
|
:- type structure_reuse_func_info.
|
|
|
|
:- instance analysis(structure_reuse_func_info, structure_reuse_call,
|
|
structure_reuse_answer).
|
|
|
|
:- instance call_pattern(structure_reuse_func_info, structure_reuse_call).
|
|
:- instance partial_order(structure_reuse_func_info, structure_reuse_call).
|
|
:- instance to_term(structure_reuse_call).
|
|
|
|
:- instance answer_pattern(structure_reuse_func_info, structure_reuse_answer).
|
|
:- instance partial_order(structure_reuse_func_info, structure_reuse_answer).
|
|
:- instance to_term(structure_reuse_answer).
|
|
|
|
:- pred structure_reuse_answer_harsher_than_in_analysis_registry(
|
|
module_info::in, reuse_as_table::in, pred_proc_id::in, bool::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.goal_path.
|
|
:- import_module hlds.hlds_out.
|
|
:- import_module hlds.hlds_out.hlds_out_util.
|
|
:- import_module hlds.passes_aux.
|
|
:- import_module hlds.pred_table.
|
|
:- import_module hlds.status.
|
|
:- import_module hlds.vartypes.
|
|
:- import_module libs.
|
|
:- import_module libs.file_util.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.op_mode.
|
|
:- import_module libs.options.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_ctgc.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_data_pragma.
|
|
:- import_module parse_tree.prog_type.
|
|
:- import_module parse_tree.set_of_var.
|
|
:- import_module transform_hlds.ctgc.selector.
|
|
:- import_module transform_hlds.ctgc.structure_reuse.direct.
|
|
:- import_module transform_hlds.ctgc.structure_reuse.indirect.
|
|
:- import_module transform_hlds.ctgc.structure_reuse.lbu.
|
|
:- import_module transform_hlds.ctgc.structure_reuse.lfu.
|
|
:- import_module transform_hlds.ctgc.structure_reuse.versions.
|
|
:- import_module transform_hlds.ctgc.structure_sharing.
|
|
:- import_module transform_hlds.ctgc.structure_sharing.domain.
|
|
:- import_module transform_hlds.intermod.
|
|
:- import_module transform_hlds.mmc_analysis.
|
|
|
|
:- import_module bimap.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
:- import_module term_conversion.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
perform_structure_reuse_analysis(!ModuleInfo, !IO):-
|
|
module_info_get_globals(!.ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
|
|
|
|
% Load all available structure sharing information into a sharing table.
|
|
SharingTable = load_structure_sharing_table(!.ModuleInfo),
|
|
|
|
% Process all imported reuse information.
|
|
globals.lookup_bool_option(Globals, intermodule_analysis,
|
|
IntermodAnalysis),
|
|
(
|
|
IntermodAnalysis = yes,
|
|
% Load structure reuse answers from the analysis registry into a reuse
|
|
% table. Add procedures to the module as necessary. Look up the
|
|
% requests made for procedures in this module by other modules.
|
|
process_intermod_analysis_reuse(!ModuleInfo, ReuseTable0,
|
|
ExternalRequests, MustHaveReuseVersions)
|
|
;
|
|
IntermodAnalysis = no,
|
|
% Convert imported structure reuse information into structure reuse
|
|
% information, then load the available reuse information into a reuse
|
|
% table.
|
|
%
|
|
% There is no way to request specific reuse versions of procedures
|
|
% across module boundaries using the old intermodule optimisation
|
|
% system.
|
|
process_imported_reuse(!ModuleInfo),
|
|
ReuseTable0 = load_structure_reuse_table(!.ModuleInfo),
|
|
ExternalRequests = [],
|
|
MustHaveReuseVersions = []
|
|
),
|
|
|
|
some [!ReuseTable] (
|
|
!:ReuseTable = ReuseTable0,
|
|
|
|
% Pre-annotate each of the goals with "Local Forward Use" and
|
|
% "Local Backward Use" information, and fill in all the goal_id slots
|
|
% as well.
|
|
trace [io(!TIO)] (
|
|
maybe_write_string(VeryVerbose,
|
|
"% Annotating in use information...", !TIO)
|
|
),
|
|
process_all_nonimported_procs(update_proc(annotate_in_use_information),
|
|
!ModuleInfo),
|
|
trace [io(!TIO)] (
|
|
maybe_write_string(VeryVerbose, "done.\n", !TIO),
|
|
maybe_write_string(VeryVerbose,
|
|
"% Reuse table before intermediate reuse:\n", !TIO),
|
|
reuse_as_table_maybe_dump(VeryVerbose, !.ModuleInfo, !.ReuseTable,
|
|
!TIO)
|
|
),
|
|
|
|
|
|
% Create copies of externally requested procedures. This must be done
|
|
% after the in-use annotations have been added to the procedures being
|
|
% copied.
|
|
list.map_foldl2(make_intermediate_reuse_proc, ExternalRequests,
|
|
_NewPPIds, !ReuseTable, !ModuleInfo),
|
|
|
|
% Determine information about possible direct reuses.
|
|
trace [io(!TIO)] (
|
|
maybe_write_string(VeryVerbose,
|
|
"% Reuse table after intermediate reuse:\n", !TIO),
|
|
reuse_as_table_maybe_dump(VeryVerbose, !.ModuleInfo, !.ReuseTable,
|
|
!TIO),
|
|
maybe_write_string(VeryVerbose, "% Direct reuse...\n", !TIO)
|
|
),
|
|
direct_reuse_pass(SharingTable, !ModuleInfo, !ReuseTable),
|
|
trace [io(!TIO)] (
|
|
maybe_write_string(VeryVerbose, "% Direct reuse: done.\n", !TIO),
|
|
reuse_as_table_maybe_dump(VeryVerbose, !.ModuleInfo, !.ReuseTable,
|
|
!TIO)
|
|
),
|
|
|
|
% Determine information about possible indirect reuses.
|
|
trace [io(!TIO)] (
|
|
maybe_write_string(VeryVerbose, "% Indirect reuse...\n", !TIO)
|
|
),
|
|
indirect_reuse_pass(SharingTable, !ModuleInfo, !ReuseTable, DepProcs0,
|
|
InternalRequests, IntermodRequests0),
|
|
trace [io(!TIO)] (
|
|
maybe_write_string(VeryVerbose, "% Indirect reuse: done.\n", !TIO),
|
|
reuse_as_table_maybe_dump(VeryVerbose, !.ModuleInfo, !.ReuseTable,
|
|
!TIO)
|
|
),
|
|
|
|
% Handle requests for "intermediate" reuse versions of procedures
|
|
% and repeat the analyses.
|
|
globals.lookup_int_option(Globals, structure_reuse_repeat, Repeats),
|
|
handle_structure_reuse_requests(Repeats, SharingTable,
|
|
InternalRequests, !ReuseTable, !ModuleInfo, DepProcs0, DepProcs,
|
|
IntermodRequests0, IntermodRequests),
|
|
|
|
% Create reuse versions of procedures. Update goals to reuse cells
|
|
% and call reuse versions of procedures.
|
|
create_reuse_procedures(!ReuseTable, !ModuleInfo),
|
|
|
|
ReuseTable = !.ReuseTable
|
|
),
|
|
|
|
(
|
|
IntermodAnalysis = no,
|
|
% Create forwarding procedures for procedures which we thought had
|
|
% conditional reuse when making the `.opt' file, but with further
|
|
% information (say, from `.trans_opt' files) we decide has no reuse
|
|
% opportunities. Otherwise other modules may contain references to
|
|
% reuse versions of procedures which we never produce.
|
|
maybe_create_forwarding_procedures_intermod_opt(ReuseTable0,
|
|
ReuseTable, !ModuleInfo)
|
|
;
|
|
IntermodAnalysis = yes,
|
|
% We may need to create forwarding procedures for procedures which had
|
|
% conditional reuse in the `.analysis' file, but which have no reuse
|
|
% or unconditional reuse now. We should only need to do this for
|
|
% procedures with NoClobbers = [].
|
|
list.foldl(
|
|
maybe_create_forwarding_procedures_intermod_analysis(ReuseTable),
|
|
MustHaveReuseVersions, !ModuleInfo)
|
|
),
|
|
|
|
ReuseTable = reuse_as_table(ReuseInfoMap, ReuseVersionMap),
|
|
|
|
% Record the results of the reuse table into the HLDS.
|
|
% This is mainly to show the reuse information in HLDS dumps as no later
|
|
% passes need the information.
|
|
map.foldl(save_reuse_in_module_info, ReuseInfoMap, !ModuleInfo),
|
|
module_info_get_proc_analysis_kinds(!.ModuleInfo, ProcAnalysisKinds0),
|
|
set.insert(pak_structure_reuse, ProcAnalysisKinds0, ProcAnalysisKinds),
|
|
module_info_set_proc_analysis_kinds(ProcAnalysisKinds, !ModuleInfo),
|
|
|
|
% If making a `.analysis' file, record structure reuse results, analysis
|
|
% dependencies, assumed answers and requests in the analysis framework.
|
|
globals.get_op_mode(Globals, OpMode),
|
|
( if OpMode = opm_top_args(opma_augment(opmau_make_analysis_registry)) then
|
|
some [!AnalysisInfo] (
|
|
module_info_get_analysis_info(!.ModuleInfo, !:AnalysisInfo),
|
|
map.foldl(
|
|
record_structure_reuse_results(!.ModuleInfo, ReuseVersionMap),
|
|
ReuseInfoMap, !AnalysisInfo),
|
|
set.fold(handle_structure_reuse_dependency(!.ModuleInfo),
|
|
DepProcs, !AnalysisInfo),
|
|
set.fold(record_intermod_requests(!.ModuleInfo),
|
|
IntermodRequests, !AnalysisInfo),
|
|
module_info_set_analysis_info(!.AnalysisInfo, !ModuleInfo)
|
|
)
|
|
else
|
|
true
|
|
),
|
|
|
|
% Delete the reuse versions of procedures which turn out to have no reuse.
|
|
% Nothing should be calling them but dead procedure elimination won't
|
|
% remove them if they were created from exported procedures (so would be
|
|
% exported themselves).
|
|
module_info_get_predicate_table(!.ModuleInfo, PredTable0),
|
|
bimap.foldl(
|
|
remove_useless_reuse_proc(!.ModuleInfo, VeryVerbose, ReuseInfoMap),
|
|
ReuseVersionMap, PredTable0, PredTable),
|
|
module_info_set_predicate_table(PredTable, !ModuleInfo),
|
|
|
|
selector.reset_tables(!IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Create intermediate reuse versions of procedures according to the
|
|
% requests from indirect reuse analysis. We perform direct reuse
|
|
% analyses on the newly created procedures, then repeat indirect reuse
|
|
% analysis on all procedures in the module so that calls to the new
|
|
% procedures can be made. This may create new requests.
|
|
%
|
|
% XXX this is temporary only; we shouldn't be redoing so much work.
|
|
%
|
|
:- pred handle_structure_reuse_requests(int::in, sharing_as_table::in,
|
|
set(sr_request)::in, reuse_as_table::in, reuse_as_table::out,
|
|
module_info::in, module_info::out,
|
|
set(ppid_no_clobbers)::in, set(ppid_no_clobbers)::out,
|
|
set(sr_request)::in, set(sr_request)::out) is det.
|
|
|
|
handle_structure_reuse_requests(Repeats, SharingTable, Requests,
|
|
!ReuseTable, !ModuleInfo, !DepProcs, !IntermodRequests) :-
|
|
( if Repeats > 0 then
|
|
handle_structure_reuse_requests_2(Repeats, SharingTable, Requests,
|
|
!ReuseTable, !ModuleInfo, !DepProcs, !IntermodRequests)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred handle_structure_reuse_requests_2(int::in, sharing_as_table::in,
|
|
set(sr_request)::in, reuse_as_table::in, reuse_as_table::out,
|
|
module_info::in, module_info::out,
|
|
set(ppid_no_clobbers)::in, set(ppid_no_clobbers)::out,
|
|
set(sr_request)::in, set(sr_request)::out) is det.
|
|
|
|
handle_structure_reuse_requests_2(Repeats, SharingTable, Requests,
|
|
!ReuseTable, !ModuleInfo, !DepProcs, !IntermodRequests) :-
|
|
module_info_get_globals(!.ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
|
|
|
|
% Create copies of the requested procedures.
|
|
RequestList = set.to_sorted_list(Requests),
|
|
list.map_foldl2(make_intermediate_reuse_proc, RequestList, NewPPIds,
|
|
!ReuseTable, !ModuleInfo),
|
|
|
|
% Perform direct reuse analysis on the new procedures.
|
|
trace [io(!IO)] (
|
|
maybe_write_string(VeryVerbose, "% Repeating direct reuse...\n", !IO)
|
|
),
|
|
direct_reuse_process_specific_procs(SharingTable, NewPPIds,
|
|
!ModuleInfo, !ReuseTable),
|
|
trace [io(!IO)] (
|
|
maybe_write_string(VeryVerbose, "% done.\n", !IO)
|
|
),
|
|
|
|
% Rerun indirect reuse analysis on all procedures.
|
|
%
|
|
% XXX goals which already have reuse annotations don't need to be
|
|
% reanalysed. For old procedures (not the ones just created) we actually
|
|
% only need to check that calls which previously had no reuse opportunity
|
|
% might be able to call the new procedures.
|
|
trace [io(!IO)] (
|
|
maybe_write_string(VeryVerbose, "% Repeating indirect reuse...\n", !IO)
|
|
),
|
|
indirect_reuse_rerun(SharingTable, !ModuleInfo, !ReuseTable,
|
|
NewDepProcs, NewRequests, !IntermodRequests),
|
|
!:DepProcs = set.union(NewDepProcs, !.DepProcs),
|
|
trace [io(!IO)] (
|
|
maybe_write_string(VeryVerbose, "% done.\n", !IO)
|
|
),
|
|
|
|
( if set.is_empty(NewRequests) then
|
|
trace [io(!IO)] (
|
|
maybe_write_string(VeryVerbose,
|
|
"% No more structure reuse requests.\n", !IO)
|
|
)
|
|
else
|
|
trace [io(!IO)] (
|
|
maybe_write_string(VeryVerbose,
|
|
"% Outstanding structure reuse requests exist.\n", !IO)
|
|
),
|
|
handle_structure_reuse_requests(Repeats - 1, SharingTable, NewRequests,
|
|
!ReuseTable, !ModuleInfo, !DepProcs, !IntermodRequests)
|
|
).
|
|
|
|
% Create a new copy of a procedure to satisfy an intermediate reuse
|
|
% request, i.e. some of its arguments are prevented from being reused.
|
|
%
|
|
% The goal of the original procedure must already be annotated with in-use
|
|
% sets. For the new procedure, we simply add the head variables at the
|
|
% no-clobber argument positions to the forward-use set of each goal.
|
|
% We also remove any existing reuse annotations on the goals.
|
|
%
|
|
:- pred make_intermediate_reuse_proc(sr_request::in, pred_proc_id::out,
|
|
reuse_as_table::in, reuse_as_table::out, module_info::in, module_info::out)
|
|
is det.
|
|
|
|
make_intermediate_reuse_proc(sr_request(PPId, NoClobbers), NewPPId,
|
|
!ReuseTable, !ModuleInfo) :-
|
|
create_fresh_pred_proc_info_copy(PPId, NoClobbers, NewPPId, !ModuleInfo),
|
|
|
|
module_info_pred_proc_info(!.ModuleInfo, NewPPId, PredInfo, ProcInfo0),
|
|
proc_info_get_headvars(ProcInfo0, HeadVars),
|
|
get_numbered_args(1, NoClobbers, HeadVars, NoClobberVars),
|
|
add_vars_to_lfu(set_of_var.list_to_set(NoClobberVars),
|
|
ProcInfo0, ProcInfo),
|
|
module_info_set_pred_proc_info(NewPPId, PredInfo, ProcInfo, !ModuleInfo),
|
|
|
|
reuse_as_table_insert_reuse_version_proc(PPId, NoClobbers, NewPPId,
|
|
!ReuseTable).
|
|
|
|
:- pred get_numbered_args(int::in, list(int)::in,
|
|
list(prog_var)::in, list(prog_var)::out) is det.
|
|
|
|
get_numbered_args(_, [], _, []).
|
|
get_numbered_args(_, [_ | _], [], _) :-
|
|
unexpected($pred, "argument list too short").
|
|
get_numbered_args(I, [N | Ns], [Var | Vars], Selected) :-
|
|
( if I = N then
|
|
get_numbered_args(I + 1, Ns, Vars, Selected0),
|
|
Selected = [Var | Selected0]
|
|
else
|
|
get_numbered_args(I + 1, [N | Ns], Vars, Selected)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred maybe_create_forwarding_procedures_intermod_opt(reuse_as_table::in,
|
|
reuse_as_table::in, module_info::in, module_info::out) is det.
|
|
|
|
maybe_create_forwarding_procedures_intermod_opt(
|
|
InitialReuseTable, FinalReuseTable, !ModuleInfo) :-
|
|
map.foldl(
|
|
maybe_create_forwarding_procedures_intermod_opt_2(FinalReuseTable),
|
|
InitialReuseTable ^ reuse_info_map, !ModuleInfo).
|
|
|
|
:- pred maybe_create_forwarding_procedures_intermod_opt_2(reuse_as_table::in,
|
|
pred_proc_id::in, reuse_as_and_status::in,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
maybe_create_forwarding_procedures_intermod_opt_2(FinalReuseTable, PPId,
|
|
reuse_as_and_status(InitialReuseAs, _), !ModuleInfo) :-
|
|
PPId = proc(PredId, _),
|
|
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
|
|
pred_info_get_status(PredInfo, PredStatus),
|
|
( if
|
|
reuse_as_conditional_reuses(InitialReuseAs),
|
|
pred_status_defined_in_this_module(PredStatus) = yes,
|
|
reuse_as_table_search(FinalReuseTable, PPId, FinalReuseAs_Status),
|
|
FinalReuseAs_Status = reuse_as_and_status(FinalReuseAs, _),
|
|
reuse_as_no_reuses(FinalReuseAs)
|
|
then
|
|
NoClobbers = [],
|
|
create_fake_reuse_procedure(PPId, NoClobbers, !ModuleInfo)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred maybe_create_forwarding_procedures_intermod_analysis(
|
|
reuse_as_table::in, pred_proc_id::in, module_info::in, module_info::out)
|
|
is det.
|
|
|
|
maybe_create_forwarding_procedures_intermod_analysis(ReuseTable, PredProcId,
|
|
!ModuleInfo) :-
|
|
% The procedure PredProcId would have been listed as having conditional
|
|
% reuse for call pattern NoClobbers = [] in the analysis registry. If our
|
|
% analysis of the procedure didn't create a conditional reuse version,
|
|
% then we need to produce a forwarding procedure to avoid linking
|
|
% problems.
|
|
( if
|
|
reuse_as_table_search(ReuseTable, PredProcId, ReuseAs_Status),
|
|
ReuseAs_Status = reuse_as_and_status(ReuseAs, _),
|
|
reuse_as_conditional_reuses(ReuseAs)
|
|
then
|
|
true
|
|
else
|
|
NoClobbers = [],
|
|
create_fake_reuse_procedure(PredProcId, NoClobbers, !ModuleInfo)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Process the imported reuse annotations from .opt files.
|
|
%
|
|
:- pred process_imported_reuse(module_info::in, module_info::out) is det.
|
|
|
|
process_imported_reuse(!ModuleInfo):-
|
|
module_info_get_valid_pred_ids(!.ModuleInfo, PredIds),
|
|
list.foldl(process_imported_reuse_in_pred, PredIds, !ModuleInfo).
|
|
|
|
:- pred process_imported_reuse_in_pred(pred_id::in, module_info::in,
|
|
module_info::out) is det.
|
|
|
|
process_imported_reuse_in_pred(PredId, !ModuleInfo) :-
|
|
some [!PredTable] (
|
|
module_info_get_preds(!.ModuleInfo, !:PredTable),
|
|
map.lookup(!.PredTable, PredId, PredInfo0),
|
|
process_imported_reuse_in_procs(PredInfo0, PredInfo),
|
|
map.det_update(PredId, PredInfo, !PredTable),
|
|
module_info_set_preds(!.PredTable, !ModuleInfo)
|
|
).
|
|
|
|
:- pred process_imported_reuse_in_procs(pred_info::in,
|
|
pred_info::out) is det.
|
|
|
|
process_imported_reuse_in_procs(!PredInfo) :-
|
|
some [!ProcTable] (
|
|
pred_info_get_proc_table(!.PredInfo, !:ProcTable),
|
|
ProcIds = pred_info_procids(!.PredInfo),
|
|
list.foldl(process_imported_reuse_in_proc(!.PredInfo),
|
|
ProcIds, !ProcTable),
|
|
pred_info_set_proc_table(!.ProcTable, !PredInfo)
|
|
).
|
|
|
|
:- pred process_imported_reuse_in_proc(pred_info::in, proc_id::in,
|
|
proc_table::in, proc_table::out) is det.
|
|
|
|
process_imported_reuse_in_proc(PredInfo, ProcId, !ProcTable) :-
|
|
some [!ProcInfo] (
|
|
!:ProcInfo = !.ProcTable ^ det_elem(ProcId),
|
|
( if
|
|
proc_info_get_imported_structure_reuse(!.ProcInfo,
|
|
ImpHeadVars, ImpTypes, ImpReuse)
|
|
then
|
|
proc_info_get_headvars(!.ProcInfo, HeadVars),
|
|
pred_info_get_arg_types(PredInfo, HeadVarTypes),
|
|
map.from_corresponding_lists(ImpHeadVars, HeadVars, VarRenaming),
|
|
some [!TypeSubst] (
|
|
!:TypeSubst = map.init,
|
|
( if
|
|
type_unify_list(ImpTypes, HeadVarTypes, [], !.TypeSubst,
|
|
TypeSubstNew)
|
|
then
|
|
!:TypeSubst = TypeSubstNew
|
|
else
|
|
true
|
|
),
|
|
rename_structure_reuse_domain(VarRenaming, !.TypeSubst,
|
|
ImpReuse, Reuse)
|
|
),
|
|
% Optimality does not apply to `--intermodule-optimisation'
|
|
% system, only `--intermodule-analysis'.
|
|
proc_info_set_structure_reuse(
|
|
structure_reuse_domain_and_status(Reuse, optimal), !ProcInfo),
|
|
proc_info_reset_imported_structure_reuse(!ProcInfo),
|
|
map.det_update(ProcId, !.ProcInfo, !ProcTable)
|
|
else
|
|
true
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Process the intermodule imported reuse information from the analysis
|
|
% framework.
|
|
%
|
|
:- pred process_intermod_analysis_reuse(module_info::in, module_info::out,
|
|
reuse_as_table::out, list(sr_request)::out, list(pred_proc_id)::out)
|
|
is det.
|
|
|
|
process_intermod_analysis_reuse(!ModuleInfo, ReuseTable, ExternalRequests,
|
|
MustHaveReuseVersions) :-
|
|
module_info_get_valid_pred_ids(!.ModuleInfo, PredIds),
|
|
list.foldl4(process_intermod_analysis_reuse_pred, PredIds,
|
|
!ModuleInfo, reuse_as_table_init, ReuseTable, [], ExternalRequests0,
|
|
[], MustHaveReuseVersions),
|
|
list.sort_and_remove_dups(ExternalRequests0, ExternalRequests).
|
|
|
|
:- pred process_intermod_analysis_reuse_pred(pred_id::in,
|
|
module_info::in, module_info::out, reuse_as_table::in, reuse_as_table::out,
|
|
list(sr_request)::in, list(sr_request)::out,
|
|
list(pred_proc_id)::in, list(pred_proc_id)::out) is det.
|
|
|
|
process_intermod_analysis_reuse_pred(PredId, !ModuleInfo, !ReuseTable,
|
|
!ExternalRequests, !MustHaveReuseVersions) :-
|
|
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
|
|
pred_info_get_status(PredInfo, PredStatus),
|
|
ProcIds = pred_info_procids(PredInfo),
|
|
( if
|
|
PredStatus = pred_status(status_imported(_))
|
|
then
|
|
% Read in answers for imported procedures.
|
|
list.foldl2(process_intermod_analysis_reuse_proc(PredId, PredInfo),
|
|
ProcIds, !ModuleInfo, !ReuseTable)
|
|
else if
|
|
pred_status_defined_in_this_module(PredStatus) = yes
|
|
then
|
|
% For procedures defined in this module we need to read in the answers
|
|
% from previous passes to know which versions of procedures other
|
|
% modules will be expecting. We also need to read in new requests.
|
|
list.foldl2(
|
|
process_intermod_analysis_defined_proc(!.ModuleInfo, PredId),
|
|
ProcIds, !ExternalRequests, !MustHaveReuseVersions)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred process_intermod_analysis_reuse_proc(pred_id::in,
|
|
pred_info::in, proc_id::in, module_info::in, module_info::out,
|
|
reuse_as_table::in, reuse_as_table::out) is det.
|
|
|
|
process_intermod_analysis_reuse_proc(PredId, PredInfo, ProcId,
|
|
!ModuleInfo, !ReuseTable) :-
|
|
PPId = proc(PredId, ProcId),
|
|
module_info_get_analysis_info(!.ModuleInfo, AnalysisInfo),
|
|
module_name_func_id(!.ModuleInfo, PPId, ModuleName, FuncId),
|
|
pred_info_proc_info(PredInfo, ProcId, ProcInfo),
|
|
lookup_results(AnalysisInfo, ModuleName, FuncId, ImportedResults),
|
|
list.foldl2(
|
|
process_intermod_analysis_imported_reuse_answer(PPId, PredInfo,
|
|
ProcInfo),
|
|
ImportedResults, !ModuleInfo, !ReuseTable).
|
|
|
|
:- pred process_intermod_analysis_imported_reuse_answer(pred_proc_id::in,
|
|
pred_info::in, proc_info::in,
|
|
analysis_result(structure_reuse_call, structure_reuse_answer)::in,
|
|
module_info::in, module_info::out, reuse_as_table::in, reuse_as_table::out)
|
|
is det.
|
|
|
|
process_intermod_analysis_imported_reuse_answer(PPId, PredInfo, ProcInfo,
|
|
ImportedResult, !ModuleInfo, !ReuseTable) :-
|
|
ImportedResult = analysis_result(Call, Answer, ResultStatus),
|
|
Call = structure_reuse_call(NoClobbers),
|
|
pred_info_get_arg_types(PredInfo, HeadVarTypes),
|
|
structure_reuse_answer_to_domain(HeadVarTypes, ProcInfo, Answer, Domain),
|
|
ReuseAs = from_structure_reuse_domain(Domain),
|
|
ReuseAs_Status = reuse_as_and_status(ReuseAs, ResultStatus),
|
|
(
|
|
NoClobbers = [],
|
|
% When the no-clobber list is empty we store the information with the
|
|
% original pred_proc_id.
|
|
reuse_as_table_set(PPId, ReuseAs_Status, !ReuseTable)
|
|
;
|
|
NoClobbers = [_ | _],
|
|
% When the no-clobber list is non-empty we need to create a new
|
|
% procedure stub and add a mapping to from the original pred_proc_id to
|
|
% the stub.
|
|
create_fresh_pred_proc_info_copy(PPId, NoClobbers, NewPPId,
|
|
!ModuleInfo),
|
|
reuse_as_table_set(NewPPId, ReuseAs_Status, !ReuseTable),
|
|
reuse_as_table_insert_reuse_version_proc(PPId, NoClobbers, NewPPId,
|
|
!ReuseTable)
|
|
).
|
|
|
|
:- pred structure_reuse_answer_to_domain(list(mer_type)::in,
|
|
proc_info::in, structure_reuse_answer::in, structure_reuse_domain::out)
|
|
is det.
|
|
|
|
structure_reuse_answer_to_domain(HeadVarTypes, ProcInfo, Answer, Reuse) :-
|
|
(
|
|
Answer = structure_reuse_answer_no_reuse,
|
|
Reuse = has_no_reuse
|
|
;
|
|
Answer = structure_reuse_answer_unconditional,
|
|
Reuse = has_only_unconditional_reuse
|
|
;
|
|
Answer = structure_reuse_answer_conditional(ImpHeadVars, ImpTypes,
|
|
ImpReuseConditions),
|
|
proc_info_get_headvars(ProcInfo, HeadVars),
|
|
map.from_corresponding_lists(ImpHeadVars, HeadVars, VarRenaming),
|
|
( if
|
|
type_unify_list(ImpTypes, HeadVarTypes, [], map.init, TypeSubst)
|
|
then
|
|
rename_structure_reuse_domain(VarRenaming, TypeSubst,
|
|
has_conditional_reuse(ImpReuseConditions), Reuse)
|
|
else
|
|
unexpected($pred, "type_unify_list failed")
|
|
)
|
|
).
|
|
|
|
:- pred process_intermod_analysis_defined_proc(module_info::in, pred_id::in,
|
|
proc_id::in, list(sr_request)::in, list(sr_request)::out,
|
|
list(pred_proc_id)::in, list(pred_proc_id)::out) is det.
|
|
|
|
process_intermod_analysis_defined_proc(ModuleInfo, PredId, ProcId,
|
|
!ExternalRequests, !MustHaveReuseVersions) :-
|
|
PPId = proc(PredId, ProcId),
|
|
module_info_get_analysis_info(ModuleInfo, AnalysisInfo),
|
|
module_name_func_id(ModuleInfo, PPId, ModuleName, FuncId),
|
|
|
|
% Only add requests for procedures that *really* belong to this module.
|
|
module_info_get_name(ModuleInfo, ThisModule),
|
|
( if ThisModule = ModuleName then
|
|
% Add requests corresponding to the call patterns of existing answers.
|
|
lookup_existing_call_patterns(AnalysisInfo, analysis_name, ModuleName,
|
|
FuncId, OldCalls),
|
|
list.foldl(add_reuse_request(PPId), OldCalls, !ExternalRequests),
|
|
|
|
% Add new requests from other modules.
|
|
lookup_requests(AnalysisInfo, analysis_name, ModuleName, FuncId,
|
|
NewCalls),
|
|
list.foldl(add_reuse_request(PPId), NewCalls, !ExternalRequests),
|
|
|
|
% A procedure listed as having conditional reuse *must* have a reuse
|
|
% version procedure, even if in this analysis we don't find
|
|
% conditional reuse.
|
|
module_info_proc_info(ModuleInfo, PPId, ProcInfo),
|
|
FuncInfo = structure_reuse_func_info(ModuleInfo, ProcInfo),
|
|
Call = structure_reuse_call([]),
|
|
lookup_best_result(AnalysisInfo, ModuleName, FuncId, FuncInfo, Call,
|
|
MaybeBestResult),
|
|
(
|
|
MaybeBestResult = yes(analysis_result(_, Answer, _)),
|
|
(
|
|
Answer = structure_reuse_answer_no_reuse
|
|
;
|
|
Answer = structure_reuse_answer_unconditional
|
|
;
|
|
Answer = structure_reuse_answer_conditional(_, _, _),
|
|
!:MustHaveReuseVersions = [PPId | !.MustHaveReuseVersions]
|
|
)
|
|
;
|
|
MaybeBestResult = no
|
|
)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred add_reuse_request(pred_proc_id::in, structure_reuse_call::in,
|
|
list(sr_request)::in, list(sr_request)::out) is det.
|
|
|
|
add_reuse_request(PPId, structure_reuse_call(NoClobbers), !Requests) :-
|
|
(
|
|
NoClobbers = []
|
|
% We don't need to add these as explicit requests, and in fact it's
|
|
% better if we don't. The analysis is already designed to analyse for
|
|
% this case by default and create the reuse procedures if necessary.
|
|
;
|
|
NoClobbers = [_ | _],
|
|
!:Requests = [sr_request(PPId, NoClobbers) | !.Requests]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred save_reuse_in_module_info(pred_proc_id::in, reuse_as_and_status::in,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
save_reuse_in_module_info(PPId, ReuseAs_Status, !ModuleInfo) :-
|
|
ReuseAs_Status = reuse_as_and_status(ReuseAs, Status),
|
|
ReuseDomain = to_structure_reuse_domain(ReuseAs),
|
|
Domain_Status = structure_reuse_domain_and_status(ReuseDomain, Status),
|
|
|
|
module_info_pred_proc_info(!.ModuleInfo, PPId, PredInfo, ProcInfo0),
|
|
proc_info_set_structure_reuse(Domain_Status, ProcInfo0, ProcInfo),
|
|
module_info_set_pred_proc_info(PPId, PredInfo, ProcInfo, !ModuleInfo).
|
|
|
|
:- pred annotate_in_use_information(module_info::in,
|
|
proc_info::in, proc_info::out) is det.
|
|
|
|
annotate_in_use_information(ModuleInfo, !ProcInfo) :-
|
|
forward_use_information(!ProcInfo),
|
|
backward_use_information(ModuleInfo, !ProcInfo),
|
|
fill_goal_path_slots_in_proc(ModuleInfo, !ProcInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Types and instances for the intermodule analysis framework.
|
|
%
|
|
|
|
:- type structure_reuse_call
|
|
---> structure_reuse_call(no_clobber_args).
|
|
|
|
:- type structure_reuse_answer
|
|
---> structure_reuse_answer_no_reuse
|
|
; structure_reuse_answer_unconditional
|
|
; structure_reuse_answer_conditional(
|
|
srac_vars :: prog_vars,
|
|
srac_types :: list(mer_type),
|
|
srac_conds :: structure_reuse_conditions
|
|
% We cannot keep this as a reuse_as. When the analysis answers
|
|
% are loaded, we don't have enough information to rename the
|
|
% variables in the .analysis answer to the correct variables
|
|
% for the proc_info that the reuse_as will be used with.
|
|
).
|
|
|
|
:- type structure_reuse_func_info
|
|
---> structure_reuse_func_info(
|
|
srfi_module :: module_info,
|
|
srfi_proc :: proc_info
|
|
).
|
|
|
|
:- func analysis_name = string.
|
|
|
|
analysis_name = "structure_reuse".
|
|
|
|
:- instance analysis(structure_reuse_func_info, structure_reuse_call,
|
|
structure_reuse_answer) where
|
|
[
|
|
analysis_name(_, _) = analysis_name,
|
|
analysis_version_number(_, _) = 3,
|
|
preferred_fixpoint_type(_, _) = greatest_fixpoint,
|
|
bottom(_, _) = structure_reuse_answer_no_reuse,
|
|
( top(_, _) = _ :-
|
|
% We have no representation for "all possible conditions".
|
|
unexpected($pred, "top/2 called")
|
|
),
|
|
( get_func_info(ModuleInfo, ModuleName, FuncId, _, _, FuncInfo) :-
|
|
func_id_to_ppid(ModuleInfo, ModuleName, FuncId, PPId),
|
|
module_info_proc_info(ModuleInfo, PPId, ProcInfo),
|
|
FuncInfo = structure_reuse_func_info(ModuleInfo, ProcInfo)
|
|
)
|
|
].
|
|
|
|
:- instance call_pattern(structure_reuse_func_info, structure_reuse_call)
|
|
where [].
|
|
|
|
:- instance partial_order(structure_reuse_func_info, structure_reuse_call)
|
|
where [
|
|
(more_precise_than(_, Call1, Call2) :-
|
|
Call1 = structure_reuse_call(Args1),
|
|
Call2 = structure_reuse_call(Args2),
|
|
set.subset(sorted_list_to_set(Args2), sorted_list_to_set(Args1))
|
|
),
|
|
equivalent(_, Call, Call)
|
|
].
|
|
|
|
:- instance to_term(structure_reuse_call) where [
|
|
( to_term(Call) = Term :-
|
|
Call = structure_reuse_call(NoClobbers),
|
|
type_to_term(NoClobbers, Term)
|
|
),
|
|
( from_term(Term, Call) :-
|
|
term_to_type(Term, NoClobbers),
|
|
Call = structure_reuse_call(NoClobbers)
|
|
)
|
|
].
|
|
|
|
:- instance answer_pattern(structure_reuse_func_info, structure_reuse_answer)
|
|
where [].
|
|
|
|
:- instance partial_order(structure_reuse_func_info, structure_reuse_answer)
|
|
where [
|
|
|
|
% We deliberately have `conditional' reuse incomparable with
|
|
% `unconditional' reuse. If they were comparable, a caller using an
|
|
% `conditional' answer would would only be marked `suboptimal' if that
|
|
% answer changes to `unconditional'. Since we don't honour the old
|
|
% `conditional' answer by generating that version of the procedure, there
|
|
% would be a linking error if the caller is not updated to call the
|
|
% unconditional version.
|
|
|
|
(more_precise_than(FuncInfo, Answer1, Answer2) :-
|
|
(
|
|
Answer1 = structure_reuse_answer_conditional(_, _, _),
|
|
Answer2 = structure_reuse_answer_no_reuse
|
|
;
|
|
Answer1 = structure_reuse_answer_unconditional,
|
|
Answer2 = structure_reuse_answer_no_reuse
|
|
;
|
|
Answer1 = structure_reuse_answer_conditional(_, _, _),
|
|
Answer2 = structure_reuse_answer_conditional(_, _, _),
|
|
FuncInfo = structure_reuse_func_info(ModuleInfo, ProcInfo),
|
|
proc_info_get_headvars(ProcInfo, HeadVars),
|
|
proc_info_get_vartypes(ProcInfo, VarTypes),
|
|
lookup_var_types(VarTypes, HeadVars, HeadVarTypes),
|
|
structure_reuse_answer_to_domain(HeadVarTypes, ProcInfo, Answer1,
|
|
Reuse1),
|
|
structure_reuse_answer_to_domain(HeadVarTypes, ProcInfo, Answer2,
|
|
Reuse2),
|
|
ReuseAs1 = from_structure_reuse_domain(Reuse1),
|
|
ReuseAs2 = from_structure_reuse_domain(Reuse2),
|
|
reuse_as_subsumed_by(ModuleInfo, ProcInfo, ReuseAs1, ReuseAs2),
|
|
not reuse_as_subsumed_by(ModuleInfo, ProcInfo, ReuseAs2, ReuseAs1)
|
|
)
|
|
),
|
|
|
|
(equivalent(FuncInfo, Answer1, Answer2) :-
|
|
(
|
|
Answer1 = Answer2
|
|
;
|
|
Answer1 = structure_reuse_answer_conditional(_, _, _),
|
|
Answer2 = structure_reuse_answer_conditional(_, _, _),
|
|
FuncInfo = structure_reuse_func_info(ModuleInfo, ProcInfo),
|
|
proc_info_get_headvars(ProcInfo, HeadVars),
|
|
proc_info_get_vartypes(ProcInfo, VarTypes),
|
|
lookup_var_types(VarTypes, HeadVars, HeadVarTypes),
|
|
structure_reuse_answer_to_domain(HeadVarTypes, ProcInfo, Answer1,
|
|
Reuse1),
|
|
structure_reuse_answer_to_domain(HeadVarTypes, ProcInfo, Answer2,
|
|
Reuse2),
|
|
ReuseAs1 = from_structure_reuse_domain(Reuse1),
|
|
ReuseAs2 = from_structure_reuse_domain(Reuse2),
|
|
reuse_as_subsumed_by(ModuleInfo, ProcInfo, ReuseAs2, ReuseAs1),
|
|
reuse_as_subsumed_by(ModuleInfo, ProcInfo, ReuseAs1, ReuseAs2)
|
|
)
|
|
)
|
|
].
|
|
|
|
:- instance to_term(structure_reuse_answer) where [
|
|
func(to_term/1) is reuse_answer_to_term,
|
|
pred(from_term/2) is reuse_answer_from_term
|
|
].
|
|
|
|
:- func reuse_answer_to_term(structure_reuse_answer) = term.
|
|
|
|
reuse_answer_to_term(Answer) = Term :-
|
|
(
|
|
Answer = structure_reuse_answer_no_reuse,
|
|
Term = term.functor(atom("no_reuse"), [], term.context_init)
|
|
;
|
|
Answer = structure_reuse_answer_unconditional,
|
|
Term = term.functor(atom("uncond"), [], term.context_init)
|
|
;
|
|
Answer = structure_reuse_answer_conditional(HeadVars, Types,
|
|
Conditions),
|
|
type_to_term(HeadVars, HeadVarsTerm),
|
|
type_to_term(Types, TypesTerm),
|
|
type_to_term(Conditions, ConditionsTerm),
|
|
Term = term.functor(atom("cond"),
|
|
[HeadVarsTerm, TypesTerm, ConditionsTerm], term.context_init)
|
|
).
|
|
|
|
:- pred reuse_answer_from_term(term::in, structure_reuse_answer::out)
|
|
is semidet.
|
|
|
|
reuse_answer_from_term(Term, Answer) :-
|
|
(
|
|
Term = functor(atom("no_reuse"), [], _),
|
|
Answer = structure_reuse_answer_no_reuse
|
|
;
|
|
Term = functor(atom("uncond"), [], _),
|
|
Answer = structure_reuse_answer_unconditional
|
|
;
|
|
Term = functor(atom("cond"),
|
|
[HeadVarsTerm, TypesTerm, ConditionsTerm], _),
|
|
term_to_type(HeadVarsTerm, HeadVars),
|
|
term_to_type(TypesTerm, Types),
|
|
term_to_type(ConditionsTerm, Conditions),
|
|
Answer = structure_reuse_answer_conditional(HeadVars, Types,
|
|
Conditions)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Additional predicates used for intermodule analysis.
|
|
%
|
|
|
|
:- pred record_structure_reuse_results(module_info::in,
|
|
bimap(ppid_no_clobbers, pred_proc_id)::in, pred_proc_id::in,
|
|
reuse_as_and_status::in, analysis_info::in, analysis_info::out) is det.
|
|
|
|
record_structure_reuse_results(ModuleInfo, CondReuseMap, PPId, ReuseAs_Status,
|
|
!AnalysisInfo) :-
|
|
( if bimap.reverse_search(CondReuseMap, Key, PPId) then
|
|
% PPId is a conditional reuse procedure created from another procedure.
|
|
% We need to record the result using the name of the original
|
|
% procedure.
|
|
Key = ppid_no_clobbers(RecordPPId, NoClobbers)
|
|
else
|
|
RecordPPId = PPId,
|
|
NoClobbers = []
|
|
),
|
|
record_structure_reuse_results_2(ModuleInfo, RecordPPId, NoClobbers,
|
|
ReuseAs_Status, !AnalysisInfo).
|
|
|
|
:- pred record_structure_reuse_results_2(module_info::in, pred_proc_id::in,
|
|
no_clobber_args::in, reuse_as_and_status::in,
|
|
analysis_info::in, analysis_info::out) is det.
|
|
|
|
record_structure_reuse_results_2(ModuleInfo, PPId, NoClobbers, ReuseAs_Status,
|
|
!AnalysisInfo) :-
|
|
PPId = proc(PredId, ProcId),
|
|
ReuseAs_Status = reuse_as_and_status(ReuseAs, Status),
|
|
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
should_write_reuse_info(ModuleInfo, PredId, ProcId, PredInfo,
|
|
for_analysis_framework, ShouldWrite),
|
|
(
|
|
ShouldWrite = should_write,
|
|
reuse_as_to_structure_reuse_answer(ModuleInfo, PPId, ReuseAs, Answer),
|
|
module_name_func_id(ModuleInfo, PPId, ModuleName, FuncId),
|
|
record_result(ModuleName, FuncId, structure_reuse_call(NoClobbers),
|
|
Answer, Status, !AnalysisInfo)
|
|
;
|
|
ShouldWrite = should_not_write
|
|
).
|
|
|
|
:- pred reuse_as_to_structure_reuse_answer(module_info::in, pred_proc_id::in,
|
|
reuse_as::in, structure_reuse_answer::out) is det.
|
|
|
|
reuse_as_to_structure_reuse_answer(ModuleInfo, PPId, ReuseAs, Answer) :-
|
|
Reuse = to_structure_reuse_domain(ReuseAs),
|
|
(
|
|
Reuse = has_no_reuse,
|
|
Answer = structure_reuse_answer_no_reuse
|
|
;
|
|
Reuse = has_only_unconditional_reuse,
|
|
Answer = structure_reuse_answer_unconditional
|
|
;
|
|
Reuse = has_conditional_reuse(Conditions),
|
|
module_info_proc_info(ModuleInfo, PPId, ProcInfo),
|
|
proc_info_get_headvars(ProcInfo, HeadVars),
|
|
proc_info_get_vartypes(ProcInfo, VarTypes),
|
|
lookup_var_types(VarTypes, HeadVars, HeadVarTypes),
|
|
Answer = structure_reuse_answer_conditional(HeadVars, HeadVarTypes,
|
|
Conditions)
|
|
).
|
|
|
|
:- pred handle_structure_reuse_dependency(module_info::in,
|
|
ppid_no_clobbers::in, analysis_info::in, analysis_info::out) is det.
|
|
|
|
handle_structure_reuse_dependency(ModuleInfo,
|
|
ppid_no_clobbers(DepPPId, NoClobbers), !AnalysisInfo) :-
|
|
% Record that we depend on the result for the called procedure.
|
|
module_name_func_id(ModuleInfo, DepPPId, DepModuleName, DepFuncId),
|
|
Call = structure_reuse_call(NoClobbers),
|
|
Answer = _ : structure_reuse_answer,
|
|
get_func_info(ModuleInfo, DepModuleName, DepFuncId, Call, Answer,
|
|
FuncInfo),
|
|
record_dependency(DepModuleName, DepFuncId, FuncInfo, Call, Answer,
|
|
!AnalysisInfo).
|
|
|
|
:- pred record_intermod_requests(module_info::in, sr_request::in,
|
|
analysis_info::in, analysis_info::out) is det.
|
|
|
|
record_intermod_requests(ModuleInfo, sr_request(PPId, NoClobbers),
|
|
!AnalysisInfo) :-
|
|
module_name_func_id(ModuleInfo, PPId, ModuleName, FuncId),
|
|
record_request(analysis_name, ModuleName, FuncId,
|
|
structure_reuse_call(NoClobbers), !AnalysisInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% for structure_reuse.versions
|
|
%
|
|
|
|
structure_reuse_answer_harsher_than_in_analysis_registry(ModuleInfo,
|
|
ReuseTable, ReusePPId, Harsher) :-
|
|
module_info_get_analysis_info(ModuleInfo, AnalysisInfo),
|
|
|
|
% Find the original pred_proc_id and no-clobber list that this reuse
|
|
% procedure was made for.
|
|
reuse_as_table_reverse_search_reuse_version_proc(ReuseTable, ReusePPId,
|
|
OrigPPId, NoClobbers),
|
|
|
|
% Look up the old result.
|
|
module_name_func_id(ModuleInfo, OrigPPId, ModuleName, FuncId),
|
|
module_info_proc_info(ModuleInfo, OrigPPId, ProcInfo),
|
|
FuncInfo = structure_reuse_func_info(ModuleInfo, ProcInfo),
|
|
Call = structure_reuse_call(NoClobbers),
|
|
analysis.lookup_best_result(AnalysisInfo, ModuleName, FuncId, FuncInfo,
|
|
Call, MaybeOldResult),
|
|
( if
|
|
MaybeOldResult = yes(analysis_result(OldCall, OldAnswer, _)),
|
|
equivalent(FuncInfo, Call, OldCall)
|
|
then
|
|
% Compare with the new result.
|
|
lookup_new_structure_reuse_answer(ModuleInfo, ReuseTable, ReusePPId,
|
|
NewAnswer),
|
|
( if more_precise_than(FuncInfo, NewAnswer, OldAnswer) then
|
|
Harsher = yes,
|
|
trace [
|
|
compile_time(flag("harsher_answer_check")),
|
|
runtime(env("HARSHER_ANSWER_CHECK")),
|
|
io(!IO)
|
|
] (
|
|
io.write_string("Structure reuse answer for ", !IO),
|
|
write_pred_proc_id(ModuleInfo, ReusePPId, !IO),
|
|
io.write_string(" has harsher conditions than listed " ++
|
|
"in analysis file.\n", !IO),
|
|
io.write_string("was: ", !IO),
|
|
io.write(OldAnswer, !IO),
|
|
io.nl(!IO),
|
|
io.write_string("now: ", !IO),
|
|
io.write(NewAnswer, !IO),
|
|
io.nl(!IO)
|
|
)
|
|
else
|
|
Harsher = no
|
|
)
|
|
else
|
|
Harsher = no
|
|
).
|
|
|
|
:- pred lookup_new_structure_reuse_answer(module_info::in, reuse_as_table::in,
|
|
pred_proc_id::in, structure_reuse_answer::out) is det.
|
|
|
|
lookup_new_structure_reuse_answer(ModuleInfo, ReuseTable, ReusePPId,
|
|
NewAnswer) :-
|
|
( if reuse_as_table_search(ReuseTable, ReusePPId, ReuseAs_Status) then
|
|
ReuseAs_Status = reuse_as_and_status(NewReuseAs, _)
|
|
else
|
|
unexpected($pred, "search failed")
|
|
),
|
|
reuse_as_to_structure_reuse_answer(ModuleInfo, ReusePPId, NewReuseAs,
|
|
NewAnswer).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred remove_useless_reuse_proc(module_info::in, bool::in,
|
|
map(pred_proc_id, reuse_as_and_status)::in,
|
|
ppid_no_clobbers::in, pred_proc_id::in,
|
|
predicate_table::in, predicate_table::out) is det.
|
|
|
|
remove_useless_reuse_proc(ModuleInfo, VeryVerbose, ReuseAsMap, _, PPId,
|
|
!PredTable) :-
|
|
map.lookup(ReuseAsMap, PPId, ReuseAs_Status),
|
|
ReuseAs_Status = reuse_as_and_status(ReuseAs, _),
|
|
% XXX perhaps we can also remove reuse procedures with only unconditional
|
|
% reuse? Such a procedure should be the same as the "non-reuse" procedure
|
|
% (which also implements any unconditional reuse).
|
|
( if reuse_as_no_reuses(ReuseAs) then
|
|
(
|
|
VeryVerbose = yes,
|
|
trace [io(!IO)] (
|
|
io.write_string("% Removing useless reuse ", !IO),
|
|
write_pred_proc_id(ModuleInfo, PPId, !IO),
|
|
io.nl(!IO)
|
|
)
|
|
;
|
|
VeryVerbose = no
|
|
),
|
|
|
|
PPId = proc(PredId, _),
|
|
% We can remove the whole predicate because we never generate
|
|
% multi-moded reuse versions of predicates.
|
|
predicate_table_remove_predicate(PredId, !PredTable)
|
|
else
|
|
true
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module transform_hlds.ctgc.structure_reuse.analysis.
|
|
%-----------------------------------------------------------------------------%
|