%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1994-2012 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: code_info.m.
% Main authors: conway, zs.
%
% This file defines the code_info type and various operations on it.
% The code_info structure is the persistent part of the 'state'
% of the code generator. The other part of the code generator state,
% the location-dependent part, is in code_loc_dep.m.
%
% This file is organized into three submodules:
%
%   - the code_info structure and its access predicates
%   - simple wrappers around access predicates
%   - managing stack slots
%
%---------------------------------------------------------------------------%

:- module ll_backend.code_info.
:- interface.

:- import_module hlds.
:- import_module hlds.code_model.
:- import_module hlds.hlds_data.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_llds.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module libs.
:- import_module libs.globals.
:- import_module libs.optimization_options.
:- import_module libs.trace_params.
:- import_module ll_backend.continuation_info.
:- import_module ll_backend.global_data.
:- import_module ll_backend.layout.
:- import_module ll_backend.llds.
:- import_module ll_backend.trace_gen.
:- import_module mdbcomp.
:- import_module mdbcomp.goal_path.
:- import_module mdbcomp.prim_data.
:- 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 bool.
:- import_module counter.
:- import_module io.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module set.
:- import_module set_tree234.
:- import_module term.

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

:- implementation.

:- import_module backend_libs.
:- import_module backend_libs.proc_label.
:- import_module check_hlds.
:- import_module check_hlds.type_util.
:- import_module hlds.hlds_proc_util.
:- import_module libs.options.
:- import_module ll_backend.code_util.
:- import_module parse_tree.prog_type.

:- import_module cord.
:- import_module int.
:- import_module pair.
:- import_module require.
:- import_module string.
:- import_module uint8.

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

    % Submodule for the code_info type and its access predicates.
    %
    % This submodule has the following components:
    %
    %   declarations for exported access predicates
    %   declarations for non-exported access predicates
    %   the definition of the type and the init predicate
    %   the definition of the get access predicates
    %   the definition of the set access predicates
    %
    % Please keep the order of mention of the various fields
    % consistent in each of these five components.

:- interface.

:- type code_info.

    % Create a new code_info structure. Also return info about the non-fixed
    % stack slots used for tracing purposes.
    %
:- pred code_info_init(module_info::in, pred_id::in, proc_id::in,
    pred_info::in, proc_info::in, var_table::in, bool::in,
    static_cell_info::in, const_struct_map::in, io.text_output_stream::in,
    maybe(containing_goal_map)::in, list(string)::in, int::in,
    trace_slot_info::out, code_info::out) is det.

:- pred get_module_info(code_info::in, module_info::out) is det.
:- pred get_globals(code_info::in, globals::out) is det.
:- pred get_exprn_opts(code_info::in, exprn_opts::out) is det.
:- pred get_eff_trace_level(code_info::in, eff_trace_level::out) is det.
:- pred get_pred_id(code_info::in, pred_id::out) is det.
:- pred get_proc_id(code_info::in, proc_id::out) is det.
:- pred get_pred_info(code_info::in, pred_info::out) is det.
:- pred get_proc_info(code_info::in, proc_info::out) is det.
:- pred get_proc_label(code_info::in, proc_label::out) is det.
:- pred get_var_table(code_info::in, var_table::out) is det.
:- pred get_var_slot_count(code_info::in, int::out) is det.
:- pred get_maybe_trace_info(code_info::in, maybe(trace_info)::out) is det.
:- pred get_opt_no_return_calls(code_info::in, bool::out) is det.
:- pred get_emit_trail_ops(code_info::in, add_trail_ops::out) is det.
:- pred get_opt_trail_ops(code_info::in, bool::out) is det.
:- pred get_emit_region_ops(code_info::in, add_region_ops::out) is det.
:- pred get_opt_region_ops(code_info::in, bool::out) is det.
:- pred get_auto_comments(code_info::in, bool::out) is det.
:- pred get_lcmc_null(code_info::in, maybe_opt_lcmc_null::out) is det.
:- pred get_profile_memory(code_info::in, bool::out) is det.
:- pred get_may_use_atomic_alloc(code_info::in,
    may_use_atomic_alloc::out) is det.
:- pred get_num_ptag_bits(code_info::in, uint8::out) is det.
:- pred get_gc_method(code_info::in, gc_method::out) is det.
:- pred get_maybe_containing_goal_map(code_info::in,
    maybe(containing_goal_map)::out) is det.
:- pred get_const_struct_map(code_info::in, const_struct_map::out) is det.
% get_progress_stream is not exported.

:- pred get_label_counter(code_info::in, counter::out) is det.
:- pred get_succip_used(code_info::in, bool::out) is det.
:- pred get_layout_info(code_info::in, proc_label_layout_info::out) is det.
:- pred get_proc_trace_events(code_info::in, bool::out) is det.
:- pred get_max_regs_in_use_at_trace(code_info::in, int::out, int::out) is det.
:- pred get_created_temp_frame(code_info::in, bool::out) is det.
:- pred get_max_temp_slot_count(code_info::in, int::out) is det.
:- pred get_temp_content_map(code_info::in, map(lval, slot_contents)::out)
    is det.
:- pred get_persistent_temps(code_info::in, set(lval)::out) is det.
% get_closure_seq_counter is not exported.
:- pred get_closure_layouts(code_info::in, list(closure_proc_id_data)::out)
    is det.
:- pred get_static_cell_info(code_info::in, static_cell_info::out) is det.
:- pred get_alloc_sites(code_info::in, set_tree234(alloc_site_info)::out)
    is det.
:- pred get_used_env_vars(code_info::in, set(string)::out) is det.
:- pred get_out_of_line_code(code_info::in, llds_code::out) is det.

% set_maybe_trace_info is not exported.

% set_label_counter is not exported.
% set_succip_used is not exported.
% set_layout_info is not exported.
:- pred set_proc_trace_events(bool::in,
    code_info::in, code_info::out) is det.
