%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2009, 2011-2012 The University of Melbourne.
% Copyright (C) 2014-2015, 2017-2024 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_code_util.m.
% Main author: fjh.
%
% This module is part of the MLDS code generator.
% It defines the ml_gen_info type and its access routines.
%
%---------------------------------------------------------------------------%

:- module ml_backend.ml_gen_info.
:- interface.

:- import_module hlds.
:- import_module hlds.code_model.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module hlds.mark_tail_calls.      % for nontail_rec_call_reason
:- import_module libs.
:- import_module libs.globals.
:- import_module libs.optimization_options.
:- import_module ml_backend.ml_global_data.
:- import_module ml_backend.mlds.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.set_of_var.
:- import_module parse_tree.var_table.

:- import_module assoc_list.
:- import_module bool.
:- import_module counter.
:- import_module list.
:- import_module map.
:- import_module one_or_more.
:- import_module set.

%---------------------------------------------------------------------------%
%
% The `ml_gen_info' ADT.
%

    % The `ml_gen_info' type holds information used during
    % MLDS code generation for a given procedure.
    %
:- type ml_gen_info.

%---------------------------------------------------------------------------%
%
% Operations on the ml_gen_info that are more than getters and setters.
%

:- pred ml_gen_info_get_globals(ml_gen_info::in, globals::out) is det.
:- pred ml_gen_info_get_module_name(ml_gen_info::in, mercury_module_name::out)
    is det.

    % Look up the --put-commit-in-nested-func option.
    %
:- pred ml_gen_info_put_commit_in_own_func(ml_gen_info::in, bool::out) is det.

    % Generate a new label number for use in label statements.
    % This is used to give unique names to the case labels generated
    % for dense switch statements.
    %
:- type label_num == int.
:- pred ml_gen_info_new_label(label_num::out,
    ml_gen_info::in, ml_gen_info::out) is det.

    % Generate a new function label number. This is used to give unique names
    % to the nested functions used when generating code for nondet procedures.
    %
:- pred ml_gen_info_new_aux_func_id(mlds_maybe_aux_func_id::out,
    ml_gen_info::in, ml_gen_info::out) is det.

    % Increase the function label and const sequence number counters by some
    % amount which is presumed to be sufficient to ensure that if we start
    % again with a fresh ml_gen_info and then call this function, we won't
    % encounter any already-used function labels or constants. (This is used
    % when generating wrapper functions for type class methods.)
    %
:- pred ml_gen_info_bump_counters(ml_gen_info::in, ml_gen_info::out) is det.

    % Generate a new auxiliary variable of the given kind,
    % with a sequence number that differentiates this aux var from all others.
    %
    % Auxiliary variables are used for purposes such as commit label numbers
    % and holding table indexes in switches.
    %
:- pred ml_gen_info_new_aux_var_name(mlds_compiler_aux_var::in,
    mlds_local_var_name::out, ml_gen_info::in, ml_gen_info::out) is det.

    % Generate a new `cond' variable number.
    %
:- type cond_seq ---> cond_seq(int).
:- pred ml_gen_info_new_cond_var(cond_seq::out,
    ml_gen_info::in, ml_gen_info::out) is det.

    % Generate a new `conv' variable number. This is used to give unique names
    % to the local variables generated by ml_gen_box_or_unbox_lval, which are
    % used to handle boxing/unboxing argument conversions.
    %
:- type conv_seq ---> conv_seq(int).
:- pred ml_gen_info_new_conv_var(conv_seq::out,
    ml_gen_info::in, ml_gen_info::out) is det.

:- type bitfield
    --->    bitfield(arg_shift, arg_num_bits, fill_kind).
            % A bitfield inside a packed argument word. It is defined by
            % its position, size and the nature of the value inside it.

:- type bitfield_value
    --->    bv_var(prog_var)
    ;       bv_rval(mlds_rval)
    ;       bv_const(uint).

:- type filled_bitfield
    --->    filled_bitfield(bitfield, bitfield_value).

:- type packed_word == one_or_more(bitfield).
:- type filled_packed_word == one_or_more(filled_bitfield).

    % get_unfilled_filled_packed_words(HeadFilledBitfield, TailFilledBitfields,
    %     PackedWord, FilledPackedWord):
    %
    % Given a word containing [HeadFilledBitfield | TailFilledBitfields],
    % return its filled_packed_word representation as FilledPackedWord,
    % and its unfilled version (obtaining by simply throwing away the value
    % of every bitfield) as PackedWord.
    %
:- pred get_unfilled_filled_packed_words(
    filled_bitfield::in, list(filled_bitfield)::in,
    packed_word::out, filled_packed_word::out) is det.

    % A filled_packed_word is an instance of a packed_word if it has
    % the exact same sequence of bitfields inside it, but with a value
    % in each bitfield. We record the rval where the filled in instance
    % is available.
:- type packed_word_instance
    --->    packed_word_instance(filled_packed_word, mlds_rval).

    % Given a packed word represented as a sequence of bitfields,
    % return a list of the different ways in which we have seen those
    % bitfields have been filled, with each way being accompanied
    % by the rval that stores the resulting word value.
    %
    % We support three different ways to specify a packed word.
    %
    % - Packed word scheme 1 contains two or more arguments packed into
    %   one word in a memory cell, with the first being apw_partial_first
    %   and the others being apw_partial_shifted.
    %
    % - Packed word scheme 2 contains a remote secondary tag and one or more
    %   arguments packed into the first word in a memory cell, with the first
    %   bitfield being the remote sectag and the rest being the arguments,
    %   which must all be apw_partial_shifted.
    %
    % - Packed word scheme 3 contains a primary tag, a local secondary tag
    %   and one or more arguments packed into a word (which need not be
    %   in memory, but could be in a register), with the first bitfield
    %   being the *combined* ptag and sectag, and rest being the arguments,
    %   which again must all be apw_partial_shifted.
    %
    % Some rules apply to all three schemes:
    %
    % - All three schemes require the bitfields involved to be nonoverlapping.
    % - All imply that any bits not covered by any of the bitfields
    %   will be zeroes.
    % - None of them contain any bitfields for apw_none_shifted arguments,
    %   since those bitfields would contain zero bits.
    % - In all three cases, the arguments should be in the list in ascending
    %   order of argument number, which means that they should be in
    %   *descending* order of offset. This rule applies *only* to the bitfields
    %   containing arguments: a bitfield contain a tag or tags always has
    %   to be at the front of the list, even though it will always have
    %   the lowest offset.
    %
