%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1993-2012 The University of Melbourne.
% Copyright (C) 2013-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: llds.m.
% Main authors: conway, fjh.
%
% LLDS - The Low-Level Data Structure.
%
% This module defines the LLDS data structure itself.
%
%-----------------------------------------------------------------------------%

:- module ll_backend.llds.
:- interface.

:- import_module backend_libs.
:- import_module backend_libs.builtin_ops.
:- import_module backend_libs.rtti.
:- import_module hlds.
:- import_module hlds.code_model.
:- import_module hlds.hlds_data.
:- import_module hlds.hlds_llds.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module libs.
:- import_module libs.trace_params.
:- import_module ll_backend.layout.
:- import_module mdbcomp.
:- import_module mdbcomp.goal_path.
:- import_module mdbcomp.prim_data.
:- import_module mdbcomp.program_representation.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_data_foreign.
:- import_module parse_tree.prog_data_pragma.
:- import_module parse_tree.prog_foreign.
:- import_module parse_tree.prog_type.

:- import_module assoc_list.
:- import_module bool.
:- import_module cord.
:- import_module counter.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module set.
:- import_module term.

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

    % The type `c_file' is the actual LLDS.

:- type c_file
    --->    c_file(
                cfile_modulename            :: module_name,
                cfile_foreign_decl_codes    :: list(foreign_decl_code),
                cfile_foreign_body_codes    :: list(foreign_body_code),
                cfile_foreign_export_defns  :: list(foreign_export_defn),
                cfile_vars                  :: list(tabling_info_struct),
                cfile_scalar_common_data    :: list(scalar_common_data_array),
                cfile_vector_common_data    :: list(vector_common_data_array),
                cfile_rtti_data             :: list(rtti_data),
                cfile_ptis                  :: list(rval),
                cfile_hlds_var_nums         :: list(int),
                cfile_short_locns           :: list(int),
                cfile_long_locns            :: list(int),
                cfile_user_event_var_nums   :: list(maybe(int)),
                cfile_user_events           :: list(user_event_data),
                cfile_no_var_label_layouts  :: list(label_layout_no_vars),
                cfile_svar_label_layouts    :: list(label_layout_short_vars),
                cfile_lvar_label_layouts    :: list(label_layout_long_vars),
                cfile_i_label_to_layout_map :: map(label, layout_slot_name),
                cfile_p_label_to_layout_map :: map(label, data_id),
                cfile_call_sites            :: list(call_site_static_data),
                cfile_coverage_points       :: list(coverage_point_info),
                cfile_proc_statics          :: list(proc_layout_proc_static),
                cfile_proc_head_var_nums    :: list(int),
                cfile_proc_var_names        :: list(int),
                cfile_proc_body_bytecodes   :: list(int),
                cfile_ts_string_table       :: list(string),
                cfile_table_io_entries      :: list(table_io_entry_data),
                cfile_table_io_entry_map    :: map(pred_proc_id,
                                                layout_slot_name),
                cfile_proc_event_layouts    :: list(layout_slot_name),
                cfile_exec_traces           :: list(proc_layout_exec_trace),
                cfile_proc_layouts          :: list(proc_layout_data),
                cfile_module_layout_data    :: list(module_layout_data),
                cfile_closure_layout_data   :: list(closure_proc_id_data),
                cfile_alloc_sites           :: list(alloc_site_info),
                cfile_alloc_site_map        :: map(alloc_site_id,
                                                layout_slot_name),
                cfile_code                  :: list(comp_gen_c_module),
                cfile_user_init_c_names     :: list(string),
                cfile_user_final_c_names    :: list(string),
                cfile_complexity            :: list(complexity_proc_info)
            ).

    % Global variables generated by the compiler.
:- type tabling_info_struct
    --->    tabling_info_struct(
                % The id of the procedure whose table this structure is.
                tis_proc_label              :: proc_label,
                tis_eval_method             :: tabled_eval_method,

                tis_num_inputs              :: int,
                tis_num_outputs             :: int,
                tis_input_steps             :: list(table_step_desc),
                tis_maybe_output_steps      :: maybe(list(table_step_desc)),

                % Pseudo-typeinfos for headvars.
                tis_ptis                    :: rval,
                % Where to fill the ptis in from.
                tis_type_params             :: rval,

                tis_size_limit              :: maybe(int),
                tis_stats                   :: table_attr_statistics
            ).

:- type typed_rval
    --->    typed_rval(rval, llds_type).

:- func typed_rvals_project_rvals(list(typed_rval)) = list(rval).
:- func typed_rvals_project_types(list(typed_rval)) = list(llds_type).

:- pred build_typed_rvals(list(rval)::in, list(llds_type)::in,
    list(typed_rval)::out) is det.

:- type common_cell_type
    --->    plain_type(list(llds_type))
            % The type is a structure with one field for each one
            % of the cell's arguments.
    ;       grouped_args_type(assoc_list(llds_type, int)).
            % The type is a structure with one field for each group
            % of the cell's arguments, with each group containing
            % at least two elements of the same llds_type.

:- type common_cell_value
    --->    plain_value(list(typed_rval))
    ;       grouped_args_value(list(common_cell_arg_group)).

:- type common_cell_arg_group
    --->    common_cell_grouped_args(
                % The shared type of the fields in the group.
                llds_type,

                % The number of fields in the group. This will contain
                % the length of the list in the third argument, but computed
                % only once. It ought to be more than one; if a field cannot be
                % grouped with neighbouring values of the same type, it should
                % be stored as an ungrouped arg.
                int,

                % The field values themselves.
                list(rval)
            )
    ;       common_cell_ungrouped_arg(
                llds_type,      % The type of the field.
                rval            % The field value.
            ).

:- type type_num
    --->    type_num(int).

:- type scalar_common_data_array
    --->    scalar_common_data_array(
                % The type of the elements of the array.
                scda_rval_types :: common_cell_type,

                % The type number.
                scda_type_num   :: type_num,

                % The array elements, starting at offset 0.
                scda_values     :: list(common_cell_value)
            ).

:- type vector_common_data_array
    --->    vector_common_data_array(
                % The type of the elements of the array.
                vcda_rval_types :: common_cell_type,
                % The type number.
                vcda_type_num   :: type_num,

                % The number of this vector, among all the vector cells
                % with this type for the elements.
                vcda_vector_num :: int,

                % The array elements, starting at offset 0.
                vcda_values     :: list(common_cell_value)
            ).

    % This maps the integer that identifies a constant structure
    % (the key of a const_struct in const_struct_db ^ csdb_structs)
    % to the rval representing the const_struct.
    %
:- type const_struct_map == map(int, typed_rval).

:- type comp_gen_c_module
    --->    comp_gen_c_module(
                % The name of this C module.
                cgcm_name               :: string,

                % The code.
                cgcm_procs              :: list(c_procedure)
            ).

:- type c_procedure
    --->    c_procedure(
                % The predicate or function's user-facing identity.
                cproc_p_or_f            :: pred_or_func,
                cproc_name              :: string,
                cproc_user_arity        :: user_arity,

                % The pred_proc_id of this code.
                cproc_id                :: pred_proc_id,

                % Proc_label of this procedure.
                cproc_proc_label        :: proc_label,

                % The code model of the procedure.
                cproc_code_model        :: code_model,

                cproc_eff_trace_level   :: eff_trace_level,

                % The code for this procedure.
                cproc_code              :: list(instruction),

                % Source for new label numbers.
                cproc_label_nums        :: counter,

                % The compiler is allowed to perform optimizations on this
                % c_procedure that could alter RTTI information (e.g. the set
                % of variables live at a label) only if this field is set
                % to `may_alter_rtti'.
                cproc_may_alter_rtti    :: may_alter_rtti,

                cproc_c_global_vars     :: set(string)
            ).

:- type may_alter_rtti
    --->    may_alter_rtti
    ;       must_not_alter_rtti.

:- type llds_proc_id    ==  int.

    % We build up instructions as trees and then flatten the tree to a list.
    %
:- type llds_code == cord(instruction).

:- type instruction
    --->    llds_instr(
                llds_inst       :: instr,
                llds_comment    :: string
            ).

:- type maybe_auto_comments
    --->    no_auto_comments
    ;       auto_comments.

