%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1994-2011 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: opt_util.m.
% Main author: zs.
%
% Utilities for LLDS to LLDS optimization.
%
%-----------------------------------------------------------------------------%

:- module ll_backend.opt_util.
:- interface.

:- import_module ll_backend.llds.
:- import_module mdbcomp.prim_data.

:- import_module bool.
:- import_module list.
:- import_module map.
:- import_module maybe.

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

:- type instrmap == map(label, instruction).
:- type lvalmap == map(label, maybe(instruction)).
:- type tailmap == map(label, list(instruction)).
:- type succmap == map(label, bool).

:- pred get_prologue(list(instruction)::in, instruction::out,
    list(instruction)::out, list(instruction)::out) is det.

:- pred gather_comments(list(instruction)::in,
    list(instruction)::out, list(instruction)::out) is det.

:- pred gather_comments_livevals(list(instruction)::in,
    list(instruction)::out, list(instruction)::out) is det.

    % Given a list of instructions, skip past any comment instructions
    % at the start and return the remaining instructions. We do this because
    % comment instructions get in the way of peephole optimization.
    %
:- pred skip_comments(list(instruction)::in, list(instruction)::out) is det.

:- pred skip_comments_livevals(list(instruction)::in,
    list(instruction)::out) is det.

:- pred skip_comments_labels(list(instruction)::in,
    list(instruction)::out) is det.

:- pred skip_comments_livevals_labels(list(instruction)::in,
    list(instruction)::out) is det.

    % Find the next assignment to the redoip of the frame whose address
    % is given by the base addresses in the second argument, provided
    % it is guaranteed to be reached from here, and guaranteed not to be
    % reached from anywhere else by a jump.
    %
:- pred next_assign_to_redoip(list(instruction)::in, list(lval)::in,
    list(instruction)::in, code_addr::out, list(instruction)::out,
    list(instruction)::out) is semidet.

    % See if these instructions touch nondet stack controls, i.e.
    % the virtual machine registers that point to the nondet stack
    % (curfr and maxfr) and the fixed slots in nondet stack frames.
    %
:- func touches_nondet_ctrl(list(instruction)) = bool.

    % Find the instructions up to and including the next one that
    % cannot fall through.
    %
:- pred find_no_fallthrough(list(instruction)::in,
    list(instruction)::out) is det.

    % Find the first label in the instruction stream.
    %
:- pred find_first_label(list(instruction)::in, label::out) is det.

    % Skip to the next label, returning the code before the label,
    % and the label together with the code after the label.
    %
:- pred skip_to_next_label(list(instruction)::in,
    list(instruction)::out, list(instruction)::out) is det.

    % Check whether the named label follows without any intervening code.
    % If yes, return the instructions after the label.
    %
:- pred is_this_label_next(label::in, list(instruction)::in,
    list(instruction)::out) is semidet.

    % Is a proceed instruction (i.e. a goto(succip) instruction)
    % next in the instruction list, possibly preceded by a restoration
    % of succip and a det stack frame removal? If yes, return the
    % instructions up to the proceed.
    %
:- pred is_proceed_next(list(instruction)::in,
    list(instruction)::out) is semidet.

    % Is a proceed instruction (i.e. a goto(succip) instruction)
    % next in the instruction list, possibly preceded by an assignment
    % to r1, a restoration of succip and a det stack frame removal?
    % If yes, return the instructions up to the proceed.
    %
:- pred is_sdproceed_next(list(instruction)::in,
    list(instruction)::out) is semidet.

    % Same as the previous predicate, but also return whether it is
    % a success or a fail.
    %
:- pred is_sdproceed_next_sf(list(instruction)::in,
    list(instruction)::out, bool::out) is semidet.

    % Is a succeed instruction (i.e. a goto(do_succeed(_)) instruction)
    % next in the instruction list? If yes, return the instructions
    % up to and including the succeed.
    %
:- pred is_succeed_next(list(instruction)::in,
    list(instruction)::out) is semidet.

    % Is the following code a test of r1, followed in both continuations
    % by a semidet proceed? Is the code in both continuations the same,
    % modulo livevals annotations and the value assigned to r1? Is MR_TRUE
    % assigned to r1 in the success continuation and MR_FALSE in the failure
    % continuation? If the answer is yes to all these questions, return
    % the code shared by the two continuations.
    %
:- pred is_forkproceed_next(list(instruction)::in, tailmap::in,
    list(instruction)::out) is semidet.

    % Remove the assignment to r1 from the list returned by is_sdproceed_next.
    %
:- pred filter_out_r1(list(instruction)::in, maybe(rval_const)::out,
    list(instruction)::out) is det.

    % Does the following code consist of straighline instructions that do not
    % modify nondet frame linkages, plus possibly if_val(..., dofail), and then
    % a succeed? If yes, then return all the instructions up to the succeed,
    % and all the following instructions.
    %
:- pred straight_alternative(list(instruction)::in,
    list(instruction)::out, list(instruction)::out) is semidet.

    % Find and return the initial sequence of instructions that do not
    % refer to stackvars and do not branch.
    %
:- pred no_stack_straight_line(list(instruction)::in,
    list(instruction)::out, list(instruction)::out) is det.

    % Check whether the given instruction sequence consist of an initial
    % sequence of instructions that do not refer to stackvars and do not
    % contain any entry points or unconditional branches away, until a
    % goto(do_succeed(yes)) instruction. If yes, return that initial sequence,
    % the comment on the goto(do_succeed(yes)) instruction, and the
    % instructions after it.
    %
:- pred may_replace_succeed_with_succeed_discard(list(instruction)::in,
    list(instruction)::out, string::out, list(instruction)::out) is semidet.

    % Remove the labels from a block of code for jumpopt.
    %
:- pred filter_out_labels(list(instruction)::in, list(instruction)::out)
    is det.

    % Remove any livevals instructions that do not precede an instruction
    % that needs one.
    %
:- pred filter_out_bad_livevals(list(instruction)::in, list(instruction)::out)
    is det.

    % Remove the livevals instruction from the list returned by
    % is_proceed_next.
    %
:- pred filter_out_livevals(list(instruction)::in, list(instruction)::out)
    is det.

    % Get just the livevals instructions from a list of instructions.
    %
:- pred filter_in_livevals(list(instruction)::in,
    list(instruction)::out) is det.

    % See if the condition of an if-then-else is constant, and if yes,
    % whether the branch will be taken or not.
    %
:- pred is_const_condition(rval::in, bool::out) is semidet.

    % Check whether an instruction can possibly branch away.
    %
:- func can_instr_branch_away(instr) = bool.

    % Check whether an instruction can possibly fall through
    % to the next instruction without using its label.
    %
:- func can_instr_fall_through(instr) = bool.

    % Check whether a code_addr, when the target of a goto, represents either
    % a call or a proceed/succeed; if so, it is the end of an extended basic
    % block and needs a livevals in front of it.
    %
:- func livevals_addr(code_addr) = bool.

    % Determine all the labels and code addresses which are referenced
    % by an instruction. The code addresses that are labels are returned
    % in both output arguments.
    %
:- pred instr_labels(instr::in, list(label)::out, list(code_addr)::out) is det.

    % Determine all the labels and code addresses which are referenced
    % by a list of instructions.
    %
:- pred instr_list_labels(list(instruction)::in,
    list(label)::out, list(code_addr)::out) is det.

    % Given an instruction, find the set of labels and other code addresses
    % to which it can cause control to transfer. In the case of calls, this
    % includes transfer via return from the called procedure.
    %
:- pred possible_targets(instr::in, list(label)::out, list(code_addr)::out)
    is det.

    % Find the maximum temp variable number used.
    %
:- pred count_temps_instr_list(list(instruction)::in, int::in, int::out,
    int::in, int::out) is det.

:- pred count_temps_instr(instr::in, int::in, int::out,
    int::in, int::out) is det.

    % See whether a (list of) instructions or instruction components
    % references the current stack frame (on either stack).
    %
:- func lval_refers_stackvars(lval) = bool.
:- func rval_refers_stackvars(rval) = bool.
:- func rvals_refer_stackvars(list(maybe(rval))) = bool.
:- func instr_refers_to_stack(instruction) = bool.
:- func block_refers_to_stack(list(instruction)) = bool.

    % See whether instructions until the next decr_sp (if any) refer to
    % any stackvars or branch away. If not, return the instructions up to
    % the decr_sp. A restoration of succip from the bottom stack slot
    % is allowed; this instruction is not returned in the output.
    % The same thing applies to assignments to detstackvars; these are
    % not useful if we throw away the stack frame.
    %
:- pred no_stackvars_til_decr_sp(list(instruction)::in, int::in,
    list(instruction)::out, list(instruction)::out) is semidet.

    % Format a label or proc_label for verbose messages during compilation.
    %
:- func format_label(label) = string.
:- func format_proc_label(proc_label) = string.

    % Find out if an instruction sequence has both incr_sp and decr_sp.
    %
:- pred has_both_incr_decr_sp(list(instruction)::in) is semidet.

    % Find out what rvals, if any, are needed to access an lval.
    %
:- pred lval_access_rvals(lval::in, list(rval)::out) is det.

    % See whether an rval is free of references to a given lval.
    %
:- pred rval_free_of_lval(rval::in, lval::in) is semidet.

    % See whether a list of rvals is free of references to a given lval.
    %
:- pred rvals_free_of_lval(list(rval)::in, lval::in) is semidet.

    % Count the number of hp increments in a block of code.
    %
:- pred count_incr_hp(list(instruction)::in, int::out) is det.

    % Whenever the input list of instructions contains two livevals pseudo-ops
    % without an intervening no-fall-through instruction, ensure that the
    % first of these registers as live every lval that is live in the second,
    % except those that are assigned to by intervening instructions. This makes
    % the shadowing of the second livevals by the first benign.
    %
:- pred propagate_livevals(list(instruction)::in, list(instruction)::out)
    is det.

    % Replace references to one set of local labels with references to another
    % set, in one instruction or a list of instructions. Control references
    % (those that can cause a transfer of control from the instruction they
    % occur in to the replaced label, either directly or via return from a
    % called procedure) are always replaced; references that treat the label
    % as data are replaced iff the third argument is set to "yes".
    %
    % With replace_labels_instruction_list, the last arg says whether
    % it is OK to replace a label in a label instruction itself.
    %
:- pred replace_labels_instr(instr::in, instr::out,
    map(label, label)::in, bool::in) is det.

:- pred replace_labels_instruction(instruction::in, instruction::out,
    map(label, label)::in, bool::in) is det.

:- pred replace_labels_instruction_list(
    list(instruction)::in, list(instruction)::out,
    map(label, label)::in, bool::in, bool::in) is det.

:- pred replace_labels_comps(
    list(foreign_proc_component)::in, list(foreign_proc_component)::out,
    map(label, label)::in) is det.

:- pred replace_labels_code_addr(code_addr::in, code_addr::out,
    map(label, label)::in) is det.

:- pred replace_labels_maybe_label_list(list(maybe(label))::in,
    list(maybe(label))::out, map(label, label)::in) is det.

:- pred replace_labels_label(label::in, label::out, map(label, label)::in)
    is det.

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

:- implementation.

:- import_module backend_libs.builtin_ops.
:- import_module check_hlds.type_util.
:- import_module hlds.special_pred.
:- import_module ll_backend.exprn_aux.
:- import_module parse_tree.prog_data.

:- import_module int.
:- import_module require.
:- import_module set.
:- import_module string.

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

get_prologue(Instrs0, LabelInstr, Comments, Instrs) :-
    gather_comments(Instrs0, Comments1, Instrs1),
    (
        Instrs1 = [Instr1 | Instrs2],
        Instr1 = llds_instr(label(_), _)
    ->
        LabelInstr = Instr1,
        gather_comments(Instrs2, Comments2, Instrs),
        list.append(Comments1, Comments2, Comments)
    ;
        unexpected($module, $pred, "procedure does not begin with label")
    ).

gather_comments(Instrs0, Comments, Instrs) :-
    (
        Instrs0 = [Instr0 | Instrs1],
        Instr0 = llds_instr(comment(_), _)
    ->
        gather_comments(Instrs1, Comments0, Instrs),
        Comments = [Instr0 | Comments0]
    ;
        Instrs = Instrs0,
        Comments = []
    ).

gather_comments_livevals(Instrs0, Comments, Instrs) :-
    (
        Instrs0 = [Instr0 | Instrs1],
        Instr0 = llds_instr(Uinstr0, _),
        ( Uinstr0 = comment(_)
        ; Uinstr0 = livevals(_)
        )
    ->
        gather_comments_livevals(Instrs1, Comments0, Instrs),
        Comments = [Instr0 | Comments0]
    ;
        Instrs = Instrs0,
        Comments = []
    ).

skip_comments(Instrs0, Instrs) :-
    ( Instrs0 = [llds_instr(comment(_), _) | Instrs1] ->
        skip_comments(Instrs1, Instrs)
    ;
        Instrs = Instrs0
    ).

skip_comments_livevals(Instrs0, Instrs) :-
    ( Instrs0 = [llds_instr(comment(_), _) | Instrs1] ->
        skip_comments(Instrs1, Instrs)
    ; Instrs0 = [llds_instr(livevals(_), _) | Instrs1] ->
        skip_comments_livevals(Instrs1, Instrs)
    ;
        Instrs = Instrs0
    ).

skip_comments_labels(Instrs0, Instrs) :-
    ( Instrs0 = [llds_instr(comment(_), _) | Instrs1] ->
        skip_comments_labels(Instrs1, Instrs)
    ; Instrs0 = [llds_instr(label(_), _) | Instrs1] ->
        skip_comments_labels(Instrs1, Instrs)
    ;
        Instrs = Instrs0
    ).

skip_comments_livevals_labels(Instrs0, Instrs) :-
    ( Instrs0 = [llds_instr(comment(_), _) | Instrs1] ->
        skip_comments_livevals_labels(Instrs1, Instrs)
    ; Instrs0 = [llds_instr(livevals(_), _) | Instrs1] ->
        skip_comments_livevals_labels(Instrs1, Instrs)
    ; Instrs0 = [llds_instr(label(_), _) | Instrs1] ->
        skip_comments_livevals_labels(Instrs1, Instrs)
    ;
        Instrs = Instrs0
    ).

