Files
mercury/compiler/peephole.m
Zoltan Somogyi ea4f95a7ed Use var_tables in lco.m, and when dumping goals.
Since this is the first converted module that dumps out goals when
debugging trace flags are enabled, this required generalizing the code
that does that, to take either varsets or var_tables as a means of
specifying the names of variables. We do this via a new type,
var_name_source, which contains either a varset or a var_table.

Almost all of this diff is there to implement this generalization.
A large part of it affects code in the parse_tree package that we use
to write out the parts of HLDS goals that are defined by types defined
in that package. Since we want to avoid making any part of the parse_tree
package dependent on the hlds package, this required defining the
var_name_source type in the parse_tree package, which in turn requires
var_table.m to be in that same package.

compiler/lco.m:
    Convert this module to use var_tables instead of varsets and vartypes.

compiler/var_table.m:
    Move this module from the hlds package to the parse_tree package.

    To make this, possible, move the parts that required access to the HLDS
    to hlds_pred.m, from where it was usually invoked.

    Export some utility predicates to allow the moved code to work
    in hlds_pred.m without access to the actual definition of the
    var_table type.

    Define the var_name_source type.

    Add some utility functions for use by code writing out variable names.

compiler/hlds_pred.m:
    Add the code moved from var_table.m.

compiler/vartypes.m:
    Move this module from the hlds package to the parse_tree package,
    for symmetry with var_table.m. It did not depend on being in hlds
    in any way.

compiler/hlds.m:
compiler/parse_tree.m:
    Move vartypes.m and var_table.m from the hlds package
    to the parse_tree package.

compiler/hlds_out_goal.m:
    Change all the predicates in this module to take a var_name_source
    instead of a prog_varset.

    Fix some comments.

compiler/hlds_out_util.m:
    Change some of the predicates in this module (those called from
    hlds_out_goal.m) to take a var_name_source instead of a prog_varset.

compiler/parse_tree_out_term.m:
    Provide variants of some existing predicates and functions that take
    var_name_sources instead of varsets. The code of the copies
    duplicates the logic of the originals, though I hope that this
    duplication can be done away with at the end of the transition.
    (The best solution would be to use a typeclass with methods
    that convert vars to their names, but we would want to ensure
    that the compiler can specialize all the affected predicates
    and functions to the two instances of this typeclass, which is
    something that we cannot do yet. In the meantime, the lack of
    any generalization in the old versions preserves their performance.)

tools/sort_imports:
tools/filter_sort_imports:
    A new tool that automatically sorts any occurrences of consecutive
    ":- import_module" declarations in the named files. The sorting is done
    in filter_sort_imports; sort_imports loops over the named files.

    After automatically replacing all occurrences of hlds.{vartypes,var_table}
    in import_module declarations with their parse_tree versions, the updated
    import_module declarations were usually out of order with respect to
    their neighbours. I used this script to fix that, and some earlier
    out-of-order imports.