:- type packed_word_map == map(packed_word, one_or_more(packed_word_instance)).

    % Generate a new unique `ml_packed_args' variable. Such compiler-generated
    % variables hold the packed-together values of two or more user variables.
    %
:- pred ml_gen_info_new_packed_word_var(mlds_compiler_var::out,
    ml_gen_info::in, ml_gen_info::out) is det.

:- type ml_ground_term
    --->    ml_ground_term(
                % The value of the ground term.
                mlds_rval,

                % The type of the ground term (actually, the type of the
                % variable the ground term was constructed for).
                mer_type,

                % The corresponding MLDS type. It could be computed from the
                % Mercury type, but there is no point in doing so when using
                % the ground term as well when constructing it.
                mlds_type
            ).

:- type ml_ground_term_map == map(prog_var, ml_ground_term).

:- type ml_const_struct_map == map(int, ml_ground_term).

    % Set the `const' variable name corresponding to the given HLDS variable.
    %
:- pred ml_gen_info_set_const_var(prog_var::in, ml_ground_term::in,
    ml_gen_info::in, ml_gen_info::out) is det.

    % Look up the `const' sequence number corresponding to a given HLDS
    % variable.
    %
:- pred ml_gen_info_lookup_const_var(ml_gen_info::in, prog_var::in,
    ml_ground_term::out) is det.
:- pred ml_gen_info_search_const_var(ml_gen_info::in, prog_var::in,
    ml_ground_term::out) is semidet.

    % A success continuation specifies the (rval for the variable holding
    % the address of the) function that a nondet procedure should call
    % if it succeeds, and possibly also the (rval for the variable holding)
    % the environment pointer for that function, and possibly also the
    % (list of rvals for the) arguments to the continuation.
    %
:- type success_cont
    --->    success_cont(
                % Function pointer.
                mlds_rval,

                % Environment pointer. Note that if we are using
                % nested functions, then the environment pointer
                % will not be used.
                mlds_rval,

                % The arguments, together with their types, if there are any.
                % (We do not include the environment pointer in this list.)
                % The list will be non-empty only if the --nondet-copy-out
                % option is enabled.
                assoc_list(mlds_lval, mlds_type)
            ).

    % The ml_gen_info contains a stack of success continuations.
    % The following routines provide access to that stack.
    %
:- pred ml_gen_info_push_success_cont(success_cont::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_pop_success_cont(ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_current_success_cont(ml_gen_info::in, success_cont::out)
    is det.

    % The ml_gen_info contains a record of how many nested functions
    % we are inside. These predicates provide access to the nesting depth.
    %
:- pred ml_gen_info_increment_func_nest_depth(
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_decrement_func_nest_depth(
    ml_gen_info::in, ml_gen_info::out) is det.

    % We keep a partial mapping from vars to lvals. This is used in special
    % cases to override the normal lval for a variable. ml_gen_var will check
    % this map first, and if the variable is not in this map, then it will go
    % ahead and generate an lval for it as usual.
    %
    % Set the lval for a variable.
    %
:- pred ml_gen_info_set_var_lval(prog_var::in, mlds_lval::in,
    ml_gen_info::in, ml_gen_info::out) is det.

    % The ml_gen_info contains a list of extra definitions of functions or
    % global constants which should be inserted before the definition of the
    % function for the current procedure. This is used for the definitions
    % of the wrapper functions needed for closures. When generating code
    % for a procedure that creates a closure, we insert the definition of
    % the wrapper function used for that closure into this list.
    %
    % Insert an extra definition at the start of the list of extra
    % definitions.
    %
:- pred ml_gen_info_add_closure_wrapper_defn(mlds_function_defn::in,
    ml_gen_info::in, ml_gen_info::out) is det.

    % Add the given string as the name of an environment variable used by
    % the function being generated.
    %
:- pred ml_gen_info_add_env_var_name(string::in,
    ml_gen_info::in, ml_gen_info::out) is det.

    % Get the value of the copy_out option appropriate to the given code model.
    %
:- pred ml_gen_info_get_copy_out(ml_gen_info::in, code_model::in, bool::out)
    is det.

:- type target_of_self_tail_rec_call
    --->    is_not_target_of_self_trcall
    ;       is_target_of_self_trcall.

:- type target_of_mutual_tail_rec_call
    --->    is_not_target_of_mutual_trcall
    ;       is_target_of_mutual_trcall.

:- type nontail_rec_call_warn_status
    --->    nontail_rec_call_warn_disabled
    ;       nontail_rec_call_warn_enabled.

:- type nontail_rec_call
    --->    nontail_rec_call(
                ntrc_caller                 :: pred_proc_id,
                ntrc_callee                 :: pred_proc_id,
                ntrc_context                :: prog_context,
                ntrc_reason                 :: nontail_rec_call_reason,
                ntrc_obviousness            :: nontail_rec_obviousness,
                ntrc_warn_status            :: nontail_rec_call_warn_status
            ).

    % This map should have an entry for each procedure in the TSCC.
    % The set of keys in the map won't change and neither will
    % the target mechanism of each, which tells the code generator
    % how to generate code for a tail recursive call to the given
    % procedure, but if the code generator *does* generate such
    % a tail recursive call, it should set the
    % have_we_done_tail_rec field to have_done_tail_rec.
:- type in_scc_map == map(pred_proc_id, in_scc_info).
:- type in_scc_info
    --->    in_scc_info(
                % If this procedure is in the TSCC of the procedure we are
                % currently generating code for, this field contains the
                % information we need to generate tail recursive calls to it.
                isi_maybe_in_tscc           :: maybe_in_tscc_target_info,

                % The next three fields say whether we have called
                % this procedure in various ways. They are updated as
                % we compile *all* the procedures in the TSCC.

                % In a tail call from itself?
                isi_is_target_of_self_tr    :: target_of_self_tail_rec_call,

                % In a tail call from another procedure in the TSCC?
                isi_is_target_of_mutual_tr  :: target_of_mutual_tail_rec_call,

                % In a NONtail call, from anywhere in the *SCC*?
                % This list gives, for each non-tail call to this procedure
                % from *any* procedure in its SCC (including itself),
                % the details of the call site.
                %
                % We use this field to gather a list of all the calls between
                % the procedures in the TSCC that are *not* tail recursive.
                % We store it in pieces, with one piece for each callee,
                % because we already have the infrastructure for this
                % in the form of the in_scc_map, and because storing
                % the list as a whole in a data structure that is always
                % passed along next to the in_scc_map would incur
                % greater overheads, in both space and time, than this field.
                isi_is_target_of_non_tail_rec :: list(nontail_rec_call)
            ).

:- type maybe_in_tscc_target_info
    --->    not_in_tscc
    ;       in_tscc(
                % The identifying small sequence number of this procedure
                % in its TSCC. These numbers are assigned sequentially
                % starting at 1, and are wrapped up in function symbol
                % to distinguish them from other integers.
                itti_id                     :: proc_id_in_tscc,

                % The list of the *input* arguments of the procedure.
                itti_input_args             :: list(mlds_argument)
            ).

:- type tail_rec_loop_kind
    --->    tail_rec_loop_while_continue
    ;       tail_rec_loop_label_goto.

:- type tscc_kind
    --->    tscc_self_rec_only
    ;       tscc_self_and_mutual_rec.

:- type tail_rec_info
    --->    tail_rec_info(
                % If a procedure has an entry in tri_target_map, then
                % calls to that procedure should look at the corresponding
                % value. They should update the field that says whether
                % the actual kind of call has occurred, and they can turn
                % tail calls into assignments to the trti_input_args,
                % followed by a transfer of control to the start of the callee.
                tri_in_scc_map              :: in_scc_map,

                % These two fields say how that transfer of control should be
                % done. The tri_loop_kind field says whether the procedure body
                % should start with a label, so that tail calls are a goto
                % to that label, or whether the procedure body or bodies
                % are wrapped up in an infinite while loop, with tail calls
                % jumping back to its start via a "continue" statement.
                % The tri_tscc_kind field says whether the loop contains
                % just procedure body, or several. In the latter case,
                % each procedure will either have its own label,
                % or the body of the while loop will be a switch on
                % lvnc_tscc_proc_selector, with each procedure body
                % being one of the cases of the switch. The id of the label,
                % or the value of the selector, is given by the trti_id field
                % of the callee in tri_target_map.
                tri_loop_kind               :: tail_rec_loop_kind,
                tri_tscc_kind               :: tscc_kind
            ).

:- func generate_tail_rec_start_label(tscc_kind, proc_id_in_tscc) = mlds_label.

%---------------------------------------------------------------------------%
%
% Initialize the ml_gen_info, which contains the state of the code generator
% while it generates MLDS code for a HLDS procedure.
%
% When the HLDS procedure is part of a TSCC of several mutually-tail-recursive
% procedures, we bundle the code we generate for each procedure into
% a single piece of code for *each* entry procedure of the TSCC.
% To help prevent accidental name collisions between the compiler-generated
% local variables of the different procedures, we get those procedures
% to use non-overlapping sets of sequence numbers in the names of those
% compiler-generated variables, by
%
% - creating an initial set of counters (the sources of those sequence numbers)
%   before starting to generate code for a TSCC, and
% - threading the values of those counters from one procedure to the next,
%   by calling ml_gen_info_final to pick up the values of those counters
%   after code generation is finished for one procedure, and passing them
%   to ml_gen_info_init when starting to generate code for the next procedure.
%
% The ml_gen_tscc_info structure contains not just these counters, but also
% information about tail recursion. By definition, the procedures in a TSCC
% contain tail recursive calls to the same set of procedures (the procedures
% of the TSCC), but TSCCs are computed from calls in the HLDS. A call that is
% a tail call in the HLDS is sometimes not a tail call in the MLDS, which can
% happen if making the call a tail call would leave a dangling reference.
% We therefore need to record whether each procedure in the TSCC has an
% *MLDS* tail call generated to it from any of the *other* procedures
% in the TSCC, because if it doesn't, then its MLDS code isn't actually
% mutually-tail-recursive, and it should be handled as such. (This is because
% the code we generate for only *self*-tail-recursive procedures has lower
% overhead than the code we generate for *mutually*-tail-recursive procedures.)

:- type ml_gen_tscc_info
    --->    ml_gen_tscc_info(
                mgti_func_label_counter     :: counter,
                mgti_label_counter          :: counter,
                mgti_aux_var_counter        :: counter,
                mgti_cond_var_counter       :: counter,
                mgti_conv_var_counter       :: counter,
                mgti_packed_word_counter    :: counter,
                mgti_tail_rec_info          :: tail_rec_info
            ).

:- pred init_ml_gen_tscc_info(module_info::in, in_scc_map::in, tscc_kind::in,
    ml_gen_tscc_info::out) is det.

    % Initialize the ml_gen_info, so that it is almost ready for
    % generating code for the given procedure. (The "almost" is because
    % the caller still needs to set the byref_output_vars field. We don't
    % set it to a meaningful value here, because the code for setting it
    % itself needs an ml_gen_info.)
    %
    % The second last argument records the persistent information
    % accumulated by the code generator so far during the processing of
    % previous procedures. The role of the last argument is described above.
    %
:- func ml_gen_info_init(module_info, mlds_target_lang, ml_const_struct_map,
    pred_proc_id, proc_info, ml_global_data, ml_gen_tscc_info) = ml_gen_info.

    % ml_gen_info_final(Info, EnvVarNames, ClosureWrapperDefns, GlobalData,
    %   TsccInfo):
    %
    % Return
    %
    % - the values of those fields that are actually part of the MLDS
    %   structure we are generating (EnvVarNames, ClosureWrapperDefns and
    %   GlobalData),
    % - the values of the counters that our caller may need when
    %   initializating the *next* ml_gen_info (in TsccInfo),
    % - the values of the fields that the code generator needs
    %   to decide what code to generate for this procedure (in TsccInfo).
    %
:- pred ml_gen_info_final(ml_gen_info::in, set(string)::out,
    list(mlds_function_defn)::out, ml_global_data::out, ml_gen_tscc_info::out)
    is det.

%---------------------------------------------------------------------------%
%
% Getters and setters of the ml_gen_info structure.
%

:- pred ml_gen_info_get_const_var_map(ml_gen_info::in,
    ml_ground_term_map::out) is det.
:- pred ml_gen_info_get_used_succeeded_var(ml_gen_info::in, bool::out) is det.
:- pred ml_gen_info_get_closure_wrapper_defns(ml_gen_info::in,
    list(mlds_function_defn)::out) is det.
:- pred ml_gen_info_get_global_data(ml_gen_info::in, ml_global_data::out)
    is det.
:- pred ml_gen_info_get_module_info(ml_gen_info::in, module_info::out) is det.
:- pred ml_gen_info_get_pred_proc_id(ml_gen_info::in,
    pred_proc_id::out) is det.
:- pred ml_gen_info_get_var_table(ml_gen_info::in, var_table::out) is det.
:- pred ml_gen_info_get_high_level_data(ml_gen_info::in, bool::out) is det.
:- pred ml_gen_info_get_target(ml_gen_info::in, mlds_target_lang::out) is det.
:- pred ml_gen_info_get_gc(ml_gen_info::in, gc_method::out) is det.
:- pred ml_gen_info_get_det_copy_out(ml_gen_info::in, bool::out) is det.
:- pred ml_gen_info_get_nondet_copy_out(ml_gen_info::in, bool::out) is det.
:- pred ml_gen_info_get_use_atomic_cells(ml_gen_info::in,
    maybe_use_atomic_cells::out) is det.
:- pred ml_gen_info_get_profile_memory(ml_gen_info::in, bool::out) is det.
:- pred ml_gen_info_get_num_ptag_bits(ml_gen_info::in, uint8::out) is det.
:- pred ml_gen_info_get_const_struct_map(ml_gen_info::in,
    map(int, ml_ground_term)::out) is det.
:- pred ml_gen_info_get_var_lvals(ml_gen_info::in,
    map(prog_var, mlds_lval)::out) is det.
:- pred ml_gen_info_get_env_var_names(ml_gen_info::in, set(string)::out)
    is det.
:- pred ml_gen_info_get_disabled_warnings(ml_gen_info::in,
    set(goal_warning)::out) is det.
:- pred ml_gen_info_get_tail_rec_info(ml_gen_info::in,
    tail_rec_info::out) is det.
:- pred ml_gen_info_get_byref_output_vars(ml_gen_info::in,
    set_of_progvar::out) is det.
:- pred ml_gen_info_get_packed_word_map(ml_gen_info::in,
    packed_word_map::out) is det.
:- pred ml_gen_info_get_func_nest_depth(ml_gen_info::in, int::out) is det.

:- pred ml_gen_info_set_const_var_map(ml_ground_term_map::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_used_succeeded_var(bool::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_global_data(ml_global_data::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_module_info(module_info::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_var_table(var_table::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_var_lvals(map(prog_var, mlds_lval)::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_disabled_warnings(set(goal_warning)::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_tail_rec_info(tail_rec_info::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_byref_output_vars(set_of_progvar::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_packed_word_map(packed_word_map::in,
    ml_gen_info::in, ml_gen_info::out) is det.

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

:- implementation.

:- import_module libs.options.
:- import_module ml_backend.ml_target_util.
:- import_module ml_backend.ml_util.

:- import_module int.
:- import_module stack.
:- import_module string.
:- import_module uint8.

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

ml_gen_info_get_globals(Info, Globals) :-
    ml_gen_info_get_module_info(Info, ModuleInfo),
    module_info_get_globals(ModuleInfo, Globals).

ml_gen_info_get_module_name(Info, ModuleName) :-
    ml_gen_info_get_module_info(Info, ModuleInfo),
    module_info_get_name(ModuleInfo, ModuleName).

ml_gen_info_put_commit_in_own_func(Info, PutCommitInNestedFunc) :-
    ml_gen_info_get_globals(Info, Globals),
    globals.lookup_bool_option(Globals, put_commit_in_own_func,
        PutCommitInNestedFunc).

ml_gen_info_new_label(Label, !Info) :-
    ml_gen_info_get_label_counter(!.Info, Counter0),
    counter.allocate(Label, Counter0, Counter),
    ml_gen_info_set_label_counter(Counter, !Info).

ml_gen_info_new_aux_func_id(MaybeAux, !Info) :-
    ml_gen_info_get_func_counter(!.Info, Counter0),
    counter.allocate(Num, Counter0, Counter),
    MaybeAux = proc_aux_func(Num),
    ml_gen_info_set_func_counter(Counter, !Info).

ml_gen_info_bump_counters(!Info) :-
    ml_gen_info_get_func_counter(!.Info, FuncLabelCounter0),
    counter.allocate(FuncLabel, FuncLabelCounter0, _),
    FuncLabelCounter = counter.init(FuncLabel + 10000),
    ml_gen_info_set_func_counter(FuncLabelCounter, !Info).

ml_gen_info_new_aux_var_name(AuxVar, VarName, !Info) :-
    ml_gen_info_get_aux_var_counter(!.Info, AuxVarCounter0),
    counter.allocate(AuxVarNum, AuxVarCounter0, AuxVarCounter),
    ml_gen_info_set_aux_var_counter(AuxVarCounter, !Info),
    VarName = lvn_comp_var(lvnc_aux_var(AuxVar, AuxVarNum)).

ml_gen_info_new_cond_var(cond_seq(CondNum), !Info) :-
    ml_gen_info_get_cond_var_counter(!.Info, CondCounter0),
    counter.allocate(CondNum, CondCounter0, CondCounter),
    ml_gen_info_set_cond_var_counter(CondCounter, !Info).

ml_gen_info_new_conv_var(conv_seq(ConvNum), !Info) :-
    ml_gen_info_get_conv_var_counter(!.Info, ConvCounter0),
    counter.allocate(ConvNum, ConvCounter0, ConvCounter),
    ml_gen_info_set_conv_var_counter(ConvCounter, !Info).

get_unfilled_filled_packed_words(HeadFilledBitfield, TailFilledBitfields,
        PackedWord, FilledPackedWord) :-
    HeadBitfield = get_unfilled_bitfield(HeadFilledBitfield),
    TailBitfields = list.map(get_unfilled_bitfield, TailFilledBitfields),
    PackedWord = one_or_more(HeadBitfield, TailBitfields),
    FilledPackedWord = one_or_more(HeadFilledBitfield, TailFilledBitfields).

:- func get_unfilled_bitfield(filled_bitfield) = bitfield.

get_unfilled_bitfield(FilledBitfield) = Bitfield :-
    FilledBitfield = filled_bitfield(Bitfield, _Value).

ml_gen_info_new_packed_word_var(LocalVarName, !Info) :-
    ml_gen_info_get_packed_word_counter(!.Info, PackedWordCounter0),
    counter.allocate(PackedWordNum, PackedWordCounter0, PackedWordCounter),
    LocalVarName = lvnc_packed_word(PackedWordNum),
    ml_gen_info_set_packed_word_counter(PackedWordCounter, !Info).

ml_gen_info_set_const_var(Var, GroundTerm, !Info) :-
    ml_gen_info_get_const_var_map(!.Info, ConstVarMap0),
    % We cannot call map.det_insert, because we do not (yet) clean up the
    % const_var_map at the start of later branches of a branched goal,
    % and thus when generating code for a later branch, we may come across
    % an entry left by an earlier branch. Using map.set instead throws away
    % such obsolete entries.
    map.set(Var, GroundTerm, ConstVarMap0, ConstVarMap),
    ml_gen_info_set_const_var_map(ConstVarMap, !Info).

ml_gen_info_lookup_const_var(Info, Var, GroundTerm) :-
    ml_gen_info_get_const_var_map(Info, ConstVarMap),
    map.lookup(ConstVarMap, Var, GroundTerm).

ml_gen_info_search_const_var(Info, Var, GroundTerm) :-
    ml_gen_info_get_const_var_map(Info, ConstVarMap),
    map.search(ConstVarMap, Var, GroundTerm).

ml_gen_info_push_success_cont(SuccCont, !Info) :-
    ml_gen_info_get_success_cont_stack(!.Info, Stack0),
    stack.push(SuccCont, Stack0, Stack),
    ml_gen_info_set_success_cont_stack(Stack, !Info).

ml_gen_info_pop_success_cont(!Info) :-
    ml_gen_info_get_success_cont_stack(!.Info, Stack0),
    stack.det_pop(_SuccCont, Stack0, Stack),
    ml_gen_info_set_success_cont_stack(Stack, !Info).

ml_gen_info_current_success_cont(Info, SuccCont) :-
    ml_gen_info_get_success_cont_stack(Info, Stack),
    stack.det_top(Stack, SuccCont).

ml_gen_info_increment_func_nest_depth(!Info) :-
    ml_gen_info_get_func_nest_depth(!.Info, Depth0),
    Depth = Depth0 + 1,
    ml_gen_info_set_func_nest_depth(Depth, !Info).

ml_gen_info_decrement_func_nest_depth(!Info) :-
    ml_gen_info_get_func_nest_depth(!.Info, Depth0),
    Depth = Depth0 - 1,
    ml_gen_info_set_func_nest_depth(Depth, !Info).

ml_gen_info_set_var_lval(Var, Lval, !Info) :-
    ml_gen_info_get_var_lvals(!.Info, VarLvals0),
    map.set(Var, Lval, VarLvals0, VarLvals),
    ml_gen_info_set_var_lvals(VarLvals, !Info).

ml_gen_info_add_closure_wrapper_defn(ClosureWrapperDefn, !Info) :-
    ml_gen_info_get_closure_wrapper_defns(!.Info, ClosureWrapperDefns0),
    ClosureWrapperDefns = [ClosureWrapperDefn | ClosureWrapperDefns0],
    ml_gen_info_set_closure_wrapper_defns(ClosureWrapperDefns, !Info).

ml_gen_info_add_env_var_name(Name, !Info) :-
    ml_gen_info_get_env_var_names(!.Info, EnvVarNames0),
    set.insert(Name, EnvVarNames0, EnvVarNames),
    ml_gen_info_set_env_var_names(EnvVarNames, !Info).

ml_gen_info_get_copy_out(Info, CodeModel, CopyOut) :-
    (
        ( CodeModel = model_det
        ; CodeModel = model_semi
        ),
        ml_gen_info_get_det_copy_out(Info, CopyOut)
    ;
        CodeModel = model_non,
        ml_gen_info_get_nondet_copy_out(Info, CopyOut)
    ).

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

generate_tail_rec_start_label(TsccKind, Id) = Label :-
    (
        TsccKind = tscc_self_rec_only,
        Label = mlds_label("top_of_proc")
    ;
        TsccKind = tscc_self_and_mutual_rec,
        Id = proc_id_in_tscc(IdNum),
        Label = mlds_label(string.format("top_of_proc_%d", [i(IdNum)]))
    ).

%---------------------------------------------------------------------------%
%
% The definition of the `ml_gen_info' ADT.
%
% The ml_gen_info structure is logically an atomic structure,
% but we split it up into three pieces for performance reasons.
% The most frequently updated fields are at the top level, in the ml_gen_info
% structure, whose size is limited to eight fields. This makes it (just) fit
% into one of Boehm gc's size categories, and it limits the amount of copying
% that needs to be done when one of the fields is updated. The other fields
% are stored in one of two substructures. The ml_gen_rare_info is for the
% fields that are never or almost-never updated, while the ml_gen_sub_info
% is for the fields that are updated reasonably frequently, though not
% so frequently as to deserve a spot in the top level structure.