:- type nondet_tail_call
    --->    no_tail_call
            % At the point of the call, the procedure has more alternatives.
            %
            % Under these conditions, the call cannot be transformed
            % into a tail call.

    ;       checked_tail_call
            % At the point of the call, the procedure has no more alternatives,
            % and curfr and maxfr are not guaranteed to be identical.
            %
            % Under these conditions, the call can be transformed into a tail
            % call whenever its return address leads to the procedure epilogue
            % AND curfr and maxfr are found to be identical at runtime.

    ;       unchecked_tail_call.
            % At the point of the call the procedure has no more alternatives,
            % and curfr and maxfr are guaranteed to be identical.
            %
            % Under these conditions, the call can be transformed into a tail
            % call whenever its return address leads to the procedure epilogue.

:- type allow_lco
    --->    do_not_allow_lco
    ;       allow_lco.

:- type call_model
    --->    call_model_det(allow_lco)
    ;       call_model_semidet(allow_lco)
    ;       call_model_nondet(nondet_tail_call).

    % The type defines the various LLDS virtual machine instructions.
    % Each instruction gets compiled to a simple piece of C code
    % or a macro invocation.
    %
:- type instr
    --->    comment(string)
            % Insert a comment into the output code.

    ;       livevals(set(lval))
            % A list of which registers and stack locations are currently live.

    ;       block(int, int, list(instruction))
            % block(NumIntTemps, NumFloatTemps, Instrs):
            % A list of instructions that make use of
            % some local temporary variables.

    ;       assign(lval, rval)
            % assign(Lval, Rval):
            % Assign Rval to the location specified by Lval.

    ;       keep_assign(lval, rval)
            % As assign, but this operation cannot be optimized away or
            % the target replaced by a temp register, even if the target
            % *appears* to be unused by the following code.

    ;       llcall(code_addr, code_addr, list(liveinfo), term.context,
                maybe(forward_goal_path), call_model)
            % llcall(Target, Continuation, _, _, _) is the same as
            % succip = Continuation; goto(Target).
            % The third argument is the live value info for the values live
            % on return. The fourth argument gives the context of the call.
            % The fifth gives the goal id of the call in the body of the
            % procedure; it is meaningful only if execution tracing is enabled.
            % The last gives the code model of the called procedure, and says
            % whether tail recursion elimination may be applied to the call.
            % For model_non calls, this depends on whether there are any other
            % stack frames on top of the stack frame of this procedure on the
            % nondet stack. For model_det and model_semi calls, this depends on
            % whether there is some other code, executing in parallel with this
            % context, that uses the current stack frame.
            %
            % The ll prefix on call is to avoid the use of the call keyword
            % and to distinguish this function symbol from a similar one
            % in the MLDS.

    ;       mkframe(nondet_frame_info, maybe(code_addr))
            % mkframe(NondetFrameInfo, MaybeAddr) creates a nondet stack frame.
            % NondetFrameInfo says whether the frame is an ordinary frame,
            % containing the variables of a model_non procedure, or a temp
            % frame used only for its redoip/redofr slots. If the former,
            % it also gives the details of the size of the variable parts
            % of the frame (temp frames have no variable sized parts).
            % If MaybeAddr = yes(CodeAddr), then CodeAddr is the code address
            % to branch to when trying to generate the next solution from this
            % choice point. If MaybeAddr = no, then the field of the choice
            % point that contains this information is not filled in by mkframe;
            % it is up to the code following to do so.

    ;       label(label)
            % Defines a label that can be used as the target of calls, gotos,
            % etc. If the comment associated with the instruction ends with
            % "nofulljump", then gotos ending at this label will not be
            % replaced by the code starting at this label.

    ;       goto(code_addr)
            % Branch to the specified address. Note that jumps to do_fail,
            % do_redo, etc can get optimized into invocations of the macros
            % fail(), redo(), etc.

    ;       computed_goto(rval, maybe(int), list(maybe(label)))
            % Evaluate the rval, which should be an integer, and jump to the
            % (rval+1)th label in the list. e.g. computed_goto(2, [A, B, C, D])
            % will branch to label C. A label that isn't there implicitly means
            % "not reached".
            %
            % The code that constructs this instructions should set the
            % second argument to contain
            %
            % - either yes(MaxIndex), if the maximum possible value of
            %   the rval argument is known at compile time (and is MaxIndex),
            % - or no, if this maximum value is not known at compile time.
            %
            % The info in this field is used to check the validity of an
            % optimization performed on computed_gotos by peeohole.m.

    ;       arbitrary_c_code(proc_affects_liveness, c_code_live_lvals, string)
            % Do whatever is specified by the string, which can be any piece
            % of C code that does not have any non-local flow of control.

    ;       if_val(rval, code_addr)
            % If rval is true, then goto code_addr.

    ;       save_maxfr(lval)
            % Save the current value of maxfr to the given lval. In most
            % grades, this does a straightforward copy, but in grades in which
            % stacks can be reallocated, it saves the offset of maxfr from the
            % start of the nondet stack.

    ;       restore_maxfr(lval)
            % Restore maxfr from the saved copy in the given lval. Assumes the
            % lval was saved with save_maxfr.

    ;       incr_hp(lval, maybe(ptag), maybe(int), rval, maybe(alloc_site_id),
                may_use_atomic_alloc, maybe(rval), llds_reuse)
            % incr_hp(Target, MaybeTag, MaybeOffset, SizeRval, MaybeAllocId,
            %   MayUseAtomicAlloc, MaybeRegionId, MaybeReuse)
            %
            % Get a memory block of a size given by SizeRval and put its
            % address in Target, possibly after incrementing it by Offset words
            % (if MaybeOffset = yes(Offset)) and/or after tagging it with Tag
            % (if MaybeTag = yes(Tag)).
            % If MaybeAllocId = yes(AllocId) then AllocId identifies the
            % allocation site, for use in memory profiling.
            % MayUseAtomicAlloc says whether we can use the atomic variants
            % of the Boehm gc allocator calls. If MaybeRegionId =
            % yes(RegionId), then the block should be allocated in the region
            % identified by RegionId (i.e. in the region whose header RegionId
            % points to). If MaybeReuse = llds_reuse(ReuseRval,
            % MaybeFlagLval), then we should try to reuse the cell ReuseRval
            % for the block. If MaybeFlagLval = yes(FlagLval) then FlagLval
            % needs to be set to true or false indicate whether reuse was
            % really possible.

    ;       mark_hp(lval)
            % Tell the heap sub-system to store a marker (for later use in
            % restore_hp/1 instructions) in the specified lval

    ;       restore_hp(rval)
            % The rval must be a marker as returned by mark_hp/1. The effect
            % is to deallocate all the memory which was allocated since that
            % call to mark_hp.

    ;       free_heap(rval)
            % Notify the garbage collector that the heap space associated with
            % the top-level cell of the rval is no longer needed. `free' is
            % useless but harmless without conservative garbage collection.

    ;       push_region_frame(region_stack_id, embedded_stack_frame_id)
            % push_region_frame(RegionStackId, EmbeddedStackId)
            %
            % Set the stack pointer of the region stack identified by
            % RegionStackId to point to the group of stack slots identified
            % by EmbeddedStackId (which specifies the new top embedded
            % stack frame on that stack) *after* saving the old value of the
            % stack pointer in the fixed slot reserved for this purpose in
            % the new frame.
            %
            % The instruction will also fill in whatever other fixed slots
            % of the new stack frame may be filled in at this time.

    ;       region_fill_frame(region_fill_frame_op, embedded_stack_frame_id,
                rval, lval, lval)
            % region_fill_frame(FillOp, EmbeddedStackId,
            %   RegionId, NumLval, AddrLval)
            %
            % EmbeddedStackId should match the parameter of the
            % push_region_frame instruction that created the embedded stack
            % frame to which this instruction refers. RegionId should
            % identify a region (i.e. it should point to the region header).
            %
            % If the condition appropriate to FillOp is true, then this
            % instruction will
            %
            % (a) increment NumLval by one, and
            % (b) store the aspects of the region relevant to FillOp
            %     in one or more consecutive memory locations starting at
            %     AddrRval,  after which it will increment AddrRval
            %     by the number of words this uses.
            %
            % If the condition is false, the instruction will do nothing.
            %
            % The size of the frame must be big enough that the sequence of
            % region_fill_frame operations executed on it don't overflow.
            %
            % At the end of the sequence, NumLval will be stored back into
            % a fixed slot in the embedded frame using a region_set_fixed_slot
            % instruction.

    ;       region_set_fixed_slot(region_set_fixed_op, embedded_stack_frame_id,
                rval)
            % region_set_fixed_op(SetOp, EmbeddedStackId, Value)
            %
            % Given an embedded stack frame identified by EmbeddedStackId,
            % set the fixed field of this frame identified by SetOp to Value.

    ;       use_and_maybe_pop_region_frame(region_use_frame_op,
                embedded_stack_frame_id)
            % use_and_maybe_pop_region_frame(UseOp, EmbeddedStackId)
            %
            % For some values of UseOp, this instruction uses the contents of
            % the frame identified by EmbeddedStackId (including values saved
            % by region_set_fixed_op instructions) to operate on the values
            % recorded in the frame by region_fill_frame instructions.
            %
            % For some other values of UseOp, this instruction logically pops
            % the embedded stack off its stack. (The Mercury stacks are
            % untouched.)
            %
            % For yet other values of UseOp, it does both.

    ;       store_ticket(lval)
            % Allocate a new "ticket" and store it in the lval.
            %
            % Operational semantics:
            %   MR_ticket_counter = ++MR_ticket_high_water;
            %   lval = MR_trail_ptr;

    ;       reset_ticket(rval, reset_trail_reason)
            % The rval must specify a ticket allocated with `store_ticket'
            % and not yet invalidated, pruned or deallocated.
            %
            % If reset_trail_reason is `undo', `exception', or `retry',
            % restore any mutable global state to the state it was in when
            % the ticket was obtained with store_ticket(); invalidates any
            % tickets allocated after this one. If reset_trail_reason is
            % `commit' or `solve', leave the state unchanged, just check that
            % it is safe to commit to this solution (i.e. that there are no
            % outstanding delayed goals -- this is the "floundering" check).
            % Note that we do not discard trail entries after commits,
            % because that would in general be unsafe.
            %
            % Any invalidated ticket which has not yet been backtracked over
            % should be pruned with `prune_ticket' or `prune_tickets_to'.
            % Any invalidated ticket which has been backtracked over is
            % useless and should be deallocated with `discard_ticket'.
            %
            % Operational semantics:
            %   MR_untrail_to(rval, reset_trail_reason);

    ;       prune_ticket
            % Invalidates the most-recently allocated ticket.
            %
            % Operational semantics:
            %   --MR_ticket_counter;

    ;       discard_ticket
            % Deallocates the most-recently allocated ticket.
            %
            % Operational semantics:
            %   MR_ticket_high_water = --MR_ticket_counter;

    ;       mark_ticket_stack(lval)
            % Tell the trail sub-system to store a ticket counter
            % (for later use in prune_tickets_to)
            % in the specified lval.
            %
            % Operational semantics:
            %   lval = MR_ticket_counter;

    ;       prune_tickets_to(rval)
            % The rval must be a ticket counter obtained via
            % `mark_ticket_stack' and not yet invalidated. Prunes any trail
            % tickets allocated after the corresponding call to
            % mark_ticket_stack. Invalidates any later ticket counters.
            %
            % Operational semantics:
            %   MR_ticket_counter = rval;