:- pred set_max_regs_in_use_at_trace(int::in, int::in,
    code_info::in, code_info::out) is det.
:- pred set_max_temp_slot_count(int::in,
    code_info::in, code_info::out) is det.
:- pred set_temp_content_map(map(lval, slot_contents)::in,
    code_info::in, code_info::out) is det.
:- pred set_persistent_temps(set(lval)::in,
    code_info::in, code_info::out) is det.
% set_closure_seq_counter is not exported.
% set_closure_layouts is not exported.
:- pred set_created_temp_frame(bool::in,
    code_info::in, code_info::out) is det.
:- pred set_static_cell_info(static_cell_info::in,
    code_info::in, code_info::out) is det.
:- pred set_alloc_sites(set_tree234(alloc_site_info)::in,
    code_info::in, code_info::out) is det.
:- pred set_used_env_vars(set(string)::in,
    code_info::in, code_info::out) is det.

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

:- implementation.

:- func init_exprn_opts(globals) = exprn_opts.

:- pred init_maybe_trace_info(globals::in, proc_info::in, eff_trace_level::in,
    trace_slot_info::out, code_info::in, code_info::out) is det.

:- pred get_progress_stream(code_info::in, io.text_output_stream::out) is det.
:- pred get_closure_seq_counter(code_info::in, counter::out) is det.

:- pred set_maybe_trace_info(maybe(trace_info)::in,
    code_info::in, code_info::out) is det.

:- pred set_label_counter(counter::in,
    code_info::in, code_info::out) is det.
:- pred set_succip_used(bool::in,
    code_info::in, code_info::out) is det.
:- pred set_layout_info(proc_label_layout_info::in,
    code_info::in, code_info::out) is det.
:- pred set_closure_seq_counter(counter::in,
    code_info::in, code_info::out) is det.
:- pred set_closure_layouts(list(closure_proc_id_data)::in,
    code_info::in, code_info::out) is det.

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

    % The code_info structure has two groups of fields.
    %
    % Some fields are static; they are set when the code_info structure
    % is initialized, and never changed afterwards.
    %
    % The other fields record persistent information that does not depend
    % on a code location. Updates to these fields must remain effective
    % even when the code generator resets its location-dependent state.

:- type code_info
    --->    code_info(
                code_info_static        :: code_info_static,
                code_info_persistent    :: code_info_persistent
            ).

:- type code_info_static
    --->    code_info_static(
                % The module_info structure - you just never know
                % when you might need it.
                cis_module_info         :: module_info,

                % For the code generation options.
                cis_globals             :: globals,
                cis_exprn_opts          :: exprn_opts,

                cis_eff_trace_level     :: eff_trace_level,

                % The id of the current predicate.
                cis_pred_id             :: pred_id,

                % The id of the current procedure.
                cis_proc_id             :: proc_id,

                % The pred_info for the predicate containing this procedure.
                cis_pred_info           :: pred_info,

                % The proc_info for this procedure.
                cis_proc_info           :: proc_info,

                % The proc_label for this procedure.
                cis_proc_label          :: proc_label,

                % The variables in this procedure.
                cis_var_table           :: var_table,

                % The number of stack slots allocated. for storing variables.
                % (Some extra stack slots are used for saving and restoring
                % registers.)
                cis_var_slot_count      :: int,

                % Information about which stack slots the call sequence number
                % and depth are stored in, provided tracing is switched on.
                cis_maybe_trace_info    :: maybe(trace_info),

                % Should we optimize calls that cannot return?
                cis_opt_no_return_calls :: bool,

                % Should we emit trail operations?
                cis_emit_trail_ops      :: add_trail_ops,

                % Should we try to avoid generating trail operations?
                cis_opt_trail_ops       :: bool,

                % Should we emit region operations?
                cis_emit_region_ops     :: add_region_ops,

                % Should we try to avoid generating region operations?
                cis_opt_region_ops      :: bool,

                % The setting of --auto-comments.
                cis_auto_comments       :: bool,

                % The settings of --optimize-constructor-last-call-null,
                % --profile-memory, and --use-atomic-cells.
                cis_lcmc_null           :: maybe_opt_lcmc_null,
                cis_profile_memory      :: bool,
                cis_may_use_atomic_alloc :: may_use_atomic_alloc,

                % The number of primary tags bits we are using.
                cis_num_ptag_bits       :: uint8,

                % The GC method.
                cis_gc_method           :: gc_method,

                cis_containing_goal_map :: maybe(containing_goal_map),

                % Maps the number of an entry in the module's const_struct_db
                % to its rval.
                cis_const_struct_map    :: const_struct_map,

                % The stream to write any debugging output to.
                cis_progress_stream         :: io.text_output_stream
            ).

