mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
... and delete their second name.
compiler/options.m:
The internal names of many optimization options have traditionally
given no clue about which backend(s) they apply to, which made it
hard to document them. Change this by including either llds or mlds
in the names of these options, unless the rest of the name already
ruled out one or the other backend.
In one case, this exercise relevaled that an option that was classified
as MLDS-only is used by the LLDS backend as well. Reclassify this option.
Also, rename many options to the names they have in the opt_tuple.
Those names are more recent, and their naming is more consistent
(e.g. they are all verb phrases).
tools/make_optimization_options_db:
Delete the different-from-the-opt-tuple name alternatives.
tools/make_optimization_options_end:
Conform to the option renames.
Also, fix a bug. The list of options enabled at each optimization
level lists each option twice, once by its name in the opt_tuple
(which is what we use to actually implement optimization levels)
and once by its name in options.m (which we use to implement
the option that prints out the optimization levels). In one case,
the two names did not match; fix this.
compiler/optimization_options.m:
Rebuild this module with tools/make_optimization_options.
compiler/add_trail_ops.m:
compiler/global_data.m:
compiler/handle_options.m:
compiler/ite_gen.m:
compiler/jumpopt.m:
compiler/mercury_compile_llds_back_end.m:
compiler/mercury_compile_mlds_back_end.m:
compiler/ml_optimize.m:
compiler/optimize.m:
compiler/options.m:
compiler/proc_gen.m:
compiler/stack_layout.m:
Conform to the option renames.
tests/warnings/help_opt_levels.err_exp:
Expect the fix to make_optimization_options_end.
tests/warnings/help_text.err_exp:
Expect the option reclassification.
1560 lines
56 KiB
Mathematica
1560 lines
56 KiB
Mathematica
% ---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2000-2011 The University of Melbourne.
|
|
% Copyright (C) 2014-2021, 2024-2025 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: ml_optimize.m.
|
|
% Main author: trd, fjh.
|
|
%
|
|
% This module runs various optimizations on the MLDS.
|
|
%
|
|
% Currently the optimizations we do here are
|
|
%
|
|
% - converting assignments to local variables into variable initializers.
|
|
% - eliminating initialized local variables entirely,
|
|
% by replacing occurrences of such variables with their initializer
|
|
%
|
|
% Ultimately this module should just consist of a skeleton to traverse
|
|
% the MLDS, and should call various optimization modules along the way.
|
|
%
|
|
% It would probably be a good idea to make each transformation optional.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ml_backend.ml_optimize.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module ml_backend.mlds.
|
|
|
|
:- pred mlds_optimize(globals::in, mlds::in, mlds::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.builtin_ops.
|
|
:- import_module libs.optimization_options.
|
|
:- import_module libs.options.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.builtin_modules.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module ml_backend.ml_util.
|
|
:- import_module ml_backend.mlds_dump.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module bool.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
:- import_module set.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type opt_info
|
|
---> opt_info(
|
|
oi_globals :: globals,
|
|
oi_module_name :: mlds_module_name,
|
|
oi_func_name :: mlds_function_name,
|
|
oi_func_params :: mlds_func_params
|
|
).
|
|
|
|
mlds_optimize(Globals, !MLDS) :-
|
|
ModuleName = mercury_module_name_to_mlds(!.MLDS ^ mlds_name),
|
|
% XXX We should optimize functions stored inside classes as well.
|
|
FuncDefns0 = !.MLDS ^ mlds_proc_defns,
|
|
list.map(optimize_in_function_defn(ModuleName, Globals),
|
|
FuncDefns0, FuncDefns),
|
|
!MLDS ^ mlds_proc_defns := FuncDefns.
|
|
|
|
:- pred optimize_in_function_defn(mlds_module_name::in, globals::in,
|
|
mlds_function_defn::in, mlds_function_defn::out) is det.
|
|
|
|
optimize_in_function_defn(ModuleName, Globals, FuncDefn0, FuncDefn) :-
|
|
FuncDefn0 = mlds_function_defn(Name, Context, Flags, PredProcId,
|
|
Params, FuncBody0, EnvVarNames, MaybeRequireTailrecInfo),
|
|
OptInfo = opt_info(Globals, ModuleName, Name, Params),
|
|
optimize_in_function_body(OptInfo, FuncBody0, FuncBody),
|
|
FuncDefn = mlds_function_defn(Name, Context, Flags, PredProcId,
|
|
Params, FuncBody, EnvVarNames, MaybeRequireTailrecInfo).
|
|
|
|
:- pred optimize_in_function_body(opt_info::in,
|
|
mlds_function_body::in, mlds_function_body::out) is det.
|
|
|
|
optimize_in_function_body(OptInfo, !Body) :-
|
|
(
|
|
!.Body = body_external
|
|
;
|
|
!.Body = body_defined_here(Stmt0),
|
|
optimize_in_stmt(OptInfo, Stmt0, Stmt),
|
|
trace [compile_time(flag("debug_ml_optimize")), io(!IO)] (
|
|
Globals = OptInfo ^ oi_globals,
|
|
ModuleName =
|
|
mlds_module_name_to_sym_name(OptInfo ^ oi_module_name),
|
|
get_debug_output_stream(Globals, ModuleName, Stream, !IO),
|
|
io.write_string(Stream, "\nfunction body before\n\n", !IO),
|
|
dump_mlds_stmt(Stream, 1, Stmt0, !IO),
|
|
io.write_string(Stream, "\nfunction body after\n\n", !IO),
|
|
dump_mlds_stmt(Stream, 1, Stmt, !IO)
|
|
),
|
|
!:Body = body_defined_here(Stmt)
|
|
).
|
|
|
|
:- pred optimize_in_maybe_stmt(opt_info::in,
|
|
maybe(mlds_stmt)::in, maybe(mlds_stmt)::out) is det.
|
|
|
|
optimize_in_maybe_stmt(OptInfo, !MaybeStmt) :-
|
|
(
|
|
!.MaybeStmt = no
|
|
;
|
|
!.MaybeStmt = yes(Stmt0),
|
|
optimize_in_stmt(OptInfo, Stmt0, Stmt),
|
|
!:MaybeStmt = yes(Stmt)
|
|
).
|
|
|
|
:- pred optimize_in_stmts(opt_info::in,
|
|
list(mlds_stmt)::in, list(mlds_stmt)::out) is det.
|
|
|
|
optimize_in_stmts(OptInfo, !Stmts) :-
|
|
list.map(optimize_in_stmt(OptInfo), !Stmts),
|
|
Globals = OptInfo ^ oi_globals,
|
|
globals.get_opt_tuple(Globals, OptTuple),
|
|
OptPeep = OptTuple ^ ot_peep_mlds,
|
|
(
|
|
OptPeep = do_not_peep_mlds
|
|
;
|
|
OptPeep = peep_mlds,
|
|
peephole_opt_statements(!Stmts)
|
|
).
|
|
|
|
:- pred optimize_in_stmt(opt_info::in,
|
|
mlds_stmt::in, mlds_stmt::out) is det.
|
|
|
|
optimize_in_stmt(OptInfo, Stmt0, Stmt) :-
|
|
(
|
|
Stmt0 = ml_stmt_call(_, _, _, _, _, _),
|
|
optimize_in_call_stmt(OptInfo, Stmt0, Stmt)
|
|
;
|
|
Stmt0 = ml_stmt_block(LocalVarDefns0, FuncDefns0, SubStmts0, Context),
|
|
maybe_convert_assignments_into_initializers(OptInfo,
|
|
LocalVarDefns0, LocalVarDefns1, SubStmts0, SubStmts1),
|
|
maybe_eliminate_locals(OptInfo, LocalVarDefns1, LocalVarDefns,
|
|
FuncDefns0, FuncDefns, SubStmts1, SubStmts2),
|
|
maybe_flatten_block(SubStmts2, SubStmts3),
|
|
optimize_in_stmts(OptInfo, SubStmts3, SubStmts),
|
|
% XXX We should also optimize in FuncDefns.
|
|
( if
|
|
LocalVarDefns = [],
|
|
FuncDefns = [],
|
|
SubStmts = [SubStmt]
|
|
then
|
|
Stmt = SubStmt
|
|
else
|
|
Stmt = ml_stmt_block(LocalVarDefns, FuncDefns, SubStmts, Context)
|
|
)
|
|
;
|
|
Stmt0 = ml_stmt_while(Kind, Rval, SubStmts0, LocalLoopVars, Context),
|
|
optimize_in_stmt(OptInfo, SubStmts0, SubStmts),
|
|
Stmt = ml_stmt_while(Kind, Rval, SubStmts, LocalLoopVars, Context)
|
|
;
|
|
Stmt0 = ml_stmt_if_then_else(Rval, Then0, MaybeElse0, Context),
|
|
optimize_in_stmt(OptInfo, Then0, Then),
|
|
optimize_in_maybe_stmt(OptInfo, MaybeElse0, MaybeElse),
|
|
( if
|
|
Then = ml_stmt_block([], [], [], _),
|
|
MaybeElse = yes(Else)
|
|
then
|
|
NotRval = ml_unop(logical_not, Rval),
|
|
Stmt = ml_stmt_if_then_else(NotRval, Else, no, Context)
|
|
else
|
|
Stmt = ml_stmt_if_then_else(Rval, Then, MaybeElse, Context)
|
|
)
|
|
;
|
|
Stmt0 = ml_stmt_switch(Type, Rval, Range, Cases0, Default0, Context),
|
|
list.map(optimize_in_case(OptInfo), Cases0, Cases),
|
|
optimize_in_default(OptInfo, Default0, Default),
|
|
Stmt = ml_stmt_switch(Type, Rval, Range, Cases, Default, Context)
|
|
;
|
|
Stmt0 = ml_stmt_try_commit(Ref, BodyStmt0, HandlerStmt0, Context),
|
|
optimize_in_stmt(OptInfo, BodyStmt0, BodyStmt),
|
|
optimize_in_stmt(OptInfo, HandlerStmt0, HandlerStmt),
|
|
Stmt = ml_stmt_try_commit(Ref, BodyStmt, HandlerStmt, Context)
|
|
;
|
|
( Stmt0 = ml_stmt_do_commit(_, _)
|
|
; Stmt0 = ml_stmt_return(_, _)
|
|
; Stmt0 = ml_stmt_label(_Label, _)
|
|
; Stmt0 = ml_stmt_goto(_Label, _)
|
|
; Stmt0 = ml_stmt_computed_goto(_Rval, _Label, _)
|
|
),
|
|
Stmt = Stmt0
|
|
;
|
|
Stmt0 = ml_stmt_atomic(Atomic0, Context),
|
|
( if Atomic0 = assign(TargetLval, SourceRval) then
|
|
( if
|
|
% Optimize away assignments to the dummy var, since it *should*
|
|
% only ever be assigned to; it should never be read.
|
|
TargetLval = ml_global_var(TargetGlobalVar, _TargetType),
|
|
TargetGlobalVar = global_dummy_var
|
|
then
|
|
Stmt = ml_stmt_block([], [], [], Context)
|
|
else if
|
|
% The only time the global dummy var seems to be read
|
|
% (during a bootcheck, at least) is to test whether it is
|
|
% equal to itself.
|
|
%
|
|
% We transform A == A into TRUE even if A is not a reference
|
|
% to the dummy variable, since the transformation is valid for
|
|
% any A (since mlds rvals can't cause side-effects), and
|
|
% thus testing for the dummy variable is an unnecessary cost.
|
|
%
|
|
% We don't test for other patterns (such as transforming
|
|
% A != A into FALSE) because I (zs) haven't seen the
|
|
% Mercury compiler generating such code.
|
|
SourceRval = ml_binop(BinOp, RvalA, RvalB),
|
|
RvalA = RvalB,
|
|
BinOp = int_cmp(_, eq)
|
|
then
|
|
Result = ml_const(mlconst_true),
|
|
Atomic = assign(TargetLval, Result),
|
|
Stmt = ml_stmt_atomic(Atomic, Context)
|
|
else
|
|
Stmt = Stmt0
|
|
)
|
|
else
|
|
Stmt = Stmt0
|
|
)
|
|
).
|
|
|
|
:- pred optimize_in_case(opt_info::in,
|
|
mlds_switch_case::in, mlds_switch_case::out) is det.
|
|
|
|
optimize_in_case(OptInfo, Case0, Case) :-
|
|
Case0 = mlds_switch_case(FirstCond, LaterConds, Stmt0),
|
|
optimize_in_stmt(OptInfo, Stmt0, Stmt),
|
|
Case = mlds_switch_case(FirstCond, LaterConds, Stmt).
|
|
|
|
:- pred optimize_in_default(opt_info::in,
|
|
mlds_switch_default::in, mlds_switch_default::out) is det.
|
|
|
|
optimize_in_default(OptInfo, Default0, Default) :-
|
|
(
|
|
Default0 = default_is_unreachable,
|
|
Default = default_is_unreachable
|
|
;
|
|
Default0 = default_do_nothing,
|
|
Default = default_do_nothing
|
|
;
|
|
Default0 = default_case(Stmt0),
|
|
optimize_in_stmt(OptInfo, Stmt0, Stmt),
|
|
Default = default_case(Stmt)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred optimize_in_call_stmt(opt_info::in,
|
|
mlds_stmt::in(ml_stmt_is_call), mlds_stmt::out) is det.
|
|
|
|
optimize_in_call_stmt(_OptInfo, Stmt0, Stmt) :-
|
|
Stmt0 = ml_stmt_call(_Signature, FuncRval, CallArgRvals, _Results,
|
|
_IsTailCall, Context),
|
|
% If we have a self-tailcall, assign to the arguments and
|
|
% then goto the top of the tailcall loop.
|
|
( if
|
|
% Convert calls to `mark_hp' and `restore_hp' to the corresponding
|
|
% MLDS instructions. This ensures that they get generated as
|
|
% inline code. (Without this they won't, since HLDS inlining doesn't
|
|
% get run again after the add_heap_ops pass that adds these calls.)
|
|
% This approach is better than running HLDS inlining again,
|
|
% because it cheaper in compilation time.
|
|
|
|
FuncRval = ml_const(mlconst_code_addr(CodeAddr)),
|
|
CodeAddr = mlds_code_addr(QualFuncLabel, _CodeAddrSignature),
|
|
QualFuncLabel = qual_func_label(ModName, FuncLabel),
|
|
FuncLabel = mlds_func_label(ProcLabel, MaybeAux),
|
|
MaybeAux = proc_func,
|
|
ProcLabel = mlds_proc_label(PredLabel, _ProcId),
|
|
PredLabel = mlds_user_pred_label(pf_predicate, _DefnModName, PredName,
|
|
_Arity),
|
|
(
|
|
PredName = "mark_hp",
|
|
CallArgRvals = [ml_mem_addr(Lval)],
|
|
AtomicStmt = mark_hp(Lval)
|
|
;
|
|
PredName = "restore_hp",
|
|
CallArgRvals = [Rval],
|
|
AtomicStmt = restore_hp(Rval)
|
|
),
|
|
PrivateBuiltin = mercury_private_builtin_module,
|
|
ModName = mercury_module_name_to_mlds(PrivateBuiltin)
|
|
then
|
|
Stmt = ml_stmt_atomic(AtomicStmt, Context)
|
|
else
|
|
Stmt = Stmt0
|
|
).
|
|
|
|
%----------------------------------------------------------------------------
|
|
|
|
% If the list of statements contains a block with no local variables,
|
|
% then bring the block up one level. This optimization is needed to avoid
|
|
% a compiler limit in the Microsoft C compiler (version 13.10.3077) for
|
|
% too deeply nested blocks.
|
|
%
|
|
:- pred maybe_flatten_block(list(mlds_stmt)::in, list(mlds_stmt)::out) is det.
|
|
|
|
maybe_flatten_block(!Stmts) :-
|
|
!:Stmts = list.condense(list.map(flatten_block, !.Stmts)).
|
|
|
|
:- func flatten_block(mlds_stmt) = list(mlds_stmt).
|
|
|
|
flatten_block(Stmt) = Stmts :-
|
|
( if Stmt = ml_stmt_block([], [], BlockStmts, _) then
|
|
Stmts = BlockStmts
|
|
else
|
|
Stmts = [Stmt]
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred peephole_opt_statements(list(mlds_stmt)::in, list(mlds_stmt)::out)
|
|
is det.
|
|
|
|
peephole_opt_statements([], []).
|
|
peephole_opt_statements([Stmt0], [Stmt0]).
|
|
peephole_opt_statements([Stmt0, Stmt1 | Stmts2], Stmts) :-
|
|
( if peephole_opt_statement(Stmt0, Stmt1, Stmts2, ReplStmts) then
|
|
peephole_opt_statements(ReplStmts, Stmts)
|
|
else
|
|
peephole_opt_statements([Stmt1 | Stmts2], StmtsTail),
|
|
Stmts = [Stmt0 | StmtsTail]
|
|
).
|
|
|
|
:- pred peephole_opt_statement(mlds_stmt::in, mlds_stmt::in,
|
|
list(mlds_stmt)::in, list(mlds_stmt)::out) is semidet.
|
|
|
|
peephole_opt_statement(Stmt0, Stmt1, Stmts2, Stmts) :-
|
|
( if
|
|
% This pattern optimizes redundant tests like this:
|
|
%
|
|
% if (TestRval) {
|
|
% ...
|
|
% } else {
|
|
% ...
|
|
% }
|
|
% if (TestRval) {
|
|
% ...
|
|
% } else {
|
|
% ...
|
|
% }
|
|
%
|
|
% If neither the then-part nor the else-part of the first if-then-else
|
|
% can update any lval in TestRval, then the second test is redundant,
|
|
% and we therefore optimize it away.
|
|
%
|
|
% The pattern seems to occur mostly with semidet deconstructions.
|
|
% The semidet deconstruction tests whether the variable has the right
|
|
% functor, the then-part of that if-then-else picks up its argument
|
|
% values, there is no else part, and the next statement starts by
|
|
% testing whether the previous one succeeded, using the exact same
|
|
% condition as the semidet deconstruction.
|
|
%
|
|
% In theory, we could also apply the pattern if there were some other
|
|
% non-TestRval-affecting statements between the if-then-elses. However,
|
|
% I (zs) have not (yet) seen any code like that.
|
|
|
|
Stmt0 = ml_stmt_if_then_else(TestRval, StmtThen0, MaybeStmtElse0,
|
|
Context0),
|
|
Stmt1 = ml_stmt_if_then_else(TestRval, StmtThen1, MaybeStmtElse1,
|
|
_Context1),
|
|
find_rval_component_lvals(TestRval, set.init, TestRvalComponents),
|
|
statement_affects_lvals(TestRvalComponents, StmtThen0, no),
|
|
(
|
|
MaybeStmtElse0 = no
|
|
;
|
|
MaybeStmtElse0 = yes(StmtElse0),
|
|
statement_affects_lvals(TestRvalComponents, StmtElse0, no)
|
|
)
|
|
then
|
|
ContextThen = get_mlds_stmt_context(StmtThen0),
|
|
ThenBlockStmts0 = [StmtThen0, StmtThen1],
|
|
maybe_flatten_block(ThenBlockStmts0, ThenBlockStmts),
|
|
Then = ml_stmt_block([], [], ThenBlockStmts, ContextThen),
|
|
(
|
|
MaybeStmtElse0 = no,
|
|
(
|
|
MaybeStmtElse1 = no,
|
|
MaybeElse = no
|
|
;
|
|
MaybeStmtElse1 = yes(_),
|
|
MaybeElse = MaybeStmtElse1
|
|
)
|
|
;
|
|
MaybeStmtElse0 = yes(Else0),
|
|
(
|
|
MaybeStmtElse1 = no,
|
|
MaybeElse = MaybeStmtElse0
|
|
;
|
|
MaybeStmtElse1 = yes(Else1),
|
|
ElseBlockStmts0 = [Else0, Else1],
|
|
maybe_flatten_block(ElseBlockStmts0, ElseBlockStmts),
|
|
Else = ml_stmt_block([], [], ElseBlockStmts, Context0),
|
|
MaybeElse = yes(Else)
|
|
)
|
|
),
|
|
Stmt = ml_stmt_if_then_else(TestRval, Then, MaybeElse, Context0),
|
|
Stmts = [Stmt | Stmts2]
|
|
else if
|
|
% This pattern optimizes code like this, which we generate often
|
|
% in automatically defined type-specific comparison predicates:
|
|
%
|
|
% succeeded = (X == Y);
|
|
% succeeded = !(succeeded);
|
|
%
|
|
% This pattern replaces that with
|
|
%
|
|
% succeeded = (X != Y);
|
|
%
|
|
% because it reduces .c file size, because it may yield a speedup
|
|
% (depending on C compiler optimizations), and because it is easier
|
|
% to read when debugging generated C code.
|
|
%
|
|
% We do this only if the assignment operation is assign itself.
|
|
% We could do the same with assign_if_in_heap operations as well,
|
|
% but I (zs) have seen no need for that.
|
|
%
|
|
% Likewise, we could check for {float,str}_{eq,ne,lt,le,gt,ge}
|
|
% as well as for their integer versions, but again, I have seen
|
|
% no need for that.
|
|
|
|
% We test for the negation operation first, since we want to fail fast
|
|
% in the usual case where the pattern does not apply, and unary ops,
|
|
% and self-negations in particular, occur less frequently than
|
|
% binary ops and comparisons.
|
|
Stmt1 = ml_stmt_atomic(Atomic1, _Context1),
|
|
Atomic1 = assign(Lval, ml_unop(logical_not, ml_lval(Lval))),
|
|
|
|
Stmt0 = ml_stmt_atomic(Atomic0, Context0),
|
|
Atomic0 = assign(Lval, ml_binop(CompareOp, CmpRvalA, CmpRvalB)),
|
|
|
|
CompareOp = int_cmp(IntType, CmpOp),
|
|
NegCompareOp = int_cmp(IntType, negate_cmp_op(CmpOp))
|
|
then
|
|
Atomic = assign(Lval, ml_binop(NegCompareOp, CmpRvalA, CmpRvalB)),
|
|
Stmt = ml_stmt_atomic(Atomic, Context0),
|
|
Stmts = [Stmt | Stmts2]
|
|
else
|
|
fail
|
|
).
|
|
|
|
:- pred find_rval_component_lvals(mlds_rval::in,
|
|
set(mlds_lval)::in, set(mlds_lval)::out) is det.
|
|
|
|
find_rval_component_lvals(Rval, !Components) :-
|
|
(
|
|
Rval = ml_lval(Lval),
|
|
set.insert(Lval, !Components),
|
|
find_lval_component_lvals(Lval, !Components)
|
|
;
|
|
Rval = ml_mkword(_, SubRval),
|
|
find_rval_component_lvals(SubRval, !Components)
|
|
;
|
|
( Rval = ml_box(_, SubRvalA)
|
|
; Rval = ml_unbox(_, SubRvalA)
|
|
; Rval = ml_cast(_, SubRvalA)
|
|
; Rval = ml_unop(_, SubRvalA)
|
|
),
|
|
find_rval_component_lvals(SubRvalA, !Components)
|
|
;
|
|
Rval = ml_binop(_, SubRvalA, SubRvalB),
|
|
find_rval_component_lvals(SubRvalA, !Components),
|
|
find_rval_component_lvals(SubRvalB, !Components)
|
|
;
|
|
Rval = ml_mem_addr(Lval),
|
|
set.insert(Lval, !Components),
|
|
find_lval_component_lvals(Lval, !Components)
|
|
;
|
|
( Rval = ml_const(_)
|
|
; Rval = ml_scalar_common(_)
|
|
; Rval = ml_scalar_common_addr(_)
|
|
; Rval = ml_vector_common_row_addr(_, _)
|
|
; Rval = ml_self(_)
|
|
)
|
|
).
|
|
|
|
:- pred find_lval_component_lvals(mlds_lval::in,
|
|
set(mlds_lval)::in, set(mlds_lval)::out) is det.
|
|
|
|
find_lval_component_lvals(Lval, !Components) :-
|
|
(
|
|
( Lval = ml_field(_, Rval, _, _, _)
|
|
; Lval = ml_mem_ref(Rval, _)
|
|
),
|
|
find_rval_component_lvals(Rval, !Components)
|
|
;
|
|
( Lval = ml_target_global_var_ref(_)
|
|
; Lval = ml_global_var(_, _)
|
|
; Lval = ml_local_var(_, _)
|
|
)
|
|
).
|
|
|
|
:- pred statement_affects_lvals(set(mlds_lval)::in,
|
|
mlds_stmt::in, bool::out) is det.
|
|
|
|
statement_affects_lvals(Lvals, Stmt, Affects) :-
|
|
(
|
|
Stmt = ml_stmt_block(_, _, SubStmts, _),
|
|
statements_affect_lvals(Lvals, SubStmts, Affects)
|
|
;
|
|
Stmt = ml_stmt_while(_, _, SubStmt, _, _),
|
|
statement_affects_lvals(Lvals, SubStmt, Affects)
|
|
;
|
|
Stmt = ml_stmt_if_then_else(_, Then, MaybeElse, _),
|
|
(
|
|
MaybeElse = no,
|
|
Stmts = [Then]
|
|
;
|
|
MaybeElse = yes(Else),
|
|
Stmts = [Then, Else]
|
|
),
|
|
statements_affect_lvals(Lvals, Stmts, Affects)
|
|
;
|
|
Stmt = ml_stmt_switch(_, _, _, Cases, Default, _),
|
|
cases_affect_lvals(Lvals, Cases, Affects0),
|
|
(
|
|
Affects0 = yes,
|
|
Affects = yes
|
|
;
|
|
Affects0 = no,
|
|
(
|
|
( Default = default_is_unreachable
|
|
; Default = default_do_nothing
|
|
),
|
|
Affects = no
|
|
;
|
|
Default = default_case(DefaultStmt),
|
|
statement_affects_lvals(Lvals, DefaultStmt, Affects)
|
|
)
|
|
)
|
|
;
|
|
Stmt = ml_stmt_label(_, _),
|
|
Affects = no
|
|
;
|
|
( Stmt = ml_stmt_goto(_, _)
|
|
; Stmt = ml_stmt_computed_goto(_, _, _)
|
|
; Stmt = ml_stmt_try_commit(_, _, _, _)
|
|
; Stmt = ml_stmt_do_commit(_, _)
|
|
; Stmt = ml_stmt_return(_, _)
|
|
),
|
|
Affects = yes
|
|
;
|
|
Stmt = ml_stmt_call(_, _, _, _, _, _),
|
|
% A call can update local variables even without referring to them
|
|
% explicitly, by referring to the environment in which they reside.
|
|
Affects = yes
|
|
;
|
|
Stmt = ml_stmt_atomic(AtomicStmt, _),
|
|
(
|
|
( AtomicStmt = comment(_)
|
|
; AtomicStmt = delete_object(_)
|
|
; AtomicStmt = gc_check
|
|
; AtomicStmt = restore_hp(_)
|
|
; AtomicStmt = trail_op(_)
|
|
),
|
|
Affects = no
|
|
;
|
|
( AtomicStmt = assign(Lval, _)
|
|
; AtomicStmt = assign_if_in_heap(Lval, _)
|
|
; AtomicStmt = new_object(Lval, _, _, _, _, _, _, _, _)
|
|
; AtomicStmt = mark_hp(Lval)
|
|
),
|
|
( if set.contains(Lvals, Lval) then
|
|
Affects = yes
|
|
else
|
|
Affects = no
|
|
)
|
|
;
|
|
( AtomicStmt = inline_target_code(_, _) % XXX could be improved
|
|
; AtomicStmt = outline_foreign_proc(_, _, _, _)
|
|
),
|
|
Affects = yes
|
|
)
|
|
).
|
|
|
|
:- pred statements_affect_lvals(set(mlds_lval)::in,
|
|
list(mlds_stmt)::in, bool::out) is det.
|
|
|
|
statements_affect_lvals(_, [], no).
|
|
statements_affect_lvals(Lvals, [Head | Tail], Affects) :-
|
|
statement_affects_lvals(Lvals, Head, HeadAffects),
|
|
(
|
|
HeadAffects = yes,
|
|
Affects = yes
|
|
;
|
|
HeadAffects = no,
|
|
statements_affect_lvals(Lvals, Tail, Affects)
|
|
).
|
|
|
|
:- pred cases_affect_lvals(set(mlds_lval)::in,
|
|
list(mlds_switch_case)::in, bool::out) is det.
|
|
|
|
cases_affect_lvals(_, [], no).
|
|
cases_affect_lvals(Lvals, [Head | Tail], Affects) :-
|
|
Head = mlds_switch_case(_, _, Stmt),
|
|
statement_affects_lvals(Lvals, Stmt, HeadAffects),
|
|
(
|
|
HeadAffects = yes,
|
|
Affects = yes
|
|
;
|
|
HeadAffects = no,
|
|
cases_affect_lvals(Lvals, Tail, Affects)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
%
|
|
% This code implements the --optimize-initializations option.
|
|
% It converts MLDS code using assignments, e.g.
|
|
%
|
|
% {
|
|
% int v1; // or any other type -- it doesn't have to be int
|
|
% int v2;
|
|
% int v3;
|
|
% int v4;
|
|
% int v5;
|
|
%
|
|
% v1 = 1;
|
|
% v2 = 2;
|
|
% v3 = 3;
|
|
% foo();
|
|
% v4 = 4;
|
|
% ...
|
|
% }
|
|
%
|
|
% into code that instead uses initializers, e.g.
|
|
%
|
|
% {
|
|
% int v1 = 1;
|
|
% int v2 = 2;
|
|
% int v3 = 3;
|
|
% int v4;
|
|
%
|
|
% foo();
|
|
% v4 = 4;
|
|
% ...
|
|
% }
|
|
%
|
|
% Note that if there are multiple initializations of the same variable,
|
|
% then we will apply the optimization successively, replacing the existing
|
|
% initializers as we go, and keeping only the last, e.g.
|
|
%
|
|
% int v = 1;
|
|
% v = 2;
|
|
% v = 3;
|
|
% ...
|
|
%
|
|
% will get replaced with
|
|
%
|
|
% int v = 3;
|
|
% ...
|
|
%
|
|
% We need to watch out for some tricky cases that can't be safely optimized.
|
|
% If the RHS of the assignment refers to a variable which was declared after
|
|
% the variable whose initialization we are optimizing, e.g.
|
|
%
|
|
% int v1 = 1;
|
|
% int v2 = 0;
|
|
% v1 = v2 + 1; // RHS refers to variable declared after v1
|
|
%
|
|
% then we can't do the optimization because it would cause a forward reference:
|
|
%
|
|
% int v1 = v2 + 1; // error -- v2 not declared yet!
|
|
% int v2 = 0;
|
|
%
|
|
% Likewise if the RHS refers to the variable itself
|
|
%
|
|
% int v1 = 1;
|
|
% v1 = v1 + 1;
|
|
%
|
|
% then we can't optimize it, because that would be bogus:
|
|
%
|
|
% int v1 = v1 + 1; // error -- v1 not initialized yet!
|
|
%
|
|
% Similarly, if the initializers of the variables that follow
|
|
% the one we are trying to optimize refer to it, e.g.
|
|
%
|
|
% int v1 = 1;
|
|
% int v2 = v1 + 1; // here v2 == 2
|
|
% v1 = 0;
|
|
% ...
|
|
%
|
|
% then we can't eliminate the assignment, because that would produce
|
|
% different results:
|
|
%
|
|
% int v1 = 0;
|
|
% int v2 = v1 + 1; // wrong -- v2 == 1
|
|
% ...
|
|
|
|
:- pred maybe_convert_assignments_into_initializers(opt_info::in,
|
|
list(mlds_local_var_defn)::in, list(mlds_local_var_defn)::out,
|
|
list(mlds_stmt)::in, list(mlds_stmt)::out) is det.
|
|
|
|
maybe_convert_assignments_into_initializers(OptInfo, !Defns, !Stmts) :-
|
|
Globals = OptInfo ^ oi_globals,
|
|
% Check if --optimize-initializations is enabled.
|
|
globals.get_opt_tuple(Globals, OptTuple),
|
|
OptInit = OptTuple ^ ot_opt_mlds_inits,
|
|
(
|
|
OptInit = opt_mlds_inits,
|
|
convert_assignments_into_initializers(OptInfo, !Defns, !Stmts)
|
|
;
|
|
OptInit = do_not_opt_mlds_inits
|
|
).
|
|
|
|
:- pred convert_assignments_into_initializers(opt_info::in,
|
|
list(mlds_local_var_defn)::in, list(mlds_local_var_defn)::out,
|
|
list(mlds_stmt)::in, list(mlds_stmt)::out) is det.
|
|
|
|
convert_assignments_into_initializers(_OptInfo, !LocalVarDefns, [], []).
|
|
convert_assignments_into_initializers(OptInfo, !LocalVarDefns,
|
|
[HeadStmt0 | TailStmts0], Stmts) :-
|
|
( if HeadStmt0 = ml_stmt_atomic(AtomicHeadStmt0, _) then
|
|
( if
|
|
% Check if the first statement in the block is an assignment
|
|
% to one of the variables declared in the block.
|
|
AtomicHeadStmt0 = assign(LHS, RHS),
|
|
LHS = ml_local_var(ThisVarName, _ThisVarType),
|
|
|
|
% We must check that the value being assigned doesn't refer to the
|
|
% variable itself.
|
|
rval_contains_var(RHS, ThisVarName) = no,
|
|
|
|
% We must check that the value being assigned doesn't refer to any
|
|
% of the variables which are declared after this one. We must also
|
|
% check that the initializers (if any) of the variables that follow
|
|
% this one don't refer to this variable.
|
|
find_this_var_defn(ThisVarName, !.LocalVarDefns, [], RevPrevDefns,
|
|
ThisVarDefn0, LaterDefns),
|
|
Filter =
|
|
( pred(OtherLocalVarDefn::in) is semidet :-
|
|
OtherLocalVarDefn = mlds_local_var_defn(OtherVarName, _,
|
|
_Type, OtherInitializer, _GC),
|
|
(
|
|
rval_contains_var(RHS, OtherVarName) = yes
|
|
;
|
|
initializer_contains_var(OtherInitializer, ThisVarName)
|
|
= yes
|
|
)
|
|
),
|
|
not list.find_first_match(Filter, LaterDefns, _)
|
|
then
|
|
% Replace the assignment statement with an initializer
|
|
% on the variable declaration.
|
|
ThisVarDefn = ThisVarDefn0 ^ mlvd_init := init_obj(RHS),
|
|
!:LocalVarDefns = list.reverse(RevPrevDefns) ++
|
|
[ThisVarDefn | LaterDefns],
|
|
|
|
% Now try to apply the same optimization again.
|
|
convert_assignments_into_initializers(OptInfo, !LocalVarDefns,
|
|
TailStmts0, Stmts)
|
|
else if
|
|
AtomicHeadStmt0 = comment(_)
|
|
then
|
|
convert_assignments_into_initializers(OptInfo, !LocalVarDefns,
|
|
TailStmts0, TailStmts),
|
|
Stmts = [HeadStmt0 | TailStmts]
|
|
else
|
|
% No optimization possible -- leave the block unchanged.
|
|
Stmts = [HeadStmt0 | TailStmts0]
|
|
)
|
|
else
|
|
% No optimization possible -- leave the block unchanged.
|
|
Stmts = [HeadStmt0 | TailStmts0]
|
|
).
|
|
|
|
:- pred find_this_var_defn(mlds_local_var_name::in,
|
|
list(mlds_local_var_defn)::in,
|
|
list(mlds_local_var_defn)::in, list(mlds_local_var_defn)::out,
|
|
mlds_local_var_defn::out, list(mlds_local_var_defn)::out) is semidet.
|
|
|
|
find_this_var_defn(VarName, [LocalVarDefn | LocalVarDefns], !RevPrevDefns,
|
|
ThisVarDefn, LaterDefns) :-
|
|
( if LocalVarDefn = mlds_local_var_defn(VarName, _, _, _, _) then
|
|
ThisVarDefn = LocalVarDefn,
|
|
LaterDefns = LocalVarDefns
|
|
else
|
|
!:RevPrevDefns = [LocalVarDefn | !.RevPrevDefns],
|
|
find_this_var_defn(VarName, LocalVarDefns, !RevPrevDefns,
|
|
ThisVarDefn, LaterDefns)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% This is a pass to eliminate initialized local variable definitions,
|
|
% by substituting the value of the initializer for occurrences of the variable.
|
|
%
|
|
% XXX This is quadratic in the number of variable definitions, since we do
|
|
% one pass over the block per variable definition. A more efficient algorithm
|
|
% would be to do one pass to figure out which variables could be eliminated,
|
|
% and then do another pass to actually eliminate them.
|
|
|
|
:- pred maybe_eliminate_locals(opt_info::in,
|
|
list(mlds_local_var_defn)::in, list(mlds_local_var_defn)::out,
|
|
list(mlds_function_defn)::in, list(mlds_function_defn)::out,
|
|
list(mlds_stmt)::in, list(mlds_stmt)::out) is det.
|
|
|
|
maybe_eliminate_locals(OptInfo, !LocalVarDefns, !FuncDefns, !Stmts) :-
|
|
Globals = OptInfo ^ oi_globals,
|
|
globals.get_opt_tuple(Globals, OptTuple),
|
|
EliminateLocalVars = OptTuple ^ ot_elim_local_vars_mlds,
|
|
(
|
|
EliminateLocalVars = elim_local_vars_mlds,
|
|
eliminate_locals(OptInfo, !LocalVarDefns, !FuncDefns, !Stmts)
|
|
;
|
|
EliminateLocalVars = do_not_elim_local_vars_mlds
|
|
).
|
|
|
|
:- pred eliminate_locals(opt_info::in,
|
|
list(mlds_local_var_defn)::in, list(mlds_local_var_defn)::out,
|
|
list(mlds_function_defn)::in, list(mlds_function_defn)::out,
|
|
list(mlds_stmt)::in, list(mlds_stmt)::out) is det.
|
|
|
|
eliminate_locals(_OptInfo, [], [], !FuncDefns, !Stmts).
|
|
eliminate_locals(OptInfo, [LocalVarDefn0 | LocalVarDefns0], LocalVarDefns,
|
|
!FuncDefns, !Stmts) :-
|
|
( if
|
|
try_to_eliminate_defn(OptInfo, LocalVarDefn0, LocalVarDefns0,
|
|
LocalVarDefns1, !FuncDefns, !Stmts)
|
|
then
|
|
eliminate_locals(OptInfo, LocalVarDefns1, LocalVarDefns,
|
|
!FuncDefns, !Stmts)
|
|
else
|
|
eliminate_locals(OptInfo, LocalVarDefns0, LocalVarDefns2,
|
|
!FuncDefns, !Stmts),
|
|
LocalVarDefns = [LocalVarDefn0 | LocalVarDefns2]
|
|
).
|
|
|
|
% This data structure holds information that we use in this pass
|
|
% to eliminate initialized local variable definitions.
|
|
:- type var_elim_info
|
|
---> var_elim_info(
|
|
% These fields remain constant.
|
|
|
|
% The name of the variable to eliminate.
|
|
var_name :: mlds_local_var_name,
|
|
|
|
% The value to replace the eliminated variable with.
|
|
var_value :: mlds_rval,
|
|
|
|
% These get updated as we go along.
|
|
|
|
% The number of occurrences of the variable.
|
|
replace_count :: int,
|
|
|
|
% `yes' if the optimization can't be applied, e.g. because
|
|
% the variable was assigned to, or because its address
|
|
% was taken.
|
|
invalidated :: bool
|
|
).
|
|
|
|
% Check if this definition is a variable that we can eliminate.
|
|
% If so, replace uses of this variable with the variable's value.
|
|
% This will fail if the definition is not a variable definition,
|
|
% or if any of the statements or definitions take the address
|
|
% of the variable, or assign to it.
|
|
%
|
|
:- pred try_to_eliminate_defn(opt_info::in, mlds_local_var_defn::in,
|
|
list(mlds_local_var_defn)::in, list(mlds_local_var_defn)::out,
|
|
list(mlds_function_defn)::in, list(mlds_function_defn)::out,
|
|
list(mlds_stmt)::in, list(mlds_stmt)::out) is semidet.
|
|
|
|
try_to_eliminate_defn(OptInfo, LocalVarDefn0, LocalVarDefns0, LocalVarDefns,
|
|
!FuncDefns, !Stmts) :-
|
|
LocalVarDefn0 = mlds_local_var_defn(VarName, _Context,
|
|
_Type, Initializer, _GCStmt),
|
|
|
|
% Check if this definition has a known initial value.
|
|
(
|
|
Initializer = init_obj(Rval)
|
|
;
|
|
Initializer = no_initializer,
|
|
find_initial_val_in_stmts(VarName, Rval, !Stmts)
|
|
;
|
|
( Initializer = init_array(_)
|
|
; Initializer = init_struct(_, _)
|
|
),
|
|
% Should we try to eliminate definitions with these kinds of
|
|
% initializers?
|
|
fail
|
|
),
|
|
|
|
% It is only safe to do this transformation if the variable's value
|
|
% is constant, otherwise we might end up moving the rvalue across
|
|
% a statement which modifies it.
|
|
rval_will_not_change(Rval),
|
|
|
|
% This transformation moves evaluation of the rvalue later in the
|
|
% computation. If the rvalue is something which might loop, throw an
|
|
% exception, or abort (e.g. for division by zero), then this might change
|
|
% the behaviour of the program. In such cases, we can only do the
|
|
% transformation if reordering of both conjunctions and disjunctions
|
|
% is allowed. (We need both permissions because can't tell here
|
|
% whether this MLDS code came from a conjunction or a disjunction.)
|
|
(
|
|
rval_cannot_throw(Rval)
|
|
;
|
|
Globals = OptInfo ^ oi_globals,
|
|
globals.lookup_bool_option(Globals, reorder_conj, yes),
|
|
globals.lookup_bool_option(Globals, reorder_disj, yes)
|
|
),
|
|
|
|
% Replace uses of this variable with the variable's value,
|
|
% checking that none of the statements or definitions took the
|
|
% address of the variable, or assigned to it.
|
|
eliminate_var(VarName, Rval, LocalVarDefns0, LocalVarDefns,
|
|
!FuncDefns, !Stmts, Count, Invalidated),
|
|
Invalidated = no,
|
|
|
|
% Make sure that we didn't duplicate the rval, unless it is just a constant
|
|
% or a variable, because duplicating any real operation would be
|
|
% a pessimization.
|
|
( Count =< 1
|
|
; rval_is_cheap_enough_to_duplicate(Rval) = yes
|
|
).
|
|
|
|
:- func rval_is_cheap_enough_to_duplicate(mlds_rval) = bool.
|
|
|
|
rval_is_cheap_enough_to_duplicate(Rval) = CheapEnough :-
|
|
(
|
|
( Rval = ml_const(_)
|
|
; Rval = ml_mem_addr(_)
|
|
; Rval = ml_self(_)
|
|
; Rval = ml_scalar_common(_)
|
|
; Rval = ml_scalar_common_addr(_)
|
|
; Rval = ml_vector_common_row_addr(_, _)
|
|
),
|
|
CheapEnough = yes
|
|
;
|
|
Rval = ml_lval(Lval),
|
|
(
|
|
( Lval = ml_local_var(_, _)
|
|
; Lval = ml_global_var(_, _)
|
|
),
|
|
CheapEnough = yes
|
|
;
|
|
( Lval = ml_mem_ref(_, _)
|
|
; Lval = ml_field(_, _, _, _, _)
|
|
; Lval = ml_target_global_var_ref(_)
|
|
),
|
|
CheapEnough = no
|
|
)
|
|
;
|
|
( Rval = ml_mkword(_, _)
|
|
; Rval = ml_box(_, _)
|
|
; Rval = ml_unbox(_, _)
|
|
; Rval = ml_unop(_, _)
|
|
; Rval = ml_binop(_, _, _)
|
|
),
|
|
% NOTE Some instances of the box and unbox operations are zero cost,
|
|
% but others are not. Since we cannot distinguish between them
|
|
% using purely local data, we take the conservative approach.
|
|
CheapEnough = no
|
|
;
|
|
Rval = ml_cast(_, SubRval),
|
|
CheapEnough = rval_is_cheap_enough_to_duplicate(SubRval)
|
|
).
|
|
|
|
% Succeed only if the specified rval definitely won't change in value.
|
|
%
|
|
:- pred rval_will_not_change(mlds_rval::in) is semidet.
|
|
|
|
rval_will_not_change(Rval) :-
|
|
require_complete_switch [Rval]
|
|
(
|
|
( Rval = ml_const(_)
|
|
; Rval = ml_scalar_common(_)
|
|
; Rval = ml_scalar_common_addr(_)
|
|
; Rval = ml_vector_common_row_addr(_, _)
|
|
)
|
|
;
|
|
Rval = ml_mem_addr(Lval),
|
|
require_complete_switch [Lval]
|
|
(
|
|
( Lval = ml_local_var(_, _)
|
|
; Lval = ml_global_var(_, _)
|
|
)
|
|
;
|
|
( Lval = ml_mem_ref(SubRval, _Type)
|
|
; Lval = ml_field(_, SubRval, _, _, _)
|
|
),
|
|
rval_will_not_change(SubRval)
|
|
;
|
|
Lval = ml_target_global_var_ref(_),
|
|
% XXX How can the address of a target language global variable
|
|
% change?
|
|
fail
|
|
)
|
|
;
|
|
( Rval = ml_mkword(_Tag, SubRval)
|
|
; Rval = ml_box(_Type, SubRval)
|
|
; Rval = ml_unbox(_Type, SubRval)
|
|
; Rval = ml_cast(_Type, SubRval)
|
|
; Rval = ml_unop(_Op, SubRval)
|
|
),
|
|
rval_will_not_change(SubRval)
|
|
;
|
|
Rval = ml_binop(_Op, SubRvalA, SubRvalB),
|
|
rval_will_not_change(SubRvalA),
|
|
rval_will_not_change(SubRvalB)
|
|
;
|
|
( Rval = ml_lval(_)
|
|
; Rval = ml_self(_)
|
|
),
|
|
fail
|
|
).
|
|
|
|
% Succeed only if the given rval definitely can't loop,
|
|
% throw an exception, or abort.
|
|
% We use a pretty conservative approximation...
|
|
%
|
|
:- pred rval_cannot_throw(mlds_rval::in) is semidet.
|
|
|
|
rval_cannot_throw(Rval) :-
|
|
require_complete_switch [Rval]
|
|
(
|
|
( Rval = ml_const(_)
|
|
; Rval = ml_scalar_common(_)
|
|
; Rval = ml_scalar_common_addr(_)
|
|
; Rval = ml_self(_)
|
|
; Rval = ml_mem_addr(_)
|
|
)
|
|
;
|
|
( Rval = ml_vector_common_row_addr(_, SubRval)
|
|
; Rval = ml_mkword(_Tag, SubRval)
|
|
; Rval = ml_box(_, SubRval)
|
|
; Rval = ml_unbox(_, SubRval)
|
|
; Rval = ml_cast(_, SubRval)
|
|
),
|
|
rval_cannot_throw(SubRval)
|
|
;
|
|
( Rval = ml_lval(_)
|
|
; Rval = ml_unop(_, _)
|
|
; Rval = ml_binop(_, _, _)
|
|
),
|
|
% Some unary and binary ops may throw, though others may not.
|
|
fail
|
|
).
|
|
|
|
% Search through a list of statements, trying to find the first assignment
|
|
% to the specified variable. Return the initial value, and a modified list
|
|
% of statements with the initial assignment deleted. Fail if the first
|
|
% value can't be determined.
|
|
%
|
|
:- pred find_initial_val_in_stmts(mlds_local_var_name::in, mlds_rval::out,
|
|
list(mlds_stmt)::in, list(mlds_stmt)::out) is semidet.
|
|
|
|
find_initial_val_in_stmts(VarName, Rval, [Stmt0 | Stmts0], Stmts) :-
|
|
( if find_initial_val_in_stmt(VarName, Rval1, Stmt0, Stmt1) then
|
|
Rval = Rval1,
|
|
( if Stmt1 = ml_stmt_block([], [], [], _) then
|
|
Stmts = Stmts0
|
|
else
|
|
Stmts = [Stmt1 | Stmts0]
|
|
)
|
|
else
|
|
% Check that Stmt0 doesn't modify the value of the variable
|
|
% -- this includes checking that there are no labels via which code
|
|
% could branch into the middle of Stmt0. Only if we are sure
|
|
% that Stmt0 can't modify the variable's value is it safe to go on
|
|
% and look for the initial value in Stmts0.
|
|
statement_contains_var(Stmt0, VarName) = no,
|
|
not (
|
|
statement_is_or_contains_statement(Stmt0, Label),
|
|
Label = ml_stmt_label(_, _)
|
|
),
|
|
find_initial_val_in_stmts(VarName, Rval, Stmts0, Stmts1),
|
|
Stmts = [Stmt0 | Stmts1]
|
|
).
|
|
|
|
:- pred find_initial_val_in_stmt(mlds_local_var_name::in, mlds_rval::out,
|
|
mlds_stmt::in, mlds_stmt::out) is semidet.
|
|
|
|
find_initial_val_in_stmt(Var, Rval, Stmt0, Stmt) :-
|
|
( if
|
|
Stmt0 = ml_stmt_atomic(AtomicStmt, Context),
|
|
AtomicStmt = assign(ml_local_var(Var, _Type), Rval0)
|
|
then
|
|
Rval = Rval0,
|
|
% Delete the assignment, by replacing it with an empty block.
|
|
Stmt = ml_stmt_block([], [], [], Context)
|
|
else if
|
|
Stmt0 = ml_stmt_block(LocalVarDefns0, FuncDefns0, SubStmts0, Context)
|
|
then
|
|
local_var_defns_contains_var(LocalVarDefns0, Var) = no,
|
|
function_defns_contains_var(FuncDefns0, Var) = no,
|
|
find_initial_val_in_stmts(Var, Rval, SubStmts0, SubStmts),
|
|
Stmt = ml_stmt_block(LocalVarDefns0, FuncDefns0, SubStmts, Context)
|
|
else
|
|
fail
|
|
).
|
|
|
|
% Replace uses of this variable with the variable's value in the specified
|
|
% definitions and statements. This will return a count of how many
|
|
% occurrences of the variable there were. It will also return
|
|
% Invalidated = yes if any of the statements or definitions take
|
|
% the address of the variable, or assign to it; in that case, the
|
|
% transformation should not be performed.
|
|
%
|
|
:- pred eliminate_var(mlds_local_var_name::in, mlds_rval::in,
|
|
list(mlds_local_var_defn)::in, list(mlds_local_var_defn)::out,
|
|
list(mlds_function_defn)::in, list(mlds_function_defn)::out,
|
|
list(mlds_stmt)::in, list(mlds_stmt)::out,
|
|
int::out, bool::out) is det.
|
|
|
|
eliminate_var(QualVarName, VarRval, !LocalVarDefns, !FuncDefns, !Stmts,
|
|
Count, Invalidated) :-
|
|
Count0 = 0,
|
|
Invalidated0 = no,
|
|
VarElimInfo0 = var_elim_info(QualVarName, VarRval, Count0, Invalidated0),
|
|
eliminate_var_in_block(!LocalVarDefns, !FuncDefns, !Stmts,
|
|
VarElimInfo0, VarElimInfo),
|
|
Count = VarElimInfo ^ replace_count,
|
|
Invalidated = VarElimInfo ^ invalidated.
|
|
|
|
% eliminate_var_in_*:
|
|
%
|
|
% Process the specified construct, replacing all rvalue occurrences of the
|
|
% variable (^var_name) with its value (^var_value), incrementing the
|
|
% ^replace_count field for each occurrence as an rvalue, and setting
|
|
% ^invalidated to yes if the variable occurs as an lvalue.
|
|
|
|
:- pred eliminate_var_in_block(
|
|
list(mlds_local_var_defn)::in, list(mlds_local_var_defn)::out,
|
|
list(mlds_function_defn)::in, list(mlds_function_defn)::out,
|
|
list(mlds_stmt)::in, list(mlds_stmt)::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_block(!LocalVarDefns, !FuncDefns, !Stmts, !VarElimInfo) :-
|
|
list.map_foldl(eliminate_var_in_local_var_defn,
|
|
!LocalVarDefns, !VarElimInfo),
|
|
list.map_foldl(eliminate_var_in_function_defn,
|
|
!FuncDefns, !VarElimInfo),
|
|
eliminate_var_in_stmts(!Stmts, !VarElimInfo).
|
|
|
|
:- pred eliminate_var_in_local_var_defn(
|
|
mlds_local_var_defn::in, mlds_local_var_defn::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_local_var_defn(LocalVarDefn0, LocalVarDefn, !VarElimInfo) :-
|
|
LocalVarDefn0 = mlds_local_var_defn(Name, Context,
|
|
Type, Initializer0, GCStmt),
|
|
eliminate_var_in_initializer(Initializer0, Initializer, !VarElimInfo),
|
|
LocalVarDefn = mlds_local_var_defn(Name, Context,
|
|
Type, Initializer, GCStmt).
|
|
|
|
:- pred eliminate_var_in_function_defn(
|
|
mlds_function_defn::in, mlds_function_defn::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_function_defn(FuncDefn0, FuncDefn, !VarElimInfo) :-
|
|
FuncDefn0 = mlds_function_defn(Name, Context, Flags, Id, Params,
|
|
Body0, EnvVarNames, MaybeRequireTailrecInfo),
|
|
(
|
|
Body0 = body_external,
|
|
Body = Body0
|
|
;
|
|
Body0 = body_defined_here(Stmt0),
|
|
eliminate_var_in_stmt(Stmt0, Stmt, !VarElimInfo),
|
|
Body = body_defined_here(Stmt)
|
|
),
|
|
FuncDefn = mlds_function_defn(Name, Context, Flags, Id, Params,
|
|
Body, EnvVarNames, MaybeRequireTailrecInfo).
|
|
|
|
:- pred eliminate_var_in_initializer(
|
|
mlds_initializer::in, mlds_initializer::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_initializer(Init0, Init, !VarElimInfo) :-
|
|
(
|
|
Init0 = no_initializer,
|
|
Init = Init0
|
|
;
|
|
Init0 = init_obj(Rval0),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
Init = init_obj(Rval)
|
|
;
|
|
Init0 = init_array(Elements0),
|
|
list.map_foldl(eliminate_var_in_initializer, Elements0, Elements,
|
|
!VarElimInfo),
|
|
Init = init_array(Elements)
|
|
;
|
|
Init0 = init_struct(Type, Members0),
|
|
list.map_foldl(eliminate_var_in_initializer, Members0, Members,
|
|
!VarElimInfo),
|
|
Init = init_struct(Type, Members)
|
|
).
|
|
|
|
:- pred eliminate_var_in_typed_rvals(
|
|
list(mlds_typed_rval)::in, list(mlds_typed_rval)::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_typed_rvals([], [], !VarElimInfo).
|
|
eliminate_var_in_typed_rvals([TypedRval0 | TypedRvals0],
|
|
[TypedRval | TypedRvals], !VarElimInfo) :-
|
|
TypedRval0 = ml_typed_rval(Rval0, Type),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
TypedRval = ml_typed_rval(Rval, Type),
|
|
eliminate_var_in_typed_rvals(TypedRvals0, TypedRvals, !VarElimInfo).
|
|
|
|
:- pred eliminate_var_in_rvals(list(mlds_rval)::in, list(mlds_rval)::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_rvals(!Rvals, !VarElimInfo) :-
|
|
list.map_foldl(eliminate_var_in_rval, !Rvals, !VarElimInfo).
|
|
|
|
:- pred eliminate_var_in_rval(mlds_rval::in, mlds_rval::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo) :-
|
|
(
|
|
Rval0 = ml_lval(Lval0),
|
|
VarName = !.VarElimInfo ^ var_name,
|
|
( if Lval0 = ml_local_var(VarName, _) then
|
|
% We found an rvalue occurrence of the variable -- replace it
|
|
% with the rval for the variable's value, and increment the counter
|
|
% for the number of occurrences that we have replaced.
|
|
Rval = !.VarElimInfo ^ var_value,
|
|
Count0 = !.VarElimInfo ^ replace_count,
|
|
!VarElimInfo ^ replace_count := Count0 + 1
|
|
else
|
|
eliminate_var_in_lval(Lval0, Lval, !VarElimInfo),
|
|
Rval = ml_lval(Lval)
|
|
)
|
|
;
|
|
Rval0 = ml_mkword(Tag, ArgRval0),
|
|
eliminate_var_in_rval(ArgRval0, ArgRval, !VarElimInfo),
|
|
Rval = ml_mkword(Tag, ArgRval)
|
|
;
|
|
Rval0 = ml_box(Type, ArgRval0),
|
|
eliminate_var_in_rval(ArgRval0, ArgRval, !VarElimInfo),
|
|
Rval = ml_box(Type, ArgRval)
|
|
;
|
|
Rval0 = ml_unbox(Type, ArgRval0),
|
|
eliminate_var_in_rval(ArgRval0, ArgRval, !VarElimInfo),
|
|
Rval = ml_unbox(Type, ArgRval)
|
|
;
|
|
Rval0 = ml_cast(Type, ArgRval0),
|
|
eliminate_var_in_rval(ArgRval0, ArgRval, !VarElimInfo),
|
|
Rval = ml_cast(Type, ArgRval)
|
|
;
|
|
Rval0 = ml_unop(Op, ArgRval0),
|
|
eliminate_var_in_rval(ArgRval0, ArgRval, !VarElimInfo),
|
|
Rval = ml_unop(Op, ArgRval)
|
|
;
|
|
Rval0 = ml_binop(Op, ArgRvalA0, ArgRvalB0),
|
|
eliminate_var_in_rval(ArgRvalA0, ArgRvalA, !VarElimInfo),
|
|
eliminate_var_in_rval(ArgRvalB0, ArgRvalB, !VarElimInfo),
|
|
Rval = ml_binop(Op, ArgRvalA, ArgRvalB)
|
|
;
|
|
Rval0 = ml_mem_addr(Lval0),
|
|
eliminate_var_in_lval(Lval0, Lval, !VarElimInfo),
|
|
Rval = ml_mem_addr(Lval)
|
|
;
|
|
Rval0 = ml_vector_common_row_addr(VectorCommon, RowRval0),
|
|
eliminate_var_in_rval(RowRval0, RowRval, !VarElimInfo),
|
|
Rval = ml_vector_common_row_addr(VectorCommon, RowRval)
|
|
;
|
|
( Rval0 = ml_const(_)
|
|
; Rval0 = ml_scalar_common(_)
|
|
; Rval0 = ml_scalar_common_addr(_)
|
|
; Rval0 = ml_self(_)
|
|
),
|
|
Rval = Rval0
|
|
).
|
|
|
|
:- pred eliminate_var_in_lvals(list(mlds_lval)::in, list(mlds_lval)::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_lvals(!Lvals, !VarElimInfo) :-
|
|
list.map_foldl(eliminate_var_in_lval, !Lvals, !VarElimInfo).
|
|
|
|
:- pred eliminate_var_in_lval(mlds_lval::in, mlds_lval::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_lval(Lval0, Lval, !VarElimInfo) :-
|
|
(
|
|
Lval0 = ml_field(MaybeTag, Rval0, PtrType, FieldId, FieldType),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
Lval = ml_field(MaybeTag, Rval, PtrType, FieldId, FieldType)
|
|
;
|
|
Lval0 = ml_mem_ref(Rval0, Type),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
Lval = ml_mem_ref(Rval, Type)
|
|
;
|
|
( Lval0 = ml_target_global_var_ref(_Ref)
|
|
; Lval0 = ml_global_var(_, _)
|
|
),
|
|
Lval = Lval0
|
|
;
|
|
Lval0 = ml_local_var(VarName, _Type),
|
|
invalidate_if_eliminating_local_loop_var(VarName, !VarElimInfo),
|
|
Lval = Lval0
|
|
).
|
|
|
|
:- pred eliminate_var_in_stmts(
|
|
list(mlds_stmt)::in, list(mlds_stmt)::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_stmts(!Stmts, !VarElimInfo) :-
|
|
list.map_foldl(eliminate_var_in_stmt, !Stmts, !VarElimInfo).
|
|
|
|
:- pred eliminate_var_in_maybe_stmt(
|
|
maybe(mlds_stmt)::in, maybe(mlds_stmt)::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_maybe_stmt(no, no, !VarElimInfo).
|
|
eliminate_var_in_maybe_stmt(yes(Stmt0), yes(Stmt), !VarElimInfo) :-
|
|
eliminate_var_in_stmt(Stmt0, Stmt, !VarElimInfo).
|
|
|
|
:- pred eliminate_var_in_stmt(mlds_stmt::in, mlds_stmt::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_stmt(Stmt0, Stmt, !VarElimInfo) :-
|
|
(
|
|
Stmt0 = ml_stmt_block(LocalVarDefns0, FuncDefns0, SubStmts0, Context),
|
|
eliminate_var_in_block(LocalVarDefns0, LocalVarDefns,
|
|
FuncDefns0, FuncDefns, SubStmts0, SubStmts, !VarElimInfo),
|
|
Stmt = ml_stmt_block(LocalVarDefns, FuncDefns, SubStmts, Context)
|
|
;
|
|
Stmt0 = ml_stmt_while(Kind, Rval0, SubStmts0, LocalLoopVars, Context),
|
|
list.foldl(invalidate_if_eliminating_local_loop_var, LocalLoopVars,
|
|
!VarElimInfo),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
eliminate_var_in_stmt(SubStmts0, SubStmts, !VarElimInfo),
|
|
Stmt = ml_stmt_while(Kind, Rval, SubStmts, LocalLoopVars, Context)
|
|
;
|
|
Stmt0 = ml_stmt_if_then_else(Cond0, Then0, MaybeElse0, Context),
|
|
eliminate_var_in_rval(Cond0, Cond, !VarElimInfo),
|
|
eliminate_var_in_stmt(Then0, Then, !VarElimInfo),
|
|
eliminate_var_in_maybe_stmt(MaybeElse0, MaybeElse, !VarElimInfo),
|
|
Stmt = ml_stmt_if_then_else(Cond, Then, MaybeElse, Context)
|
|
;
|
|
Stmt0 = ml_stmt_switch(Type, Val0, Range, Cases0, Default0, Context),
|
|
eliminate_var_in_rval(Val0, Val, !VarElimInfo),
|
|
list.map_foldl(eliminate_var_in_case, Cases0, Cases, !VarElimInfo),
|
|
eliminate_var_in_default(Default0, Default, !VarElimInfo),
|
|
Stmt = ml_stmt_switch(Type, Val, Range, Cases, Default, Context)
|
|
;
|
|
Stmt0 = ml_stmt_label(_, _),
|
|
Stmt = Stmt0
|
|
;
|
|
Stmt0 = ml_stmt_goto(_, _),
|
|
Stmt = Stmt0
|
|
;
|
|
Stmt0 = ml_stmt_computed_goto(Rval0, Labels, Context),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
Stmt = ml_stmt_computed_goto(Rval, Labels, Context)
|
|
;
|
|
Stmt0 = ml_stmt_call(Sig, Func0, Args0, RetLvals0, TailCall,
|
|
Context),
|
|
eliminate_var_in_rval(Func0, Func, !VarElimInfo),
|
|
eliminate_var_in_rvals(Args0, Args, !VarElimInfo),
|
|
eliminate_var_in_lvals(RetLvals0, RetLvals, !VarElimInfo),
|
|
Stmt = ml_stmt_call(Sig, Func, Args, RetLvals, TailCall,
|
|
Context)
|
|
;
|
|
Stmt0 = ml_stmt_return(Rvals0, Context),
|
|
eliminate_var_in_rvals(Rvals0, Rvals, !VarElimInfo),
|
|
Stmt = ml_stmt_return(Rvals, Context)
|
|
;
|
|
Stmt0 = ml_stmt_do_commit(Ref0, Context),
|
|
eliminate_var_in_rval(Ref0, Ref, !VarElimInfo),
|
|
Stmt = ml_stmt_do_commit(Ref, Context)
|
|
;
|
|
Stmt0 = ml_stmt_try_commit(Ref0, BodyStmt0, HandlerStmt0, Context),
|
|
eliminate_var_in_lval(Ref0, Ref, !VarElimInfo),
|
|
eliminate_var_in_stmt(BodyStmt0, BodyStmt, !VarElimInfo),
|
|
eliminate_var_in_stmt(HandlerStmt0, HandlerStmt, !VarElimInfo),
|
|
Stmt = ml_stmt_try_commit(Ref, BodyStmt, HandlerStmt, Context)
|
|
;
|
|
Stmt0 = ml_stmt_atomic(AtomicStmt0, Context),
|
|
eliminate_var_in_atomic_stmt(AtomicStmt0, AtomicStmt, !VarElimInfo),
|
|
Stmt = ml_stmt_atomic(AtomicStmt, Context)
|
|
).
|
|
|
|
:- pred eliminate_var_in_case(mlds_switch_case::in, mlds_switch_case::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_case(Case0, Case, !VarElimInfo) :-
|
|
Case0 = mlds_switch_case(FirstCond0, LaterConds0, Stmt0),
|
|
eliminate_var_in_case_cond(FirstCond0, FirstCond, !VarElimInfo),
|
|
list.map_foldl(eliminate_var_in_case_cond, LaterConds0, LaterConds,
|
|
!VarElimInfo),
|
|
eliminate_var_in_stmt(Stmt0, Stmt, !VarElimInfo),
|
|
Case = mlds_switch_case(FirstCond, LaterConds, Stmt).
|
|
|
|
:- pred eliminate_var_in_default(
|
|
mlds_switch_default::in, mlds_switch_default::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_default(Default0, Default, !VarElimInfo) :-
|
|
(
|
|
( Default0 = default_is_unreachable
|
|
; Default0 = default_do_nothing
|
|
),
|
|
Default = Default0
|
|
;
|
|
Default0 = default_case(Stmt0),
|
|
eliminate_var_in_stmt(Stmt0, Stmt, !VarElimInfo),
|
|
Default = default_case(Stmt)
|
|
).
|
|
|
|
:- pred eliminate_var_in_atomic_stmt(
|
|
mlds_atomic_statement::in, mlds_atomic_statement::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_atomic_stmt(Stmt0, Stmt, !VarElimInfo) :-
|
|
(
|
|
( Stmt0 = comment(_)
|
|
; Stmt0 = gc_check
|
|
),
|
|
Stmt = Stmt0
|
|
;
|
|
Stmt0 = assign(Lval0, Rval0),
|
|
eliminate_var_in_lval(Lval0, Lval, !VarElimInfo),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
Stmt = assign(Lval, Rval)
|
|
;
|
|
Stmt0 = assign_if_in_heap(Lval0, Rval0),
|
|
eliminate_var_in_lval(Lval0, Lval, !VarElimInfo),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
Stmt = assign_if_in_heap(Lval, Rval)
|
|
;
|
|
Stmt0 = delete_object(Rval0),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
Stmt = delete_object(Rval)
|
|
;
|
|
Stmt0 = new_object(Target0, Ptag, ExplicitSecTag, Type,
|
|
MaybeSize, MaybeCtorName, ArgRvalsTypes0, MayUseAtomic,
|
|
MaybeAllocId),
|
|
eliminate_var_in_lval(Target0, Target, !VarElimInfo),
|
|
eliminate_var_in_typed_rvals(ArgRvalsTypes0, ArgRvalsTypes,
|
|
!VarElimInfo),
|
|
Stmt = new_object(Target, Ptag, ExplicitSecTag, Type,
|
|
MaybeSize, MaybeCtorName, ArgRvalsTypes, MayUseAtomic,
|
|
MaybeAllocId)
|
|
;
|
|
Stmt0 = mark_hp(Lval0),
|
|
eliminate_var_in_lval(Lval0, Lval, !VarElimInfo),
|
|
Stmt = mark_hp(Lval)
|
|
;
|
|
Stmt0 = restore_hp(Rval0),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
Stmt = restore_hp(Rval)
|
|
;
|
|
Stmt0 = trail_op(TrailOp0),
|
|
eliminate_var_in_trail_op(TrailOp0, TrailOp, !VarElimInfo),
|
|
Stmt = trail_op(TrailOp)
|
|
;
|
|
Stmt0 = inline_target_code(Lang, Components0),
|
|
list.map_foldl(eliminate_var_in_target_code_component,
|
|
Components0, Components, !VarElimInfo),
|
|
Stmt = inline_target_code(Lang, Components)
|
|
;
|
|
Stmt0 = outline_foreign_proc(Lang, Vs, Lvals0, Code),
|
|
eliminate_var_in_lvals(Lvals0, Lvals, !VarElimInfo),
|
|
Stmt = outline_foreign_proc(Lang, Vs, Lvals, Code)
|
|
).
|
|
|
|
:- pred eliminate_var_in_case_cond(
|
|
mlds_case_match_cond::in, mlds_case_match_cond::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_case_cond(Cond0, Cond, !VarElimInfo) :-
|
|
(
|
|
Cond0 = match_value(Rval0),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
Cond = match_value(Rval)
|
|
;
|
|
Cond0 = match_range(Low0, High0),
|
|
eliminate_var_in_rval(Low0, Low, !VarElimInfo),
|
|
eliminate_var_in_rval(High0, High, !VarElimInfo),
|
|
Cond = match_range(Low, High)
|
|
).
|
|
|
|
:- pred eliminate_var_in_target_code_component(
|
|
target_code_component::in, target_code_component::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_target_code_component(Component0, Component, !VarElimInfo) :-
|
|
(
|
|
( Component0 = raw_target_code(_Code)
|
|
; Component0 = user_target_code(_Code, _Context)
|
|
; Component0 = target_code_type(_Type)
|
|
; Component0 = target_code_function_name(_Name)
|
|
; Component0 = target_code_alloc_id(_AllocId)
|
|
),
|
|
Component = Component0
|
|
;
|
|
Component0 = target_code_input(Rval0),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
Component = target_code_input(Rval)
|
|
;
|
|
Component0 = target_code_output(Lval0),
|
|
eliminate_var_in_lval(Lval0, Lval, !VarElimInfo),
|
|
Component = target_code_output(Lval)
|
|
).
|
|
|
|
:- pred eliminate_var_in_trail_op(trail_op::in, trail_op::out,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
eliminate_var_in_trail_op(Op0, Op, !VarElimInfo) :-
|
|
(
|
|
Op0 = store_ticket(Lval0),
|
|
eliminate_var_in_lval(Lval0, Lval, !VarElimInfo),
|
|
Op = store_ticket(Lval)
|
|
;
|
|
Op0 = reset_ticket(Rval0, Reason),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
Op = reset_ticket(Rval, Reason)
|
|
;
|
|
( Op0 = discard_ticket
|
|
; Op0 = prune_ticket
|
|
),
|
|
Op = Op0
|
|
;
|
|
Op0 = mark_ticket_stack(Lval0),
|
|
eliminate_var_in_lval(Lval0, Lval, !VarElimInfo),
|
|
Op = mark_ticket_stack(Lval)
|
|
;
|
|
Op0 = prune_tickets_to(Rval0),
|
|
eliminate_var_in_rval(Rval0, Rval, !VarElimInfo),
|
|
Op = prune_tickets_to(Rval)
|
|
).
|
|
|
|
:- pred invalidate_if_eliminating_local_loop_var(mlds_local_var_name::in,
|
|
var_elim_info::in, var_elim_info::out) is det.
|
|
|
|
invalidate_if_eliminating_local_loop_var(VarName, !VarElimInfo) :-
|
|
( if VarName = !.VarElimInfo ^ var_name then
|
|
% We found an lvalue occurrence of the variable.
|
|
% If the variable that we are trying to eliminate
|
|
% - has its address taken, or
|
|
% - is assigned to, or
|
|
% - in general if it is used as an lvalue,
|
|
% then it is NOT safe to eliminate it.
|
|
!VarElimInfo ^ invalidated := yes
|
|
else
|
|
true
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ml_backend.ml_optimize.
|
|
%---------------------------------------------------------------------------%
|