Files
mercury/compiler/proc_requests.m
Zoltan Somogyi d8a31e574e Move six utility modules from check_hlds to hlds.
compiler/inst_lookup.m:
compiler/inst_mode_type_prop.m:
compiler/inst_test.m:
compiler/inst_util.m:
compiler/mode_util.m:
compiler/type_util.m:
    Move these modules from the check_hlds package to the hlds package.
    The reason is that all the content of five of these modules, and
    most of the content of one module (inst_util.m) is not used
    exclusively during semantic checking passes. (A later diff
    should deal with the exception.) Some are used by the pass that
    builds the initial HLDS, and all are used by middle-end and backend
    passes. The move therefore reduces the number of inappropriate imports
    of the check_hlds package.

compiler/check_hlds.m:
compiler/hlds.m:
    Effect the transfer.

compiler/*.m:
    Conform to the changes above.
2025-10-08 23:07:13 +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-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: 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 check_hlds.mode_test.
:- import_module hlds.add_special_pred.
:- import_module hlds.goal_transform.
:- import_module hlds.hlds_data.
:- import_module hlds.inst_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.
%---------------------------------------------------------------------------%