mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
226 lines
7.2 KiB
Mathematica
226 lines
7.2 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2003-2004, 2006-2011 The University of Melbourne.
|
|
% Copyright (C) 2024 The Mercury team.
|
|
% 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://njn.valgrind.org/pubs/masters2001.ps>.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module analysis.framework.
|
|
:- interface.
|
|
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.file_names.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module term.
|
|
:- import_module unit.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% The intention is that eventually any compiler can use this library
|
|
% via .NET by defining an instance of this type class.
|
|
:- typeclass compiler(Compiler) where [
|
|
func compiler_name(Compiler) = string,
|
|
|
|
% Describe the analyses which can be performed by a compiler.
|
|
%
|
|
pred analyses(Compiler, analysis_name, analysis_type),
|
|
mode analyses(in, in, out) is semidet,
|
|
mode analyses(in, out, out) is multi,
|
|
|
|
% module_name_to_read_file_name(Compiler, Globals, Ext,
|
|
% ModuleName, MaybeFileName, !IO)
|
|
%
|
|
pred module_name_to_read_file_name(Compiler::in, globals::in,
|
|
ext::in(ext_analysis), module_name::in, maybe_error(string)::out,
|
|
io::di, io::uo) is det,
|
|
|
|
% module_name_to_write_file_name(Compiler, Globals, Ext,
|
|
% ModuleName, FileName, !IO)
|
|
%
|
|
pred module_name_to_write_file_name(Compiler::in, globals::in,
|
|
ext::in(ext_analysis), module_name::in, string::out,
|
|
io::di, io::uo) is det
|
|
].
|
|
|
|
:- type analysis_name == string.
|
|
|
|
:- type analysis_type
|
|
---> some [FuncInfo, Call, Answer]
|
|
analysis_type(
|
|
unit(Call),
|
|
unit(Answer)
|
|
) => analysis(FuncInfo, 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(FuncInfo, Call, Answer)
|
|
<= (call_pattern(FuncInfo, Call),
|
|
answer_pattern(FuncInfo, 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,
|
|
|
|
func bottom(FuncInfo::in, Call::unused) = (Answer::out) is det,
|
|
func top(FuncInfo::in, Call::unused) = (Answer::out) is det,
|
|
|
|
pred get_func_info(module_info::in, module_name::in, func_id::in,
|
|
Call::unused, Answer::unused, FuncInfo::out) is det
|
|
].
|
|
|
|
:- type fixpoint_type
|
|
---> least_fixpoint
|
|
% Start at `bottom'.
|
|
% Must run to completion.
|
|
|
|
; greatest_fixpoint.
|
|
% Start at `top'.
|
|
% Can stop at any time.
|
|
|
|
% 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
|
|
---> func_id(
|
|
fid_pf :: pred_or_func,
|
|
fid_name :: string,
|
|
fid_arity :: pred_form_arity,
|
|
fid_mode :: proc_id
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- typeclass call_pattern(FuncInfo, Call)
|
|
<= (partial_order(FuncInfo, Call),
|
|
to_term(Call))
|
|
where [].
|
|
|
|
:- typeclass answer_pattern(FuncInfo, Answer)
|
|
<= (partial_order(FuncInfo, Answer),
|
|
to_term(Answer))
|
|
where [].
|
|
|
|
%---------------------%
|
|
|
|
% An analysis result is a call pattern paired with an answer.
|
|
% The result has a status associated with it.
|
|
%
|
|
:- type some_analysis_result
|
|
---> some [FuncInfo, Call, Answer]
|
|
some_analysis_result(
|
|
some_ar_call :: Call,
|
|
some_ar_answer :: Answer,
|
|
some_ar_status :: analysis_status
|
|
)
|
|
=> analysis(FuncInfo, Call, Answer).
|
|
|
|
:- type analysis_result(Call, Answer)
|
|
---> analysis_result(
|
|
ar_call :: Call,
|
|
ar_answer :: Answer,
|
|
ar_status :: analysis_status
|
|
).
|
|
|
|
% The status of a module or a specific analysis result.
|
|
%
|
|
% NOTE: the lub operation below depends on the function symbols
|
|
% being in this exact order.
|
|
%
|
|
:- type analysis_status
|
|
---> invalid
|
|
; suboptimal
|
|
; optimal.
|
|
|
|
%---------------------%
|
|
|
|
:- type analysis_map(T) == map(module_name, 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)).
|
|
|
|
%---------------------%
|
|
|
|
:- typeclass partial_order(FuncInfo, T) <= (T -> FuncInfo) where
|
|
[
|
|
pred more_precise_than(FuncInfo::in, T::in, T::in) is semidet,
|
|
pred equivalent(FuncInfo::in, T::in, T::in) is semidet
|
|
].
|
|
|
|
%---------------------%
|
|
|
|
:- typeclass to_term(S) where [
|
|
func to_term(S) = term,
|
|
pred from_term(term::in, S::out) is semidet
|
|
].
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Least upper bound of two analysis_status values.
|
|
%
|
|
:- func lub(analysis_status, analysis_status) = analysis_status.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
lub(StatusA, StatusB) = Status :-
|
|
compare(Cmp, StatusA, StatusB),
|
|
(
|
|
Cmp = (=),
|
|
Status = StatusA
|
|
;
|
|
Cmp = (<),
|
|
Status = StatusA
|
|
;
|
|
Cmp = (>),
|
|
Status = StatusB
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module analysis.framework.
|
|
%---------------------------------------------------------------------------%
|