Files
mercury/compiler/inst_lookup.m
Zoltan Somogyi 593212d807 Minor improvements to comments and code.
compiler/inst_lookup.m:
    Fix minor bugs in comments.

    Do not use state variables where they do not help.

compiler/inst_merge.m:
    Special-case merges between two bound insts.

    Expand a comment.

compiler/inst_test.m:
    Delete a test that cannot ever succeed, and simplify the resulting code.

    Fix comment rot.
2025-12-11 10:21:37 +11:00

508 lines
21 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1997-2012 The University of Melbourne.
% Copyright (C) 2015, 2021, 2023-2025 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: inst_lookup.m.
% Author: fjh.
%
% This module looks up insts in the module_info, optionally expanding out
% references to user defined insts in the process.
%
%---------------------------------------------------------------------------%
:- module hlds.inst_lookup.
:- interface.
:- import_module hlds.hlds_module.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
%---------------------------------------------------------------------------%
%
% Looking up named insts.
%
% Given a user-defined or compiler-defined inst name, look up the
% corresponding inst in the inst table.
%
% Named insts may be recursive, i.e. they may contain references to
% themselves. When we unify or merge two insts, we therefore have to be
% able to recognize and handle situations in which such recursive
% references could lead to infinite loops.
%
% The way in which we do so for unifications is as follows.
%
% 1 Before we start to unify InstA and InstB, we create a unify_inst
% for the pair, and insert it into the unify inst table as the key,
% with the value being inst_det_unknown.
%
% 2 We start unifying the two insts.
%
% 3 If, during this process, we find that the two (sub)insts we are
% trying to unify are already in the unify inst table with value
% inst_det_unknown, then (due to 4 below) we know that the process
% of unifying that pair of (sub)insts is already in progress.
% After step 4, the unify_inst we constructed in step 1 for that pair
% of (sub) insts, which may or may not be InstA and InstB,
% will represent the outcome of the unification of that pair of
% (sub)insts, so in that case, we return that unify_inst as
% the result, even though the result inst is not known yet.
% (In effect, we know the inst_name of the result without knowing
% the actual inst result.)
%
% 4 When the unification of InstA and InstB is finished,
% we update the entry of the pair in the unify inst table
% to record the actual result.
%
% We handle the merging of two insts, or making an inst ground, similarly.
%
% Regardless of the form of the input inst_name, the value recorded
% for it in the appropriate part of the inst table (the parts being
% the unify inst table, the merge inst table, and so on) should be
% inst_unknown or inst_det_unknown ONLY WHILE A RECURSIVE OPERATION
% ON INSTS IS EXECUTING STEPS 1 THROUGH 3. As soon as the last recursive
% call of that operation is finished, all the entries it has set
% to inst_unknown or inst_det_unknown should have been set to inst_known
% or inst_det_known.
%
% There are two separate reasons why this lookup should always succeed
% once the HLDS has been fully built, including module qualification.
%
% - The module qualification process should find and report any reference
% to undefined user_inst names, and should not let the compiler proceed
% any further if it finds any.
%
% - Non-user_inst inst names should only start being introduced
% by compiler passes that do mode analysis, either as their main task,
% or as a necessary auxiliary component of their main task. These
% compiler passes should not construct non-user_inst inst names
% without ensuring that (a) they put the definition of the inst name
% into the appropriate subtable of the mode table, and (b) ensuring
% that the effect of this update does not get lost. (No compiler pass
% should ever introduce any new *user_inst* inst names, since
% the compiler is not a user. The inst_lookup_debug predicate below
% does introduce such user_inst inst names, but the compiler does
% nothing with those inst names except print them out.)
%
:- pred inst_lookup(module_info::in, inst_name::in, mer_inst::out) is det.
% This predicate is intended to help detect and report bugs in the
% operations that update (or are supposed to update) the inst table.
% It looks for and detects two separate kinds of bugs:
%
% - unknown insts, where a key is left in one of the inst tables
% (such as the unify inst table or the merge inst table) with a value of
% inst_unknown or inst_det_unknown *even after the operation
% that created that entry has finished*; and
%
% - missing insts, which are inst names left in the HLDS which have
% *no entry* in the relevant inst table.
%
% If inst_lookup_debug finds one that given inst_name has one of
% these problems, it will return a user_inst with a special form
% that is_unknown_or_missing_user_inst_name will recognize.
% The original inst_name passed to inst_lookup_debug will be
% reconstructable from this inst.
%
:- pred inst_lookup_debug(module_info::in, inst_name::in, mer_inst::out)
is det.
:- inst inst_name_user for inst_name/0
---> user_inst(ground, ground).
% Was the user_inst inst_name constructed by inst_lookup_debug
% to represent an error?
%
% If the answer is yes, then succeed; the sym_name part of the user_inst
% will describe the non-inst parts of the failed inst_name lookup, and
% its argument insts will describe its inst parts.
%
% If the answer is no, then fail.
%
:- pred is_unknown_or_missing_user_inst_name(inst_name::in(inst_name_user))
is semidet.
%---------------------------------------------------------------------------%
:- inst mer_inst_expanded for mer_inst/0
---> ground(ground, ground)
; free
; bound(ground, ground, ground)
; constrained_inst_vars(ground, ground)
; not_reached
; any(ground, ground)
; inst_var(ground).
:- inst mer_inst_expanded_nc for mer_inst/0
---> ground(ground, ground)
; free
; bound(ground, ground, ground)
; not_reached
; any(ground, ground)
; inst_var(ground).
% inst_expand(ModuleInfo, Inst0, Inst) checks if the top-level part
% of the inst is a defined inst, and if so replaces it with the definition.
%
% This leaves insts with constrained_inst_vars at the top level unchanged.
%
:- pred inst_expand(module_info::in, mer_inst::in,
mer_inst::out(mer_inst_expanded)) is det.
% inst_expand_and_remove_constrained_inst_vars is the same as inst_expand
% except that it also removes constrained_inst_vars from the top level,
% replacing them with the constraining inst.
%
:- pred inst_expand_and_remove_constrained_inst_vars(module_info::in,
mer_inst::in, mer_inst::out(mer_inst_expanded_nc)) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module hlds.hlds_inst_mode.
:- import_module hlds.inst_mode_type_prop.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.parse_tree_to_term.
:- import_module parse_tree.prog_mode.
:- import_module list.
:- import_module map.
:- import_module require.
:- import_module string.
%---------------------------------------------------------------------------%
inst_lookup(ModuleInfo, InstName, Inst) :-
% The non-error-handling parts of inst_lookup and inst_lookup_debug
% should be kept in sync.
(
InstName = unify_inst(Live, Real, InstA, InstB),
UnifyInstInfo = unify_inst_info(Live, Real, InstA, InstB),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_unify_insts(InstTable, UnifyInstTable),
lookup_unify_inst(UnifyInstTable, UnifyInstInfo, MaybeInstDet),
(
MaybeInstDet = inst_det_known(Inst, _)
;
MaybeInstDet = inst_det_unknown,
Inst = defined_inst(InstName)
)
;
InstName = merge_inst(InstA, InstB),
MergeInstInfo = merge_inst_info(InstA, InstB),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_merge_insts(InstTable, MergeInstTable),
lookup_merge_inst(MergeInstTable, MergeInstInfo, MaybeInst),
(
MaybeInst = inst_known(Inst)
;
MaybeInst = inst_unknown,
Inst = defined_inst(InstName)
)
;
InstName = ground_inst(SubInstName, Uniq, Live, Real),
GroundInstInfo = ground_inst_info(SubInstName, Uniq, Live, Real),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_ground_insts(InstTable, GroundInstTable),
lookup_ground_inst(GroundInstTable, GroundInstInfo, MaybeInstDet),
(
MaybeInstDet = inst_det_known(Inst, _)
;
MaybeInstDet = inst_det_unknown,
Inst = defined_inst(InstName)
)
;
InstName = any_inst(SubInstName, Uniq, Live, Real),
AnyInstInfo = any_inst_info(SubInstName, Uniq, Live, Real),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_any_insts(InstTable, AnyInstTable),
lookup_any_inst(AnyInstTable, AnyInstInfo, MaybeInstDet),
(
MaybeInstDet = inst_det_known(Inst, _)
;
MaybeInstDet = inst_det_unknown,
Inst = defined_inst(InstName)
)
;
InstName = shared_inst(SharedInstName),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_shared_insts(InstTable, SharedInstTable),
lookup_shared_inst(SharedInstTable, SharedInstName, MaybeInst),
(
MaybeInst = inst_known(Inst)
;
MaybeInst = inst_unknown,
Inst = defined_inst(InstName)
)
;
InstName = mostly_uniq_inst(NondetLiveInstName),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_mostly_uniq_insts(InstTable, MostlyUniqInstTable),
lookup_mostly_uniq_inst(MostlyUniqInstTable, NondetLiveInstName,
MaybeInst),
(
MaybeInst = inst_known(Inst)
;
MaybeInst = inst_unknown,
Inst = defined_inst(InstName)
)
;
InstName = user_inst(SymName, ArgInsts),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_user_insts(InstTable, UserInstTable),
list.length(ArgInsts, Arity),
InstCtor = inst_ctor(SymName, Arity),
( if map.search(UserInstTable, InstCtor, InstDefn) then
InstDefn = hlds_inst_defn(_VarSet, InstParams, InstBody, _MMTC,
_Context, _Status),
InstBody = eqv_inst(Inst0),
inst_substitute_arg_list(InstParams, ArgInsts, Inst0, Inst)
else
NameStr = sym_name_to_string(SymName),
string.format("reference to undefined inst %s", [s(NameStr)], Msg),
unexpected($pred, Msg)
)
;
InstName = typed_ground(Uniq, Type),
Inst0 = ground(Uniq, none_or_default_func),
propagate_unchecked_type_into_inst(ModuleInfo, Type, Inst0, Inst)
;
InstName = typed_inst(Type, TypedInstName),
inst_lookup(ModuleInfo, TypedInstName, Inst0),
% XXX Each invocation of inst_lookup expands out one inst_name.
% An inst_name of nonzero arity will be applied to a list of insts,
% some of which may contain other inst_names, whose arguments
% may contain other inst_names, and so on.
%
% Such situations represent potential performance problems, because
% this call will propagate type information into *all* parts of Inst0,
% not just the top layer. This means that an inst inside argument
% lists of N nested inst_names will have type information propagated
% into it N times.
propagate_unchecked_type_into_inst(ModuleInfo, Type, Inst0, Inst)
).
%---------------------------------------------------------------------------%
inst_lookup_debug(ModuleInfo, InstName, Inst) :-
% The non-error-handling parts of inst_lookup and inst_lookup_debug
% should be kept in sync.
(
InstName = unify_inst(Live, Real, InstA, InstB),
UnifyInstInfo = unify_inst_info(Live, Real, InstA, InstB),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_unify_insts(InstTable, UnifyInstTable),
string.format("unify_%s_%s",
[s(is_live_to_str(Live)), s(unify_is_real_to_str(Real))], Desc),
( if
search_unify_inst(UnifyInstTable, UnifyInstInfo, MaybeInstDet)
then
(
MaybeInstDet = inst_det_known(Inst, _)
;
MaybeInstDet = inst_det_unknown,
Inst = make_unknown_inst_name(Desc, [InstA, InstB])
)
else
Inst = make_missing_inst_name(Desc, [InstA, InstB])
)
;
InstName = merge_inst(InstA, InstB),
MergeInstInfo = merge_inst_info(InstA, InstB),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_merge_insts(InstTable, MergeInstTable),
( if search_merge_inst(MergeInstTable, MergeInstInfo, MaybeInst) then
(
MaybeInst = inst_known(Inst)
;
MaybeInst = inst_unknown,
Inst = make_unknown_inst_name("merge", [InstA, InstB])
)
else
Inst = make_missing_inst_name("merge", [InstA, InstB])
)
;
InstName = ground_inst(SubInstName, Uniq, Live, Real),
GroundInstInfo = ground_inst_info(SubInstName, Uniq, Live, Real),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_ground_insts(InstTable, GroundInstTable),
string.format("ground_%s_%s_%s",
[s(inst_uniqueness(Uniq, "shared")), s(is_live_to_str(Live)),
s(unify_is_real_to_str(Real))], Desc),
( if
search_ground_inst(GroundInstTable, GroundInstInfo, MaybeInstDet)
then
(
MaybeInstDet = inst_det_known(Inst, _)
;
MaybeInstDet = inst_det_unknown,
Inst = make_unknown_inst_name(Desc,
[defined_inst(SubInstName)])
)
else
Inst = make_missing_inst_name(Desc, [defined_inst(SubInstName)])
)
;
InstName = any_inst(SubInstName, Uniq, Live, Real),
AnyInstInfo = any_inst_info(SubInstName, Uniq, Live, Real),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_any_insts(InstTable, AnyInstTable),
string.format("any_%s_%s_%s",
[s(inst_uniqueness(Uniq, "shared")), s(is_live_to_str(Live)),
s(unify_is_real_to_str(Real))], Desc),
( if search_any_inst(AnyInstTable, AnyInstInfo, MaybeInstDet) then
(
MaybeInstDet = inst_det_known(Inst, _)
;
MaybeInstDet = inst_det_unknown,
Inst = make_unknown_inst_name(Desc,
[defined_inst(SubInstName)])
)
else
Inst = make_missing_inst_name(Desc, [defined_inst(SubInstName)])
)
;
InstName = shared_inst(SharedInstName),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_shared_insts(InstTable, SharedInstTable),
( if
search_shared_inst(SharedInstTable, SharedInstName, MaybeInst)
then
(
MaybeInst = inst_known(Inst)
;
MaybeInst = inst_unknown,
Inst = make_unknown_inst_name("shared",
[defined_inst(SharedInstName)])
)
else
Inst = make_missing_inst_name("shared",
[defined_inst(SharedInstName)])
)
;
InstName = mostly_uniq_inst(NondetLiveInstName),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_mostly_uniq_insts(InstTable, MostlyUniqInstTable),
( if
search_mostly_uniq_inst(MostlyUniqInstTable, NondetLiveInstName,
MaybeInst)
then
(
MaybeInst = inst_known(Inst)
;
MaybeInst = inst_unknown,
Inst = make_unknown_inst_name("mostly_uniq",
[defined_inst(NondetLiveInstName)])
)
else
Inst = make_missing_inst_name("mostly_uniq",
[defined_inst(NondetLiveInstName)])
)
;
InstName = user_inst(SymName, ArgInsts),
module_info_get_inst_table(ModuleInfo, InstTable),
inst_table_get_user_insts(InstTable, UserInstTable),
list.length(ArgInsts, Arity),
InstCtor = inst_ctor(SymName, Arity),
( if map.search(UserInstTable, InstCtor, InstDefn) then
InstDefn = hlds_inst_defn(_VarSet, InstParams, InstBody, _MMTC,
_Context, _Status),
InstBody = eqv_inst(Inst0),
inst_substitute_arg_list(InstParams, ArgInsts, Inst0, Inst)
else if is_unknown_or_missing_user_inst_name(InstName) then
Inst = defined_inst(InstName)
else
NameStr = sym_name_to_string(SymName),
string.format("reference to undefined inst %s", [s(NameStr)], Msg),
unexpected($pred, Msg)
)
;
InstName = typed_ground(Uniq, Type),
Inst0 = ground(Uniq, none_or_default_func),
propagate_unchecked_type_into_inst(ModuleInfo, Type, Inst0, Inst)
;
InstName = typed_inst(Type, TypedInstName),
inst_lookup_debug(ModuleInfo, TypedInstName, Inst0),
% XXX Each invocation of inst_lookup expands out one inst_name.
% An inst_name of nonzero arity will be applied to a list of insts,
% some of which may contain other inst_names, whose arguments
% may contain other inst_names, and so on.
%
% Such situations represent potential performance problems, because
% this call will propagate type information into *all* parts of Inst0,
% not just the top layer. This means that an inst inside argument
% lists of N nested inst_names will have type information propagated
% into it N times.
propagate_unchecked_type_into_inst(ModuleInfo, Type, Inst0, Inst)
).
:- func make_unknown_inst_name(string, list(mer_inst)) = mer_inst.
make_unknown_inst_name(Desc, ArgInsts) = UnknownInst :-
UnknownInstNameStr = "UNKNOWN_INST_" ++ Desc,
UnknownInstName = user_inst(unqualified(UnknownInstNameStr), ArgInsts),
UnknownInst = defined_inst(UnknownInstName).
:- func make_missing_inst_name(string, list(mer_inst)) = mer_inst.
make_missing_inst_name(Desc, ArgInsts) = MissingInst :-
MissingInstNameStr = "MISSING_INST_" ++ Desc,
MissingInstName = user_inst(unqualified(MissingInstNameStr), ArgInsts),
MissingInst = defined_inst(MissingInstName).
is_unknown_or_missing_user_inst_name(user_inst(SymName, _ArgInsts)) :-
SymName = unqualified(Name),
( string.prefix(Name, "UNKNOWN_INST_")
; string.prefix(Name, "MISSING_INST_")
).
%---------------------------------------------------------------------------%
inst_expand(ModuleInfo, Inst0, Inst) :-
(
Inst0 = defined_inst(InstName),
inst_lookup(ModuleInfo, InstName, Inst1),
inst_expand(ModuleInfo, Inst1, Inst)
;
( Inst0 = free
; Inst0 = not_reached
; Inst0 = ground(_, _)
; Inst0 = any(_, _)
; Inst0 = bound(_, _, _)
; Inst0 = constrained_inst_vars(_, _)
; Inst0 = inst_var(_)
),
Inst = Inst0
).
inst_expand_and_remove_constrained_inst_vars(ModuleInfo, Inst0, Inst) :-
(
Inst0 = defined_inst(InstName),
inst_lookup(ModuleInfo, InstName, Inst1),
inst_expand_and_remove_constrained_inst_vars(ModuleInfo, Inst1, Inst)
;
Inst0 = constrained_inst_vars(_, Inst1),
inst_expand_and_remove_constrained_inst_vars(ModuleInfo, Inst1, Inst)
;
( Inst0 = free
; Inst0 = not_reached
; Inst0 = ground(_, _)
; Inst0 = any(_, _)
; Inst0 = bound(_, _, _)
; Inst0 = inst_var(_)
),
Inst = Inst0
).
%---------------------------------------------------------------------------%
:- end_module hlds.inst_lookup.
%---------------------------------------------------------------------------%