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