mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-24 13:53:54 +00:00
This implements Mantis feature request #495. NEWS: Announce the change. compiler/optimization_options.m: A new module for managing optimization options. It defines a separate bespoke type for every boolean optimization option to make it harder to confuse them. It defines a tuple type (opt_tuple) for accessing optimization options quickly. It implements the turning on (but NOT turning off) of optimizations when a given optimization level is selected. tools/make_optimization_options_middle: tools/make_optimization_options_db: The script that generates the meat of optimization_options.m, and the database of option names, kinds and initial values that it uses as its input. The script also generates some code for the special_handler predicate in compiler/options.m. tools/make_optimization_options_start: tools/make_optimization_options_end: The handwritten initial and final parts of optimization_options.m. tools/make_optimization_options: The script that pulls these parts together to form optimization_options.m. compiler/options.m: Make every optimization option a special option, to be handled by the special_handler predicate. That handling consists of simply adding a representation of the option to the end of a cord of optimization options, to be processed later by optimization_options.m. That processing will record the values of these options in the opt_tuple, which is where every other part of the compiler should get them from. Change the interface of special_handler to make the above possible. Add an "optopt_" (optimization option) prefix to the name of every optimization option, to make them inaccessible to the rest of the compiler under their old name, and thus help enforce the switch to using the opt_tuple. Any access to these options to look up their values would fail anyway, since the option data would no longer be e.g. bool(yes), but bool_special, but the name change makes this failure happen at compile time, not runtime. Reclassify a few options to make the above make sense. Some options (unneeded_code_debug, unneeded_code_debug_pred_name, and common_struct_preds) were classified as oc_opt even though they control only the *debugging* of optimizations, while some options (c_optimize and inline_alloc) were not classified as oc_opt even though we do set them automatically at some optimization levels. Delete the opt_level_number option, since it was not used anywhere. Delete the code for handling -ON and --opt-space, since that is now done in optimization_options.m. Add some XXXs. compiler/handle_options.m: Switch to using getopt_io.process_options_userdata_se, as required by the new interface of the special_handler in options.m. In the absence of errors, invoke optimization_options.m to initialize the opt_tuple. Then update the opt_tuple incrementally when processing option implications that affect optimization options. compiler/globals.m: Put the opt_tuple into a new field of the globals structure. compiler/accumulator.m: compiler/add_pragma_type_spec.m: compiler/add_trail_ops.m: compiler/code_info.m: compiler/code_loc_dep.m: compiler/compile_target_code.m: compiler/const_struct.m: compiler/deforest.m: compiler/dep_par_conj.m: compiler/disj_gen.m: compiler/erl_code_gen.m: compiler/format_call.m: compiler/global_data.m: compiler/grab_modules.m: compiler/higher_order.m: compiler/hlds_pred.m: compiler/inlining.m: compiler/intermod.m: compiler/ite_gen.m: compiler/jumpopt.m: compiler/libs.m: compiler/llds_out_code_addr.m: compiler/llds_out_data.m: compiler/llds_out_file.m: compiler/llds_out_instr.m: compiler/llds_out_util.m: compiler/matching.m: compiler/mercury_compile_front_end.m: compiler/mercury_compile_llds_back_end.m: compiler/mercury_compile_main.m: compiler/mercury_compile_middle_passes.m: compiler/mercury_compile_mlds_back_end.m: compiler/ml_disj_gen.m: compiler/ml_gen_info.m: compiler/ml_lookup_switch.m: compiler/ml_optimize.m: compiler/ml_proc_gen.m: compiler/ml_simplify_switch.m: compiler/ml_switch_gen.m: compiler/ml_unify_gen_construct.m: compiler/optimize.m: compiler/pd_util.m: compiler/peephole.m: compiler/polymorphism.m: compiler/proc_gen.m: compiler/simplify_goal_call.m: compiler/simplify_goal_scope.m: compiler/simplify_info.m: compiler/simplify_proc.m: compiler/simplify_tasks.m: compiler/stack_layout.m: compiler/stack_opt.m: compiler/switch_gen.m: compiler/switch_util.m: compiler/tag_switch.m: compiler/tupling.m: compiler/unify_gen_construct.m: compiler/unneeded_code.m: compiler/unused_args.m: Conform to the changes above, mostly by looking up optimization options in the opt_tuple. In some places, replace bools containing optimization options with the bespoke type of that specific optimization option. library/getopt_template: Fix a bug that screwed up an error message. The bug happened when processing a --file option. If one of the options in the file was a special option whose special handler failed, the code handling that failing option returned both an error indication, and the rest of the argument list read in from the file. The code handling the --file option then *ignored* the error indication from the failed special option, and returned an error message of its own complaining about the unconsumed remaining arguments in the file, believing them to be non-option arguments, even though these arguments were never looked it to see if they were options. The fix is for the code handling --flag options to check whether the code processing the file contents found any errors, and if so, return that error *without* looking at the list of remaining arguments. In an unrelated change, factor out a duplicate call.
776 lines
28 KiB
Mathematica
776 lines
28 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-1998,2002-2007, 2010-2011 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: peeophole.m:
|
|
% Authors: fjh, zs.
|
|
%
|
|
% Local LLDS to LLDS optimizations based on pattern-matching.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend.peephole.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.optimization_options.
|
|
:- import_module ll_backend.llds.
|
|
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
|
|
% Peephole optimize a list of instructions.
|
|
%
|
|
:- pred peephole_optimize(gc_method::in, maybe_opt_peep_mkword::in,
|
|
list(instruction)::in, list(instruction)::out, bool::out) is det.
|
|
|
|
:- pred combine_decr_sp(list(instruction)::in, list(instruction)::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.builtin_ops.
|
|
:- import_module ll_backend.opt_util.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module string.
|
|
:- import_module uint8.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Patterns that can be switched off.
|
|
%
|
|
:- type pattern
|
|
---> pattern_incr_sp
|
|
; pattern_mkword.
|
|
|
|
% 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, OptPeepMkword, Instrs0, Instrs, Mod) :-
|
|
invalid_peephole_opts(GC_Method, OptPeepMkword, InvalidPatterns),
|
|
peephole_optimize_2(InvalidPatterns, Instrs0, Instrs, Mod).
|
|
|
|
:- pred peephole_optimize_2(list(pattern)::in, list(instruction)::in,
|
|
list(instruction)::out, bool::out) is det.
|
|
|
|
peephole_optimize_2(_, [], [], no).
|
|
peephole_optimize_2(InvalidPatterns, [Instr0 | Instrs0], Instrs, Mod) :-
|
|
peephole_optimize_2(InvalidPatterns, Instrs0, Instrs1, Mod0),
|
|
peephole_opt_instr(Instr0, Instrs1, InvalidPatterns, Instrs, Mod1),
|
|
( if Mod0 = no, Mod1 = no then
|
|
Mod = no
|
|
else
|
|
Mod = yes
|
|
).
|
|
|
|
% Try to optimize the beginning of the given instruction sequence.
|
|
% If successful, try it again.
|
|
%
|
|
:- pred peephole_opt_instr(instruction::in, list(instruction)::in,
|
|
list(pattern)::in, list(instruction)::out, bool::out) is det.
|
|
|
|
peephole_opt_instr(Instr0, Instrs0, InvalidPatterns, Instrs, Mod) :-
|
|
opt_util.skip_comments(Instrs0, Instrs1),
|
|
( if
|
|
peephole_match(Instr0, Instrs1, InvalidPatterns, Instrs2)
|
|
then
|
|
(
|
|
Instrs2 = [Instr2 | Instrs3],
|
|
peephole_opt_instr(Instr2, Instrs3, InvalidPatterns, Instrs, _)
|
|
;
|
|
Instrs2 = [],
|
|
Instrs = Instrs2
|
|
),
|
|
Mod = yes
|
|
else if
|
|
peephole_match_norepeat(Instr0, Instrs1, InvalidPatterns, Instrs2)
|
|
then
|
|
Instrs = Instrs2,
|
|
Mod = yes
|
|
else
|
|
Instrs = [Instr0 | 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 build_peephole_jump_label_map(list(maybe(label))::in, int::in,
|
|
map(label, list(int))::in, map(label, list(int))::out) is semidet.
|
|
|
|
build_peephole_jump_label_map([], _, !LabelMap).
|
|
build_peephole_jump_label_map([MaybeLabel | MaybeLabels], Val, !LabelMap) :-
|
|
MaybeLabel = yes(Label),
|
|
( if map.search(!.LabelMap, Label, Vals0) then
|
|
map.det_update(Label, [Val | Vals0], !LabelMap)
|
|
else
|
|
map.det_insert(Label, [Val], !LabelMap)
|
|
),
|
|
build_peephole_jump_label_map(MaybeLabels, Val + 1, !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,
|
|
( if Vals1 = [Val1] then
|
|
OneValLabel = Label1,
|
|
Val = Val1,
|
|
OtherLabel = Label2
|
|
else if Vals2 = [Val2] then
|
|
OneValLabel = Label2,
|
|
Val = Val2,
|
|
OtherLabel = Label1
|
|
else
|
|
fail
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Look for code patterns that can be optimized, and optimize them.
|
|
% Unlike peephole_match_norepeat, this predicate guarantees that the
|
|
% instruction sequence it returns on success won't be transformable
|
|
% by the same transformation as it applies. This allows peephole_opt_instr
|
|
% to call peephole_match repeatedly until it fails without the possibility
|
|
% of an infinite loop.
|
|
%
|
|
:- pred peephole_match(instruction::in, list(instruction)::in,
|
|
list(pattern)::in, list(instruction)::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(Instr0, Instrs0, _, Instrs) :-
|
|
Instr0 = llds_instr(Uinstr0, Comment0),
|
|
Uinstr0 = computed_goto(SelectorRval, Labels),
|
|
build_peephole_jump_label_map(Labels, 0, map.init, LabelMap),
|
|
map.to_assoc_list(LabelMap, LabelValsList),
|
|
( if
|
|
LabelValsList = [Label - _]
|
|
then
|
|
GotoInstr = llds_instr(goto(code_label(Label)), Comment0),
|
|
Instrs = [GotoInstr | Instrs0]
|
|
else if
|
|
LabelValsList = [LabelVals1, LabelVals2],
|
|
peephole_pick_one_val_label(LabelVals1, LabelVals2, OneValLabel,
|
|
Val, OtherLabel)
|
|
then
|
|
CondRval = binop(eq(int_type_int), SelectorRval,
|
|
const(llconst_int(Val))),
|
|
CommentInstr = llds_instr(comment(Comment0), ""),
|
|
BranchInstr = llds_instr(if_val(CondRval, code_label(OneValLabel)),
|
|
""),
|
|
GotoInstr = llds_instr(goto(code_label(OtherLabel)), Comment0),
|
|
Instrs = [CommentInstr, BranchInstr, GotoInstr | Instrs0]
|
|
else
|
|
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(Instr0, Instrs0, _, Instrs) :-
|
|
Instr0 = llds_instr(Uinstr0, Comment0),
|
|
Uinstr0 = if_val(Rval, CodeAddr),
|
|
( if
|
|
opt_util.is_const_condition(Rval, Taken)
|
|
then
|
|
(
|
|
Taken = yes,
|
|
Instrs = [llds_instr(goto(CodeAddr), Comment0) | Instrs0]
|
|
;
|
|
Taken = no,
|
|
Instrs = Instrs0
|
|
)
|
|
else if
|
|
opt_util.skip_comments(Instrs0, Instrs1),
|
|
Instrs1 = [Instr1 | _],
|
|
Instr1 = llds_instr(goto(CodeAddr), _)
|
|
then
|
|
Instrs = Instrs0
|
|
else if
|
|
CodeAddr = code_label(Label),
|
|
opt_util.is_this_label_next(Label, Instrs0, _)
|
|
then
|
|
Instrs = Instrs0
|
|
else
|
|
fail
|
|
).
|
|
|
|
% If a `mkframe' is followed by an assignment to its redoip slot,
|
|
% with the instructions in between containing only straight-line code,
|
|
% and no labels, we can delete the assignment and instead just set
|
|
% the redoip directly in the `mkframe'. (If the code between the
|
|
% mkframe and the redoip contains a label, then we need to keep the
|
|
% redoip assign, in case we reach it via the label and not from the
|
|
% mkframe above.
|
|
%
|
|
% 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)
|
|
%
|
|
% If a `mkframe' is followed directly by a `fail', we optimize away
|
|
% the creation of the stack frame:
|
|
%
|
|
% mkframe(NFI, <any>) => goto redo
|
|
% goto fail
|
|
%
|
|
% This last pattern can be created by frameopt.m.
|
|
%
|
|
% These three classes of patterns are mutually exclusive because if_val
|
|
% and goto are not straight-line code.
|
|
%
|
|
% The fourth pattern we look for can happen when predicates that are
|
|
% actually semidet are declared to be nondet:
|
|
%
|
|
% mkframe(NFI, dofail)
|
|
% <straight,nostack instrs> => <straight,nostack instrs>
|
|
% succeed proceed
|
|
%
|
|
% The fifth pattern can happen e.g. for lookup switches:
|
|
%
|
|
% mkframe(NFI, dofail) mkframe(NFI, dofail)
|
|
% <nostack instrs, no labels> => <nostack instrs, no labels>
|
|
% succeed succeed_discard
|
|
%
|
|
% The fifth pattern may apply even if the fourth doesn't, since it permits
|
|
% branches away between the mkframe and the succeed. However, if there are
|
|
% none, and both patterns apply, we prefer to apply the fourth pattern's
|
|
% transformation.
|
|
%
|
|
peephole_match(Instr0, Instrs0, _, Instrs) :-
|
|
Instr0 = llds_instr(Uinstr0, Comment0),
|
|
Uinstr0 = mkframe(NondetFrameInfo, yes(Redoip0)),
|
|
( if
|
|
% A mkframe sets curfr to point to the new frame
|
|
% only for ordinary frames, not temp frames.
|
|
(
|
|
NondetFrameInfo = ordinary_frame(_, _),
|
|
AllowedBases = [maxfr, curfr]
|
|
;
|
|
NondetFrameInfo = temp_frame(_),
|
|
AllowedBases = [maxfr]
|
|
),
|
|
opt_util.next_assign_to_redoip(Instrs0, AllowedBases, [], Redoip1,
|
|
Skipped, Rest),
|
|
opt_util.touches_nondet_ctrl(Skipped) = no
|
|
then
|
|
Instrs1 = Skipped ++ Rest,
|
|
NewInstr = llds_instr(mkframe(NondetFrameInfo, yes(Redoip1)),
|
|
Comment0),
|
|
Instrs = [NewInstr | Instrs1]
|
|
else if
|
|
opt_util.skip_comments_livevals(Instrs0, Instrs1),
|
|
Instrs1 = [Instr1 | Instrs2],
|
|
Instr1 = llds_instr(if_val(Test, Target), Comment1),
|
|
( if
|
|
Redoip0 = do_fail,
|
|
( Target = do_redo ; Target = do_fail)
|
|
then
|
|
InstrsPrime = [
|
|
llds_instr(if_val(Test, do_redo), Comment1),
|
|
llds_instr(mkframe(NondetFrameInfo, yes(do_fail)), Comment0)
|
|
| Instrs2
|
|
]
|
|
else if
|
|
Redoip0 = code_label(_)
|
|
then
|
|
( if
|
|
Target = do_fail
|
|
then
|
|
InstrsPrime = [
|
|
llds_instr(if_val(Test, do_redo), Comment1),
|
|
Instr0
|
|
| Instrs2
|
|
]
|
|
else if
|
|
Target = do_redo
|
|
then
|
|
InstrsPrime = [
|
|
Instr0,
|
|
llds_instr(if_val(Test, Redoip0), Comment1)
|
|
| Instrs2
|
|
]
|
|
else
|
|
fail
|
|
)
|
|
else
|
|
fail
|
|
)
|
|
then
|
|
Instrs = InstrsPrime
|
|
else if
|
|
opt_util.skip_comments_livevals(Instrs0, Instrs1),
|
|
Instrs1 = [Instr1 | Instrs2],
|
|
Instr1 = llds_instr(goto(do_fail), Comment2)
|
|
then
|
|
Instrs = [llds_instr(goto(do_redo), Comment2) | Instrs2]
|
|
else if
|
|
Redoip0 = do_fail,
|
|
no_stack_straight_line(Instrs0, Straight, Instrs1),
|
|
Instrs1 = [Instr1 | Instrs2],
|
|
Instr1 = llds_instr(goto(do_succeed(_)), _)
|
|
then
|
|
GotoSuccip = llds_instr(goto(code_succip),
|
|
"return from optimized away mkframe"),
|
|
Instrs = Straight ++ [GotoSuccip | Instrs2]
|
|
else if
|
|
Redoip0 = do_fail,
|
|
may_replace_succeed_with_succeed_discard(Instrs0, UntilSucceed,
|
|
SucceedComment, Instrs2)
|
|
then
|
|
DiscardUinstr = goto(do_succeed(no)),
|
|
DiscardComment = SucceedComment ++ " (added discard)",
|
|
DiscardInstr = llds_instr(DiscardUinstr, DiscardComment),
|
|
Instrs = [Instr0 | UntilSucceed] ++ [DiscardInstr | Instrs2]
|
|
else
|
|
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(Instr0, Instrs0, _, Instrs) :-
|
|
Instr0 = llds_instr(Uinstr0, Comment0),
|
|
Uinstr0 = store_ticket(Lval),
|
|
|
|
opt_util.skip_comments(Instrs0, Instrs1),
|
|
Instrs1 = [Instr1 | Instrs2],
|
|
Instr1 = llds_instr(reset_ticket(lval(Lval), _Reason), _Comment1),
|
|
NewInstr2 = llds_instr(store_ticket(Lval), Comment0),
|
|
Instrs = [NewInstr2 | Instrs2].
|
|
|
|
% If an assignment to a redoip slot is followed by another, with
|
|
% the instructions in between containing only straight-line code
|
|
% without labels, 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(Instr0, Instrs0, _, Instrs) :-
|
|
Instr0 = llds_instr(Uinstr0, Comment0),
|
|
Uinstr0 = assign(redoip_slot(lval(Base)), Redoip0),
|
|
( if
|
|
opt_util.next_assign_to_redoip(Instrs0, [Base], [], Redoip1,
|
|
Skipped, Rest),
|
|
opt_util.touches_nondet_ctrl(Skipped) = no
|
|
then
|
|
Instrs1 = Skipped ++ Rest,
|
|
RedoipInstr = llds_instr(assign(redoip_slot(lval(Base)),
|
|
const(llconst_code_addr(Redoip1))), Comment0),
|
|
Instrs = [RedoipInstr | Instrs1]
|
|
else if
|
|
Base = curfr,
|
|
Redoip0 = const(llconst_code_addr(do_fail)),
|
|
opt_util.straight_alternative(Instrs0, Between, After),
|
|
opt_util.touches_nondet_ctrl(Between) = no,
|
|
string.sub_string_search(Comment0, "curfr==maxfr", _)
|
|
then
|
|
SucceedInstr = llds_instr(goto(do_succeed(yes)), "early discard"),
|
|
Instrs = Between ++ [SucceedInstr] ++ After
|
|
else
|
|
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(Instr0, Instrs0, InvalidPatterns, Instrs) :-
|
|
Instr0 = llds_instr(Uinstr0, _Comment0),
|
|
Uinstr0 = incr_sp(N, _, _),
|
|
not list.member(pattern_incr_sp, InvalidPatterns),
|
|
( if opt_util.no_stackvars_til_decr_sp(Instrs0, N, Between, Remain) then
|
|
Instrs = Between ++ Remain
|
|
else
|
|
fail
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Look for code patterns that can be optimized, and optimize them.
|
|
% See the comment at the top of peephole_match for the difference
|
|
% between the two predicates.
|
|
%
|
|
:- pred peephole_match_norepeat(instruction::in, list(instruction)::in,
|
|
list(pattern)::in, list(instruction)::out) is semidet.
|
|
|
|
% If none of the instructions in brackets can affect Lval, then
|
|
% we can transform references to tag(Lval) to Ptag and body(Lval, Ptag)
|
|
% to Base.
|
|
%
|
|
% Lval = mkword(Ptag, Base) Lval = mkword(Ptag, Base)
|
|
% <...> => <...>
|
|
% ... tag(Lval) ... ... Ptag ...
|
|
% ... body(Lval, Ptag) ... ... Base ...
|
|
%
|
|
peephole_match_norepeat(Instr0, Instrs0, InvalidPatterns, Instrs) :-
|
|
Instr0 = llds_instr(Uinstr0, _),
|
|
Uinstr0 = assign(Lval, mkword(Ptag, Base)),
|
|
not list.member(pattern_mkword, InvalidPatterns),
|
|
replace_tagged_ptr_components_in_instrs(Lval, Ptag, Base,
|
|
Instrs0, Instrs1),
|
|
Instrs = [Instr0 | Instrs1].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred replace_tagged_ptr_components_in_instrs(lval::in, ptag::in, rval::in,
|
|
list(instruction)::in, list(instruction)::out) is det.
|
|
|
|
replace_tagged_ptr_components_in_instrs(_, _, _, [], []).
|
|
replace_tagged_ptr_components_in_instrs(Lval, Ptag, Base, Instrs0, Instrs) :-
|
|
Instrs0 = [HeadInstr0 | TailInstrs0],
|
|
replace_tagged_ptr_components_in_instr(Lval, Ptag, Base,
|
|
HeadInstr0, MaybeHeadInstr),
|
|
(
|
|
MaybeHeadInstr = no,
|
|
Instrs = Instrs0
|
|
;
|
|
MaybeHeadInstr = yes(HeadInstr),
|
|
replace_tagged_ptr_components_in_instrs(Lval, Ptag, Base,
|
|
TailInstrs0, TailInstrs),
|
|
Instrs = [HeadInstr | TailInstrs]
|
|
).
|
|
|
|
:- pred replace_tagged_ptr_components_in_instr(lval::in, ptag::in, rval::in,
|
|
instruction::in, maybe(instruction)::out) is det.
|
|
|
|
replace_tagged_ptr_components_in_instr(OldLval, OldPtag, OldBase,
|
|
Instr0, MaybeInstr) :-
|
|
Instr0 = llds_instr(Uinstr0, Comment),
|
|
(
|
|
Uinstr0 = assign(Lval, Rval0),
|
|
( if Lval = OldLval then
|
|
MaybeInstr = no
|
|
else if Lval = mem_ref(_) then
|
|
MaybeInstr = no
|
|
else
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
Rval0, Rval),
|
|
Uinstr = assign(Lval, Rval),
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
MaybeInstr = yes(Instr)
|
|
)
|
|
;
|
|
Uinstr0 = keep_assign(Lval, Rval0),
|
|
( if Lval = OldLval then
|
|
MaybeInstr = no
|
|
else if Lval = mem_ref(_) then
|
|
MaybeInstr = no
|
|
else
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
Rval0, Rval),
|
|
Uinstr = keep_assign(Lval, Rval),
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
MaybeInstr = yes(Instr)
|
|
)
|
|
;
|
|
Uinstr0 = computed_goto(Rval0, Targets),
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
Rval0, Rval),
|
|
Uinstr = computed_goto(Rval, Targets),
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
MaybeInstr = yes(Instr)
|
|
;
|
|
Uinstr0 = if_val(Rval0, Target),
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
Rval0, Rval),
|
|
Uinstr = if_val(Rval, Target),
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
MaybeInstr = yes(Instr)
|
|
;
|
|
Uinstr0 = incr_hp(Target, MaybePtag, MaybeOffset, SizeRval0,
|
|
TypeMsg, MayUseAtomicAlloc, MaybeRegionId, MaybeReuse),
|
|
( if Target = OldLval then
|
|
MaybeInstr = no
|
|
else if Target = mem_ref(_) then
|
|
MaybeInstr = no
|
|
else
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
SizeRval0, SizeRval),
|
|
Uinstr = incr_hp(Target, MaybePtag, MaybeOffset, SizeRval,
|
|
TypeMsg, MayUseAtomicAlloc, MaybeRegionId, MaybeReuse),
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
MaybeInstr = yes(Instr)
|
|
)
|
|
;
|
|
Uinstr0 = restore_hp(Rval0),
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
Rval0, Rval),
|
|
Uinstr = restore_hp(Rval),
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
MaybeInstr = yes(Instr)
|
|
;
|
|
Uinstr0 = free_heap(Rval0),
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
Rval0, Rval),
|
|
Uinstr = free_heap(Rval),
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
MaybeInstr = yes(Instr)
|
|
;
|
|
Uinstr0 = reset_ticket(Rval0, Reason),
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
Rval0, Rval),
|
|
Uinstr = reset_ticket(Rval, Reason),
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
MaybeInstr = yes(Instr)
|
|
;
|
|
Uinstr0 = prune_tickets_to(Rval0),
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
Rval0, Rval),
|
|
Uinstr = prune_tickets_to(Rval),
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
MaybeInstr = yes(Instr)
|
|
;
|
|
Uinstr0 = lc_wait_free_slot(Rval0, Lval0, Label),
|
|
( if Lval0 = OldLval then
|
|
MaybeInstr = no
|
|
else if Lval0 = mem_ref(_) then
|
|
MaybeInstr = no
|
|
else
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
Rval0, Rval),
|
|
Uinstr = lc_wait_free_slot(Rval, Lval0, Label),
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
MaybeInstr = yes(Instr)
|
|
)
|
|
;
|
|
Uinstr0 = lc_spawn_off(LCRval0, LCSRval0, Label),
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
LCRval0, LCRval),
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
LCSRval0, LCSRval),
|
|
Uinstr = lc_spawn_off(LCRval, LCSRval, Label),
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
MaybeInstr = yes(Instr)
|
|
;
|
|
Uinstr0 = lc_join_and_terminate(LCRval0, LCSRval0),
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
LCRval0, LCRval),
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
LCSRval0, LCSRval),
|
|
Uinstr = lc_join_and_terminate(LCRval, LCSRval),
|
|
Instr = llds_instr(Uinstr, Comment),
|
|
MaybeInstr = yes(Instr)
|
|
;
|
|
( Uinstr0 = save_maxfr(Lval0)
|
|
; Uinstr0 = mark_hp(Lval0)
|
|
; Uinstr0 = store_ticket(Lval0)
|
|
; Uinstr0 = mark_ticket_stack(Lval0)
|
|
; Uinstr0 = lc_create_loop_control(_NumSlots, Lval0)
|
|
),
|
|
( if Lval0 = OldLval then
|
|
MaybeInstr = no
|
|
else if Lval0 = mem_ref(_) then
|
|
MaybeInstr = no
|
|
else
|
|
MaybeInstr = yes(Instr0)
|
|
)
|
|
;
|
|
( Uinstr0 = comment(_)
|
|
; Uinstr0 = livevals(_)
|
|
; Uinstr0 = restore_maxfr(_)
|
|
; Uinstr0 = prune_ticket
|
|
; Uinstr0 = discard_ticket
|
|
),
|
|
MaybeInstr = yes(Instr0)
|
|
;
|
|
( Uinstr0 = block(_, _, _)
|
|
; Uinstr0 = llcall(_, _, _, _, _, _)
|
|
; Uinstr0 = mkframe(_, _)
|
|
; Uinstr0 = label(_)
|
|
; Uinstr0 = goto(_)
|
|
; Uinstr0 = arbitrary_c_code(_, _, _)
|
|
; Uinstr0 = foreign_proc_code(_, _, _, _, _, _, _, _, _, _)
|
|
; Uinstr0 = push_region_frame(_, _)
|
|
; Uinstr0 = region_fill_frame(_, _, _, _, _)
|
|
; Uinstr0 = region_set_fixed_slot(_, _, _)
|
|
; Uinstr0 = use_and_maybe_pop_region_frame(_, _)
|
|
; Uinstr0 = incr_sp(_, _, _)
|
|
; Uinstr0 = decr_sp(_)
|
|
; Uinstr0 = decr_sp_and_return(_)
|
|
; Uinstr0 = init_sync_term(_, _, _)
|
|
; Uinstr0 = fork_new_child(_, _)
|
|
; Uinstr0 = join_and_continue(_, _)
|
|
),
|
|
MaybeInstr = no
|
|
).
|
|
|
|
:- pred replace_tagged_ptr_components_in_rval(lval::in, ptag::in, rval::in,
|
|
rval::in, rval::out) is det.
|
|
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
Rval0, Rval) :-
|
|
(
|
|
Rval0 = unop(UnOp, RvalA0),
|
|
( if
|
|
UnOp = tag,
|
|
RvalA0 = lval(OldLval)
|
|
then
|
|
OldPtag = ptag(OldPtagUint8),
|
|
Rval = const(llconst_int(uint8.cast_to_int(OldPtagUint8)))
|
|
else
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
RvalA0, RvalA),
|
|
Rval = unop(UnOp, RvalA)
|
|
)
|
|
;
|
|
Rval0 = binop(BinOp, RvalA0, RvalB0),
|
|
( if
|
|
BinOp = body,
|
|
RvalA0 = lval(OldLval),
|
|
RvalB0 = const(llconst_int(RvalB0Int)),
|
|
OldPtag = ptag(OldPtagUint8),
|
|
RvalB0Int = uint8.cast_to_int(OldPtagUint8),
|
|
OldBase = const(_)
|
|
then
|
|
Rval = OldBase
|
|
else
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
RvalA0, RvalA),
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
RvalB0, RvalB),
|
|
Rval = binop(BinOp, RvalA, RvalB)
|
|
)
|
|
;
|
|
Rval0 = mkword(Ptag, BaseRval0),
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
BaseRval0, BaseRval),
|
|
Rval = mkword(Ptag, BaseRval)
|
|
;
|
|
Rval0 = cast(Type, BaseRval0),
|
|
replace_tagged_ptr_components_in_rval(OldLval, OldPtag, OldBase,
|
|
BaseRval0, BaseRval),
|
|
Rval = cast(Type, BaseRval)
|
|
;
|
|
( Rval0 = lval(_)
|
|
; Rval0 = var(_)
|
|
; Rval0 = mkword_hole(_)
|
|
; Rval0 = const(_)
|
|
; Rval0 = mem_addr(_)
|
|
),
|
|
Rval = Rval0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Given a GC method, return the list of invalid peephole optimizations.
|
|
%
|
|
:- pred invalid_peephole_opts(gc_method::in, maybe_opt_peep_mkword::in,
|
|
list(pattern)::out) is det.
|
|
|
|
invalid_peephole_opts(GC_Method, OptPeepMkword, InvalidPatterns) :-
|
|
(
|
|
GC_Method = gc_accurate,
|
|
InvalidPatterns0 = [pattern_incr_sp]
|
|
;
|
|
( GC_Method = gc_automatic
|
|
; GC_Method = gc_none
|
|
; GC_Method = gc_boehm
|
|
; GC_Method = gc_boehm_debug
|
|
; GC_Method = gc_hgc
|
|
),
|
|
InvalidPatterns0 = []
|
|
),
|
|
(
|
|
OptPeepMkword = opt_peep_mkword,
|
|
InvalidPatterns = InvalidPatterns0
|
|
;
|
|
OptPeepMkword = do_not_opt_peep_mkword,
|
|
InvalidPatterns = [pattern_mkword | InvalidPatterns0]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
combine_decr_sp([], []).
|
|
combine_decr_sp([Instr0 | Instrs0], Instrs) :-
|
|
combine_decr_sp(Instrs0, Instrs1),
|
|
( if
|
|
Instr0 = llds_instr(assign(succip, lval(stackvar(N))), _),
|
|
opt_util.skip_comments_livevals(Instrs1, Instrs2),
|
|
Instrs2 = [Instr2 | Instrs3],
|
|
Instr2 = llds_instr(decr_sp(N), _),
|
|
opt_util.skip_comments_livevals(Instrs3, Instrs4),
|
|
Instrs4 = [Instr4 | Instrs5],
|
|
Instr4 = llds_instr(goto(code_succip), Comment)
|
|
then
|
|
NewInstr = llds_instr(decr_sp_and_return(N), Comment),
|
|
Instrs = [NewInstr | Instrs5]
|
|
else
|
|
Instrs = [Instr0 | Instrs1]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module ll_backend.peephole.
|
|
%-----------------------------------------------------------------------------%
|