mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-16 14:25:56 +00:00
Estimated hours taken: 2 Branches: main compiler/*.m: Import only one compiler module per line. Sort the blocks of imports. This makes it easier to merge in changes. In a couple of places, remove unnecessary imports.
892 lines
31 KiB
Mathematica
892 lines
31 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2003 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.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% jumpopt.m - optimize jumps to jumps.
|
|
%
|
|
% Author: zs.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend__jumpopt.
|
|
|
|
:- interface.
|
|
|
|
:- import_module backend_libs__proc_label.
|
|
:- import_module ll_backend__llds.
|
|
|
|
:- import_module list, set, bool, counter.
|
|
|
|
% Take an instruction list and optimize jumps. This includes jumps
|
|
% implicit in procedure returns.
|
|
%
|
|
% The second argument 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. The third argument says whether we are allowed to perform
|
|
% optimizations that may interfere with RTTI.
|
|
%
|
|
% The final four bool inputs should be
|
|
%
|
|
% - the value of the --optimize-fulljumps option,
|
|
%
|
|
% - an indication of whether this is the final application of this
|
|
% optimization,
|
|
%
|
|
% - the value of the --pessimize-tailcalls option, and
|
|
%
|
|
% - the value of the --checked-nondet-tailcalls option respectively.
|
|
%
|
|
% The bool output says whether the instruction sequence was modified
|
|
% by the optimization.
|
|
|
|
:- pred jumpopt_main(list(instruction)::in, set(label)::in, may_alter_rtti::in,
|
|
proc_label::in, counter::in, counter::out,
|
|
bool::in, bool::in, bool::in, bool::in,
|
|
list(instruction)::out, bool::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs__builtin_ops.
|
|
:- import_module ll_backend__code_util.
|
|
:- import_module ll_backend__opt_util.
|
|
|
|
:- import_module int, std_util, map, string, require.
|
|
|
|
% 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 the success or failure epilog
|
|
% depending on the result) to the epilog.
|
|
%
|
|
% If we are not doing full jump optimization, Blockmap will be empty.
|
|
% Even with full jump optimization, Blockmap will not contain the initial
|
|
% block of the procedure unless Recjump is set. The intention is that
|
|
% Recjump will not be set until optimizations such as frameopt and value
|
|
% numbering, which can do a better job of optimizing this block, have
|
|
% been applied.
|
|
|
|
jumpopt_main(Instrs0, LayoutLabels, MayAlterRtti, ProcLabel, C0, C,
|
|
Blockopt, Recjump, PessimizeTailCalls, CheckedNondetTailCall,
|
|
Instrs, Mod) :-
|
|
map__init(Instrmap0),
|
|
map__init(Lvalmap0),
|
|
map__init(Procmap0),
|
|
map__init(Sdprocmap0),
|
|
map__init(Succmap0),
|
|
map__init(Blockmap0),
|
|
jumpopt__build_maps(Instrs0, Blockopt, Recjump, Instrmap0, Instrmap,
|
|
Blockmap0, Blockmap, Lvalmap0, Lvalmap,
|
|
Procmap0, Procmap1, Sdprocmap0, Sdprocmap1,
|
|
Succmap0, Succmap1),
|
|
map__init(Forkmap0),
|
|
jumpopt__build_forkmap(Instrs0, Sdprocmap1, Forkmap0, Forkmap1),
|
|
(
|
|
PessimizeTailCalls = no,
|
|
Procmap = Procmap1,
|
|
Sdprocmap = Sdprocmap1,
|
|
Succmap = Succmap1,
|
|
Forkmap = Forkmap1
|
|
;
|
|
PessimizeTailCalls = yes,
|
|
Procmap = map__init,
|
|
Sdprocmap = map__init,
|
|
Succmap = map__init,
|
|
Forkmap = map__init
|
|
),
|
|
(
|
|
CheckedNondetTailCall = yes,
|
|
CheckedNondetTailCallInfo0 = yes(ProcLabel - C0),
|
|
jumpopt__instr_list(Instrs0, comment(""), Instrmap, Blockmap,
|
|
Lvalmap, Procmap, Sdprocmap, Forkmap, Succmap,
|
|
LayoutLabels, MayAlterRtti, CheckedNondetTailCallInfo0,
|
|
CheckedNondetTailCallInfo, Instrs1),
|
|
( CheckedNondetTailCallInfo = yes(_ - Cprime) ->
|
|
C = Cprime
|
|
;
|
|
error("jumpopt_main: lost the next label number")
|
|
)
|
|
;
|
|
CheckedNondetTailCall = no,
|
|
CheckedNondetTailCallInfo0 = no,
|
|
jumpopt__instr_list(Instrs0, comment(""), Instrmap, Blockmap,
|
|
Lvalmap, Procmap, Sdprocmap, Forkmap, Succmap,
|
|
LayoutLabels, MayAlterRtti, CheckedNondetTailCallInfo0,
|
|
_, Instrs1),
|
|
C = C0
|
|
),
|
|
opt_util__filter_out_bad_livevals(Instrs1, Instrs),
|
|
( Instrs = Instrs0 ->
|
|
Mod = no
|
|
;
|
|
Mod = yes
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred jumpopt__build_maps(list(instruction), bool, bool,
|
|
instrmap, instrmap, tailmap, tailmap, lvalmap, lvalmap,
|
|
tailmap, tailmap, tailmap, tailmap, tailmap, tailmap).
|
|
% :- mode jumpopt__build_maps(in, in, in, di, uo, di, uo, di, uo, di, uo,
|
|
% di, uo, di, uo) is det.
|
|
:- mode jumpopt__build_maps(in, in, in, in, out, in, out, in, out, in, out,
|
|
in, out, in, out) is det.
|
|
|
|
jumpopt__build_maps([], _, _,
|
|
Instrmap, Instrmap, Blockmap, Blockmap, Lvalmap, Lvalmap,
|
|
Procmap, Procmap, Sdprocmap, Sdprocmap, Succmap, Succmap).
|
|
jumpopt__build_maps([Instr0 | Instrs0], Blockopt, Recjump, Instrmap0, Instrmap,
|
|
Blockmap0, Blockmap, Lvalmap0, Lvalmap,
|
|
Procmap0, Procmap, Sdprocmap0, Sdprocmap, Succmap0, Succmap) :-
|
|
Instr0 = Uinstr0 - _,
|
|
( Uinstr0 = label(Label) ->
|
|
opt_util__skip_comments(Instrs0, Instrs1),
|
|
( Instrs1 = [Instr1 | _], Instr1 = livevals(_) - _ ->
|
|
map__det_insert(Lvalmap0, Label, yes(Instr1), Lvalmap1)
|
|
;
|
|
map__det_insert(Lvalmap0, Label, no, Lvalmap1)
|
|
),
|
|
opt_util__skip_comments_livevals(Instrs1, Instrs2),
|
|
( Instrs2 = [Instr2 | _] ->
|
|
map__det_insert(Instrmap0, Label, Instr2, Instrmap1)
|
|
;
|
|
Instrmap1 = Instrmap0
|
|
),
|
|
( opt_util__is_proceed_next(Instrs1, Between1) ->
|
|
map__det_insert(Procmap0, Label, Between1, Procmap1)
|
|
;
|
|
Procmap1 = Procmap0
|
|
),
|
|
( opt_util__is_sdproceed_next(Instrs1, Between2) ->
|
|
map__det_insert(Sdprocmap0, Label, Between2, Sdprocmap1)
|
|
;
|
|
Sdprocmap1 = Sdprocmap0
|
|
),
|
|
( opt_util__is_succeed_next(Instrs1, Between3) ->
|
|
map__det_insert(Succmap0, Label, Between3, Succmap1)
|
|
;
|
|
Succmap1 = Succmap0
|
|
),
|
|
% put the start of the procedure into Blockmap
|
|
% only after frameopt and value_number have had a shot at it
|
|
( Blockopt = yes, ( Label = local(_, _) ; Recjump = yes ) ->
|
|
opt_util__find_no_fallthrough(Instrs1, Block),
|
|
map__det_insert(Blockmap0, Label, Block, Blockmap1)
|
|
;
|
|
Blockmap1 = Blockmap0
|
|
)
|
|
;
|
|
Instrmap1 = Instrmap0,
|
|
Blockmap1 = Blockmap0,
|
|
Lvalmap1 = Lvalmap0,
|
|
Procmap1 = Procmap0,
|
|
Sdprocmap1 = Sdprocmap0,
|
|
Succmap1 = Succmap0
|
|
),
|
|
jumpopt__build_maps(Instrs0, Blockopt, Recjump, Instrmap1, Instrmap,
|
|
Blockmap1, Blockmap, Lvalmap1, Lvalmap,
|
|
Procmap1, Procmap, Sdprocmap1, Sdprocmap, Succmap1, Succmap).
|
|
|
|
% Find labels followed by a test of r1 where both paths set r1 to
|
|
% its original value and proceed.
|
|
|
|
:- pred jumpopt__build_forkmap(list(instruction), tailmap, tailmap, tailmap).
|
|
% :- mode jumpopt__build_forkmap(in, in, di, uo) is det.
|
|
:- mode jumpopt__build_forkmap(in, in, in, out) is det.
|
|
|
|
jumpopt__build_forkmap([], _Sdprocmap, Forkmap, Forkmap).
|
|
jumpopt__build_forkmap([Instr - _Comment|Instrs], Sdprocmap,
|
|
Forkmap0, Forkmap) :-
|
|
(
|
|
Instr = label(Label),
|
|
opt_util__is_forkproceed_next(Instrs, Sdprocmap, Between)
|
|
->
|
|
map__det_insert(Forkmap0, Label, Between, Forkmap1)
|
|
;
|
|
Forkmap1 = Forkmap0
|
|
),
|
|
jumpopt__build_forkmap(Instrs, Sdprocmap, Forkmap1, Forkmap).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% 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.
|
|
|
|
:- pred jumpopt__instr_list(list(instruction), instr, instrmap, tailmap,
|
|
lvalmap, tailmap, tailmap, tailmap, tailmap, set(label),
|
|
may_alter_rtti, maybe(pair(proc_label, counter)),
|
|
maybe(pair(proc_label, counter)), list(instruction)).
|
|
:- mode jumpopt__instr_list(in, in, in, in, in, in, in, in, in, in, in,
|
|
in, out, out) is det.
|
|
|
|
jumpopt__instr_list([], _PrevInstr, _Instrmap, _Blockmap, _Lvalmap,
|
|
_Procmap, _Sdprocmap, _Forkmap, _Succmap, _LayoutLabels,
|
|
_, CheckedNondetTailCallInfo, CheckedNondetTailCallInfo, []).
|
|
jumpopt__instr_list([Instr0 | Instrs0], PrevInstr, Instrmap, Blockmap,
|
|
Lvalmap, Procmap, Sdprocmap, Forkmap, Succmap, LayoutLabels,
|
|
MayAlterRtti, CheckedNondetTailCallInfo0,
|
|
CheckedNondetTailCallInfo, Instrs) :-
|
|
Instr0 = Uinstr0 - Comment0,
|
|
(
|
|
Uinstr0 = call(Proc, label(RetLabel), LiveInfos, Context,
|
|
GoalPath, CallModel)
|
|
->
|
|
(
|
|
% 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 = det ; CallModel = semidet ),
|
|
map__search(Procmap, RetLabel, Between0),
|
|
PrevInstr = livevals(Livevals),
|
|
MayAlterRtti = may_alter_rtti,
|
|
not set__member(RetLabel, LayoutLabels)
|
|
->
|
|
opt_util__filter_out_livevals(Between0, Between1),
|
|
list__append(Between1, [livevals(Livevals) - "",
|
|
goto(Proc) - redirect_comment(Comment0)],
|
|
NewInstrs),
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
;
|
|
% Look for semidet style tailcalls.
|
|
CallModel = semidet,
|
|
map__search(Forkmap, RetLabel, Between),
|
|
PrevInstr = livevals(Livevals),
|
|
MayAlterRtti = may_alter_rtti,
|
|
not set__member(RetLabel, LayoutLabels)
|
|
->
|
|
list__append(Between, [livevals(Livevals) - "",
|
|
goto(Proc) - redirect_comment(Comment0)],
|
|
NewInstrs),
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
;
|
|
% Look for nondet style tailcalls which do not need
|
|
% a runtime check.
|
|
CallModel = nondet(unchecked_tail_call),
|
|
map__search(Succmap, RetLabel, BetweenIncl),
|
|
BetweenIncl = [livevals(_) - _, goto(_) - _],
|
|
PrevInstr = livevals(Livevals),
|
|
MayAlterRtti = may_alter_rtti,
|
|
not set__member(RetLabel, LayoutLabels)
|
|
->
|
|
NewInstrs = [
|
|
assign(maxfr, lval(prevfr(lval(curfr))))
|
|
- "discard this frame",
|
|
assign(succip, lval(succip(lval(curfr))))
|
|
- "setup PC on return from tailcall",
|
|
assign(curfr, lval(succfr(lval(curfr))))
|
|
- "setup curfr on return from tailcall",
|
|
livevals(Livevals) - "",
|
|
goto(Proc) - redirect_comment(Comment0)
|
|
],
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
;
|
|
% Look for nondet style tailcalls which do need
|
|
% a runtime check.
|
|
CallModel = nondet(checked_tail_call),
|
|
CheckedNondetTailCallInfo0 =
|
|
yes(ProcLabel - Counter0),
|
|
map__search(Succmap, RetLabel, BetweenIncl),
|
|
BetweenIncl = [livevals(_) - _, goto(_) - _],
|
|
PrevInstr = livevals(Livevals),
|
|
MayAlterRtti = may_alter_rtti,
|
|
not set__member(RetLabel, LayoutLabels)
|
|
->
|
|
counter__allocate(LabelNum, Counter0, Counter1),
|
|
NewLabel = local(LabelNum, ProcLabel),
|
|
NewInstrs = [
|
|
if_val(binop(ne, lval(curfr), lval(maxfr)),
|
|
label(NewLabel))
|
|
- "branch around if cannot tail call",
|
|
assign(maxfr, lval(prevfr(lval(curfr))))
|
|
- "discard this frame",
|
|
assign(succip, lval(succip(lval(curfr))))
|
|
- "setup PC on return from tailcall",
|
|
assign(curfr, lval(succfr(lval(curfr))))
|
|
- "setup curfr on return from tailcall",
|
|
livevals(Livevals) - "",
|
|
goto(Proc) - redirect_comment(Comment0),
|
|
label(NewLabel) - "non tail call",
|
|
Instr0
|
|
],
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = yes(ProcLabel - Counter1)
|
|
;
|
|
% Short circuit the return label if possible.
|
|
map__search(Instrmap, RetLabel, RetInstr),
|
|
MayAlterRtti = may_alter_rtti,
|
|
not set__member(RetLabel, LayoutLabels)
|
|
->
|
|
jumpopt__final_dest(RetLabel, RetInstr, Instrmap,
|
|
DestLabel, _DestInstr),
|
|
( RetLabel = DestLabel ->
|
|
NewInstrs = [Instr0],
|
|
RemainInstrs = Instrs0
|
|
;
|
|
NewInstrs = [call(Proc, label(DestLabel),
|
|
LiveInfos, Context, GoalPath,
|
|
CallModel)
|
|
- redirect_comment(Comment0)],
|
|
RemainInstrs = Instrs0
|
|
),
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
;
|
|
NewInstrs = [Instr0],
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
)
|
|
;
|
|
Uinstr0 = goto(label(TargetLabel))
|
|
->
|
|
(
|
|
% Eliminate the goto if possible.
|
|
opt_util__is_this_label_next(TargetLabel, Instrs0, _)
|
|
->
|
|
NewInstrs = [],
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
;
|
|
PrevInstr = if_val(_, label(IfTargetLabel)),
|
|
opt_util__is_this_label_next(IfTargetLabel, Instrs0, _)
|
|
->
|
|
% 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 = [Instr0],
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
;
|
|
% Replace a jump to a det epilog with the epilog.
|
|
map__search(Procmap, TargetLabel, Between0)
|
|
->
|
|
jumpopt__adjust_livevals(PrevInstr, Between0, Between),
|
|
list__append(Between, [goto(succip) - "shortcircuit"],
|
|
NewInstrs),
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
;
|
|
% Replace a jump to a semidet epilog with the epilog.
|
|
map__search(Sdprocmap, TargetLabel, Between0)
|
|
->
|
|
jumpopt__adjust_livevals(PrevInstr, Between0, Between),
|
|
list__append(Between, [goto(succip) - "shortcircuit"],
|
|
NewInstrs),
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
;
|
|
% Replace a jump to a nondet epilog with the epilog.
|
|
map__search(Succmap, TargetLabel, BetweenIncl0)
|
|
->
|
|
jumpopt__adjust_livevals(PrevInstr, BetweenIncl0,
|
|
NewInstrs),
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
;
|
|
% 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 (if it wasn't, Blockmap will be empty).
|
|
% 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.
|
|
map__search(Instrmap, TargetLabel, TargetInstr),
|
|
jumpopt__final_dest(TargetLabel, TargetInstr,
|
|
Instrmap, DestLabel, _DestInstr),
|
|
map__search(Blockmap, DestLabel, Block)
|
|
->
|
|
opt_util__filter_out_labels(Block, FilteredBlock),
|
|
jumpopt__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(Blockmap, DestLabel, CrippledBlockmap),
|
|
jumpopt__instr_list(AdjustedBlock, comment(""),
|
|
Instrmap, CrippledBlockmap, Lvalmap, Procmap,
|
|
Sdprocmap, Forkmap, Succmap, LayoutLabels,
|
|
MayAlterRtti, CheckedNondetTailCallInfo0,
|
|
CheckedNondetTailCallInfo1, NewInstrs),
|
|
RemainInstrs = Instrs0
|
|
;
|
|
% Short-circuit the goto.
|
|
map__search(Instrmap, TargetLabel, TargetInstr)
|
|
->
|
|
jumpopt__final_dest(TargetLabel, TargetInstr,
|
|
Instrmap, DestLabel, DestInstr),
|
|
DestInstr = UdestInstr - _Destcomment,
|
|
string__append("shortcircuited jump: ",
|
|
Comment0, Shorted),
|
|
opt_util__can_instr_fall_through(UdestInstr,
|
|
Canfallthrough),
|
|
( Canfallthrough = no ->
|
|
NewInstrs0 = [UdestInstr - Shorted],
|
|
RemainInstrs = Instrs0
|
|
;
|
|
( TargetLabel = DestLabel ->
|
|
NewInstrs0 = [Instr0],
|
|
RemainInstrs = Instrs0
|
|
;
|
|
NewInstrs0 = [goto(label(DestLabel))
|
|
- Shorted],
|
|
RemainInstrs = Instrs0
|
|
)
|
|
),
|
|
( map__search(Lvalmap, DestLabel, yes(Lvalinstr)) ->
|
|
jumpopt__adjust_livevals(PrevInstr,
|
|
[Lvalinstr | NewInstrs0], NewInstrs)
|
|
;
|
|
NewInstrs = NewInstrs0
|
|
),
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
;
|
|
NewInstrs = [Instr0],
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
)
|
|
; Uinstr0 = computed_goto(Index, LabelList0) ->
|
|
% Short-circuit all the destination labels.
|
|
jumpopt__short_labels(LabelList0, Instrmap, LabelList),
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0,
|
|
( LabelList = LabelList0 ->
|
|
NewInstrs = [Instr0]
|
|
;
|
|
string__append(Comment0, " (some shortcircuits)",
|
|
Shorted),
|
|
NewInstrs = [computed_goto(Index, LabelList) - Shorted]
|
|
)
|
|
; Uinstr0 = if_val(Cond, label(TargetLabel)) ->
|
|
(
|
|
% 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 can short-circuit 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],
|
|
( Instr1 = label(_) - _ ->
|
|
opt_util__skip_comments(Instrs2, Instrs3),
|
|
Instrs3 = [GotoInstr | AfterGoto],
|
|
HaveLabel = yes
|
|
;
|
|
Instr1 = GotoInstr,
|
|
AfterGoto = Instrs2,
|
|
HaveLabel = no
|
|
),
|
|
GotoInstr = goto(GotoTarget) - GotoComment,
|
|
( HaveLabel = no ; GotoTarget = label(_) ),
|
|
opt_util__skip_comments(AfterGoto, AfterGotoComments),
|
|
AfterGotoComments = [LabelInstr | _],
|
|
LabelInstr = label(TargetLabel) - _
|
|
->
|
|
code_util__neg_rval(Cond, NotCond),
|
|
NewInstr = 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],
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
;
|
|
map__search(Instrmap, TargetLabel, TargetInstr)
|
|
->
|
|
jumpopt__final_dest(TargetLabel, TargetInstr,
|
|
Instrmap, DestLabel, _DestInstr),
|
|
(
|
|
% 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 = true,
|
|
SuccessBR = false,
|
|
code_util__neg_rval(Cond, NewCond)
|
|
;
|
|
SuccessFT = false,
|
|
SuccessBR = true,
|
|
NewCond = Cond
|
|
),
|
|
\+ needs_workaround(reg(r, 1), NewCond)
|
|
->
|
|
( NewCond = lval(reg(r, 1)) ->
|
|
NewAssign = comment("r1 = old r1") - ""
|
|
;
|
|
NewAssign = assign(reg(r, 1), NewCond) -
|
|
"shortcircuit bool computation"
|
|
),
|
|
Proceed = goto(succip) - "shortcircuit",
|
|
list__append([NewAssign | Between], [Proceed],
|
|
NewInstrs),
|
|
RemainInstrs = Instrs0
|
|
;
|
|
% Try to short-circuit the destination.
|
|
|
|
TargetLabel \= DestLabel
|
|
->
|
|
string__append("shortcircuited jump: ",
|
|
Comment0, Shorted),
|
|
NewInstrs = [if_val(Cond, label(DestLabel))
|
|
- Shorted],
|
|
RemainInstrs = Instrs0
|
|
;
|
|
NewInstrs = [Instr0],
|
|
RemainInstrs = Instrs0
|
|
),
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
;
|
|
NewInstrs = [Instr0],
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
)
|
|
; Uinstr0 = assign(Lval, Rval0) ->
|
|
% Any labels mentioned in Rval0 should be short-circuited.
|
|
jumpopt__short_labels_rval(Rval0, Instrmap, Rval),
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0,
|
|
( Rval = Rval0 ->
|
|
NewInstrs = [Instr0]
|
|
;
|
|
string__append(Comment0, " (some shortcircuits)",
|
|
Shorted),
|
|
NewInstrs = [assign(Lval, Rval) - Shorted]
|
|
)
|
|
; Uinstr0 = mkframe(FrameInfo, label(Label0)) ->
|
|
jumpopt__short_label(Label0, Instrmap, Label),
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0,
|
|
( Label = Label0 ->
|
|
NewInstrs = [Instr0]
|
|
;
|
|
string__append(Comment0, " (some shortcircuits)",
|
|
Shorted),
|
|
NewInstrs = [mkframe(FrameInfo, label(Label))
|
|
- Shorted]
|
|
)
|
|
;
|
|
NewInstrs = [Instr0],
|
|
RemainInstrs = Instrs0,
|
|
CheckedNondetTailCallInfo1 = CheckedNondetTailCallInfo0
|
|
),
|
|
( ( Uinstr0 = comment(_) ; NewInstrs = [] ) ->
|
|
NewPrevInstr = PrevInstr
|
|
;
|
|
NewPrevInstr = Uinstr0
|
|
),
|
|
jumpopt__instr_list(RemainInstrs, NewPrevInstr, Instrmap, Blockmap,
|
|
Lvalmap, Procmap, Sdprocmap, Forkmap, Succmap, LayoutLabels,
|
|
MayAlterRtti, CheckedNondetTailCallInfo1,
|
|
CheckedNondetTailCallInfo, Instrs9),
|
|
list__append(NewInstrs, Instrs9, Instrs).
|
|
|
|
:- func redirect_comment(string) = string.
|
|
redirect_comment(Comment0) = string__append(Comment0, " (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, rval).
|
|
:- mode needs_workaround(in, in) is semidet.
|
|
|
|
needs_workaround(Lval, Cond) :-
|
|
(
|
|
Cond = unop(not, lval(Lval))
|
|
;
|
|
Cond = binop(Op, Left, Right),
|
|
( Op = eq ; Op = ne ),
|
|
(
|
|
Right = lval(Lval),
|
|
( Left = const(int_const(0))
|
|
; Left = mkword(0, unop(mkbody, const(int_const(0))))
|
|
)
|
|
;
|
|
Left = lval(Lval),
|
|
( Right = const(int_const(0))
|
|
; Right = mkword(0, unop(mkbody, const(int_const(0))))
|
|
)
|
|
)
|
|
).
|
|
|
|
:- pred jumpopt__adjust_livevals(instr, list(instruction), list(instruction)).
|
|
% :- mode jumpopt__adjust_livevals(in, di, uo) is det.
|
|
:- mode jumpopt__adjust_livevals(in, in, out) is det.
|
|
|
|
jumpopt__adjust_livevals(PrevInstr, Instrs0, Instrs) :-
|
|
(
|
|
PrevInstr = livevals(PrevLivevals),
|
|
opt_util__skip_comments(Instrs0, Instrs1),
|
|
Instrs1 = [livevals(BetweenLivevals) - _ | Instrs2]
|
|
->
|
|
( BetweenLivevals = PrevLivevals ->
|
|
Instrs = Instrs2
|
|
;
|
|
error("betweenLivevals and prevLivevals differ in jumpopt")
|
|
)
|
|
;
|
|
Instrs = Instrs0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Short-circuit the given label by following any gotos at the
|
|
% labelled instruction or by falling through consecutive labels.
|
|
|
|
:- pred jumpopt__short_label(label, instrmap, label).
|
|
:- mode jumpopt__short_label(in, in, out) is det.
|
|
|
|
jumpopt__short_label(Label0, Instrmap, Label) :-
|
|
( map__search(Instrmap, Label0, Instr0) ->
|
|
jumpopt__final_dest(Label0, Instr0, Instrmap, Label, _Instr)
|
|
;
|
|
Label = Label0
|
|
).
|
|
|
|
:- pred jumpopt__short_labels(list(label), instrmap, list(label)).
|
|
:- mode jumpopt__short_labels(in, in, out) is det.
|
|
|
|
% XXX these uses of the Mod argument should be replaced by accumulator passing
|
|
|
|
jumpopt__short_labels([], _Instrmap, []).
|
|
jumpopt__short_labels([Label0 | Labels0], Instrmap, [Label | Labels]) :-
|
|
jumpopt__short_label(Label0, Instrmap, Label),
|
|
jumpopt__short_labels(Labels0, Instrmap, Labels).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Find the final destination of a given instruction at a given label.
|
|
% We follow gotos as well as consecutive labels.
|
|
|
|
:- pred jumpopt__final_dest(label, instruction, instrmap, label, instruction).
|
|
:- mode jumpopt__final_dest(in, in, in, out, out) is det.
|
|
|
|
:- pred jumpopt__final_dest_2(label, instruction, instrmap, list(label),
|
|
label, instruction).
|
|
:- mode jumpopt__final_dest_2(in, in, in, in, out, out) is det.
|
|
|
|
jumpopt__final_dest(SrcLabel, SrcInstr, Instrmap, DestLabel, DestInstr) :-
|
|
jumpopt__final_dest_2(SrcLabel, SrcInstr, Instrmap, [],
|
|
DestLabel, DestInstr).
|
|
|
|
jumpopt__final_dest_2(SrcLabel, SrcInstr, Instrmap, LabelsSofar,
|
|
DestLabel, DestInstr) :-
|
|
(
|
|
SrcInstr = SrcUinstr - _Comment,
|
|
(
|
|
SrcUinstr = goto(label(TargetLabel))
|
|
;
|
|
SrcUinstr = label(TargetLabel)
|
|
),
|
|
map__search(Instrmap, TargetLabel, TargetInstr),
|
|
\+ list__member(SrcLabel, LabelsSofar)
|
|
->
|
|
jumpopt__final_dest_2(TargetLabel, TargetInstr, Instrmap,
|
|
[SrcLabel | LabelsSofar], DestLabel, DestInstr)
|
|
;
|
|
DestLabel = SrcLabel,
|
|
DestInstr = SrcInstr
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred jumpopt__short_labels_rval(rval, instrmap, rval).
|
|
:- mode jumpopt__short_labels_rval(in, in, out) is det.
|
|
|
|
jumpopt__short_labels_rval(lval(Lval0), Instrmap, lval(Lval)) :-
|
|
jumpopt__short_labels_lval(Lval0, Instrmap, Lval).
|
|
jumpopt__short_labels_rval(var(_), _, _) :-
|
|
error("var rval in jumpopt__short_labels_rval").
|
|
jumpopt__short_labels_rval(
|
|
create(Tag, Rvals0, ArgTypes, StatDyn, Cell, Type, Reuse0),
|
|
Instrmap,
|
|
create(Tag, Rvals, ArgTypes, StatDyn, Cell, Type, Reuse)) :-
|
|
jumpopt__short_labels_maybe_rvals(Rvals0, Instrmap, Rvals),
|
|
jumpopt__short_labels_maybe_rval(Reuse0, Instrmap, Reuse).
|
|
jumpopt__short_labels_rval(mkword(Tag, Rval0), Instrmap,
|
|
mkword(Tag, Rval)) :-
|
|
jumpopt__short_labels_rval(Rval0, Instrmap, Rval).
|
|
jumpopt__short_labels_rval(const(Const0), Instrmap, const(Const)) :-
|
|
jumpopt__short_labels_const(Const0, Instrmap, Const).
|
|
jumpopt__short_labels_rval(unop(Op, Rval0), Instrmap, unop(Op, Rval)) :-
|
|
jumpopt__short_labels_rval(Rval0, Instrmap, Rval).
|
|
jumpopt__short_labels_rval(binop(Op, LRval0, RRval0), Instrmap,
|
|
binop(Op, LRval, RRval)) :-
|
|
jumpopt__short_labels_rval(LRval0, Instrmap, LRval),
|
|
jumpopt__short_labels_rval(RRval0, Instrmap, RRval).
|
|
jumpopt__short_labels_rval(mem_addr(MemRef), _, mem_addr(MemRef)).
|
|
|
|
:- pred jumpopt__short_labels_const(rval_const, instrmap, rval_const).
|
|
:- mode jumpopt__short_labels_const(in, in, out) is det.
|
|
|
|
jumpopt__short_labels_const(true, _, true).
|
|
jumpopt__short_labels_const(false, _, false).
|
|
jumpopt__short_labels_const(int_const(I), _, int_const(I)).
|
|
jumpopt__short_labels_const(float_const(F), _, float_const(F)).
|
|
jumpopt__short_labels_const(string_const(S), _, string_const(S)).
|
|
jumpopt__short_labels_const(multi_string_const(L, S), _,
|
|
multi_string_const(L, S)).
|
|
jumpopt__short_labels_const(label_entry(Label0), Instrmap,
|
|
label_entry(Label)) :-
|
|
jumpopt__short_label(Label0, Instrmap, Label).
|
|
jumpopt__short_labels_const(code_addr_const(CodeAddr0), Instrmap,
|
|
code_addr_const(CodeAddr)) :-
|
|
( CodeAddr0 = label(Label0) ->
|
|
jumpopt__short_label(Label0, Instrmap, Label),
|
|
CodeAddr = label(Label)
|
|
;
|
|
CodeAddr = CodeAddr0
|
|
).
|
|
jumpopt__short_labels_const(data_addr_const(D), _, data_addr_const(D)).
|
|
|
|
:- pred jumpopt__short_labels_maybe_rvals(list(maybe(rval)), instrmap,
|
|
list(maybe(rval))).
|
|
:- mode jumpopt__short_labels_maybe_rvals(in, in, out) is det.
|
|
|
|
jumpopt__short_labels_maybe_rvals([], _, []).
|
|
jumpopt__short_labels_maybe_rvals([MaybeRval0 | MaybeRvals0], Instrmap,
|
|
[MaybeRval | MaybeRvals]) :-
|
|
jumpopt__short_labels_maybe_rval(MaybeRval0, Instrmap, MaybeRval),
|
|
jumpopt__short_labels_maybe_rvals(MaybeRvals0, Instrmap, MaybeRvals).
|
|
|
|
:- pred jumpopt__short_labels_maybe_rval(maybe(rval), instrmap, maybe(rval)).
|
|
:- mode jumpopt__short_labels_maybe_rval(in, in, out) is det.
|
|
|
|
jumpopt__short_labels_maybe_rval(MaybeRval0, Instrmap, MaybeRval) :-
|
|
(
|
|
MaybeRval0 = no,
|
|
MaybeRval = no
|
|
;
|
|
MaybeRval0 = yes(Rval0),
|
|
jumpopt__short_labels_rval(Rval0, Instrmap, Rval),
|
|
MaybeRval = yes(Rval)
|
|
).
|
|
|
|
:- pred jumpopt__short_labels_lval(lval, instrmap, lval).
|
|
:- mode jumpopt__short_labels_lval(in, in, out) is det.
|
|
|
|
jumpopt__short_labels_lval(reg(T, N), _, reg(T, N)).
|
|
jumpopt__short_labels_lval(succip, _, succip).
|
|
jumpopt__short_labels_lval(maxfr, _, maxfr).
|
|
jumpopt__short_labels_lval(curfr, _, curfr).
|
|
jumpopt__short_labels_lval(hp, _, hp).
|
|
jumpopt__short_labels_lval(sp, _, sp).
|
|
jumpopt__short_labels_lval(temp(T, N), _, temp(T, N)).
|
|
jumpopt__short_labels_lval(stackvar(N), _, stackvar(N)).
|
|
jumpopt__short_labels_lval(framevar(N), _, framevar(N)).
|
|
jumpopt__short_labels_lval(succip(Rval0), Instrmap, succip(Rval)) :-
|
|
jumpopt__short_labels_rval(Rval0, Instrmap, Rval).
|
|
jumpopt__short_labels_lval(redoip(Rval0), Instrmap, redoip(Rval)) :-
|
|
jumpopt__short_labels_rval(Rval0, Instrmap, Rval).
|
|
jumpopt__short_labels_lval(redofr(Rval0), Instrmap, redofr(Rval)) :-
|
|
jumpopt__short_labels_rval(Rval0, Instrmap, Rval).
|
|
jumpopt__short_labels_lval(succfr(Rval0), Instrmap, succfr(Rval)) :-
|
|
jumpopt__short_labels_rval(Rval0, Instrmap, Rval).
|
|
jumpopt__short_labels_lval(prevfr(Rval0), Instrmap, prevfr(Rval)) :-
|
|
jumpopt__short_labels_rval(Rval0, Instrmap, Rval).
|
|
jumpopt__short_labels_lval(field(Tag, Rval0, Field0), Instrmap,
|
|
field(Tag, Rval, Field)) :-
|
|
jumpopt__short_labels_rval(Rval0, Instrmap, Rval),
|
|
jumpopt__short_labels_rval(Field0, Instrmap, Field).
|
|
jumpopt__short_labels_lval(mem_ref(Rval0), Instrmap, mem_ref(Rval)) :-
|
|
jumpopt__short_labels_rval(Rval0, Instrmap, Rval).
|
|
jumpopt__short_labels_lval(lvar(_), _, _) :-
|
|
error("lvar lval in jumpopt__short_labels_lval").
|
|
|
|
%-----------------------------------------------------------------------------%
|