next_assign_to_redoip([Instr | Instrs], AllowedBases, RevSkip,
        Redoip, Skip, Rest) :-
    Instr = llds_instr(Uinstr, _Comment),
    (
        Uinstr = assign(redoip_slot(lval(Fr)),
            const(llconst_code_addr(Redoip0))),
        list.member(Fr, AllowedBases)
    ->
        Redoip = Redoip0,
        list.reverse(RevSkip, Skip),
        Rest = Instrs
    ;
        Uinstr = mkframe(_, _)
    ->
        fail

    ;
        Uinstr = label(_)
    ->
        fail
    ;
        CanBranchAway = can_instr_branch_away(Uinstr),
        (
            CanBranchAway = no,
            next_assign_to_redoip(Instrs, AllowedBases, [Instr | RevSkip],
                Redoip, Skip, Rest)
        ;
            CanBranchAway = yes,
            fail
        )
    ).

find_no_fallthrough([], []).
find_no_fallthrough([Instr0 | Instrs0], Instrs) :-
    (
        Instr0 = llds_instr(Uinstr0, _),
        can_instr_fall_through(Uinstr0) = no
    ->
        Instrs = [Instr0]
    ;
        find_no_fallthrough(Instrs0, Instrs1),
        Instrs = [Instr0 | Instrs1]
    ).

find_first_label([], _) :-
    unexpected($module, $pred, "cannot find first label").
find_first_label([Instr0 | Instrs0], Label) :-
    ( Instr0 = llds_instr(label(LabelPrime), _) ->
        Label = LabelPrime
    ;
        find_first_label(Instrs0, Label)
    ).

skip_to_next_label([], [], []).
skip_to_next_label([Instr0 | Instrs0], Before, Remain) :-
    ( Instr0 = llds_instr(label(_), _) ->
        Before = [],
        Remain = [Instr0 | Instrs0]
    ;
        skip_to_next_label(Instrs0, Before1, Remain),
        Before = [Instr0 | Before1]
    ).

is_this_label_next(Label, [Instr | Moreinstr], Remainder) :-
    Instr = llds_instr(Uinstr, _Comment),
    ( Uinstr = comment(_) ->
        is_this_label_next(Label, Moreinstr, Remainder)
    ; Uinstr = livevals(_) ->
        % this is questionable
        is_this_label_next(Label, Moreinstr, Remainder)
    ; Uinstr = label(NextLabel) ->
        ( Label = NextLabel ->
            Remainder = Moreinstr
        ;
            is_this_label_next(Label, Moreinstr, Remainder)
        )
    ;
        fail
    ).

is_proceed_next(Instrs0, InstrsBetween) :-
    skip_comments_labels(Instrs0, Instrs1),
    Instrs1 = [Instr1 | Instrs2],
    ( Instr1 = llds_instr(assign(succip, lval(stackvar(_))), _) ->
        Instr1use = Instr1,
        skip_comments_labels(Instrs2, Instrs3)
    ;
        Instr1use = llds_instr(comment("no succip restoration"), ""),
        Instrs3 = Instrs1
    ),
    Instrs3 = [Instr3 | Instrs4],
    ( Instr3 = llds_instr(decr_sp(_), _) ->
        Instr3use = Instr3,
        skip_comments_labels(Instrs4, Instrs5)
    ;
        Instr3use = llds_instr(comment("no sp restoration"), ""),
        Instrs5 = Instrs3
    ),
    Instrs5 = [Instr5 | Instrs6],
    Instr5 = llds_instr(livevals(_), _),
    skip_comments_labels(Instrs6, Instrs7),
    Instrs7 = [Instr7 | _],
    Instr7 = llds_instr(goto(code_succip), _),
    InstrsBetween = [Instr1use, Instr3use, Instr5].

is_sdproceed_next(Instrs0, InstrsBetween) :-
    is_sdproceed_next_sf(Instrs0, InstrsBetween, _).

is_sdproceed_next_sf(Instrs0, InstrsBetween, Success) :-
    skip_comments_labels(Instrs0, Instrs1),
    Instrs1 = [Instr1 | Instrs2],
    ( Instr1 = llds_instr(assign(succip, lval(stackvar(_))), _) ->
        Instr1use = Instr1,
        skip_comments_labels(Instrs2, Instrs3)
    ;
        Instr1use = llds_instr(comment("no succip restoration"), ""),
        Instrs3 = Instrs1
    ),
    Instrs3 = [Instr3 | Instrs4],
    ( Instr3 = llds_instr(decr_sp(_), _) ->
        Instr3use = Instr3,
        skip_comments_labels(Instrs4, Instrs5)
    ;
        Instr3use = llds_instr(comment("no sp restoration"), ""),
        Instrs5 = Instrs3
    ),
    Instrs5 = [Instr5 | Instrs6],
    Instr5 = llds_instr(assign(reg(reg_r, 1), const(R1val)), _),
    (
        R1val = llconst_true,
        Success = yes
    ;
        R1val = llconst_false,
        Success = no
    ),
    skip_comments_labels(Instrs6, Instrs7),
    Instrs7 = [Instr7 | Instrs8],
    Instr7 = llds_instr(livevals(_), _),
    skip_comments_labels(Instrs8, Instrs9),
    Instrs9 = [Instr9 | _],
    Instr9 = llds_instr(goto(code_succip), _),
    InstrsBetween = [Instr1use, Instr3use, Instr5, Instr7].

is_succeed_next(Instrs0, InstrsBetweenIncl) :-
    skip_comments_labels(Instrs0, Instrs1),
    Instrs1 = [Instr1 | Instrs2],
    Instr1 = llds_instr(livevals(_), _),
    skip_comments_labels(Instrs2, Instrs3),
    Instrs3 = [Instr3 | _],
    Instr3 = llds_instr(goto(do_succeed(_)), _),
    InstrsBetweenIncl = [Instr1, Instr3].

is_forkproceed_next(Instrs0, Sdprocmap, Between) :-
    skip_comments_labels(Instrs0, Instrs1),
    Instrs1 = [Instr1 | Instrs2],
    Instr1 = llds_instr(Uinstr1, _),
    (
        Uinstr1 = if_val(lval(reg(reg_r, 1)), code_label(JumpLabel))
    ->
        map.search(Sdprocmap, JumpLabel, BetweenJump),
        is_sdproceed_next(Instrs2, BetweenFall),
        filter_out_r1(BetweenJump, yes(llconst_true), BetweenTrue0),
        filter_out_livevals(BetweenTrue0, Between),
        filter_out_r1(BetweenFall, yes(llconst_false), BetweenFalse0),
        filter_out_livevals(BetweenFalse0, Between)
    ;
        Uinstr1 = if_val(unop(logical_not, lval(reg(reg_r, 1))),
            code_label(JumpLabel))
    ->
        map.search(Sdprocmap, JumpLabel, BetweenJump),
        is_sdproceed_next(Instrs2, BetweenFall),
        filter_out_r1(BetweenJump, yes(llconst_false), BetweenFalse0),
        filter_out_livevals(BetweenFalse0, Between),
        filter_out_r1(BetweenFall, yes(llconst_true), BetweenTrue0),
        filter_out_livevals(BetweenTrue0, Between)
    ;
        fail
    ).

filter_out_r1([], no, []).
filter_out_r1([Instr0 | Instrs0], Success, Instrs) :-
    filter_out_r1(Instrs0, Success0, Instrs1),
    ( Instr0 = llds_instr(assign(reg(reg_r, 1), const(Success1)), _) ->
        Instrs = Instrs1,
        Success = yes(Success1)
    ;
        Instrs = [Instr0 | Instrs1],
        Success = Success0
    ).

straight_alternative(Instrs0, Between, After) :-
    straight_alternative_2(Instrs0, [], BetweenRev, After),
    list.reverse(BetweenRev, Between).

:- pred straight_alternative_2(list(instruction)::in, list(instruction)::in,
    list(instruction)::out, list(instruction)::out) is semidet.

straight_alternative_2([Instr0 | Instrs0], !Between, After) :-
    Instr0 = llds_instr(Uinstr0, _),
    (
        Uinstr0 = label(_)
    ->
        fail
    ;
        Uinstr0 = goto(do_succeed(no))
    ->
        After = Instrs0
    ;
        (
            can_instr_branch_away(Uinstr0) = no,
            touches_nondet_ctrl_instr(Uinstr0) = no
        ;
            Uinstr0 = if_val(_, CodeAddr),
            ( CodeAddr = do_fail ; CodeAddr = do_redo )
        )
    ->
        !:Between = [Instr0 | !.Between],
        straight_alternative_2(Instrs0, !Between, After)
    ;
        fail
    ).

no_stack_straight_line(Instrs0, StraightLine, Instrs) :-
    no_stack_straight_line_2(Instrs0, [], RevStraightLine, Instrs),
    list.reverse(RevStraightLine, StraightLine).

:- pred no_stack_straight_line_2(list(instruction)::in, list(instruction)::in,
    list(instruction)::out, list(instruction)::out) is det.

no_stack_straight_line_2([], !RevStraightLine, []).
no_stack_straight_line_2([Instr0 | Instrs0], !RevStraightLine, Instrs) :-
    Instr0 = llds_instr(Uinstr, _),
    (
        (
            Uinstr = comment(_)
        ;
            Uinstr = livevals(_)
        ;
            Uinstr = assign(Lval, Rval),
            lval_refers_stackvars(Lval) = no,
            rval_refers_stackvars(Rval) = no
        )
    ->
        !:RevStraightLine = [Instr0 | !.RevStraightLine],
        no_stack_straight_line_2(Instrs0, !RevStraightLine, Instrs)
    ;
        Instrs = [Instr0 | Instrs0]
    ).

may_replace_succeed_with_succeed_discard(Instrs0, UntilSucceed, SucceedComment,
        Remain) :-
    may_replace_succeed_with_succeed_discard_2(Instrs0, [], RevUntilSucceed,
        SucceedComment, Remain),
    list.reverse(RevUntilSucceed, UntilSucceed).

:- pred may_replace_succeed_with_succeed_discard_2(list(instruction)::in,
    list(instruction)::in, list(instruction)::out, string::out,
    list(instruction)::out) is semidet.

may_replace_succeed_with_succeed_discard_2([Instr0 | Instrs0],
        !RevUntilSucceed, SucceedComment, Remain) :-
    Instr0 = llds_instr(Uinstr, Comment),
    (
        Uinstr = goto(do_succeed(yes))
    ->
        SucceedComment = Comment,
        Remain = Instrs0
    ;
        (
            Uinstr = assign(Lval, Rval),
            lval_refers_stackvars(Lval) = no,
            rval_refers_stackvars(Rval) = no
        ;
            ( Uinstr = comment(_)
            ; Uinstr = livevals(_)
            ; Uinstr = if_val(_, _)
            ; Uinstr = incr_hp(_, _, _, _, _, _, _, _)
            ; Uinstr = mark_hp(_)
            ; Uinstr = restore_hp(_)
            ; Uinstr = free_heap(_)
            ; Uinstr = store_ticket(_)
            ; Uinstr = store_ticket(_)
            ; Uinstr = reset_ticket(_, _)
            ; Uinstr = prune_ticket
            ; Uinstr = discard_ticket
            ; Uinstr = mark_ticket_stack(_)
            ; Uinstr = prune_tickets_to(_)
            )
        )
    ->
        !:RevUntilSucceed = [Instr0 | !.RevUntilSucceed],
        may_replace_succeed_with_succeed_discard_2(Instrs0, !RevUntilSucceed,
            SucceedComment, Remain)
    ;
        fail
    ).

lval_refers_stackvars(reg(_, _)) = no.
lval_refers_stackvars(stackvar(_)) = yes.
lval_refers_stackvars(parent_stackvar(_)) = yes.
lval_refers_stackvars(framevar(_)) = yes.
lval_refers_stackvars(succip) = no.
lval_refers_stackvars(maxfr) = no.
lval_refers_stackvars(curfr) = no.
lval_refers_stackvars(succfr_slot(_)) = yes.
lval_refers_stackvars(prevfr_slot(_)) = yes.
lval_refers_stackvars(redofr_slot(_)) = yes.
lval_refers_stackvars(redoip_slot(_)) = yes.
lval_refers_stackvars(succip_slot(_)) = yes.
lval_refers_stackvars(hp) = no.
lval_refers_stackvars(sp) = no.
lval_refers_stackvars(parent_sp) = no.
lval_refers_stackvars(field(_, Rval, FieldNum)) =
    bool.or(
        rval_refers_stackvars(Rval),
        rval_refers_stackvars(FieldNum)).
lval_refers_stackvars(lvar(_)) = _ :-
    unexpected($module, $pred, "lvar").
lval_refers_stackvars(temp(_, _)) = no.
lval_refers_stackvars(mem_ref(Rval)) =
    rval_refers_stackvars(Rval).
lval_refers_stackvars(global_var_ref(_)) = no.

:- func mem_ref_refers_stackvars(mem_ref) = bool.

mem_ref_refers_stackvars(stackvar_ref(_)) = yes.
mem_ref_refers_stackvars(framevar_ref(_)) = yes.
mem_ref_refers_stackvars(heap_ref(Rval1, _, Rval2)) =
    bool.or(rval_refers_stackvars(Rval1), rval_refers_stackvars(Rval2)).

rval_refers_stackvars(lval(Lval)) =
    lval_refers_stackvars(Lval).
rval_refers_stackvars(var(_)) = _ :-
    unexpected($module, $pred, "var").
rval_refers_stackvars(mkword(_, Rval)) =
    rval_refers_stackvars(Rval).
rval_refers_stackvars(const(_)) = no.
rval_refers_stackvars(unop(_, Rval)) =
    rval_refers_stackvars(Rval).
rval_refers_stackvars(binop(_, Rval1, Rval2)) =
    bool.or(rval_refers_stackvars(Rval1), rval_refers_stackvars(Rval2)).
rval_refers_stackvars(mem_addr(MemRef)) =
    mem_ref_refers_stackvars(MemRef).