%   ;       discard_tickets_to(rval)
            % This is only used in the hand-written code in exception.m.
            %
            % The rval must be a ticket counter obtained via
            % `mark_ticket_stack' and not yet invalidated. Deallocates any
            % trail tickets allocated after the corresponding call to
            % mark_ticket_stack. Invalidates any later ticket counters.
            %
            % Operational semantics:
            %   MR_ticket_counter = rval;
            %   MR_ticket_high_water = MR_ticket_counter;

    ;       incr_sp(int, string, stack_incr_kind)
            % Increment the det stack pointer. The string is the name of the
            % procedure, for use in collecting statistics about stack frame
            % sizes.

    ;       decr_sp(int)
            % Decrement the det stack pointer.

    ;       decr_sp_and_return(int)
            % Pick up the return address from its slot in the stack frame,
            % decrement the det stack pointer, and jump to the return address.

    ;       foreign_proc_code(
                fproc_decls             :: list(foreign_proc_decl),
                fproc_components        :: list(foreign_proc_component),
                fproc_may_call_merc     :: proc_may_call_mercury,
                fproc_fix_nolayout      :: maybe(label),
                fproc_fix_layout        :: maybe(label),
                fproc_fix_onlylayout    :: maybe(label),
                fproc_nofix             :: maybe(label),
                fproc_hash_def_label    :: maybe(label),
                fproc_stack_slot_ref    :: maybe_refers_to_llds_stack,
                fproc_maybe_dupl        :: proc_may_duplicate
            )
            % foreign_proc_code(Decls, Components. MayCallMercury,
            %   FixNoLayout, FixLayout, FixOnlyLayout, NoFix, HashDef,
            %   StackSlotRef, MayBeDupl)
            %
            % Decls says what local variable declarations are required for
            % Components, which in turn can specify how the inputs should be
            % placed in their variables, how the outputs should be picked up
            % from their variables, and C code both from the source program
            % and generated by the compiler. These components can be sequenced
            % in various ways. This flexibility is needed for nondet
            % foreign_procs, which need different copies of several components
            % for different paths through the code.
            %
            % MayCallMercury says whether the user C code components
            % may call Mercury; certain optimizations can be performed
            % across foreign_proc_code instructions that cannot call Mercury.
            %
            % Some components in some foreign_proc_code instructions refer to
            % a Mercury label. If they do, we must prevent the label
            % from being optimized away. To make it known to labelopt,
            % we mention it in the FixNoLayout, FixLayout or FixOnlyLayout
            % fields.
            %
            % FixNoLayout may give the name of a label whose name is fixed
            % because it embedded in raw C code, and which does not have
            % a layout structure. FixLayout and FixOnlyLayout may give
            % the names of labels whose names are fixed because they *do*
            % have an associated label layout structure. The label in FixLayout
            % may appear in C code; the label in FixOnlyLayout argument may not
            % (such a label may therefore may be deleted from the LLDS code
            % if it is not referred to from anywhere else). The NoFix field
            % may give the name of a label that can be changed (because it is
            % not mentioned in C code and has no associated layout structure,
            % being mentioned only in foreign_proc_fail_to components).
            %
            % If HashDef is yes, then when the code generator generates C code
            % for this foreign_proc, it should surround it with code that
            % #defines the symbol MR_HASH_DEF_LABEL_LAYOUT to the address of
            % the label layout structure of the given label. We use this
            % mechanism to pass such addresses to the debugger because when
            % the LLDS code is generated, we do not yet know what the address
            % will be (even in C source form), since the slots in the arrays
            % of label layout structures have not yet been allocated.
            %
            % If we are generating code for a target language other than C,
            % HashDef will always be no.
            %
            % StackSlotRef says whether the contents of the foreign_proc
            % C code can refer to stack slots. User-written shouldn't refer
            % to stack slots, the question is whether any compiler-generated
            % C code does.
            %
            % MayBeDupl says whether this instruction may be duplicated
            % by jump optimization.

    ;       init_sync_term(lval, int, int)
            % Initialize a synchronization term, which is a continuous number
            % of slots on the detstack.  The first argument contains the base
            % address of the synchronization term.  The second argument
            % indicates how many branches we expect to join at the end of the
            % parallel conjunction.  The third argument is an index into the
            % threadscope string table.  The string that it refers to
            % identifies this parallel conjunction within the source code.
            % (See the documentation in par_conj_gen.m and
            % runtime/mercury_context.{c,h} for further information about
            % synchronisation terms.)

    ;       fork_new_child(lval, label)
            % Create a new spark. fork(SyncTerm, Child) creates spark, to begin
            % execution at Child, where SyncTerm contains the base address of
            % the synchronisation term. Control continues at the next
            % instruction.

    ;       join_and_continue(lval, label)
            % Signal that this thread of execution has finished in the current
            % parallel conjunct. For details of how we at the end of a parallel
            % conjunct see runtime/mercury_context.{c,h}.
            % The synchronisation term is specified by the given lval.
            % The label gives the address of the code following the parallel
            % conjunction.

    ;       lc_create_loop_control(int, lval)
            % Create a loop control structure with the given number of slots,
            % and put its address in the given lval.

    ;       lc_wait_free_slot(rval, lval, label)
            % Given an rval that holds the address of a loop control structure,
            % return the index of a free slot in that structure, waiting for
            % one to become free if necessary. The label acts as a resumption
            % point if the context suspends. It will be both defined and used
            % in the C code generated for this instruction, and should not
            % be referred to from anywhere else.

    ;       lc_spawn_off(rval, rval, label)
            % Spawn off an independent computation whose execution starts
            % at the given label and which will terminate by executing
            % a join_and_terminate_lc instruction. The first rval should
            % hold the address of the loop control structure that controls
            % the spawned-off computation, and the second should hold
            % the index of the slot occupied by that computation.
            %
            % After spawning off that computation, the original thread
            % will just fall through.

    ;       lc_join_and_terminate(rval, rval).
            % Terminate the current context, which was spawned off by a
            % spawn_off instruction. The first rval gives the address of
            % the loop control structure, and the second is the index of
            % this goal's slot in it. These two rvals should be exactly
            % the same as the two rval arguments in the original lc_spawn_off
            % instruction.

:- inst instr_llcall for instr/0
    --->    llcall(ground, ground, ground, ground, ground, ground).
:- inst instr_goto for instr/0
    --->    goto(ground).
:- inst instr_if_val for instr/0
    --->    if_val(ground, ground).
:- inst instr_foreign_proc_code for instr/0
    --->    foreign_proc_code(ground, ground, ground, ground, ground,
                ground, ground, ground, ground, ground).

:- type alloc_site_id
    --->    alloc_site_id(alloc_site_info).

:- type stack_incr_kind
    --->    stack_incr_leaf         % The incr_sp creates the stack frame
                                    % of a leaf procedure.
    ;       stack_incr_nonleaf.     % The incr_sp creates the stack frame
                                    % of a nonleaf procedure.

:- type nondet_frame_info
    --->    temp_frame(
                temp_frame_type
            )
    ;       ordinary_frame(
                string,                 % Name of the predicate.
                int                     % Number of framevar slots.
            ).

:- type c_code_live_lvals
    --->    no_live_lvals_info  % There is no information available about
                                % the live lvals used in the c_code.

    ;       live_lvals_info(
                set(lval)       % The set of lvals defined before the c_code
                                % that are live inside the c_code.
            ).

    % Temporary frames on the nondet stack exist only to provide a failure
    % environment, i.e. a place to store a redoip and a redofr. Accurate
    % garbage collection and execution tracing need to know how to
    % interpret the layout information associated with the label whose
    % address is in the redoip slot. If the label is in a procedure that
    % stores its variables on the nondet stack, the redofr slot will give
    % the address of the relevant stack frame. If the label is in a
    % procedure that stores its variables on the det stack, the temporary
    % frame will contain an extra slot containing the address of the
    % relevant frame on the det stack.
    %
:- type temp_frame_type
    --->    det_stack_proc
    ;       nondet_stack_proc.

:- type llds_reuse
    --->    no_llds_reuse
    ;       llds_reuse(
                rval,           % The cell to reuse.
                maybe(lval)     % An optional lval to set to indicate
                                % whether cell reuse was actually possible.
            ).

    % A foreign_proc_decl holds the information needed for the declaration
    % of a local variable in a block of C code emitted for a foreign_proc_code
    % instruction.
    %
:- type foreign_proc_decl
    --->    foreign_proc_arg_decl(
                % This local variable corresponds to a procedure arg.
                mer_type,   % The Mercury type of the argument.
                string,     % The string which is used to describe the type
                            % in the C code.
                string      % The name of the local variable that will hold
                            % the value of that argument inside the C block.
            ).

    % A foreign_proc_component holds one component of a foreign_proc_code
    % instruction.
    %
:- type foreign_proc_component
    --->    foreign_proc_inputs(list(foreign_proc_input))
    ;       foreign_proc_outputs(list(foreign_proc_output))
    ;       foreign_proc_user_code(maybe(prog_context), proc_affects_liveness,
                string)
    ;       foreign_proc_raw_code(can_branch_away, proc_affects_liveness,
                c_code_live_lvals, string)
    ;       foreign_proc_fail_to(label)
    ;       foreign_proc_alloc_id(alloc_site_id)
    ;       foreign_proc_noop.

:- type can_branch_away
    --->    can_branch_away
    ;       cannot_branch_away.

    % A foreign_proc_input represents the code that initializes one
    % of the input variables for a foreign_proc_code instruction.
    %
:- type foreign_proc_input
    --->    foreign_proc_input(
                % The name of the foreign language variable.
                in_foreign_lang_var_name    :: string,

                % The type of the Mercury variable being passed.
                in_var_type                 :: mer_type,

                % Whether in_var_type is a dummy type.
                in_var_type_is_dummy        :: is_dummy_type,

                % The type of the argument in original foreign_proc procedure.
                % If the foreign_proc was inlined in some other procedure,
                % then the in_var_type can be an instance of in_original_type;
                % otherwise, the two should be the same.
                in_original_type            :: mer_type,

                % The value being passed.
                in_arg_value                :: rval,

                % If in_original_type is a foreign type, info about
                % that foreign type.
                in_maybe_foreign_type       :: maybe(foreign_proc_type),

                in_box_policy               :: box_policy
            ).

    % A foreign_proc_output represents the code that stores one of
    % of the outputs for a foreign_proc_code instruction.
    %
:- type foreign_proc_output
    --->    foreign_proc_output(
                % The place where the foreign_proc should put this output.
                out_arg_dest                :: lval,

                % The type of the Mercury variable being passed.
                out_var_type                :: mer_type,

                % Whether out_var_type is a dummy type.
                out_var_type_is_dummy       :: is_dummy_type,

                % The type of the argument in original foreign_proc procedure;
                % see in_original_type above.
                out_original_type           :: mer_type,

                % The name of the foreign language variable.
                out_var_name                :: string,

                % If in_original_type is a foreign type, info about
                % that foreign type.
                out_maybe_foreign_type      :: maybe(foreign_proc_type),

                out_box_policy              :: box_policy
            ).

:- type foreign_proc_type
    --->    foreign_proc_type(
                string,         % The C type name.
                foreign_type_assertions
                                % The assertions on the foreign_type
                                % declarations that the C type name came from.
            ).

:- type add_trail_ops
    --->    add_trail_ops
    ;       do_not_add_trail_ops.

:- type add_region_ops
    --->    add_region_ops
    ;       do_not_add_region_ops.

    % See runtime/mercury_trail.h.
:- type reset_trail_reason
    --->    reset_reason_undo
    ;       reset_reason_commit
    ;       reset_reason_solve
    ;       reset_reason_exception
    ;       reset_reason_retry
    ;       reset_reason_gc.

    % See runtime/mercury_region.h
    % XXX The documentation is not there yet, but should be there soon.
:- type region_stack_id
    --->    region_stack_ite
    ;       region_stack_disj
    ;       region_stack_commit.

:- type region_fill_frame_op
    --->    region_fill_ite_protect
    ;       region_fill_ite_snapshot(removed_at_start_of_else)
    ;       region_fill_semi_disj_protect
    ;       region_fill_disj_snapshot
    ;       region_fill_commit.

:- type removed_at_start_of_else
    --->    removed_at_start_of_else
    ;       not_removed_at_start_of_else.

:- type region_set_fixed_op
    --->    region_set_ite_num_protects
    ;       region_set_ite_num_snapshots
    ;       region_set_disj_num_protects
    ;       region_set_disj_num_snapshots
    ;       region_set_commit_num_entries.

:- type region_use_frame_op
    --->    region_ite_then(region_ite_kind)    % uses; pop only if semi
    ;       region_ite_else(region_ite_kind)    % uses and pops
    ;       region_ite_nondet_cond_fail         % pops
    ;       region_disj_later                   % uses
    ;       region_disj_last                    % uses and pops
    ;       region_disj_nonlast_semi_commit     % uses and pops
    ;       region_commit_success               % uses and pops
    ;       region_commit_failure.              % only pops

:- type region_ite_kind
    --->    region_ite_semidet_cond
    ;       region_ite_nondet_cond.

:- type embedded_stack_frame_id
    --->    embedded_stack_frame_id(
                % The embedded stack frame consists of the lvals
                %
                %   stack_slot_num_to_lval(StackId, FirstSlot)
                % to
                %   stack_slot_num_to_lval(StackId, LastSlot)
                %
                % with FirstSlot < LastSlot.

                main_stack,                     % StackId
                int,                            % FirstSlot
                int                             % LastSlot
            ).

    % first_nonfixed_embedded_slot_addr(EmbeddedStackId, FixedSize):
    %
    % Return the address of the lowest-address non-fixed slot in the given
    % embedded stack frame.
    %
:- func first_nonfixed_embedded_slot_addr(embedded_stack_frame_id, int) = rval.

    % Each call instruction has a list of liveinfo, which stores information
    % about which variables are live after the call (that is, on return).
    % The information is intended for use by the native garbage collector.
    %
:- type liveinfo
    --->    live_lvalue(
                % What location does this lifeinfo structure refer to?
                layout_locn,

                % What is the type of this live value?
                live_value_type,

                % For each tvar that is a parameter of the type of this value,
                % give the set of locations where the type_info variable
                % describing the actual type bound to the type parameter
                % may be found.
                %
                % We record all the locations of the typeinfo, in case
                % different paths of arriving a this program point leave
                % the typeinfo in different sets of locations. However,
                % there must be at least type_info location that is valid
                % along all paths leading to this point.
                map(tvar, set(layout_locn))
            ).

    % For an explanation of this type, see the comment on
    % stack_layout.represent_locn.
    %
:- type layout_locn
    --->    locn_direct(lval)
    ;       locn_indirect(lval, int).

    % live_value_type describes the different sorts of data that
    % can be considered live.
    %
:- type live_value_type
    --->    live_value_succip              % A stored succip.
    ;       live_value_curfr               % A stored curfr.
    ;       live_value_maxfr               % A stored maxfr.
    ;       live_value_redoip              % A stored redoip.
    ;       live_value_redofr              % A stored redofr.
    ;       live_value_hp                  % A stored heap pointer.
    ;       live_value_trail_ptr           % A stored trail pointer.
    ;       live_value_ticket              % A stored ticket.
    ;       live_value_region_ite
    ;       live_value_region_disj
    ;       live_value_region_commit

    ;       live_value_var(prog_var, string, mer_type, llds_inst)
            % A variable (the var number and name are for execution tracing;
            % we have to store the name here because when we want to use
            % the live_value_type, we won't have access to the varset).

    ;       live_value_unwanted.
            % Something we don't need, or at least don't need
            % information about.

    % For recording information about the inst of a variable for use
    % by the garbage collector or the debugger, we don't need to know
    % what functors its parts are bound to, or which parts of it are
    % unique; we just need to know which parts of it are bound.
    % If we used the HLDS type inst to represent the instantiatedness
    % in the LLDS, we would find that insts that the LLDS wants to treat
    % as the same would compare as different. The live_value_types and
    % var_infos containing them would compare as different as well,
    % which can lead to a variable being listed more than once in
    % a label's list of live variables.
    %
    % At the moment, the LLDS only handles ground insts. When this changes,
    % the argument type of partial will have to be changed, and the code
    % that sets this field in live_value_var will have some actual work to do.
    %
:- type llds_inst
    --->    llds_inst_better_be_ground.

:- func stack_slot_to_lval(stack_slot) = lval.
:- func key_stack_slot_to_lval(_, stack_slot) = lval.

:- type lval_or_any_reg
    --->    loa_lval(lval)
    ;       loa_any_reg.

:- func abs_locn_to_lval_or_any_reg(abs_locn) = lval_or_any_reg.

:- func abs_locn_to_lval(abs_locn) = lval.

:- func key_abs_locn_to_lval(_, abs_locn) = lval.

:- type main_stack
    --->    det_stack
    ;       nondet_stack.

    % Return the id of the stack on which procedures with the given code model
    % have their stack frames.
    %
:- func code_model_to_main_stack(code_model) = main_stack.

    % stack_slot_num_to_lval(StackId, N):
    %
    % Return an lval for slot N in a stack frame on StackId.
    %
:- func stack_slot_num_to_lval(main_stack, int) = lval.

    % stack_slot_num_to_lval(StackId, N):
    %
    % Return an rval for the address of slot N in a stack frame on StackId.
    %
:- func stack_slot_num_to_lval_ref(main_stack, int) = rval.

    % An lval represents a data location or register that can be used
    % as the target of an assignment.
    %
:- type lval

    % Virtual machine registers.

    --->    reg(reg_type, int)
            % One of the general-purpose virtual machine registers
            % (either an int or float reg).

    ;       succip
            % Virtual machine register holding the return address for
            % det/semidet code.

    ;       maxfr
            % Virtual machine register holding a pointer to the top of
            % the nondet stack.

    ;       curfr
            % Virtual machine register holding a pointer to the current
            % nondet stack frame.

    ;       hp
            % Virtual machine register holding the heap pointer.

    ;       sp
            % Virtual machine register pointing to the top of det stack.

    ;       parent_sp
            % Virtual machine register pointing to the top of the det stack.
            % This is only set at the beginning of a parallel conjunction (and
            % restored afterwards). Parallel conjuncts which refer to stack
            % slots use this register instead of sp, as they could be running
            % in a different context, where sp would be pointing into a
            % different det stack.

    ;       temp(reg_type, int)
            % A local temporary register. These temporary registers are
            % actually local variables declared in `block' instructions.
            % They may only be used inside blocks. The code generator doesn't
            % generate these; they are introduced by use_local_vars.m.
            % The basic idea is to improve efficiency by using local variables
            % that the C compiler may be able to allocate in a register
            % rather than using stack slots.

    % Values on the stack.

    ;       stackvar(int)
            % A det stack slot. The number is the offset relative to the
            % current value of `sp'. These are used in both det and semidet
            % code. Stackvar slot numbers start at 1.

    ;       parent_stackvar(int)
            % A det stack slot. The number is the offset relative to the
            % value of `parent_sp'. These are used only in the code
            % of parallel conjuncts. Stackvar slot numbers start at 1.

    ;       framevar(int)
            % A nondet stack slot. The reference is relative to the current
            % value of `curfr'. These are used in nondet code. Framevar slot
            % numbers start at 1.

    ;       double_stackvar(double_stack_type, int)
            % Two consecutive stack slots for storing a double-precision float.
            % The number is the offset relative to one of the stack pointers
            % and represents the lower-numbered of two consecutive slots.
            % As our stacks grow upward, they are addressed relative to the
            % stack pointer at the top, so the higher-numbered slot has the
            % lower address, and must be aligned for the target architecture.
            %
            % - stackvar(Slot), stackvar(Slot + 1)
            % - parent_stackvar(Slot), parent_stackvar(Slot + 1)

    ;       succip_slot(rval)
            % The succip slot of the specified nondet stack frame; holds the
            % code address to jump to on successful exit from this nondet
            % procedure.

    ;       succfr_slot(rval)
            % The succfr slot of the specified nondet stack frame; holds the
            % address of caller's nondet stack frame.  On successful exit
            % from this nondet procedure, we will set curfr to this value.

    ;       redoip_slot(rval)
            % The redoip slot of the specified nondet stack frame; holds the
            % code address to jump to on failure.

    ;       redofr_slot(rval)
            % The redofr slot of the specified nondet stack frame; holds the
            % address of the frame that the curfr register should be set to
            % when backtracking through the redoip slot.

    ;       prevfr_slot(rval)
            % The prevfr slot of the specified nondet stack frame; holds the
            % address of the previous frame on the nondet stack.

    % Values on the heap.

    ;       field(maybe(ptag), rval, rval)
            % field(MaybePtag, Address, FieldNum) selects one field of a
            % compound term. Address is a tagged pointer to a cell on the heap;
            % the offset into the cell is FieldNum words. If MaybePtag is yes,
            % its argument gives the value of the primary tag to be subtracted
            % from Address; if it is no, the primary tag bits will have to be
            % masked off. The value of the primary tag should be given
            % if it is known, since this will lead to faster code.

    % Values somewhere in memory.

    ;       mem_ref(rval)
            % A word in the heap, in the det stack or in the nondet stack.
            % The rval should have originally come from a mem_addr rval.

    ;       global_var_ref(c_global_var_ref)
            % A reference to the value of the C global variable with the given
            % name. At least for now, the global variable's type must be
            % MR_Word.

    % Pseudo-values.

    ;       lvar(prog_var).
            % The location of the specified variable. `var' lvals are used
            % during code generation, but should not be present in the LLDS
            % at any stage after code generation.

