Files
mercury/compiler/arg_info.m
Zoltan Somogyi b0dd1ac0ed Move mode_top_functor.m from check_hlds to hlds.
compiler/check_hlds.m:
compiler/hlds.m:
compiler/mode_top_functor.m:
    Move the mode_top_functor module from the check_hlds package
    to the hlds package, because most of its users are outside check_hlds.

compiler/arg_info.m:
compiler/deep_profiling.m:
compiler/foreign_proc_gen.m:
compiler/hlds_rtti.m:
compiler/lco.m:
compiler/liveness.m:
compiler/mark_tail_calls.m:
compiler/ml_args_util.m:
compiler/ml_foreign_proc_gen.m:
compiler/ml_unify_gen_construct.m:
compiler/ml_unify_gen_util.m:
compiler/modecheck_unify.m:
compiler/structure_sharing.domain.m:
compiler/unify_gen_construct.m:
compiler/unify_gen_util.m:
    Conform to the changes above. (Many of these modules do not import
    anything from the check_hlds package after this.)
2025-10-20 14:57:16 +11:00

674 lines
26 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1994-2000,2002-2007, 2010-2012 The University of Melbourne.
% Copyright (c) 2014-2017, 2019-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: arg_info.m.
% Main author: fjh.
%
% This module is one of the pre-passes of the code generator.
% It initializes the arg_info field of the proc_info structure in the HLDS,
% which records for each argument of each procedure, whether the
% argument is input/output/unused, and which register it is supposed to
% go into.
%
% The code in this module assumes that none of the modes are undefined.
% The predicates for partitioning arguments into input, unused and output
% should not be used before undefined modes have been detected. For an example
% of how this kind of partitioning can be done in the presence of undefined
% modes, see superhomogeneous.partition_args_and_lambda_vars/8.
%
%---------------------------------------------------------------------------%
:- module hlds.arg_info.
:- interface.
:- import_module hlds.code_model.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_llds.
:- import_module hlds.hlds_markers.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.var_table.
:- import_module assoc_list.
:- import_module list.
:- import_module set.
%---------------------------------------------------------------------------%
% Annotate every procedure in the module with information
% about its argument passing interface.
%
:- pred generate_arg_info(module_info::in, module_info::out) is det.
% Annotate a single procedure with information
% about its argument passing interface.
%
:- pred generate_proc_arg_info(module_info::in, pred_markers::in,
list(mer_type)::in, proc_info::in, proc_info::out) is det.
% Given the list of types and modes of the arguments of a procedure
% and its code model, return the standard argument passing interface
% for procedure. This will pass float arguments via float registers
% if present, while passing all other arguments via regular registers.
%
:- pred make_standard_arg_infos(module_info::in, code_model::in,
list(mer_type)::in, list(mer_mode)::in, list(arg_info)::out) is det.
% As above, but pass the register type for each argument explicitly.
% This is necessary for procedures with float arguments that must be
% passed via the regular registers instead of float registers.
%
:- pred make_arg_infos(module_info::in, code_model::in,
list(mer_type)::in, list(mer_mode)::in, list(reg_type)::in,
list(arg_info)::out) is det.
% Return the register type to use for each argument of a generic call.
%
:- pred generic_call_arg_reg_types(module_info::in, generic_call::in,
list(prog_var)::in, arg_reg_type_info::in, list(reg_type)::out) is det.
% Divide the given list of arguments into those treated as inputs
% by the calling convention and those treated as outputs.
%
:- pred compute_in_and_out_vars(module_info::in, var_table::in,
list(prog_var)::in, list(mer_mode)::in,
list(prog_var)::out, list(prog_var)::out) is det.
% Divide the given list of arguments into those treated as inputs
% by the calling convention and those treated as outputs.
% Arguments are further divided those passed via regular registers
% or via float registers.
%
:- pred compute_in_and_out_vars_sep_regs(module_info::in, var_table::in,
list(prog_var)::in, list(mer_mode)::in, list(reg_type)::in,
list(prog_var)::out, list(prog_var)::out,
list(prog_var)::out, list(prog_var)::out) is det.
% Divide the given list of arguments and the arg_infos into two
% lists: those which are treated as inputs by the calling convention
% and those which are treated as outputs by the calling convention.
%
:- pred partition_args(assoc_list(prog_var, arg_info)::in,
assoc_list(prog_var, arg_info)::out,
assoc_list(prog_var, arg_info)::out) is det.
% Divide the given list of arguments and the arg_infos into three lists:
% the inputs, the outputs, and the unused arguments, in that order.
%
:- pred partition_args(assoc_list(prog_var, arg_info)::in,
assoc_list(prog_var, arg_info)::out,
assoc_list(prog_var, arg_info)::out,
assoc_list(prog_var, arg_info)::out) is det.
% Partition the head variables of the given procedure into three sets:
% the inputs, the outputs, and the unused arguments. This is done based
% on the arg_info annotations of the arguments, which means that this
% predicate should only be called after the arg_info pass has been run.
%
:- pred partition_proc_args(module_info::in, proc_info::in,
set(prog_var)::out, set(prog_var)::out, set(prog_var)::out) is det.
% Like partition_proc_args, but partitions the actual
% arguments of a call (given in the fourth argument) instead of the
% head variables. Since the first (proc_info) argument is now the
% proc_info of the callee, we need to pass the types of the arguments
% (in the caller) separately.
%
:- pred partition_proc_call_args(module_info::in, proc_info::in,
var_table::in, list(prog_var)::in,
set(prog_var)::out, set(prog_var)::out, set(prog_var)::out) is det.
% Like partition_proc_call_args, but partitions the actual
% arguments of a generic call, so instead of looking up the types and modes
% of the arguments in the module_info, we get them from the arguments.
%
:- pred partition_generic_call_args(module_info::in,
list(prog_var)::in, list(mer_type)::in, list(mer_mode)::in,
set(prog_var)::out, set(prog_var)::out, set(prog_var)::out) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module hlds.mode_top_functor.
:- import_module libs.
:- import_module libs.globals.
:- import_module libs.options.
:- import_module parse_tree.builtin_lib_types.
:- import_module parse_tree.set_of_var.
:- import_module bool.
:- import_module int.
:- import_module map.
:- import_module pair.
:- import_module require.
%---------------------------------------------------------------------------%
%
% This whole section just traverses the module structure
%
generate_arg_info(!ModuleInfo) :-
module_info_get_globals(!.ModuleInfo, Globals),
globals.lookup_bool_option(Globals, use_float_registers, UseFloatRegs),
module_info_get_pred_id_table(!.ModuleInfo, PredIdTable0),
map.to_sorted_assoc_list(PredIdTable0, PredIdsInfos0),
generate_arg_infos_for_preds(!.ModuleInfo, UseFloatRegs, PredIdsInfos0,
[], RevPredIdsInfos),
map.from_rev_sorted_assoc_list(RevPredIdsInfos, PredIdTable),
module_info_set_pred_id_table(PredIdTable, !ModuleInfo).
% We use an accumulator because the list of predicates can be quite long.
%
:- pred generate_arg_infos_for_preds(module_info::in, bool::in,
assoc_list(pred_id, pred_info)::in,
assoc_list(pred_id, pred_info)::in,
assoc_list(pred_id, pred_info)::out) is det.
generate_arg_infos_for_preds(_, _, [], !RevPredIdsInfos).
generate_arg_infos_for_preds(ModuleInfo, UseFloatRegs,
[PredIdInfo0 | PredIdsInfos0], !RevPredIdsInfos) :-
PredIdInfo0 = PredId - PredInfo0,
pred_info_get_markers(PredInfo0, Markers),
pred_info_get_arg_types(PredInfo0, ArgTypes),
pred_info_get_proc_table(PredInfo0, ProcTable0),
map.to_sorted_assoc_list(ProcTable0, ProcIdsInfos0),
generate_arg_infos_for_procs(ModuleInfo, UseFloatRegs, Markers, ArgTypes,
ProcIdsInfos0, ProcIdsInfos),
map.from_sorted_assoc_list(ProcIdsInfos, ProcTable),
pred_info_set_proc_table(ProcTable, PredInfo0, PredInfo),
PredIdInfo = PredId - PredInfo,
!:RevPredIdsInfos = [PredIdInfo | !.RevPredIdsInfos],
generate_arg_infos_for_preds(ModuleInfo, UseFloatRegs,
PredIdsInfos0, !RevPredIdsInfos).
% We *don't* use an accumulator because in practice,
% the list of procedures is never too long except in pathological code.
%
:- pred generate_arg_infos_for_procs(module_info::in, bool::in,
pred_markers::in, list(mer_type)::in,
assoc_list(proc_id, proc_info)::in,
assoc_list(proc_id, proc_info)::out) is det.
generate_arg_infos_for_procs(_, _, _, _, [], []).
generate_arg_infos_for_procs(ModuleInfo, UseFloatRegs, Markers, ArgTypes,
[ProcIdInfo0 | ProcIdInfos0], [ProcIdInfo | ProcIdInfos]) :-
ProcIdInfo0 = ProcId - ProcInfo0,
generate_arg_infos_for_proc(ModuleInfo, UseFloatRegs, Markers, ArgTypes,
ProcInfo0, ProcInfo),
ProcIdInfo = ProcId - ProcInfo,
generate_arg_infos_for_procs(ModuleInfo, UseFloatRegs, Markers, ArgTypes,
ProcIdInfos0, ProcIdInfos).
generate_proc_arg_info(ModuleInfo, Markers, ArgTypes, !ProcInfo) :-
module_info_get_globals(ModuleInfo, Globals),
globals.lookup_bool_option(Globals, use_float_registers, UseFloatRegs),
generate_arg_infos_for_proc(ModuleInfo, UseFloatRegs, Markers, ArgTypes,
!ProcInfo).
:- pred generate_arg_infos_for_proc(module_info::in, bool::in,
pred_markers::in, list(mer_type)::in,
proc_info::in, proc_info::out) is det.
generate_arg_infos_for_proc(ModuleInfo, UseFloatRegs, Markers, ArgTypes,
!ProcInfo) :-
proc_info_get_headvars(!.ProcInfo, HeadVars),
proc_info_get_argmodes(!.ProcInfo, ArgModes),
CodeModel = proc_info_interface_code_model(!.ProcInfo),
( if
UseFloatRegs = yes,
% XXX we don't yet use float registers for class method calls
not marker_is_present(Markers, marker_class_instance_method)
then
proc_info_get_reg_r_headvars(!.ProcInfo, RegR_HeadVars),
list.map_corresponding(reg_type_for_headvar(RegR_HeadVars),
HeadVars, ArgTypes, ArgRegTypes),
make_arg_infos(ModuleInfo, CodeModel, ArgTypes, ArgModes, ArgRegTypes,
ArgInfos)
else
make_reg_r_arg_infos(ModuleInfo, CodeModel, ArgTypes, ArgModes,
ArgInfos)
),
proc_info_set_arg_info(ArgInfos, !ProcInfo).
:- pred reg_type_for_headvar(set_of_progvar::in, prog_var::in, mer_type::in,
reg_type::out) is det.
reg_type_for_headvar(RegR_HeadVars, HeadVar, Type, RegType) :-
( if set_of_var.contains(RegR_HeadVars, HeadVar) then
RegType = reg_r
else
( if Type = float_type then
RegType = reg_f
else
RegType = reg_r
)
).
%---------------------------------------------------------------------------%
make_standard_arg_infos(ModuleInfo, CodeModel, ArgTypes, ArgModes, ArgInfos) :-
module_info_get_globals(ModuleInfo, Globals),
globals.lookup_bool_option(Globals, use_float_registers, FloatRegs),
(
FloatRegs = yes,
make_std_arg_infos(ModuleInfo, CodeModel, ArgTypes, ArgModes,
ArgInfos)
;
FloatRegs = no,
make_reg_r_arg_infos(ModuleInfo, CodeModel, ArgTypes, ArgModes,
ArgInfos)
).
%---------------------------------------------------------------------------%
%
% This is the useful part of the code ;-).
%
% This code the only place in the compiler where we make assumptions about
% the calling convention, though there are other places scattered around
% the runtime and the library that also rely on it.
%
% We assume all input arguments always go in sequentially numbered
% registers starting at registers r1 and f1. We also assume that all output
% arguments go in sequentially numbered registers starting at r1 and f1,
% except for model_semi procedures, where r1 is reserved for the result and
% hence the output arguments start at registers r2 and f1.
%
% We allocate unused args as if they were regular (non-floating point)
% outputs. The calling convention requires that we allocate them a
% register, and the choice should not matter since unused args should be
% rare. However, we do have to make sure that all the predicates in this
% module implement this decision consistently. (No code outside this module
% should know about the outcome of this decision.)
%
% The next three sections of code define
%
% - make_std_arg_infos,
% - make_reg_r_arg_infos, and
% - make_arg_infos.
%
% These all do variations of the same job, so a change in one may require
% corresponding changes in the other two as well.
%
% make_arg_infos is the most general version, in that it allows the caller
% to specify the reg_type for every argument individually. However, this
% also makes it the slowest. The other two are specialized versions that
% do not take a separate reg_type for every argument. make_reg_r_arg_infos
% allocates every argument a reg_r register, while make_std_arg_infos
% allocates every argument whose type is not "float" a reg_r register.
%
%---------------------------------------------------------------------------%
:- pred make_std_arg_infos(module_info::in, code_model::in,
list(mer_type)::in, list(mer_mode)::in, list(arg_info)::out) is det.
make_std_arg_infos(ModuleInfo, CodeModel, ArgTypes, ArgModes, ArgInfos) :-
initial_r_regs(CodeModel, FirstInRegR, FirstOutRegR),
FirstInRegF = 1,
FirstOutRegF = 1,
make_std_arg_infos_loop(ModuleInfo, ArgTypes, ArgModes,
FirstInRegR, FirstInRegF, FirstOutRegR, FirstOutRegF, ArgInfos).
:- pred make_std_arg_infos_loop(module_info::in,
list(mer_type)::in, list(mer_mode)::in,
int::in, int::in, int::in, int::in, list(arg_info)::out) is det.
make_std_arg_infos_loop(_, [], [], _, _, _, _, []).
make_std_arg_infos_loop(_, [], [_ | _], _, _, _, _, _) :-
unexpected($pred, "length mismatch").
make_std_arg_infos_loop(_, [_ | _], [], _, _, _, _, _) :-
unexpected($pred, "length mismatch").
make_std_arg_infos_loop(ModuleInfo, [Type | Types], [Mode | Modes],
!.InRegR, !.InRegF, !.OutRegR, !.OutRegF, [ArgInfo | ArgInfos]) :-
require_det (
mode_to_top_functor_mode(ModuleInfo, Mode, Type, TopFunctorMode),
(
TopFunctorMode = top_in,
get_std_arg_loc(Type, ArgLoc, !InRegR, !InRegF)
;
( TopFunctorMode = top_out
; TopFunctorMode = top_unused
),
get_std_arg_loc(Type, ArgLoc, !OutRegR, !OutRegF)
),
ArgInfo = arg_info(ArgLoc, TopFunctorMode)
),
make_std_arg_infos_loop(ModuleInfo, Types, Modes,
!.InRegR, !.InRegF, !.OutRegR, !.OutRegF, ArgInfos).
:- pred get_std_arg_loc(mer_type::in, arg_loc::out, int::in, int::out,
int::in, int::out) is det.
get_std_arg_loc(Type, ArgLoc, !RegR, !RegF) :-
( if Type = float_type then
ArgLoc = reg(reg_f, !.RegF),
!:RegF = !.RegF + 1
else
ArgLoc = reg(reg_r, !.RegR),
!:RegR = !.RegR + 1
).
%---------------------%
:- pred make_reg_r_arg_infos(module_info::in, code_model::in,
list(mer_type)::in, list(mer_mode)::in, list(arg_info)::out) is det.
make_reg_r_arg_infos(ModuleInfo, CodeModel, ArgTypes, ArgModes, ArgInfos) :-
initial_r_regs(CodeModel, FirstInRegR, FirstOutRegR),
make_reg_r_arg_infos_loop(ModuleInfo, ArgTypes, ArgModes,
FirstInRegR, FirstOutRegR, ArgInfos).
:- pred make_reg_r_arg_infos_loop(module_info::in,
list(mer_type)::in, list(mer_mode)::in,
int::in, int::in, list(arg_info)::out) is det.
make_reg_r_arg_infos_loop(_, [], [], _, _, []).
make_reg_r_arg_infos_loop(_, [], [_ | _], _, _, _) :-
unexpected($pred, "length mismatch").
make_reg_r_arg_infos_loop(_, [_ | _], [], _, _, _) :-
unexpected($pred, "length mismatch").
make_reg_r_arg_infos_loop(ModuleInfo, [Type | Types], [Mode | Modes],
!.InRegR, !.OutRegR, [ArgInfo | ArgInfos]) :-
mode_to_top_functor_mode(ModuleInfo, Mode, Type, TopFunctorMode),
(
TopFunctorMode = top_in,
get_reg_r_arg_loc(ArgLoc, !InRegR)
;
( TopFunctorMode = top_out
; TopFunctorMode = top_unused
),
get_reg_r_arg_loc(ArgLoc, !OutRegR)
),
ArgInfo = arg_info(ArgLoc, TopFunctorMode),
make_reg_r_arg_infos_loop(ModuleInfo, Types, Modes,
!.InRegR, !.OutRegR, ArgInfos).
:- pred get_reg_r_arg_loc(arg_loc::out, int::in, int::out) is det.
get_reg_r_arg_loc(ArgLoc, !RegR) :-
ArgLoc = reg(reg_r, !.RegR),
!:RegR = !.RegR + 1.
%---------------------%
make_arg_infos(ModuleInfo, CodeModel, ArgTypes, ArgModes, ArgRegTypes,
ArgInfos) :-
initial_r_regs(CodeModel, FirstInRegR, FirstOutRegR),
FirstInRegF = 1,
FirstOutRegF = 1,
( if
make_arg_infos_loop(ModuleInfo, ArgTypes, ArgModes, ArgRegTypes,
FirstInRegR, FirstInRegF, FirstOutRegR, FirstOutRegF,
ArgInfosPrime)
then
ArgInfos = ArgInfosPrime
else
unexpected($pred, "length mismatch")
).
:- pred make_arg_infos_loop(module_info::in,
list(mer_type)::in, list(mer_mode)::in, list(reg_type)::in,
int::in, int::in, int::in, int::in, list(arg_info)::out) is semidet.
make_arg_infos_loop(_, [], [], [], _, _, _, _, []).
make_arg_infos_loop(ModuleInfo,
[Type | Types], [Mode | Modes], [RegType | RegTypes],
!.InRegR, !.InRegF, !.OutRegR, !.OutRegF, [ArgInfo | ArgInfos]) :-
require_det (
mode_to_top_functor_mode(ModuleInfo, Mode, Type, TopFunctorMode),
(
TopFunctorMode = top_in,
get_arg_loc(RegType, ArgLoc, !InRegR, !InRegF)
;
( TopFunctorMode = top_out
; TopFunctorMode = top_unused
),
get_arg_loc(RegType, ArgLoc, !OutRegR, !OutRegF)
),
ArgInfo = arg_info(ArgLoc, TopFunctorMode)
),
make_arg_infos_loop(ModuleInfo, Types, Modes, RegTypes,
!.InRegR, !.InRegF, !.OutRegR, !.OutRegF, ArgInfos).
:- pred get_arg_loc(reg_type::in, arg_loc::out, int::in, int::out,
int::in, int::out) is det.
get_arg_loc(RegType, ArgLoc, !RegR, !RegF) :-
(
RegType = reg_r,
ArgLoc = reg(reg_r, !.RegR),
!:RegR = !.RegR + 1
;
RegType = reg_f,
ArgLoc = reg(reg_f, !.RegF),
!:RegF = !.RegF + 1
).
%---------------------%
:- pred initial_r_regs(code_model::in, int::out, int::out) is det.
:- pragma inline(pred(initial_r_regs/3)).
initial_r_regs(CodeModel, FirstInRegR, FirstOutRegR) :-
FirstInRegR = 1,
(
CodeModel = model_semi,
FirstOutRegR = 2
;
( CodeModel = model_det
; CodeModel = model_non
),
FirstOutRegR = 1
).
%---------------------------------------------------------------------------%
generic_call_arg_reg_types(ModuleInfo, GenericCall, ArgVars,
MaybeArgRegs, ArgRegTypes) :-
(
GenericCall = higher_order(_, _, _, _, _),
module_info_get_globals(ModuleInfo, Globals),
globals.lookup_bool_option(Globals, use_float_registers, UseFloatRegs),
(
UseFloatRegs = no,
list.duplicate(length(ArgVars), reg_r, ArgRegTypes)
;
UseFloatRegs = yes,
MaybeArgRegs = arg_reg_types(ArgRegs),
ArgRegTypes = list.map(arg_reg_to_reg_type, ArgRegs)
;
UseFloatRegs = yes,
MaybeArgRegs = arg_reg_types_unset,
% This should have been set by the float register wrapper pass.
unexpected($pred, "missing ho_arg_regs")
)
;
% We don't yet use float registers for class method calls.
( GenericCall = class_method(_, _, _, _)
; GenericCall = event_call(_)
; GenericCall = cast(_)
),
list.duplicate(length(ArgVars), reg_r, ArgRegTypes)
).
:- func arg_reg_to_reg_type(ho_arg_reg) = reg_type.
arg_reg_to_reg_type(ho_arg_reg_r) = reg_r.
arg_reg_to_reg_type(ho_arg_reg_f) = reg_f.
%---------------------------------------------------------------------------%
compute_in_and_out_vars(_, _, [], [], [], []).
compute_in_and_out_vars(_, _, [], [_ | _], _, _) :-
unexpected($pred, "length mismatch").
compute_in_and_out_vars(_, _, [_ | _], [], _, _) :-
unexpected($pred, "length mismatch").
compute_in_and_out_vars(ModuleInfo, VarTable,
[Var | Vars], [Mode | Modes],
!:InVars, !:OutVars) :-
compute_in_and_out_vars(ModuleInfo, VarTable, Vars, Modes,
!:InVars, !:OutVars),
lookup_var_entry(VarTable, Var, Entry),
Type = Entry ^ vte_type,
mode_to_top_functor_mode(ModuleInfo, Mode, Type, TopFunctorMode),
(
TopFunctorMode = top_in,
!:InVars = [Var | !.InVars]
;
( TopFunctorMode = top_out
; TopFunctorMode = top_unused
),
!:OutVars = [Var | !.OutVars]
).
%---------------------------------------------------------------------------%
compute_in_and_out_vars_sep_regs(ModuleInfo, VarTable,
Vars, Modes, ArgRegTypes,
!:InVarsR, !:InVarsF, !:OutVarsR, !:OutVarsF) :-
( if
compute_in_and_out_vars_sep_regs_2(ModuleInfo, VarTable,
Vars, Modes, ArgRegTypes,
!:InVarsR, !:InVarsF, !:OutVarsR, !:OutVarsF)
then
true
else
unexpected($pred, "length mismatch")
).
:- pred compute_in_and_out_vars_sep_regs_2(module_info::in,
var_table::in, list(prog_var)::in, list(mer_mode)::in, list(reg_type)::in,
list(prog_var)::out, list(prog_var)::out,
list(prog_var)::out, list(prog_var)::out) is semidet.
compute_in_and_out_vars_sep_regs_2(_, _, [], [], [], [], [], [], []).
compute_in_and_out_vars_sep_regs_2(ModuleInfo, VarTable,
[Var | Vars], [Mode | Modes], [RegType | RegTypes],
!:InVarsR, !:InVarsF, !:OutVarsR, !:OutVarsF) :-
compute_in_and_out_vars_sep_regs_2(ModuleInfo, VarTable,
Vars, Modes, RegTypes, !:InVarsR, !:InVarsF, !:OutVarsR, !:OutVarsF),
require_det (
lookup_var_entry(VarTable, Var, Entry),
Type = Entry ^ vte_type,
mode_to_top_functor_mode(ModuleInfo, Mode, Type, TopFunctorMode),
(
TopFunctorMode = top_in,
(
RegType = reg_r,
!:InVarsR = [Var | !.InVarsR]
;
RegType = reg_f,
!:InVarsF = [Var | !.InVarsF]
)
;
( TopFunctorMode = top_out
; TopFunctorMode = top_unused
),
(
RegType = reg_r,
!:OutVarsR = [Var | !.OutVarsR]
;
RegType = reg_f,
!:OutVarsF = [Var | !.OutVarsF]
)
)
).
%---------------------------------------------------------------------------%
partition_args(Args, Ins, Outs) :-
partition_args(Args, Ins, Outs0, Unuseds),
list.append(Outs0, Unuseds, Outs).
partition_args([], [], [], []).
partition_args([Var - ArgInfo | VarsArgInfos], !:Ins, !:Outs, !:Unuseds) :-
partition_args(VarsArgInfos, !:Ins, !:Outs, !:Unuseds),
ArgInfo = arg_info(_, ArgMode),
(
ArgMode = top_in,
!:Ins = [Var - ArgInfo | !.Ins]
;
ArgMode = top_out,
!:Outs = [Var - ArgInfo | !.Outs]
;
ArgMode = top_unused,
!:Unuseds = [Var - ArgInfo | !.Unuseds]
).
%---------------------------------------------------------------------------%
partition_proc_args(ModuleInfo, ProcInfo, Inputs, Outputs, Unuseds) :-
proc_info_get_headvars(ProcInfo, Vars),
proc_info_get_argmodes(ProcInfo, Modes),
proc_info_get_var_table(ProcInfo, VarTable),
lookup_var_types(VarTable, Vars, Types),
do_partition_proc_args(ModuleInfo, Vars, Types, Modes,
Inputs, Outputs, Unuseds).
partition_proc_call_args(ModuleInfo, ProcInfo, VarTable, Vars,
Inputs, Outputs, Unuseds) :-
proc_info_get_argmodes(ProcInfo, Modes),
lookup_var_types(VarTable, Vars, Types),
do_partition_proc_args(ModuleInfo, Vars, Types, Modes,
Inputs, Outputs, Unuseds).
partition_generic_call_args(ModuleInfo, Vars, Types, Modes,
Inputs, Outputs, Unuseds) :-
do_partition_proc_args(ModuleInfo, Vars, Types, Modes,
Inputs, Outputs, Unuseds).
:- pred do_partition_proc_args(module_info::in, list(prog_var)::in,
list(mer_type)::in, list(mer_mode)::in, set(prog_var)::out,
set(prog_var)::out, set(prog_var)::out) is det.
do_partition_proc_args(ModuleInfo, Vars, Types, Modes,
!:Inputs, !:Outputs, !:Unuseds) :-
( if
partition_proc_args_2(ModuleInfo, Vars, Types, Modes,
set.init, !:Inputs, set.init, !:Outputs,
set.init, !:Unuseds)
then
true
else
unexpected($pred, "list length mismatch")
).
:- pred partition_proc_args_2(module_info::in, list(prog_var)::in,
list(mer_type)::in, list(mer_mode)::in,
set(prog_var)::in, set(prog_var)::out,
set(prog_var)::in, set(prog_var)::out,
set(prog_var)::in, set(prog_var)::out) is semidet.
partition_proc_args_2(_ModuleInfo, [], [], [],
!Inputs, !Outputs, !Unuseds).
partition_proc_args_2(ModuleInfo, [Var | Vars], [Type | Types], [Mode | Modes],
!Inputs, !Outputs, !Unuseds) :-
require_det (
mode_to_top_functor_mode(ModuleInfo, Mode, Type, TopFunctorMode),
(
TopFunctorMode = top_in,
set.insert(Var, !Inputs)
;
TopFunctorMode = top_out,
set.insert(Var, !Outputs)
;
TopFunctorMode = top_unused,
set.insert(Var, !Unuseds)
)
),
partition_proc_args_2(ModuleInfo, Vars, Types, Modes,
!Inputs, !Outputs, !Unuseds).
%---------------------------------------------------------------------------%
:- end_module hlds.arg_info.
%---------------------------------------------------------------------------%