% XXX probably unused
rvals_refer_stackvars([]) = no.
rvals_refer_stackvars([MaybeRval | Tail]) =
    (
        (
            MaybeRval = no
        ;
            MaybeRval = yes(Rval),
            rval_refers_stackvars(Rval) = no
        )
    ->
        rvals_refer_stackvars(Tail)
    ;
        yes
    ).

:- func code_addr_refers_to_stack(code_addr) = bool.

code_addr_refers_to_stack(code_label(_)) = no.
code_addr_refers_to_stack(code_imported_proc(_)) = no.
code_addr_refers_to_stack(code_succip) = no.
code_addr_refers_to_stack(do_succeed(_)) = yes.
code_addr_refers_to_stack(do_redo) = yes.
code_addr_refers_to_stack(do_fail) = yes.
code_addr_refers_to_stack(do_trace_redo_fail_shallow) = yes.
code_addr_refers_to_stack(do_trace_redo_fail_deep) = yes.
code_addr_refers_to_stack(do_call_closure(_)) = no.
code_addr_refers_to_stack(do_call_class_method(_)) = no.
code_addr_refers_to_stack(do_not_reached) = no.

no_stackvars_til_decr_sp([Instr0 | Instrs0], FrameSize, Between, Remain) :-
    Instr0 = llds_instr(Uinstr0, _),
    (
        Uinstr0 = comment(_),
        no_stackvars_til_decr_sp(Instrs0, FrameSize, Between0, Remain),
        Between = [Instr0 | Between0]
    ;
        Uinstr0 = livevals(_),
        no_stackvars_til_decr_sp(Instrs0, FrameSize, Between0, Remain),
        Between = [Instr0 | Between0]
    ;
        Uinstr0 = assign(Lval, Rval),
        (
            Lval = stackvar(_),
            rval_refers_stackvars(Rval) = no
        ->
            no_stackvars_til_decr_sp(Instrs0, FrameSize, Between, Remain)
        ;
            Lval = succip,
            Rval = lval(stackvar(FrameSize)),
            skip_comments(Instrs0, Instrs1),
            Instrs1 = [llds_instr(decr_sp(FrameSize), _) | Instrs2]
        ->
            Between = [],
            Remain = Instrs2
        ;
            lval_refers_stackvars(Lval) = no,
            rval_refers_stackvars(Rval) = no,
            no_stackvars_til_decr_sp(Instrs0, FrameSize, Between0, Remain),
            Between = [Instr0 | Between0]
        )
    ;
        Uinstr0 = incr_hp(Lval, _, _, Rval, _, _, MaybeRegionRval,
            MaybeReuse),
        lval_refers_stackvars(Lval) = no,
        rval_refers_stackvars(Rval) = no,
        (
            MaybeRegionRval = yes(RegionRval),
            rval_refers_stackvars(RegionRval) = no
        ;
            MaybeRegionRval = no
        ),
        (
            MaybeReuse = llds_reuse(ReuseRval, MaybeFlagLval),
            rval_refers_stackvars(ReuseRval) = no,
            (
                MaybeFlagLval = yes(FlagLval),
                lval_refers_stackvars(FlagLval) = no
            ;
                MaybeFlagLval = no
            )
        ;
            MaybeReuse = no_llds_reuse
        ),
        no_stackvars_til_decr_sp(Instrs0, FrameSize, Between0, Remain),
        Between = [Instr0 | Between0]
    ;
        Uinstr0 = decr_sp(FrameSize),
        Between = [],
        Remain = Instrs0
    ).

block_refers_to_stack([]) = no.
block_refers_to_stack([Instr | Instrs]) = Refers :-
    instr_refers_to_stack(Instr) = InstrRefers,
    (
        InstrRefers = yes,
        Refers = yes
    ;
        InstrRefers = no,
        Instr = llds_instr(Uinstr, _),
        CanFallThrough = can_instr_fall_through(Uinstr),
        (
            CanFallThrough = yes,
            Refers = block_refers_to_stack(Instrs)
        ;
            CanFallThrough = no,
            Refers = no
        )
    ).

instr_refers_to_stack(llds_instr(Uinstr, _)) = Refers :-
    (
        ( Uinstr = comment(_)
        ; Uinstr = livevals(_)
        ; Uinstr = label(_)
        ; Uinstr = arbitrary_c_code(_, _, _)
        ; Uinstr = discard_ticket
        ; Uinstr = prune_ticket
        ),
        Refers = no
    ;
        ( Uinstr = llcall(_, _, _, _, _, _)
        ; Uinstr = mkframe(_, _)
        ; Uinstr = push_region_frame(_, _)
        ; Uinstr = region_fill_frame(_, _, _, _, _)
        ; Uinstr = region_set_fixed_slot(_, _, _)
        ; Uinstr = use_and_maybe_pop_region_frame(_, _)
        ; Uinstr = incr_sp(_, _, _)
        ; Uinstr = decr_sp(_)
        ; Uinstr = decr_sp_and_return(_)
        ; Uinstr = init_sync_term(_, _, _)
        ; Uinstr = fork_new_child(_, _)
        ; Uinstr = join_and_continue(_, _)
        ),
        Refers = yes
    ;
        Uinstr = block(_, _, BlockInstrs),
        Refers = block_refers_to_stack(BlockInstrs)
    ;
        ( Uinstr = assign(Lval, Rval)
        ; Uinstr = keep_assign(Lval, Rval)
        ),
        Refers = bool.or(
            lval_refers_stackvars(Lval),
            rval_refers_stackvars(Rval))
    ;
        Uinstr = goto(CodeAddr),
        Refers = code_addr_refers_to_stack(CodeAddr)
    ;
        Uinstr = computed_goto(Rval, _Labels),
        Refers = rval_refers_stackvars(Rval)
    ;
        Uinstr = if_val(Rval, CodeAddr),
        Refers = bool.or(
            rval_refers_stackvars(Rval),
            code_addr_refers_to_stack(CodeAddr))
    ;
        Uinstr = save_maxfr(Lval),
        Refers = lval_refers_stackvars(Lval)
    ;
        Uinstr = restore_maxfr(Lval),
        Refers = lval_refers_stackvars(Lval)
    ;
        Uinstr = incr_hp(Lval, _, _, Rval, _, _, MaybeRegionRval,
            MaybeReuse),
        some [!Refers] (
            !:Refers = bool.or(
                lval_refers_stackvars(Lval),
                rval_refers_stackvars(Rval)),
            (
                MaybeRegionRval = yes(RegionRval),
                bool.or(rval_refers_stackvars(RegionRval), !Refers)
            ;
                MaybeRegionRval = no
            ),
            (
                MaybeReuse = llds_reuse(ReuseRval, MaybeFlagLval),
                bool.or(rval_refers_stackvars(ReuseRval), !Refers),
                (
                    MaybeFlagLval = yes(FlagLval),
                    bool.or(lval_refers_stackvars(FlagLval), !Refers)
                ;
                    MaybeFlagLval = no
                )
            ;
                MaybeReuse = no_llds_reuse
            ),
            Refers = !.Refers
        )
    ;
        Uinstr = mark_hp(Lval),
        Refers = lval_refers_stackvars(Lval)
    ;
        Uinstr = restore_hp(Rval),
        Refers = rval_refers_stackvars(Rval)
    ;
        Uinstr = free_heap(Rval),
        Refers = rval_refers_stackvars(Rval)
    ;
        Uinstr = store_ticket(Lval),
        Refers = lval_refers_stackvars(Lval)
    ;
        Uinstr = reset_ticket(Rval, _Reason),
        Refers = rval_refers_stackvars(Rval)
    ;
        Uinstr = mark_ticket_stack(Lval),
        Refers = lval_refers_stackvars(Lval)
    ;
        Uinstr = prune_tickets_to(Rval),
        Refers = rval_refers_stackvars(Rval)
    ;
        Uinstr = foreign_proc_code(_, Components, _, _, _, _, _, _, _, _),
        Refers = bool.or_list(list.map(foreign_proc_component_refers_stackvars,
            Components))
    ).

:- func foreign_proc_component_refers_stackvars(foreign_proc_component) = bool.

foreign_proc_component_refers_stackvars(Component) = Refers :-
    (
        Component = foreign_proc_inputs(Inputs),
        bool.or_list(list.map(foreign_proc_input_refers_stackvars, Inputs),
            Refers)
    ;
        Component = foreign_proc_outputs(Outputs),
        bool.or_list(list.map(foreign_proc_output_refers_stackvars, Outputs),
            Refers)
    ;
        ( Component = foreign_proc_user_code(_, _, _)
        ; Component = foreign_proc_raw_code(_, _, _, _)
        ; Component = foreign_proc_fail_to(_)
        ; Component = foreign_proc_alloc_id(_)
        ; Component = foreign_proc_noop
        ),
        Refers = no
    ).

:- func foreign_proc_input_refers_stackvars(foreign_proc_input) = bool.

foreign_proc_input_refers_stackvars(Input) = Refers :-
    Input = foreign_proc_input(_Name, _Type, IsDummy, _OrigType, Rval,
        _MaybeForeign, _BoxPolicy),
    (
        IsDummy = is_dummy_type,
        Refers = no
    ;
        IsDummy = is_not_dummy_type,
        Refers = rval_refers_stackvars(Rval)
    ).

:- func foreign_proc_output_refers_stackvars(foreign_proc_output) = bool.

foreign_proc_output_refers_stackvars(Input) = Refers :-
    Input = foreign_proc_output(Lval, _Type, IsDummy, _OrigType, _Name,
        _MaybeForeign, _BoxPolicy),
    (
        IsDummy = is_dummy_type,
        Refers = no
    ;
        IsDummy = is_not_dummy_type,
        Refers = lval_refers_stackvars(Lval)
    ).

filter_out_labels([], []).
filter_out_labels([Instr0 | Instrs0], Instrs) :-
    filter_out_labels(Instrs0, Instrs1),
    ( Instr0 = llds_instr(label(_), _) ->
        Instrs = Instrs1
    ;
        Instrs = [Instr0 | Instrs1]
    ).

filter_out_bad_livevals([], []).
filter_out_bad_livevals([Instr0 | Instrs0], Instrs) :-
    filter_out_bad_livevals(Instrs0, Instrs1),
    (
        Instr0 = llds_instr(livevals(_), _),
        skip_comments(Instrs1, Instrs2),
        Instrs2 = [llds_instr(Uinstr2, _) | _],
        can_use_livevals(Uinstr2, no)
    ->
        Instrs = Instrs1
    ;
        Instrs = [Instr0 | Instrs1]
    ).

filter_out_livevals([], []).
filter_out_livevals([Instr0 | Instrs0], Instrs) :-
    filter_out_livevals(Instrs0, Instrs1),
    ( Instr0 = llds_instr(livevals(_), _) ->
        Instrs = Instrs1
    ;
        Instrs = [Instr0 | Instrs1]
    ).

filter_in_livevals([], []).
filter_in_livevals([Instr0 | Instrs0], Instrs) :-
    filter_in_livevals(Instrs0, Instrs1),
    ( Instr0 = llds_instr(livevals(_), _) ->
        Instrs = [Instr0 | Instrs1]
    ;
        Instrs = Instrs1
    ).

    % We recognize only a subset of all constant conditions.
    % The time to extend this predicate is when the rest of the compiler
    % generates more complicated constant conditions.
is_const_condition(const(Const), Taken) :-
    ( Const = llconst_true ->
        Taken = yes
    ; Const = llconst_false ->
        Taken = no
    ;
        unexpected($module, $pred,
            "non-boolean constant as if-then-else condition")
    ).
is_const_condition(unop(Op, Rval1), Taken) :-
    Op = logical_not,
    is_const_condition(Rval1, Taken1),
    bool.not(Taken1, Taken).
is_const_condition(binop(Op, Rval1, Rval2), Taken) :-
    Op = eq,
    Rval1 = Rval2,
    Taken = yes.

can_instr_branch_away(comment(_)) = no.
can_instr_branch_away(livevals(_)) = no.
can_instr_branch_away(block(_, _, _)) = yes.
can_instr_branch_away(assign(_, _)) = no.
can_instr_branch_away(keep_assign(_, _)) = no.
can_instr_branch_away(llcall(_, _, _, _, _, _)) = yes.
can_instr_branch_away(mkframe(_, _)) = no.
can_instr_branch_away(label(_)) = no.
can_instr_branch_away(goto(_)) = yes.
can_instr_branch_away(computed_goto(_, _)) = yes.
can_instr_branch_away(arbitrary_c_code(_, _, _)) = no.
can_instr_branch_away(if_val(_, _)) = yes.
can_instr_branch_away(save_maxfr(_)) = no.
can_instr_branch_away(restore_maxfr(_)) = no.
can_instr_branch_away(incr_hp(_, _, _, _, _, _, _, _)) = no.
can_instr_branch_away(mark_hp(_)) = no.
can_instr_branch_away(restore_hp(_)) = no.
can_instr_branch_away(free_heap(_)) = no.
can_instr_branch_away(push_region_frame(_, _)) = no.
can_instr_branch_away(region_fill_frame(_, _, _, _, _)) = no.
can_instr_branch_away(region_set_fixed_slot(_, _, _)) = no.
can_instr_branch_away(use_and_maybe_pop_region_frame(_, _)) = no.
can_instr_branch_away(store_ticket(_)) = no.
can_instr_branch_away(reset_ticket(_, _)) = no.
can_instr_branch_away(discard_ticket) = no.
can_instr_branch_away(prune_ticket) = no.
can_instr_branch_away(mark_ticket_stack(_)) = no.
can_instr_branch_away(prune_tickets_to(_)) = no.
can_instr_branch_away(incr_sp(_, _, _)) = no.
can_instr_branch_away(decr_sp(_)) = no.
can_instr_branch_away(decr_sp_and_return(_)) = yes.
can_instr_branch_away(init_sync_term(_, _, _)) = no.
can_instr_branch_away(fork_new_child(_, _)) = no.
can_instr_branch_away(join_and_continue(_, _)) = yes.
can_instr_branch_away(foreign_proc_code(_, Comps, _, _, _, _, _, _, _, _)) =
    can_components_branch_away(Comps).

:- func can_components_branch_away(list(foreign_proc_component)) = bool.