:- type code_info_persistent
    --->    code_info_persistent(
                % Counter for the local labels used by this procedure.
                cip_label_num_src           :: counter,

                % Do we need to store succip?
                cip_store_succip            :: bool,

                % Information on which values are live and where at which
                % labels, for tracing and/or accurate gc.
                cip_label_layout_info       :: proc_label_layout_info,

                % Did the procedure have any trace events?
                cip_proc_trace_events       :: bool,

                % At each call to MR_trace, we compute the highest rN and fN
                % registers that contain useful values. These slot contain the
                % maximum of these highest values. Therefore at all calls to
                % MR_trace in the procedure, we need only save the registers
                % whose numbers are equal to or smaller than this field.
                % This slot contains -1 if tracing is not enabled.
                cip_max_reg_r_used          :: int,
                cip_max_reg_f_used          :: int,

                % The maximum number of extra temporary stackslots that
                % have been used during the procedure.
                cip_stackslot_max           :: int,

                % The temporary locations that have ever been used on the
                % stack, and what they contain. Once we have used a stack slot
                % to store e.g. a ticket, we never reuse that slot to hold
                % something else, e.g. a saved hp. This policy prevents us
                % from making such conflicting choices in parallel branches,
                % which would make it impossible to describe to gc what the
                % slot contains after the end of the branched control
                % structure.
                cip_temp_content_map        :: map(lval, slot_contents),

                % Stack slot locations that should not be released even when
                % the code generator resets its location-dependent state.
                cip_persistent_temps        :: set(lval),

                cip_closure_layout_seq      :: counter,

                % Closure layout structures generated by this procedure.
                cip_closure_layouts         :: list(closure_proc_id_data),

                % True iff the procedure has created one or more temporary
                % nondet frames.
                cip_created_temp_frame      :: bool,

                cip_static_cell_info        :: static_cell_info,

                cip_alloc_sites             :: set_tree234(alloc_site_info),

                cip_used_env_vars           :: set(string),

                % A counter and table for allocating and maintaining slots
                % where string IDs will be placed at runtime for threadscope
                % profiling. The actual string IDs are allocated at runtime
                % and their IDs are placed in an array slot which can be
                % referred to statically.
                cip_ts_string_table_size    :: int,
                cip_ts_rev_string_table     :: list(string),

                % Code that is part of this procedure, but that can be placed
                % after the procedure without a cache penalty. For example,
                % code that is spawned off by loop control is placed here.
                cip_out_of_line_code        :: llds_code
            ).

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

code_info_init(ModuleInfo, PredId, ProcId, PredInfo, ProcInfo, VarTable,
        SaveSuccip, StaticCellInfo, ConstStructMap, ProgressStream,
        MaybeContainingGoalMap, TSRevStringTable, TSStringTableSize,
        TraceSlotInfo, CodeInfo) :-
    % argument ModuleInfo
    module_info_get_globals(ModuleInfo, Globals),
    ExprnOpts = init_exprn_opts(Globals),
    % argument PredId
    % argument ProcId
    % argument PredInfo
    % argument ProcInfo
    ProcLabel = make_proc_label(ModuleInfo, PredId, ProcId),
    proc_info_get_stack_slots(ProcInfo, StackSlots),
    max_var_slot(StackSlots, VarSlotMax),
    globals.get_trace_level(Globals, TraceLevel),
    EffTraceLevel =
        eff_trace_level_for_proc(ModuleInfo, PredInfo, ProcInfo, TraceLevel),
    trace_reserved_slots(Globals, ProcInfo, EffTraceLevel, FixedSlots, _),
    int.max(VarSlotMax, FixedSlots, SlotMax),
    MaybeTraceInfo = no,
    globals.lookup_bool_option(Globals, opt_no_return_calls, OptNoReturnCalls),
    globals.lookup_bool_option(Globals, use_trail, UseTrail),
    globals.lookup_bool_option(Globals, disable_trail_ops, DisableTrailOps),
    ( if
        UseTrail = yes,
        DisableTrailOps = no
    then
        EmitTrailOps = add_trail_ops
    else
        EmitTrailOps = do_not_add_trail_ops
    ),
    globals.lookup_bool_option(Globals, optimize_trail_usage, OptTrailOps),
    globals.get_opt_tuple(Globals, OptTuple),
    UseRegions = OptTuple ^ ot_analyse_regions,
    (
        UseRegions = analyse_regions,
        EmitRegionOps = add_region_ops
    ;
        UseRegions = do_not_analyse_regions,
        EmitRegionOps = do_not_add_region_ops
    ),
    globals.lookup_bool_option(Globals, optimize_region_ops, OptRegionOps),
    globals.lookup_bool_option(Globals, auto_comments, AutoComments),
    LCMCNull = OptTuple ^ ot_opt_lcmc_null,
    globals.lookup_bool_option(Globals, profile_memory, ProfileMemory),
    UseAtomicCells = OptTuple ^ ot_use_atomic_cells,
    (
        UseAtomicCells = do_not_use_atomic_cells,
        InitMayUseAtomic = may_not_use_atomic_alloc
    ;
        UseAtomicCells = use_atomic_cells,
        InitMayUseAtomic = may_use_atomic_alloc
    ),
    globals.lookup_int_option(Globals, num_ptag_bits, NumPtagBitsInt),
    NumPtagBits = uint8.det_from_int(NumPtagBitsInt),
    globals.get_gc_method(Globals, GCMethod),
    % argument MaybeContainingGoalMap
    % argument ConstStructMap

    CodeInfoStatic0 = code_info_static(
        ModuleInfo,
        Globals,
        ExprnOpts,
        EffTraceLevel,
        PredId,
        ProcId,
        PredInfo,
        ProcInfo,
        ProcLabel,
        VarTable,
        SlotMax,
        MaybeTraceInfo,
        OptNoReturnCalls,
        EmitTrailOps,
        OptTrailOps,
        EmitRegionOps,
        OptRegionOps,
        AutoComments,
        LCMCNull,
        ProfileMemory,
        InitMayUseAtomic,
        NumPtagBits,
        GCMethod,
        MaybeContainingGoalMap,
        ConstStructMap,
        ProgressStream
    ),

    LabelNumCounter0 = counter.init(1),
    % argument SaveSuccip
    map.init(LayoutMap),
    ProcTraceEvents = no,
    MaxRegRUsed = -1,
    MaxRegFUsed = -1,
    MaxTempSlotCount = 0,
    map.init(TempContentMap),
    set.init(PersistentTemps),
    ClosureLayoutSeqNumCounter0 = counter.init(1),
    ClosureLayouts = [],
    CreatedTempFrame = no,
    % argument StaticCellInfo
    AllocSitesMap0 = set_tree234.init,
    set.init(UsedEnvVars),
    OutOfLineCode = cord.init,

    CodeInfoPersistent0 = code_info_persistent(
        LabelNumCounter0,
        SaveSuccip,
        LayoutMap,
        ProcTraceEvents,
        MaxRegRUsed,
        MaxRegFUsed,
        MaxTempSlotCount,
        TempContentMap,
        PersistentTemps,
        ClosureLayoutSeqNumCounter0,
        ClosureLayouts,
        CreatedTempFrame,
        StaticCellInfo,
        AllocSitesMap0,
        UsedEnvVars,
        TSStringTableSize,
        TSRevStringTable,
        OutOfLineCode
    ),
    CodeInfo0 = code_info(CodeInfoStatic0, CodeInfoPersistent0),
    init_maybe_trace_info(Globals, ProcInfo, EffTraceLevel, TraceSlotInfo,
        CodeInfo0, CodeInfo).

