%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2017 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: ml_args_util.m.
%
% This module contains utility predicates that classify and process
% predicate arguments.
%
%---------------------------------------------------------------------------%

:- module ml_backend.ml_args_util.
:- interface.

:- import_module hlds.
:- import_module hlds.code_model.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module hlds.hlds_rtti.
:- import_module mdbcomp.
:- import_module mdbcomp.prim_data.
:- import_module ml_backend.ml_gen_info.
:- import_module ml_backend.mlds.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.var_table.

:- import_module assoc_list.
:- import_module bool.
:- import_module list.
:- import_module map.

%---------------------------------------------------------------------------%
%
% Various utility routines used for MLDS code generation.
%

    % Append an appropriate `return' statement for the given code_model
    % and returning the given lvals, if needed.
    %
:- pred ml_append_return_statement(code_model::in, prog_context::in,
    list(mlds_rval)::in, list(mlds_stmt)::in, list(mlds_stmt)::out) is det.

%---------------------------------------------------------------------------%
%
% Routines for generating function declarations (i.e. mlds_func_params).
%

% Note that when generating function *definitions*, the versions that take
% an ml_gen_info pair should be used, since those are the only ones that will
% generate the correct GC tracing code for the parameters.

:- type copy_out_when
    --->    copy_out_never
    ;       copy_out_only_last_arg
    ;       copy_out_always.

    % Decide how the interface of a procedure should handle output arguments.
    %
    % One way we can handle an output argument is to return it
    % via a return statement. The MLDS code generator calls this
    % a "copy out" output argument.
    %
    % The other way is to have the caller pass a pointer for the output arg,
    % and have the callee return the value by assigning through that pointer.
    % The MLDS code generator calls this a "byref" output argument.
    %
    % This function tells its caller for which set of a procedure's output
    % arguments it should use the first approach: for none (copy_out_never),
    % for the last one only (copy_out_only_last_arg), or for all of them
    % (copy_out_always).
    %
:- func compute_when_to_copy_out(bool, code_model, pred_or_func)
    = copy_out_when.

    % A tuple containing the information about a procedure argument
    % that most code that processes such arguments needs to know.
    %
:- type var_mvar_type_mode
    --->    var_mvar_type_mode(
                % The argument in the HLDS.
                prog_var,

                % The argument in the MLDS.
                mlds_local_var_name,

                % The type of the (formal) argument from the procedure
                % declaration. (Note that the type of the *actual* argument
                % inside the body of the caller may be more specific.)
                mer_type,

                % Is the arg input, output, or neither?
                top_functor_mode
            ).

    % Generate the function prototype for the given procedure
    % without filling in the gc statement field of the input parameters.
    %
:- pred ml_gen_proc_params_no_gc_stmts(module_info::in, pred_proc_id::in,
    list(var_mvar_type_mode)::out, mlds_func_params::out) is det.

    % Generate the function prototype for the given procedure, filling in
    % the gc statement slots, and return
    %
    % (a) the list of nondummy by-ref output arguments, and
    % (b) the list of nondummy copied output arguments.
    %
:- pred ml_gen_info_proc_params(pred_proc_id::in,
    list(var_mvar_type_mode)::out, mlds_func_params::out,
    list(prog_var)::out, list(prog_var)::out,
    ml_gen_info::in, ml_gen_info::out) is det.

    % Generate the function prototype for *only the input arguments*
    % of the given procedure.
    %
:- func ml_gen_proc_params_inputs_only_no_gc_stmts(module_info, pred_proc_id)
    = list(mlds_argument).

    % As above, but from the rtti_proc_id rather than from the module_info,
    % pred_id, and proc_id.
    %
:- func ml_gen_proc_params_from_rtti_no_gc_stmts(module_info, rtti_proc_label)
    = mlds_func_params.

    % Generate the function prototype for a procedure with the
    % given argument names, types, modes, and code model.
    %
:- pred ml_gen_params_no_gc_stmts(module_info::in, pred_or_func::in,
    code_model::in, list(prog_var)::in, list(mlds_local_var_name)::in,
    list(mer_type)::in, list(mer_mode)::in,
    list(var_mvar_type_mode)::out, mlds_func_params::out) is det.

    % ml_gen_tscc_arg_params(ModuleInfo, PredOrFunc, CodeModel, Context,
    %     ProcIdInTscc, VarTable, Vars, Types, Modes, ArgTuples, !OutArgNames,
    %     TsccInArgs, FuncParams, ReturnRvalsTypes, OutVarsTypes,
    %     OwnLocalVarDefns, TsccInLocalVarDefns, TsccOutLocalVarDefns,
    %     CopyTsccInToOwnStmts, CopyOwnToTsccOutStmts,
    %     CopyOutValThroughPtrStmts) :-
    %
    % For background and terminology used in this predicate and its
    % description, see notes/mlds_tail_recursion.html.
    %
    % The inputs of this predicate describe a procedure in a TSCC
    % (PredOrFunc, CodeModel, Context, ProcIdInTscc, VarTable) and its argument
    % list (Vars, Types, and Modes). The outputs return information about the
    % arguments in a more conveniently packaged form (ArgTuples), as well as
    % the information the code generator needs to generate parameter passing
    % code both for the wrapped procedures and the container procedures.
    % Specifically, we return information that is needed
    %
    % - to generate code for tail calls to this procedure, from other
    %   procedures in the TSCC as well from itself,
    % - to generate the signature of container procedures,
    % - to generate the declarations at the start of container procedures,
    % - to generate code at the start of the wrapped procedure for copying
    %   input arguments from the tscc variables in which the TSCC gets them
    %   to the procedure's own variables,
    % - to generate code at the end of the wrapped procedure for copying
    %   output arguments from the procedure's own variables to the variables
    %   in which the TSCC expects them, and
    % - to generate code at the end of the container procedure to return
    %   the output arguments to the caller.
    %
    % The info about tail calls is needed before we can start generating code
    % for *any* of the procedures in the TSCC.
    %
    % (Arguments whose type is a dummy type or whose mode is "unused"
    % don't participate in parameter passing, so this predicate ignores them.
    % All the discussion below uses "arguments" as shorthand for "arguments
    % of non-dummy types whose modes are not unused.)
    %
    % For each input argument, this predicate creates a corresponding
    % lvn_tscc_proc_input_var, while for each output argument, it creates
    % a corresponding lvn_tscc_output_var. Model semi procedures have
    % a success indication as an extra output, and for such procedures
    % we create a corresponding lvn_tscc_output_var_succeeded variable.
    % We call the MLDS variables representing the procedure's input and
    % output HLDS variables the procedure's own variables, while we call
    % the lvn_tscc_proc_input_vars and lvn_tscc_output_vars (including
    % lvn_tscc_output_var_succeededs) the TSCC in and out variables
    % respectively.
    %
    % Wrapped procedures always get their inputs in TSCC in variables
    % (both from the caller and from each other at tail calls), and they
    % always put their results in the TSCC out variables. For output
    % arguments passed by reference, this predicate will create an
    % lvn_tscc_output_var_ptr variable (the argument's TSCC out ptr variable)
    % to hold the address of the memory location where the value of the
    % argument should be put on return.
    %
    % Both lvn_tscc_proc_input_vars and lvn_tscc_output_vars include
    % an argument sequence number, with separate sequences for input and
    % output arguments, with both sequences starting at 1. These variables
    % also have names (to make the code easier to read). The input variables
    % are specific to a procedure, so they can (and do) take their name
    % from the name of the corresponding head variable. The output variables
    % are shared between all the procedures, so they take their names
    % from the name of the corresponding head variable in the first procedure
    % in the TSCC. These names are stored in !OutArgNames across calls to
    % ml_gen_tscc_arg_params.
    %
    % The value we return in TsccInArgs will be a list of the
    % lvn_tscc_proc_input_vars of the input arguments only. Every tail call
    % will assign a value to every lvn_tscc_proc_input_var of the callee.
    %
    % The next three parameters, FuncParams, ReturnRvalsTypes and OutArgNames,
    % describe the signature of the container functions. FuncParams is intended
    % to be put directly into the func_params slot in the container
    % procedure's mlds_function: it lists the names and types of the arguments
    % (which will be the procedure's TSCC in variables, and the TSCC out ptr
    % variables of the output arguments returned by reference) in declaration
    % order, and the types of the output arguments that are returned by value
    % (including the success indicator for model semi procedures).
    % ReturnRvalsTypes is intended to be put almost directly into the return
    % statements of container functions. It gives the rvals and types of the
    % output args returned by value, with the rvals being just the values of
    % their TSCC out variables. The value in OutVarsTypes just lists the names
    % of all the TSCC out variables together with their types. It is not meant
    % to be used in the construction of any MLDS construct; it is meant purely
    % to be used in a sanity check. Every procedure in a TSCC should get
    % the exact same OutVarsTypes value; any mismatch indicates a compiler bug.
    % The reason why OutVarsTypes is separate from FuncParams is that it is
    % possible for two procedures in the TSCC to return the exact same vector
    % of arguments (so that they can tail call each other), but not return
    % those arguments the same way. This can happen when one procedure comes
    % from a predicate (which in C returns all its outputs via reference),
    % while another comes from a function (which in C returns its last
    % argument, the result argument, via the return value).
    %
    % When generating code for a procedure that is *not* in a TSCC,
    % the declaration of its MLDS function would define the MLDS variables
    % of the some of the procedure's arguments (specifically, the inputs).
    % The signatures of MLDS container functions defining a procedure in a TSCC
    % never define any of the procedure's own variables of the arguments;
    % they all need to be defined at the top of the container function.
    % OwnLocalVarDefns will contain these definitions.
    %
    % The values of TsccInLocalVarDefns and TsccOutLocalVarDefns are also
    % destined for the top of container functions; they will contain
    % the local variable definitions of the TSCC in and out variables
    % respectively.
    %
    % For container function's argument list will define the TSCC in variables
    % of the procedure that the container function is for, but the top of
    % that container function must define the TSCC in variables of all the
    % *other* procedures in the TSCC. (This is how we ensure that each
    % lvn_tscc_proc_input_var of each TSCC procedure will be defined by
    % exactly one of TsccInLocalVarDefns and FuncParams in each container
    % function.)
    %
    % The TSCC out variables (which are *not* specific to a given procedure
    % in the TSCC) are never defined in container function signatures.
    % If an output argument is returned by reference, the argument list
    % contain the TSCC out ptr variable, not the TSCC out variable, while
    % if the argument is returned by value, then only its type will be listed
    % in the return types part of the signature. This is why the top of the
    % container function must include the contents of TsccOutLocalVarDefns.
    %
    % Each wrapped procedure starts by copying the values of its TSCC in
    % variables to their corresponding own variables; CopyTsccInToOwnStmts
    % contains this code. Each wrapped procedure ends by copying the values
    % of its own variables (including the success indicator, if present)
    % to the corresponding TSCC out variables; CopyOwnToTsccOutStmts
    % contains this code. If any output arguments are to be returned
    % via reference, then the epilogue of the container function must copy
    % the values of those arguments from their TSCC out variables to the
    % address pointed to by their TSCC out ptr variables;
    % CopyOutValThroughPtrStmts contains this code.
    %
:- pred ml_gen_tscc_arg_params(module_info::in, pred_or_func::in,
    code_model::in, prog_context::in, proc_id_in_tscc::in, var_table::in,
    list(prog_var)::in, list(mer_type)::in, list(mer_mode)::in,
    list(var_mvar_type_mode)::out,
    map(int, string)::in, map(int, string)::out,
    list(mlds_argument)::out, mlds_func_params::out,
    assoc_list(mlds_rval, mlds_type)::out,
    assoc_list(mlds_local_var_name, mlds_type)::out,
    list(mlds_local_var_defn)::out, list(mlds_local_var_defn)::out,
    list(mlds_local_var_defn)::out,
    list(mlds_stmt)::out, list(mlds_stmt)::out, list(mlds_stmt)::out) is det.

%---------------------------------------------------------------------------%
%
% Generate code for parameter passing.
%

    % The ml_gen_args predicate allows the argument being passed to a callee
    % procedure may be specified in one of two ways.
    %
    % The usual way is to simply specify the HLDS variable being passed.
    %
    % When creating closure wrappers, not all variables being passed
    % come for the HLDS; some come from the closure. In such cases,
    % we specify the MLDS local variable being passed, its lval, and its type.
    %
    % The ml_gen_args predicate and its subcontractors all require that
    %
    % - either all arguments are arg_not_for_closure_wrapper,
    % - or all arguments are arg_for_closure_wrapper.
    %
:- type ml_call_arg
    --->    arg_not_for_closure_wrapper(prog_var)
    ;       arg_for_closure_wrapper(mlds_local_var_name, mlds_lval, mer_type).

:- inst not_fcw for ml_call_arg/0
    --->    arg_not_for_closure_wrapper(ground).
:- inst fcw for ml_call_arg/0
    --->    arg_for_closure_wrapper(ground, ground, ground).

:- func wrap_plain_not_fcw_arg(prog_var::in)
    = (ml_call_arg::out(not_fcw)) is det.
:- func wrap_plain_not_fcw_args(list(prog_var)::in)
    = (list(ml_call_arg)::out(list_skel(not_fcw))) is det.

:- type what_params
    --->    input_params_only
    ;       input_and_output_params.

    % Generate rvals and lvals for the arguments of a procedure call.
    %
:- pred ml_gen_args(pred_or_func, code_model, prog_context, what_params,
    list(mer_type), list(mer_mode), list(ml_call_arg),
    list(mlds_rval), assoc_list(mlds_lval, mlds_type),
    list(mlds_local_var_defn), list(mlds_stmt), ml_gen_info, ml_gen_info).
:- mode ml_gen_args(in, in, in, in, in, in, in(list_skel(not_fcw)),
    out, out, out, out, in, out) is det.
:- mode ml_gen_args(in, in, in, in, in, in, in(list_skel(fcw)),
    out, out, out, out, in, out) is det.

%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%

:- implementation.

:- import_module check_hlds.
:- import_module check_hlds.mode_top_functor.
:- import_module check_hlds.type_util.
:- import_module libs.
:- import_module libs.globals.
:- import_module libs.options.
:- import_module mdbcomp.program_representation.
:- import_module ml_backend.ml_accurate_gc.
:- import_module ml_backend.ml_code_util.
:- import_module parse_tree.prog_data_foreign.
:- import_module parse_tree.prog_type.

:- import_module int.
:- import_module maybe.
:- import_module pair.
:- import_module require.
:- import_module term.
:- import_module term_context.

%---------------------------------------------------------------------------%
%
% Code for various utility routines.
%

ml_append_return_statement(CodeModel, Context, CopiedOutputVarRvals, !Stmts) :-
    (
        CodeModel = model_semi,
        SucceededVar = lvn_comp_var(lvnc_succeeded),
        SucceededLval = ml_local_var(SucceededVar, mlds_native_bool_type),
        SucceededRval = ml_lval(SucceededLval),
        ReturnedRvals = [SucceededRval | CopiedOutputVarRvals],
        ReturnStmt = ml_stmt_return(ReturnedRvals, Context),
        !:Stmts = !.Stmts ++ [ReturnStmt]
    ;
        CodeModel = model_det,
        (
            CopiedOutputVarRvals = [_ | _],
            ReturnStmt = ml_stmt_return(CopiedOutputVarRvals, Context),
            !:Stmts = !.Stmts ++ [ReturnStmt]
        ;
            CopiedOutputVarRvals = []
            % This return statement is not needed when we generate code
            % for a standalone HLDS procedure, since the end of the MLDS
            % function acts as an implicit return. It *is* needed when the
            % MLDS function also contains the code of other HLDS procedures
            % in the same TSCC, but that case is handled in ml_proc_gen.m.
            %
            % Note that adding a return statement after the body of a procedure
            % that throws an exception may, if this fact is visible to the
            % target language compiler, cause that compiler to generate an
            % error. The Java compiler does this when the body of a HLDS
            % procedure is defined by Java code that does a throw.
        )
    ;
        CodeModel = model_non
    ).

%---------------------------------------------------------------------------%

compute_when_to_copy_out(CopyOut, CodeModel, PredOrFunc) = CopyOutWhen :-
    (
        CopyOut = yes,
        CopyOutWhen = copy_out_always
    ;
        CopyOut = no,
        ( if
            CodeModel = model_det,
            PredOrFunc = pf_function
        then
            % Return the result argument (i.e. the last argument)
            % of a procedure if that procedure is a det function.
            CopyOutWhen = copy_out_only_last_arg
        else
            CopyOutWhen = copy_out_never
        )
    ).

%---------------------------------------------------------------------------%
%
% Code for generating function declarations (i.e. mlds_func_params).
%

:- pred get_raw_data_for_proc_params(module_info::in, pred_proc_id::in,
    pred_info::out, pred_or_func::out, code_model::out,
    list(var_mvar_type_mode)::out) is det.
% Some of our callers don't need all our outputs.
:- pragma inline(pred(get_raw_data_for_proc_params/6)).

get_raw_data_for_proc_params(ModuleInfo, PredProcId, PredInfo,
        PredOrFunc, CodeModel, ArgTuples) :-
    module_info_pred_proc_info(ModuleInfo, PredProcId, PredInfo, ProcInfo),
    PredOrFunc = pred_info_is_pred_or_func(PredInfo),
    CodeModel = proc_info_interface_code_model(ProcInfo),
    proc_info_get_var_table(ProcInfo, VarTable),
    proc_info_get_headvars(ProcInfo, HeadVars),
    pred_info_get_arg_types(PredInfo, HeadTypes),
    proc_info_get_argmodes(ProcInfo, HeadModes),
    package_vars_types_modes(ModuleInfo, VarTable,
        HeadVars, HeadTypes, HeadModes, ArgTuples).

:- pred package_vars_types_modes(module_info::in, var_table::in,
    list(prog_var)::in, list(mer_type)::in, list(mer_mode)::in,
    list(var_mvar_type_mode)::out) is det.

package_vars_types_modes(ModuleInfo, VarTable, Vars, Types, Modes,
        ArgTuples) :-
    ( if
        Vars = [],
        Types = [],
        Modes = []
    then
        ArgTuples = []
    else if
        Vars = [HeadVar | TailVars],
        Types = [HeadType | TailTypes],
        Modes = [HeadMode | TailModes]
    then
        package_vars_types_modes(ModuleInfo, VarTable,
            TailVars, TailTypes, TailModes, TailArgTuples),
        mode_to_top_functor_mode(ModuleInfo, HeadMode, HeadType,
            HeadTopFunctorMode),
        lookup_var_entry(VarTable, HeadVar, HeadVarEntry),
        HeadMLDSVarName = ml_gen_local_var_name(HeadVar, HeadVarEntry),
        HeadArgTuple = var_mvar_type_mode(HeadVar, HeadMLDSVarName, HeadType,
            HeadTopFunctorMode),
        ArgTuples = [HeadArgTuple | TailArgTuples]
    else
        unexpected($pred, "length mismatch")
    ).

:- pred package_vars_mvars_types_modes(module_info::in, list(prog_var)::in,
    list(mlds_local_var_name)::in, list(mer_type)::in, list(mer_mode)::in,
    list(var_mvar_type_mode)::out) is det.

package_vars_mvars_types_modes(ModuleInfo,
        Vars, MLDSVarNames, Types, Modes, ArgTuples) :-
    ( if
        Vars = [],
        MLDSVarNames = [],
        Types = [],
        Modes = []
    then
        ArgTuples = []
    else if
        Vars = [HeadVar | TailVars],
        MLDSVarNames = [HeadMLDSVarName | TailMLDSVarNames],
        Types = [HeadType | TailTypes],
        Modes = [HeadMode | TailModes]
    then
        package_vars_mvars_types_modes(ModuleInfo, TailVars, TailMLDSVarNames,
            TailTypes, TailModes, TailArgTuples),
        mode_to_top_functor_mode(ModuleInfo, HeadMode, HeadType,
            HeadTopFunctorMode),
        HeadArgTuple = var_mvar_type_mode(HeadVar, HeadMLDSVarName, HeadType,
            HeadTopFunctorMode),
        ArgTuples = [HeadArgTuple | TailArgTuples]
    else
        unexpected($pred, "length mismatch")
    ).

:- pred package_rtti_vars_types_topmodes(
    assoc_list(prog_var, prog_var_name)::in,
    list(mer_type)::in, list(top_functor_mode)::in,
    list(var_mvar_type_mode)::out) is det.

package_rtti_vars_types_topmodes(VarPairs, Types, TopFunctorModes,
        ArgTuples) :-
    ( if
        VarPairs = [],
        Types = [],
        TopFunctorModes = []
    then
        ArgTuples = []
    else if
        VarPairs = [HeadVarPair | TailVarPairs],
        Types = [HeadType | TailTypes],
        TopFunctorModes = [HeadTopFunctorMode | TailTopFunctorModes]
    then
        package_rtti_vars_types_topmodes(TailVarPairs, TailTypes,
            TailTopFunctorModes, TailArgTuples),
        HeadVarPair = HeadVar - HeadVarName,
        term.var_to_int(HeadVar, HeadVarNum),
        HeadMLDSVarName = lvn_prog_var(HeadVarName, HeadVarNum),
        HeadArgTuple = var_mvar_type_mode(HeadVar, HeadMLDSVarName, HeadType,
            HeadTopFunctorMode),
        ArgTuples = [HeadArgTuple | TailArgTuples]
    else
        unexpected($pred, "length mismatch")
    ).

%---------------------%

ml_gen_proc_params_no_gc_stmts(ModuleInfo, PredProcId,
        ArgTuples, FuncParams) :-
    get_raw_data_for_proc_params(ModuleInfo, PredProcId, _PredInfo,
        PredOrFunc, CodeModel, ArgTuples),
    ml_gen_params_base(ModuleInfo, PredOrFunc, CodeModel,
        input_and_output_params, ArgTuples, FuncParams, _ByRef, _Copied,
        no, _).

ml_gen_info_proc_params(PredProcId, ArgTuples, FuncParams,
        ByRefOutputVars, CopiedOutputVars, !Info) :-
    ml_gen_info_get_module_info(!.Info, ModuleInfo),
    get_raw_data_for_proc_params(ModuleInfo, PredProcId, PredInfo,
        PredOrFunc, CodeModel, ArgTuples),
    % We must not generate GC tracing code for no_type_info_builtin procedures,
    % because the generated GC tracing code would refer to type_infos
    % that don't get passed.
    PredModule = pred_info_module(PredInfo),
    PredName = pred_info_name(PredInfo),
    pred_info_get_orig_arity(PredInfo, PredFormArity),
    PredFormArity = pred_form_arity(PredFormArityInt),
    ( if no_type_info_builtin(PredModule, PredName, PredFormArityInt) then
        ml_gen_params_base(ModuleInfo, PredOrFunc, CodeModel,
            input_and_output_params, ArgTuples, FuncParams,
            ByRefOutputVars, CopiedOutputVars, no, _)
    else
        ml_gen_params_base(ModuleInfo, PredOrFunc, CodeModel,
            input_and_output_params, ArgTuples, FuncParams,
            ByRefOutputVars, CopiedOutputVars, yes(!.Info), yes(!:Info))
    ).

ml_gen_proc_params_inputs_only_no_gc_stmts(ModuleInfo, PredProcId)
        = FuncArgs :-
    get_raw_data_for_proc_params(ModuleInfo, PredProcId, _PredInfo,
        _PredOrFunc, _CodeModel, ArgTuples),
    ml_gen_arg_decls(ModuleInfo, copy_out_never, input_params_only, ArgTuples,
        FuncArgs, RetTypes, _ByRef, _Copied, no, _),
    expect(unify(RetTypes, []), $pred, "RetTypes != []").

ml_gen_proc_params_from_rtti_no_gc_stmts(ModuleInfo, RttiProcId)
        = FuncParams :-
    PredOrFunc = RttiProcId ^ rpl_pred_or_func,
    Detism = RttiProcId ^ rpl_proc_interface_detism,
    determinism_to_code_model(Detism, CodeModel),
    HeadVars = RttiProcId ^ rpl_proc_headvars,
    ArgTypes = RttiProcId ^ rpl_proc_arg_types,
    TopFunctorModes = RttiProcId ^ rpl_proc_top_modes,
    package_rtti_vars_types_topmodes(HeadVars, ArgTypes, TopFunctorModes,
        ArgTuples),
    ml_gen_params_base(ModuleInfo, PredOrFunc, CodeModel,
        input_and_output_params, ArgTuples, FuncParams, _ByRef, _Copied,
        no, _).

ml_gen_params_no_gc_stmts(ModuleInfo, PredOrFunc, CodeModel,
        HeadVars, HeadMLDSVarNames, HeadTypes, HeadModes,
        ArgTuples, FuncParams) :-
    package_vars_mvars_types_modes(ModuleInfo, HeadVars, HeadMLDSVarNames,
        HeadTypes, HeadModes, ArgTuples),
    ml_gen_params_base(ModuleInfo, PredOrFunc, CodeModel,
        input_and_output_params, ArgTuples, FuncParams, _ByRef, _Copied,
        no, _).

:- inst is_in for what_params/0
    --->    input_params_only.
:- inst is_inout for what_params/0
    --->    input_and_output_params.

:- inst is_no for maybe/1
    --->    no.
:- inst is_yes for maybe/1
    --->    yes(ground).

    % The mlds_arguments in the generated parameters have the gc stmt field
    % filled in only if !.MaybeInfo is yes(_).
    %
:- pred ml_gen_params_base(module_info, pred_or_func, code_model, what_params,
    list(var_mvar_type_mode), mlds_func_params,
    list(prog_var), list(prog_var), maybe(ml_gen_info), maybe(ml_gen_info)).
:- mode ml_gen_params_base(in, in, in, in(is_in), in, out, out, out,
    in(is_no), out(is_no)) is det.
:- mode ml_gen_params_base(in, in, in, in(is_inout), in, out, out, out,
    in(is_no), out(is_no)) is det.
:- mode ml_gen_params_base(in, in, in, in(is_inout), in, out, out, out,
    in(is_yes), out(is_yes)) is det.

ml_gen_params_base(ModuleInfo, PredOrFunc, CodeModel, WhatParams, ArgTuples,
        FuncParams, ByRefOutputVars, CopiedOutputVars, !MaybeInfo) :-
    module_info_get_globals(ModuleInfo, Globals),
    CopyOut = get_copy_out_option(Globals, CodeModel),
    CopyOutWhen = compute_when_to_copy_out(CopyOut, CodeModel, PredOrFunc),
    ml_gen_arg_decls(ModuleInfo, CopyOutWhen, WhatParams, ArgTuples,
        FuncArgs0, RetTypes0, ByRefOutputVars, CopiedOutputVars, !MaybeInfo),
    (
        CodeModel = model_det,
        FuncArgs = FuncArgs0,
        RetTypes = RetTypes0
    ;
        CodeModel = model_semi,
        % For model_semi procedures, return a bool.
        FuncArgs = FuncArgs0,
        RetTypes = [mlds_native_bool_type | RetTypes0]
    ;
        CodeModel = model_non,
        % For model_non procedures, we return values by passing them
        % to the continuation.
        (
            CopyOut = yes,
            ContType = mlds_cont_type(RetTypes0),
            RetTypes = []
        ;
            CopyOut = no,
            ContType = mlds_cont_type([]),
            RetTypes = RetTypes0
        ),
        ContVarName = lvn_comp_var(lvnc_cont),
        % The cont variable always points to code, not to the heap,
        % so the GC never needs to trace it.
        ContArg = mlds_argument(ContVarName, ContType, gc_no_stmt),
        ContEnvType = mlds_generic_env_ptr_type,
        ContEnvVarName = lvn_comp_var(lvnc_cont_env_ptr),
        % When targeting C, we always allocate continuation environments
        % on the stack. Since cont_env_ptr points to the stack, accurage GC
        % does not need to trace it.
        % When targeting C# or Java, we use the target's builtin GC,
        % so again, there is no need for any info meant for our own
        % accurate GC system.
        ContEnvArg = mlds_argument(ContEnvVarName, ContEnvType, gc_no_stmt),
        FuncArgs = FuncArgs0 ++ [ContArg, ContEnvArg]
    ),
    FuncParams = mlds_func_params(FuncArgs, RetTypes).

    % Given the argument variable names, and corresponding lists of their
    % types and modes, generate the MLDS argument declarations
    % and return types.
    %
    % The generated mlds_arguments have the gc stmt field filled in
    % only if !.MaybeInfo is yes(_).
    %
:- pred ml_gen_arg_decls(module_info, copy_out_when, what_params,
    list(var_mvar_type_mode), list(mlds_argument), mlds_return_types,
    list(prog_var), list(prog_var), maybe(ml_gen_info), maybe(ml_gen_info)).
:- mode ml_gen_arg_decls(in, in, in(is_in), in, out, out, out, out,
    in(is_no), out(is_no)) is det.
:- mode ml_gen_arg_decls(in, in, in(is_inout), in, out, out, out, out,
    in(is_no), out(is_no)) is det.
:- mode ml_gen_arg_decls(in, in, in(is_inout), in, out, out, out, out,
    in(is_yes), out(is_yes)) is det.

ml_gen_arg_decls(_, _, _, [], [], [], [], [], !MaybeInfo).
ml_gen_arg_decls(ModuleInfo, CopyOutWhen, WhatParams, [HeadTuple | TailTuples],
        FuncArgs, RetTypes, ByRefOutputVars, CopiedOutputVars, !MaybeInfo) :-
    ml_gen_arg_decls(ModuleInfo, CopyOutWhen, WhatParams, TailTuples,
        TailFuncArgs, TailRetTypes, TailByRefOutputVars, TailCopiedOutputVars,
        !MaybeInfo),

    HeadTuple = var_mvar_type_mode(HeadVar, HeadMLDSVar, HeadType, HeadMode),
    HeadIsDummy = is_type_a_dummy(ModuleInfo, HeadType),
    (
        HeadIsDummy = is_dummy_type,
        % Exclude types such as io.state, etc.
        FuncArgs = TailFuncArgs,
        RetTypes = TailRetTypes,
        ByRefOutputVars = TailByRefOutputVars,
        CopiedOutputVars = TailCopiedOutputVars
    ;
        HeadIsDummy = is_not_dummy_type,
        HeadMLDS_Type = mercury_type_to_mlds_type(ModuleInfo, HeadType),
        (
            HeadMode = top_unused,
            % Also exclude values with arg_mode `top_unused'.
            FuncArgs = TailFuncArgs,
            RetTypes = TailRetTypes,
            ByRefOutputVars = TailByRefOutputVars,
            CopiedOutputVars = TailCopiedOutputVars
        ;
            HeadMode = top_in,
            % For inputs, generate an argument.
            HeadMLDS_ArgType = HeadMLDS_Type,
            ml_gen_arg_decl(HeadMLDSVar, HeadType, HeadMLDS_ArgType,
                HeadFuncArg, !MaybeInfo),
            FuncArgs = [HeadFuncArg | TailFuncArgs],
            RetTypes = TailRetTypes,
            ByRefOutputVars = TailByRefOutputVars,
            CopiedOutputVars = TailCopiedOutputVars
        ;
            HeadMode = top_out,
            (
                WhatParams = input_params_only,
                FuncArgs = TailFuncArgs,
                RetTypes = TailRetTypes,
                ByRefOutputVars = TailByRefOutputVars,
                CopiedOutputVars = TailCopiedOutputVars
            ;
                WhatParams = input_and_output_params,
                ( if
                    (
                        CopyOutWhen = copy_out_only_last_arg,
                        TailTuples = []
                    ;
                        CopyOutWhen = copy_out_always
                    )
                then
                    % For by-value outputs, generate a return type.
                    FuncArgs = TailFuncArgs,
                    RetTypes = [HeadMLDS_Type | TailRetTypes],
                    ByRefOutputVars = TailByRefOutputVars,
                    CopiedOutputVars = [HeadVar | TailCopiedOutputVars]
                else
                    % For by-reference outputs, generate an argument.
                    HeadMLDS_ArgType = mlds_ptr_type(HeadMLDS_Type),
                    ml_gen_arg_decl(HeadMLDSVar, HeadType, HeadMLDS_ArgType,
                        HeadFuncArg, !MaybeInfo),
                    FuncArgs = [HeadFuncArg | TailFuncArgs],
                    RetTypes = TailRetTypes,
                    ByRefOutputVars = [HeadVar | TailByRefOutputVars],
                    CopiedOutputVars = TailCopiedOutputVars
                )
            )
        )
    ).

    % Given an argument variable, its actual type and its arg type,
    % generate an MLDS argument declaration for it.
    %
    % The generated mlds_argument has the gc stmt field filled in
    % only if !.MaybeInfo is yes(_).
    %
