Files
mercury/compiler/recompilation.record_uses.m
Julien Fischer 153590a30f Update and fix more copyright notices.
compiler/*.m:
    As above.
2024-12-30 16:01:59 +11:00

356 lines
13 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2001-2007, 2011 The University of Melbourne.
% Copyright (C) 2014-2015, 2017, 2019-2024 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File: recompilation.m.
% Main author: stayl.
%
% Type declarations for smart recompilation.
% Predicates to record program items used by a compilation.
%
% A module must be recompiled if
% - The file itself has changed.
% - An imported item used in compiling the module has changed or been removed.
% - An item has been added to an imported module which could cause an
% ambiguity with an item used in compiling the module.
%
% Currently smart recompilation does not work properly with
% inter-module optimization. If a `.opt' file changes, all modules
% importing it need to be recompiled.
%
%-----------------------------------------------------------------------------%
:- module recompilation.record_uses.
:- interface.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module recompilation.item_types.
:- import_module map.
:- import_module maybe.
:- import_module set.
%-----------------------------------------------------------------------------%
:- type recompilation_info
---> recompilation_info(
% Name of the current module.
recomp_module_name :: module_name,
% Used items imported from other modules.
recomp_used_items :: used_items,
% For now we only record dependencies of imported items
% on equivalence types. The rest of the dependencies can be
% found by examining the pred_infos, type_defns etc of the
% items recorded in the used_items field above.
recomp_dependencies :: map(recomp_item_id,
set(recomp_item_id)),
recomp_version_numbers :: module_item_version_numbers_map
).
:- func init_recompilation_info(module_name) = recompilation_info.
%-----------------------------------------------------------------------------%
:- type used_item_type
---> used_type_name
% Just the name of the type, not its body. It is common
% for a value of a type to be passed through a predicate without
% inspecting the value -- such predicates do not need to be
% recompiled if the body of the type changes (except for
% equivalence types).
; used_type_defn
; used_inst
; used_mode
; used_typeclass
; used_functor % The RHS of a var-functor unification.
; used_predicate
; used_function.
% A simple_item_set records the single possible match for an item.
%
% XXX RECOMP RENAME The type name should reflect that fact.
%
:- type simple_item_set == map(name_arity, map(module_qualifier, module_name)).
% Items which are used by local items.
%
% XXX That "documentation" is not exactly complete ...
%
:- type used_items
---> used_items(
used_type_names :: simple_item_set,
used_type_defns :: simple_item_set,
used_insts :: simple_item_set,
used_modes :: simple_item_set,
used_typeclasses :: simple_item_set,
used_functors :: simple_item_set,
used_predicates :: simple_item_set,
used_functions :: simple_item_set
).
%-----------------------------------------------------------------------------%
% unqualified("") if the symbol was unqualified.
:- type module_qualifier == module_name.
:- func find_module_qualifier(sym_name) = module_qualifier.
:- func module_qualify_name(module_qualifier, string) = sym_name.
%-----------------------------------------------------------------------------%
% record_used_item(ItemType, UnqualifiedId, QualifiedId, !Info).
%
% Record a reference to UnqualifiedId, for which QualifiedId
% is the only match. If a new declaration is added so that
% QualifiedId is not the only match, we need to recompile.
%
:- pred record_used_item(used_item_type::in,
recomp_item_name::in, recomp_item_name::in,
recompilation_info::in, recompilation_info::out) is det.
% For each imported item we need to record which equivalence types
% are used because equiv_type.m removes all references to the
% equivalence types, and at that point we don't know which imported
% items are going to be used by the compilation.
%
% For predicates declared using `with_type` annotations,
% the version number in the interface file and the
% version_numbers map will refer to the arity before expansion
% of the `with_type` annotation, so that needs to be recorded
% here as well.
%
:- pred record_expanded_items(recomp_item_id::in, set(recomp_item_id)::in,
recompilation_info::in, recompilation_info::out) is det.
%-----------------------------------------------------------------------------%
% XXX RECOMP RENAME It is the eqv_expanded_item_set type that plays
% the role that X_info types (for X = polymorphism, simplify etc)
% play in the rest of the compiler, in that it contains a data structure
% that is threaded through a set of predicates (a) to give them the
% info they need to do their jobs, and (b) to collect their observations.
%
:- type eqv_expand_info
---> no_eqv_expand_info
; eqv_expand_info(module_name, set(recomp_item_id)).
% The module_name field contains the name of the module
% currently being compiled.
%
% XXX Document the meaning of the second field.
% For smart recompilation we need to record which items were expanded
% in each declaration. Any items which depend on that declaration also
% depend on the expanded items.
%
:- pred maybe_start_recording_expanded_items(module_name::in, sym_name::in,
maybe(recompilation_info)::in, eqv_expand_info::out) is det.
:- pred record_expanded_item(recomp_item_id::in,
eqv_expand_info::in, eqv_expand_info::out) is det.
% Record all the expanded items in the recompilation_info.
%
:- pred finish_recording_expanded_items(recomp_item_id::in,
eqv_expand_info::in,
maybe(recompilation_info)::in, maybe(recompilation_info)::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module require.
%-----------------------------------------------------------------------------%
init_recompilation_info(ModuleName) =
recompilation_info(ModuleName, init_used_items, map.init, map.init).
:- func init_used_items = used_items.
init_used_items =
used_items(map.init, map.init, map.init, map.init, map.init, map.init,
map.init, map.init).
:- func get_used_item_ids(used_items, used_item_type) = simple_item_set.
get_used_item_ids(Used, used_type_name) = Used ^ used_type_names.
get_used_item_ids(Used, used_type_defn) = Used ^ used_type_defns.
get_used_item_ids(Used, used_inst) = Used ^ used_insts.
get_used_item_ids(Used, used_mode) = Used ^ used_modes.
get_used_item_ids(Used, used_typeclass) = Used ^ used_typeclasses.
get_used_item_ids(Used, used_functor) = Used ^ used_functors.
get_used_item_ids(Used, used_predicate) = Used ^ used_predicates.
get_used_item_ids(Used, used_function) = Used ^ used_functions.
:- pred set_used_item_ids(used_item_type::in, simple_item_set::in,
used_items::in, used_items::out) is det.
set_used_item_ids(used_type_name, IdMap, !Used) :-
!Used ^ used_type_names := IdMap.
set_used_item_ids(used_type_defn, IdMap, !Used) :-
!Used ^ used_type_defns := IdMap.
set_used_item_ids(used_inst, IdMap, !Used) :-
!Used ^ used_insts := IdMap.
set_used_item_ids(used_mode, IdMap, !Used) :-
!Used ^ used_modes := IdMap.
set_used_item_ids(used_typeclass, IdMap, !Used) :-
!Used ^ used_typeclasses := IdMap.
set_used_item_ids(used_functor, IdMap, !Used) :-
!Used ^ used_functors := IdMap.
set_used_item_ids(used_predicate, IdMap, !Used) :-
!Used ^ used_predicates := IdMap.
set_used_item_ids(used_function, IdMap, !Used) :-
!Used ^ used_functions := IdMap.
%-----------------------------------------------------------------------------%
find_module_qualifier(unqualified(_)) = unqualified("").
find_module_qualifier(qualified(ModuleName, _)) = ModuleName.
module_qualify_name(Qualifier, Name) =
( if Qualifier = unqualified("") then
unqualified(Name)
else
qualified(Qualifier, Name)
).
%-----------------------------------------------------------------------------%
record_used_item(UsedItemType, Id, QualifiedId, !Info) :-
QualifiedId = recomp_item_name(QualifiedName, Arity),
( if
% Don't record builtin items (QualifiedId may be unqualified
% for predicates, functions and functors because they aren't
% qualified until after typechecking).
QualifiedName = unqualified(_),
ignore_unqual_item_for_item_type(UsedItemType) = ignore
then
true
else
Used0 = !.Info ^ recomp_used_items,
IdSet0 = get_used_item_ids(Used0, UsedItemType),
UnqualifiedName = unqualify_name(QualifiedName),
ModuleName = find_module_qualifier(QualifiedName),
UnqualifiedId = name_arity(UnqualifiedName, Arity),
Id = recomp_item_name(SymName, _),
ModuleQualifier = find_module_qualifier(SymName),
( if map.search(IdSet0, UnqualifiedId, MatchingNames0) then
( if map.contains(MatchingNames0, ModuleQualifier) then
true
else
map.det_insert(ModuleQualifier, ModuleName,
MatchingNames0, MatchingNames),
map.det_update(UnqualifiedId, MatchingNames, IdSet0, IdSet),
set_used_item_ids(UsedItemType, IdSet, Used0, Used),
!Info ^ recomp_used_items := Used
)
else
MatchingNames = map.singleton(ModuleQualifier, ModuleName),
map.det_insert(UnqualifiedId, MatchingNames, IdSet0, IdSet),
set_used_item_ids(UsedItemType, IdSet, Used0, Used),
!Info ^ recomp_used_items := Used
)
).
:- type maybe_ignore
---> do_not_ignore
; ignore.
:- func ignore_unqual_item_for_item_type(used_item_type) = maybe_ignore.
ignore_unqual_item_for_item_type(UsedItemType) = Ignore :-
(
( UsedItemType = used_type_name
; UsedItemType = used_type_defn
; UsedItemType = used_inst
; UsedItemType = used_mode
; UsedItemType = used_typeclass
),
Ignore = ignore
;
( UsedItemType = used_functor
; UsedItemType = used_predicate
; UsedItemType = used_function
),
Ignore = do_not_ignore
).
%-----------------------------------------------------------------------------%
record_expanded_items(Item, ExpandedItems, !Info) :-
( if set.is_empty(ExpandedItems) then
true
else
DepsMap0 = !.Info ^ recomp_dependencies,
( if map.search(DepsMap0, Item, Deps0) then
set.union(ExpandedItems, Deps0, Deps),
map.det_update(Item, Deps, DepsMap0, DepsMap)
else
map.det_insert(Item, ExpandedItems, DepsMap0, DepsMap)
),
!Info ^ recomp_dependencies := DepsMap
).
maybe_start_recording_expanded_items(ModuleName, SymName, MaybeRecompInfo,
ExpandInfo) :-
(
MaybeRecompInfo = no,
ExpandInfo = no_eqv_expand_info
;
MaybeRecompInfo = yes(_),
( if SymName = qualified(ModuleName, _) then
ExpandInfo = no_eqv_expand_info
else
ExpandInfo = eqv_expand_info(ModuleName, set.init)
)
).
record_expanded_item(ItemId, !ExpandInfo) :-
(
!.ExpandInfo = no_eqv_expand_info
;
!.ExpandInfo = eqv_expand_info(ModuleName, ExpandedItemIds0),
ItemId = recomp_item_id(_, ItemName),
( if ItemName = recomp_item_name(qualified(ModuleName, _), _) then
% We don't need to record local items.
true
else
set.insert(ItemId, ExpandedItemIds0, ExpandedItemIds),
!:ExpandInfo = eqv_expand_info(ModuleName, ExpandedItemIds)
)
).
finish_recording_expanded_items(ItemId, ExpandInfo,
MaybeRecomp0, MaybeRecomp) :-
(
ExpandInfo = no_eqv_expand_info,
MaybeRecomp = MaybeRecomp0
;
ExpandInfo = eqv_expand_info(_, ExpandedItemIds),
(
MaybeRecomp0 = no,
unexpected($pred, "items but no info")
;
MaybeRecomp0 = yes(Recomp0),
record_expanded_items(ItemId, ExpandedItemIds, Recomp0, Recomp),
MaybeRecomp = yes(Recomp)
)
).
%-----------------------------------------------------------------------------%
:- end_module recompilation.record_uses.
%-----------------------------------------------------------------------------%