mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
421 lines
17 KiB
Mathematica
421 lines
17 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, 2026 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 smart recompilation we need to record which items were expanded
|
|
% in each declaration.
|
|
%
|
|
% For each imported item we need to record which equivalence types
|
|
% are used in it, 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
|
|
:- type item_recomp_deps
|
|
---> no_item_recomp_deps
|
|
; item_recomp_deps(
|
|
% The name of the module currently being compiled.
|
|
module_name,
|
|
|
|
% We create one of these structures when we start recording
|
|
% the set of items expanded out within a declaration item
|
|
% (such as a item_type_defn_info, item_pred_decl_info,
|
|
% item_mode_decl_info, item_typeclass_info etc).
|
|
% This field contains the ids of those expanded-out items.
|
|
set(recomp_item_id)
|
|
).
|
|
|
|
% maybe_start_gathering_item_recomp_deps(ModuleName, ItemId,
|
|
% MaybeRecompInfo, MaybeGatheredItemDeps):
|
|
%
|
|
% If smart recompilation is enabled (as shown by the presence of a
|
|
% recompilation_info in the third argument), then return the initial
|
|
% version of the data structure in we will gather (by means of calls
|
|
% to gather_item_recomp_dep) the set of items that were expanded out
|
|
% while processing the given item, which will be a declaration.
|
|
%
|
|
% We do this because entities that depend on that declaration
|
|
% also depend on the expanded-out items within that declaration.
|
|
%
|
|
% maybe_start_gathering_item_recomp_deps_sym_name does the same job,
|
|
% but it callable from a context in which a recomp_item_id cannot (yet)
|
|
% be constructed. The contexts are pred declarations and mode declarations
|
|
% that, due to the presence of with_type and/or with_inst annotations,
|
|
% don't know their final arity yet, and may possibly also lack
|
|
% a pred_or_func indication. (recomp_item_ids for those kinds of items
|
|
% need both the arity and the pred_or_func indication.)
|
|
%
|
|
:- pred maybe_start_gathering_item_recomp_deps(module_name::in,
|
|
recomp_item_id::in, maybe(recompilation_info)::in,
|
|
item_recomp_deps::out) is det.
|
|
:- pred maybe_start_gathering_item_recomp_deps_sym_name(module_name::in,
|
|
sym_name::in, maybe(recompilation_info)::in,
|
|
item_recomp_deps::out) is det.
|
|
|
|
% gather_item_recomp_dep(DepItemId, !MaybeGatheredItemDeps):
|
|
%
|
|
% Record DepItemId as being expanded during the processing
|
|
% of the declaration item for which !.MaybeGatheredItemDeps was created.
|
|
%
|
|
:- pred gather_item_recomp_dep(recomp_item_id::in,
|
|
item_recomp_deps::in, item_recomp_deps::out) is det.
|
|
|
|
% finish_gathering_item_recomp_deps(ItemId, MaybeGatheredItemDeps,
|
|
% MaybeRecomp0, MaybeRecomp):
|
|
%
|
|
% This predicate should be called when the compiler has finished
|
|
% expanding out equivalences in the various components of the
|
|
% original declaration item. This predicate then records
|
|
% the fact that
|
|
% Record all the expanded items in the recompilation_info.
|
|
%
|
|
:- pred finish_gathering_item_recomp_deps(recomp_item_id::in,
|
|
item_recomp_deps::in,
|
|
maybe(recompilation_info)::in, maybe(recompilation_info)::out) is det.
|
|
|
|
% record_gathered_item_deps(ItemId, GatheredItemDeps, !Info):
|
|
%
|
|
% This predicate does the final part of the job of
|
|
% finish_gathering_item_recomp_deps, i.e. the incorporation
|
|
% of the gathered information in the recompilation_info.
|
|
%
|
|
% It is a separate predicate because for decl_pragma_type_spec_info
|
|
% and decl_pragma_type_spec_constr_info items, even though equiv_type.m
|
|
% gathers the ids of the items expanded out within them, it does NOT
|
|
% record the result of this gathering in the recompilation_info.
|
|
% (In fact, the code there does this gathering even in the *absence*
|
|
% of any recompilation_info.) Instead, it records the gathered item_ids
|
|
% in the updated decl_pragma_type_spec{,_constr}_info, and leaves it
|
|
% to add add_pragma_type_spec.m to invoke this predicate.
|
|
%
|
|
% I (zs) have no idea what the point of this delay is.
|
|
%
|
|
:- pred record_gathered_item_deps(recomp_item_id::in, set(recomp_item_id)::in,
|
|
recompilation_info::in, 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
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
maybe_start_gathering_item_recomp_deps(ModuleName, ItemId, MaybeRecompInfo,
|
|
MaybeGatheredItemDeps) :-
|
|
(
|
|
MaybeRecompInfo = no,
|
|
MaybeGatheredItemDeps = no_item_recomp_deps
|
|
;
|
|
MaybeRecompInfo = yes(_),
|
|
ItemId = recomp_item_id(_, ItemName),
|
|
ItemName = recomp_item_name(ItemSymName, _ItemArity),
|
|
( if ItemSymName = qualified(ModuleName, _) then
|
|
MaybeGatheredItemDeps = no_item_recomp_deps
|
|
else
|
|
MaybeGatheredItemDeps = item_recomp_deps(ModuleName, set.init)
|
|
)
|
|
).
|
|
|
|
maybe_start_gathering_item_recomp_deps_sym_name(ModuleName, ItemSymName,
|
|
MaybeRecompInfo, MaybeGatheredItemDeps) :-
|
|
(
|
|
MaybeRecompInfo = no,
|
|
MaybeGatheredItemDeps = no_item_recomp_deps
|
|
;
|
|
MaybeRecompInfo = yes(_),
|
|
( if ItemSymName = qualified(ModuleName, _) then
|
|
MaybeGatheredItemDeps = no_item_recomp_deps
|
|
else
|
|
MaybeGatheredItemDeps = item_recomp_deps(ModuleName, set.init)
|
|
)
|
|
).
|
|
|
|
gather_item_recomp_dep(DepItemId, !MaybeGatheredItemDeps) :-
|
|
(
|
|
!.MaybeGatheredItemDeps = no_item_recomp_deps
|
|
;
|
|
!.MaybeGatheredItemDeps = item_recomp_deps(ModuleName, GatheredDeps0),
|
|
DepItemId = recomp_item_id(_, DepItemName),
|
|
( if DepItemName = recomp_item_name(qualified(ModuleName, _), _) then
|
|
% We don't need to record local items.
|
|
true
|
|
else
|
|
set.insert(DepItemId, GatheredDeps0, GatheredDeps),
|
|
!:MaybeGatheredItemDeps = item_recomp_deps(ModuleName, GatheredDeps)
|
|
)
|
|
).
|
|
|
|
finish_gathering_item_recomp_deps(ItemId, MaybeGatheredItemDeps,
|
|
MaybeRecomp0, MaybeRecomp) :-
|
|
(
|
|
MaybeGatheredItemDeps = no_item_recomp_deps,
|
|
MaybeRecomp = MaybeRecomp0
|
|
;
|
|
MaybeGatheredItemDeps = item_recomp_deps(_, GatheredDeps),
|
|
(
|
|
MaybeRecomp0 = no,
|
|
unexpected($pred, "gathered deps but no recomp info")
|
|
;
|
|
MaybeRecomp0 = yes(Recomp0),
|
|
record_gathered_item_deps(ItemId, GatheredDeps, Recomp0, Recomp),
|
|
MaybeRecomp = yes(Recomp)
|
|
)
|
|
).
|
|
|
|
record_gathered_item_deps(ItemId, GatheredItemDeps, !Info) :-
|
|
( if set.is_empty(GatheredItemDeps) then
|
|
true
|
|
else
|
|
DepsMap0 = !.Info ^ recomp_dependencies,
|
|
( if map.search(DepsMap0, ItemId, Deps0) then
|
|
set.union(GatheredItemDeps, Deps0, Deps),
|
|
map.det_update(ItemId, Deps, DepsMap0, DepsMap)
|
|
else
|
|
map.det_insert(ItemId, GatheredItemDeps, DepsMap0, DepsMap)
|
|
),
|
|
!Info ^ recomp_dependencies := DepsMap
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module recompilation.record_uses.
|
|
%-----------------------------------------------------------------------------%
|