:- type ml_gen_info
    --->    ml_gen_info(
                % A variable can be bound to a constant in one branch
                % of a control structure and to a non-constant term
                % in another branch. We store information about variables
                % bound to constants in the mgsi_const_var_map field.
                %
                % Branched control structures should reset the map
                % to its original value at the start of every branch
                % after the first (to prevent a later branch from using
                % information that is applicable only in a previous branch).
                %
                % They must also ensure that the const_var_map at the
                % program point just after the branched control structure
                % contains only entries that exist at the ends of all the
                % branches whose ends are actually reachable (to prevent
                % the code after it using information whose correctness
                % depends on the exact route that execution took to there).
/*  1 */        mgi_const_var_map       :: ml_ground_term_map,

/*  2 */        mgi_func_counter        :: counter,
/*  3 */        mgi_conv_var_counter    :: counter,
/*  4 */        mgi_used_succeeded_var  :: bool,

/*  5 */        mgi_closure_wrapper_defns :: list(mlds_function_defn),

/*  6 */        mgi_global_data         :: ml_global_data,

/*  7 */        mgi_rare_info           :: ml_gen_rare_info,
/*  8 */        mgi_sub_info            :: ml_gen_sub_info
            ).

:- type ml_gen_rare_info
    --->    ml_gen_rare_info(
                % The module_info. Read-only except when ml_accurate_gc.m
                % makes new type_info variables.
                %
                % XXX This is because it creates them by calling
                % polymorphism_make_type_info_var_raw, which updates
                % the module_info. It should be possible to avoid this.
/*  1 */        mgri_module_info        :: module_info,

                % The identity of the procedure we are generating code for.
                % Read-only.
/*  2 */        mgri_pred_proc_id       :: pred_proc_id,

                % The table containing information about the names and types
                % of the variables in the procedure we are generating code for.
                % Read-only except when ml_accurate_gc.m makes new type_info
                % variables.
/*  3 */        mgri_var_table          :: var_table,

                % Quick-access read-only copies of parts of the globals
                % structure taken from the module_info. Read-only.
/*  4 */        mgri_high_level_data    :: bool,
/*  - */        mgri_target             :: mlds_target_lang,
/*  - */        mgri_gc                 :: gc_method,
/*  - */        mgri_det_copy_out       :: bool,
/*  - */        mgri_nondet_copy_out    :: bool,
/*  - */        mgri_use_atomic_cells   :: maybe_use_atomic_cells,
/*  - */        mgri_profile_memory     :: bool,

/*  - */        mgri_num_ptag_bits      :: uint8,

                % The map of the constant ground structures generated by
                % ml_code_gen before we start generating code for procedures.
                % Read-only.
/*  5 */        mgri_const_struct_map   :: map(int, ml_ground_term),

                % Normally, we convert each HLDS variable to its own MLDS lval
                % each time the HLDS code refers it, using a simple
                % determininistic algorithm (the ml_gen_var function).
                % However, inside a commit scope, we currently translate
                % the output variables of that scope not to the MLDS lval
                % that the code outside the commit uses to refer to the
                % variable, but to a local *copy* of that variable;
                % when the goal inside the commit succeeds, we then assign
                % the value of the local copy to the MLDS variable used
                % outside the scope. The var_lvals field maps each output var
                % of every commit scope we are in to the local copy MLDS
                % variable.
                %
                % Currenly, this complexity is not actually necessary for the
                % C backend, which has nondet_copy_out set to "no". When the
                % output variable is generated, the code inside the commit
                % could assign its new value to the usual MLDS variable
                % (the one returned by ml_gen_var) directly. I (zs) have
                % just tried a bootcheck which did that, and it works.
                %
                % However, in the future, when we generate implicitly
                % AND-parallel MLDS code, this could come in useful for C as
                % well. This is because it is possible for an output variable
                % of a commit scope to become bound many times inside the scope
                % before the scope as a whole succeeds, if each binding but
                % the last is followed by a local failure. We want to signal
                % any consumer of the variable *outside* the scope that
                % the variable has actually been bound only when the commit
                % scope succeeds and *its usual MLDS variable* is assigned to,
                % while we want to signal any consumer *inside* the scope
                % when *the local copy* is assigned to. The distinction
                % would then give us two separate assignments to follow with
                % two separate signal operations for two separate classes
                % of consumers.
                %
                % Writeable.
/*  6 */        mgri_var_lvals          :: map(prog_var, mlds_lval),

                % The set of used environment variables. Writeable.
/*  7 */        mgri_env_var_names      :: set(string),

                % The set of warnings disabled in the current scope. Writeable.
/*  8 */        mgri_disabled_warnings  :: set(goal_warning),

/*  9 */        % For each procedure to whose tail calls we can apply
                % tail recursion optimization, this maps the label of that
                % procedure to (a) the information we need to generate
                % the code to jump to the start of that procedure, and
                % (b) a record of whether we *have* generated (either tail-
                % or nontail-) calls to this procedure.
                %
                % This field also contains the information we need to generate
                % the right set of warnings for calls marked as tail recursive
                % by mark_tail_calls.m but which we cannot actually turn
                % into tail calls, and the warnings so generated.
                %
                % Writeable.
                mgri_tail_rec_info      :: tail_rec_info
            ).