:- pred ml_gen_arg_decl(mlds_local_var_name, mer_type, mlds_type,
    mlds_argument, maybe(ml_gen_info), maybe(ml_gen_info)).
:- mode ml_gen_arg_decl(in, in, in, out, in(is_no), out(is_no)) is det.
:- mode ml_gen_arg_decl(in, in, in, out, in(is_yes), out(is_yes)) is det.

ml_gen_arg_decl(Var, Type, MLDS_ArgType, FuncArg, !MaybeInfo) :-
    (
        !.MaybeInfo = yes(Info0),
        % XXX We should fill in the context properly.
        ml_gen_gc_statement(Var, Type, dummy_context, GCStmt, Info0, Info),
        !:MaybeInfo = yes(Info)
    ;
        !.MaybeInfo = no,
        GCStmt = gc_no_stmt,
        !:MaybeInfo = no
    ),
    FuncArg = mlds_argument(Var, MLDS_ArgType, GCStmt).

%---------------------%

ml_gen_tscc_arg_params(ModuleInfo, PredOrFunc, CodeModel, Context,
        ProcIdInTscc, VarTable, Vars, Types, Modes, ArgTuples, !OutArgNames,
        TsccInArgs, FuncParams, ReturnRvalsTypes, OutVarsTypes,
        OwnLocalVarDefns, TsccInLocalVarDefns, TsccOutLocalVarDefns,
        CopyTsccInToOwnStmts, CopyOwnToTsccOutStmts,
        CopyOutValThroughPtrStmts) :-
    module_info_get_globals(ModuleInfo, Globals),
    CopyOut = get_copy_out_option(Globals, CodeModel),
    CopyOutWhen = compute_when_to_copy_out(CopyOut, CodeModel, PredOrFunc),
    package_vars_types_modes(ModuleInfo, VarTable, Vars, Types, Modes,
        ArgTuples),
    NextInArgNum0 = 1,
    NextOutArgNum0 = 1,
    ml_gen_tscc_arg_decls(ModuleInfo, CopyOutWhen, Context,
        ProcIdInTscc, ArgTuples, NextInArgNum0, NextOutArgNum0,
        !OutArgNames, TsccInArgs, TsccArgs, ReturnRvalsTypes0, OutVarsTypes0,
        OwnLocalVarDefns, TsccInLocalVarDefns, TsccOutLocalVarDefns0,
        CopyTsccInToOwnStmts,
        CopyOwnToTsccOutStmts0, CopyOutValThroughPtrStmts),
    (
        CodeModel = model_det,
        ReturnRvalsTypes = ReturnRvalsTypes0,
        OutVarsTypes = OutVarsTypes0,
        TsccOutLocalVarDefns = TsccOutLocalVarDefns0,
        CopyOwnToTsccOutStmts = CopyOwnToTsccOutStmts0
    ;
        CodeModel = model_semi,
        SucceededType = mlds_native_bool_type,

        SucceededOwnVar = lvn_comp_var(lvnc_succeeded),
        SucceededOwnLval = ml_local_var(SucceededOwnVar, SucceededType),
        SucceededOwnRval = ml_lval(SucceededOwnLval),

        SucceededTsccOutVar = lvn_tscc_output_var_succeeded,
        SucceededTsccOutLval =
            ml_local_var(SucceededTsccOutVar, SucceededType),
        SucceededTsccOutRval = ml_lval(SucceededTsccOutLval),

        SucceededTsccOutVarDefn = mlds_local_var_defn(SucceededTsccOutVar,
            Context, SucceededType, no_initializer, gc_no_stmt),
        CopySucceededStmt = ml_stmt_atomic(
            assign(SucceededTsccOutLval, SucceededOwnRval),
            Context),

        ReturnRvalsTypes =
            [SucceededTsccOutRval - SucceededType | ReturnRvalsTypes0],
        OutVarsTypes = [SucceededTsccOutVar - SucceededType | OutVarsTypes0],
        TsccOutLocalVarDefns =
            [SucceededTsccOutVarDefn | TsccOutLocalVarDefns0],
        CopyOwnToTsccOutStmts = [CopySucceededStmt | CopyOwnToTsccOutStmts0]
    ;
        CodeModel = model_non,
        unexpected($pred, "model_non")
    ),
    assoc_list.values(ReturnRvalsTypes, ReturnTypes),
    FuncParams = mlds_func_params(TsccArgs, ReturnTypes).