can_components_branch_away([]) = no.
can_components_branch_away([Component | Components]) = !:BranchAway :-
    !:BranchAway = can_component_branch_away(Component),
    (
        !.BranchAway = yes
    ;
        !.BranchAway = no,
        !:BranchAway = can_components_branch_away(Components)
    ).

    % The input and output components get expanded to straight line code.
    % Some of the raw_code components we generate for nondet pragma C codes
    % invoke succeed(), which definitely does branch away.
    % Also the raw_code components for semidet pragma C codes can
    % branch to a label on failure.
    % User-written C code cannot branch away because users do not know
    % how to do that. (They can call other functions, but those functions
    % will return, so control will still go to the instruction following
    % this one. We the developers could write C code that branched away,
    % but we are careful to preserve a declarative interface, and that
    % is incompatible with branching away.)
    %
:- func can_component_branch_away(foreign_proc_component) = bool.

can_component_branch_away(foreign_proc_inputs(_)) = no.
can_component_branch_away(foreign_proc_outputs(_)) = no.
can_component_branch_away(foreign_proc_raw_code(CanBranchAway, _, _, _))
        = CanBranchAwayBool :-
    (
        CanBranchAway = can_branch_away,
        CanBranchAwayBool = yes
    ;
        CanBranchAway = cannot_branch_away,
        CanBranchAwayBool = no
    ).
can_component_branch_away(foreign_proc_user_code(_, _, _)) = no.
can_component_branch_away(foreign_proc_fail_to(_)) = yes.
can_component_branch_away(foreign_proc_alloc_id(_)) = no.
can_component_branch_away(foreign_proc_noop) = no.

can_instr_fall_through(comment(_)) = yes.
can_instr_fall_through(livevals(_)) = yes.
can_instr_fall_through(block(_, _, Instrs)) = FallThrough :-
    can_block_fall_through(Instrs, FallThrough).
can_instr_fall_through(assign(_, _)) = yes.
can_instr_fall_through(keep_assign(_, _)) = yes.
can_instr_fall_through(llcall(_, _, _, _, _, _)) = no.
can_instr_fall_through(mkframe(_, _)) = yes.
can_instr_fall_through(label(_)) = yes.
can_instr_fall_through(goto(_)) = no.
can_instr_fall_through(computed_goto(_, _)) = no.
can_instr_fall_through(arbitrary_c_code(_, _, _)) = yes.
can_instr_fall_through(if_val(_, _)) = yes.
can_instr_fall_through(save_maxfr(_)) = yes.
can_instr_fall_through(restore_maxfr(_)) = yes.
can_instr_fall_through(incr_hp(_, _, _, _, _, _, _, _)) = yes.
can_instr_fall_through(mark_hp(_)) = yes.
can_instr_fall_through(restore_hp(_)) = yes.
can_instr_fall_through(free_heap(_)) = yes.
can_instr_fall_through(push_region_frame(_, _)) = yes.
can_instr_fall_through(region_fill_frame(_, _, _, _, _)) = yes.
can_instr_fall_through(region_set_fixed_slot(_, _, _)) = yes.
can_instr_fall_through(use_and_maybe_pop_region_frame(_, _)) = yes.
can_instr_fall_through(store_ticket(_)) = yes.
can_instr_fall_through(reset_ticket(_, _)) = yes.
can_instr_fall_through(discard_ticket) = yes.
can_instr_fall_through(prune_ticket) = yes.
can_instr_fall_through(mark_ticket_stack(_)) = yes.
can_instr_fall_through(prune_tickets_to(_)) = yes.
can_instr_fall_through(incr_sp(_, _, _)) = yes.
can_instr_fall_through(decr_sp(_)) = yes.
can_instr_fall_through(decr_sp_and_return(_)) = no.
can_instr_fall_through(init_sync_term(_, _, _)) = yes.
can_instr_fall_through(fork_new_child(_, _)) = yes.
can_instr_fall_through(join_and_continue(_, _)) = no.
can_instr_fall_through(foreign_proc_code(_, _, _, _, _, _, _, _, _, _)) = yes.

    % Check whether an instruction sequence can possibly fall through
    % to the next instruction without using its label.
    %
:- pred can_block_fall_through(list(instruction)::in, bool::out) is det.

can_block_fall_through([], yes).
can_block_fall_through([llds_instr(Uinstr, _) | Instrs], FallThrough) :-
    ( can_instr_fall_through(Uinstr) = no ->
        FallThrough = no
    ;
        can_block_fall_through(Instrs, FallThrough)
    ).

:- pred can_use_livevals(instr::in, bool::out) is det.

can_use_livevals(comment(_), no).
can_use_livevals(livevals(_), no).
can_use_livevals(block(_, _, _), no).
can_use_livevals(assign(_, _), no).
can_use_livevals(keep_assign(_, _), no).
can_use_livevals(llcall(_, _, _, _, _, _), yes).
can_use_livevals(mkframe(_, _), no).
can_use_livevals(label(_), no).
can_use_livevals(goto(_), yes).
can_use_livevals(computed_goto(_, _), no).
can_use_livevals(arbitrary_c_code(_, _, _), no).
can_use_livevals(if_val(_, _), yes).
can_use_livevals(save_maxfr(_), no).
can_use_livevals(restore_maxfr(_), no).
can_use_livevals(incr_hp(_, _, _, _, _, _, _, _), no).
can_use_livevals(mark_hp(_), no).
can_use_livevals(restore_hp(_), no).
can_use_livevals(free_heap(_), no).
can_use_livevals(push_region_frame(_, _), no).
can_use_livevals(region_fill_frame(_, _, _, _, _), no).
can_use_livevals(region_set_fixed_slot(_, _, _), no).
can_use_livevals(use_and_maybe_pop_region_frame(_, _), no).
can_use_livevals(store_ticket(_), no).
can_use_livevals(reset_ticket(_, _), no).
can_use_livevals(discard_ticket, no).
can_use_livevals(prune_ticket, no).
can_use_livevals(mark_ticket_stack(_), no).
can_use_livevals(prune_tickets_to(_), no).
can_use_livevals(incr_sp(_, _, _), no).
can_use_livevals(decr_sp(_), no).
can_use_livevals(decr_sp_and_return(_), yes).
can_use_livevals(init_sync_term(_, _, _), no).
can_use_livevals(fork_new_child(_, _), no).
can_use_livevals(join_and_continue(_, _), no).
can_use_livevals(foreign_proc_code(_, _, _, _, _, _, _, _, _, _), no).

instr_labels(Instr, Labels, CodeAddrs) :-
    instr_labels_2(Instr, Labels0, CodeAddrs1),
    instr_rvals_and_lvals(Instr, Rvals, Lvals),
    exprn_aux.rval_list_addrs(Rvals, CodeAddrs2, _),
    exprn_aux.lval_list_addrs(Lvals, CodeAddrs3, _),
    CodeAddrs = CodeAddrs1 ++ CodeAddrs2 ++ CodeAddrs3,
    find_label_code_addrs(CodeAddrs, Labels0, Labels).

    % Determine all the labels and code_addresses that are directly referenced
    % by an instruction (not counting ones referenced indirectly via rvals or
    % lvals).
    %
:- pred instr_labels_2(instr::in, list(label)::out, list(code_addr)::out)
    is det.

instr_labels_2(Uinstr, Labels, CodeAddrs) :-
    (
        ( Uinstr = comment(_)
        ; Uinstr = livevals(_)
        ; Uinstr = assign(_,_)
        ; Uinstr = keep_assign(_,_)
        ; Uinstr = mkframe(_, no)
        ; Uinstr = label(_)
        ; Uinstr = arbitrary_c_code(_, _, _)
        ; Uinstr = save_maxfr(_)
        ; Uinstr = restore_maxfr(_)
        ; Uinstr = incr_hp(_, _, _, _, _, _, _, _)
        ; Uinstr = mark_hp(_)
        ; Uinstr = restore_hp(_)
        ; Uinstr = free_heap(_)
        ; Uinstr = push_region_frame(_, _)
        ; Uinstr = region_fill_frame(_, _, _, _, _)
        ; Uinstr = region_set_fixed_slot(_, _, _)
        ; Uinstr = use_and_maybe_pop_region_frame(_, _)
        ; Uinstr = store_ticket(_)
        ; Uinstr = reset_ticket(_, _)
        ; Uinstr = discard_ticket
        ; Uinstr = prune_ticket
        ; Uinstr = mark_ticket_stack(_)
        ; Uinstr = prune_tickets_to(_)
        ; Uinstr = incr_sp(_, _, _)
        ; Uinstr = decr_sp(_)
        ; Uinstr = init_sync_term(_, _, _)
        ),
        Labels = [],
        CodeAddrs = []
    ;
        Uinstr = llcall(Target, Ret, _, _, _, _),
        Labels = [],
        CodeAddrs = [Target, Ret]
    ;
        ( Uinstr = mkframe(_, yes(Addr))
        ; Uinstr = goto(Addr)
        ; Uinstr = if_val(_, Addr)
        ),
        Labels = [],
        CodeAddrs = [Addr]
    ;
        Uinstr = decr_sp_and_return(_),
        % XXX decr_sp_and_return does refer to a code addr, but the code addr
        % it refers to is the original succip (now in a stack slot), which is
        % not necessarily the current succip. However, we introduce
        % decr_sp_and_return so late that this predicate should never be
        % invoked on such instructions.
        unexpected($module, $pred, "decr_sp_and_return")
    ;
        Uinstr = fork_new_child(_, Child),
        Labels = [Child],
        CodeAddrs = []
    ;
        Uinstr = join_and_continue(_, Label),
        Labels = [Label],
        CodeAddrs = []
    ;
        Uinstr = block(_, _, Instrs),
        instr_list_labels(Instrs, Labels, CodeAddrs)
    ;
        Uinstr = computed_goto(_, MaybeLabels),
        possible_targets_maybe_labels(MaybeLabels, [], RevLabels),
        list.reverse(RevLabels, Labels),
        CodeAddrs = []
    ;
        Uinstr = foreign_proc_code(_, _, _, MaybeFixLabel, MaybeLayoutLabel,
            MaybeOnlyLayoutLabel, MaybeSubLabel, MaybeDefLabel, _, _),
        foreign_proc_labels(MaybeFixLabel, MaybeLayoutLabel,
            MaybeOnlyLayoutLabel, MaybeSubLabel, MaybeDefLabel, Labels),
        CodeAddrs = []
    ).

    % Find out which code addresses are also labels.
    %
:- pred find_label_code_addrs(list(code_addr)::in,
    list(label)::in, list(label)::out) is det.

find_label_code_addrs([], Labels, Labels).
find_label_code_addrs([CodeAddr | Rest], Labels0, Labels) :-
    ( CodeAddr = code_label(Label) ->
        Labels1 = [Label | Labels0]
    ;
        Labels1 = Labels0
    ),
    find_label_code_addrs(Rest, Labels1, Labels).

possible_targets(Uinstr, Labels, CodeAddrs) :-
    (
        ( Uinstr = comment(_)
        ; Uinstr = livevals(_)
        ; Uinstr = assign(_,_)
        ; Uinstr = keep_assign(_,_)
        ; Uinstr = mkframe(_, _)
        ; Uinstr = label(_)
        ; Uinstr = arbitrary_c_code(_, _, _)
        ; Uinstr = save_maxfr(_)
        ; Uinstr = restore_maxfr(_)
        ; Uinstr = incr_hp(_, _, _, _, _, _, _, _)
        ; Uinstr = mark_hp(_)
        ; Uinstr = restore_hp(_)
        ; Uinstr = free_heap(_)
        ; Uinstr = push_region_frame(_, _)
        ; Uinstr = region_fill_frame(_, _, _, _, _)
        ; Uinstr = region_set_fixed_slot(_, _, _)
        ; Uinstr = use_and_maybe_pop_region_frame(_, _)
        ; Uinstr = store_ticket(_)
        ; Uinstr = reset_ticket(_, _)
        ; Uinstr = discard_ticket
        ; Uinstr = prune_ticket
        ; Uinstr = mark_ticket_stack(_)
        ; Uinstr = prune_tickets_to(_)
        ; Uinstr = incr_sp(_, _, _)
        ; Uinstr = decr_sp(_)
        ; Uinstr = init_sync_term(_, _, _)
        ; Uinstr = fork_new_child(_, _)
        ),
        Labels = [],
        CodeAddrs = []
    ;
        Uinstr = llcall(_, Return, _, _, _, _),
        ( Return = code_label(ReturnLabel) ->
            Labels = [ReturnLabel],
            CodeAddrs = []
        ;
            Labels = [],
            CodeAddrs = [Return]
        )
    ;
        ( Uinstr = goto(CodeAddr)
        ; Uinstr = if_val(_, CodeAddr)
        ),
        ( CodeAddr = code_label(Label) ->
            Labels = [Label],
            CodeAddrs = []
        ;
            Labels = [],
            CodeAddrs = [CodeAddr]
        )
    ;
        Uinstr = decr_sp_and_return(_),
        % XXX see the comment in instr_labels_2.
        unexpected($module, $pred, "decr_sp_and_return")
    ;
        Uinstr = join_and_continue(_, Label),
        Labels = [Label],
        CodeAddrs = []
    ;
        Uinstr = block(_, _, _),
        unexpected($module, $pred, "block")
    ;
        Uinstr = computed_goto(_, MaybeLabels),
        possible_targets_maybe_labels(MaybeLabels, [], RevLabels),
        list.reverse(RevLabels, Labels),
        CodeAddrs = []
    ;
        Uinstr = foreign_proc_code(_, _, _, MaybeFixLabel, MaybeLayoutLabel,
            MaybeOnlyLayoutLabel, MaybeSubLabel, MaybeDefLabel, _, _),
        foreign_proc_labels(MaybeFixLabel, MaybeLayoutLabel,
            MaybeOnlyLayoutLabel, MaybeSubLabel, MaybeDefLabel, Labels),
        CodeAddrs = []
    ).

:- pred possible_targets_maybe_labels(list(maybe(label))::in,
    list(label)::in, list(label)::out) is det.