:- type ml_gen_sub_info
    --->    ml_gen_sub_info(
                % Output arguments that are passed by reference.
                % (We used to store the list of output arguments that are
                % returned as values in another field, but we don't need that
                % information anymore.)
/*  1 */        mgsi_byref_output_vars      :: set_of_progvar,

/*  2 */        mgsi_label_counter          :: counter,
/*  3 */        mgsi_aux_var_counter        :: counter,
/*  4 */        mgsi_cond_var_counter       :: counter,

/*  5 */        mgsi_packed_word_counter    :: counter,
/*  6 */        mgsi_packed_word_map        :: packed_word_map,

/*  7 */        mgsi_success_cont_stack     :: stack(success_cont),
/*  8 */        mgsi_func_nest_depth        :: int
            ).

% Access stats for the ml_gen_info structure:
%
%  i      read      same      diff   same%
%  0  18766903         0         0              module_info
%  1    548868         0         0              high_level_data
%  2    232588         0         0              target
%  3   2721027         0         0              gc
%  4    158848         0         0              pred_id
%  5    158848         0         0              proc_id
%  6   4647635         0         0              varset
%  7   7835272         0         0              vartypes
%  8   2964012     64588     11516  84.87%      byref_output_vars
%  9         0     65734     11516  85.09%      value_output_vars
% 10   2998238       594         0 100.00%      var_lvals
% 11    135553     13144     27277  32.52%      global_data
% 12     53820         0     53820   0.00%      func_counter
% 13       237         0       237   0.00%      label_counter
% 14      1805         0      1805   0.00%      aux_var_counter
% 15        21         0        21   0.00%      cond_var_counter
% 16     52544         0     52544   0.00%      conv_var_counter
% 17    727348    151728    477494  24.11%      const_var_map
% 18     32375         0         0              const_struct_map
% 19     14408         0      8197   0.00%      success_cont_stack
% 20    127335         0     32258   0.00%      closure_wrapper_defns
% 21     77412         8         8  50.00%      env_var_names
% 22    352575         0         2   0.00%      disabled_warnings
% 23     77250    341887     45872  88.17%      used_succeeded_var

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