init_exprn_opts(Globals) = ExprnOpts :-
    globals.lookup_bool_option(Globals, gcc_non_local_gotos, OptNLG),
    (
        OptNLG = yes,
        NLG = have_non_local_gotos
    ;
        OptNLG = no,
        NLG = do_not_have_non_local_gotos
    ),
    globals.lookup_bool_option(Globals, asm_labels, OptASM),
    (
        OptASM = yes,
        ASM = have_asm_labels
    ;
        OptASM = no,
        ASM = do_not_have_asm_labels
    ),
    globals.get_opt_tuple(Globals, OptTuple),
    OptSGCell = OptTuple ^ ot_use_static_ground_cells,
    (
        OptSGCell = use_static_ground_cells,
        SGCell = have_static_ground_cells
    ;
        OptSGCell = do_not_use_static_ground_cells,
        SGCell = do_not_have_static_ground_cells
    ),
    globals.lookup_bool_option(Globals, unboxed_float, OptUBF),
    (
        OptUBF = yes,
        UBF = have_unboxed_floats
    ;
        OptUBF = no,
        UBF = do_not_have_unboxed_floats
    ),
    globals.lookup_bool_option(Globals, use_float_registers, OptFloatRegs),
    (
        OptFloatRegs = yes,
        UseFloatRegs = use_float_registers
    ;
        OptFloatRegs = no,
        UseFloatRegs = do_not_use_float_registers
    ),
    double_width_floats_on_det_stack(Globals, FloatDwords),
    (
        FloatDwords = yes,
        DetStackFloatWidth = double_width
    ;
        FloatDwords = no,
        DetStackFloatWidth = single_width
    ),
    globals.lookup_bool_option(Globals, unboxed_int64s, OptUBI64s),
    (
        OptUBI64s = yes,
        UBI64s = have_unboxed_int64s
    ;
        OptUBI64s = no,
        UBI64s = do_not_have_unboxed_int64s
    ),
    OptSGFloat = OptTuple ^ ot_use_static_ground_floats,
    (
        OptSGFloat = use_static_ground_floats,
        SGFloat = have_static_ground_floats
    ;
        OptSGFloat = do_not_use_static_ground_floats,
        SGFloat = do_not_have_static_ground_floats
    ),
    OptSGInt64s = OptTuple ^ ot_use_static_ground_int64s,
    (
        OptSGInt64s = use_static_ground_int64s,
        SGInt64s = have_static_ground_int64s
    ;
        OptSGInt64s = do_not_use_static_ground_int64s,
        SGInt64s = do_not_have_static_ground_int64s
    ),
    OptStaticCodeAddr = OptTuple ^ ot_use_static_code_addresses,
    (
        OptStaticCodeAddr = use_static_code_addresses,
        StaticCodeAddrs = have_static_code_addresses
    ;
        OptStaticCodeAddr = do_not_use_static_code_addresses,
        StaticCodeAddrs = do_not_have_static_code_addresses
    ),
    ExprnOpts = exprn_opts(NLG, ASM, UBF, UseFloatRegs, DetStackFloatWidth,
        UBI64s, SGCell, SGFloat, SGInt64s, StaticCodeAddrs).

init_maybe_trace_info(Globals, ProcInfo, EffTraceLevel, TraceSlotInfo, !CI) :-
    TraceEnabled = is_exec_trace_enabled_at_eff_trace_level(EffTraceLevel),
    (
        TraceEnabled = exec_trace_is_enabled,
        proc_info_get_has_tail_rec_call(ProcInfo, HasTailRecCall),
        HasTailRecCall =
            has_tail_rec_call(HasSelfTailRecCall, _HasMutualTailRecCall),
        (
            HasSelfTailRecCall = has_self_tail_rec_call,
            get_next_label(TailRecLabel, !CI),
            MaybeTailRecLabel = yes(TailRecLabel)
        ;
            HasSelfTailRecCall = has_no_self_tail_rec_call,
            MaybeTailRecLabel = no
        ),
        trace_setup(Globals, ProcInfo, EffTraceLevel, MaybeTailRecLabel,
            TraceSlotInfo, TraceInfo, !CI),
        set_maybe_trace_info(yes(TraceInfo), !CI)
    ;
        TraceEnabled = exec_trace_is_not_enabled,
        TraceSlotInfo = trace_slot_info(no, no, no, no, no, no)
    ).

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

get_module_info(CI, X) :-
    X = CI ^ code_info_static ^ cis_module_info.
get_globals(CI, X) :-
    X = CI ^ code_info_static ^ cis_globals.
get_exprn_opts(CI, X) :-
    X = CI ^ code_info_static ^ cis_exprn_opts.
get_eff_trace_level(CI, X) :-
    X = CI ^ code_info_static ^ cis_eff_trace_level.
get_pred_id(CI, X) :-
    X = CI ^ code_info_static ^ cis_pred_id.
get_proc_id(CI, X) :-
    X = CI ^ code_info_static ^ cis_proc_id.
get_pred_info(CI, X) :-
    X = CI ^ code_info_static ^ cis_pred_info.