:- type double_stack_type
    --->    double_stackvar
    ;       double_parent_stackvar.

    % An rval is an expression that represents a value.
    %
:- type rval
    --->    lval(lval)
            % The value of an `lval' rval is just the value stored in
            % the specified lval.

    ;       var(prog_var)
            % The value of a `var' rval is just the value of the specified
            % variable. `var' rvals are used during code generation, but
            % should not be present in the LLDS at any stage after code
            % generation.

    ;       mkword(ptag, rval)
            % Given a pointer and a ptag, mkword returns a tagged pointer.

    ;       mkword_hole(ptag)
            % Make a tagged pointer to an address which is not yet known.

    ;       const(rval_const)

    ;       cast(llds_type, rval)

    ;       unop(unary_op, rval)

    ;       binop(binary_op, rval, rval)

    ;       mem_addr(mem_ref).
            % The address of a word in the heap, the det stack or the nondet
            % stack.

:- type mem_ref
    --->    stackvar_ref(rval)
            % Stack slot number.

    ;       framevar_ref(rval)
            % Stack slot number.

    ;       heap_ref(rval, maybe(ptag), rval).
            % The cell pointer, the ptag to subtract (if unknown, all the
            % primary tag bits must be masked off), and the field number.

:- type c_global_var_ref
    --->    env_var_ref(string).

:- type rval_const
    --->    llconst_true
    ;       llconst_false

    ;       llconst_int(int)
    ;       llconst_uint(uint)

    ;       llconst_int8(int8)
    ;       llconst_uint8(uint8)

    ;       llconst_int16(int16)
    ;       llconst_uint16(uint16)

    ;       llconst_int32(int32)
    ;       llconst_uint32(uint32)

    ;       llconst_int64(int64)
    ;       llconst_uint64(uint64)

    ;       llconst_foreign(string, llds_type)
            % A constant in the target language.
            % It may be a #defined constant in C which is why
            % it is represented as string.

    ;       llconst_float(float)

    ;       llconst_string(string)
            % A string constant that may not contain NULL characters,
            % but for which the compiler will emit a NULL character
            % at the end.
    ;       llconst_multi_string(list(string))
            % A sequence of zero or more of the above kind of string constants,
            % for which the compiler will emit a NULL character after
            % each and every one. There will thus be a NULL character
            % between consecutive strings in the list, as well as at the end.

            % As mentioned in the top-of-module comment in library/string.m,
            % the strings in both kinds of constants may contain code unit
            % sequences that are invalid according to the rules of the encoding
            % (UTF-8 or UTF-16) used by the platform that the compiler is
            % running on. This may happen accidentally, due to a string
            % read in from a file not being vetted, but it can also happen
            % deliberately: the trie method of implementing switches on strings
            % sometimes constructs strings from code unit sequences *even when*
            % it is known that those sequences may contain some but not all
            % of the code units belonging to a single code point.

    ;       llconst_code_addr(code_addr)
    ;       llconst_data_addr(data_id)
    ;       llconst_data_addr_word_offset(data_id, int).
            % The second version of llconst_data_addr specifies that the
            % effective address should be the given number of words *past*
            % the start of the given data_id.

    % A data_id is an lval representing the given variable or array slot.
    % Most references to the data_ref will want to take the address of this
    % lval (by sticking a & in front of it), but in a few situations we need
    % to be able to refer to the lval itself. One of those situations is when
    % we are defining the variable. Another is when the variable is an array,
    % and its name is implicitly taken by the C compiler as the address of the
    % first element.
    %
:- type data_id
    --->    rtti_data_id(rtti_id)
            % The global variable holding the RTTI structure identified
            % by the rtti_id.

    ;       proc_tabling_data_id(proc_label, proc_tabling_struct_id)
            % The tabling structure of the kind identified by the
            % proc_tabling_struct_id for the procedure given by the
            % proc_label.

    ;       scalar_common_data_id(type_num, int)
            % scalar_common_ref(TypeNum, CellNum) is the slot at index CellNum
            % in the array of scalar cells of type TypeNum.

    ;       vector_common_data_id(type_num, int)
            % vector_common_ref(TypeNum, CellNum) is the sequence of slots
            % starting at index CellNum in the array of vector cells of
            % type TypeNum.

    ;       layout_id(layout_name)
            % The global variable holding the layout structure identified
            % by the layout_name.

    ;       layout_slot_id(layout_slot_id_kind, pred_proc_id).
            % The slot reserved for the given pred_proc_id in the array
            % identified by the layout_slot_id_kind.

:- type layout_slot_id_kind
    --->    table_io_entry_id.

    % There are two kinds of labels: entry labels and internal labels.
    % Entry labels are the entry points of procedures; internal labels are not.
    %
    % We have three ways of referring to entry labels. One way is valid from
    % everywhere in the program (external). Another way is valid only from
    % within the C file that defines the procedure (local). The last is valid
    % only from within the BEGIN_MODULE/END_MODULE pair that contains the
    % procedure definition. The more specialized the reference, the faster
    % jumping to the label may be, though the implementations of adjacent
    % entry_label_types may be identical in some grades.
    %
    % It is valid to declare a label using one entry_label_type and to
    % refer to it using a more specialized entry_label_type.

:- type entry_label_type
    --->    entry_label_c_local
            % proc entry; internal to a C module
    ;       entry_label_local
            % proc entry; internal to a Mercury module
    ;       entry_label_exported.
            % proc entry; exported from a Mercury module

:- type label
    --->    internal_label(int, proc_label)
    ;       entry_label(entry_label_type, proc_label).

:- type code_addr
    --->    code_label(label)
            % A label defined in this Mercury module.

    ;       code_imported_proc(proc_label)
            % A label for a procedure from another Mercury module.

    ;       code_succip
            % The address in the `succip' register.

    ;       do_succeed(bool)
            % The bool is `yes' if there are any alternatives left.
            % If the bool is `no', we do a succeed_discard() rather than
            % a succeed().

    ;       do_redo
    ;       do_fail

    ;       do_trace_redo_fail_shallow
    ;       do_trace_redo_fail_deep
            % Labels in the runtime, the code at which calls MR_trace with
            % a REDO event and then fails. The shallow variety only does this
            % if the from_full flag was set on entry to the given procedure.

    ;       do_call_closure(ho_call_variant)
    ;       do_call_class_method(ho_call_variant)

    ;       do_not_reached.
            % We should never jump to this address.