:- pragma inline(pred(init_ml_gen_tscc_info/4)).

init_ml_gen_tscc_info(ModuleInfo, InSccMap, TsccKind, TsccInfo) :-
    % XXX FuncLabelCounter needs to start at 1 rather than 0,
    % otherwise the transformation for adding the shadow stack
    % for accurate garbage collection does not work properly,
    % and we will end up generating two C functions with the same name
    % (see ml_elim_nested.gen_gc_trace_func/8 for details).
    counter.init(1, FuncLabelCounter),
    counter.init(0, LabelCounter),
    counter.init(0, AuxVarCounter),
    counter.init(0, CondVarCounter),
    counter.init(0, PackedWordCounter),
    counter.init(0, ConvVarCounter),

    module_info_get_globals(ModuleInfo, Globals),
    globals.get_target(Globals, Target),
    % Can we implement tail calls by adding a label at the start of
    % the function, translating tail calls into a goto to that label?
    SupportsGoto = target_supports_goto(Target),
    % Can we implement tail calls by wrapping the function body inside
    % `while (true) { ... break; }', translating tail calls into `continue'?
    % Yes: all the current MLDS target languages support break and continue.
    % SupportsBreakContinue = target_supports_break_and_continue(Target),
    SupportsBreakContinue = yes,
    (
%       SupportsBreakContinue = no,
%       SupportsGoto = no,
%       unexpected($pred, "SupportsGoto = SupportsBreakContinue = no")
%   ;
%       SupportsBreakContinue = no,
%       SupportsGoto = yes,
%       LoopKind = tail_rec_loop_label_goto
%   ;
        SupportsBreakContinue = yes,
        SupportsGoto = no,
        LoopKind = tail_rec_loop_while_continue
    ;
        SupportsBreakContinue = yes,
        SupportsGoto = yes,
        (
            TsccKind = tscc_self_rec_only,
            PreferBreakContinueOption = prefer_while_loop_over_jump_self
        ;
            TsccKind = tscc_self_and_mutual_rec,
            PreferBreakContinueOption = prefer_while_loop_over_jump_mutual
        ),
        globals.lookup_bool_option(Globals, PreferBreakContinueOption,
            PreferBreakContinue),
        (
            PreferBreakContinue = no,
            LoopKind = tail_rec_loop_label_goto
        ;
            PreferBreakContinue = yes,
            LoopKind = tail_rec_loop_while_continue
        )
    ),
    TailRecInfo = tail_rec_info(InSccMap, LoopKind, TsccKind),
    TsccInfo = ml_gen_tscc_info(FuncLabelCounter, LabelCounter,
        AuxVarCounter, CondVarCounter, PackedWordCounter, ConvVarCounter,
        TailRecInfo).