possible_targets_maybe_labels([], !RevLabels).
possible_targets_maybe_labels([MaybeLabel | MaybeLabels], !RevLabels) :-
    (
        MaybeLabel = yes(Label),
        !:RevLabels = [Label | !.RevLabels]
    ;
        MaybeLabel = no
    ),
    possible_targets_maybe_labels(MaybeLabels, !RevLabels).

:- pred foreign_proc_labels(maybe(label)::in, maybe(label)::in,
    maybe(label)::in, maybe(label)::in, maybe(label)::in, list(label)::out)
    is det.

foreign_proc_labels(MaybeFixedLabel, MaybeLayoutLabel,
        MaybeOnlyLayoutLabel, MaybeSubLabel, MaybeDefLabel, !:Labels) :-
    !:Labels = [],
    (
        MaybeFixedLabel = yes(FixedLabel),
        !:Labels = [FixedLabel | !.Labels]
    ;
        MaybeFixedLabel = no
    ),
    (
        MaybeLayoutLabel = yes(LayoutLabel),
        !:Labels = [LayoutLabel | !.Labels]
    ;
        MaybeLayoutLabel = no
    ),
    (
        MaybeOnlyLayoutLabel = yes(OnlyLayoutLabel),
        !:Labels = [OnlyLayoutLabel | !.Labels]
    ;
        MaybeOnlyLayoutLabel = no
    ),
    (
        MaybeSubLabel = yes(SubLabel),
        !:Labels = [SubLabel | !.Labels]
    ;
        MaybeSubLabel = no
    ),
    (
        MaybeDefLabel = yes(DefLabel),
        !:Labels = [DefLabel | !.Labels]
    ;
        MaybeDefLabel = no
    ).

    % Determine all the rvals and lvals referenced by an instruction.
    %
:- pred instr_rvals_and_lvals(instr::in, list(rval)::out, list(lval)::out)
    is det.

instr_rvals_and_lvals(comment(_), [], []).
instr_rvals_and_lvals(livevals(_), [], []).
instr_rvals_and_lvals(block(_, _, Instrs), Labels, CodeAddrs) :-
    instr_list_rvals_and_lvals(Instrs, Labels, CodeAddrs).
instr_rvals_and_lvals(assign(Lval,Rval), [Rval], [Lval]).
instr_rvals_and_lvals(keep_assign(Lval,Rval), [Rval], [Lval]).
instr_rvals_and_lvals(llcall(_, _, _, _, _, _), [], []).
instr_rvals_and_lvals(mkframe(_, _), [], []).
instr_rvals_and_lvals(label(_), [], []).
instr_rvals_and_lvals(goto(_), [], []).
instr_rvals_and_lvals(computed_goto(Rval, _), [Rval], []).
instr_rvals_and_lvals(arbitrary_c_code(_, _, _), [], []).
instr_rvals_and_lvals(if_val(Rval, _), [Rval], []).
instr_rvals_and_lvals(save_maxfr(Lval), [], [Lval]).
instr_rvals_and_lvals(restore_maxfr(Lval), [], [Lval]).
instr_rvals_and_lvals(incr_hp(Lval, _, _, SizeRval, _, _, MaybeRegionRval,
        MaybeReuse), Rvals, Lvals) :-
    some [!Rvals, !Lvals] (
        !:Rvals = [SizeRval],
        !:Lvals = [Lval],
        (
            MaybeRegionRval = yes(RegionRval),
            !:Rvals = [RegionRval | !.Rvals]
        ;
            MaybeRegionRval = no
        ),
        (
            MaybeReuse = llds_reuse(ReuseRval, MaybeFlagLval),
            !:Rvals = [ReuseRval | !.Rvals],
            (
                MaybeFlagLval = yes(FlagLval),
                !:Lvals = [FlagLval | !.Lvals]
            ;
                MaybeFlagLval = no
            )
        ;
            MaybeReuse = no_llds_reuse
        ),
        Rvals = !.Rvals,
        Lvals = !.Lvals
    ).
instr_rvals_and_lvals(mark_hp(Lval), [], [Lval]).
instr_rvals_and_lvals(restore_hp(Rval), [Rval], []).
instr_rvals_and_lvals(free_heap(Rval), [Rval], []).
    % The region instructions implicitly specify some stackvars or framevars,
    % but they cannot reference lvals or rvals that involve code addresses or
    % labels, and that is the motivation of the only current invoker of
    % instr_rvals_and_lvals.
instr_rvals_and_lvals(push_region_frame(_, _), [], []).
instr_rvals_and_lvals(region_fill_frame(_, _, IdRval, NumLval, AddrLval),
    [IdRval], [NumLval, AddrLval]).
instr_rvals_and_lvals(region_set_fixed_slot(_, _, ValueRval),
    [ValueRval], []).
instr_rvals_and_lvals(use_and_maybe_pop_region_frame(_, _), [], []).
instr_rvals_and_lvals(store_ticket(Lval), [], [Lval]).
instr_rvals_and_lvals(reset_ticket(Rval, _Reason), [Rval], []).
instr_rvals_and_lvals(discard_ticket, [], []).
instr_rvals_and_lvals(prune_ticket, [], []).
instr_rvals_and_lvals(mark_ticket_stack(Lval), [], [Lval]).
instr_rvals_and_lvals(prune_tickets_to(Rval), [Rval], []).
instr_rvals_and_lvals(incr_sp(_, _, _), [], []).
instr_rvals_and_lvals(decr_sp(_), [], []).
instr_rvals_and_lvals(decr_sp_and_return(_), [], []).
instr_rvals_and_lvals(init_sync_term(Lval, _, _), [], [Lval]).
instr_rvals_and_lvals(fork_new_child(Lval, _), [], [Lval]).
instr_rvals_and_lvals(join_and_continue(Lval, _), [], [Lval]).
instr_rvals_and_lvals(foreign_proc_code(_, Cs, _, _, _, _, _, _, _, _),
        Rvals, Lvals) :-
    foreign_proc_components_get_rvals_and_lvals(Cs, Rvals, Lvals).

    % Extract the rvals and lvals from the foreign_proc_components.
    %
:- pred foreign_proc_components_get_rvals_and_lvals(
    list(foreign_proc_component)::in,
    list(rval)::out, list(lval)::out) is det.

foreign_proc_components_get_rvals_and_lvals([], [], []).
foreign_proc_components_get_rvals_and_lvals([Comp | Comps], !:Rvals, !:Lvals) :-
    foreign_proc_components_get_rvals_and_lvals(Comps, !:Rvals, !:Lvals),
    foreign_proc_component_get_rvals_and_lvals(Comp, !Rvals, !Lvals).

    % Extract the rvals and lvals from the foreign_proc_component
    % and add them to the list.
    %
:- pred foreign_proc_component_get_rvals_and_lvals(foreign_proc_component::in,
    list(rval)::in, list(rval)::out, list(lval)::in, list(lval)::out) is det.

foreign_proc_component_get_rvals_and_lvals(foreign_proc_inputs(Inputs),
        !Rvals, !Lvals) :-
    NewRvals = foreign_proc_inputs_get_rvals(Inputs),
    list.append(NewRvals, !Rvals).
foreign_proc_component_get_rvals_and_lvals(foreign_proc_outputs(Outputs),
        !Rvals, !Lvals) :-
    NewLvals = foreign_proc_outputs_get_lvals(Outputs),
    list.append(NewLvals, !Lvals).
foreign_proc_component_get_rvals_and_lvals(foreign_proc_user_code(_, _, _),
        !Rvals, !Lvals).
foreign_proc_component_get_rvals_and_lvals(foreign_proc_raw_code(_, _, _, _),
        !Rvals, !Lvals).
foreign_proc_component_get_rvals_and_lvals(foreign_proc_fail_to(_),
        !Rvals, !Lvals).
foreign_proc_component_get_rvals_and_lvals(foreign_proc_alloc_id(_),
        !Rvals, !Lvals).
foreign_proc_component_get_rvals_and_lvals(foreign_proc_noop,
        !Rvals, !Lvals).

    % Extract the rvals from the foreign_proc_input.
    %
:- func foreign_proc_inputs_get_rvals(list(foreign_proc_input)) = list(rval).

foreign_proc_inputs_get_rvals([]) = [].
foreign_proc_inputs_get_rvals([Input | Inputs]) = [Rval | Rvals] :-
    Input = foreign_proc_input(_Name, _VarType, _IsDummy, _OrigType, Rval,
        _, _),
    Rvals = foreign_proc_inputs_get_rvals(Inputs).

    % Extract the lvals from the foreign_proc_output.
    %
:- func foreign_proc_outputs_get_lvals(list(foreign_proc_output)) = list(lval).

foreign_proc_outputs_get_lvals([]) = [].
foreign_proc_outputs_get_lvals([Output | Outputs]) = [Lval | Lvals] :-
    Output = foreign_proc_output(Lval, _VarType, _IsDummy, _OrigType,
        _Name, _, _),
    Lvals = foreign_proc_outputs_get_lvals(Outputs).

    % Determine all the rvals and lvals referenced by a list of instructions.
    %
:- pred instr_list_rvals_and_lvals(list(instruction)::in,
    list(rval)::out, list(lval)::out) is det.

instr_list_rvals_and_lvals([], [], []).
instr_list_rvals_and_lvals([llds_instr(Uinstr, _) | Instrs], Rvals, Lvals) :-
    instr_rvals_and_lvals(Uinstr, HeadRvals, HeadLvals),
    instr_list_rvals_and_lvals(Instrs, TailRvals, TailLvals),
    Rvals = HeadRvals ++ TailRvals,
    Lvals = HeadLvals ++ TailLvals.

instr_list_labels([], [], []).
instr_list_labels([llds_instr(Uinstr, _) | Instrs], Labels, CodeAddrs) :-
    instr_labels(Uinstr, HeadLabels, HeadCodeAddrs),
    instr_list_labels(Instrs, TailLabels, TailCodeAddrs),
    Labels = HeadLabels ++ TailLabels,
    CodeAddrs = HeadCodeAddrs ++ TailCodeAddrs.

livevals_addr(code_label(Label)) = Result :-
    (
        Label = internal_label(_, _),
        Result = no
    ;
        Label = entry_label(_, _),
        Result = yes
    ).
livevals_addr(code_imported_proc(_)) = yes.
livevals_addr(code_succip) = yes.
livevals_addr(do_succeed(_)) = yes.
livevals_addr(do_redo) = no.
livevals_addr(do_fail) = no.
livevals_addr(do_trace_redo_fail_shallow) = no.
livevals_addr(do_trace_redo_fail_deep) = no.
livevals_addr(do_call_closure(_)) = yes.
livevals_addr(do_call_class_method(_)) = yes.
livevals_addr(do_not_reached) = no.

count_temps_instr_list([], !R, !F).
count_temps_instr_list([llds_instr(Uinstr, _Comment) | Instrs], !R, !F) :-
    count_temps_instr(Uinstr, !R, !F),
    count_temps_instr_list(Instrs, !R, !F).

count_temps_instr(comment(_), !R, !F).
count_temps_instr(livevals(_), !R, !F).
count_temps_instr(block(_, _, _), !R, !F).
count_temps_instr(assign(Lval, Rval), !R, !F) :-
    count_temps_lval(Lval, !R, !F),
    count_temps_rval(Rval, !R, !F).
count_temps_instr(keep_assign(Lval, Rval), !R, !F) :-
    count_temps_lval(Lval, !R, !F),
    count_temps_rval(Rval, !R, !F).
count_temps_instr(llcall(_, _, _, _, _, _), !R, !F).
count_temps_instr(mkframe(_, _), !R, !F).
count_temps_instr(label(_), !R, !F).
count_temps_instr(goto(_), !R, !F).
count_temps_instr(computed_goto(Rval, _), !R, !F) :-
    count_temps_rval(Rval, !R, !F).
count_temps_instr(if_val(Rval, _), !R, !F) :-
    count_temps_rval(Rval, !R, !F).
count_temps_instr(arbitrary_c_code(_, _, _), !R, !F).
count_temps_instr(save_maxfr(Lval), !R, !F) :-
    count_temps_lval(Lval, !R, !F).
count_temps_instr(restore_maxfr(Lval), !R, !F) :-
    count_temps_lval(Lval, !R, !F).
count_temps_instr(incr_hp(Lval, _, _, Rval, _, _, MaybeRegionRval,
        MaybeReuse), !R, !F) :-
    count_temps_lval(Lval, !R, !F),
    count_temps_rval(Rval, !R, !F),
    (
        MaybeRegionRval = yes(RegionRval),
        count_temps_rval(RegionRval, !R, !F)
    ;
        MaybeRegionRval = no
    ),
    (
        MaybeReuse = llds_reuse(ReuseRval, MaybeFlagLval),
        count_temps_rval(ReuseRval, !R, !F),
        (
            MaybeFlagLval = yes(FlagLval),
            count_temps_lval(FlagLval, !R, !F)
        ;
            MaybeFlagLval = no
        )
    ;
        MaybeReuse = no_llds_reuse
    ).
count_temps_instr(mark_hp(Lval), !R, !F) :-
    count_temps_lval(Lval, !R, !F).
count_temps_instr(restore_hp(Rval), !R, !F) :-
    count_temps_rval(Rval, !R, !F).
count_temps_instr(free_heap(Rval), !R, !F) :-
    count_temps_rval(Rval, !R, !F).
count_temps_instr(push_region_frame(_StackId, _EmbeddedStackFrame), !R, !F).
count_temps_instr(region_fill_frame(_FillOp, _EmbeddedStackFrame, IdRval,
        NumLval, AddrLval), !R, !F) :-
    count_temps_rval(IdRval, !R, !F),
    count_temps_lval(NumLval, !R, !F),
    count_temps_lval(AddrLval, !R, !F).
count_temps_instr(region_set_fixed_slot(_SetlOp, _EmbeddedStackFrame,
        ValueRval), !R, !F) :-
    count_temps_rval(ValueRval, !R, !F).
count_temps_instr(use_and_maybe_pop_region_frame(_UseOp, _EmbeddedStackFrame),
        !R, !F).
count_temps_instr(store_ticket(Lval), !R, !F) :-
    count_temps_lval(Lval, !R, !F).