get_proc_info(CI, X) :-
    X = CI ^ code_info_static ^ cis_proc_info.
get_proc_label(CI, X) :-
    X = CI ^ code_info_static ^ cis_proc_label.
get_var_table(CI, X) :-
    X = CI ^ code_info_static ^ cis_var_table.
get_var_slot_count(CI, X) :-
    X = CI ^ code_info_static ^ cis_var_slot_count.
get_maybe_trace_info(CI, X) :-
    X = CI ^ code_info_static ^ cis_maybe_trace_info.
get_opt_no_return_calls(CI, X) :-
    X = CI ^ code_info_static ^ cis_opt_no_return_calls.
get_emit_trail_ops(CI, X) :-
    X = CI ^ code_info_static ^ cis_emit_trail_ops.
get_opt_trail_ops(CI, X) :-
    X = CI ^ code_info_static ^ cis_opt_trail_ops.
get_emit_region_ops(CI, X) :-
    X = CI ^ code_info_static ^ cis_emit_region_ops.
get_opt_region_ops(CI, X) :-
    X = CI ^ code_info_static ^ cis_opt_region_ops.
get_auto_comments(CI, X) :-
    X = CI ^ code_info_static ^ cis_auto_comments.
get_lcmc_null(CI, X) :-
    X = CI ^ code_info_static ^ cis_lcmc_null.
get_profile_memory(CI, X) :-
    X = CI ^ code_info_static ^ cis_profile_memory.
get_may_use_atomic_alloc(CI, X) :-
    X = CI ^ code_info_static ^ cis_may_use_atomic_alloc.
get_num_ptag_bits(CI, X) :-
    X = CI ^ code_info_static ^ cis_num_ptag_bits.
get_gc_method(CI, X) :-
    X = CI ^ code_info_static ^ cis_gc_method.
get_maybe_containing_goal_map(CI, X) :-
    X = CI ^ code_info_static ^ cis_containing_goal_map.
get_const_struct_map(CI, X) :-
    X = CI ^ code_info_static ^ cis_const_struct_map.
get_progress_stream(CI, X) :-
    X = CI ^ code_info_static ^ cis_progress_stream.