ml_gen_info_init(ModuleInfo, Target, ConstStructMap, PredProcId, ProcInfo,
        GlobalData, TsccInfo) = Info :-
    TsccInfo = ml_gen_tscc_info(FuncLabelCounter, LabelCounter,
        AuxVarCounter, CondVarCounter, PackedWordCounter, ConvVarCounter,
        TailRecInfo),

    proc_info_get_var_table(ProcInfo, VarTable),
    HighLevelData = mlds_target_high_level_data(Target),
    module_info_get_globals(ModuleInfo, Globals),
    globals.get_gc_method(Globals, GC),
    globals.lookup_bool_option(Globals, det_copy_out, DetCopyOut),
    globals.lookup_bool_option(Globals, nondet_copy_out, NondetCopyOut),
    globals.get_opt_tuple(Globals, OptTuple),
    UseAtomicCells = OptTuple ^ ot_use_atomic_cells,
    globals.lookup_bool_option(Globals, profile_memory, ProfileMemory),
    globals.lookup_int_option(Globals, num_ptag_bits, NumPtagBitsInt),
    NumPtagBits = uint8.det_from_int(NumPtagBitsInt),
    map.init(VarLvals),
    set.init(EnvVarNames),
    set.init(DisabledWarnings),
    RareInfo = ml_gen_rare_info(
        ModuleInfo,
        PredProcId,
        VarTable,
        HighLevelData,
        Target,
        GC,
        DetCopyOut,
        NondetCopyOut,
        UseAtomicCells,
        ProfileMemory,
        NumPtagBits,
        ConstStructMap,
        VarLvals,
        EnvVarNames,
        DisabledWarnings,
        TailRecInfo
    ),

    set_of_var.init(ByRefOutputVars),
    map.init(PackedWordMap),
    stack.init(SuccContStack),
    FuncNestDepth = 0,
    SubInfo = ml_gen_sub_info(
        ByRefOutputVars,
        LabelCounter,
        AuxVarCounter,
        CondVarCounter,
        PackedWordCounter,
        PackedWordMap,
        SuccContStack,
        FuncNestDepth
    ),

    map.init(ConstVarMap),
    UsedSucceededVar = no,
    ClosureWrapperDefns = [],
    Info = ml_gen_info(
        ConstVarMap,
        FuncLabelCounter,
        ConvVarCounter,
        UsedSucceededVar,
        ClosureWrapperDefns,
        GlobalData,
        RareInfo,
        SubInfo
    ).