count_temps_instr(reset_ticket(Rval, _Reason), !R, !F) :-
    count_temps_rval(Rval, !R, !F).
count_temps_instr(discard_ticket, !R, !F).
count_temps_instr(prune_ticket, !R, !F).
count_temps_instr(mark_ticket_stack(Lval), !R, !F) :-
    count_temps_lval(Lval, !R, !F).
count_temps_instr(prune_tickets_to(Rval), !R, !F) :-
    count_temps_rval(Rval, !R, !F).
count_temps_instr(incr_sp(_, _, _), !R, !F).
count_temps_instr(decr_sp(_), !R, !F).
count_temps_instr(decr_sp_and_return(_), !R, !F).
count_temps_instr(init_sync_term(Lval, _, _), !R, !F) :-
    count_temps_lval(Lval, !R, !F).
count_temps_instr(fork_new_child(Lval, _), !R, !F) :-
    count_temps_lval(Lval, !R, !F).
count_temps_instr(join_and_continue(Lval, _), !R, !F) :-
    count_temps_lval(Lval, !R, !F).
count_temps_instr(foreign_proc_code(_, Comps, _, _, _, _, _, _, _, _),
        !R, !F) :-
    count_temps_components(Comps, !R, !F).

:- pred count_temps_components(list(foreign_proc_component)::in,
    int::in, int::out, int::in, int::out) is det.

count_temps_components([], !R, !F).
count_temps_components([Comp | Comps], !R, !F) :-
    count_temps_component(Comp, !R, !F),
    count_temps_components(Comps, !R, !F).

:- pred count_temps_component(foreign_proc_component::in,
    int::in, int::out, int::in, int::out) is det.

count_temps_component(Comp, !R, !F) :-
    (
        Comp = foreign_proc_inputs(Inputs),
        count_temps_inputs(Inputs, !R, !F)
    ;
        Comp = foreign_proc_outputs(Outputs),
        count_temps_outputs(Outputs, !R, !F)
    ;
        Comp = foreign_proc_user_code(_, _, _)
    ;
        Comp = foreign_proc_raw_code(_, _, _, _)
    ;
        Comp = foreign_proc_fail_to(_)
    ;
        Comp = foreign_proc_alloc_id(_)
    ;
        Comp = foreign_proc_noop
    ).

:- pred count_temps_inputs(list(foreign_proc_input)::in,
    int::in, int::out, int::in, int::out) is det.

count_temps_inputs([], !R, !F).
count_temps_inputs([Input | Inputs], !R, !F) :-
    Input = foreign_proc_input(_VarName, _VarType, _IsDummy, _OrigType,
        ArgRval, _MaybeForeignType, _BoxPolicy),
    count_temps_rval(ArgRval, !R, !F),
    count_temps_inputs(Inputs, !R, !F).

:- pred count_temps_outputs(list(foreign_proc_output)::in,
    int::in, int::out, int::in, int::out) is det.

count_temps_outputs([], !R, !F).
count_temps_outputs([Output | Outputs], !R, !F) :-
    Output = foreign_proc_output(DestLval, _VarType, _IsDummy, _OrigType,
        _VarName, _MaybeForeignType, _BoxPolicy),
    count_temps_lval(DestLval, !R, !F),
    count_temps_outputs(Outputs, !R, !F).

:- pred count_temps_lval(lval::in, int::in, int::out, int::in, int::out)
    is det.

count_temps_lval(Lval, !R, !F) :-
    (
        ( Lval = reg(_, _)
        ; Lval = succip
        ; Lval = maxfr
        ; Lval = curfr
        ; Lval = hp
        ; Lval = sp
        ; Lval = parent_sp
        ; Lval = stackvar(_)
        ; Lval = framevar(_)
        ; Lval = parent_stackvar(_)
        ; Lval = global_var_ref(_)
        )
    ;
        Lval = temp(Type, N),
        (
            Type = reg_r,
            int.max(N, !R)
        ;
            Type = reg_f,
            int.max(N, !F)
        )
    ;
        Lval = field(_, BaseAddrRval, FieldNumRval),
        count_temps_rval(BaseAddrRval, !R, !F),
        count_temps_rval(FieldNumRval, !R, !F)
    ;
        ( Lval = succip_slot(Rval)
        ; Lval = succfr_slot(Rval)
        ; Lval = redoip_slot(Rval)
        ; Lval = redofr_slot(Rval)
        ; Lval = prevfr_slot(Rval)
        ; Lval = mem_ref(Rval)
        ),
        count_temps_rval(Rval, !R, !F)
    ;
        Lval = lvar(_),
        unexpected($module, $pred, "lvar")
    ).

:- pred count_temps_rval(rval::in, int::in, int::out, int::in, int::out)
    is det.

count_temps_rval(Rval, !R, !F) :-
    (
        Rval = lval(Lval),
        count_temps_lval(Lval, !R, !F)
    ;
        Rval = var(_),
        unexpected($module, $pred, "var")
    ;
        Rval = mkword(_Tag, SubRval),
        count_temps_rval(SubRval, !R, !F)
    ;
        Rval = const(_Const)
    ;
        Rval = unop(_Unop, SubRvalA),
        count_temps_rval(SubRvalA, !R, !F)
    ;
        Rval = binop(_Binop, SubRvalA, SubRvalB),
        count_temps_rval(SubRvalA, !R, !F),
        count_temps_rval(SubRvalB, !R, !F)
    ;
        Rval = mem_addr(MemRef),
        count_temps_mem_ref(MemRef, !R, !F)
    ).

:- pred count_temps_mem_ref(mem_ref::in, int::in, int::out, int::in, int::out)
    is det.

count_temps_mem_ref(MemRef, !R, !F) :-
    (
        ( MemRef = stackvar_ref(Rval)
        ; MemRef = framevar_ref(Rval)
        ),
        count_temps_rval(Rval, !R, !F)
    ;
        MemRef = heap_ref(CellRval, _Tag, FieldNumRval),
        count_temps_rval(CellRval, !R, !F),
        count_temps_rval(FieldNumRval, !R, !F)
    ).

format_label(internal_label(_, ProcLabel)) = format_proc_label(ProcLabel).
format_label(entry_label(_, ProcLabel)) = format_proc_label(ProcLabel).

format_proc_label(ordinary_proc_label(_Module, _PredOrFunc, _, Name,
        Arity, Mode)) =
    Name ++ "/" ++ int_to_string(Arity) ++ " mode " ++ int_to_string(Mode).
format_proc_label(special_proc_label(_Module, SpecialPredId, TypeModule,
        TypeName, TypeArity, Mode)) =
        PredName ++ "_" ++ TypeName ++ "/" ++ int_to_string(TypeArity)
            ++ " mode " ++ int_to_string(Mode) :-
    TypeCtor = type_ctor(qualified(TypeModule, TypeName), TypeArity),
    PredName = special_pred_name(SpecialPredId, TypeCtor).

has_both_incr_decr_sp(Instrs) :-
    has_both_incr_decr_sp_2(Instrs, no, yes, no, yes).

:- pred has_both_incr_decr_sp_2(list(instruction)::in,
    bool::in, bool::out, bool::in, bool::out) is det.

has_both_incr_decr_sp_2([], !HasIncr, !HasDecr).
has_both_incr_decr_sp_2([llds_instr(Uinstr, _) | Instrs],
        !HasIncr, !HasDecr) :-
    ( Uinstr = incr_sp(_, _, _) ->
        !:HasIncr = yes
    ;
        true
    ),
    ( Uinstr = decr_sp(_) ->
        !:HasDecr = yes
    ;
        true
    ),
    has_both_incr_decr_sp_2(Instrs, !HasIncr, !HasDecr).

touches_nondet_ctrl([]) = no.
touches_nondet_ctrl([llds_instr(Uinstr, _) | Instrs]) = !:Touch :-
    !:Touch = touches_nondet_ctrl_instr(Uinstr),
    (
        !.Touch = yes
    ;
        !.Touch = no,
        !:Touch = touches_nondet_ctrl(Instrs)
    ).

:- func touches_nondet_ctrl_instr(instr) = bool.

touches_nondet_ctrl_instr(Uinstr) = Touch :-
    (
        ( Uinstr = comment(_)
        ; Uinstr = livevals(_)
        ; Uinstr = label(_)
        ; Uinstr = prune_ticket
        ; Uinstr = discard_ticket
        ; Uinstr = incr_sp(_, _, _)
        ; Uinstr = decr_sp(_)
        ; Uinstr = decr_sp_and_return(_)
        ),
        Touch = no
    ;
        ( Uinstr = mkframe(_, _)
        ; Uinstr = goto(_)
        ; Uinstr = computed_goto(_, _)
        ; Uinstr = llcall(_, _, _, _, _, _) % This is a safe approximation.
        ; Uinstr = if_val(_, _)
        ; Uinstr = arbitrary_c_code(_, _, _)
        ; Uinstr = save_maxfr(_)
        ; Uinstr = restore_maxfr(_)
        ; Uinstr = init_sync_term(_, _, _)  % This is a safe approximation.
        ; Uinstr = fork_new_child(_, _)     % This is a safe approximation.
        ; Uinstr = join_and_continue(_, _)  % This is a safe approximation.
        ),
        Touch = yes
    ;
        Uinstr = block(_, _, _),
        % Blocks aren't introduced until after the last user of this predicate.
        unexpected($module, $pred, "block")
    ;
        ( Uinstr = assign(Lval, Rval)
        ; Uinstr = keep_assign(Lval, Rval)
        ),
        TouchLval = touches_nondet_ctrl_lval(Lval),
        TouchRval = touches_nondet_ctrl_rval(Rval),
        bool.or(TouchLval, TouchRval, Touch)
    ;
        Uinstr = incr_hp(Lval, _, _, Rval, _, _, MaybeRegionRval,
            MaybeReuse),
        some [!Touch] (
            !:Touch = bool.or(
                touches_nondet_ctrl_lval(Lval),
                touches_nondet_ctrl_rval(Rval)),
            (
                MaybeRegionRval = yes(RegionRval),
                bool.or(touches_nondet_ctrl_rval(RegionRval), !Touch)
            ;
                MaybeRegionRval = no
            ),
            (
                MaybeReuse = llds_reuse(ReuseRval, MaybeFlagLval),
                bool.or(touches_nondet_ctrl_rval(ReuseRval), !Touch),
                (
                    MaybeFlagLval = yes(FlagLval),
                    bool.or(touches_nondet_ctrl_lval(FlagLval), !Touch)
                ;
                    MaybeFlagLval = no
                )
            ;
                MaybeReuse = no_llds_reuse
            ),
            Touch = !.Touch
        )
    ;
        Uinstr = mark_hp(Lval),
        Touch = touches_nondet_ctrl_lval(Lval)
    ;
        Uinstr = restore_hp(Rval),
        Touch = touches_nondet_ctrl_rval(Rval)
    ;
        Uinstr = free_heap(Rval),
        Touch = touches_nondet_ctrl_rval(Rval)
    ;
        Uinstr = push_region_frame(_StackId, _EmbeddedStackFrame),
        Touch = no
    ;
        Uinstr = region_fill_frame(_FillOp, _EmbeddedStackFrame, IdRval,
            NumLval, AddrLval),
        Touch = bool.or(
            touches_nondet_ctrl_rval(IdRval),
            bool.or(
                touches_nondet_ctrl_lval(NumLval),
                touches_nondet_ctrl_lval(AddrLval)))
    ;
        Uinstr = region_set_fixed_slot(_SetOp, _EmbeddedStackFrame, ValueRval),
        Touch = touches_nondet_ctrl_rval(ValueRval)
    ;
        Uinstr = use_and_maybe_pop_region_frame(_UseOp, _EmbeddedStackFrame),
        Touch = no
    ;
        Uinstr = store_ticket(Lval),
        Touch = touches_nondet_ctrl_lval(Lval)
    ;
        Uinstr = reset_ticket(Rval, _),
        Touch = touches_nondet_ctrl_rval(Rval)
    ;
        Uinstr = mark_ticket_stack(Lval),
        Touch = touches_nondet_ctrl_lval(Lval)
    ;
        Uinstr = prune_tickets_to(Rval),
        Touch = touches_nondet_ctrl_rval(Rval)
    ;
        Uinstr = foreign_proc_code(_, Components, _, _, _, _, _, _, _, _),
        Touch = touches_nondet_ctrl_components(Components)
    ).

:- func touches_nondet_ctrl_lval(lval) = bool.

touches_nondet_ctrl_lval(reg(_, _)) = no.
touches_nondet_ctrl_lval(stackvar(_)) = no.
touches_nondet_ctrl_lval(parent_stackvar(_)) = no.
touches_nondet_ctrl_lval(framevar(_)) = no.
touches_nondet_ctrl_lval(succip) = no.
touches_nondet_ctrl_lval(maxfr) = yes.
touches_nondet_ctrl_lval(curfr) = yes.
touches_nondet_ctrl_lval(succfr_slot(_)) = yes.
touches_nondet_ctrl_lval(prevfr_slot(_)) = yes.
touches_nondet_ctrl_lval(redofr_slot(_)) = yes.
touches_nondet_ctrl_lval(redoip_slot(_)) = yes.
touches_nondet_ctrl_lval(succip_slot(_)) = yes.
touches_nondet_ctrl_lval(hp) = no.
touches_nondet_ctrl_lval(sp) = no.
touches_nondet_ctrl_lval(parent_sp) = no.
touches_nondet_ctrl_lval(field(_, Rval1, Rval2)) = Touch :-
    Touch1 = touches_nondet_ctrl_rval(Rval1),
    Touch2 = touches_nondet_ctrl_rval(Rval2),
    bool.or(Touch1, Touch2, Touch).
touches_nondet_ctrl_lval(lvar(_)) = no.
touches_nondet_ctrl_lval(temp(_, _)) = no.
touches_nondet_ctrl_lval(mem_ref(Rval)) =
    touches_nondet_ctrl_rval(Rval).
touches_nondet_ctrl_lval(global_var_ref(_)) = no.

:- func touches_nondet_ctrl_rval(rval) = bool.

touches_nondet_ctrl_rval(lval(Lval)) =
    touches_nondet_ctrl_lval(Lval).