:- pred ml_gen_tscc_arg_decls(module_info::in, copy_out_when::in,
    prog_context::in, proc_id_in_tscc::in, list(var_mvar_type_mode)::in,
    int::in, int::in, map(int, string)::in, map(int, string)::out,
    list(mlds_argument)::out, list(mlds_argument)::out,
    assoc_list(mlds_rval, mlds_type)::out,
    assoc_list(mlds_local_var_name, mlds_type)::out,
    list(mlds_local_var_defn)::out, list(mlds_local_var_defn)::out,
    list(mlds_local_var_defn)::out,
    list(mlds_stmt)::out, list(mlds_stmt)::out, list(mlds_stmt)::out) is det.

ml_gen_tscc_arg_decls(_ModuleInfo, _CopyOutWhen, _Context, _ProcIdInTscc, [],
        _NextInArgNum0, _NextOutArgNum0, !OutArgNames,
        [], [], [], [], [], [], [], [], [], []).
ml_gen_tscc_arg_decls(ModuleInfo, CopyOutWhen, Context, ProcIdInTscc,
        [HeadTuple | TailTuples], NextInArgNum0, NextOutArgNum0,
        !OutArgNames, TsccInArgs, TsccArgs, ReturnRvalsTypes, OutVarsTypes,
        OwnLocalVarDefns, TsccInLocalVarDefns, TsccOutLocalVarDefns,
        CopyTsccInToOwnStmts,
        CopyOwnToTsccOutStmts, CopyOutValThroughPtrStmts) :-
    HeadTuple =
        var_mvar_type_mode(_HeadVar, HeadMLDSVarName, HeadType, HeadMode),
    HeadIsDummy = is_type_a_dummy(ModuleInfo, HeadType),
    (
        HeadIsDummy = is_dummy_type,
        % Exclude types such as io.state, etc.
        ml_gen_tscc_arg_decls(ModuleInfo, CopyOutWhen, Context,
            ProcIdInTscc, TailTuples,
            NextInArgNum0, NextOutArgNum0, !OutArgNames,
            TsccInArgs, TsccArgs, ReturnRvalsTypes, OutVarsTypes,
            OwnLocalVarDefns, TsccInLocalVarDefns, TsccOutLocalVarDefns,
            CopyTsccInToOwnStmts,
            CopyOwnToTsccOutStmts, CopyOutValThroughPtrStmts)
    ;
        HeadIsDummy = is_not_dummy_type,
        HeadVarNameStr = ml_local_var_name_to_string(HeadMLDSVarName),
        HeadMLDS_Type = mercury_type_to_mlds_type(ModuleInfo, HeadType),
        (
            HeadMode = top_unused,
            % Also exclude values with arg_mode `top_unused'.
            ml_gen_tscc_arg_decls(ModuleInfo, CopyOutWhen, Context,
                ProcIdInTscc, TailTuples,
                NextInArgNum0, NextOutArgNum0, !OutArgNames,
                TsccInArgs, TsccArgs, ReturnRvalsTypes, OutVarsTypes,
                OwnLocalVarDefns, TsccInLocalVarDefns,
                TsccOutLocalVarDefns, CopyTsccInToOwnStmts,
                CopyOwnToTsccOutStmts, CopyOutValThroughPtrStmts)
        ;
            (
                HeadMode = top_in,
                InArgNum = NextInArgNum0,
                NextInArgNum = NextInArgNum0 + 1,
                NextOutArgNum = NextOutArgNum0,
                HeadTsccInVar = lvn_tscc_proc_input_var(ProcIdInTscc,
                    InArgNum, HeadVarNameStr),
                HeadMLDSVarLval =
                    ml_local_var(HeadMLDSVarName, HeadMLDS_Type),
                HeadTsccInVarLval =
                    ml_local_var(HeadTsccInVar, HeadMLDS_Type),
                HeadTsccInArg = mlds_argument(HeadTsccInVar, HeadMLDS_Type,
                    gc_no_stmt),

                HeadTsccInArgs = [HeadTsccInArg],
                HeadTsccArgs = [HeadTsccInArg],
                HeadReturnRvalsTypes = [],
                HeadOutVarsTypes = [],

                HeadOwnLocalVarDefn = mlds_local_var_defn(HeadMLDSVarName,
                    Context, HeadMLDS_Type, no_initializer, gc_no_stmt),
                HeadTsccInLocalVarDefns = [mlds_local_var_defn(HeadTsccInVar,
                    Context, HeadMLDS_Type, no_initializer, gc_no_stmt)],
                HeadTsccOutLocalVarDefns = [],

                HeadCopyTsccInToOwnStmts = [ml_stmt_atomic(
                    assign(HeadMLDSVarLval, ml_lval(HeadTsccInVarLval)),
                    Context)],
                HeadCopyOwnToTsccOutStmts = [],
                HeadCopyOutValThroughPtrStmts = []
            ;
                HeadMode = top_out,
                OutArgNum = NextOutArgNum0,
                NextInArgNum = NextInArgNum0,
                NextOutArgNum = NextOutArgNum0 + 1,
                ( if map.search(!.OutArgNames, OutArgNum, OldOutArgName) then
                    OutArgName = OldOutArgName
                else
                    OutArgName = HeadVarNameStr,
                    map.det_insert(OutArgNum, OutArgName, !OutArgNames)
                ),
                HeadMLDSVarLval = ml_local_var(HeadMLDSVarName, HeadMLDS_Type),
                HeadTsccOutVarValue =
                    lvn_tscc_output_var(OutArgNum, OutArgName),
                HeadTsccOutVarValueLval =
                    ml_local_var(HeadTsccOutVarValue, HeadMLDS_Type),

                HeadTsccInArgs = [],
                % HeadTsccArgs in if-then-else below
                % HeadReturnRvalsTypes in if-then-else below
                HeadOutVarsTypes = [HeadTsccOutVarValue - HeadMLDS_Type],

                HeadOwnLocalVarDefn = mlds_local_var_defn(HeadMLDSVarName,
                    Context, HeadMLDS_Type, no_initializer, gc_no_stmt),
                HeadTsccInLocalVarDefns = [],
                HeadTsccOutLocalVarDefns =
                    [mlds_local_var_defn(HeadTsccOutVarValue,
                        Context, HeadMLDS_Type, no_initializer, gc_no_stmt)],

                HeadCopyTsccInToOwnStmts = [],
                HeadCopyOwnToTsccOutStmts = [ml_stmt_atomic(
                    assign(HeadTsccOutVarValueLval, ml_lval(HeadMLDSVarLval)),
                    Context)],
                % HeadCopyOutValThroughPtrStmts in if-then-else below
                ( if
                    (
                        CopyOutWhen = copy_out_only_last_arg,
                        TailTuples = []
                    ;
                        CopyOutWhen = copy_out_always
                    )
                then
                    % This is a by-value output.
                    HeadReturnRval = ml_lval(HeadTsccOutVarValueLval),

                    HeadTsccArgs = [],
                    HeadReturnRvalsTypes = [HeadReturnRval - HeadMLDS_Type],
                    HeadCopyOutValThroughPtrStmts = []
                else
                    % This is a by-reference output.
                    HeadMLDS_PtrType = mlds_ptr_type(HeadMLDS_Type),
                    HeadTsccOutVarPtr =
                        lvn_tscc_output_var_ptr(OutArgNum, OutArgName),
                    HeadTsccOutVarPtrLval =
                        ml_local_var(HeadTsccOutVarPtr, HeadMLDS_PtrType),
                    HeadTsccPtrArg = mlds_argument(HeadTsccOutVarPtr,
                        HeadMLDS_PtrType, gc_no_stmt),

                    HeadTsccArgs = [HeadTsccPtrArg],
                    HeadReturnRvalsTypes = [],
                    HeadCopyOutValThroughPtrStmts = [ml_stmt_atomic(
                        assign(ml_mem_ref(
                            ml_lval(HeadTsccOutVarPtrLval), HeadMLDS_Type),
                            ml_lval(HeadTsccOutVarValueLval)),
                        Context)]
                )
            ),
            ml_gen_tscc_arg_decls(ModuleInfo, CopyOutWhen, Context,
                ProcIdInTscc, TailTuples,
                NextInArgNum, NextOutArgNum, !OutArgNames,
                TailTsccInArgs, TailTsccArgs,
                TailReturnRvalsTypes, TailOutVarsTypes,
                TailOwnLocalVarDefns, TailTsccInLocalVarDefns,
                TailTsccOutLocalVarDefns,
                TailCopyTsccInToOwnStmts, TailCopyOwnToTsccOutStmts,
                TailCopyOutValThroughPtrStmts),
            TsccInArgs = HeadTsccInArgs ++ TailTsccInArgs,
            TsccArgs = HeadTsccArgs ++ TailTsccArgs,
            ReturnRvalsTypes = HeadReturnRvalsTypes ++ TailReturnRvalsTypes,
            OutVarsTypes = HeadOutVarsTypes ++ TailOutVarsTypes,

            OwnLocalVarDefns = [HeadOwnLocalVarDefn | TailOwnLocalVarDefns],
            TsccInLocalVarDefns =
                HeadTsccInLocalVarDefns ++ TailTsccInLocalVarDefns,
            TsccOutLocalVarDefns =
                HeadTsccOutLocalVarDefns ++ TailTsccOutLocalVarDefns,

            CopyTsccInToOwnStmts =
                HeadCopyTsccInToOwnStmts ++ TailCopyTsccInToOwnStmts,
            CopyOwnToTsccOutStmts =
                HeadCopyOwnToTsccOutStmts ++ TailCopyOwnToTsccOutStmts,
            CopyOutValThroughPtrStmts =
                HeadCopyOutValThroughPtrStmts ++ TailCopyOutValThroughPtrStmts
        )
    ).

%---------------------------------------------------------------------------%
%
% Generate code for parameter passing.
%

wrap_plain_not_fcw_arg(Var) = arg_not_for_closure_wrapper(Var).

wrap_plain_not_fcw_args([]) = [].
wrap_plain_not_fcw_args([Var | Vars]) =
    [arg_not_for_closure_wrapper(Var) | wrap_plain_not_fcw_args(Vars)].

ml_gen_args(PredOrFunc, CodeModel, Context, WhatParams,
        CalleeTypes, CalleeModes, CallerArgs, !:InputRvals, !:OutputLvalsTypes,
        !:ConvOutputDefns, !:ConvOutputStmts, !Info) :-
    ml_gen_info_get_copy_out(!.Info, CodeModel, CopyOut),
    CopyOutWhen = compute_when_to_copy_out(CopyOut, CodeModel, PredOrFunc),
    ml_gen_args_loop(CopyOutWhen, Context, WhatParams, 1,
        CalleeTypes, CalleeModes, CallerArgs, !:InputRvals, !:OutputLvalsTypes,
        !:ConvOutputDefns, !:ConvOutputStmts, !Info).

:- pred ml_gen_args_loop(copy_out_when, prog_context, what_params, int,
    list(mer_type), list(mer_mode), list(ml_call_arg),
    list(mlds_rval), assoc_list(mlds_lval, mlds_type),
    list(mlds_local_var_defn), list(mlds_stmt), ml_gen_info, ml_gen_info).
:- mode ml_gen_args_loop(in, in, in, in, in, in, in(list_skel(not_fcw)),
    out, out, out, out, in, out) is det.
:- mode ml_gen_args_loop(in, in, in, in, in, in, in(list_skel(fcw)),
    out, out, out, out, in, out) is det.

ml_gen_args_loop(CopyOutWhen, Context, WhatParams, ArgNum,
        CalleeTypes, CalleeModes, CallerArgs, !:InputRvals, !:OutputLvalsTypes,
        !:ConvOutputDefns, !:ConvOutputStmts, !Info) :-
    ( if
        CalleeTypes = [],
        CalleeModes = [],
        CallerArgs = []
    then
        !:InputRvals = [],
        !:OutputLvalsTypes = [],
        !:ConvOutputDefns = [],
        !:ConvOutputStmts = []
    else if
        CalleeTypes = [CalleeType | CalleeTypesTail],
        CalleeModes = [CalleeMode | CalleeModesTail],
        CallerArgs = [CallerArg | CallerArgsTail]
    then
        ml_gen_args_loop(CopyOutWhen, Context, WhatParams, ArgNum + 1,
            CalleeTypesTail, CalleeModesTail, CallerArgsTail,
            !:InputRvals, !:OutputLvalsTypes,
            !:ConvOutputDefns, !:ConvOutputStmts, !Info),
        ml_gen_arg(CopyOutWhen, Context, WhatParams, ArgNum,
            CalleeType, CalleeMode, CallerArg, CallerArgsTail,
            !InputRvals, !OutputLvalsTypes,
            !ConvOutputDefns, !ConvOutputStmts, !Info)
    else
        unexpected($pred, "length mismatch")
    ).

:- pred ml_gen_arg(copy_out_when, prog_context, what_params, int,
    mer_type, mer_mode, ml_call_arg, list(ml_call_arg),
    list(mlds_rval), list(mlds_rval),
    assoc_list(mlds_lval, mlds_type), assoc_list(mlds_lval, mlds_type),
    list(mlds_local_var_defn), list(mlds_local_var_defn),
    list(mlds_stmt), list(mlds_stmt), ml_gen_info, ml_gen_info).
:- mode ml_gen_arg(in, in, in, in, in, in, in(not_fcw),
    in, in, out, in, out, in, out, in, out, in, out) is det.
:- mode ml_gen_arg(in, in, in, in, in, in, in(fcw),
    in, in, out, in, out, in, out, in, out, in, out) is det.
:- pragma inline(pred(ml_gen_arg/18)).

ml_gen_arg(CopyOutWhen, Context, WhatParams, ArgNum,
        CalleeType, CalleeMode, CallerArg,
        CallerArgsTail, !InputRvals, !OutputLvalsTypes,
        !ConvOutputDefns, !ConvOutputStmts, !Info) :-
    ml_gen_info_get_module_info(!.Info, ModuleInfo),
    mode_to_top_functor_mode(ModuleInfo, CalleeMode, CalleeType,
        CalleeArgTopFunctorMode),
    CalleeIsDummy = is_type_a_dummy(ModuleInfo, CalleeType),
    (
        CalleeIsDummy = is_dummy_type
        % Exclude arguments of type io.state etc.
    ;
        CalleeIsDummy = is_not_dummy_type,
        (
            CallerArg = arg_not_for_closure_wrapper(CallerArgVar),
            ml_gen_info_get_var_table(!.Info, VarTable),
            lookup_var_entry(VarTable, CallerArgVar, CallerArgVarEntry),
            CallerMLDSVarName =
                ml_gen_local_var_name(CallerArgVar, CallerArgVarEntry),
            ml_gen_var(!.Info, CallerArgVar, CallerArgVarEntry, CallerVarLval),
            CallerType = CallerArgVarEntry ^ vte_type,
            ForClosureWrapper = no
        ;
            CallerArg = arg_for_closure_wrapper(CallerMLDSVarName,
                CallerVarLval, CallerType),
            ForClosureWrapper = yes
        ),
        (
            CalleeArgTopFunctorMode = top_unused
            % Also exclude those with arg_mode `top_unused'.
        ;
            CalleeArgTopFunctorMode = top_in,
            % It is an input argument.
            CallerIsDummy = is_type_a_dummy(ModuleInfo, CallerType),
            (
                CallerIsDummy = is_dummy_type,
                % The variable may not have been declared, so we need to
                % generate a dummy value for it. Using `0' here is more
                % efficient than using private_builtin.dummy_var, which is
                % what ml_gen_var will have generated for this variable.
                CallerVarRval = ml_const(mlconst_int(0))
            ;
                CallerIsDummy = is_not_dummy_type,
                CallerVarRval = ml_lval(CallerVarLval)
            ),
            ml_gen_box_or_unbox_rval(ModuleInfo, CallerType, CalleeType,
                bp_native_if_possible, CallerVarRval, ArgRval),
            !:InputRvals = [ArgRval | !.InputRvals]
        ;
            CalleeArgTopFunctorMode = top_out,
            % It is an output argument.
            (
                WhatParams = input_and_output_params,
                ml_gen_box_or_unbox_lval(CallerType, CalleeType,
                    bp_native_if_possible, CallerVarLval, CallerMLDSVarName,
                    Context, ForClosureWrapper, ArgNum, ArgLval,
                    ThisArgConvDecls, _ThisArgConvInput, ThisArgConvOutput,
                    !Info),
                !:ConvOutputDefns = ThisArgConvDecls ++ !.ConvOutputDefns,
                !:ConvOutputStmts = ThisArgConvOutput ++ !.ConvOutputStmts,

                ( if
                    (
                        CopyOutWhen = copy_out_only_last_arg,
                        CallerArgsTail = []
                    ;
                        CopyOutWhen = copy_out_always
                    )
                then
                    ml_gen_mlds_type(!.Info, CalleeType, OutputType),
                    !:OutputLvalsTypes =
                        [ArgLval - OutputType | !.OutputLvalsTypes]
                else
                    % Otherwise use the traditional C style of passing
                    % the address where the output value should be put.
                    !:InputRvals = [ml_gen_mem_addr(ArgLval) | !.InputRvals]
                )
            ;
                WhatParams = input_params_only
                % For tail calls, we ignore the output arguments.
                % The callee's output arguments are known to be exactly
                % the same as the caller's output arguments, and will be
                % set by the non-recursive paths through the procedure body.
                %
                % The reason why our caller cannot just ignore the value of
                % !:OutputLvals and !:OutputTypes, and why we need the
                % WhatParams argument, is the addition of the address
                % of ArgLval to !:InputRvals above.
            )
        )
    ).

    % ml_gen_mem_addr(Lval) returns a value equal to &Lval. For the case
    % where Lval = *Rval for some Rval, we optimize &*Rval to just Rval.
    %
:- func ml_gen_mem_addr(mlds_lval) = mlds_rval.

ml_gen_mem_addr(Lval) =
    (if Lval = ml_mem_ref(Rval, _) then Rval else ml_mem_addr(Lval)).

%---------------------------------------------------------------------------%
%
% Miscellaneous routines.
%

    % Get the value of the appropriate --det-copy-out or --nondet-copy-out
    % option, depending on the code model.
    %
:- func get_copy_out_option(globals, code_model) = bool.

get_copy_out_option(Globals, CodeModel) = CopyOut :-
    (
        CodeModel = model_non,
        globals.lookup_bool_option(Globals, nondet_copy_out, CopyOut)
    ;
        ( CodeModel = model_det
        ; CodeModel = model_semi
        ),
        globals.lookup_bool_option(Globals, det_copy_out, CopyOut)
    ).

%---------------------------------------------------------------------------%
:- end_module ml_backend.ml_args_util.
%---------------------------------------------------------------------------%
