mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-17 06:47:17 +00:00
Estimated hours taken: 18 Branches: main Move the univ, maybe, pair and unit types from std_util into their own modules. std_util still contains the general purpose higher-order programming constructs. library/std_util.m: Move univ, maybe, pair and unit (plus any other related types and procedures) into their own modules. library/maybe.m: New module. This contains the maybe and maybe_error types and the associated procedures. library/pair.m: New module. This contains the pair type and associated procedures. library/unit.m: New module. This contains the types unit/0 and unit/1. library/univ.m: New module. This contains the univ type and associated procedures. library/library.m: Add the new modules. library/private_builtin.m: Update the declaration of the type_ctor_info struct for univ. runtime/mercury.h: Update the declaration for the type_ctor_info struct for univ. runtime/mercury_mcpp.h: runtime/mercury_hlc_types.h: Update the definition of MR_Univ. runtime/mercury_init.h: Fix a comment: ML_type_name is now exported from type_desc.m. compiler/mlds_to_il.m: Update the the name of the module that defines univs (which are handled specially by the il code generator.) library/*.m: compiler/*.m: browser/*.m: mdbcomp/*.m: profiler/*.m: deep_profiler/*.m: Conform to the above changes. Import the new modules where they are needed; don't import std_util where it isn't needed. Fix formatting in lots of modules. Delete duplicate module imports. tests/*: Update the test suite to confrom to the above changes.
1170 lines
40 KiB
Mathematica
1170 lines
40 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=8 sw=8 noet
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2003-2004, 2006 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU Library General
|
|
% Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
% File: analysis.m
|
|
% Main authors: stayl, wangp
|
|
%
|
|
% An inter-module analysis framework, as described in
|
|
%
|
|
% Nicholas Nethercote. The Analysis Framework of HAL,
|
|
% Chapter 7: Inter-module Analysis, Master's Thesis,
|
|
% University of Melbourne, September 2001, revised April 2002.
|
|
% <http://www.cl.cam.ac.uk/~njn25/pubs/masters2001.ps.gz>.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module analysis.
|
|
:- interface.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
:- import_module set.
|
|
:- import_module unit.
|
|
:- import_module univ.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The intention is that eventually any compiler can
|
|
% use this library via .NET by defining an instance
|
|
% of this class.
|
|
:- typeclass compiler(Compiler) where [
|
|
func compiler_name(Compiler) = string,
|
|
|
|
% Describe the analyses which can be performed by a compiler.
|
|
func analyses(Compiler, analysis_name) = analysis_type is semidet,
|
|
|
|
% module_id_to_read_file_name(Compiler, ModuleId, Ext, FileName)
|
|
pred module_id_to_read_file_name(Compiler::in, module_id::in,
|
|
string::in, maybe_error(string)::out, io::di, io::uo) is det,
|
|
|
|
% module_id_to_write_file_name(Compiler, ModuleId, Ext, FileName)
|
|
pred module_id_to_write_file_name(Compiler::in, module_id::in,
|
|
string::in, string::out, io::di, io::uo) is det,
|
|
|
|
% module_is_local(Compiler, ModuleId, IsLocal, !IO)
|
|
%
|
|
% IsLocal is `yes' if the module is not a "library" module, i.e. we are
|
|
% able to reanalyse the module, not just use results that already
|
|
% exist.
|
|
%
|
|
pred module_is_local(Compiler::in, module_id::in, bool::out,
|
|
io::di, io::uo) is det
|
|
].
|
|
|
|
:- type module_id == string.
|
|
|
|
:- type analysis_name == string.
|
|
|
|
:- type analysis_type
|
|
---> some [Call, Answer]
|
|
analysis_type(unit(Call), unit(Answer))
|
|
=> analysis(Call, Answer).
|
|
|
|
% An analysis is defined by a type describing call patterns and
|
|
% a type defining answer patterns. If the analysis needs to store
|
|
% more information about the function being analysed (e.g. arity)
|
|
% it should be stored as part of the type for call patterns.
|
|
%
|
|
:- typeclass analysis(Call, Answer) <=
|
|
(call_pattern(Call),
|
|
answer_pattern(Answer))
|
|
where
|
|
[
|
|
func analysis_name(Call::unused, Answer::unused) =
|
|
(analysis_name::out) is det,
|
|
|
|
% The version number should be changed when the Call or Answer
|
|
% types are changed so that results which use the old types
|
|
% can be discarded.
|
|
func analysis_version_number(Call::unused,
|
|
Answer::unused) = (int::out) is det,
|
|
|
|
func preferred_fixpoint_type(Call::unused,
|
|
Answer::unused) = (fixpoint_type::out) is det,
|
|
|
|
% `top' and `bottom' should not really depend on the call pattern.
|
|
% However some analyses may choose to store extra information about
|
|
% the function in their `Call' types that might be needed for the
|
|
% answer pattern.
|
|
%
|
|
func bottom(Call) = Answer,
|
|
func top(Call) = Answer
|
|
].
|
|
|
|
:- type fixpoint_type
|
|
--->
|
|
% Start at `bottom'.
|
|
% Must run to completion.
|
|
least_fixpoint
|
|
;
|
|
% Start at `top'.
|
|
% Can stop at any time.
|
|
greatest_fixpoint.
|
|
|
|
:- typeclass call_pattern(Call)
|
|
<= (partial_order(Call), to_string(Call)) where [].
|
|
|
|
:- typeclass answer_pattern(Answer)
|
|
<= (partial_order(Answer), to_string(Answer)) where [].
|
|
|
|
% Extra information may be stored in a module's `.analysis' file, apart
|
|
% from the analysis results. This information is indexed by a string
|
|
% key. The extra information must be convertable to/from a string.
|
|
%
|
|
:- type extra_info_key == string.
|
|
|
|
:- typeclass extra_info(ExtraInfo) <= to_string(ExtraInfo) where [].
|
|
|
|
:- typeclass partial_order(T) where [
|
|
pred more_precise_than(T::in, T::in) is semidet,
|
|
pred equivalent(T::in, T::in) is semidet
|
|
].
|
|
|
|
:- typeclass to_string(S) where [
|
|
func to_string(S) = string,
|
|
func from_string(string) = S is semidet
|
|
].
|
|
|
|
% A call pattern that can be used by analyses that do not need
|
|
% finer granularity.
|
|
%
|
|
:- type any_call ---> any_call.
|
|
:- instance call_pattern(any_call).
|
|
:- instance partial_order(any_call).
|
|
:- instance to_string(any_call).
|
|
|
|
% The status of a module or a specific analysis result.
|
|
%
|
|
:- type analysis_status
|
|
---> invalid
|
|
; suboptimal
|
|
; optimal.
|
|
|
|
% Least upper bound of two analysis_status values.
|
|
%
|
|
:- func lub(analysis_status, analysis_status) = analysis_status.
|
|
|
|
% This will need to encode language specific details like
|
|
% whether it is a predicate or a function, and the arity
|
|
% and mode number.
|
|
:- type func_id == string.
|
|
|
|
% Holds information used while analysing a module.
|
|
:- type analysis_info.
|
|
|
|
:- func init_analysis_info(Compiler) = analysis_info <= compiler(Compiler).
|
|
|
|
% Look up all results for a given function.
|
|
%
|
|
% N.B. Newly recorded results will NOT be found. This
|
|
% is intended for looking up results from _other_ modules.
|
|
%
|
|
:- pred lookup_results(module_id::in, func_id::in,
|
|
list({Call, Answer, analysis_status})::out,
|
|
analysis_info::in, analysis_info::out, io::di, io::uo) is det
|
|
<= analysis(Call, Answer).
|
|
|
|
% Look up all results for a given function and call pattern CP such
|
|
% that the results have call patterns CP' that are equivalent
|
|
% to CP or less specific than CP.
|
|
%
|
|
% N.B. Newly recorded results will NOT be found. This
|
|
% is intended for looking up results from _other_ modules.
|
|
%
|
|
:- pred lookup_matching_results(module_id::in, func_id::in, Call::in,
|
|
list({Call, Answer, analysis_status})::out,
|
|
analysis_info::in, analysis_info::out, io::di, io::uo) is det
|
|
<= analysis(Call, Answer).
|
|
|
|
% Look up the best result matching a given call.
|
|
%
|
|
% N.B. Newly recorded results will NOT be found. This
|
|
% is intended for looking up results from _other_ modules.
|
|
%
|
|
% If the returned best result has a call pattern that is different
|
|
% from the given call pattern, then it is the analysis writer's
|
|
% responsibility to request a more precise analysis from the
|
|
% called module, using `record_request'.
|
|
%
|
|
:- pred lookup_best_result(module_id::in, func_id::in, Call::in,
|
|
maybe({Call, Answer, analysis_status})::out,
|
|
analysis_info::in, analysis_info::out, io::di, io::uo) is det
|
|
<= analysis(Call, Answer).
|
|
|
|
% Record an analysis result for a (usually local) function.
|
|
% XXX at the moment the result is assumed to be for a function local to
|
|
% the currently-compiled module and things will probably break if it
|
|
% isn't.
|
|
%
|
|
:- pred record_result(module_id::in, func_id::in, Call::in,
|
|
Answer::in, analysis_status::in,
|
|
analysis_info::in, analysis_info::out) is det
|
|
<= analysis(Call, Answer).
|
|
|
|
% Record the dependency of a module on the analysis result of another
|
|
% module.
|
|
%
|
|
:- pred record_dependency(module_id::in, analysis_name::in, module_id::in,
|
|
func_id::in, Call::in, analysis_info::in, analysis_info::out) is det
|
|
<= call_pattern(Call).
|
|
|
|
% Lookup all the requests for a given (usually local) function.
|
|
:- pred lookup_requests(analysis_name::in, module_id::in, func_id::in,
|
|
list(Call)::out, analysis_info::in, analysis_info::out,
|
|
io::di, io::uo) is det <= call_pattern(Call).
|
|
|
|
% Record a request for a function in an imported module.
|
|
%
|
|
:- pred record_request(analysis_name::in, module_id::in, func_id::in,
|
|
Call::in, analysis_info::in, analysis_info::out) is det
|
|
<= call_pattern(Call).
|
|
|
|
% Lookup extra information about a module, using the key given.
|
|
%
|
|
:- pred lookup_module_extra_info(module_id::in, extra_info_key::in,
|
|
maybe(ExtraInfo)::out, analysis_info::in, analysis_info::out,
|
|
io::di, io::uo) is det <= extra_info(ExtraInfo).
|
|
|
|
% Record extra information about a module under the given key.
|
|
%
|
|
:- pred record_module_extra_info(module_id::in, extra_info_key::in,
|
|
ExtraInfo::in, analysis_info::in, analysis_info::out) is det
|
|
<= extra_info(ExtraInfo).
|
|
|
|
% Should be called after all analysis is completed to write the
|
|
% requests and results for the current compilation to the
|
|
% analysis files.
|
|
%
|
|
:- pred write_analysis_files(Compiler::in, module_id::in, set(module_id)::in,
|
|
analysis_info::in, analysis_info::out, io::di, io::uo) is det
|
|
<= compiler(Compiler).
|
|
|
|
% read_module_overall_status(Compiler, ModuleId, MaybeModuleStatus,
|
|
% !IO)
|
|
% Attempt to read the overall status from a module `.analysis' file.
|
|
%
|
|
:- pred read_module_overall_status(Compiler::in, module_id::in,
|
|
maybe(analysis_status)::out, io::di, io::uo) is det
|
|
<= compiler(Compiler).
|
|
|
|
:- pred enable_debug_messages(bool::in, io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- include_module analysis__file.
|
|
:- import_module analysis__file.
|
|
|
|
:- import_module bool.
|
|
:- import_module map.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
|
|
:- type analysis_info
|
|
---> some [Compiler] analysis_info(
|
|
compiler :: Compiler,
|
|
|
|
% Holds outstanding requests for more specialised
|
|
% variants of procedures. Requests are added to this
|
|
% map as analyses proceed and written out to disk
|
|
% at the end of the compilation of this module.
|
|
%
|
|
analysis_requests :: analysis_map(analysis_request),
|
|
|
|
% The overall status of each module.
|
|
%
|
|
module_statuses :: map(module_id, analysis_status),
|
|
|
|
% The "old" map stores analysis results read in from
|
|
% disk. New results generated while analysing the
|
|
% current module are added to the "new" map. After
|
|
% all the analyses the two maps are compared to
|
|
% see which analysis results have changed. Other
|
|
% modules may need to be marked or invalidated as a
|
|
% result. Then "new" results are moved into the "old"
|
|
% map, from where they can be written to disk.
|
|
%
|
|
old_analysis_results :: analysis_map(analysis_result),
|
|
new_analysis_results :: analysis_map(analysis_result),
|
|
|
|
% The extra info map stores any extra information
|
|
% needed by one or more analysis results.
|
|
%
|
|
old_extra_infos :: map(module_id, module_extra_info_map),
|
|
new_extra_infos :: map(module_id, module_extra_info_map),
|
|
|
|
% The Inter-module Dependency Graph records
|
|
% dependencies of an entire module's analysis results
|
|
% on another module's answer patterns. e.g. assume
|
|
% module M1 contains function F1 that has an analysis
|
|
% result that used the answer F2:CP2->AP2 from module
|
|
% M2. If AP2 changes then all of M1 will either be
|
|
% marked `suboptimal' or `invalid'. Finer-grained
|
|
% dependency tracking would allow only F1 to be
|
|
% recompiled, instead of all of M1, but we don't do
|
|
% that.
|
|
%
|
|
% IMDGs are loaded from disk into the old map.
|
|
% During analysis any dependences of the current module
|
|
% on other modules is added into the new map.
|
|
% At the end of analysis all the arcs which terminate
|
|
% at the current module are cleared from the old map
|
|
% and replaced by those in the new map.
|
|
%
|
|
% XXX: check if we really need two maps
|
|
%
|
|
old_imdg :: analysis_map(imdg_arc),
|
|
new_imdg :: analysis_map(imdg_arc)
|
|
) => compiler(Compiler).
|
|
|
|
% An analysis result is a call pattern paired with an answer.
|
|
% The result has a status associated with it.
|
|
%
|
|
:- type analysis_result
|
|
---> some [Call, Answer]
|
|
analysis_result(Call, Answer, analysis_status)
|
|
=> analysis(Call, Answer).
|
|
|
|
:- type analysis_request
|
|
---> some [Call]
|
|
analysis_request(Call)
|
|
=> call_pattern(Call).
|
|
|
|
:- type imdg_arc
|
|
---> some [Call]
|
|
imdg_arc(
|
|
Call, % Call pattern of the analysis result
|
|
% being depended on.
|
|
module_id % The module that _depends on_ this
|
|
% function's result.
|
|
) => call_pattern(Call).
|
|
|
|
:- type analysis_map(T) == map(module_id, module_analysis_map(T)).
|
|
:- type module_analysis_map(T) == map(analysis_name, func_analysis_map(T)).
|
|
:- type func_analysis_map(T) == map(func_id, list(T)).
|
|
|
|
:- type module_extra_info_map == map(extra_info_key, string).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% The "any" call pattern
|
|
%
|
|
|
|
:- instance call_pattern(any_call) where [].
|
|
:- instance partial_order(any_call) where [
|
|
more_precise_than(_, _) :- semidet_fail,
|
|
equivalent(_, _) :- semidet_succeed
|
|
].
|
|
:- instance to_string(any_call) where [
|
|
to_string(any_call) = "",
|
|
from_string("") = any_call
|
|
].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
init_analysis_info(Compiler) =
|
|
'new analysis_info'(Compiler, map__init, map__init, map__init, map__init,
|
|
map__init, map__init, map__init, map__init).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
lookup_results(ModuleId, FuncId, ResultList, !Info, !IO) :-
|
|
lookup_results(no, ModuleId, FuncId, ResultList, !Info, !IO).
|
|
|
|
:- pred lookup_results(bool::in, module_id::in, func_id::in,
|
|
list({Call, Answer, analysis_status})::out,
|
|
analysis_info::in, analysis_info::out, io::di, io::uo) is det
|
|
<= analysis(Call, Answer).
|
|
|
|
lookup_results(AllowInvalidModules, ModuleId, FuncId, ResultList,
|
|
!Info, !IO) :-
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.write_string("Looking up analysis results for ", !IO),
|
|
io.write_string(ModuleId, !IO),
|
|
io.write_string(".", !IO),
|
|
io.write_string(FuncId, !IO),
|
|
io.nl(!IO)
|
|
), !IO),
|
|
ensure_old_module_analysis_results_loaded(ModuleId, !Info, !IO),
|
|
(if
|
|
AllowInvalidModules = no,
|
|
!.Info ^ module_statuses ^ det_elem(ModuleId) = invalid
|
|
then
|
|
ResultList = []
|
|
else
|
|
lookup_results_2(!.Info ^ old_analysis_results,
|
|
ModuleId, FuncId, ResultList),
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.write_string("Found these results: ", !IO),
|
|
io.print(ResultList, !IO),
|
|
io.nl(!IO)
|
|
), !IO)
|
|
).
|
|
|
|
:- pred lookup_results_2(analysis_map(analysis_result)::in, module_id::in,
|
|
func_id::in, list({Call, Answer, analysis_status})::out) is det
|
|
<= analysis(Call, Answer).
|
|
|
|
lookup_results_2(Map, ModuleId, FuncId, ResultList) :-
|
|
AnalysisName = analysis_name(_ : Call, _ : Answer),
|
|
(if
|
|
ModuleResults = Map ^ elem(ModuleId),
|
|
Results = ModuleResults ^ elem(AnalysisName) ^ elem(FuncId)
|
|
then
|
|
% XXX we might have to discard results which are
|
|
% `invalid' or `fixpoint_invalid' if they are written at all
|
|
ResultList = list.map(
|
|
(func(Result) = {Call, Answer, Status} :-
|
|
Result = analysis_result(Call0, Answer0, Status),
|
|
det_univ_to_type(univ(Call0), Call),
|
|
det_univ_to_type(univ(Answer0), Answer)
|
|
), Results)
|
|
else
|
|
ResultList = []
|
|
).
|
|
|
|
lookup_matching_results(ModuleId, FuncId, Call, ResultList, !Info, !IO) :-
|
|
lookup_results(ModuleId, FuncId, AllResultsList, !Info, !IO),
|
|
ResultList = list.filter(
|
|
(pred(({ResultCall, _, _})::in) is semidet :-
|
|
( more_precise_than(Call, ResultCall)
|
|
; equivalent(Call, ResultCall)
|
|
)
|
|
), AllResultsList).
|
|
|
|
lookup_best_result(ModuleId, FuncId, Call, MaybeBestResult, !Info, !IO) :-
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.write_string("Looking up best analysis result for ", !IO),
|
|
io.write_string(ModuleId, !IO),
|
|
io.write_string(".", !IO),
|
|
io.write_string(FuncId, !IO),
|
|
io.nl(!IO)
|
|
), !IO),
|
|
lookup_matching_results(ModuleId, FuncId, Call, MatchingResults,
|
|
!Info, !IO),
|
|
(
|
|
MatchingResults = [],
|
|
MaybeBestResult = no
|
|
;
|
|
MatchingResults = [_ | _],
|
|
MaybeBestResult = yes(BestResult),
|
|
most_precise_answer(MatchingResults, BestResult)
|
|
).
|
|
|
|
:- pred most_precise_answer(
|
|
list({Call, Answer, analysis_status})::in(non_empty_list),
|
|
{Call, Answer, analysis_status}::out) is det
|
|
<= analysis(Call, Answer).
|
|
|
|
most_precise_answer([Result | Results], BestResult) :-
|
|
list.foldl(more_precise_answer, Results, Result, BestResult).
|
|
|
|
:- pred more_precise_answer({Call, Answer, analysis_status}::in,
|
|
{Call, Answer, analysis_status}::in,
|
|
{Call, Answer, analysis_status}::out) is det
|
|
<= analysis(Call, Answer).
|
|
|
|
more_precise_answer(Result, Best0, Best) :-
|
|
Result = {_, ResultAnswer, _},
|
|
Best0 = {_, BestAnswer0, _},
|
|
(if more_precise_than(ResultAnswer, BestAnswer0) then
|
|
Best = Result
|
|
else
|
|
Best = Best0
|
|
).
|
|
|
|
:- pred lookup_exactly_matching_result_even_from_invalid_modules(module_id::in,
|
|
func_id::in, Call::in, maybe({Call, Answer, analysis_status})::out,
|
|
analysis_info::in, analysis_info::out, io::di, io::uo) is det
|
|
<= analysis(Call, Answer).
|
|
|
|
lookup_exactly_matching_result_even_from_invalid_modules(ModuleId,
|
|
FuncId, Call, MaybeResult, !Info, !IO) :-
|
|
lookup_results(yes, ModuleId, FuncId, AllResultsList, !Info, !IO),
|
|
ResultList = list.filter(
|
|
(pred(({ResultCall, _, _})::in) is semidet :-
|
|
equivalent(Call, ResultCall)
|
|
), AllResultsList),
|
|
(
|
|
ResultList = [],
|
|
MaybeResult = no
|
|
;
|
|
ResultList = [Result],
|
|
MaybeResult = yes(Result)
|
|
;
|
|
ResultList = [_, _ | _],
|
|
error("lookup_exactly_matching_result: zero or one " ++
|
|
"exactly matching results expected")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
record_result(ModuleId, FuncId, CallPattern, AnswerPattern, Status, !Info) :-
|
|
Map0 = !.Info ^ new_analysis_results,
|
|
record_result_in_analysis_map(ModuleId, FuncId,
|
|
CallPattern, AnswerPattern, Status, Map0, Map),
|
|
!:Info = !.Info ^ new_analysis_results := Map.
|
|
|
|
:- pred record_result_in_analysis_map(module_id::in, func_id::in,
|
|
Call::in, Answer::in, analysis_status::in,
|
|
analysis_map(analysis_result)::in,
|
|
analysis_map(analysis_result)::out) is det
|
|
<= analysis(Call, Answer).
|
|
|
|
record_result_in_analysis_map(ModuleId, FuncId,
|
|
CallPattern, AnswerPattern, Status, !Map) :-
|
|
( ModuleResults0 = map.search(!.Map, ModuleId) ->
|
|
ModuleResults1 = ModuleResults0
|
|
;
|
|
ModuleResults1 = map.init
|
|
),
|
|
AnalysisName = analysis_name(CallPattern, AnswerPattern),
|
|
( AnalysisResults0 = map.search(ModuleResults1, AnalysisName) ->
|
|
AnalysisResults1 = AnalysisResults0
|
|
;
|
|
AnalysisResults1 = map.init
|
|
),
|
|
( FuncResults0 = map.search(AnalysisResults1, FuncId) ->
|
|
FuncResults1 = FuncResults0
|
|
;
|
|
FuncResults1 = []
|
|
),
|
|
!:Map = map.set(!.Map, ModuleId,
|
|
map.set(ModuleResults1, AnalysisName,
|
|
map.set(AnalysisResults1, FuncId,
|
|
FuncResults))),
|
|
FuncResults = [Result | FuncResults1],
|
|
Result = 'new analysis_result'(CallPattern, AnswerPattern, Status).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
lookup_requests(AnalysisName, ModuleId, FuncId, CallPatterns, !Info, !IO) :-
|
|
( map__search(!.Info ^ analysis_requests, ModuleId, ModuleRequests0) ->
|
|
ModuleRequests = ModuleRequests0
|
|
;
|
|
read_module_analysis_requests(!.Info, ModuleId, ModuleRequests, !IO),
|
|
!:Info = !.Info ^ analysis_requests ^ elem(ModuleId) := ModuleRequests
|
|
),
|
|
( CallPatterns0 = ModuleRequests ^ elem(AnalysisName) ^ elem(FuncId) ->
|
|
CallPatterns = list__filter_map(
|
|
(func(analysis_request(Call0)) = Call is semidet :-
|
|
univ(Call) = univ(Call0)),
|
|
CallPatterns0)
|
|
;
|
|
CallPatterns = []
|
|
).
|
|
|
|
record_request(AnalysisName, ModuleId, FuncId, CallPattern, !Info) :-
|
|
( ModuleResults0 = map.search(!.Info ^ analysis_requests, ModuleId) ->
|
|
ModuleResults1 = ModuleResults0
|
|
;
|
|
ModuleResults1 = map.init
|
|
),
|
|
( AnalysisResults0 = map.search(ModuleResults1, AnalysisName) ->
|
|
AnalysisResults1 = AnalysisResults0
|
|
;
|
|
AnalysisResults1 = map.init
|
|
),
|
|
( FuncResults0 = map.search(AnalysisResults1, FuncId) ->
|
|
FuncResults1 = FuncResults0
|
|
;
|
|
FuncResults1 = []
|
|
),
|
|
!:Info = !.Info ^ analysis_requests :=
|
|
map.set(!.Info ^ analysis_requests, ModuleId,
|
|
map.set(ModuleResults1, AnalysisName,
|
|
map.set(AnalysisResults1, FuncId,
|
|
['new analysis_request'(CallPattern) | FuncResults1]))).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
record_dependency(CallerModuleId, AnalysisName, CalleeModuleId, FuncId, Call,
|
|
!Info) :-
|
|
(if CallerModuleId = CalleeModuleId then
|
|
% XXX this assertion breaks compiling the standard library with
|
|
% --analyse-trail-usage at the moment
|
|
%
|
|
% error("record_dependency: " ++ CalleeModuleId ++ " and " ++
|
|
% CallerModuleId ++ " must be different")
|
|
true
|
|
else
|
|
( Analyses0 = map.search(!.Info ^ new_imdg, CalleeModuleId) ->
|
|
Analyses1 = Analyses0
|
|
;
|
|
Analyses1 = map.init
|
|
),
|
|
( Funcs0 = map.search(Analyses1, AnalysisName) ->
|
|
Funcs1 = Funcs0
|
|
;
|
|
Funcs1 = map.init
|
|
),
|
|
( FuncArcs0 = map.search(Funcs1, FuncId) ->
|
|
FuncArcs1 = FuncArcs0
|
|
;
|
|
FuncArcs1 = []
|
|
),
|
|
Dep = 'new imdg_arc'(Call, CallerModuleId),
|
|
% XXX this should really be a set to begin with
|
|
( Dep `list.member` FuncArcs1 ->
|
|
true
|
|
;
|
|
!:Info = !.Info ^ new_imdg :=
|
|
map.set(!.Info ^ new_imdg, CalleeModuleId,
|
|
map.set(Analyses1, AnalysisName,
|
|
map.set(Funcs1, FuncId, FuncArcs))),
|
|
FuncArcs = [Dep | FuncArcs1]
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
lookup_module_extra_info(ModuleId, Key, MaybeExtraInfo, !Info, !IO) :-
|
|
ensure_old_module_analysis_results_loaded(ModuleId, !Info, !IO),
|
|
ModuleExtraInfos = !.Info ^ old_extra_infos ^ det_elem(ModuleId),
|
|
(
|
|
String = ModuleExtraInfos ^ elem(Key),
|
|
ExtraInfo = from_string(String)
|
|
->
|
|
MaybeExtraInfo = yes(ExtraInfo)
|
|
;
|
|
MaybeExtraInfo = no
|
|
).
|
|
|
|
record_module_extra_info(ModuleId, Key, ExtraInfo, !Info) :-
|
|
( ModuleMap0 = !.Info ^ new_extra_infos ^ elem(ModuleId) ->
|
|
ModuleMap1 = ModuleMap0
|
|
;
|
|
ModuleMap1 = map.init
|
|
),
|
|
ModuleMap = map.set(ModuleMap1, Key, to_string(ExtraInfo)),
|
|
!:Info = !.Info ^ new_extra_infos ^ elem(ModuleId) := ModuleMap.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The algorithm is from Nick's thesis, pp. 108-9.
|
|
% Or my corruption thereof.
|
|
% See the `analysis/README' file for a reference.
|
|
%
|
|
% For each new analysis result (P^M:DP --> Ans_new):
|
|
% Read in the registry of M if necessary
|
|
% If there is an existing analysis result (P^M:DP --> Ans_old):
|
|
% if Ans_new \= Ans_old:
|
|
% Replace the entry in the registry with P^M:DP --> Ans_new
|
|
% if Ans_new `more_precise_than` Ans_old
|
|
% Status = suboptimal
|
|
% else
|
|
% Status = invalid
|
|
% For each entry (Q^N:DQ --> P^M:DP) in the IMDG:
|
|
% % Mark Q^N:DQ --> _ (_) with Status
|
|
% Actually, we don't do that. We only mark the
|
|
% module N's _overall_ status with the
|
|
% least upper bound of its old status and Status.
|
|
% Else (P:DP --> Ans_old) did not exist:
|
|
% Insert result (P:DP --> Ans_new) into the registry.
|
|
%
|
|
% Finally, clear out the "new" analysis results map. When we write
|
|
% out the analysis files we will do it from the "old" results map.
|
|
%
|
|
:- pred update_analysis_registry(analysis_info::in, analysis_info::out,
|
|
io::di, io::uo) is det.
|
|
:- pred update_analysis_registry_2(module_id::in,
|
|
module_analysis_map(analysis_result)::in,
|
|
analysis_info::in, analysis_info::out, io::di, io::uo) is det.
|
|
:- pred update_analysis_registry_3(module_id::in, analysis_name::in,
|
|
func_analysis_map(analysis_result)::in,
|
|
analysis_info::in, analysis_info::out, io::di, io::uo) is det.
|
|
:- pred update_analysis_registry_4(module_id::in, analysis_name::in,
|
|
func_id::in, list(analysis_result)::in,
|
|
analysis_info::in, analysis_info::out, io::di, io::uo) is det.
|
|
:- pred update_analysis_registry_5(module_id::in, analysis_name::in,
|
|
func_id::in, analysis_result::in,
|
|
analysis_info::in, analysis_info::out, io::di, io::uo) is det.
|
|
|
|
update_analysis_registry(!Info, !IO) :-
|
|
debug_msg(io.print("Updating analysis registry.\n"), !IO),
|
|
map.foldl2(update_analysis_registry_2, !.Info ^ new_analysis_results,
|
|
!Info, !IO),
|
|
!:Info = !.Info ^ new_analysis_results := map.init.
|
|
|
|
update_analysis_registry_2(ModuleId, ModuleMap, !Info, !IO) :-
|
|
ensure_old_module_analysis_results_loaded(ModuleId, !Info, !IO),
|
|
ensure_old_imdg_loaded(ModuleId, !Info, !IO),
|
|
map.foldl2(update_analysis_registry_3(ModuleId), ModuleMap, !Info, !IO).
|
|
|
|
update_analysis_registry_3(ModuleId, AnalysisName, FuncMap, !Info, !IO) :-
|
|
map.foldl2(update_analysis_registry_4(ModuleId, AnalysisName),
|
|
FuncMap, !Info, !IO).
|
|
|
|
update_analysis_registry_4(ModuleId, AnalysisName, FuncId, NewResults,
|
|
!Info, !IO) :-
|
|
% XXX Currently we do not prevent there being more than one recorded result
|
|
% for a given call pattern.
|
|
list.foldl2(update_analysis_registry_5(ModuleId, AnalysisName, FuncId),
|
|
NewResults, !Info, !IO).
|
|
|
|
update_analysis_registry_5(ModuleId, AnalysisName, FuncId, NewResult,
|
|
!Info, !IO) :-
|
|
NewResult = analysis_result(Call, NewAnswer, NewStatus),
|
|
lookup_exactly_matching_result_even_from_invalid_modules(ModuleId, FuncId,
|
|
Call, MaybeResult, !Info, !IO),
|
|
(
|
|
% There was a previous answer for this call pattern.
|
|
%
|
|
MaybeResult = yes({_OldCall, OldAnswer, OldStatus}),
|
|
(if equivalent(NewAnswer, OldAnswer) then
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.print("No change in the result ", !IO),
|
|
io.print(ModuleId, !IO),
|
|
io.print(".", !IO),
|
|
io.print(FuncId, !IO),
|
|
io.print(":", !IO),
|
|
io.print(Call, !IO),
|
|
io.print(" --> ", !IO),
|
|
io.print(NewAnswer, !IO),
|
|
io.nl(!IO)
|
|
), !IO),
|
|
|
|
(if NewStatus \= OldStatus then
|
|
OldMap0 = !.Info ^ old_analysis_results,
|
|
replace_result_in_analysis_map(ModuleId, FuncId,
|
|
Call, NewAnswer, NewStatus, OldMap0, OldMap),
|
|
!:Info = !.Info ^ old_analysis_results := OldMap
|
|
else
|
|
true
|
|
)
|
|
else
|
|
% Answer has changed.
|
|
% Replace the old answer in the registry with the new answer.
|
|
%
|
|
OldMap0 = !.Info ^ old_analysis_results,
|
|
replace_result_in_analysis_map(ModuleId, FuncId,
|
|
Call, NewAnswer, NewStatus, OldMap0, OldMap),
|
|
!:Info = !.Info ^ old_analysis_results := OldMap,
|
|
|
|
% If the answer is more precise than before then dependent
|
|
% modules should be marked suboptimal. Otherwise the answer
|
|
% is less precise than it was before, so dependent modules
|
|
% should be invalidated.
|
|
%
|
|
(if NewAnswer `more_precise_than` OldAnswer then
|
|
Status = suboptimal
|
|
else
|
|
Status = invalid
|
|
),
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.print(OldAnswer, !IO),
|
|
io.print(" changed to ", !IO),
|
|
io.print(NewAnswer, !IO),
|
|
io.nl(!IO),
|
|
io.print("Mark dependent modules as ", !IO),
|
|
io.print(Status, !IO),
|
|
io.nl(!IO),
|
|
io.print("The modules to mark are: ", !IO),
|
|
io.print(DepModules, !IO),
|
|
io.nl(!IO)
|
|
), !IO),
|
|
DepModules = imdg_dependent_modules(
|
|
!.Info ^ old_imdg ^ det_elem(ModuleId), AnalysisName,
|
|
FuncId, Call),
|
|
set.fold2(taint_module_overall_status(Status), DepModules,
|
|
!Info, !IO)
|
|
)
|
|
;
|
|
% There was no previous answer for this call pattern.
|
|
% Just add this result to the registry.
|
|
%
|
|
MaybeResult = no,
|
|
OldMap0 = !.Info ^ old_analysis_results,
|
|
record_result_in_analysis_map(ModuleId, FuncId,
|
|
Call, NewAnswer, NewStatus, OldMap0, OldMap),
|
|
!:Info = !.Info ^ old_analysis_results := OldMap
|
|
).
|
|
|
|
% replace_result_in_analysis_map(ModuleId, FuncId,
|
|
% Call, Answer, Status, !Map)
|
|
%
|
|
% Replace an analysis result for the given function/call pattern
|
|
% with a new result. A previous result _must_ already exist
|
|
% in the map with exactly the same call pattern.
|
|
%
|
|
:- pred replace_result_in_analysis_map(module_id::in, func_id::in,
|
|
Call::in, Answer::in, analysis_status::in,
|
|
analysis_map(analysis_result)::in,
|
|
analysis_map(analysis_result)::out) is det
|
|
<= analysis(Call, Answer).
|
|
|
|
replace_result_in_analysis_map(ModuleId, FuncId,
|
|
CallPattern, AnswerPattern, Status, Map0, Map) :-
|
|
AnalysisName = analysis_name(CallPattern, AnswerPattern),
|
|
ModuleResults0 = map.lookup(Map0, ModuleId),
|
|
AnalysisResults0 = map.lookup(ModuleResults0, AnalysisName),
|
|
FuncResults0 = map.lookup(AnalysisResults0, FuncId),
|
|
replace_result_in_list(CallPattern, AnswerPattern, Status,
|
|
FuncResults0, FuncResults),
|
|
Map = map.det_update(Map0, ModuleId,
|
|
map.det_update(ModuleResults0, AnalysisName,
|
|
map.det_update(AnalysisResults0, FuncId, FuncResults))).
|
|
|
|
:- pred replace_result_in_list(Call::in, Answer::in, analysis_status::in,
|
|
list(analysis_result)::in, list(analysis_result)::out)
|
|
is det <= analysis(Call, Answer).
|
|
|
|
replace_result_in_list(_Call, _Answer, _Status, [], _) :-
|
|
error("replace_result_in_list/5: found no result to replace").
|
|
replace_result_in_list(Call, Answer, Status, [H0 | T0], [H | T]) :-
|
|
H0 = analysis_result(HCall0, _, _),
|
|
det_univ_to_type(univ(HCall0), HCall),
|
|
(if equivalent(Call, HCall) then
|
|
H = 'new analysis_result'(Call, Answer, Status),
|
|
T = T0
|
|
else
|
|
H = H0,
|
|
replace_result_in_list(Call, Answer, Status, T0, T)
|
|
).
|
|
|
|
:- func imdg_dependent_modules(module_analysis_map(imdg_arc), analysis_name,
|
|
func_id, Call) = set(module_id) <= call_pattern(Call).
|
|
|
|
imdg_dependent_modules(ModuleMap, AnalysisName, FuncId, Call) =
|
|
(if map.search(ModuleMap, AnalysisName, FuncAnalysisMap),
|
|
map.search(FuncAnalysisMap, FuncId, IMDGEntries)
|
|
then
|
|
set.from_list(list.filter_map(arc_module_id(Call), IMDGEntries))
|
|
else
|
|
set.init
|
|
).
|
|
|
|
% XXX: compiler aborts if the modes are removed
|
|
:- func arc_module_id(Call::in, imdg_arc::in) = (module_id::out) is semidet
|
|
<= call_pattern(Call).
|
|
|
|
arc_module_id(CallA, imdg_arc(CallB0, ModuleId)) = ModuleId :-
|
|
det_univ_to_type(univ(CallB0), CallB),
|
|
equivalent(CallA, CallB).
|
|
|
|
:- pred taint_module_overall_status(analysis_status::in,
|
|
module_id::in, analysis_info::in, analysis_info::out,
|
|
io::di, io::uo) is det.
|
|
|
|
taint_module_overall_status(Status, ModuleId, !Info, !IO) :-
|
|
(if Status = optimal then
|
|
true
|
|
else
|
|
ensure_old_module_analysis_results_loaded(ModuleId, !Info, !IO),
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.print("Tainting the overall module status of ", !IO),
|
|
io.print(ModuleId, !IO),
|
|
io.print(" with ", !IO),
|
|
io.print(ModuleStatus, !IO),
|
|
io.nl(!IO)
|
|
), !IO),
|
|
ModuleStatus0 = !.Info ^ module_statuses ^ det_elem(ModuleId),
|
|
ModuleStatus = lub(ModuleStatus0, Status),
|
|
!:Info = !.Info ^ module_statuses ^ elem(ModuleId) := ModuleStatus
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred update_extra_infos(analysis_info::in, analysis_info::out) is det.
|
|
|
|
update_extra_infos(Info0, Info) :-
|
|
map.foldl(update_extra_infos_2,
|
|
Info0 ^ new_extra_infos,
|
|
Info0 ^ old_extra_infos, ExtraInfos),
|
|
Info = (Info0
|
|
^ old_extra_infos := ExtraInfos)
|
|
^ new_extra_infos := map.init.
|
|
|
|
:- pred update_extra_infos_2(module_id::in, module_extra_info_map::in,
|
|
map(module_id, module_extra_info_map)::in,
|
|
map(module_id, module_extra_info_map)::out) is det.
|
|
|
|
update_extra_infos_2(ModuleId, ExtraInfoB, ModuleMap0, ModuleMap) :-
|
|
( ExtraInfoA = ModuleMap0 ^ elem(ModuleId) ->
|
|
map.overlay(ExtraInfoA, ExtraInfoB, ExtraInfo),
|
|
ModuleMap = ModuleMap0 ^ elem(ModuleId) := ExtraInfo
|
|
;
|
|
ModuleMap = ModuleMap0 ^ elem(ModuleId) := ExtraInfoB
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% For each module N imported by M:
|
|
% Delete all entries leading to module M from N's IMDG:
|
|
% For each P^M:DP in S (call patterns to analyse):
|
|
% add P^M:DP --> Q^N:DQ to N's IMDG
|
|
%
|
|
:- pred update_intermodule_dependencies(module_id::in, set(module_id)::in,
|
|
analysis_info::in, analysis_info::out, io::di, io::uo) is det.
|
|
|
|
update_intermodule_dependencies(ModuleId, ImportedModules, !Info, !IO) :-
|
|
set.fold2(update_intermodule_dependencies_2(ModuleId),
|
|
ImportedModules, !Info, !IO).
|
|
|
|
:- pred update_intermodule_dependencies_2(module_id::in, module_id::in,
|
|
analysis_info::in, analysis_info::out, io::di, io::uo) is det.
|
|
|
|
update_intermodule_dependencies_2(ModuleId, ImportedModuleId, !Info, !IO) :-
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.print("Clearing entries involving ", !IO),
|
|
io.print(ModuleId, !IO),
|
|
io.print(" from ", !IO),
|
|
io.print(ImportedModuleId, !IO),
|
|
io.print("'s IMDG.\n", !IO)
|
|
), !IO),
|
|
ensure_old_imdg_loaded(ImportedModuleId, !Info, !IO),
|
|
IMDG0 = !.Info ^ old_imdg ^ det_elem(ImportedModuleId),
|
|
clear_imdg_entries_pointing_at(ModuleId, IMDG0, IMDG1),
|
|
|
|
(if NewArcs = !.Info ^ new_imdg ^ elem(ImportedModuleId) then
|
|
map.union(combine_func_imdg, IMDG1, NewArcs, IMDG)
|
|
else
|
|
IMDG = IMDG1
|
|
),
|
|
!:Info = !.Info ^ old_imdg ^ elem(ImportedModuleId) := IMDG,
|
|
!:Info = !.Info ^ new_imdg :=
|
|
map.delete(!.Info ^ new_imdg, ImportedModuleId).
|
|
|
|
:- pred clear_imdg_entries_pointing_at(module_id::in,
|
|
module_analysis_map(imdg_arc)::in,
|
|
module_analysis_map(imdg_arc)::out) is det.
|
|
:- pred clear_imdg_entries_pointing_at_2(module_id::in, analysis_name::in,
|
|
func_analysis_map(imdg_arc)::in,
|
|
func_analysis_map(imdg_arc)::out) is det.
|
|
:- pred clear_imdg_entries_pointing_at_3(module_id::in, func_id::in,
|
|
list(imdg_arc)::in, list(imdg_arc)::out) is det.
|
|
|
|
clear_imdg_entries_pointing_at(ModuleId, Map0, Map) :-
|
|
map.map_values(clear_imdg_entries_pointing_at_2(ModuleId), Map0, Map).
|
|
clear_imdg_entries_pointing_at_2(ModuleId, _, FuncMap0, FuncMap) :-
|
|
map.map_values(clear_imdg_entries_pointing_at_3(ModuleId),
|
|
FuncMap0, FuncMap).
|
|
clear_imdg_entries_pointing_at_3(ModuleId, _, Arcs0, Arcs) :-
|
|
list.filter((pred(imdg_arc(_, ModId)::in) is semidet :- ModuleId \= ModId),
|
|
Arcs0, Arcs).
|
|
|
|
:- pred combine_func_imdg(func_analysis_map(imdg_arc)::in,
|
|
func_analysis_map(imdg_arc)::in, func_analysis_map(imdg_arc)::out)
|
|
is det.
|
|
|
|
combine_func_imdg(FuncImdgA, FuncImdgB, FuncImdg) :-
|
|
map.union(combine_imdg_lists, FuncImdgA, FuncImdgB, FuncImdg).
|
|
|
|
:- pred combine_imdg_lists(list(imdg_arc)::in, list(imdg_arc)::in,
|
|
list(imdg_arc)::out) is det.
|
|
|
|
combine_imdg_lists(ArcsA, ArcsB, ArcsA ++ ArcsB).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred ensure_old_module_analysis_results_loaded(module_id::in,
|
|
analysis_info::in, analysis_info::out, io::di, io::uo) is det.
|
|
|
|
ensure_old_module_analysis_results_loaded(ModuleId, !Info, !IO) :-
|
|
(if map.search(!.Info ^ old_analysis_results, ModuleId, _Results) then
|
|
% sanity check
|
|
map.lookup(!.Info ^ module_statuses, ModuleId, _StatusMustExist)
|
|
else
|
|
read_module_analysis_results(!.Info, ModuleId,
|
|
ModuleStatus, ModuleResults, ExtraInfos, !IO),
|
|
!:Info = ((!.Info
|
|
^ module_statuses ^ elem(ModuleId) := ModuleStatus)
|
|
^ old_analysis_results ^ elem(ModuleId) := ModuleResults)
|
|
^ old_extra_infos ^ elem(ModuleId) := ExtraInfos
|
|
).
|
|
|
|
:- pred ensure_old_imdg_loaded(module_id::in, analysis_info::in,
|
|
analysis_info::out, io::di, io::uo) is det.
|
|
|
|
ensure_old_imdg_loaded(ModuleId, !Info, !IO) :-
|
|
Map0 = !.Info ^ old_imdg,
|
|
(if map.search(Map0, ModuleId, _) then
|
|
% already loaded
|
|
true
|
|
else
|
|
read_module_imdg(!.Info, ModuleId, IMDG, !IO),
|
|
map.det_insert(Map0, ModuleId, IMDG, Map),
|
|
!:Info = !.Info ^ old_imdg := Map
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% In this procedure we have just finished compiling module ModuleId
|
|
% and will write out data currently cached in the analysis_info
|
|
% structure out to disk.
|
|
%
|
|
write_analysis_files(Compiler, ModuleId, ImportedModuleIds, !Info, !IO) :-
|
|
% The current module was just compiled so we set its status to the
|
|
% lub of all the new analysis results generated.
|
|
(if NewResults = !.Info ^ new_analysis_results ^ elem(ModuleId) then
|
|
ModuleStatus = lub_result_statuses(NewResults)
|
|
else
|
|
ModuleStatus = optimal,
|
|
% Force an `.analysis' file to be written out for this module,
|
|
% even though there are no results recorded for it.
|
|
!:Info = !.Info ^ new_analysis_results ^ elem(ModuleId) := map.init
|
|
),
|
|
|
|
update_analysis_registry(!Info, !IO),
|
|
update_extra_infos(!Info),
|
|
|
|
!:Info = !.Info ^ module_statuses ^ elem(ModuleId) := ModuleStatus,
|
|
|
|
update_intermodule_dependencies(ModuleId, ImportedModuleIds,
|
|
!Info, !IO),
|
|
(if map.is_empty(!.Info ^ new_analysis_results),
|
|
map.is_empty(!.Info ^ new_extra_infos)
|
|
then
|
|
true
|
|
else
|
|
io.print("Warning: new_analysis_results or extra_infos is not empty\n",
|
|
!IO),
|
|
io.print(!.Info ^ new_analysis_results, !IO),
|
|
io.nl(!IO),
|
|
io.print(!.Info ^ new_extra_infos, !IO),
|
|
io.nl(!IO)
|
|
),
|
|
|
|
% Write the results for all the modules we know of. For the
|
|
% module being compiled, the analysis results may have changed.
|
|
% For other modules, their overall statuses may have changed.
|
|
write_local_modules(!.Info, write_module_analysis_results,
|
|
!.Info ^ old_analysis_results, !IO),
|
|
|
|
% Write the requests for the imported modules.
|
|
write_local_modules(!.Info, write_module_analysis_requests,
|
|
!.Info ^ analysis_requests, !IO),
|
|
|
|
% Remove the requests for the current module since we (should have)
|
|
% fulfilled them in this pass.
|
|
empty_request_file(!.Info, ModuleId, !IO),
|
|
|
|
% Write the intermodule dependency graphs.
|
|
write_local_modules(!.Info, write_module_imdg,
|
|
!.Info ^ old_imdg, !IO),
|
|
|
|
% Touch a timestamp file to indicate the last time that this module was
|
|
% analysed.
|
|
module_id_to_write_file_name(Compiler, ModuleId, ".analysis_date",
|
|
TimestampFileName, !IO),
|
|
io.open_output(TimestampFileName, Result, !IO),
|
|
(
|
|
Result = ok(OutputStream),
|
|
io.write_string(OutputStream, "\n", !IO),
|
|
io.close_output(OutputStream, !IO)
|
|
;
|
|
Result = error(IOError),
|
|
error(io.error_message(IOError))
|
|
).
|
|
|
|
:- type write_module_analysis_map(T) ==
|
|
(pred(analysis_info, module_id, module_analysis_map(T), io, io)).
|
|
:- mode write_module_analysis_map == in(pred(in, in, in, di, uo) is det).
|
|
|
|
:- pred write_local_modules(analysis_info::in,
|
|
write_module_analysis_map(T)::write_module_analysis_map,
|
|
analysis_map(T)::in, io::di, io::uo) is det.
|
|
:- pred write_local_modules_2(analysis_info::in,
|
|
write_module_analysis_map(T)::write_module_analysis_map,
|
|
module_id::in, module_analysis_map(T)::in, io::di, io::uo) is det.
|
|
|
|
write_local_modules(Info, Write, AnalysisMap, !IO) :-
|
|
map.foldl(write_local_modules_2(Info, Write), AnalysisMap, !IO).
|
|
|
|
write_local_modules_2(Info, Write, ModuleId, ModuleResults, !IO) :-
|
|
module_is_local(Info ^ compiler, ModuleId, IsLocal, !IO),
|
|
(
|
|
IsLocal = yes,
|
|
Write(Info, ModuleId, ModuleResults, !IO)
|
|
;
|
|
IsLocal = no,
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.write_string("Not writing file for non-local module ", !IO),
|
|
io.write_string(ModuleId, !IO),
|
|
io.nl(!IO)
|
|
), !IO)
|
|
).
|
|
|
|
:- pred write_module_analysis_results(analysis_info::in, module_id::in,
|
|
module_analysis_map(analysis_result)::in, io::di, io::uo) is det.
|
|
|
|
write_module_analysis_results(Info, ModuleId, ModuleResults, !IO) :-
|
|
ModuleStatus = Info ^ module_statuses ^ det_elem(ModuleId),
|
|
( ModuleExtraInfo0 = Info ^ old_extra_infos ^ elem(ModuleId) ->
|
|
ModuleExtraInfo = ModuleExtraInfo0
|
|
;
|
|
ModuleExtraInfo = map.init
|
|
),
|
|
analysis.file.write_module_analysis_results(Info, ModuleId,
|
|
ModuleStatus, ModuleResults, ModuleExtraInfo, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
read_module_overall_status(Compiler, ModuleId, MaybeModuleStatus, !IO) :-
|
|
analysis.file.read_module_overall_status(Compiler, ModuleId,
|
|
MaybeModuleStatus, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
lub(StatusA, StatusB) = Status :-
|
|
compare(Cmp, StatusA, StatusB),
|
|
(
|
|
Cmp = (=),
|
|
Status = StatusA
|
|
;
|
|
Cmp = (<),
|
|
Status = StatusA
|
|
;
|
|
Cmp = (>),
|
|
Status = StatusB
|
|
).
|
|
|
|
:- func lub_result_statuses(module_analysis_map(analysis_result))
|
|
= analysis_status.
|
|
:- func lub_result_statuses_2(analysis_name,
|
|
func_analysis_map(analysis_result), analysis_status) = analysis_status.
|
|
:- func lub_result_statuses_3(func_id, list(analysis_result), analysis_status)
|
|
= analysis_status.
|
|
:- func lub_result_statuses_4(analysis_result, analysis_status)
|
|
= analysis_status.
|
|
|
|
lub_result_statuses(ModuleMap) =
|
|
map.foldl(lub_result_statuses_2, ModuleMap, optimal).
|
|
lub_result_statuses_2(_AnalysisName, FuncMap, Acc) =
|
|
map.foldl(lub_result_statuses_3, FuncMap, Acc).
|
|
lub_result_statuses_3(_FuncId, Results, Acc) =
|
|
list.foldl(lub_result_statuses_4, Results, Acc).
|
|
lub_result_statuses_4(analysis_result(_, _, Status), Acc) =
|
|
lub(Status, Acc).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- mutable(debug_analysis, bool, no, ground, [untrailed, attach_to_io_state]).
|
|
|
|
enable_debug_messages(Debug, !IO) :-
|
|
set_debug_analysis(Debug, !IO).
|
|
|
|
:- pred debug_msg(pred(io, io)::in(pred(di, uo) is det), io::di, io::uo)
|
|
is det.
|
|
|
|
debug_msg(P, !IO) :-
|
|
get_debug_analysis(Debug, !IO),
|
|
(
|
|
Debug = yes,
|
|
P(!IO)
|
|
;
|
|
Debug = no
|
|
).
|