:- type ho_call_variant
    --->    generic
            % This calls for the use of one of do_call_closure_compact and
            % do_call_class_method_compact which works for any number of
            % visible input arguments.

    ;       specialized_known(int).
            % If the integer is N, this calls for the use of do_call_closure_N
            % or do_call_class_method_N. These are specialized to assume N
            % visible regular register input arguments, and zero visible float
            % register input arguments.

    % Each function symbol of this type corresponds to one of the C types
    % {int,uint}_least{8,16,32}.
    %
    % Values of these types are intended for use in static data declarations,
    % not for data that gets stored in registers, stack slots etc.
:- type int_least_type
    --->    int_least8
    ;       uint_least8
    ;       int_least16
    ;       uint_least16
    ;       int_least32
    ;       uint_least32.

    % We categorize the data types used in the LLDS into a small number of
    % categories, for purposes such as choosing the right sort of register
    % for a given value to avoid unnecessary boxing/unboxing of floats.
    %
:- type llds_type
    --->    lt_bool
            % A boolean value represented using the C type `MR_Integer'.

    ;       lt_int_least(int_least_type)
            % As documented above.

    ;       lt_int(int_type)
            % A Mercury `int', represented in C as a value of type `MR_Integer'
            % (which is a signed integral type of the same size as a pointer).
            % Something whose C type is `MR_Unsigned' (the unsigned equivalent
            % of `MR_Integer').

    ;       lt_float
            % A Mercury `float', represented in C as a value of type `MR_Float'
            % (which may be either `float' or `double', but is usually
            % `double').

    ;       lt_string
            % A Mercury string; represented in C as a value of type
            % `MR_String'.

    ;       lt_data_ptr
            % A pointer to data; represented in C as a value of C type
            % `MR_Word *'.

    ;       lt_code_ptr
            % A pointer to code; represented in C as a value of C type
            % `MR_Code *'.

    ;       lt_word.
            % Something that can be assigned to a value of C type `MR_Word',
            % i.e., something whose size is a word but which may be either
            % signed or unsigned (used for registers, stack slots, etc).

:- type cell_arg
    --->    cell_arg_full_word(rval, completeness)
            % Fill a single word field of a cell with the given rval, which
            % could hold a single constructor argument, or multiple constructor
            % arguments if they are packed. If the second argument is
            % `incomplete' it means that the rval covers multiple constructor
            % arguments but some of the arguments are not instantiated.

    ;       cell_arg_double_word(rval)
            % Fill two words of a cell with the given rval, which must be a
            % double precision float.

    ;       cell_arg_skip_one_word
    ;       cell_arg_skip_two_words
            % Leave one or two words of a cell unfilled.

    ;       cell_arg_take_addr_one_word(prog_var, maybe(rval))
    ;       cell_arg_take_addr_two_words(prog_var, maybe({rval, rval})).
            % Take the address of a one- or two-word field.
            % If the second argument is `yes(Rval/Rvals)',
            % then set the field to Rval/Rvals beforehand.

:- type completeness
    --->    complete
    ;       incomplete.

:- func region_stack_id_to_string(region_stack_id) = string.

:- pred break_up_local_label(label::in, proc_label::out, int::out) is det.

    % Given a non-var lval, figure out its type.
    %
:- pred lval_type(lval::in, llds_type::out) is det.

    % Given a non-var rval, figure out its type.
    %
:- pred rval_type(rval::in, llds_type::out) is det.

    % Given a constant, figure out its type.
    %
:- pred const_type(rval_const::in, llds_type::out) is det.

    % Given a unary operator, figure out its return type.
    %
:- pred unop_return_type(unary_op::in, llds_type::out) is det.

    % Given a unary operator, figure out the type of its argument.
    %
:- pred unop_arg_type(unary_op::in, llds_type::out) is det.

    % Given a binary operator, figure out its return type.
    %
:- pred binop_return_type(binary_op::in, llds_type::out) is det.

    % Given a register, figure out its type.
    %
:- pred register_type(reg_type::in, llds_type::out) is det.

:- func get_proc_label(label) = proc_label.

:- func get_defining_module_name(proc_label) = module_name.

:- type have_non_local_gotos
    --->    have_non_local_gotos
    ;       do_not_have_non_local_gotos.

:- type have_asm_labels
    --->    have_asm_labels
    ;       do_not_have_asm_labels.

:- type have_unboxed_floats
    --->    have_unboxed_floats
    ;       do_not_have_unboxed_floats.

:- type use_float_registers
    --->    use_float_registers
    ;       do_not_use_float_registers.

:- type have_unboxed_int64s
    --->    have_unboxed_int64s
    ;       do_not_have_unboxed_int64s.

:- type have_static_ground_cells
    --->    have_static_ground_cells
    ;       do_not_have_static_ground_cells.

:- type have_static_ground_floats
    --->    have_static_ground_floats
    ;       do_not_have_static_ground_floats.

:- type have_static_ground_int64s
    --->    have_static_ground_int64s
    ;       do_not_have_static_ground_int64s.

:- type have_static_code_addresses
    --->    have_static_code_addresses
    ;       do_not_have_static_code_addresses.

:- type exprn_opts
    --->    exprn_opts(
                non_local_gotos         :: have_non_local_gotos,
                asm_labels              :: have_asm_labels,
                unboxed_floats          :: have_unboxed_floats,
                float_registers         :: use_float_registers,
                det_stack_float_width   :: stack_slot_width,
                unboxed_int64s          :: have_unboxed_int64s,
                static_ground_cells     :: have_static_ground_cells,
                static_ground_floats    :: have_static_ground_floats,
                static_ground_int64s    :: have_static_ground_int64s,
                static_code_addresses   :: have_static_code_addresses
            ).

:- func get_nonlocal_gotos(exprn_opts) = have_non_local_gotos.
:- func get_asm_labels(exprn_opts) = have_asm_labels.
:- func get_unboxed_floats(exprn_opts) = have_unboxed_floats.
:- func get_float_registers(exprn_opts) = use_float_registers.
:- func get_det_stack_float_width(exprn_opts) = stack_slot_width.
:- func get_unboxed_int64s(exprn_opts) = have_unboxed_int64s.
:- func get_static_ground_cells(exprn_opts) = have_static_ground_cells.
:- func get_static_ground_floats(exprn_opts) = have_static_ground_floats.
:- func get_static_ground_int64s(exprn_opts) = have_static_ground_int64s.
:- func get_static_code_addresses(exprn_opts) = have_static_code_addresses.

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

:- implementation.

:- import_module int.
:- import_module require.

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

typed_rvals_project_rvals([]) = [].
typed_rvals_project_rvals([typed_rval(Rval, _Type) | TypedRvals]) =
    [Rval | typed_rvals_project_rvals(TypedRvals)].

typed_rvals_project_types([]) = [].
typed_rvals_project_types([typed_rval(_Rval, Type) | TypedRvals]) =
    [Type | typed_rvals_project_types(TypedRvals)].

build_typed_rvals([], [], []).
build_typed_rvals([_|_], [], _) :-
    unexpected($pred, "length mismatch").
build_typed_rvals([], [_|_], _) :-
    unexpected($pred, "length mismatch").
build_typed_rvals([Rval | Rvals], [Type | Types], [TypedRval | TypedRvals]) :-
    TypedRval = typed_rval(Rval, Type),
    build_typed_rvals(Rvals, Types, TypedRvals).

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

first_nonfixed_embedded_slot_addr(EmbeddedStackId, FixedSize) = Rval :-
    EmbeddedStackId = embedded_stack_frame_id(MainStackId,
        _FirstSlot, LastSlot),
    % LastSlot has the lowest address; FirstSlot has the highest address.
    % The fixed slots are at the lowest addresses.
    % XXX Quan: we may need a +1 here.
    LowestAddrNonfixedSlot = LastSlot - FixedSize,
    Rval = stack_slot_num_to_lval_ref(MainStackId, LowestAddrNonfixedSlot).

stack_slot_to_lval(Slot) = Lval :-
    (
        Slot = det_slot(N, Width),
        (
            Width = single_width,
            Lval = stackvar(N)
        ;
            Width = double_width,
            Lval = double_stackvar(double_stackvar, N)
        )
    ;
        Slot = parent_det_slot(N, Width),
        (
            Width = single_width,
            Lval = parent_stackvar(N)
        ;
            Width = double_width,
            Lval = double_stackvar(double_parent_stackvar, N)
        )
    ;
        Slot = nondet_slot(N),
        Lval = framevar(N)
    ).

key_stack_slot_to_lval(_, Slot) =
    stack_slot_to_lval(Slot).

abs_locn_to_lval_or_any_reg(any_reg) = loa_any_reg.
abs_locn_to_lval_or_any_reg(abs_reg(Type, N)) = loa_lval(reg(Type, N)).
abs_locn_to_lval_or_any_reg(abs_stackvar(N, Width)) =
    loa_lval(stack_slot_to_lval(det_slot(N, Width))).
abs_locn_to_lval_or_any_reg(abs_parent_stackvar(N, Width)) =
    loa_lval(stack_slot_to_lval(parent_det_slot(N, Width))).
abs_locn_to_lval_or_any_reg(abs_framevar(N)) =
    loa_lval(stack_slot_to_lval(nondet_slot(N))).

abs_locn_to_lval(any_reg) = _ :-
    unexpected($pred, "any_reg").
abs_locn_to_lval(abs_reg(Type, N)) = reg(Type, N).
abs_locn_to_lval(abs_stackvar(N, Width)) =
    stack_slot_to_lval(det_slot(N, Width)).
abs_locn_to_lval(abs_parent_stackvar(N, Width)) =
    stack_slot_to_lval(parent_det_slot(N, Width)).
abs_locn_to_lval(abs_framevar(N)) =
    stack_slot_to_lval(nondet_slot(N)).

key_abs_locn_to_lval(_, AbsLocn) =
    abs_locn_to_lval(AbsLocn).

code_model_to_main_stack(model_det) = det_stack.
code_model_to_main_stack(model_semi) = det_stack.
code_model_to_main_stack(model_non) = nondet_stack.

stack_slot_num_to_lval(det_stack, SlotNum) = stackvar(SlotNum).
stack_slot_num_to_lval(nondet_stack, SlotNum) = framevar(SlotNum).

stack_slot_num_to_lval_ref(det_stack, SlotNum) =
    mem_addr(stackvar_ref(const(llconst_int(SlotNum)))).
stack_slot_num_to_lval_ref(nondet_stack, SlotNum) =
    mem_addr(framevar_ref(const(llconst_int(SlotNum)))).

region_stack_id_to_string(region_stack_ite) = "region_ite_stack".
region_stack_id_to_string(region_stack_disj) = "region_disj_stack".
region_stack_id_to_string(region_stack_commit) = "region_commit_stack".

break_up_local_label(Label, ProcLabel, LabelNum) :-
    (
        Label = internal_label(LabelNum, ProcLabel)
    ;
        Label = entry_label(_, _),
        unexpected($pred, "entry label")
    ).

lval_type(reg(RegType, _), Type) :-
    register_type(RegType, Type).
lval_type(succip, lt_code_ptr).
lval_type(maxfr, lt_data_ptr).
lval_type(curfr, lt_data_ptr).
lval_type(hp, lt_data_ptr).
lval_type(sp, lt_data_ptr).
lval_type(parent_sp, lt_data_ptr).
lval_type(temp(RegType, _), Type) :-
    register_type(RegType, Type).
lval_type(stackvar(_), lt_word).
lval_type(parent_stackvar(_), lt_word).
lval_type(framevar(_), lt_word).
lval_type(double_stackvar(_, _), lt_float).
lval_type(succip_slot(_), lt_code_ptr).
lval_type(redoip_slot(_), lt_code_ptr).
lval_type(redofr_slot(_), lt_data_ptr).
lval_type(succfr_slot(_), lt_data_ptr).
lval_type(prevfr_slot(_), lt_data_ptr).
lval_type(field(_, _, _), lt_word).
lval_type(lvar(_), _) :-
    unexpected($pred, "lvar").
lval_type(mem_ref(_), lt_word).
lval_type(global_var_ref(_), lt_word).

rval_type(lval(Lval), Type) :-
    lval_type(Lval, Type).
rval_type(var(_), _) :-
    unexpected($pred, "var").
    %
    % Note that mkword and data_addr consts must be of type data_ptr,
    % not of type word, to ensure that static consts containing them
    % get type `const MR_Word *', not type `MR_Word'; this is necessary because
    % casts from pointer to int must not be used in the initializers for
    % constant expressions -- if they are, then lcc barfs, and gcc generates
    % bogus code on some systems, (e.g. IRIX with shared libs). If the second
    % argument to mkword is an integer, not a pointer, then we will end up
    % casting it to a pointer, but casts from integer to pointer are OK;
    % it is only the reverse direction we need to avoid.
    %
rval_type(mkword(_, _), lt_data_ptr).
rval_type(mkword_hole(_), lt_data_ptr).
rval_type(const(Const), Type) :-
    const_type(Const, Type).
rval_type(cast(Type, _), Type).
rval_type(unop(UnOp, _), Type) :-
    unop_return_type(UnOp, Type).
rval_type(binop(BinOp, _, _), Type) :-
    binop_return_type(BinOp, Type).
rval_type(mem_addr(_), lt_data_ptr).

const_type(llconst_true, lt_bool).
const_type(llconst_false, lt_bool).
const_type(llconst_int(_), lt_int(int_type_int)).
const_type(llconst_uint(_), lt_int(int_type_uint)).
const_type(llconst_int8(_), lt_int(int_type_int8)).
const_type(llconst_uint8(_), lt_int(int_type_uint8)).
const_type(llconst_int16(_), lt_int(int_type_int16)).
const_type(llconst_uint16(_), lt_int(int_type_uint16)).
const_type(llconst_int32(_), lt_int(int_type_int32)).
const_type(llconst_uint32(_), lt_int(int_type_uint32)).
const_type(llconst_int64(_), lt_int(int_type_int64)).
const_type(llconst_uint64(_), lt_int(int_type_uint64)).
const_type(llconst_foreign(_, Type), Type).
const_type(llconst_float(_), lt_float).
const_type(llconst_string(_), lt_string).
const_type(llconst_multi_string(_), lt_string).
const_type(llconst_code_addr(_), lt_code_ptr).
const_type(llconst_data_addr(_), lt_data_ptr).
const_type(llconst_data_addr_word_offset(_, _), lt_data_ptr).

unop_return_type(tag, lt_word).
unop_return_type(strip_tag, lt_word).
unop_return_type(mkbody, lt_word).
unop_return_type(unmkbody, lt_word).
unop_return_type(bitwise_complement(IntType), lt_int(IntType)).
unop_return_type(logical_not, lt_bool).
unop_return_type(hash_string, lt_int(int_type_int)).
unop_return_type(hash_string2, lt_int(int_type_int)).
unop_return_type(hash_string3, lt_int(int_type_int)).
unop_return_type(hash_string4, lt_int(int_type_int)).
unop_return_type(hash_string5, lt_int(int_type_int)).
unop_return_type(hash_string6, lt_int(int_type_int)).
unop_return_type(dword_float_get_word0,  lt_word).
unop_return_type(dword_float_get_word1,  lt_word).
unop_return_type(dword_int64_get_word0,  lt_word).
unop_return_type(dword_int64_get_word1,  lt_word).
unop_return_type(dword_uint64_get_word0, lt_word).
unop_return_type(dword_uint64_get_word1, lt_word).

unop_arg_type(tag, lt_word).
unop_arg_type(strip_tag, lt_word).
unop_arg_type(mkbody, lt_word).
unop_arg_type(unmkbody, lt_word).
unop_arg_type(bitwise_complement(IntType), lt_int(IntType)).
unop_arg_type(logical_not, lt_bool).
unop_arg_type(hash_string, lt_string).
unop_arg_type(hash_string2, lt_string).
unop_arg_type(hash_string3, lt_string).
unop_arg_type(hash_string4, lt_string).
unop_arg_type(hash_string5, lt_string).
unop_arg_type(hash_string6, lt_string).
unop_arg_type(dword_float_get_word0,  lt_float).
unop_arg_type(dword_float_get_word1,  lt_float).
unop_arg_type(dword_int64_get_word0,  lt_int(int_type_int64)).
unop_arg_type(dword_int64_get_word1,  lt_int(int_type_int64)).
unop_arg_type(dword_uint64_get_word0, lt_int(int_type_uint64)).
unop_arg_type(dword_uint64_get_word1, lt_int(int_type_uint64)).

binop_return_type(int_arith(IntType, _), lt_int(IntType)).
binop_return_type(unchecked_left_shift(IntType, _), lt_int(IntType)).
binop_return_type(unchecked_right_shift(IntType, _), lt_int(IntType)).
binop_return_type(bitwise_and(IntType), lt_int(IntType)).
binop_return_type(bitwise_or(IntType), lt_int(IntType)).
binop_return_type(bitwise_xor(IntType), lt_int(IntType)).
binop_return_type(logical_and, lt_bool).
binop_return_type(logical_or, lt_bool).
binop_return_type(int_cmp(_, _), lt_bool).
binop_return_type(array_index(_Type), lt_word).
binop_return_type(string_unsafe_index_code_unit, lt_int(int_type_int)).
binop_return_type(offset_str_eq(_, _), lt_bool).
binop_return_type(str_cmp(_), lt_bool).
binop_return_type(str_nzp, lt_int(int_type_int)).
binop_return_type(int_as_uint_cmp(_), lt_bool).
binop_return_type(float_arith(_), lt_float).
binop_return_type(float_cmp(_), lt_bool).
binop_return_type(float_from_dword,  lt_float).
binop_return_type(int64_from_dword,  lt_int(int_type_int64)).
binop_return_type(uint64_from_dword, lt_int(int_type_uint64)).
binop_return_type(body, lt_word).
binop_return_type(pointer_equal_conservative, lt_bool).

register_type(reg_r, lt_word).
register_type(reg_f, lt_float).

get_proc_label(entry_label(_, ProcLabel)) = ProcLabel.
get_proc_label(internal_label(_, ProcLabel)) = ProcLabel.

get_defining_module_name(ordinary_proc_label(ModuleName, _, _, _, _, _))
    = ModuleName.
get_defining_module_name(special_proc_label(ModuleName, _, _, _, _, _))
    = ModuleName.

get_nonlocal_gotos(ExprnOpts) = ExprnOpts ^ non_local_gotos.
get_asm_labels(ExprnOpts) = ExprnOpts ^ asm_labels.
get_unboxed_floats(ExprnOpts) = ExprnOpts ^ unboxed_floats.
get_float_registers(ExprnOpts) = ExprnOpts ^ float_registers.
get_det_stack_float_width(ExprnOpts) = ExprnOpts ^ det_stack_float_width.
get_unboxed_int64s(ExprnOpts) = ExprnOpts ^ unboxed_int64s.
get_static_ground_cells(ExprnOpts) = ExprnOpts ^ static_ground_cells.
get_static_ground_floats(ExprnOpts) = ExprnOpts ^ static_ground_floats.
get_static_ground_int64s(ExprnOpts) = ExprnOpts ^ static_ground_int64s.
get_static_code_addresses(ExprnOpts) = ExprnOpts ^ static_code_addresses.

%-----------------------------------------------------------------------------%
:- end_module ll_backend.llds.
%-----------------------------------------------------------------------------%