ml_gen_info_final(Info, EnvVarNames, ClosureWrapperDefns, GlobalData,
        TsccInfo) :-
    Info = ml_gen_info(
        _ConstVarMap,
        FuncLabelCounter,
        ConvVarCounter,
        _UsedSucceededVar,
        ClosureWrapperDefns,
        GlobalData,
        RareInfo,
        SubInfo
    ),
    RareInfo = ml_gen_rare_info(
        _ModuleInfo,
        _PredProcId,
        _VarTable,
        _HighLevelData,
        _Target,
        _GC,
        _DetCopyOut,
        _NondetCopyOut,
        _UseAtomicCells,
        _ProfileMemory,
        _NumPtagBits,
        _ConstStructMap,
        _VarLvals,
        EnvVarNames,
        _DisabledWarnings,
        TailRecInfo
    ),
    SubInfo = ml_gen_sub_info(
        _ByRefOutputVars,
        LabelCounter,
        AuxVarCounter,
        CondVarCounter,
        PackedWordCounter,
        _PackedWordMap,
        _SuccContStack,
        _FuncNestDepth
    ),
    TsccInfo = ml_gen_tscc_info(FuncLabelCounter, LabelCounter,
        AuxVarCounter, CondVarCounter, PackedWordCounter, ConvVarCounter,
        TailRecInfo).

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

:- pred ml_gen_info_get_func_counter(ml_gen_info::in, counter::out) is det.
:- pred ml_gen_info_get_conv_var_counter(ml_gen_info::in, counter::out) is det.
:- pred ml_gen_info_get_label_counter(ml_gen_info::in, counter::out) is det.
:- pred ml_gen_info_get_aux_var_counter(ml_gen_info::in, counter::out) is det.
:- pred ml_gen_info_get_cond_var_counter(ml_gen_info::in, counter::out) is det.
:- pred ml_gen_info_get_packed_word_counter(ml_gen_info::in,
    counter::out) is det.
:- pred ml_gen_info_get_success_cont_stack(ml_gen_info::in,
    stack(success_cont)::out) is det.

ml_gen_info_get_const_var_map(Info, X) :-
    X = Info ^ mgi_const_var_map.
ml_gen_info_get_func_counter(Info, X) :-
    X = Info ^ mgi_func_counter.
ml_gen_info_get_conv_var_counter(Info, X) :-
    X = Info ^ mgi_conv_var_counter.
ml_gen_info_get_used_succeeded_var(Info, X) :-
    X = Info ^ mgi_used_succeeded_var.
ml_gen_info_get_closure_wrapper_defns(Info, X) :-
    X = Info ^ mgi_closure_wrapper_defns.
ml_gen_info_get_global_data(Info, X) :-
    X = Info ^ mgi_global_data.