touches_nondet_ctrl_rval(var(_)) = no.
touches_nondet_ctrl_rval(mkword(_, Rval)) =
    touches_nondet_ctrl_rval(Rval).
touches_nondet_ctrl_rval(const(_)) = no.
touches_nondet_ctrl_rval(unop(_, Rval)) =
    touches_nondet_ctrl_rval(Rval).
touches_nondet_ctrl_rval(binop(_, Rval1, Rval2)) = Touch :-
    Touch1 = touches_nondet_ctrl_rval(Rval1),
    Touch2 = touches_nondet_ctrl_rval(Rval2),
    bool.or(Touch1, Touch2, Touch).
touches_nondet_ctrl_rval(mem_addr(MemRef)) =
    touches_nondet_ctrl_mem_ref(MemRef).

:- func touches_nondet_ctrl_mem_ref(mem_ref) = bool.

touches_nondet_ctrl_mem_ref(stackvar_ref(_)) = no.
touches_nondet_ctrl_mem_ref(framevar_ref(_)) = no.
touches_nondet_ctrl_mem_ref(heap_ref(Rval, _, _)) =
    touches_nondet_ctrl_rval(Rval).

:- func touches_nondet_ctrl_components(list(foreign_proc_component)) = bool.

touches_nondet_ctrl_components([]) = no.
touches_nondet_ctrl_components([Comp | Comps]) = Touch :-
    Touch1 = touches_nondet_ctrl_component(Comp),
    Touch2 = touches_nondet_ctrl_components(Comps),
    bool.or(Touch1, Touch2, Touch).

    % The inputs and outputs components get emitted as simple straight-line
    % code that do not refer to control slots. The compiler does not generate
    % raw_code that refers to control slots. User code shouldn't either, but
    % until we have prohibited the use of ordinary pragma C codes for model_non
    % procedures, some user code will need to ignore this restriction.
    %
:- func touches_nondet_ctrl_component(foreign_proc_component) = bool.

touches_nondet_ctrl_component(foreign_proc_inputs(_)) = no.
touches_nondet_ctrl_component(foreign_proc_outputs(_)) = no.
touches_nondet_ctrl_component(foreign_proc_raw_code(_, _, _, _)) = no.
touches_nondet_ctrl_component(foreign_proc_user_code(_, _, _)) = yes.
touches_nondet_ctrl_component(foreign_proc_fail_to(_)) = no.
touches_nondet_ctrl_component(foreign_proc_alloc_id(_)) = no.
touches_nondet_ctrl_component(foreign_proc_noop) = no.

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

lval_access_rvals(reg(_, _), []).
lval_access_rvals(stackvar(_), []).
lval_access_rvals(parent_stackvar(_), []).
lval_access_rvals(framevar(_), []).
lval_access_rvals(succip, []).
lval_access_rvals(maxfr, []).
lval_access_rvals(curfr, []).
lval_access_rvals(redoip_slot(Rval), [Rval]).
lval_access_rvals(succip_slot(Rval), [Rval]).
lval_access_rvals(redofr_slot(Rval), [Rval]).
lval_access_rvals(prevfr_slot(Rval), [Rval]).
lval_access_rvals(succfr_slot(Rval), [Rval]).
lval_access_rvals(hp, []).
lval_access_rvals(sp, []).
lval_access_rvals(parent_sp, []).
lval_access_rvals(field(_, Rval1, Rval2), [Rval1, Rval2]).
lval_access_rvals(temp(_, _), []).
lval_access_rvals(lvar(_), _) :-
    unexpected($module, $pred, "lvar").
lval_access_rvals(mem_ref(Rval), [Rval]).
lval_access_rvals(global_var_ref(_), []).

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

rvals_free_of_lval([], _).
rvals_free_of_lval([Rval | Rvals], Forbidden) :-
    rval_free_of_lval(Rval, Forbidden),
    rvals_free_of_lval(Rvals, Forbidden).

rval_free_of_lval(lval(Lval), Forbidden) :-
    Lval \= Forbidden,
    lval_access_rvals(Lval, Rvals),
    rvals_free_of_lval(Rvals, Forbidden).
rval_free_of_lval(var(_), _) :-
    unexpected($module, $pred, "var").
rval_free_of_lval(mkword(_, Rval), Forbidden) :-
    rval_free_of_lval(Rval, Forbidden).
rval_free_of_lval(const(_), _).
rval_free_of_lval(unop(_, Rval), Forbidden) :-
    rval_free_of_lval(Rval, Forbidden).
rval_free_of_lval(binop(_, Rval1, Rval2), Forbidden) :-
    rval_free_of_lval(Rval1, Forbidden),
    rval_free_of_lval(Rval2, Forbidden).

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

count_incr_hp(Instrs, N) :-
    count_incr_hp_2(Instrs, 0, N).

:- pred count_incr_hp_2(list(instruction)::in, int::in, int::out) is det.

count_incr_hp_2([], !N).
count_incr_hp_2([llds_instr(Uinstr0, _) | Instrs], !N) :-
    ( Uinstr0 = incr_hp(_, _, _, _, _, _, _, _) ->
        !:N = !.N + 1
    ;
        true
    ),
    count_incr_hp_2(Instrs, !N).

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

propagate_livevals(Instrs0, Instrs) :-
    list.reverse(Instrs0, RevInstrs0),
    set.init(Livevals),
    propagate_livevals_2(RevInstrs0, Livevals, RevInstrs),
    list.reverse(RevInstrs, Instrs).

:- pred propagate_livevals_2(list(instruction)::in, set(lval)::in,
    list(instruction)::out) is det.

propagate_livevals_2([], _, []).
propagate_livevals_2([Instr0 | Instrs0], Livevals0,
        [Instr | Instrs]) :-
    Instr0 = llds_instr(Uinstr0, Comment),
    ( Uinstr0 = livevals(ThisLivevals) ->
        set.union(Livevals0, ThisLivevals, Livevals),
        Instr = llds_instr(livevals(Livevals), Comment)
    ;
        Instr = Instr0,
        ( Uinstr0 = assign(Lval, _) ->
            set.delete(Lval, Livevals0, Livevals)
        ; can_instr_fall_through(Uinstr0) = no ->
            set.init(Livevals)
        ;
            Livevals = Livevals0
        )
    ),
    propagate_livevals_2(Instrs0, Livevals, Instrs).

%-----------------------------------------------------------------------------%
%
% The code in this section is concerned with replacing all references
% to one given label with a reference to another given label.

replace_labels_instruction_list([], [], _, _, _).
replace_labels_instruction_list([Instr0 | Instrs0], [Instr | Instrs],
        ReplMap, ReplData, ReplLabel) :-
    (
        Instr0 = llds_instr(label(InstrLabel), Comment),
        ReplLabel = yes
    ->
        replace_labels_label(InstrLabel, ReplInstrLabel, ReplMap),
        Instr = llds_instr(label(ReplInstrLabel), Comment)
    ;
        replace_labels_instruction(Instr0, Instr, ReplMap, ReplData)
    ),
    replace_labels_instruction_list(Instrs0, Instrs,
        ReplMap, ReplData, ReplLabel).

replace_labels_instruction(Instr0, Instr, ReplMap, ReplData) :-
    Instr0 = llds_instr(Uinstr0, Comment),
    replace_labels_instr(Uinstr0, Uinstr, ReplMap, ReplData),
    Instr = llds_instr(Uinstr, Comment).

replace_labels_instr(Uinstr0, Uinstr, ReplMap, ReplData) :-
    (
        ( Uinstr0 = comment(_)
        ; Uinstr0 = livevals(_)
        ; Uinstr0 = discard_ticket
        ; Uinstr0 = prune_ticket
        ; Uinstr0 = incr_sp(_, _, _)
        ; Uinstr0 = decr_sp(_)
        ; Uinstr0 = decr_sp_and_return(_)
        ),
        Uinstr = Uinstr0
    ;
        Uinstr0 = block(R, F, Instrs0),
        % There should be no labels in Instrs0.
        replace_labels_instruction_list(Instrs0, Instrs,
            ReplMap, ReplData, no),
        Uinstr = block(R, F, Instrs)
    ;
        Uinstr0 = assign(Lval0, Rval0),
        (
            ReplData = yes,
            replace_labels_lval(Lval0, Lval, ReplMap),
            replace_labels_rval(Rval0, Rval, ReplMap)
        ;
            ReplData = no,
            Lval = Lval0,
            Rval = Rval0
        ),
        Uinstr = assign(Lval, Rval)
    ;
        Uinstr0 = keep_assign(Lval0, Rval0),
        (
            ReplData = yes,
            replace_labels_lval(Lval0, Lval, ReplMap),
            replace_labels_rval(Rval0, Rval, ReplMap)
        ;
            ReplData = no,
            Lval = Lval0,
            Rval = Rval0
        ),
        Uinstr = keep_assign(Lval, Rval)
    ;
        Uinstr0 = llcall(Target, Return0, LiveInfo, CXT, GP, CM),
        replace_labels_code_addr(Return0, Return, ReplMap),
        Uinstr = llcall(Target, Return, LiveInfo, CXT, GP, CM)
    ;
        Uinstr0 = mkframe(NondetFrameInfo, MaybeRedoip0),
        (
            ReplData = yes,
            (
                MaybeRedoip0 = yes(Redoip0),
                replace_labels_code_addr(Redoip0, Redoip, ReplMap),
                MaybeRedoip = yes(Redoip)
            ;
                MaybeRedoip0 = no,
                MaybeRedoip = no
            )
        ;
            ReplData = no,
            MaybeRedoip = MaybeRedoip0
        ),
        Uinstr = mkframe(NondetFrameInfo, MaybeRedoip)
    ;
        Uinstr0 = label(Label),
        ( map.search(ReplMap, Label, _) ->
            % The reason why we are replacing references to this label is that
            % it is being eliminated, and in fact should have been already
            % eliminated by the time replace_labels_instr is called.
            unexpected($module, $pred, "eliminated label")
        ;
            true
        ),
        Uinstr = label(Label)
    ;
        Uinstr0 = goto(Target0),
        replace_labels_code_addr(Target0, Target, ReplMap),
        Uinstr = goto(Target)
    ;
        Uinstr0 = computed_goto(Rval0, MaybeLabels0),
        (
            ReplData = yes,
            replace_labels_rval(Rval0, Rval, ReplMap)
        ;
            ReplData = no,
            Rval = Rval0
        ),
        replace_labels_maybe_label_list(MaybeLabels0, MaybeLabels, ReplMap),
        Uinstr = computed_goto(Rval, MaybeLabels)
    ;
        Uinstr0 = arbitrary_c_code(AffectsLiveness, Lvals0, Code),
        (
            ReplData = yes,
            replace_labels_c_code_live_lvals(Lvals0, Lvals, ReplMap)
        ;
            ReplData = no,
            Lvals = Lvals0
        ),
        Uinstr = arbitrary_c_code(AffectsLiveness, Lvals, Code)
    ;
        Uinstr0 = if_val(Rval0, Target0),
        (
            ReplData = yes,
            replace_labels_rval(Rval0, Rval, ReplMap)
        ;
            ReplData = no,
            Rval = Rval0
        ),
        replace_labels_code_addr(Target0, Target, ReplMap),
        Uinstr = if_val(Rval, Target)
    ;
        Uinstr0 = save_maxfr(Lval0),
        (
            ReplData = yes,
            replace_labels_lval(Lval0, Lval, ReplMap)
        ;
            ReplData = no,
            Lval = Lval0
        ),
        Uinstr = save_maxfr(Lval)
    ;
        Uinstr0 = restore_maxfr(Lval0),
        (
            ReplData = yes,
            replace_labels_lval(Lval0, Lval, ReplMap)
        ;
            ReplData = no,
            Lval = Lval0
        ),
        Uinstr = restore_maxfr(Lval)
    ;
        Uinstr0 = incr_hp(Lval0, MaybeTag, MO, Rval0, Msg, Atomic,
            MaybeRegionRval0, MaybeReuse0),
        (
            ReplData = yes,
            replace_labels_lval(Lval0, Lval, ReplMap),
            replace_labels_rval(Rval0, Rval, ReplMap),
            (
                MaybeRegionRval0 = yes(RegionRval0),
                replace_labels_rval(RegionRval0, RegionRval, ReplMap),
                MaybeRegionRval = yes(RegionRval)
            ;
                MaybeRegionRval0 = no,
                MaybeRegionRval = MaybeRegionRval0
            ),
            (
                MaybeReuse0 = llds_reuse(ReuseRval0, MaybeFlagLval0),
                replace_labels_rval(ReuseRval0, ReuseRval, ReplMap),
                (
                    MaybeFlagLval0 = yes(FlagLval0),
                    replace_labels_lval(FlagLval0, FlagLval, ReplMap),
                    MaybeFlagLval = yes(FlagLval)
                ;
                    MaybeFlagLval0 = no,
                    MaybeFlagLval = no
                ),
                MaybeReuse = llds_reuse(ReuseRval, MaybeFlagLval)
            ;
                MaybeReuse0 = no_llds_reuse,
                MaybeReuse = no_llds_reuse
            )
        ;
            ReplData = no,
            Lval = Lval0,
            Rval = Rval0,
            MaybeRegionRval = MaybeRegionRval0,
            MaybeReuse = MaybeReuse0
        ),
        Uinstr = incr_hp(Lval, MaybeTag, MO, Rval, Msg, Atomic,
            MaybeRegionRval, MaybeReuse)
    ;
        Uinstr0 = mark_hp(Lval0),
        (
            ReplData = yes,
            replace_labels_lval(Lval0, Lval, ReplMap)
        ;
            ReplData = no,
            Lval = Lval0
        ),
        Uinstr = mark_hp(Lval)
    ;
        Uinstr0 = restore_hp(Rval0),
        (
            ReplData = yes,
            replace_labels_rval(Rval0, Rval, ReplMap)
        ;
            ReplData = no,
            Rval = Rval0
        ),
        Uinstr = restore_hp(Rval)
    ;
        Uinstr0 = free_heap(Rval0),
        (
            ReplData = yes,
            replace_labels_rval(Rval0, Rval, ReplMap)
        ;
            ReplData = no,
            Rval = Rval0
        ),
        Uinstr = free_heap(Rval)
    ;
        Uinstr0 = push_region_frame(StackId, EmbeddedStackFrame),
        Uinstr = push_region_frame(StackId, EmbeddedStackFrame)
    ;
        Uinstr0 = region_fill_frame(FillOp, EmbeddedStackFrame, IdRval0,
            NumLval0, AddrLval0),
        (
            ReplData = yes,
            replace_labels_rval(IdRval0, IdRval, ReplMap),
            replace_labels_lval(NumLval0, NumLval, ReplMap),
            replace_labels_lval(AddrLval0, AddrLval, ReplMap)
        ;
            ReplData = no,
            IdRval = IdRval0,
            NumLval = NumLval0,
            AddrLval = AddrLval0
        ),
        Uinstr = region_fill_frame(FillOp, EmbeddedStackFrame, IdRval,
            NumLval, AddrLval)
    ;
        Uinstr0 = region_set_fixed_slot(SetOp, EmbeddedStackFrame,
            ValueRval0),
        (
            ReplData = yes,
            replace_labels_rval(ValueRval0, ValueRval, ReplMap)
        ;
            ReplData = no,
            ValueRval = ValueRval0
        ),
        Uinstr = region_set_fixed_slot(SetOp, EmbeddedStackFrame,
            ValueRval)
    ;
        Uinstr0 = use_and_maybe_pop_region_frame(UseOp, EmbeddedStackFrame),
        Uinstr = use_and_maybe_pop_region_frame(UseOp, EmbeddedStackFrame)
    ;
        Uinstr0 = store_ticket(Lval0),
        (
            ReplData = yes,
            replace_labels_lval(Lval0, Lval, ReplMap)
        ;
            ReplData = no,
            Lval = Lval0
        ),
        Uinstr = store_ticket(Lval)
    ;
        Uinstr0 = reset_ticket(Rval0, Reason),
        (
            ReplData = yes,
            replace_labels_rval(Rval0, Rval, ReplMap)
        ;
            ReplData = no,
            Rval = Rval0
        ),
        Uinstr = reset_ticket(Rval, Reason)
    ;
        Uinstr0 = mark_ticket_stack(Lval0),
        (
            ReplData = yes,
            replace_labels_lval(Lval0, Lval, ReplMap)
        ;
            ReplData = no,
            Lval = Lval0
        ),
        Uinstr = mark_ticket_stack(Lval)
    ;
        Uinstr0 = prune_tickets_to(Rval0),
        (
            ReplData = yes,
            replace_labels_rval(Rval0, Rval, ReplMap)
        ;
            ReplData = no,
            Rval = Rval0
        ),
        Uinstr = prune_tickets_to(Rval)
    ;
        Uinstr0 = init_sync_term(Lval0, NumConjuncts, TSStringIndex),
        (
            ReplData = yes,
            replace_labels_lval(Lval0, Lval, ReplMap)
        ;
            ReplData = no,
            Lval = Lval0
        ),
        Uinstr = init_sync_term(Lval, NumConjuncts, TSStringIndex)
    ;
        Uinstr0 = fork_new_child(Lval0, Child0),
        replace_labels_lval(Lval0, Lval, ReplMap),
        replace_labels_label(Child0, Child, ReplMap),
        Uinstr = fork_new_child(Lval, Child)
    ;
        Uinstr0 = join_and_continue(Lval0, Label0),
        replace_labels_label(Label0, Label, ReplMap),
        replace_labels_lval(Lval0, Lval, ReplMap),
        Uinstr = join_and_continue(Lval, Label)
    ;
        Uinstr0 = foreign_proc_code(Decls, Comps0, MayCallMercury,
            MaybeFix, MaybeLayout, MaybeOnlyLayout, MaybeSub0, MaybeDef,
            StackSlotRef, MayDupl),
        (
            MaybeFix = no
        ;
            MaybeFix = yes(FixLabel0),
            replace_labels_label(FixLabel0, FixLabel, ReplMap),
            % We cannot replace the label in the C code string itself.
            expect(unify(FixLabel0, FixLabel), $module, $pred,
                "trying to replace Mercury label in C code")
        ),
        (
            MaybeLayout = no
        ;
            MaybeLayout = yes(LayoutLabel0),
            replace_labels_label(LayoutLabel0, LayoutLabel, ReplMap),
            % We cannot replace a label that has a layout structure.
            expect(unify(LayoutLabel0, LayoutLabel), $module, $pred,
                "trying to replace Mercury label with layout")
        ),
        (
            MaybeOnlyLayout = no
        ;
            MaybeOnlyLayout = yes(OnlyLayoutLabel0),
            replace_labels_label(OnlyLayoutLabel0, OnlyLayoutLabel, ReplMap),
            % We cannot replace a label that has a layout structure.
            expect(unify(OnlyLayoutLabel0, OnlyLayoutLabel), $module, $pred,
                "trying to replace Mercury label with layout")
        ),
        (
            MaybeSub0 = no,
            MaybeSub = no,
            Comps = Comps0
        ;
            MaybeSub0 = yes(SubLabel0),
            replace_labels_label(SubLabel0, SubLabel, ReplMap),
            MaybeSub = yes(SubLabel),
            replace_labels_comps(Comps0, Comps, ReplMap)
        ),
        (
            MaybeDef = no
        ;
            MaybeDef = yes(DefLabel0),
            replace_labels_label(DefLabel0, DefLabel, ReplMap),
            % We cannot replace a label that has a layout structure.
            expect(unify(DefLabel0, DefLabel), $module, $pred,
                "trying to replace Mercury label with layout")
        ),
        Uinstr = foreign_proc_code(Decls, Comps, MayCallMercury,
            MaybeFix, MaybeLayout, MaybeOnlyLayout, MaybeSub, MaybeDef,
            StackSlotRef, MayDupl)
    ).

