Files
mercury/compiler/proc_requests.m
Zoltan Somogyi b560f66ab9 Move four modules from check_hlds.m to hlds.m.
After this, I think all modules in the check_hlds package belong there.

compiler/inst_match.m:
compiler/mode_test.m:
    Move these modules from the check_hlds package to the hlds package
    because most of their uses are outside the semantic analysis passes
    that the check_hlds package is intended to contain.

compiler/inst_merge.m:
    Move this module from the check_hlds package to the hlds package
    because it is imported by only two modules, instmap.m and inst_match.m,
    and after this diff, both are in the hlds package.

compiler/implementation_defined_literals.m:
    Move this module from the check_hlds package to the hlds package
    because it does a straightforward program transformation that
    does not have anything to do with semantic analysis (though its
    invocation does happen between semantic analysis passes).

compiler/notes/compiler_design.html:
    Update the documentation of the goal_path.m module. (I checked the
    documentation of the moved modules, which did not need updates,
    and found the need for this instead.)

compiler/*.m:
    Conform to the changes above. (For many modules, this deletes
    their import of the check_hlds package itself.)
2026-02-27 15:16:44 +11:00

344 lines
14 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1994-2012 The University of Melbourne.
% Copyright (C) 2015, 2017-2019, 2021-2022, 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: proc_requests.m.
%
% This module encapsulates access to the proc_requests table, and constructs
% the clauses for out-of-line complicated unification procedures.
% It also generates the code for other compiler-generated type-specific
% predicates such as compare/3.
%
% During mode analysis, we notice each different complicated unification
% that occurs. For each one we add a new mode to the out-of-line
% unification predicate for that type, and we record in the `proc_requests'
% table that we need to eventually modecheck that mode of the unification
% procedure.
%
% After we have done mode analysis for all the ordinary predicates, we then
% do mode analysis for the out-of-line unification procedures. Note that
% unification procedures may call other unification procedures which have
% not yet been encountered, causing new entries to be added to the
% proc_requests table. We store the entries in a queue and continue the
% process until the queue is empty.
%
% The same queuing mechanism is also used for procedures created by
% mode inference during mode analysis and unique mode analysis.
%
% Currently if the same complicated unification procedure is called by
% different modules, each module will end up with a copy of the code for
% that procedure. In the long run it would be desirable to either delay
% generation of complicated unification procedures until link time (like
% Cfront does with C++ templates) or to have a smart linker which could
% merge duplicate definitions (like Borland C++). However the amount of
% code duplication involved is probably very small, so it is definitely not
% worth worrying about right now.
%
% XXX What about complicated unification of an abstract type in a partially
% instantiated mode? Currently we don't implement it correctly. Probably
% it should be disallowed, but we should issue a proper error message.
%
%---------------------------------------------------------------------------%
:- module check_hlds.proc_requests.
:- interface.
:- import_module hlds.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module list.
:- import_module maybe.
:- import_module queue.
%---------------------------------------------------------------------------%
:- type proc_requests.
:- type req_queue == queue(pred_proc_id).
:- pred get_req_queue(proc_requests::in, req_queue::out) is det.
:- pred set_req_queue(req_queue::in,
proc_requests::in, proc_requests::out) is det.
%---------------------------------------------------------------------------%
:- type unify_proc_id
---> unify_proc_id(type_ctor, unify_mode).
% Initialize the proc_requests table.
%
:- pred init_requests(proc_requests::out) is det.
% Add a new request for a unification procedure to the proc_requests table.
%
:- pred request_unify(unify_proc_id::in, inst_varset::in, determinism::in,
prog_context::in, module_info::in, module_info::out) is det.
% Add a new request for a procedure (not necessarily a unification)
% to the request queue. Return the procedure's newly allocated proc_id.
% (This is used by mode inference and unique_modes.m.)
%
:- pred request_proc(pred_id::in, list(mer_mode)::in, inst_varset::in,
maybe(list(is_live))::in, maybe(determinism)::in, prog_context::in,
proc_id::out, module_info::in, module_info::out) is det.
%---------------------------------------------------------------------------%
% Given the type and mode of a unification, look up the mode number
% for the unification proc.
%
:- pred lookup_mode_num(module_info::in, type_ctor::in, unify_mode::in,
determinism::in, proc_id::out) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module check_hlds.clause_to_proc.
:- import_module hlds.add_special_pred.
:- import_module hlds.goal_transform.
:- import_module hlds.hlds_data.
:- import_module hlds.inst_test.
:- import_module hlds.mode_test.
:- import_module hlds.pred_name.
:- import_module hlds.special_pred.
:- import_module hlds.type_util.
:- import_module mdbcomp.
:- import_module mdbcomp.prim_data.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.prog_mode.
:- import_module recompilation.
:- import_module recompilation.item_types.
:- import_module recompilation.record_uses.
:- import_module map.
:- import_module require.
:- import_module varset.
%---------------------------------------------------------------------------%
% We keep track of all the complicated unification procs we need
% by storing them in the proc_requests structure.
% For each unify_proc_id (i.e. type & mode), we store the proc_id
% (mode number) of the unification procedure which corresponds to
% that mode.
%
:- type unify_req_map == map(unify_proc_id, proc_id).
:- type proc_requests
---> proc_requests(
unify_req_map :: unify_req_map,
% The assignment of proc_id numbers to unify_proc_ids.
req_queue :: req_queue
% The queue of procs we still to generate code for.
).
:- pred get_unify_req_map(proc_requests::in, unify_req_map::out) is det.
:- pred set_unify_req_map(unify_req_map::in,
proc_requests::in, proc_requests::out) is det.
get_unify_req_map(PR, X) :-
X = PR ^ unify_req_map.
get_req_queue(PR, X) :-
X = PR ^ req_queue.
set_unify_req_map(X, !PR) :-
!PR ^ unify_req_map := X.
set_req_queue(X, !PR) :-
!PR ^ req_queue := X.
%---------------------------------------------------------------------------%
init_requests(Requests) :-
map.init(UnifyReqMap),
queue.init(ReqQueue),
Requests = proc_requests(UnifyReqMap, ReqQueue).
%---------------------------------------------------------------------------%
request_unify(UnifyId, InstVarSet, Determinism, Context, !ModuleInfo) :-
UnifyId = unify_proc_id(TypeCtor, UnifyMode),
% Generating a unification procedure for a type uses its body.
module_info_get_maybe_recompilation_info(!.ModuleInfo, MaybeRecompInfo0),
(
MaybeRecompInfo0 = yes(RecompInfo0),
TypeCtorItem = type_ctor_to_recomp_item_name(TypeCtor),
recompilation.record_uses.record_used_item(used_type_defn,
TypeCtorItem, TypeCtorItem, RecompInfo0, RecompInfo),
module_info_set_maybe_recompilation_info(yes(RecompInfo), !ModuleInfo)
;
MaybeRecompInfo0 = no
),
% Check if this unification has already been requested, or
% if the proc is hand defined.
( if
(
search_mode_num(!.ModuleInfo, TypeCtor, UnifyMode, Determinism, _)
;
module_info_get_type_table(!.ModuleInfo, TypeTable),
search_type_ctor_defn(TypeTable, TypeCtor, TypeDefn),
hlds_data.get_type_defn_body(TypeDefn, TypeBody),
(
TypeCtor = type_ctor(TypeName, _TypeArity),
TypeName = qualified(TypeModuleName, _),
module_info_get_name(!.ModuleInfo, ModuleName),
ModuleName = TypeModuleName,
TypeBody = hlds_abstract_type(_)
;
type_ctor_has_hand_defined_rtti(TypeCtor, TypeBody)
)
)
then
true
else
% Lookup the pred_id for the unification procedure that we are
% going to generate.
module_info_get_special_pred_maps(!.ModuleInfo, SpecialPredMaps),
UnifyMap = SpecialPredMaps ^ spm_unify_map,
( if map.search(UnifyMap, TypeCtor, PredIdPrime) then
PredId = PredIdPrime
else
% We generate unification predicates for most imported types
% lazily, so add the declarations and clauses now.
add_lazily_generated_unify_pred(TypeCtor, PredId, !ModuleInfo)
),
UnifyMode = unify_modes_li_lf_ri_rf(LHSInit, LHSFinal,
RHSInit, RHSFinal),
LHSMode = from_to_mode(LHSInit, LHSFinal),
RHSMode = from_to_mode(RHSInit, RHSFinal),
ArgModes0 = [LHSMode, RHSMode],
% For polymorphic types, add extra modes for the type_infos.
in_mode(InMode),
TypeCtor = type_ctor(_, TypeArity),
list.duplicate(TypeArity, InMode, TypeInfoModes),
ArgModes = TypeInfoModes ++ ArgModes0,
ArgLives = no, % XXX ArgLives should be part of the UnifyId
request_proc(PredId, ArgModes, InstVarSet, ArgLives, yes(Determinism),
Context, ProcId, !ModuleInfo),
% Save the proc_id for this unify_proc_id.
module_info_get_proc_requests(!.ModuleInfo, Requests0),
get_unify_req_map(Requests0, UnifyReqMap0),
map.set(UnifyId, ProcId, UnifyReqMap0, UnifyReqMap),
set_unify_req_map(UnifyReqMap, Requests0, Requests),
module_info_set_proc_requests(Requests, !ModuleInfo)
).
request_proc(PredId, ArgModes, InstVarSet, ArgLives, MaybeDet, Context, ProcId,
!ModuleInfo) :-
some [!PredInfo, !ProcInfo, !Goal] (
% Create a new proc_info for this procedure.
module_info_pred_info(!.ModuleInfo, PredId, !:PredInfo),
SeqNum = item_no_seq_num,
DeclaredArgModes = no,
% Before the simplification pass, HasParallelConj is not meaningful.
HasParallelConj = has_no_parallel_conj,
add_new_proc(!.ModuleInfo, Context, SeqNum,
InstVarSet, ArgModes, DeclaredArgModes, ArgLives,
detism_decl_implicit, MaybeDet, address_is_not_taken,
HasParallelConj, !PredInfo, ProcId),
% Copy the clauses for the procedure from the pred_info
% to the proc_info, and mark the procedure as one that
% cannot be processed yet. (The mark will be changed to
% `can_process_now' by modecheck_queued_proc.)
pred_info_proc_info(!.PredInfo, ProcId, !:ProcInfo),
proc_info_set_can_process(cannot_process_yet, !ProcInfo),
copy_clauses_to_proc_in_proc_info(!.PredInfo, ProcId, !ProcInfo),
proc_info_get_goal(!.ProcInfo, !:Goal),
set_goal_contexts(Context, !Goal),
% The X == Y pretest on unifications makes sense only for in-in
% unifications, and if the initial insts are incompatible, then
% casts in the pretest would prevent mode analysis from discovering
% this fact.
pred_info_get_origin(!.PredInfo, Origin),
pred_info_get_arg_types(!.PredInfo, ArgTypes),
( if
Origin = origin_compiler(made_for_uci(spec_pred_unify, _TypeCtor)),
all_modes_are_fully_input(!.ModuleInfo, ArgTypes, ArgModes),
not MaybeDet = yes(detism_failure)
then
true
else
!:Goal = maybe_strip_equality_pretest(!.Goal)
),
proc_info_set_goal(!.Goal, !ProcInfo),
pred_info_set_proc_info(ProcId, !.ProcInfo, !PredInfo),
module_info_set_pred_info(PredId, !.PredInfo, !ModuleInfo),
% Insert the pred_proc_id into the request queue.
module_info_get_proc_requests(!.ModuleInfo, Requests0),
get_req_queue(Requests0, ReqQueue0),
queue.put(proc(PredId, ProcId), ReqQueue0, ReqQueue),
set_req_queue(ReqQueue, Requests0, Requests),
module_info_set_proc_requests(Requests, !ModuleInfo)
).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
lookup_mode_num(ModuleInfo, TypeCtor, UniMode, Det, Num) :-
( if search_mode_num(ModuleInfo, TypeCtor, UniMode, Det, NumPrime) then
Num = NumPrime
else
unexpected($pred, "search_num failed")
).
% Given the type, mode, and determinism of a unification, look up the
% mode number for the unification proc.
% We handle semidet unifications with mode (in, in) specially - they
% are always mode zero. Similarly for unifications of `any' insts.
% (It should be safe to use the `in, in' mode for any insts, since
% we assume that `ground' and `any' have the same representation.)
% For unreachable unifications, we also use mode zero.
%
:- pred search_mode_num(module_info::in, type_ctor::in, unify_mode::in,
determinism::in, proc_id::out) is semidet.
search_mode_num(ModuleInfo, TypeCtor, UnifyMode, Determinism, ProcId) :-
UnifyMode = unify_modes_li_lf_ri_rf(InitInstX, _FinalInstX,
InitInstY, _FinalInstY),
( if
Determinism = detism_semi,
inst_is_ground_or_any(ModuleInfo, InitInstX),
inst_is_ground_or_any(ModuleInfo, InitInstY)
then
hlds_pred.in_in_unification_proc_id(ProcId)
else if
InitInstX = not_reached
then
hlds_pred.in_in_unification_proc_id(ProcId)
else if
InitInstY = not_reached
then
hlds_pred.in_in_unification_proc_id(ProcId)
else
module_info_get_proc_requests(ModuleInfo, Requests),
get_unify_req_map(Requests, UnifyReqMap),
map.search(UnifyReqMap, unify_proc_id(TypeCtor, UnifyMode), ProcId)
).
%---------------------------------------------------------------------------%
:- end_module check_hlds.proc_requests.
%---------------------------------------------------------------------------%