get_label_counter(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_label_num_src.
get_succip_used(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_store_succip.
get_layout_info(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_label_layout_info.
get_proc_trace_events(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_proc_trace_events.
get_max_regs_in_use_at_trace(CI, MaxRegR, MaxRegF) :-
    MaxRegR = CI ^ code_info_persistent ^ cip_max_reg_r_used,
    MaxRegF = CI ^ code_info_persistent ^ cip_max_reg_f_used.
get_created_temp_frame(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_created_temp_frame.
get_max_temp_slot_count(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_stackslot_max.
get_temp_content_map(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_temp_content_map.
get_persistent_temps(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_persistent_temps.
get_closure_seq_counter(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_closure_layout_seq.
get_closure_layouts(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_closure_layouts.
get_static_cell_info(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_static_cell_info.
get_alloc_sites(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_alloc_sites.
get_used_env_vars(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_used_env_vars.
get_out_of_line_code(CI, X) :-
    X = CI ^ code_info_persistent ^ cip_out_of_line_code.

set_maybe_trace_info(X, !CI) :-
    !CI ^ code_info_static ^ cis_maybe_trace_info := X.

set_label_counter(X, !CI) :-
    !CI ^ code_info_persistent ^ cip_label_num_src := X.
set_succip_used(X, !CI) :-
    !CI ^ code_info_persistent ^ cip_store_succip := X.
set_layout_info(X, !CI) :-
    !CI ^ code_info_persistent ^ cip_label_layout_info := X.
set_proc_trace_events(X, !CI) :-
    !CI ^ code_info_persistent ^ cip_proc_trace_events := X.
set_max_regs_in_use_at_trace(MR, MF, !CI) :-
    !CI ^ code_info_persistent ^ cip_max_reg_r_used := MR,
    !CI ^ code_info_persistent ^ cip_max_reg_f_used := MF.
set_max_temp_slot_count(X, !CI) :-
    !CI ^ code_info_persistent ^ cip_stackslot_max := X.
set_temp_content_map(X, !CI) :-
    !CI ^ code_info_persistent ^ cip_temp_content_map := X.
set_persistent_temps(X, !CI) :-
    !CI ^ code_info_persistent ^ cip_persistent_temps := X.
set_closure_seq_counter(X, !CI) :-
    !CI ^ code_info_persistent ^ cip_closure_layout_seq := X.
set_closure_layouts(X, !CI) :-
    !CI ^ code_info_persistent ^ cip_closure_layouts := X.
set_created_temp_frame(X, !CI) :-
    !CI ^ code_info_persistent ^ cip_created_temp_frame := X.
set_static_cell_info(X, !CI) :-
    !CI ^ code_info_persistent ^ cip_static_cell_info := X.
set_alloc_sites(X, !CI) :-
    !CI ^ code_info_persistent ^ cip_alloc_sites := X.
set_used_env_vars(X, !CI) :-
    !CI ^ code_info_persistent ^ cip_used_env_vars := X.

:- pred max_var_slot(stack_slots::in, int::out) is det.

max_var_slot(StackSlots, SlotCount) :-
    map.values(StackSlots, StackSlotList),
    max_var_slot_loop(StackSlotList, 0, SlotCount).

:- pred max_var_slot_loop(list(stack_slot)::in, int::in, int::out) is det.

max_var_slot_loop([], !Max).
max_var_slot_loop([Slot | Slots], !Max) :-
    (
        Slot = det_slot(N, Width)
    ;
        Slot = parent_det_slot(N, Width)
    ;
        Slot = nondet_slot(N),
        Width = single_width
    ),
    (
        Width = single_width,
        int.max(N, !Max)
    ;
        Width = double_width,
        int.max(N + 1, !Max)
    ),
    max_var_slot_loop(Slots, !Max).

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

    % Submodule for simple wrappers around access predicates.

:- interface.

    % Get the hlds mapping from variables to stack slots.
    %
:- pred get_stack_slots(code_info::in, stack_slots::out) is det.

    % Find out whether the body of the current procedure should use
    % typeinfo liveness.
    %
:- func body_typeinfo_liveness(code_info) = bool.

    % Find out the type of the given variable.
    %
:- func variable_type(code_info, prog_var) = mer_type.

    % Compute the principal type constructor of the given type, and return
    % the definition of this type constructor, if it has one (some type
    % constructors are built in, and some are hidden behind abstraction
    % barriers).
    %
:- pred search_type_defn(code_info::in, mer_type::in, hlds_type_defn::out) is
    semidet.

    % Compute the principal type constructor of the given type, and return
    % the definition of this type constructor. Abort if it doesn't have a
    % definition (e.g. because it is a builtin).
    %
:- func lookup_type_defn(code_info, mer_type) = hlds_type_defn.

:- func lookup_cheaper_tag_test(code_info, mer_type) = maybe_cheaper_tag_test.

:- func filter_region_vars(code_info, set_of_progvar) = set_of_progvar.

    % Get the code model of the current procedure.
    %
:- func get_proc_model(code_info) = code_model.

    % Get the list of the head variables of the current procedure.
    %
:- func get_headvars(code_info) = list(prog_var).

    % Get the call argument information for the current procedure
    %
:- func get_arginfo(code_info) = list(arg_info).

    % Get the call argument info for a given mode of a given predicate
    %
:- func get_pred_proc_arginfo(code_info, pred_id, proc_id) = list(arg_info).

:- type for_call_or_closure
    --->    for_immediate_call
    ;       for_closure.

    % Create a code address which holds the address of the specified procedure.
    % The fourth argument should be `for_immediate_call' if the caller
    % will use the returned address only to construct calls from the
    % current procedure, and `for_closure' if the returned address is
    % to be put into a closure. (These are the only circumstances in which
    % this predicate is called.)
    %
:- func make_proc_entry_label(code_info, module_info, pred_id, proc_id,
    for_call_or_closure) = code_addr.

    % Generate the next local label in sequence.
    %
:- pred get_next_label(label::out, code_info::in, code_info::out)
    is det.

    % Note that the succip slot is used, and thus cannot be optimized away.
    %
:- pred succip_is_used(code_info::in, code_info::out) is det.

:- pred add_trace_layout_for_label(label::in, term.context::in, trace_port::in,
    bool::in, forward_goal_path::in, maybe(user_event_info)::in,
    layout_label_info::in, code_info::in, code_info::out) is det.

:- pred get_next_closure_seq_no(int::out,
    code_info::in, code_info::out) is det.

:- pred add_resume_layout_for_label(label::in,
    layout_label_info::in, code_info::in, code_info::out) is det.

:- pred add_closure_layout(closure_proc_id_data::in,
    code_info::in, code_info::out) is det.

:- pred add_threadscope_string(string::in, int::out,
    code_info::in, code_info::out) is det.

:- pred get_threadscope_rev_string_table(code_info::in,
    list(string)::out, int::out) is det.

:- pred add_scalar_static_cell(list(typed_rval)::in,
    data_id::out, code_info::in, code_info::out) is det.

:- pred add_scalar_static_cell_natural_types(list(rval)::in,
    data_id::out, code_info::in, code_info::out) is det.

:- pred add_vector_static_cell(list(llds_type)::in, list(list(rval))::in,
    data_id::out, code_info::in, code_info::out) is det.

:- pred maybe_add_alloc_site_info(prog_context::in, string::in, int::in,
    maybe(alloc_site_id)::out, code_info::in, code_info::out) is det.

:- pred add_alloc_site_info(prog_context::in, string::in, int::in,
    alloc_site_id::out, code_info::in, code_info::out) is det.

    % Should we add trail ops to the code we generate for the goal with the
    % given goal_info. This will be 'no' unless we are in a trailing grade.
    %
:- func should_add_trail_ops(code_info, hlds_goal_info) = add_trail_ops.

    % Should we add region ops to the code we generate for the goal with the
    % given goal_info. This will be 'no' unless we are in a rbmm grade.
    %
:- func should_add_region_ops(code_info, hlds_goal_info) = add_region_ops.

:- pred get_containing_goal_map(code_info::in, containing_goal_map::out)
    is det.

:- pred add_out_of_line_code(llds_code::in, code_info::in, code_info::out)
    is det.

    % Should we trace the operation of the code generator?
    %
:- pred should_trace_code_gen(code_info::in, maybe(io.text_output_stream)::out)
    is det.

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

:- implementation.

get_stack_slots(CI, StackSlots) :-
    get_proc_info(CI, ProcInfo),
    proc_info_get_stack_slots(ProcInfo, StackSlots).

body_typeinfo_liveness(CI) = TypeInfoLiveness :-
    get_module_info(CI, ModuleInfo),
    get_pred_id(CI, PredId),
    module_info_pred_info(ModuleInfo, PredId, PredInfo),
    get_globals(CI, Globals),
    body_should_use_typeinfo_liveness(PredInfo, Globals, TypeInfoLiveness).

variable_type(CI, Var) = Type :-
    get_var_table(CI, VarTable),
    lookup_var_entry(VarTable, Var, Entry),
    Type = Entry ^ vte_type.

search_type_defn(CI, Type, TypeDefn) :-
    get_module_info(CI, ModuleInfo),
    type_to_ctor_det(Type, TypeCtor),
    module_info_get_type_table(ModuleInfo, TypeTable),
    search_type_ctor_defn(TypeTable, TypeCtor, TypeDefn).

lookup_type_defn(CI, Type) = TypeDefn :-
    ( if search_type_defn(CI, Type, TypeDefnPrime) then
        TypeDefn = TypeDefnPrime
    else
        unexpected($pred, "type ctor has no definition")
    ).

lookup_cheaper_tag_test(CI, Type) = CheaperTagTest :-
    ( if
        search_type_defn(CI, Type, TypeDefn),
        get_type_defn_body(TypeDefn, TypeBody),
        TypeBody = hlds_du_type(type_body_du(_, _, _, MaybeRepn, _)),
        MaybeRepn = yes(Repn)
    then
        CheaperTagTest = Repn ^ dur_cheaper_tag_test
    else
        CheaperTagTest = no_cheaper_tag_test
    ).

filter_region_vars(CI, ForwardLiveVarsBeforeGoal) = RegionVars :-
    get_var_table(CI, VarTable),
    RegionVars = set_of_var.filter(is_region_var(VarTable),
        ForwardLiveVarsBeforeGoal).

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

get_proc_model(CI) = CodeModel :-
    get_proc_info(CI, ProcInfo),
    CodeModel = proc_info_interface_code_model(ProcInfo).

get_headvars(CI) = HeadVars :-
    get_module_info(CI, ModuleInfo),
    get_pred_id(CI, PredId),
    get_proc_id(CI, ProcId),
    module_info_pred_proc_info(ModuleInfo, PredId, ProcId, _, ProcInfo),
    proc_info_get_headvars(ProcInfo, HeadVars).

get_arginfo(CI) = ArgInfo :-
    get_pred_id(CI, PredId),
    get_proc_id(CI, ProcId),
    ArgInfo = get_pred_proc_arginfo(CI, PredId, ProcId).

get_pred_proc_arginfo(CI, PredId, ProcId) = ArgInfo :-
    get_module_info(CI, ModuleInfo),
    module_info_pred_proc_info(ModuleInfo, PredId, ProcId, _, ProcInfo),
    proc_info_arg_info(ProcInfo, ArgInfo).

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

make_proc_entry_label(CI, ModuleInfo, PredId, ProcId, CallOrClosure)
        = CodeAddr :-
    (
        CallOrClosure = for_closure,
        ForFromWhere = for_from_everywhere
    ;
        CallOrClosure = for_immediate_call,
        get_globals(CI, Globals),
        globals.get_opt_tuple(Globals, OptTuple),
        UseJustOneCFunc = OptTuple ^ ot_use_just_one_c_func,
        get_pred_id(CI, CurPredId),
        get_proc_id(CI, CurProcId),
        ForFromWhere = for_from_proc(UseJustOneCFunc, CurPredId, CurProcId)
    ),
    CodeAddr = make_entry_label(ModuleInfo, PredId, ProcId, ForFromWhere).

get_next_label(Label, !CI) :-
    get_proc_label(!.CI, ProcLabel),
    get_label_counter(!.CI, C0),
    counter.allocate(N, C0, C),
    set_label_counter(C, !CI),
    Label = internal_label(N, ProcLabel).

succip_is_used(!CI) :-
    set_succip_used(yes, !CI).

add_trace_layout_for_label(Label, Context, Port, IsHidden, GoalPath,
        MaybeSolverEventInfo, Layout, !CI) :-
    get_layout_info(!.CI, Internals0),
    Exec = yes(trace_port_layout_info(Context, Port, IsHidden, GoalPath,
        MaybeSolverEventInfo, Layout)),
    (
        Label = internal_label(LabelNum, _)
    ;
        Label = entry_label(_, _),
        unexpected($pred, "entry")
    ),
    ( if map.search(Internals0, LabelNum, Internal0) then
        Internal0 = internal_layout_info(Exec0, Resume, Return),
        (
            Exec0 = no
        ;
            Exec0 = yes(_),
            unexpected($pred, "already known label")
        ),
        Internal = internal_layout_info(Exec, Resume, Return),
        map.det_update(LabelNum, Internal, Internals0, Internals)
    else
        Internal = internal_layout_info(Exec, no, no),
        map.det_insert(LabelNum, Internal, Internals0, Internals)
    ),
    set_layout_info(Internals, !CI).

get_next_closure_seq_no(SeqNo, !CI) :-
    get_closure_seq_counter(!.CI, C0),
    counter.allocate(SeqNo, C0, C),
    set_closure_seq_counter(C, !CI).

add_resume_layout_for_label(Label, LayoutInfo, !CI) :-
    get_layout_info(!.CI, Internals0),
    Resume = yes(LayoutInfo),
    (
        Label = internal_label(LabelNum, _)
    ;
        Label = entry_label(_, _),
        unexpected($pred, "entry")
    ),
    ( if map.search(Internals0, LabelNum, Internal0) then
        Internal0 = internal_layout_info(Exec, Resume0, Return),
        (
            Resume0 = no
        ;
            Resume0 = yes(_),
            unexpected($pred, "already known label")
        ),
        Internal = internal_layout_info(Exec, Resume, Return),
        map.det_update(LabelNum, Internal, Internals0, Internals)
    else
        Internal = internal_layout_info(no, Resume, no),
        map.det_insert(LabelNum, Internal, Internals0, Internals)
    ),
    set_layout_info(Internals, !CI).

add_closure_layout(ClosureLayout, !CI) :-
    get_closure_layouts(!.CI, ClosureLayouts),
    set_closure_layouts([ClosureLayout | ClosureLayouts], !CI).

add_threadscope_string(String, SlotNum, !CI) :-
    Persistent0 = !.CI ^ code_info_persistent,
    Size0 = Persistent0 ^ cip_ts_string_table_size,
    RevTable0 = Persistent0 ^ cip_ts_rev_string_table,
    SlotNum = Size0,
    Size = Size0 + 1,
    RevTable = [String | RevTable0],
    Persistent1 = Persistent0 ^ cip_ts_string_table_size := Size,
    Persistent = Persistent1 ^ cip_ts_rev_string_table := RevTable,
    !CI ^ code_info_persistent := Persistent.

get_threadscope_rev_string_table(CI, RevTable, TableSize) :-
    RevTable = CI ^ code_info_persistent ^ cip_ts_rev_string_table,
    TableSize = CI ^ code_info_persistent ^ cip_ts_string_table_size.

add_scalar_static_cell(RvalsTypes, DataAddr, !CI) :-
    get_static_cell_info(!.CI, StaticCellInfo0),
    global_data.add_scalar_static_cell(RvalsTypes, DataAddr,
        StaticCellInfo0, StaticCellInfo),
    set_static_cell_info(StaticCellInfo, !CI).

add_scalar_static_cell_natural_types(Rvals, DataAddr, !CI) :-
    get_static_cell_info(!.CI, StaticCellInfo0),
    global_data.add_scalar_static_cell_natural_types(Rvals, DataAddr,
        StaticCellInfo0, StaticCellInfo),
    set_static_cell_info(StaticCellInfo, !CI).

add_vector_static_cell(Types, Vector, DataAddr, !CI) :-
    get_static_cell_info(!.CI, StaticCellInfo0),
    global_data.add_vector_static_cell(Types, Vector, DataAddr,
        StaticCellInfo0, StaticCellInfo),
    set_static_cell_info(StaticCellInfo, !CI).

maybe_add_alloc_site_info(Context, VarTypeMsg, Size, MaybeAllocId, !CI) :-
    get_profile_memory(!.CI, ProfileMemory),
    (
        ProfileMemory = yes,
        add_alloc_site_info(Context, VarTypeMsg, Size, AllocId, !CI),
        MaybeAllocId = yes(AllocId)
    ;
        ProfileMemory = no,
        MaybeAllocId = no
    ).

add_alloc_site_info(Context, VarTypeMsg, Size, AllocId, !CI) :-
    get_proc_label(!.CI, ProcLabel),
    AllocSite = alloc_site_info(ProcLabel, Context, VarTypeMsg, Size),
    AllocId = alloc_site_id(AllocSite),
    get_alloc_sites(!.CI, AllocSites0),
    set_tree234.insert(AllocSite, AllocSites0, AllocSites),
    set_alloc_sites(AllocSites, !CI).

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

should_add_trail_ops(CodeInfo, _GoalInfo) = AddTrailOps :-
    % XXX We will eventually need to make use of GoalInfo here.
    get_emit_trail_ops(CodeInfo, AddTrailOps).

should_add_region_ops(CodeInfo, _GoalInfo) = AddRegionOps :-
    % XXX We will eventually need to make use of GoalInfo here.
    get_emit_region_ops(CodeInfo, AddRegionOps).

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

get_containing_goal_map(CI, ContainingGoalMap) :-
    get_maybe_containing_goal_map(CI, MaybeContainingGoalMap),
    (
        MaybeContainingGoalMap = yes(ContainingGoalMap)
    ;
        MaybeContainingGoalMap = no,
        unexpected($pred, "no map")
    ).

add_out_of_line_code(NewCode, !CI) :-
    Code0 = !.CI ^ code_info_persistent ^ cip_out_of_line_code,
    Code = Code0 ++ NewCode,
    !CI ^ code_info_persistent ^ cip_out_of_line_code := Code.

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

should_trace_code_gen(CI, ShouldDebug) :-
    get_pred_id(CI, PredId),
    pred_id_to_int(PredId, PredIdInt),
    get_module_info(CI, ModuleInfo),
    module_info_get_globals(ModuleInfo, Globals),
    globals.lookup_int_option(Globals, debug_code_gen_pred_id, DebugPredIdInt),
    ( if PredIdInt = DebugPredIdInt then
        get_progress_stream(CI, ProgressStream),
        ShouldDebug = yes(ProgressStream)
    else
        ShouldDebug = no
    ).

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

    % Submodule for managing stack slots.

:- interface.

    % Return the lval of the stack slot in which the given variable is stored.
    % Aborts if the variable does not have a stack slot an assigned to it.
    %
:- pred get_variable_slot(code_info::in, prog_var::in, lval::out) is det.

    % Returns the total stackslot count, but not including space for
    % succip, and without padding for alignment. This total can change in the
    % future if this call is followed by further allocations of temp slots.
    %
:- pred get_total_stackslot_count(code_info::in, int::out) is det.

    % If necessary, round up a det stack frame allocation so that the stack
    % pointer remains on an even word boundary.
    %
:- func round_det_stack_frame_size(code_info, int) = int.

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

:- implementation.

get_variable_slot(CI, Var, Slot) :-
    get_stack_slots(CI, StackSlots),
    ( if map.search(StackSlots, Var, SlotLocn) then
        Slot = stack_slot_to_lval(SlotLocn)
    else
        get_var_table(CI, VarTable),
        lookup_var_entry(VarTable, Var, Entry),
        Name = var_entry_name(Var, Entry),
        term.var_to_int(Var, VarNum),
        string.format("variable `%s' (%d) not found",
            [s(Name), i(VarNum)], Msg),
        unexpected($pred, Msg)
    ).

get_total_stackslot_count(CI, NumSlots) :-
    get_var_slot_count(CI, SlotsForVars),
    get_max_temp_slot_count(CI, SlotsForTemps),
    NumSlots = SlotsForVars + SlotsForTemps.

round_det_stack_frame_size(CI, NumSlots) = NumSlotsRoundup :-
    ( if
        odd(NumSlots),
        get_exprn_opts(CI, ExprnOpts),
        get_det_stack_float_width(ExprnOpts) = double_width
    then
        NumSlotsRoundup = NumSlots + 1
    else
        NumSlotsRoundup = NumSlots
    ).

%---------------------------------------------------------------------------%
:- end_module ll_backend.code_info.
%---------------------------------------------------------------------------%