replace_labels_comps([], [], _).
replace_labels_comps([Comp0 | Comps0], [Comp | Comps], ReplMap) :-
    replace_labels_comp(Comp0, Comp, ReplMap),
    replace_labels_comps(Comps0, Comps, ReplMap).

:- pred replace_labels_comp(
    foreign_proc_component::in, foreign_proc_component::out,
    map(label, label)::in) is det.

replace_labels_comp(Comp0, Comp, ReplMap) :-
    (
        ( Comp0 = foreign_proc_inputs(_)
        ; Comp0 = foreign_proc_outputs(_)
        ; Comp0 = foreign_proc_user_code(_, _, _)
        ; Comp0 = foreign_proc_raw_code(_, _, _, _)
        ; Comp0 = foreign_proc_alloc_id(_)
        ; Comp0 = foreign_proc_noop
        ),
        Comp = Comp0
    ;
        Comp0 = foreign_proc_fail_to(Label0),
        replace_labels_label(Label0, Label, ReplMap),
        Comp = foreign_proc_fail_to(Label)
    ).

:- pred replace_labels_c_code_live_lvals(
    c_code_live_lvals::in, c_code_live_lvals::out, map(label, label)::in)
    is det.

replace_labels_c_code_live_lvals(LiveLvals0, LiveLvals, ReplMap) :-
    (
        LiveLvals0 = no_live_lvals_info,
        LiveLvals = LiveLvals0
    ;
        LiveLvals0 = live_lvals_info(LvalSet0),
        set.to_sorted_list(LvalSet0, Lvals0),
        list.map(replace_labels_lval_map(ReplMap), Lvals0, Lvals),
        % We cannot replace the lvals inside the C code.
        expect(unify(Lvals0, Lvals), $module, $pred, "some replacements"),
        LiveLvals = LiveLvals0
    ).

:- pred replace_labels_lval_map(map(label, label)::in, lval::in, lval::out)
    is det.

replace_labels_lval_map(ReplMap, Lval0, Lval) :-
    replace_labels_lval(Lval0, Lval, ReplMap).

:- pred replace_labels_lval(lval::in, lval::out, map(label, label)::in) is det.

replace_labels_lval(Lval0, Lval, ReplMap) :-
    (
        ( Lval0 = reg(_, _)
        ; Lval0 = temp(_, _)
        ; Lval0 = stackvar(_)
        ; Lval0 = framevar(_)
        ; Lval0 = parent_stackvar(_)
        ; Lval0 = succip
        ; Lval0 = maxfr
        ; Lval0 = curfr
        ; Lval0 = hp
        ; Lval0 = sp
        ; Lval0 = parent_sp
        ; Lval0 = lvar(_)
        ; Lval0 = global_var_ref(_)
        ),
        Lval = Lval0
    ;
        Lval0 = succip_slot(Rval0),
        replace_labels_rval(Rval0, Rval, ReplMap),
        Lval = succip_slot(Rval)
    ;
        Lval0 = succfr_slot(Rval0),
        replace_labels_rval(Rval0, Rval, ReplMap),
        Lval = succfr_slot(Rval)
    ;
        Lval0 = redoip_slot(Rval0),
        replace_labels_rval(Rval0, Rval, ReplMap),
        Lval = redoip_slot(Rval)
    ;
        Lval0 = redofr_slot(Rval0),
        replace_labels_rval(Rval0, Rval, ReplMap),
        Lval = redofr_slot(Rval)
    ;
        Lval0 = prevfr_slot(Rval0),
        replace_labels_rval(Rval0, Rval, ReplMap),
        Lval = prevfr_slot(Rval)
    ;
        Lval0 = field(Tag, BaseRval0, OffsetRval0),
        replace_labels_rval(BaseRval0, BaseRval, ReplMap),
        replace_labels_rval(OffsetRval0, OffsetRval, ReplMap),
        Lval = field(Tag, BaseRval, OffsetRval)
    ;
        Lval0 = mem_ref(Rval0),
        replace_labels_rval(Rval0, Rval, ReplMap),
        Lval = mem_ref(Rval)
    ).

:- pred replace_labels_rval(rval::in, rval::out, map(label, label)::in) is det.

replace_labels_rval(Rval0, Rval, ReplMap) :-
    (
        Rval0 = lval(Lval0),
        replace_labels_lval(Lval0, Lval, ReplMap),
        Rval = lval(Lval)
    ;
        Rval0 = var(_Var),
        Rval = Rval0
    ;
        Rval0 = mkword(Tag, SubRval0),
        replace_labels_rval(SubRval0, SubRval, ReplMap),
        Rval = mkword(Tag, SubRval)
    ;
        Rval0 = const(Const0),
        replace_labels_rval_const(Const0, Const, ReplMap),
        Rval = const(Const)
    ;
        Rval0 = unop(UnOp, SubRvalA0),
        replace_labels_rval(SubRvalA0, SubRvalA, ReplMap),
        Rval = unop(UnOp, SubRvalA)
    ;
        Rval0 = binop(BinOp, SubRvalA0, SubRvalB0),
        replace_labels_rval(SubRvalA0, SubRvalA, ReplMap),
        replace_labels_rval(SubRvalB0, SubRvalB, ReplMap),
        Rval = binop(BinOp, SubRvalA, SubRvalB)
    ;
        Rval0 = mem_addr(MemRef0),
        replace_labels_mem_ref(MemRef0, MemRef, ReplMap),
        Rval = mem_addr(MemRef)
    ).

:- pred replace_labels_mem_ref(mem_ref::in, mem_ref::out,
    map(label, label)::in) is det.

replace_labels_mem_ref(MemRef0, MemRef, ReplMap) :-
    (
        ( MemRef0 = stackvar_ref(_)
        ; MemRef0 = framevar_ref(_)
        ),
        MemRef = MemRef0
    ;
        MemRef0 = heap_ref(CellRval0, Tag, FieldNumRval0),
        replace_labels_rval(CellRval0, CellRval, ReplMap),
        replace_labels_rval(FieldNumRval0, FieldNumRval, ReplMap),
        MemRef = heap_ref(CellRval, Tag, FieldNumRval)
    ).

:- pred replace_labels_rval_const(rval_const::in, rval_const::out,
    map(label, label)::in) is det.

replace_labels_rval_const(Const0, Const, ReplMap) :-
    (
        ( Const0 = llconst_true
        ; Const0 = llconst_false
        ; Const0 = llconst_int(_)
        ; Const0 = llconst_foreign(_, _)
        ; Const0 = llconst_float(_)
        ; Const0 = llconst_string(_)
        ; Const0 = llconst_multi_string(_)
        ; Const0 = llconst_data_addr(_, _)
        ),
        Const = Const0
    ;
        Const0 = llconst_code_addr(Addr0),
        replace_labels_code_addr(Addr0, Addr, ReplMap),
        Const = llconst_code_addr(Addr)
    ).

replace_labels_code_addr(Addr0, Addr, ReplMap) :-
    (
        Addr0 = code_label(Label0),
        replace_labels_label(Label0, Label, ReplMap),
        Addr = code_label(Label)
    ;
        ( Addr0 = code_imported_proc(_)
        ; Addr0 = code_succip
        ; Addr0 = do_succeed(_)
        ; Addr0 = do_redo
        ; Addr0 = do_fail
        ; Addr0 = do_trace_redo_fail_shallow
        ; Addr0 = do_trace_redo_fail_deep
        ; Addr0 = do_call_closure(_)
        ; Addr0 = do_call_class_method(_)
        ; Addr0 = do_not_reached
        ),
        Addr = Addr0
    ).

replace_labels_maybe_label_list([], [], _ReplMap).
replace_labels_maybe_label_list([MaybeLabel0 | MaybeLabels0],
        [MaybeLabel | MaybeLabels], ReplMap) :-
    (
        MaybeLabel0 = yes(Label0),
        replace_labels_label(Label0, Label, ReplMap),
        MaybeLabel = yes(Label)
    ;
        MaybeLabel0 = no,
        MaybeLabel = no
    ),
    replace_labels_maybe_label_list(MaybeLabels0, MaybeLabels, ReplMap).

replace_labels_label(Label0, Label, ReplMap) :-
    ( map.search(ReplMap, Label0, NewLabel) ->
        Label = NewLabel
    ;
        Label = Label0
    ).

%-----------------------------------------------------------------------------%
:- end_module ll_backend.opt_util.
%-----------------------------------------------------------------------------%
