mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-12 12:26:29 +00:00
compiler/hlds_data.m:
Add bespoke types to record information about local and remote secondary
tags. The one for local secondary tags includes the value of the
primary and secondary tag together, since construct unifications
need to assign this value, and it is better to compute this once,
instead leaving the target language compiler to do it, potentially
many times.
Use a wrapped uint8 to record primary tag values, and wrapped uints
to record secondary tag values. The wrap is to prevent any accidental
confusion with other values. The use of uint8 and uint has two purposes.
First, using the tighest possible representation. Tags are never negative,
and primary tags cannot exceed 7. Second, using these types in the compiler
help us eat our own dogfood; if a change causes a problem affecting
these types, its bootcheck should fail, alerting us to the problem.
Add commented-out types and fields that will be needed for packing
sub-word-sized arguments together with both local and remote secondary
tags.
compiler/du_type_layout.m:
Generate references to tags in the new format.
compiler/ml_unify_gen.m:
compiler/unify_gen.m:
compiler/modecheck_goal.m:
Conform to the changes above.
Fix an old bug: the inst corresponding to a constant with a primary
and a local secondary tag is not the secondary tag alone, but both tags
together.
compiler/bytecode.m:
compiler/bytecode_gen.m:
compiler/closure_gen.m:
compiler/disj_gen.m:
compiler/export.m:
compiler/hlds_code_util.m:
compiler/jumpopt.m:
compiler/lco.m:
compiler/llds_out_data.m:
compiler/llds_out_instr.m:
compiler/lookup_switch.m:
compiler/lookup_util.m:
compiler/ml_accurate_gc.m:
compiler/ml_call_gen.m:
compiler/ml_closure_gen.m:
compiler/ml_code_util.m:
compiler/ml_elim_nested.m:
compiler/ml_string_switch.m:
compiler/ml_switch_gen.m:
compiler/ml_tag_switch.m:
compiler/ml_type_gen.m:
compiler/mlds_dump.m:
compiler/mlds_to_c_data.m:
compiler/mlds_to_c_stmt.m:
compiler/opt_debug.m:
compiler/peephole.m:
compiler/rtti.m:
compiler/rtti_out.m:
compiler/rtti_to_mlds.m:
compiler/string_switch.m:
compiler/switch_util.m:
compiler/tag_switch.m:
compiler/type_ctor_info.m:
Conform to the change to hlds_data.m.
In two places, in rtti_out.m and rtti_to_mlds.m, delete old code
that was needed only to implement reserved tags, which we have
stopped supporting a few months ago.
library/uint8.m:
library/uint16.m:
library/uint32.m:
library/uint64.m:
Add predicates to cast from each of these types to uint.
1296 lines
50 KiB
Mathematica
1296 lines
50 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2007, 2009-2012 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: jumpopt.m.
|
|
% Author: zs.
|
|
%
|
|
% This module contains code that optimizes jumps to jumps.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend.jumpopt.
|
|
:- interface.
|
|
|
|
:- import_module ll_backend.llds.
|
|
:- import_module mdbcomp.prim_data.
|
|
|
|
:- import_module bool.
|
|
:- import_module counter.
|
|
:- import_module list.
|
|
:- import_module set_tree234.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% optimize_jumps_in_proc(LayoutLabels, MayAlterRtti, ProcLabel,
|
|
% Fulljumpopt, Recjump, PessimizeTailCalls, CheckedNondetTailCall,
|
|
% !LabelNumCounter, !Instrs, Mod):
|
|
%
|
|
% Take an instruction list and optimize jumps. This includes the jumps
|
|
% implicit in procedure returns.
|
|
%
|
|
% LayoutLabels gives the set of labels that have layout structures.
|
|
% This module will not optimize jumps to labels in this set, since
|
|
% this may interfere with the RTTI recorded for these labels.
|
|
% MayAlterRtti says whether we are allowed to perform optimizations
|
|
% that may interfere with RTTI.
|
|
%
|
|
% Fulljumpopt should be the value of the --optimize-fulljumps option.
|
|
%
|
|
% Recjump should be an indication of whether this is the final
|
|
% application of this optimization.
|
|
%
|
|
% PessimizeTailCalls should be the value of the --pessimize-tailcalls
|
|
% option.
|
|
%
|
|
% CheckedNondetTailCall should be the value of the
|
|
% --checked-nondet-tailcalls option.
|
|
%
|
|
% Mod will say whether the instruction sequence was modified
|
|
% by the optimization.
|
|
%
|
|
:- pred optimize_jumps_in_proc(set_tree234(label)::in, may_alter_rtti::in,
|
|
proc_label::in, bool::in, bool::in, bool::in, bool::in,
|
|
counter::in, counter::out, list(instruction)::in, list(instruction)::out,
|
|
bool::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs.builtin_ops.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_llds.
|
|
:- import_module ll_backend.code_util.
|
|
:- import_module ll_backend.opt_util.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_data_foreign.
|
|
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% We first build up a bunch of tables giving information about labels.
|
|
% We then traverse the instruction list, using the information in the
|
|
% tables to short-circuit jumps.
|
|
%
|
|
% InstrMap: Maps each label to the next real (non-comment, non-livevals)
|
|
% instruction after that label.
|
|
% LvalMap: Maps each label to yes(Livevals) if the label is followed
|
|
% by a livevals instruction, and to no otherwise.
|
|
% BlockMap: Maps each label to the block following that label.
|
|
% This includes all instructions up to the first one that
|
|
% cannot fall through.
|
|
% ProcMap: Maps each label that begins a det epilog to the epilog.
|
|
% SuccMap: Maps each label that begins a nondet epilog to the epilog.
|
|
% SdprocMap: Maps each label that begins a semidet epilog to the epilog.
|
|
% This can be the success epilog or the failure epilog.
|
|
% ForkMap: Maps each label that begins a full semidet epilog (code to
|
|
% test r1, and to execute the success or failure epilog
|
|
% depending on the result) to the epilog.
|
|
%
|
|
% BlockMap will not contain the initial block of the procedure unless
|
|
% Recjump is set. The intention is that Recjump will not be set until
|
|
% frameopt, which can do a better job of optimizing this block, have
|
|
% been applied.
|
|
|
|
optimize_jumps_in_proc(LayoutLabels, MayAlterRtti, ProcLabel, Fulljumpopt,
|
|
Recjump, PessimizeTailCalls, CheckedNondetTailCall, !LabelNumCounter,
|
|
!Instrs, Mod) :-
|
|
some [!InstrMap, !BlockMap, !LvalMap, !ProcMap, !SdprocMap, !SuccMap,
|
|
!ForkMap]
|
|
(
|
|
Instrs0 = !.Instrs,
|
|
map.init(!:InstrMap),
|
|
map.init(!:BlockMap),
|
|
map.init(!:LvalMap),
|
|
map.init(!:ProcMap),
|
|
map.init(!:SdprocMap),
|
|
map.init(!:SuccMap),
|
|
jump_opt_build_maps(!.Instrs, Recjump, !InstrMap, !BlockMap, !LvalMap,
|
|
!ProcMap, !SdprocMap, !SuccMap),
|
|
jump_opt_build_forkmap(!.Instrs, !.SdprocMap, map.init, !:ForkMap),
|
|
(
|
|
PessimizeTailCalls = no
|
|
;
|
|
PessimizeTailCalls = yes,
|
|
!:ProcMap = map.init,
|
|
!:SdprocMap = map.init,
|
|
!:SuccMap = map.init,
|
|
!:ForkMap = map.init
|
|
),
|
|
JumpOptInfo = jump_opt_info(!.InstrMap, !.BlockMap, !.LvalMap,
|
|
!.ProcMap, !.SdprocMap, !.ForkMap, !.SuccMap, LayoutLabels,
|
|
Fulljumpopt, MayAlterRtti),
|
|
(
|
|
CheckedNondetTailCall = yes,
|
|
CheckedNondetTailCallInfo0 =
|
|
check_nondet_tailcalls(ProcLabel, !.LabelNumCounter),
|
|
jump_opt_instr_list(!.Instrs, comment(""), JumpOptInfo,
|
|
CheckedNondetTailCallInfo0, CheckedNondetTailCallInfo,
|
|
[], RevInstrs),
|
|
(
|
|
CheckedNondetTailCallInfo =
|
|
check_nondet_tailcalls(_, !:LabelNumCounter)
|
|
;
|
|
CheckedNondetTailCallInfo = dont_check_nondet_tailcalls,
|
|
unexpected($pred, "lost the next label number")
|
|
)
|
|
;
|
|
CheckedNondetTailCall = no,
|
|
CheckedNondetTailCallInfo0 = dont_check_nondet_tailcalls,
|
|
jump_opt_instr_list(!.Instrs, comment(""), JumpOptInfo,
|
|
CheckedNondetTailCallInfo0, _, [], RevInstrs)
|
|
),
|
|
list.reverse(RevInstrs, !:Instrs),
|
|
opt_util.filter_out_bad_livevals(!Instrs),
|
|
( if !.Instrs = Instrs0 then
|
|
Mod = no
|
|
else
|
|
Mod = yes
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred jump_opt_build_maps(list(instruction)::in, bool::in,
|
|
instrmap::in, instrmap::out, tailmap::in, tailmap::out,
|
|
lvalmap::in, lvalmap::out, tailmap::in, tailmap::out,
|
|
tailmap::in, tailmap::out, tailmap::in, tailmap::out) is det.
|
|
|
|
jump_opt_build_maps([], _, !InstrMap, !BlockMap,
|
|
!LvalMap, !ProcMap, !SdprocMap, !SuccMap).
|
|
jump_opt_build_maps([Instr0 | Instrs0], Recjump, !InstrMap, !BlockMap,
|
|
!LvalMap, !ProcMap, !SdprocMap, !SuccMap) :-
|
|
Instr0 = llds_instr(Uinstr0, Comment0),
|
|
( if Uinstr0 = label(Label) then
|
|
opt_util.skip_comments(Instrs0, Instrs1),
|
|
( if Instrs1 = [Instr1 | _], Instr1 = llds_instr(livevals(_), _) then
|
|
map.det_insert(Label, yes(Instr1), !LvalMap)
|
|
else
|
|
map.det_insert(Label, no, !LvalMap)
|
|
),
|
|
opt_util.skip_comments_livevals(Instrs1, Instrs2),
|
|
( if Instrs2 = [Instr2 | _] then
|
|
map.det_insert(Label, Instr2, !InstrMap)
|
|
else
|
|
true
|
|
),
|
|
( if opt_util.is_proceed_next(Instrs1, Between1) then
|
|
map.det_insert(Label, Between1, !ProcMap)
|
|
else
|
|
true
|
|
),
|
|
( if opt_util.is_sdproceed_next(Instrs1, Between2) then
|
|
map.det_insert(Label, Between2, !SdprocMap)
|
|
else
|
|
true
|
|
),
|
|
( if opt_util.is_succeed_next(Instrs1, Between3) then
|
|
map.det_insert(Label, Between3, !SuccMap)
|
|
else
|
|
true
|
|
),
|
|
% Put the start of the procedure into Blockmap only after
|
|
% frameopt has had a shot at it.
|
|
( if
|
|
(
|
|
Label = internal_label(_, _),
|
|
% We put entry labels into !BlockMap only if the comment
|
|
% does NOT end with "nofulljump".
|
|
not string.suffix(Comment0, "nofulljump")
|
|
;
|
|
Label = entry_label(_, _),
|
|
% We put entry labels into !BlockMap only if Recjump = yes.
|
|
Recjump = yes
|
|
)
|
|
then
|
|
opt_util.find_no_fallthrough(Instrs1, Block),
|
|
map.det_insert(Label, Block, !BlockMap)
|
|
else
|
|
true
|
|
)
|
|
else
|
|
true
|
|
),
|
|
jump_opt_build_maps(Instrs0, Recjump, !InstrMap, !BlockMap, !LvalMap,
|
|
!ProcMap, !SdprocMap, !SuccMap).
|
|
|
|
% Find labels followed by a test of r1 where both paths set r1 to
|
|
% its original value and proceed.
|
|
%
|
|
:- pred jump_opt_build_forkmap(list(instruction)::in, tailmap::in,
|
|
tailmap::in, tailmap::out) is det.
|
|
|
|
jump_opt_build_forkmap([], _SdprocMap, !ForkMap).
|
|
jump_opt_build_forkmap([llds_instr(Uinstr, _Comment) | Instrs], SdprocMap,
|
|
!ForkMap) :-
|
|
( if
|
|
Uinstr = label(Label),
|
|
opt_util.is_forkproceed_next(Instrs, SdprocMap, Between)
|
|
then
|
|
map.det_insert(Label, Between, !ForkMap)
|
|
else
|
|
true
|
|
),
|
|
jump_opt_build_forkmap(Instrs, SdprocMap, !ForkMap).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type jump_opt_info
|
|
---> jump_opt_info(
|
|
joi_instr_map :: instrmap,
|
|
joi_block_map :: tailmap,
|
|
joi_lval_map :: lvalmap,
|
|
joi_proc_map :: tailmap,
|
|
joi_sdproc_map :: tailmap,
|
|
joi_fork_map :: tailmap,
|
|
joi_succ_map :: tailmap,
|
|
joi_layout_labels :: set_tree234(label),
|
|
joi_full_jump_opt :: bool,
|
|
joi_may_alter_rtti :: may_alter_rtti
|
|
).
|
|
|
|
:- type new_remain
|
|
---> nr_specified(
|
|
new_instructions :: list(instruction),
|
|
remaining_instructions :: list(instruction)
|
|
)
|
|
; nr_usual_case.
|
|
% The list of new instructions contains just Instr0, and
|
|
% the list of remaining instructions, on which to recurse,
|
|
% Instrs0.
|
|
|
|
:- type maybe_check_nondet_tailcalls
|
|
---> dont_check_nondet_tailcalls
|
|
; check_nondet_tailcalls(proc_label, counter).
|
|
|
|
% Optimize the given instruction list by eliminating unnecessary jumps.
|
|
%
|
|
% We handle calls by attempting to turn them into tailcalls. If this fails,
|
|
% we try to short-circuit the return address.
|
|
%
|
|
% We handle gotos by first trying to eliminate them. If this fails,
|
|
% we check whether their target label begins a proceed/succeed sequence;
|
|
% if it does, we replace the label by that sequence. If this fails as well,
|
|
% we check whether the instruction at the ultimate target label can
|
|
% fall through. If it cannot (e.g. call), we replace the goto with
|
|
% this instruction.
|
|
%
|
|
% We handle computed gotos by attempting to short-circuit all the labels
|
|
% in the label list.
|
|
%
|
|
% We handle if-vals by trying to turn them into the assignment
|
|
% of a boolean value to r1, or by short-circuiting the target label.
|
|
% We also try to eliminate a goto following an if-val, if we can do so
|
|
% by negating the condition and possibly also deleting a label between
|
|
% the if-val and the goto.
|
|
%
|
|
% We build up the generated instruction list in reverse order, because
|
|
% building it in right order would make instr_list not tail recursive,
|
|
% and thus unable to handle very long instruction lists.
|
|
%
|
|
:- pred jump_opt_instr_list(list(instruction)::in, instr::in,
|
|
jump_opt_info::in,
|
|
maybe_check_nondet_tailcalls::in, maybe_check_nondet_tailcalls::out,
|
|
list(instruction)::in, list(instruction)::out) is det.
|
|
|
|
jump_opt_instr_list([], _PrevInstr, _, !CheckedNondetTailCallInfo, !RevInstrs).
|
|
jump_opt_instr_list([Instr0 | Instrs0], PrevInstr, JumpOptInfo,
|
|
!CheckedNondetTailCallInfo, !RevInstrs) :-
|
|
Instr0 = llds_instr(Uinstr0, Comment0),
|
|
% We do a switch on the instruction type to ensure that we short circuit
|
|
% all the labels that are in InstrMap but not in LayoutLabels in *all*
|
|
% instructions in which they occur. This means we must fully search
|
|
% every part of every instruction that may possibly hold a label.
|
|
% In theory, this means every lval and every rval, but in practice we
|
|
% know that rvals representing e.g. the tags of fields cannot contain
|
|
% labels.
|
|
(
|
|
Uinstr0 = llcall(_, _, _, _, _, _),
|
|
jump_opt_llcall(Uinstr0, Comment0, Instrs0, PrevInstr, JumpOptInfo,
|
|
!CheckedNondetTailCallInfo, NewRemain)
|
|
;
|
|
Uinstr0 = goto(_),
|
|
jump_opt_goto(Uinstr0, Comment0, Instrs0, PrevInstr, JumpOptInfo,
|
|
!CheckedNondetTailCallInfo, NewRemain)
|
|
;
|
|
Uinstr0 = computed_goto(Index, MaybeTargets0),
|
|
InstrMap = JumpOptInfo ^ joi_instr_map,
|
|
% Short-circuit all the destination labels.
|
|
short_circuit_maybe_labels(InstrMap, MaybeTargets0, MaybeTargets),
|
|
( if MaybeTargets = MaybeTargets0 then
|
|
NewRemain = nr_usual_case
|
|
else
|
|
Shorted = Comment0 ++ " (some shortcircuits)",
|
|
NewInstrs =
|
|
[llds_instr(computed_goto(Index, MaybeTargets), Shorted)],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
)
|
|
;
|
|
Uinstr0 = if_val(_, _),
|
|
jump_opt_if_val(Uinstr0, Comment0, Instrs0, PrevInstr, JumpOptInfo,
|
|
!CheckedNondetTailCallInfo, NewRemain)
|
|
;
|
|
Uinstr0 = assign(Lval, Rval0),
|
|
% Any labels mentioned in Rval0 should be short-circuited.
|
|
InstrMap = JumpOptInfo ^ joi_instr_map,
|
|
short_circuit_labels_rval(InstrMap, Rval0, Rval),
|
|
( if Rval = Rval0 then
|
|
NewRemain = nr_usual_case
|
|
else
|
|
Shorted = Comment0 ++ " (some shortcircuits)",
|
|
NewInstrs = [llds_instr(assign(Lval, Rval), Shorted)],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
)
|
|
;
|
|
Uinstr0 = keep_assign(Lval, Rval0),
|
|
% Any labels mentioned in Rval0 should be short-circuited.
|
|
InstrMap = JumpOptInfo ^ joi_instr_map,
|
|
short_circuit_labels_rval(InstrMap, Rval0, Rval),
|
|
( if Rval = Rval0 then
|
|
NewRemain = nr_usual_case
|
|
else
|
|
Shorted = Comment0 ++ " (some shortcircuits)",
|
|
NewInstrs = [llds_instr(keep_assign(Lval, Rval), Shorted)],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
)
|
|
;
|
|
Uinstr0 = mkframe(FrameInfo, Redoip),
|
|
( if Redoip = yes(code_label(Label0)) then
|
|
InstrMap = JumpOptInfo ^ joi_instr_map,
|
|
short_circuit_label(InstrMap, Label0, Label),
|
|
( if Label = Label0 then
|
|
NewRemain = nr_usual_case
|
|
else
|
|
Shorted = Comment0 ++ " (some shortcircuits)",
|
|
NewInstrs =
|
|
[llds_instr(mkframe(FrameInfo, yes(code_label(Label))),
|
|
Shorted)],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
)
|
|
else
|
|
NewRemain = nr_usual_case
|
|
)
|
|
;
|
|
Uinstr0 = foreign_proc_code(_, _, _, _, _, _, _, _, _, _),
|
|
jump_opt_foreign_proc_code(Uinstr0, Comment0, Instrs0, PrevInstr,
|
|
JumpOptInfo, !CheckedNondetTailCallInfo, NewRemain)
|
|
;
|
|
Uinstr0 = block(_, _, _),
|
|
% These are supposed to be introduced only after jumpopt is run
|
|
% for the last time.
|
|
unexpected($pred, "block")
|
|
;
|
|
Uinstr0 = fork_new_child(SyncTerm, Child0),
|
|
InstrMap = JumpOptInfo ^ joi_instr_map,
|
|
short_circuit_label(InstrMap, Child0, Child),
|
|
( if Child = Child0 then
|
|
NewRemain = nr_usual_case
|
|
else
|
|
Uinstr = fork_new_child(SyncTerm, Child),
|
|
Comment = Comment0 ++ " (redirect)",
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
NewRemain = nr_specified([Instr], Instrs0)
|
|
)
|
|
;
|
|
Uinstr0 = join_and_continue(SyncTerm, Label0),
|
|
InstrMap = JumpOptInfo ^ joi_instr_map,
|
|
short_circuit_label(InstrMap, Label0, Label),
|
|
( if Label = Label0 then
|
|
NewRemain = nr_usual_case
|
|
else
|
|
Uinstr = join_and_continue(SyncTerm, Label),
|
|
Comment = Comment0 ++ " (redirect)",
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
NewRemain = nr_specified([Instr], Instrs0)
|
|
)
|
|
;
|
|
Uinstr0 = lc_wait_free_slot(_, _, _),
|
|
% The label in the third argument should not be referred to
|
|
% from any code in the procedure's LLDS instruction sequence,
|
|
% so there is no way for it to be short circuited.
|
|
NewRemain = nr_usual_case
|
|
;
|
|
Uinstr0 = lc_spawn_off(LCRval, LCSRval, Child0),
|
|
InstrMap = JumpOptInfo ^ joi_instr_map,
|
|
short_circuit_label(InstrMap, Child0, Child),
|
|
( if Child = Child0 then
|
|
NewRemain = nr_usual_case
|
|
else
|
|
Uinstr = lc_spawn_off(LCRval, LCSRval, Child),
|
|
Comment = Comment0 ++ " (redirect)",
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
NewRemain = nr_specified([Instr], Instrs0)
|
|
)
|
|
;
|
|
( Uinstr0 = arbitrary_c_code(_, _, _)
|
|
; Uinstr0 = comment(_)
|
|
; Uinstr0 = livevals(_)
|
|
; Uinstr0 = label(_)
|
|
; Uinstr0 = save_maxfr(_)
|
|
; Uinstr0 = restore_maxfr(_)
|
|
; Uinstr0 = incr_sp(_, _, _)
|
|
; Uinstr0 = decr_sp(_)
|
|
; Uinstr0 = decr_sp_and_return(_)
|
|
; Uinstr0 = push_region_frame(_, _)
|
|
; Uinstr0 = region_fill_frame(_, _, _, _, _)
|
|
; Uinstr0 = region_set_fixed_slot(_, _, _)
|
|
; Uinstr0 = use_and_maybe_pop_region_frame(_, _)
|
|
; Uinstr0 = store_ticket(_)
|
|
; Uinstr0 = reset_ticket(_, _)
|
|
; Uinstr0 = discard_ticket
|
|
; Uinstr0 = prune_ticket
|
|
; Uinstr0 = prune_tickets_to(_)
|
|
; Uinstr0 = mark_ticket_stack(_)
|
|
; Uinstr0 = mark_hp(_)
|
|
; Uinstr0 = free_heap(_)
|
|
; Uinstr0 = incr_hp(_, _, _, _, _, _, _, _)
|
|
; Uinstr0 = restore_hp(_)
|
|
; Uinstr0 = init_sync_term(_, _, _)
|
|
; Uinstr0 = lc_create_loop_control(_, _)
|
|
; Uinstr0 = lc_join_and_terminate(_, _)
|
|
),
|
|
NewRemain = nr_usual_case
|
|
),
|
|
(
|
|
NewRemain = nr_usual_case,
|
|
ReplacementInstrsEmpty = no,
|
|
RecurseInstrs = Instrs0,
|
|
!:RevInstrs = [Instr0 | !.RevInstrs]
|
|
;
|
|
NewRemain = nr_specified(ReplacementInstrs, RecurseInstrs),
|
|
% ReplacementInstrs are in the right order, but they will be reversed
|
|
% by our caller. We therefore reverse them here, which allows that
|
|
% final reverse to put them in the right order.
|
|
!:RevInstrs = list.reverse(ReplacementInstrs) ++ !.RevInstrs,
|
|
(
|
|
ReplacementInstrs = [],
|
|
ReplacementInstrsEmpty = yes
|
|
;
|
|
ReplacementInstrs = [_ | _],
|
|
ReplacementInstrsEmpty = no
|
|
)
|
|
),
|
|
( if
|
|
( Uinstr0 = comment(_)
|
|
; ReplacementInstrsEmpty = yes
|
|
)
|
|
then
|
|
NewPrevInstr = PrevInstr
|
|
else
|
|
NewPrevInstr = Uinstr0
|
|
),
|
|
jump_opt_instr_list(RecurseInstrs, NewPrevInstr, JumpOptInfo,
|
|
!CheckedNondetTailCallInfo, !RevInstrs).
|
|
|
|
:- pred jump_opt_llcall(instr::in(instr_llcall), string::in,
|
|
list(instruction)::in, instr::in, jump_opt_info::in,
|
|
maybe_check_nondet_tailcalls::in, maybe_check_nondet_tailcalls::out,
|
|
new_remain::out) is det.
|
|
|
|
jump_opt_llcall(Uinstr0, Comment0, Instrs0, PrevInstr, JumpOptInfo,
|
|
!CheckedNondetTailCallInfo, NewRemain) :-
|
|
Uinstr0 = llcall(Proc, RetAddr, LiveInfos, Context, GoalPath, CallModel),
|
|
( if RetAddr = code_label(RetLabel) then
|
|
( if
|
|
(
|
|
JumpOptInfo ^ joi_may_alter_rtti = must_not_alter_rtti
|
|
;
|
|
LayoutLabels = JumpOptInfo ^ joi_layout_labels,
|
|
set_tree234.member(RetLabel, LayoutLabels)
|
|
)
|
|
then
|
|
% We cannot optimize the call. Test for this once, here, instead
|
|
% of at the end of each of the following conditions.
|
|
NewRemain = nr_usual_case
|
|
else if
|
|
% Look for det style tailcalls. We look for this even if
|
|
% the call is semidet, because one of the optimizations below
|
|
% turns a pair of semidet epilogs into a det epilog.
|
|
( CallModel = call_model_det(allow_lco)
|
|
; CallModel = call_model_semidet(allow_lco)
|
|
),
|
|
ProcMap = JumpOptInfo ^ joi_proc_map,
|
|
map.search(ProcMap, RetLabel, Between0),
|
|
PrevInstr = livevals(Livevals)
|
|
then
|
|
opt_util.filter_out_livevals(Between0, Between1),
|
|
NewInstrs = Between1 ++
|
|
[llds_instr(livevals(Livevals), ""),
|
|
llds_instr(goto(Proc), redirect_comment(Comment0))],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
else if
|
|
% Look for semidet style tailcalls.
|
|
CallModel = call_model_semidet(allow_lco),
|
|
ForkMap = JumpOptInfo ^ joi_fork_map,
|
|
map.search(ForkMap, RetLabel, Between),
|
|
PrevInstr = livevals(Livevals)
|
|
then
|
|
NewInstrs = Between ++
|
|
[llds_instr(livevals(Livevals), ""),
|
|
llds_instr(goto(Proc), redirect_comment(Comment0))],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
else if
|
|
% Look for nondet style tailcalls which do not need
|
|
% a runtime check.
|
|
CallModel = call_model_nondet(unchecked_tail_call),
|
|
SuccMap = JumpOptInfo ^ joi_succ_map,
|
|
map.search(SuccMap, RetLabel, BetweenIncl),
|
|
BetweenIncl = [llds_instr(livevals(_), _), llds_instr(goto(_), _)],
|
|
PrevInstr = livevals(Livevals)
|
|
then
|
|
NewInstrs = [
|
|
llds_instr(assign(maxfr, lval(prevfr_slot(lval(curfr)))),
|
|
"discard this frame"),
|
|
llds_instr(assign(succip, lval(succip_slot(lval(curfr)))),
|
|
"setup PC on return from tailcall"),
|
|
llds_instr(assign(curfr, lval(succfr_slot(lval(curfr)))),
|
|
"setup curfr on return from tailcall"),
|
|
llds_instr(livevals(Livevals), ""),
|
|
llds_instr(goto(Proc), redirect_comment(Comment0))
|
|
],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
else if
|
|
% Look for nondet style tailcalls which do need
|
|
% a runtime check.
|
|
CallModel = call_model_nondet(checked_tail_call),
|
|
!.CheckedNondetTailCallInfo =
|
|
check_nondet_tailcalls(ProcLabel, LabelNumCounter0),
|
|
SuccMap = JumpOptInfo ^ joi_succ_map,
|
|
map.search(SuccMap, RetLabel, BetweenIncl),
|
|
BetweenIncl = [llds_instr(livevals(_), _), llds_instr(goto(_), _)],
|
|
PrevInstr = livevals(Livevals)
|
|
then
|
|
counter.allocate(LabelNum, LabelNumCounter0, LabelNumCounter1),
|
|
NewLabel = internal_label(LabelNum, ProcLabel),
|
|
NewInstrs = [
|
|
llds_instr(if_val(binop(
|
|
ne(int_type_int), lval(curfr), lval(maxfr)),
|
|
code_label(NewLabel)),
|
|
"branch around if cannot tail call"),
|
|
llds_instr(assign(maxfr, lval(prevfr_slot(lval(curfr)))),
|
|
"discard this frame"),
|
|
llds_instr(assign(succip, lval(succip_slot(lval(curfr)))),
|
|
"setup PC on return from tailcall"),
|
|
llds_instr(assign(curfr, lval(succfr_slot(lval(curfr)))),
|
|
"setup curfr on return from tailcall"),
|
|
llds_instr(livevals(Livevals), ""),
|
|
llds_instr(goto(Proc), redirect_comment(Comment0)),
|
|
llds_instr(label(NewLabel), "non tail call"),
|
|
llds_instr(livevals(Livevals), ""),
|
|
llds_instr(Uinstr0, Comment0)
|
|
],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0),
|
|
!:CheckedNondetTailCallInfo =
|
|
check_nondet_tailcalls(ProcLabel, LabelNumCounter1)
|
|
else if
|
|
% Short circuit the return label if possible.
|
|
InstrMap = JumpOptInfo ^ joi_instr_map,
|
|
map.search(InstrMap, RetLabel, RetInstr)
|
|
then
|
|
final_dest(InstrMap, RetLabel, DestLabel, RetInstr, _DestInstr),
|
|
( if RetLabel = DestLabel then
|
|
NewInstrs = [llds_instr(Uinstr0, Comment0)]
|
|
else
|
|
NewInstrs = [llds_instr(llcall(Proc, code_label(DestLabel),
|
|
LiveInfos, Context, GoalPath, CallModel),
|
|
redirect_comment(Comment0))]
|
|
),
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
else
|
|
NewRemain = nr_usual_case
|
|
)
|
|
else
|
|
NewRemain = nr_usual_case
|
|
).
|
|
|
|
:- pred jump_opt_goto(instr::in(instr_goto), string::in,
|
|
list(instruction)::in, instr::in, jump_opt_info::in,
|
|
maybe_check_nondet_tailcalls::in, maybe_check_nondet_tailcalls::out,
|
|
new_remain::out) is det.
|
|
|
|
jump_opt_goto(Uinstr0, Comment0, Instrs0, PrevInstr, JumpOptInfo,
|
|
!CheckedNondetTailCallInfo, NewRemain) :-
|
|
Uinstr0 = goto(TargetAddr),
|
|
( if TargetAddr = code_label(TargetLabel) then
|
|
( if
|
|
% Eliminate the goto if possible.
|
|
opt_util.is_this_label_next(TargetLabel, Instrs0, _)
|
|
then
|
|
NewInstrs = [],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
else if
|
|
PrevInstr = if_val(_, code_label(IfTargetLabel)),
|
|
opt_util.is_this_label_next(IfTargetLabel, Instrs0, _)
|
|
then
|
|
% Eliminating the goto (by the local peephole pass)
|
|
% is better than shortcircuiting it here,
|
|
% PROVIDED the test will succeed most of the time;
|
|
% we could use profiling feedback on this.
|
|
% We cannot eliminate the instruction here because
|
|
% that would require altering the if_val instruction.
|
|
NewInstrs = [llds_instr(Uinstr0, Comment0)],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
else if
|
|
% Replace a jump to a det epilog with the epilog.
|
|
ProcMap = JumpOptInfo ^ joi_proc_map,
|
|
map.search(ProcMap, TargetLabel, Between0)
|
|
then
|
|
adjust_livevals(PrevInstr, Between0, Between),
|
|
NewInstrs = Between ++
|
|
[llds_instr(goto(code_succip), "shortcircuit")],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
else if
|
|
% Replace a jump to a semidet epilog with the epilog.
|
|
SdprocMap = JumpOptInfo ^ joi_sdproc_map,
|
|
map.search(SdprocMap, TargetLabel, Between0)
|
|
then
|
|
adjust_livevals(PrevInstr, Between0, Between),
|
|
NewInstrs = Between ++
|
|
[llds_instr(goto(code_succip), "shortcircuit")],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
else if
|
|
% Replace a jump to a nondet epilog with the epilog.
|
|
SuccMap = JumpOptInfo ^ joi_succ_map,
|
|
map.search(SuccMap, TargetLabel, BetweenIncl0)
|
|
then
|
|
adjust_livevals(PrevInstr, BetweenIncl0, NewInstrs),
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
else if
|
|
% Replace a jump to a non-epilog block with the block itself.
|
|
% These jumps are treated separately from jumps to epilog blocks,
|
|
% for two reasons. First, epilog blocks are always short, so we
|
|
% always want to replace jumps to them, whereas other blocks
|
|
% may be long, so we want to replace jumps to them only if the
|
|
% fulljumps option was given. Second, non-epilog blocks may contain
|
|
% branches to other labels in this procedure, and we want to
|
|
% make sure that these are short-circuited. This short-circuiting
|
|
% is necessary because another optimization below eliminates
|
|
% labels, which is correct only if jumps to those labels are
|
|
% short-circuited everywhere.
|
|
JumpOptInfo ^ joi_full_jump_opt = yes,
|
|
InstrMap = JumpOptInfo ^ joi_instr_map,
|
|
map.search(InstrMap, TargetLabel, TargetInstr),
|
|
final_dest(InstrMap, TargetLabel, DestLabel,
|
|
TargetInstr, _DestInstr),
|
|
BlockMap = JumpOptInfo ^ joi_block_map,
|
|
map.search(BlockMap, DestLabel, Block),
|
|
block_may_be_duplicated(Block) = yes
|
|
then
|
|
opt_util.filter_out_labels(Block, FilteredBlock),
|
|
adjust_livevals(PrevInstr, FilteredBlock, AdjustedBlock),
|
|
% Block may end with a goto to DestLabel. We avoid infinite
|
|
% expansion in such cases by removing DestLabel from BlockMap,
|
|
% though only while processing AdjustedBlock.
|
|
map.delete(DestLabel, BlockMap, CrippledBlockMap),
|
|
CrippledJumpOptInfo = JumpOptInfo ^ joi_block_map :=
|
|
CrippledBlockMap,
|
|
jump_opt_instr_list(AdjustedBlock, comment(""),
|
|
CrippledJumpOptInfo, !CheckedNondetTailCallInfo,
|
|
[], RevNewInstrs),
|
|
NewRemain = nr_specified(list.reverse(RevNewInstrs), Instrs0)
|
|
else if
|
|
% Short-circuit the goto.
|
|
InstrMap = JumpOptInfo ^ joi_instr_map,
|
|
map.search(InstrMap, TargetLabel, TargetInstr)
|
|
then
|
|
final_dest(InstrMap, TargetLabel, DestLabel,
|
|
TargetInstr, DestInstr),
|
|
DestInstr = llds_instr(UdestInstr, _Destcomment),
|
|
Shorted = "shortcircuited jump: " ++ Comment0,
|
|
opt_util.can_instr_fall_through(UdestInstr) = Canfallthrough,
|
|
(
|
|
Canfallthrough = no,
|
|
NewInstrs0 = [llds_instr(UdestInstr, Shorted)]
|
|
;
|
|
Canfallthrough = yes,
|
|
( if TargetLabel = DestLabel then
|
|
NewInstrs0 = [llds_instr(Uinstr0, Comment0)]
|
|
else
|
|
NewInstrs0 =
|
|
[llds_instr(goto(code_label(DestLabel)), Shorted)]
|
|
)
|
|
),
|
|
LvalMap = JumpOptInfo ^ joi_lval_map,
|
|
( if map.search(LvalMap, DestLabel, yes(Lvalinstr)) then
|
|
adjust_livevals(PrevInstr, [Lvalinstr | NewInstrs0], NewInstrs)
|
|
else
|
|
NewInstrs = NewInstrs0
|
|
),
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
else
|
|
NewRemain = nr_usual_case
|
|
)
|
|
else
|
|
NewRemain = nr_usual_case
|
|
).
|
|
|
|
:- pred jump_opt_if_val(instr::in(instr_if_val), string::in,
|
|
list(instruction)::in, instr::in, jump_opt_info::in,
|
|
maybe_check_nondet_tailcalls::in, maybe_check_nondet_tailcalls::out,
|
|
new_remain::out) is det.
|
|
|
|
jump_opt_if_val(Uinstr0, Comment0, Instrs0, _PrevInstr, JumpOptInfo,
|
|
!CheckedNondetTailCallInfo, NewRemain) :-
|
|
Uinstr0 = if_val(Cond, TargetAddr),
|
|
( if TargetAddr = code_label(TargetLabel) then
|
|
JumpOptInfo = jump_opt_info(InstrMap, BlockMap, _LvalMap,
|
|
_ProcMap, _SdprocMap, _ForkMap, _SuccMap, LayoutLabels,
|
|
Fulljumpopt, _MayAlterRtti),
|
|
( if
|
|
% Attempt to transform code such as
|
|
%
|
|
% if (Cond) L2
|
|
% L1:
|
|
% goto L3
|
|
% L2: ...
|
|
%
|
|
% into
|
|
%
|
|
% if (! Cond) L3
|
|
% L2: ...
|
|
%
|
|
% The label L1 may be present or not. If it is present, we are
|
|
% eliminating it, which is possible only because we short-circuit
|
|
% all jumps to it (make them jump directly to L3). This may not be
|
|
% possible if L3 is a non-label code address; e.g. we cannot jump
|
|
% to non-label code addresses from computed gotos.
|
|
opt_util.skip_comments(Instrs0, Instrs1),
|
|
Instrs1 = [Instr1 | Instrs2],
|
|
( if Instr1 = llds_instr(label(ElimLabel), _) then
|
|
not set_tree234.member(ElimLabel, LayoutLabels),
|
|
opt_util.skip_comments(Instrs2, Instrs3),
|
|
Instrs3 = [GotoInstr | AfterGoto],
|
|
HaveLabel = yes
|
|
else
|
|
Instr1 = GotoInstr,
|
|
AfterGoto = Instrs2,
|
|
HaveLabel = no
|
|
),
|
|
GotoInstr = llds_instr(goto(GotoTarget), GotoComment),
|
|
( HaveLabel = no ; GotoTarget = code_label(_) ),
|
|
opt_util.skip_comments(AfterGoto, AfterGotoComments),
|
|
AfterGotoComments = [LabelInstr | _],
|
|
LabelInstr = llds_instr(label(TargetLabel), _)
|
|
then
|
|
code_util.neg_rval(Cond, NotCond),
|
|
NewInstr = llds_instr(if_val(NotCond, GotoTarget), GotoComment),
|
|
NewInstrs = [],
|
|
% The transformed code may fit the pattern again, so make sure that
|
|
% we look for the pattern again by giving all of the transformed
|
|
% instructions to the recursive call. We can't go into an infinite
|
|
% loop because each application of the transformation strictly
|
|
% reduces the size of the code.
|
|
RemainInstrs = [NewInstr | AfterGoto],
|
|
NewRemain = nr_specified(NewInstrs, RemainInstrs)
|
|
else if
|
|
% Attempt to transform code such as
|
|
%
|
|
% if (Cond) L1
|
|
% goto L2
|
|
%
|
|
% into
|
|
%
|
|
% if (! Cond) L2
|
|
% <code at L1>
|
|
%
|
|
% when we know the code at L1 and don't know the code
|
|
% at L2. Here, we just generate
|
|
%
|
|
% if (! Cond) L2
|
|
% goto L1
|
|
%
|
|
% and get the code processed again starting after the if_val,
|
|
% to get the recursive call to replace the goto to L1
|
|
% with the code at L1.
|
|
Fulljumpopt = yes,
|
|
map.search(BlockMap, TargetLabel, _TargetBlock),
|
|
opt_util.skip_comments(Instrs0, Instrs1),
|
|
Instrs1 = [GotoInstr | AfterGoto],
|
|
GotoInstr = llds_instr(goto(GotoAddr), GotoComment),
|
|
not (
|
|
GotoAddr = code_label(GotoLabel),
|
|
map.search(BlockMap, GotoLabel, _)
|
|
)
|
|
then
|
|
code_util.neg_rval(Cond, NotCond),
|
|
NewIfInstr = llds_instr(if_val(NotCond, GotoAddr), GotoComment),
|
|
NewInstrs = [NewIfInstr],
|
|
NewGotoComment = Comment0 ++ " (switched)",
|
|
NewGotoInstr =
|
|
llds_instr(goto(code_label(TargetLabel)), NewGotoComment),
|
|
RemainInstrs = [NewGotoInstr | AfterGoto],
|
|
NewRemain = nr_specified(NewInstrs, RemainInstrs)
|
|
else if
|
|
map.search(InstrMap, TargetLabel, TargetInstr)
|
|
then
|
|
final_dest(InstrMap, TargetLabel, DestLabel,
|
|
TargetInstr, _DestInstr),
|
|
( if
|
|
% Attempt to transform code such as
|
|
%
|
|
% if (Cond) L1
|
|
% r1 = MR_TRUE
|
|
% <epilog>
|
|
% ...
|
|
% L1:
|
|
% r1 = MR_FALSE
|
|
% <epilog>
|
|
%
|
|
% into
|
|
%
|
|
% r1 = Cond
|
|
% <epilog>
|
|
%
|
|
opt_util.is_sdproceed_next(Instrs0, BetweenFT),
|
|
map.search(BlockMap, DestLabel, Block),
|
|
opt_util.is_sdproceed_next(Block, BetweenBR),
|
|
opt_util.filter_out_r1(BetweenFT, yes(SuccessFT), Between),
|
|
opt_util.filter_out_r1(BetweenBR, yes(SuccessBR), Between),
|
|
(
|
|
SuccessFT = llconst_true,
|
|
SuccessBR = llconst_false,
|
|
code_util.neg_rval(Cond, NewCond)
|
|
;
|
|
SuccessFT = llconst_false,
|
|
SuccessBR = llconst_true,
|
|
NewCond = Cond
|
|
),
|
|
not needs_workaround(reg(reg_r, 1), NewCond)
|
|
then
|
|
( if NewCond = lval(reg(reg_r, 1)) then
|
|
NewAssign = llds_instr(comment("r1 = old r1"), "")
|
|
else
|
|
NewAssign = llds_instr(assign(reg(reg_r, 1), NewCond),
|
|
"shortcircuit bool computation")
|
|
),
|
|
Proceed = llds_instr(goto(code_succip), "shortcircuit"),
|
|
NewInstrs = [NewAssign | Between] ++ [Proceed],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
else if
|
|
% Try to short-circuit the destination.
|
|
TargetLabel \= DestLabel
|
|
then
|
|
Shorted = "shortcircuited jump: " ++ Comment0,
|
|
NewInstrs = [
|
|
llds_instr(if_val(Cond, code_label(DestLabel)), Shorted)
|
|
],
|
|
NewRemain = nr_specified(NewInstrs, Instrs0)
|
|
else
|
|
NewRemain = nr_usual_case
|
|
)
|
|
else
|
|
NewRemain = nr_usual_case
|
|
)
|
|
else
|
|
NewRemain = nr_usual_case
|
|
).
|
|
|
|
:- pred jump_opt_foreign_proc_code(instr::in(instr_foreign_proc_code),
|
|
string::in, list(instruction)::in, instr::in, jump_opt_info::in,
|
|
maybe_check_nondet_tailcalls::in, maybe_check_nondet_tailcalls::out,
|
|
new_remain::out) is det.
|
|
|
|
jump_opt_foreign_proc_code(Uinstr0, Comment0, Instrs0, _PrevInstr,
|
|
JumpOptInfo, !CheckedNondetTailCallInfo, NewRemain) :-
|
|
Uinstr0 = foreign_proc_code(Decls, Components0, MayCallMercury,
|
|
MaybeFixNoLayout, MaybeFixLayout, MaybeFixOnlyLayout,
|
|
MaybeNoFix0, MaybeDefLabel, StackSlotRef, MaybeDup),
|
|
some [!Redirect] (
|
|
InstrMap = JumpOptInfo ^ joi_instr_map,
|
|
list.map_foldl(short_circuit_foreign_proc_component(InstrMap),
|
|
Components0, Components, no, !:Redirect),
|
|
(
|
|
MaybeNoFix0 = yes(NoFix),
|
|
short_circuit_label(InstrMap, NoFix, NoFixDest),
|
|
MaybeNoFix = yes(NoFixDest),
|
|
!:Redirect = yes
|
|
;
|
|
MaybeNoFix0 = no,
|
|
MaybeNoFix = no
|
|
),
|
|
% These sanity checks are too strong, because we don't prohibit labels
|
|
% appearing these slots of foreign_proc_code instructions from appearing
|
|
% in InstrMap; we only prohibit the use of those entries in InstrMap
|
|
% to optimize away these labels.
|
|
%
|
|
% ( if
|
|
% MaybeFixNoLayout = yes(FixNoLayout),
|
|
% short_circuit_label(InstrMap, FixNoLayout, FixNoLayoutDest),
|
|
% FixNoLayoutDest \= FixNoLayout
|
|
% then
|
|
% error("jump_opt_instr_list: foreign_proc_code fix_no_layout")
|
|
% else
|
|
% true
|
|
% ),
|
|
% ( if
|
|
% MaybeFixLayout = yes(FixLayout),
|
|
% short_circuit_label(InstrMap, FixLayout, FixLayoutDest),
|
|
% FixLayoutDest \= FixLayout
|
|
% then
|
|
% error("jump_opt_instr_list: foreign_proc_code fix_layout")
|
|
% else
|
|
% true
|
|
% ),
|
|
% ( if
|
|
% MaybeFixOnlyLayout = yes(FixOnlyLayout),
|
|
% short_circuit_label(InstrMap, FixOnlyLayout, FixOnlyLayoutDest),
|
|
% FixOnlyLayoutDest \= FixOnlyLayout
|
|
% then
|
|
% error("jump_opt_instr_list: foreign_proc_code fix_only_layout")
|
|
% else
|
|
% true
|
|
% ),
|
|
(
|
|
!.Redirect = no,
|
|
NewRemain = nr_usual_case
|
|
;
|
|
!.Redirect = yes,
|
|
Comment = Comment0 ++ " (some redirects)",
|
|
Uinstr = foreign_proc_code(Decls, Components, MayCallMercury,
|
|
MaybeFixNoLayout, MaybeFixLayout, MaybeFixOnlyLayout,
|
|
MaybeNoFix, MaybeDefLabel, StackSlotRef, MaybeDup),
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
NewRemain = nr_specified([Instr], Instrs0)
|
|
)
|
|
).
|
|
|
|
:- func block_may_be_duplicated(list(instruction)) = bool.
|
|
|
|
block_may_be_duplicated([]) = yes.
|
|
block_may_be_duplicated([Instr | Instrs]) = BlockMayBeDuplicated :-
|
|
Instr = llds_instr(Uinstr, _),
|
|
InstrMayBeDuplicated = instr_may_be_duplicated(Uinstr),
|
|
(
|
|
InstrMayBeDuplicated = no,
|
|
BlockMayBeDuplicated = no
|
|
;
|
|
InstrMayBeDuplicated = yes,
|
|
BlockMayBeDuplicated = block_may_be_duplicated(Instrs)
|
|
).
|
|
|
|
:- func instr_may_be_duplicated(instr) = bool.
|
|
|
|
instr_may_be_duplicated(Instr) = InstrMayBeDuplicated :-
|
|
( if Instr ^ fproc_fix_onlylayout = yes(_) then
|
|
% This instruction is a trace event. Duplicating it would
|
|
% increase code size, and may cost more in locality than
|
|
% the benefit represented by the elimination of the jump.
|
|
% When debugging is enabled, size is in any case more important
|
|
% than the last bit of speed.
|
|
InstrMayBeDuplicated = no
|
|
else if Instr ^ fproc_maybe_dupl = proc_may_not_duplicate then
|
|
InstrMayBeDuplicated = no
|
|
else
|
|
InstrMayBeDuplicated = yes
|
|
).
|
|
|
|
:- func redirect_comment(string) = string.
|
|
|
|
redirect_comment(Comment) = Comment ++ " (redirected return)".
|
|
|
|
% We avoid generating statements that redefine the value of a location
|
|
% by comparing its old contents for non-equality with zero.
|
|
%
|
|
% The reason is that code such as r1 = !r1 causes gcc 2.7 on SPARCs to
|
|
% abort with an internal error.
|
|
%
|
|
% Apparently this is the only place where the Mercury compiler generates
|
|
% assignments like that, otherwise we might need a more general work-around
|
|
% that worked for code generated by other parts of the compiler as well.
|
|
%
|
|
% (It is likely that the problem would occur if bool_not was ever inlined
|
|
% into a procedure where the value being complemented was already known to
|
|
% be false.)
|
|
%
|
|
:- pred needs_workaround(lval::in, rval::in) is semidet.
|
|
|
|
needs_workaround(Lval, Cond) :-
|
|
(
|
|
Cond = unop(logical_not, lval(Lval))
|
|
;
|
|
Cond = binop(Op, Left, Right),
|
|
( Op = eq(_) ; Op = ne(_) ),
|
|
(
|
|
Right = lval(Lval),
|
|
( Left = const(llconst_int(0))
|
|
; Left = mkword(ptag(0u8), unop(mkbody, const(llconst_int(0))))
|
|
)
|
|
;
|
|
Left = lval(Lval),
|
|
( Right = const(llconst_int(0))
|
|
; Right = mkword(ptag(0u8), unop(mkbody, const(llconst_int(0))))
|
|
)
|
|
)
|
|
).
|
|
|
|
:- pred adjust_livevals(instr::in, list(instruction)::in,
|
|
list(instruction)::out) is det.
|
|
|
|
adjust_livevals(PrevInstr, Instrs0, Instrs) :-
|
|
( if
|
|
PrevInstr = livevals(PrevLivevals),
|
|
opt_util.skip_comments(Instrs0, Instrs1),
|
|
Instrs1 = [llds_instr(livevals(BetweenLivevals), _) | Instrs2]
|
|
then
|
|
( if BetweenLivevals = PrevLivevals then
|
|
Instrs = Instrs2
|
|
else
|
|
unexpected($pred, "BetweenLivevals and PrevLivevals differ")
|
|
)
|
|
else
|
|
Instrs = Instrs0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Short-circuit the given label by following any gotos at the
|
|
% labelled instruction or by falling through consecutive labels.
|
|
%
|
|
:- pred short_circuit_label(instrmap::in, label::in, label::out) is det.
|
|
|
|
short_circuit_label(InstrMap, Label0, Label) :-
|
|
( if map.search(InstrMap, Label0, Instr0) then
|
|
final_dest(InstrMap, Label0, Label, Instr0, _Instr)
|
|
else
|
|
Label = Label0
|
|
).
|
|
|
|
% Find the final destination of a given instruction at a given label.
|
|
% We follow gotos as well as consecutive labels.
|
|
%
|
|
:- pred final_dest(instrmap::in, label::in, label::out, instruction::in,
|
|
instruction::out) is det.
|
|
|
|
final_dest(InstrMap, SrcLabel, DestLabel, SrcInstr, DestInstr) :-
|
|
final_dest_loop(InstrMap, [], SrcLabel, DestLabel, SrcInstr, DestInstr).
|
|
|
|
:- pred final_dest_loop(instrmap::in, list(label)::in,
|
|
label::in, label::out, instruction::in, instruction::out) is det.
|
|
|
|
final_dest_loop(InstrMap, LabelsSofar, SrcLabel, DestLabel,
|
|
SrcInstr, DestInstr) :-
|
|
( if
|
|
SrcInstr = llds_instr(SrcUinstr, _Comment),
|
|
(
|
|
SrcUinstr = goto(code_label(TargetLabel))
|
|
;
|
|
SrcUinstr = label(TargetLabel)
|
|
),
|
|
map.search(InstrMap, TargetLabel, TargetInstr),
|
|
not list.member(SrcLabel, LabelsSofar)
|
|
then
|
|
final_dest_loop(InstrMap, [SrcLabel | LabelsSofar],
|
|
TargetLabel, DestLabel, TargetInstr, DestInstr)
|
|
else
|
|
DestLabel = SrcLabel,
|
|
DestInstr = SrcInstr
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred short_circuit_maybe_labels(instrmap::in,
|
|
list(maybe(label))::in, list(maybe(label))::out) is det.
|
|
|
|
short_circuit_maybe_labels(_InstrMap, [], []).
|
|
short_circuit_maybe_labels(InstrMap, [MaybeLabel0 | MaybeLabels0],
|
|
[MaybeLabel | MaybeLabels]) :-
|
|
(
|
|
MaybeLabel0 = yes(Label0),
|
|
short_circuit_label(InstrMap, Label0, Label),
|
|
MaybeLabel = yes(Label)
|
|
;
|
|
MaybeLabel0 = no,
|
|
MaybeLabel = no
|
|
),
|
|
short_circuit_maybe_labels(InstrMap, MaybeLabels0, MaybeLabels).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred short_circuit_labels_rval(instrmap::in, rval::in, rval::out) is det.
|
|
|
|
short_circuit_labels_rval(InstrMap, Rval0, Rval) :-
|
|
(
|
|
Rval0 = lval(Lval0),
|
|
short_circuit_labels_lval(InstrMap, Lval0, Lval),
|
|
Rval = lval(Lval)
|
|
;
|
|
Rval0 = var(_),
|
|
unexpected($pred, "var")
|
|
;
|
|
Rval0 = mkword(Tag, SubRval0),
|
|
short_circuit_labels_rval(InstrMap, SubRval0, SubRval),
|
|
Rval = mkword(Tag, SubRval)
|
|
;
|
|
Rval0 = const(Const0),
|
|
short_circuit_labels_const(InstrMap, Const0, Const),
|
|
Rval = const(Const)
|
|
;
|
|
Rval0 = cast(Type, SubRval0),
|
|
short_circuit_labels_rval(InstrMap, SubRval0, SubRval),
|
|
Rval = cast(Type, SubRval)
|
|
;
|
|
Rval0 = unop(UnOp, SubRval0),
|
|
short_circuit_labels_rval(InstrMap, SubRval0, SubRval),
|
|
Rval = unop(UnOp, SubRval)
|
|
;
|
|
Rval0 = binop(BinOp, LRval0, RRval0),
|
|
short_circuit_labels_rval(InstrMap, LRval0, LRval),
|
|
short_circuit_labels_rval(InstrMap, RRval0, RRval),
|
|
Rval = binop(BinOp, LRval, RRval)
|
|
;
|
|
( Rval0 = mkword_hole(_)
|
|
; Rval0 = mem_addr(_)
|
|
),
|
|
Rval = Rval0
|
|
).
|
|
|
|
:- pred short_circuit_labels_const(instrmap::in,
|
|
rval_const::in, rval_const::out) is det.
|
|
|
|
short_circuit_labels_const(InstrMap, RvalConst0, RvalConst) :-
|
|
(
|
|
( RvalConst0 = llconst_true
|
|
; RvalConst0 = llconst_false
|
|
; RvalConst0 = llconst_int(_I)
|
|
; RvalConst0 = llconst_uint(_U)
|
|
; RvalConst0 = llconst_int8(_I8)
|
|
; RvalConst0 = llconst_uint8(_U8)
|
|
; RvalConst0 = llconst_int16(_I16)
|
|
; RvalConst0 = llconst_uint16(_U16)
|
|
; RvalConst0 = llconst_int32(_I32)
|
|
; RvalConst0 = llconst_uint32(_U32)
|
|
; RvalConst0 = llconst_int64(_I64)
|
|
; RvalConst0 = llconst_uint64(_U64)
|
|
; RvalConst0 = llconst_foreign(_V, _T)
|
|
; RvalConst0 = llconst_float(_F)
|
|
; RvalConst0 = llconst_string(_S)
|
|
; RvalConst0 = llconst_multi_string(_S)
|
|
; RvalConst0 = llconst_data_addr(_D, _O)
|
|
),
|
|
RvalConst = RvalConst0
|
|
;
|
|
RvalConst0 = llconst_code_addr(CodeAddr0),
|
|
( if CodeAddr0 = code_label(Label0) then
|
|
short_circuit_label(InstrMap, Label0, Label),
|
|
CodeAddr = code_label(Label)
|
|
else
|
|
CodeAddr = CodeAddr0
|
|
),
|
|
RvalConst = llconst_code_addr(CodeAddr)
|
|
).
|
|
|
|
% Not currently needed.
|
|
%
|
|
% :- pred short_circuit_labels_maybe_rvals(instrmap::in, list(maybe(rval))::in,
|
|
% list(maybe(rval))::out) is det.
|
|
%
|
|
% short_circuit_labels_maybe_rvals(_, [], []).
|
|
% short_circuit_labels_maybe_rvals(InstrMap, [MaybeRval0 | MaybeRvals0],
|
|
% [MaybeRval | MaybeRvals]) :-
|
|
% short_circuit_labels_maybe_rval(InstrMap, MaybeRval0, MaybeRval),
|
|
% short_circuit_labels_maybe_rvals(InstrMap, MaybeRvals0, MaybeRvals).
|
|
%
|
|
% :- pred short_circuit_labels_maybe_rval(instrmap::in,
|
|
% maybe(rval)::in, maybe(rval)::out) is det.
|
|
%
|
|
% short_circuit_labels_maybe_rval(InstrMap, MaybeRval0, MaybeRval) :-
|
|
% (
|
|
% MaybeRval0 = no,
|
|
% MaybeRval = no
|
|
% ;
|
|
% MaybeRval0 = yes(Rval0),
|
|
% short_circuit_labels_rval(InstrMap, Rval0, Rval),
|
|
% MaybeRval = yes(Rval)
|
|
% ).
|
|
|
|
:- pred short_circuit_labels_lval(instrmap::in, lval::in, lval::out) is det.
|
|
|
|
short_circuit_labels_lval(InstrMap, Lval0, Lval) :-
|
|
(
|
|
( Lval0 = reg(_T, _N)
|
|
; Lval0 = succip
|
|
; Lval0 = maxfr
|
|
; Lval0 = curfr
|
|
; Lval0 = hp
|
|
; Lval0 = sp
|
|
; Lval0 = parent_sp
|
|
; Lval0 = temp(_T, _N)
|
|
; Lval0 = stackvar(_N)
|
|
; Lval0 = parent_stackvar(_N)
|
|
; Lval0 = framevar(_N)
|
|
; Lval0 = double_stackvar(_Type, _N)
|
|
; Lval0 = global_var_ref(_Var)
|
|
),
|
|
Lval = Lval0
|
|
;
|
|
Lval0 = succip_slot(Rval0),
|
|
short_circuit_labels_rval(InstrMap, Rval0, Rval),
|
|
Lval = succip_slot(Rval)
|
|
;
|
|
Lval0 = redoip_slot(Rval0),
|
|
short_circuit_labels_rval(InstrMap, Rval0, Rval),
|
|
Lval = redoip_slot(Rval)
|
|
;
|
|
Lval0 = redofr_slot(Rval0),
|
|
short_circuit_labels_rval(InstrMap, Rval0, Rval),
|
|
Lval = redofr_slot(Rval)
|
|
;
|
|
Lval0 = succfr_slot(Rval0),
|
|
short_circuit_labels_rval(InstrMap, Rval0, Rval),
|
|
Lval = succfr_slot(Rval)
|
|
;
|
|
Lval0 = prevfr_slot(Rval0),
|
|
short_circuit_labels_rval(InstrMap, Rval0, Rval),
|
|
Lval = prevfr_slot(Rval)
|
|
;
|
|
Lval0 = field(Tag, Rval0, Field0),
|
|
short_circuit_labels_rval(InstrMap, Rval0, Rval),
|
|
short_circuit_labels_rval(InstrMap, Field0, Field),
|
|
Lval = field(Tag, Rval, Field)
|
|
;
|
|
Lval0 = mem_ref(Rval0),
|
|
short_circuit_labels_rval(InstrMap, Rval0, Rval),
|
|
Lval = mem_ref(Rval)
|
|
;
|
|
Lval0 = lvar(_),
|
|
unexpected($pred, "lvar")
|
|
).
|
|
|
|
:- pred short_circuit_foreign_proc_component(instrmap::in,
|
|
foreign_proc_component::in, foreign_proc_component::out,
|
|
bool::in, bool::out) is det.
|
|
|
|
short_circuit_foreign_proc_component(InstrMap, !Component, !Redirect) :-
|
|
(
|
|
!.Component = foreign_proc_fail_to(Label0),
|
|
short_circuit_label(InstrMap, Label0, Label),
|
|
!:Component = foreign_proc_fail_to(Label),
|
|
( if Label = Label0 then
|
|
true
|
|
else
|
|
!:Redirect = yes
|
|
)
|
|
;
|
|
( !.Component = foreign_proc_inputs(_)
|
|
; !.Component = foreign_proc_outputs(_)
|
|
; !.Component = foreign_proc_user_code(_, _, _)
|
|
; !.Component = foreign_proc_raw_code(_, _, _, _)
|
|
; !.Component = foreign_proc_alloc_id(_)
|
|
; !.Component = foreign_proc_noop
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module ll_backend.jumpopt.
|
|
%-----------------------------------------------------------------------------%
|