compiler/accumulator.m:
compiler/add_class.m:
compiler/add_clause.m:
compiler/add_foreign_proc.m:
compiler/add_heap_ops.m:
compiler/add_pragma_type_spec.m:
compiler/add_pred.m:
compiler/add_trail_ops.m:
compiler/analysis.m:
compiler/arg_info.m:
compiler/build_mode_constraints.m:
compiler/bytecode_gen.m:
compiler/call_gen.m:
compiler/check_promise.m:
compiler/closure_analysis.m:
compiler/closure_gen.m:
compiler/code_info.m:
compiler/code_loc_dep.m:
compiler/common.m:
compiler/compile_target_code.m:
compiler/complexity.m:
compiler/const_prop.m:
compiler/constraint.m:
compiler/continuation_info.m:
compiler/convert_parse_tree.m:
compiler/coverage_profiling.m:
compiler/cse_detection.m:
compiler/ctgc.datastruct.m:
compiler/ctgc.util.m:
compiler/dead_proc_elim.m:
compiler/deep_profiling.m:
compiler/deforest.m:
compiler/delay_construct.m:
compiler/delay_partial_inst.m:
compiler/dep_par_conj.m:
compiler/det_analysis.m:
compiler/det_report.m:
compiler/det_util.m:
compiler/direct_arg_in_out.m:
compiler/disj_gen.m:
compiler/distance_granularity.m:
compiler/equiv_type_hlds.m:
compiler/exception_analysis.m:
compiler/file_names.m:
compiler/float_regs.m:
compiler/follow_vars.m:
compiler/format_call.m:
compiler/generate_dep_d_files.m:
compiler/get_dependencies.m:
compiler/goal_expr_to_goal.m:
compiler/goal_mode.m:
compiler/goal_path.m:
compiler/goal_store.m:
compiler/goal_util.m:
compiler/granularity.m:
compiler/hhf.m:
compiler/higher_order.m:
compiler/hlds_clauses.m:
compiler/hlds_code_util.m:
compiler/hlds_error_util.m:
compiler/hlds_goal.m:
compiler/hlds_llds.m:
compiler/hlds_out_pred.m:
compiler/hlds_rtti.m:
compiler/hlds_statistics.m:
compiler/inlining.m:
compiler/inst_check.m:
compiler/inst_test.m:
compiler/inst_user.m:
compiler/instance_method_clauses.m:
compiler/instmap.m:
compiler/intermod.m:
compiler/intermod_analysis.m:
compiler/interval.m:
compiler/introduce_exists_casts.m:
compiler/introduce_parallelism.m:
compiler/item_util.m:
compiler/lambda.m:
compiler/live_vars.m:
compiler/liveness.m:
compiler/llds.m:
compiler/llds_out_data.m:
compiler/llds_out_file.m:
compiler/llds_out_util.m:
compiler/lookup_switch.m:
compiler/loop_inv.m:
compiler/make.module_target.m:
compiler/make.util.m:
compiler/make_goal.m:
compiler/make_hlds_separate_items.m:
compiler/make_hlds_types.m:
compiler/mark_tail_calls.m:
compiler/mercury_compile_mlds_back_end.m:
compiler/middle_rec.m:
compiler/ml_accurate_gc.m:
compiler/ml_args_util.m:
compiler/ml_call_gen.m:
compiler/ml_closure_gen.m:
compiler/ml_code_gen.m:
compiler/ml_code_util.m:
compiler/ml_commit_gen.m:
compiler/ml_disj_gen.m:
compiler/ml_foreign_proc_gen.m:
compiler/ml_gen_info.m:
compiler/ml_lookup_switch.m:
compiler/ml_proc_gen.m:
compiler/ml_simplify_switch.m:
compiler/ml_switch_gen.m:
compiler/ml_tag_switch.m:
compiler/ml_unify_gen.m:
compiler/ml_unify_gen_construct.m:
compiler/ml_unify_gen_deconstruct.m:
compiler/ml_unify_gen_test.m:
compiler/ml_unify_gen_util.m:
compiler/mlds_to_c_data.m:
compiler/mlds_to_c_func.m:
compiler/mlds_to_c_global.m:
compiler/mlds_to_cs_class.m:
compiler/mlds_to_cs_file.m:
compiler/mlds_to_java_data.m:
compiler/mlds_to_java_file.m:
compiler/mlds_to_java_stmt.m:
compiler/mlds_to_java_type.m:
compiler/mmc_analysis.m:
compiler/mode_comparison.m:
compiler/mode_constraints.m:
compiler/mode_debug.m:
compiler/mode_errors.m:
compiler/mode_info.m:
compiler/mode_ordering.m:
compiler/modecheck_call.m:
compiler/modecheck_coerce.m:
compiler/modecheck_goal.m:
compiler/modecheck_unify.m:
compiler/modecheck_util.m:
compiler/modes.m:
compiler/module_cmds.m:
compiler/old_type_constraints.m:
compiler/opt_debug.m:
compiler/optimize.m:
compiler/options_file.m:
compiler/ordering_mode_constraints.m:
compiler/par_loop_control.m:
compiler/parse_item.m:
compiler/parse_string_format.m:
compiler/parse_tree_out_inst.m:
compiler/parse_tree_to_term.m:
compiler/parse_util.m:
compiler/pd_debug.m:
compiler/pd_info.m:
compiler/pd_util.m:
compiler/peephole.m:
compiler/polymorphism.m:
compiler/polymorphism_info.m:
compiler/polymorphism_lambda.m:
compiler/polymorphism_type_class_info.m:
compiler/polymorphism_type_info.m:
compiler/post_typecheck.m:
compiler/pragma_c_gen.m:
compiler/pred_name.m:
compiler/pred_table.m:
compiler/prog_item.m:
compiler/prog_rep.m:
compiler/prop_mode_constraints.m:
compiler/purity.m:
compiler/push_goals_together.m:
compiler/qual_info.m:
compiler/quantification.m:
compiler/rbmm.execution_path.m:
compiler/rbmm.m:
compiler/rbmm.points_to_analysis.m:
compiler/rbmm.points_to_graph.m:
compiler/rbmm.points_to_info.m:
compiler/rbmm.region_resurrection_renaming.m:
compiler/rbmm.region_transformation.m:
compiler/recompilation.used_file.m:
compiler/recompilation.version.m:
compiler/recompute_instmap_deltas.m:
compiler/resolve_unify_functor.m:
compiler/rtti.m:
compiler/rtti_out.m:
compiler/rtti_to_mlds.m:
compiler/saved_vars.m:
compiler/set_of_var.m:
compiler/simplify_goal_call.m:
compiler/simplify_goal_conj.m:
compiler/simplify_goal_disj.m:
compiler/simplify_goal_ite.m:
compiler/simplify_goal_scope.m:
compiler/simplify_goal_switch.m:
compiler/simplify_goal_unify.m:
compiler/simplify_info.m:
compiler/simplify_proc.m:
compiler/size_prof.m:
compiler/smm_common.m:
compiler/ssdebug.m:
compiler/stack_alloc.m:
compiler/stack_layout.m:
compiler/stack_opt.m:
compiler/stm_expand.m:
compiler/store_alloc.m:
compiler/structure_reuse.analysis.m:
compiler/structure_reuse.direct.choose_reuse.m:
compiler/structure_reuse.direct.detect_garbage.m:
compiler/structure_reuse.domain.m:
compiler/structure_reuse.indirect.m:
compiler/structure_reuse.lbu.m:
compiler/structure_reuse.lfu.m:
compiler/structure_sharing.analysis.m:
compiler/structure_sharing.domain.m:
compiler/superhomogeneous.m:
compiler/switch_detection.m:
compiler/switch_gen.m:
compiler/switch_util.m:
compiler/table_gen.m:
compiler/tabling_analysis.m:
compiler/term_constr_build.m:
compiler/term_constr_data.m:
compiler/term_constr_initial.m:
compiler/term_constr_main.m:
compiler/term_constr_main_types.m:
compiler/term_constr_util.m:
compiler/term_pass1.m:
compiler/term_traversal.m:
compiler/term_util.m:
compiler/trace_gen.m:
compiler/trailing_analysis.m:
compiler/transform_llds.m:
compiler/try_expand.m:
compiler/tupling.m:
compiler/type_assign.m:
compiler/type_ctor_info.m:
compiler/type_util.m:
compiler/typecheck.m:
compiler/typecheck_debug.m:
compiler/typecheck_errors.m:
compiler/typecheck_info.m:
compiler/unify_gen_construct.m:
compiler/unify_gen_deconstruct.m:
compiler/unify_proc.m:
compiler/unique_modes.m:
compiler/unneeded_code.m:
compiler/untupling.m:
compiler/unused_args.m:
compiler/unused_imports.m:
compiler/var_locn.m:
compiler/write_deps_file.m:
compiler/write_module_interface_files.m:
    Conform to the changes above.
2022-04-18 02:00:38 +10:00

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 backend_libs.
:- import_module backend_libs.builtin_ops.
:- import_module hlds.
:- import_module hlds.hlds_data.
:- 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.
%-----------------------------------------------------------------------------%