mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-15 13:55:07 +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.
382 lines
11 KiB
Mathematica
382 lines
11 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-1998,2002-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.
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% peephole.m - local LLDS to LLDS optimizations based on pattern-matching.
|
|
|
|
% Authors: fjh and zs.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend__peephole.
|
|
|
|
:- interface.
|
|
|
|
:- import_module ll_backend__llds.
|
|
:- import_module libs__globals.
|
|
|
|
:- import_module bool, list.
|
|
|
|
% Peephole optimize a list of instructions.
|
|
|
|
:- pred peephole__optimize(gc_method, list(instruction), list(instruction),
|
|
bool).
|
|
:- mode peephole__optimize(in, in, out, out) is det.
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs__builtin_ops.
|
|
:- import_module ll_backend__code_util.
|
|
:- import_module ll_backend__opt_debug.
|
|
:- import_module ll_backend__opt_util.
|
|
|
|
:- import_module int, map, string, std_util.
|
|
|
|
% Patterns that can be switched off.
|
|
|
|
:- type pattern ---> incr_sp.
|
|
|
|
% We zip down to the end of the instruction list, and start attempting
|
|
% to optimize instruction sequences. As long as we can continue
|
|
% optimizing the instruction sequence, we keep doing so;
|
|
% when we find a sequence we can't optimize, we back up and try
|
|
% to optimize the sequence starting with the previous instruction.
|
|
|
|
peephole__optimize(GC_Method, Instrs0, Instrs, Mod) :-
|
|
peephole__invalid_opts(GC_Method, InvalidPatterns),
|
|
peephole__optimize_2(InvalidPatterns, Instrs0, Instrs, Mod).
|
|
|
|
:- pred peephole__optimize_2(list(pattern), list(instruction),
|
|
list(instruction), bool).
|
|
:- mode peephole__optimize_2(in, in, out, out) is det.
|
|
peephole__optimize_2(_, [], [], no).
|
|
peephole__optimize_2(InvalidPatterns, [Instr0 - Comment | Instrs0],
|
|
Instrs, Mod) :-
|
|
peephole__optimize_2(InvalidPatterns, Instrs0, Instrs1, Mod0),
|
|
peephole__opt_instr(Instr0, Comment, InvalidPatterns, Instrs1,
|
|
Instrs, Mod1),
|
|
( Mod0 = no, Mod1 = no ->
|
|
Mod = no
|
|
;
|
|
Mod = yes
|
|
).
|
|
|
|
% Try to optimize the beginning of the given instruction sequence.
|
|
% If successful, try it again.
|
|
|
|
:- pred peephole__opt_instr(instr, string, list(pattern),
|
|
list(instruction), list(instruction), bool).
|
|
:- mode peephole__opt_instr(in, in, in, in, out, out) is det.
|
|
|
|
peephole__opt_instr(Instr0, Comment0, InvalidPatterns, Instrs0, Instrs, Mod) :-
|
|
(
|
|
opt_util__skip_comments(Instrs0, Instrs1),
|
|
peephole__match(Instr0, Comment0, InvalidPatterns, Instrs1,
|
|
Instrs2)
|
|
->
|
|
( Instrs2 = [Instr2 - Comment2 | Instrs3] ->
|
|
peephole__opt_instr(Instr2, Comment2, InvalidPatterns,
|
|
Instrs3, Instrs, _)
|
|
;
|
|
Instrs = Instrs2
|
|
),
|
|
Mod = yes
|
|
;
|
|
Instrs = [Instr0 - Comment0 | Instrs0],
|
|
Mod = no
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Build a map that associates each label in a computed goto with the
|
|
% values of the switch rval that cause a jump to it.
|
|
|
|
:- pred peephole__build_jump_label_map(list(label)::in, int::in,
|
|
map(label, list(int))::in, map(label, list(int))::out) is det.
|
|
|
|
peephole__build_jump_label_map([], _, LabelMap, LabelMap).
|
|
peephole__build_jump_label_map([Label | Labels], Val, LabelMap0, LabelMap) :-
|
|
( map__search(LabelMap0, Label, Vals0) ->
|
|
map__det_update(LabelMap0, Label, [Val | Vals0], LabelMap1)
|
|
;
|
|
map__det_insert(LabelMap0, Label, [Val], LabelMap1)
|
|
),
|
|
peephole__build_jump_label_map(Labels, Val + 1, LabelMap1, LabelMap).
|
|
|
|
% If one of the two labels has only one associated value, return it and
|
|
% the associated value as the first two output arguments, and the
|
|
% remaining label as the last output argument.
|
|
|
|
:- pred peephole__pick_one_val_label(pair(label, list(int))::in,
|
|
pair(label, list(int))::in, label::out, int::out, label::out)
|
|
is semidet.
|
|
|
|
peephole__pick_one_val_label(LabelVals1, LabelVals2, OneValLabel, Val,
|
|
OtherLabel) :-
|
|
LabelVals1 = Label1 - Vals1,
|
|
LabelVals2 = Label2 - Vals2,
|
|
( Vals1 = [Val1] ->
|
|
OneValLabel = Label1,
|
|
Val = Val1,
|
|
OtherLabel = Label2
|
|
; Vals2 = [Val2] ->
|
|
OneValLabel = Label2,
|
|
Val = Val2,
|
|
OtherLabel = Label1
|
|
;
|
|
fail
|
|
).
|
|
|
|
% Look for code patterns that can be optimized, and optimize them.
|
|
|
|
:- pred peephole__match(instr, string, list(pattern),
|
|
list(instruction), list(instruction)).
|
|
:- mode peephole__match(in, in, in, in, out) is semidet.
|
|
|
|
% A `computed_goto' with all branches pointing to the same
|
|
% label can be replaced with an unconditional goto.
|
|
%
|
|
% A `computed_goto' with all branches but one pointing to the same
|
|
% label can be replaced with a conditional branch followed by an
|
|
% unconditional goto.
|
|
|
|
peephole__match(computed_goto(SelectorRval, Labels), Comment, _,
|
|
Instrs0, Instrs) :-
|
|
peephole__build_jump_label_map(Labels, 0, map__init, LabelMap),
|
|
map__to_assoc_list(LabelMap, LabelValsList),
|
|
(
|
|
LabelValsList = [Label - _]
|
|
->
|
|
GotoInstr = goto(label(Label)) - Comment,
|
|
Instrs = [GotoInstr | Instrs0]
|
|
;
|
|
LabelValsList = [LabelVals1, LabelVals2],
|
|
peephole__pick_one_val_label(LabelVals1, LabelVals2,
|
|
OneValLabel, Val, OtherLabel)
|
|
->
|
|
CondRval = binop(eq, SelectorRval, const(int_const(Val))),
|
|
CommentInstr = comment(Comment) - "",
|
|
BranchInstr = if_val(CondRval, label(OneValLabel)) - "",
|
|
GotoInstr = goto(label(OtherLabel)) - Comment,
|
|
Instrs = [CommentInstr, BranchInstr, GotoInstr | Instrs0]
|
|
;
|
|
fail
|
|
).
|
|
|
|
% A conditional branch whose condition is constant
|
|
% can be either eliminated or replaced by an unconditional goto.
|
|
%
|
|
% A conditional branch to an address followed by an unconditional
|
|
% branch to the same address can be eliminated.
|
|
%
|
|
% A conditional branch to a label followed by that label
|
|
% can be eliminated.
|
|
|
|
peephole__match(if_val(Rval, CodeAddr), Comment, _, Instrs0, Instrs) :-
|
|
(
|
|
opt_util__is_const_condition(Rval, Taken)
|
|
->
|
|
(
|
|
Taken = yes,
|
|
Instrs = [goto(CodeAddr) - Comment | Instrs0]
|
|
;
|
|
Taken = no,
|
|
Instrs = Instrs0
|
|
)
|
|
;
|
|
opt_util__skip_comments(Instrs0, Instrs1),
|
|
Instrs1 = [Instr1 | _],
|
|
Instr1 = goto(CodeAddr) - _
|
|
->
|
|
Instrs = Instrs0
|
|
;
|
|
CodeAddr = label(Label),
|
|
opt_util__is_this_label_next(Label, Instrs0, _)
|
|
->
|
|
Instrs = Instrs0
|
|
;
|
|
fail
|
|
).
|
|
|
|
% If a `mkframe' is followed by an assignment to its redoip slot,
|
|
% with the instructions in between containing only straight-line code,
|
|
% we can delete the assignment and instead just set the redoip
|
|
% directly in the `mkframe'.
|
|
%
|
|
% mkframe(NFI, _) => mkframe(NFI, Redoip)
|
|
% <straightline instrs> <straightline instrs>
|
|
% assign(redoip(lval(_)), Redoip)
|
|
%
|
|
% If a `mkframe' is followed by a test that can fail, we try to
|
|
% swap the two instructions to avoid doing the mkframe unnecessarily.
|
|
%
|
|
% mkframe(NFI, dofail) => if_val(test, redo)
|
|
% if_val(test, redo/fail) mkframe(NFI, dofail)
|
|
%
|
|
% mkframe(NFI, label) => if_val(test, redo)
|
|
% if_val(test, fail) mkframe(NFI, label)
|
|
%
|
|
% mkframe(NFI, label) => mkframe(NFI, label)
|
|
% if_val(test, redo) if_val(test, label)
|
|
%
|
|
% These two patterns are mutually exclusive because if_val is not
|
|
% straight-line code.
|
|
|
|
peephole__match(mkframe(NondetFrameInfo, Redoip1), Comment, _,
|
|
Instrs0, Instrs) :-
|
|
(
|
|
% A mkframe sets curfr to point to the new frame
|
|
% only for ordinary frames, not temp frames.
|
|
( NondetFrameInfo = ordinary_frame(_, _, _) ->
|
|
AllowedBases = [maxfr, curfr]
|
|
;
|
|
AllowedBases = [maxfr]
|
|
),
|
|
opt_util__next_assign_to_redoip(Instrs0, AllowedBases,
|
|
[], Redoip2, Skipped, Rest),
|
|
opt_util__touches_nondet_ctrl(Skipped, no)
|
|
->
|
|
list__append(Skipped, Rest, Instrs1),
|
|
Instrs = [mkframe(NondetFrameInfo, Redoip2) - Comment
|
|
| Instrs1]
|
|
;
|
|
opt_util__skip_comments_livevals(Instrs0, Instrs1),
|
|
Instrs1 = [Instr1 | Instrs2],
|
|
Instr1 = if_val(Test, Target) - Comment2,
|
|
(
|
|
Redoip1 = do_fail,
|
|
( Target = do_redo ; Target = do_fail)
|
|
->
|
|
Instrs = [
|
|
if_val(Test, do_redo)
|
|
- Comment2,
|
|
mkframe(NondetFrameInfo, do_fail)
|
|
- Comment
|
|
| Instrs2
|
|
]
|
|
;
|
|
Redoip1 = label(_)
|
|
->
|
|
(
|
|
Target = do_fail
|
|
->
|
|
Instrs = [
|
|
if_val(Test, do_redo)
|
|
- Comment2,
|
|
mkframe(NondetFrameInfo, Redoip1)
|
|
- Comment
|
|
| Instrs2
|
|
]
|
|
;
|
|
Target = do_redo
|
|
->
|
|
Instrs = [
|
|
mkframe(NondetFrameInfo, Redoip1)
|
|
- Comment,
|
|
if_val(Test, Redoip1)
|
|
- Comment2
|
|
| Instrs2
|
|
]
|
|
;
|
|
fail
|
|
)
|
|
;
|
|
fail
|
|
)
|
|
).
|
|
|
|
% If a `store_ticket' is followed by a `reset_ticket',
|
|
% we can delete the `reset_ticket'.
|
|
%
|
|
% store_ticket(Lval) => store_ticket(Lval)
|
|
% reset_ticket(Lval, _R)
|
|
|
|
peephole__match(store_ticket(Lval), Comment, _, Instrs0, Instrs) :-
|
|
opt_util__skip_comments(Instrs0, Instrs1),
|
|
Instrs1 = [reset_ticket(lval(Lval), _Reason) - _Comment2 | Instrs2],
|
|
Instrs = [store_ticket(Lval) - Comment | Instrs2].
|
|
|
|
% If an assignment to a redoip slot is followed by another, with
|
|
% the instructions in between containing only straight-line code,
|
|
% we can delete one of the asignments:
|
|
%
|
|
% assign(redoip(Fr), Redoip1) => assign(redoip(Fr), Redoip2)
|
|
% <straightline instrs> <straightline instrs>
|
|
% assign(redoip(Fr), Redoip2)
|
|
|
|
% If an assignment of do_fail to the redoip slot of the current frame
|
|
% is followed by straight-line instructions except possibly for if_val
|
|
% with do_fail or do_redo as target, until a goto to do_succeed(no),
|
|
% and if the nondet stack linkages are not touched by the
|
|
% straight-line instructions, then we can discard the nondet stack
|
|
% frame early.
|
|
|
|
peephole__match(assign(redoip(lval(Base)), Redoip), Comment, _,
|
|
Instrs0, Instrs) :-
|
|
(
|
|
opt_util__next_assign_to_redoip(Instrs0, [Base],
|
|
[], Redoip2, Skipped, Rest),
|
|
opt_util__touches_nondet_ctrl(Skipped, no)
|
|
->
|
|
list__append(Skipped, Rest, Instrs1),
|
|
Instrs = [assign(redoip(lval(Base)),
|
|
const(code_addr_const(Redoip2))) - Comment
|
|
| Instrs1]
|
|
;
|
|
Base = curfr,
|
|
Redoip = const(code_addr_const(do_fail)),
|
|
opt_util__straight_alternative(Instrs0, Between, After),
|
|
opt_util__touches_nondet_ctrl(Between, no)
|
|
->
|
|
list__condense([Between,
|
|
[goto(do_succeed(yes)) - "early discard"], After],
|
|
Instrs)
|
|
;
|
|
fail
|
|
).
|
|
|
|
% If a decr_sp follows an incr_sp of the same amount, with the code
|
|
% in between not referencing the stack, except possibly for a
|
|
% restoration of succip, then the two cancel out. Assignments to
|
|
% stack slots are allowed and are thrown away.
|
|
%
|
|
% incr_sp N
|
|
% <...> => <...>
|
|
% decr_sp N
|
|
%
|
|
% incr_sp N
|
|
% <...> => <...>
|
|
% succip = detstackvar(N)
|
|
% decr_sp N
|
|
|
|
peephole__match(incr_sp(N, _), _, InvalidPatterns, Instrs0, Instrs) :-
|
|
\+ list__member(incr_sp, InvalidPatterns),
|
|
(
|
|
opt_util__no_stackvars_til_decr_sp(Instrs0, N, Between, Remain)
|
|
->
|
|
list__append(Between, Remain, Instrs)
|
|
;
|
|
fail
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Given a GC method, return the list of invalid peephole
|
|
% optimizations.
|
|
|
|
:- pred peephole__invalid_opts(gc_method, list(pattern)).
|
|
:- mode peephole__invalid_opts(in, out) is det.
|
|
|
|
peephole__invalid_opts(GC_Method, InvalidPatterns) :-
|
|
(
|
|
GC_Method = accurate
|
|
->
|
|
InvalidPatterns = [incr_sp]
|
|
;
|
|
InvalidPatterns = []
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|