ml_gen_info_get_module_info(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_module_info.
ml_gen_info_get_pred_proc_id(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_pred_proc_id.
ml_gen_info_get_var_table(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_var_table.
ml_gen_info_get_high_level_data(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_high_level_data.
ml_gen_info_get_target(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_target.
ml_gen_info_get_gc(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_gc.
ml_gen_info_get_det_copy_out(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_det_copy_out.
ml_gen_info_get_nondet_copy_out(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_nondet_copy_out.
ml_gen_info_get_use_atomic_cells(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_use_atomic_cells.
ml_gen_info_get_profile_memory(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_profile_memory.
ml_gen_info_get_num_ptag_bits(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_num_ptag_bits.
ml_gen_info_get_const_struct_map(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_const_struct_map.
ml_gen_info_get_var_lvals(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_var_lvals.
ml_gen_info_get_env_var_names(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_env_var_names.
ml_gen_info_get_disabled_warnings(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_disabled_warnings.
ml_gen_info_get_tail_rec_info(Info, X) :-
    X = Info ^ mgi_rare_info ^ mgri_tail_rec_info.

ml_gen_info_get_byref_output_vars(Info, X) :-
    X = Info ^ mgi_sub_info ^ mgsi_byref_output_vars.
ml_gen_info_get_label_counter(Info, X) :-
    X = Info ^ mgi_sub_info ^ mgsi_label_counter.
ml_gen_info_get_aux_var_counter(Info, X) :-
    X = Info ^ mgi_sub_info ^ mgsi_aux_var_counter.
ml_gen_info_get_cond_var_counter(Info, X) :-
    X = Info ^ mgi_sub_info ^ mgsi_cond_var_counter.
ml_gen_info_get_packed_word_counter(Info, X) :-
    X = Info ^ mgi_sub_info ^ mgsi_packed_word_counter.
ml_gen_info_get_packed_word_map(Info, X) :-
    X = Info ^ mgi_sub_info ^ mgsi_packed_word_map.
ml_gen_info_get_success_cont_stack(Info, X) :-
    X = Info ^ mgi_sub_info ^ mgsi_success_cont_stack.
ml_gen_info_get_func_nest_depth(Info, X) :-
    X = Info ^ mgi_sub_info ^ mgsi_func_nest_depth.

:- pred ml_gen_info_set_func_counter(counter::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_conv_var_counter(counter::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_closure_wrapper_defns(list(mlds_function_defn)::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_env_var_names(set(string)::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_label_counter(counter::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_aux_var_counter(counter::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_cond_var_counter(counter::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_packed_word_counter(counter::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_success_cont_stack(stack(success_cont)::in,
    ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_info_set_func_nest_depth(int::in,
    ml_gen_info::in, ml_gen_info::out) is det.

ml_gen_info_set_const_var_map(X, !Info) :-
    ( if private_builtin.pointer_equal(X, !.Info ^ mgi_const_var_map) then
        true
    else
        !Info ^ mgi_const_var_map := X
    ).
ml_gen_info_set_func_counter(X, !Info) :-
    !Info ^ mgi_func_counter := X.
ml_gen_info_set_conv_var_counter(X, !Info) :-
    !Info ^ mgi_conv_var_counter := X.
ml_gen_info_set_used_succeeded_var(X, !Info) :-
    ( if X = !.Info ^ mgi_used_succeeded_var then
        true
    else
        !Info ^ mgi_used_succeeded_var := X
    ).
ml_gen_info_set_closure_wrapper_defns(X, !Info) :-
    !Info ^ mgi_closure_wrapper_defns := X.
ml_gen_info_set_global_data(X, !Info) :-
    ( if private_builtin.pointer_equal(X, !.Info ^ mgi_global_data) then
        true
    else
        !Info ^ mgi_global_data := X
    ).

ml_gen_info_set_module_info(X, !Info) :-
    RareInfo0 = !.Info ^ mgi_rare_info,
    RareInfo = RareInfo0 ^ mgri_module_info := X,
    !Info ^ mgi_rare_info := RareInfo.
ml_gen_info_set_var_table(X, !Info) :-
    RareInfo0 = !.Info ^ mgi_rare_info,
    RareInfo = RareInfo0 ^ mgri_var_table := X,
    !Info ^ mgi_rare_info := RareInfo.
ml_gen_info_set_var_lvals(X, !Info) :-
    RareInfo0 = !.Info ^ mgi_rare_info,
    ( if private_builtin.pointer_equal(X, RareInfo0 ^ mgri_var_lvals) then
        true
    else
        RareInfo = RareInfo0 ^ mgri_var_lvals := X,
        !Info ^ mgi_rare_info := RareInfo
    ).
ml_gen_info_set_env_var_names(X, !Info) :-
    RareInfo0 = !.Info ^ mgi_rare_info,
    RareInfo = RareInfo0 ^ mgri_env_var_names := X,
    !Info ^ mgi_rare_info := RareInfo.
ml_gen_info_set_disabled_warnings(X, !Info) :-
    RareInfo0 = !.Info ^ mgi_rare_info,
    RareInfo = RareInfo0 ^ mgri_disabled_warnings := X,
    !Info ^ mgi_rare_info := RareInfo.
ml_gen_info_set_tail_rec_info(X, !Info) :-
    RareInfo0 = !.Info ^ mgi_rare_info,
    RareInfo = RareInfo0 ^ mgri_tail_rec_info := X,
    !Info ^ mgi_rare_info := RareInfo.

ml_gen_info_set_byref_output_vars(X, !Info) :-
    SubInfo0 = !.Info ^ mgi_sub_info,
    ( if
        private_builtin.pointer_equal(X, SubInfo0 ^ mgsi_byref_output_vars)
    then
        true
    else
        SubInfo = SubInfo0 ^ mgsi_byref_output_vars := X,
        !Info ^ mgi_sub_info := SubInfo
    ).
ml_gen_info_set_label_counter(X, !Info) :-
    SubInfo0 = !.Info ^ mgi_sub_info,
    SubInfo = SubInfo0 ^ mgsi_label_counter := X,
    !Info ^ mgi_sub_info := SubInfo.
ml_gen_info_set_aux_var_counter(X, !Info) :-
    SubInfo0 = !.Info ^ mgi_sub_info,
    SubInfo = SubInfo0 ^ mgsi_aux_var_counter := X,
    !Info ^ mgi_sub_info := SubInfo.
ml_gen_info_set_cond_var_counter(X, !Info) :-
    SubInfo0 = !.Info ^ mgi_sub_info,
    SubInfo = SubInfo0 ^ mgsi_cond_var_counter := X,
    !Info ^ mgi_sub_info := SubInfo.
ml_gen_info_set_packed_word_counter(X, !Info) :-
    SubInfo0 = !.Info ^ mgi_sub_info,
    SubInfo = SubInfo0 ^ mgsi_packed_word_counter := X,
    !Info ^ mgi_sub_info := SubInfo.
ml_gen_info_set_packed_word_map(X, !Info) :-
    SubInfo0 = !.Info ^ mgi_sub_info,
    ( if
        private_builtin.pointer_equal(X, SubInfo0 ^ mgsi_packed_word_map)
    then
        true
    else
        SubInfo = SubInfo0 ^ mgsi_packed_word_map := X,
        !Info ^ mgi_sub_info := SubInfo
    ).
ml_gen_info_set_success_cont_stack(X, !Info) :-
    SubInfo0 = !.Info ^ mgi_sub_info,
    SubInfo = SubInfo0 ^ mgsi_success_cont_stack := X,
    !Info ^ mgi_sub_info := SubInfo.
ml_gen_info_set_func_nest_depth(X, !Info) :-
    SubInfo0 = !.Info ^ mgi_sub_info,
    SubInfo = SubInfo0 ^ mgsi_func_nest_depth := X,
    !Info ^ mgi_sub_info := SubInfo.

%---------------------------------------------------------------------------%
:- end_module ml_backend.ml_gen_info.
%---------------------------------------------------------------------------%
