Files
mercury/compiler/use_local_vars.m
2018-04-07 18:25:43 +10:00

904 lines
34 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2001-2011 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File: use_local_vars.m
% Author: zs.
%
% This module implements an LLDS->LLDS transformation that optimizes the
% sequence of instructions in a procedure body by replacing references to
% relatively expensive locations: fake registers (Mercury abstract machine
% registers that are not mapped to machine registers) or stack slots with
% references to cheaper locations: local variables in C blocks, which should
% be mapped to machine registers by the C compiler. The C blocks should be
% introduced later by wrap_blocks.m, possibly after the LLDS code has been
% transformed further. Wrap_blocks will know what local variables to declare
% in each block by looking for the temp(_, _) lvals that represent those local
% variables.
%
% This module looks for three patterns. The first is
%
% <instruction that defines a fake register>
% <instructions that use and possibly define the fake register>
% <end of basic block, at which the fake register is not live>
%
% When it finds an occurrence of that pattern, it replaces all references to
% the fake register with a local variable.
%
% If the basic block jumps to a code address which is not a label (e.g.
% do_redo, do_fail), we consider all registers to be live at the end of the
% basic block. This is because livemap.m, which computes liveness information
% for us, does not know about liveness requirements introduced by backtracking.
% This is a conservative approximation. The union of the livenesses of all the
% labels that represent resume points is a better approximation, but it would
% be tedious to compute and is unlikely to yield significantly better code.
%
% The second pattern we look for is simply an instruction that defines a fake
% register or stack slot, followed by some uses of that register or stack slot
% before code that redefines the register or stack slot. When we find this
% pattern, we again replace all references to the fake register or stack slot
% with a local variable, but since this time we cannot be sure that the
% original lval will not be referred to, we assign the local variable to the
% lval as well. This is a win because the cost of the assignment is less than
% the savings from replacing the fake register or stack slot references with
% local variable references.
%
% The third pattern we look for consists of a sequence of instructions in which
% a false register or stack slot is used several times, including at least once
% in the first instruction as a part of a path to a memory location, before
% being redefined or maybe aliased. This typically occurs when the code
% generator fills in the fields of a structure or extracts the fields of a
% structure. Again, we replace the false register or stack slot with a
% temporary after assigning the value in the false register or stack slot to
% the temporary.
%
% If we cannot find out what registers are live at each label, we still look
% for the second and third patterns.
%
%-----------------------------------------------------------------------------%
:- module ll_backend.use_local_vars.
:- interface.
:- import_module ll_backend.llds.
:- import_module mdbcomp.prim_data.
:- import_module bool.
:- import_module counter.
:- import_module list.
%-----------------------------------------------------------------------------%
:- pred use_local_vars_proc(list(instruction)::in, list(instruction)::out,
int::in, int::in, bool::in, proc_label::in, counter::in, counter::out)
is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module hlds.hlds_llds.
:- import_module ll_backend.basic_block.
:- import_module ll_backend.code_util.
:- import_module ll_backend.exprn_aux.
:- import_module ll_backend.livemap.
:- import_module ll_backend.opt_debug.
:- import_module ll_backend.opt_util.
:- import_module parse_tree.prog_data_foreign.
:- import_module int.
:- import_module map.
:- import_module maybe.
:- import_module require.
:- import_module set.
:- import_module string.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
use_local_vars_proc(Instrs0, Instrs, NumRealRRegs, AccessThreshold,
AutoComments, ProcLabel, !C) :-
create_basic_blocks(Instrs0, Comments0, ProcLabel, !C, NewLabels,
LabelSeq, BlockMap0),
flatten_basic_blocks(LabelSeq, BlockMap0, TentativeInstrs,
NumTentativeInstrs),
% If the number of instructions in the procedure is too big,
% then building MaybeLiveMap will probably take too long.
( if NumTentativeInstrs < local_vars_proc_size_limit then
build_livemap(TentativeInstrs, MaybeLiveMap),
extend_basic_blocks(LabelSeq, EBBLabelSeq, BlockMap0, EBBBlockMap0,
NewLabels),
list.foldl(use_local_vars_block(MaybeLiveMap, NumRealRRegs,
AccessThreshold), EBBLabelSeq, EBBBlockMap0, EBBBlockMap),
flatten_basic_blocks(EBBLabelSeq, EBBBlockMap, Instrs1, _),
( if
MaybeLiveMap = yes(LiveMap),
AutoComments = yes
then
NewComment = "\n" ++ dump_livemap(yes(ProcLabel), LiveMap),
NewCommentInstr = llds_instr(comment(NewComment), ""),
Comments = Comments0 ++ [NewCommentInstr]
else
Comments = Comments0
),
Instrs = Comments ++ Instrs1
else
Instrs = Instrs0
).
:- pred use_local_vars_block(maybe(livemap)::in, int::in, int::in, label::in,
block_map::in, block_map::out) is det.
use_local_vars_block(MaybeLiveMap, NumRealRRegs, AccessThreshold, Label,
!BlockMap) :-
map.lookup(!.BlockMap, Label, BlockInfo0),
BlockInfo0 = block_info(BlockLabel, LabelInstr, RestInstrs0, BlockSize,
FallInto, JumpLabels, MaybeFallThrough),
% The algorithm we use is half-quadratic, so using it on long instruction
% lists is not a good idea. The correct fix of course would be to reduce
% the complexity of the algorithm, but that is not trivial to do.
( if BlockSize < local_vars_block_size_limit then
counter.init(1, TempCounter0),
use_local_vars_instrs(RestInstrs0, RestInstrs,
TempCounter0, TempCounter, NumRealRRegs,
AccessThreshold, MaybeLiveMap, MaybeFallThrough),
( if TempCounter = TempCounter0 then
true
else
BlockInfo = block_info(BlockLabel, LabelInstr, RestInstrs,
BlockSize, FallInto, JumpLabels, MaybeFallThrough),
map.det_update(Label, BlockInfo, !BlockMap)
)
else
true
).
%-----------------------------------------------------------------------------%
:- pred use_local_vars_instrs(list(instruction)::in, list(instruction)::out,
counter::in, counter::out, int::in, int::in, maybe(livemap)::in,
maybe(label)::in) is det.
use_local_vars_instrs(!RestInstrs, !TempCounter,
NumRealRRegs, AccessThreshold, MaybeLiveMap, MaybeFallThrough) :-
opt_assign(!RestInstrs, !TempCounter, NumRealRRegs, [], MaybeLiveMap,
MaybeFallThrough),
( if AccessThreshold >= 1 then
opt_access(!RestInstrs, !TempCounter, NumRealRRegs,
set.init, AccessThreshold)
else
true
).
%-----------------------------------------------------------------------------%
:- pred opt_assign(list(instruction)::in, list(instruction)::out,
counter::in, counter::out, int::in, list(lval)::in,
maybe(livemap)::in, maybe(label)::in) is det.
opt_assign([], [], !TempCounter, _, _, _, _).
opt_assign([Instr0 | TailInstrs0], Instrs, !TempCounter, NumRealRRegs,
!.AvoidLvals, MaybeLiveMap, MaybeFallThrough) :-
Instr0 = llds_instr(Uinstr0, _Comment0),
( if
(
% We don't optimize keep_assign instructions.
(
Uinstr0 = assign(ToLval, _FromRval)
;
Uinstr0 = incr_hp(ToLval, _MaybeTag, _SizeRval, _MO, _Type,
_Atomic, _, _)
),
base_lval_worth_replacing(NumRealRRegs, ToLval),
MaybeMore = no
;
Uinstr0 = foreign_proc_code(_D, Comps, _MCM, _FNL, _FL, _FOL, _NF,
_MDL, _S, _MD),
opt_assign_find_output_in_components(Comps, NumRealRRegs,
!.AvoidLvals, ToLval),
MaybeMore = yes
)
then
( if
ToLval = reg(_, _),
find_compulsory_lvals(TailInstrs0, MaybeLiveMap, MaybeFallThrough,
no, MaybeCompulsoryLvals),
MaybeCompulsoryLvals = known(CompulsoryLvals),
not set.member(ToLval, CompulsoryLvals)
then
counter.allocate(TempNum, !TempCounter),
NewLval = temp(reg_type_for_lval(ToLval), TempNum),
substitute_lval_in_defn(ToLval, NewLval, Instr0, Instr),
list.map_foldl(
exprn_aux.substitute_lval_in_instr(ToLval, NewLval),
TailInstrs0, TailInstrs1, 0, _),
(
MaybeMore = no,
opt_assign(TailInstrs1, TailInstrs, !TempCounter, NumRealRRegs,
[], MaybeLiveMap, MaybeFallThrough),
Instrs = [Instr | TailInstrs]
;
MaybeMore = yes,
!:AvoidLvals = [ToLval | !.AvoidLvals],
Instrs1 = [Instr | TailInstrs1],
opt_assign(Instrs1, Instrs, !TempCounter, NumRealRRegs,
!.AvoidLvals, MaybeLiveMap, MaybeFallThrough)
)
else if
counter.allocate(TempNum, !TempCounter),
NewLval = temp(reg_type_for_lval(ToLval), TempNum),
substitute_lval_in_instr_until_defn(ToLval, NewLval,
TailInstrs0, TailInstrs1, 0, NumSubst),
NumSubst > 1
then
substitute_lval_in_defn(ToLval, NewLval, Instr0, Instr),
CopyInstr = llds_instr(assign(ToLval, lval(NewLval)), ""),
(
MaybeMore = no,
opt_assign(TailInstrs1, TailInstrs, !TempCounter, NumRealRRegs,
[], MaybeLiveMap, MaybeFallThrough),
Instrs = [Instr, CopyInstr | TailInstrs]
;
MaybeMore = yes,
!:AvoidLvals = [ToLval | !.AvoidLvals],
Instrs1 = [Instr, CopyInstr | TailInstrs1],
opt_assign(Instrs1, Instrs, !TempCounter, NumRealRRegs,
!.AvoidLvals, MaybeLiveMap, MaybeFallThrough)
)
else
(
MaybeMore = no,
opt_assign(TailInstrs0, TailInstrs, !TempCounter, NumRealRRegs,
[], MaybeLiveMap, MaybeFallThrough),
Instrs = [Instr0 | TailInstrs]
;
MaybeMore = yes,
!:AvoidLvals = [ToLval | !.AvoidLvals],
Instrs1 = [Instr0 | TailInstrs0],
opt_assign(Instrs1, Instrs, !TempCounter, NumRealRRegs,
!.AvoidLvals, MaybeLiveMap, MaybeFallThrough)
)
)
else
opt_assign(TailInstrs0, TailInstrs, !TempCounter, NumRealRRegs,
[], MaybeLiveMap, MaybeFallThrough),
Instrs = [Instr0 | TailInstrs]
).
:- pred opt_assign_find_output_in_components(list(foreign_proc_component)::in,
int::in, list(lval)::in, lval::out) is semidet.
opt_assign_find_output_in_components([Comp | Comps], NumRealRRegs, AvoidLvals,
ToLval) :-
( if
Comp = foreign_proc_outputs(Outputs),
opt_assign_find_output_in_outputs(Outputs, NumRealRRegs, AvoidLvals,
ToLvalPrime)
then
ToLval = ToLvalPrime
else
opt_assign_find_output_in_components(Comps, NumRealRRegs, AvoidLvals,
ToLval)
).
:- pred opt_assign_find_output_in_outputs(list(foreign_proc_output)::in,
int::in, list(lval)::in, lval::out) is semidet.
opt_assign_find_output_in_outputs([Output | Outputs], NumRealRRegs, AvoidLvals,
ToLval) :-
Output = foreign_proc_output(Dest, _Type, _IsDummy, _VarName,
_OrigType, _MaybeForeignType, _BoxPolicy),
( if
base_lval_worth_replacing(NumRealRRegs, Dest),
not list.member(Dest, AvoidLvals)
then
ToLval = Dest
else
opt_assign_find_output_in_outputs(Outputs, NumRealRRegs, AvoidLvals,
ToLval)
).
%-----------------------------------------------------------------------------%
:- type maybe_compulsory_lvals
---> known(lvalset)
; unknown_must_assume_all.
:- pred find_compulsory_lvals(list(instruction)::in, maybe(livemap)::in,
maybe(label)::in, bool::in, maybe_compulsory_lvals::out) is det.
find_compulsory_lvals([], MaybeLiveMap, MaybeFallThrough, _PrevLivevals,
MaybeCompulsoryLvals) :-
(
MaybeFallThrough = yes(FallThrough),
(
MaybeLiveMap = yes(LiveMap),
map.lookup(LiveMap, FallThrough, CompulsoryLvals),
MaybeCompulsoryLvals = known(CompulsoryLvals)
;
MaybeLiveMap = no,
MaybeCompulsoryLvals = unknown_must_assume_all
)
;
MaybeFallThrough = no,
MaybeCompulsoryLvals = unknown_must_assume_all
).
find_compulsory_lvals([Instr | Instrs], MaybeLiveMap, MaybeFallThrough,
PrevLivevals, !:MaybeCompulsoryLvals) :-
Instr = llds_instr(Uinstr, _),
( if
Uinstr = livevals(LiveLvals)
then
find_compulsory_lvals(Instrs, MaybeLiveMap, MaybeFallThrough,
yes, !:MaybeCompulsoryLvals),
union_maybe_compulsory_lvals(LiveLvals, !MaybeCompulsoryLvals)
else if
Uinstr = llcall(_, _, _, _, _, _)
then
expect(unify(PrevLivevals, yes), $pred, "call without livevals"),
% The livevals instruction will include all the live lvals
% in MaybeCompulsoryLvals after we return.
!:MaybeCompulsoryLvals = known(set.init)
else if
Uinstr = goto(_Target),
PrevLivevals = yes
then
% The livevals instruction will include all the live lvals
% in MaybeCompulsoryLvals after we return.
!:MaybeCompulsoryLvals = known(set.init)
else
possible_targets(Uinstr, Labels, NonLabelCodeAddrs),
(
NonLabelCodeAddrs = [],
(
Labels = [],
% Optimize the common case
find_compulsory_lvals(Instrs, MaybeLiveMap, MaybeFallThrough,
no, !:MaybeCompulsoryLvals)
;
Labels = [_ | _],
(
MaybeLiveMap = yes(LiveMap),
list.map(map.lookup(LiveMap), Labels, LabelsLiveLvals),
AllLabelsLiveLvals = set.union_list(LabelsLiveLvals),
find_compulsory_lvals(Instrs, MaybeLiveMap,
MaybeFallThrough, no, !:MaybeCompulsoryLvals),
union_maybe_compulsory_lvals(AllLabelsLiveLvals,
!MaybeCompulsoryLvals)
;
MaybeLiveMap = no,
!:MaybeCompulsoryLvals = unknown_must_assume_all
)
)
;
NonLabelCodeAddrs = [_ | _],
!:MaybeCompulsoryLvals = unknown_must_assume_all
)
).
:- pred union_maybe_compulsory_lvals(lvalset::in,
maybe_compulsory_lvals::in, maybe_compulsory_lvals::out) is det.
union_maybe_compulsory_lvals(New, !MaybeCompulsoryLvals) :-
(
!.MaybeCompulsoryLvals = known(OldCompulsoryLvals),
set.union(New, OldCompulsoryLvals, AllCompulsoryLvals),
!:MaybeCompulsoryLvals = known(AllCompulsoryLvals)
;
!.MaybeCompulsoryLvals = unknown_must_assume_all
).
%-----------------------------------------------------------------------------%
:- pred opt_access(list(instruction)::in, list(instruction)::out,
counter::in, counter::out, int::in, lvalset::in, int::in) is det.
opt_access([], [], !TempCounter, _, _, _).
opt_access([Instr0 | TailInstrs0], Instrs, !TempCounter, NumRealRRegs,
AlreadyTried0, AccessThreshold) :-
Instr0 = llds_instr(Uinstr0, _Comment0),
( if
Uinstr0 = assign(ToLval, FromRval),
SubLvals = lvals_in_lval(ToLval) ++ lvals_in_rval(FromRval),
list.filter(
base_lval_worth_replacing_not_tried(AlreadyTried0, NumRealRRegs),
SubLvals, ReplaceableSubLvals),
ReplaceableSubLvals = [ChosenLval | ChooseableRvals]
then
OrigTempCounter = !.TempCounter,
counter.allocate(TempNum, !TempCounter),
TempLval = temp(reg_type_for_lval(ChosenLval), TempNum),
SubChosenLvals = lvals_in_lval(ChosenLval),
expect(unify(SubChosenLvals, []), $pred,
"nonempty SubChosenLvals"),
substitute_lval_in_instr_until_defn(ChosenLval, TempLval,
[Instr0 | TailInstrs0], Instrs1, 0, NumReplacements),
set.insert(ChosenLval, AlreadyTried0, AlreadyTried1),
( if NumReplacements >= AccessThreshold then
TempAssign = llds_instr(assign(TempLval, lval(ChosenLval)),
"factor out common sub lval"),
Instrs2 = [TempAssign | Instrs1],
opt_access(Instrs2, Instrs, !TempCounter, NumRealRRegs,
AlreadyTried1, AccessThreshold)
else
(
ChooseableRvals = [_ | _],
!:TempCounter = OrigTempCounter,
opt_access([Instr0 | TailInstrs0], Instrs, !TempCounter,
NumRealRRegs, AlreadyTried1, AccessThreshold)
;
ChooseableRvals = [],
!:TempCounter = OrigTempCounter,
opt_access(TailInstrs0, TailInstrs, !TempCounter, NumRealRRegs,
set.init, AccessThreshold),
Instrs = [Instr0 | TailInstrs]
)
)
else
opt_access(TailInstrs0, TailInstrs, !TempCounter, NumRealRRegs,
set.init, AccessThreshold),
Instrs = [Instr0 | TailInstrs]
).
%-----------------------------------------------------------------------------%
:- pred base_lval_worth_replacing(int::in, lval::in) is semidet.
base_lval_worth_replacing(NumRealRRegs, Lval) :-
(
Lval = reg(reg_r, RegNum),
RegNum > NumRealRRegs
;
Lval = reg(reg_f, _)
;
Lval = stackvar(_)
;
Lval = framevar(_)
;
Lval = double_stackvar(_, _)
).
:- pred base_lval_worth_replacing_not_tried(lvalset::in, int::in, lval::in)
is semidet.
base_lval_worth_replacing_not_tried(AlreadyTried, NumRealRRegs, Lval) :-
\+ set.member(Lval, AlreadyTried),
base_lval_worth_replacing(NumRealRRegs, Lval).
:- func reg_type_for_lval(lval) = reg_type.
reg_type_for_lval(Lval) = RegType :-
(
Lval = reg(RegType, _)
;
Lval = temp(RegType, _)
;
Lval = double_stackvar(_, _),
RegType = reg_f
;
( Lval = succip
; Lval = maxfr
; Lval = curfr
; Lval = hp
; Lval = sp
; Lval = parent_sp
; Lval = stackvar(_)
; Lval = parent_stackvar(_)
; Lval = framevar(_)
; Lval = succip_slot(_)
; Lval = succfr_slot(_)
; Lval = redoip_slot(_)
; Lval = redofr_slot(_)
; Lval = prevfr_slot(_)
; Lval = field(_, _, _)
; Lval = mem_ref(_)
; Lval = global_var_ref(_)
; Lval = lvar(_)
),
RegType = reg_r
).
%-----------------------------------------------------------------------------%
% When processing substituting e.g. tempr1 for e.g. r2 in the instruction
% that defines r2, we must be careful to leave intact the value being
% assigned. Given the instruction
%
% r2 = field(0, r2, 5)
%
% we must generate
%
% tempr1 = field(0, r2, 5)
%
% Generating
%
% tempr1 = field(0, tempr1, 5)
%
% would introduce a bug, since the right hand side now refers to
% an as yet undefined variable.
%
:- pred substitute_lval_in_defn(lval::in, lval::in,
instruction::in, instruction::out) is det.
substitute_lval_in_defn(OldLval, NewLval, Instr0, Instr) :-
Instr0 = llds_instr(Uinstr0, Comment),
( if
Uinstr0 = assign(ToLval, FromRval)
then
expect(unify(ToLval, OldLval), $pred, "mismatch in assign"),
Uinstr = assign(NewLval, FromRval)
else if
Uinstr0 = incr_hp(ToLval, MaybeTag, SizeRval, MO, Type,
MayUseAtomic, MaybeRegionRval, MaybeReuse)
then
expect(unify(ToLval, OldLval), $pred, "mismatch in incr_hp"),
Uinstr = incr_hp(NewLval, MaybeTag, SizeRval, MO, Type,
MayUseAtomic, MaybeRegionRval, MaybeReuse)
else if
Uinstr0 = foreign_proc_code(D, Comps0, MCM, FNL, FL, FOL, NF, MDL,
S, MD)
then
substitute_lval_in_defn_components(OldLval, NewLval, Comps0, Comps,
0, NumSubsts),
expect(unify(NumSubsts, 1), $pred, "mismatch in foreign_proc_code"),
Uinstr = foreign_proc_code(D, Comps, MCM, FNL, FL, FOL, NF, MDL, S, MD)
else
unexpected($pred, "unexpected instruction")
),
Instr = llds_instr(Uinstr, Comment).
:- pred substitute_lval_in_defn_components(lval::in, lval::in,
list(foreign_proc_component)::in, list(foreign_proc_component)::out,
int::in, int::out) is det.
substitute_lval_in_defn_components(_OldLval, _NewLval, [], [], !NumSubsts).
substitute_lval_in_defn_components(OldLval, NewLval,
[Comp0 | Comps0], [Comp | Comps], !NumSubsts) :-
(
Comp0 = foreign_proc_outputs(Outputs0),
substitute_lval_in_defn_outputs(OldLval, NewLval,
Outputs0, Outputs, !NumSubsts),
Comp = foreign_proc_outputs(Outputs)
;
( Comp0 = foreign_proc_inputs(_)
; Comp0 = foreign_proc_user_code(_, _, _)
; Comp0 = foreign_proc_raw_code(_, _, _, _)
; Comp0 = foreign_proc_fail_to(_)
; Comp0 = foreign_proc_alloc_id(_)
; Comp0 = foreign_proc_noop
),
Comp = Comp0
),
substitute_lval_in_defn_components(OldLval, NewLval, Comps0, Comps,
!NumSubsts).
:- pred substitute_lval_in_defn_outputs(lval::in, lval::in,
list(foreign_proc_output)::in, list(foreign_proc_output)::out,
int::in, int::out) is det.
substitute_lval_in_defn_outputs(_OldLval, _NewLval, [], [], !NumSubsts).
substitute_lval_in_defn_outputs(OldLval, NewLval,
[Output0 | Outputs0], [Output | Outputs], !NumSubsts) :-
Output0 = foreign_proc_output(Dest0, Type, IsDummy, VarName,
OrigType, MaybeForeignType, BoxPolicy),
( if Dest0 = OldLval then
Output = foreign_proc_output(NewLval, Type, IsDummy, VarName,
OrigType, MaybeForeignType, BoxPolicy),
!:NumSubsts = !.NumSubsts + 1
else
Output = Output0
),
substitute_lval_in_defn_outputs(OldLval, NewLval, Outputs0, Outputs,
!NumSubsts).
% Substitute NewLval for OldLval in an instruction sequence
% until we come an instruction that may define OldLval.
% We don't worry about instructions that define a variable that
% occurs in the access path to OldLval (and which therefore indirectly
% modifies the value that OldLval refers to), because our caller will
% call us only with OldLvals (and NewLvals for that matter) that have
% no lvals in their access path. The NewLvals will be temporaries,
% representing local variables in C blocks.
%
% When control leaves this instruction sequence via a if_val, goto or
% call, the local variables of the block in which this instruction
% sequence will go out of scope, so we must stop using them. At points
% at which control can enter this instruction sequence, i.e. at labels,
% the C block ends, so again we must stop using its local variables.
% (Livevals pseudo-instructions occur only immediately before
% instructions that cause control transfer, so we stop at them too.)
%
% Our caller ensures that we can also so stop at any point. By doing so
% we may fail to exploit an optimization opportunity, but the code we
% generate will still be correct. At the moment we stop at instructions
% whose correct handling would be non-trivial and which rarely if ever
% appear between the definition and a use of a location we want to
% substitute. These include instructions that manipulate stack frames,
% the heap, the trail and synchronization data.
%
:- pred substitute_lval_in_instr_until_defn(lval::in, lval::in,
list(instruction)::in, list(instruction)::out, int::in, int::out)
is det.
substitute_lval_in_instr_until_defn(_, _, [], [], !N).
substitute_lval_in_instr_until_defn(OldLval, NewLval,
[Instr0 | Instrs0], [Instr | Instrs], !N) :-
substitute_lval_in_instr_until_defn_2(OldLval, NewLval,
Instr0, Instr, Instrs0, Instrs, !N).
:- pred substitute_lval_in_instr_until_defn_2(lval::in, lval::in,
instruction::in, instruction::out,
list(instruction)::in, list(instruction)::out, int::in, int::out) is det.
:- pragma inline(substitute_lval_in_instr_until_defn_2/8).
substitute_lval_in_instr_until_defn_2(OldLval, NewLval, !Instr, !Instrs, !N) :-
!.Instr = llds_instr(Uinstr0, Comment),
(
Uinstr0 = block(_, _, _),
unexpected($pred, "block")
;
Uinstr0 = assign(Lval, Rval0),
Updates = assignment_updates_oldlval(Lval, OldLval),
(
Updates = yes,
% If we alter any lval that occurs in OldLval, we must stop
% the substitutions.
exprn_aux.substitute_lval_in_rval(OldLval, NewLval, Rval0, Rval),
Uinstr = assign(Lval, Rval),
!:Instr = llds_instr(Uinstr, Comment)
;
Updates = no,
exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N),
substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
)
;
Uinstr0 = keep_assign(Lval, Rval0),
Updates = assignment_updates_oldlval(Lval, OldLval),
(
Updates = yes,
% If we alter any lval that occurs in OldLval, we must stop
% the substitutions.
exprn_aux.substitute_lval_in_rval(OldLval, NewLval, Rval0, Rval),
Uinstr = keep_assign(Lval, Rval),
!:Instr = llds_instr(Uinstr, Comment)
;
Updates = no,
exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N),
substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
)
;
Uinstr0 = lc_create_loop_control(_NumSlots, Lval),
Updates = assignment_updates_oldlval(Lval, OldLval),
(
Updates = yes
% If we alter any lval that occurs in OldLval, we must stop
% the substitutions.
;
Updates = no,
exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N),
substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
)
;
Uinstr0 = lc_wait_free_slot(Rval0, Lval, Label),
Updates = assignment_updates_oldlval(Lval, OldLval),
(
Updates = yes,
% If we alter any lval that occurs in OldLval, we must stop
% the substitutions.
exprn_aux.substitute_lval_in_rval(OldLval, NewLval, Rval0, Rval),
Uinstr = lc_wait_free_slot(Rval, Lval, Label),
!:Instr = llds_instr(Uinstr, Comment)
;
Updates = no,
exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N),
substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
)
;
( Uinstr0 = incr_hp(Lval, _, _, _, _, _, _, _)
; Uinstr0 = save_maxfr(Lval)
; Uinstr0 = mark_hp(Lval)
),
Updates = assignment_updates_oldlval(Lval, OldLval),
(
Updates = yes
% If we alter any lval that occurs in OldLval, we must stop
% the substitutions.
;
Updates = no,
exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N),
substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
)
;
Uinstr0 = region_fill_frame(_, _, _, NumLval, AddrLval),
( if
( assignment_updates_oldlval(NumLval, OldLval) = yes
; assignment_updates_oldlval(AddrLval, OldLval) = yes
)
then
% If we alter any lval that occurs in NumLval or AddrLval,
% we must stop the substitutions.
true
else
exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N),
substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
)
;
( Uinstr0 = restore_maxfr(_)
; Uinstr0 = restore_hp(_)
; Uinstr0 = push_region_frame(_, _)
; Uinstr0 = region_set_fixed_slot(_, _, _)
; Uinstr0 = use_and_maybe_pop_region_frame(_, _)
; Uinstr0 = lc_spawn_off(_, _, _)
; Uinstr0 = lc_join_and_terminate(_, _)
),
exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N),
substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
;
Uinstr0 = foreign_proc_code(_, Components, _, _, _, _, _, _, _, _),
AffectsLiveness = components_affect_liveness(Components),
(
AffectsLiveness = no,
Updates = components_update_oldlval(Components, OldLval),
(
Updates = yes
% If we alter any lval that occurs in OldLval, we must stop
% the substitutions.
;
Updates = no,
exprn_aux.substitute_lval_in_instr(OldLval, NewLval,
!Instr, !N),
substitute_lval_in_instr_until_defn(OldLval, NewLval,
!Instrs, !N)
)
;
AffectsLiveness = yes
)
;
Uinstr0 = comment(_),
substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
;
Uinstr0 = if_val(_, _),
exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N),
substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
;
Uinstr0 = computed_goto(_, _),
exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N)
;
( Uinstr0 = label(_)
; Uinstr0 = livevals(_)
; Uinstr0 = llcall(_, _, _, _, _, _)
; Uinstr0 = mkframe(_, _)
; Uinstr0 = goto(_)
; Uinstr0 = free_heap(_)
; Uinstr0 = store_ticket(_)
; Uinstr0 = reset_ticket(_, _)
; Uinstr0 = discard_ticket
; Uinstr0 = prune_ticket
; Uinstr0 = mark_ticket_stack(_)
; Uinstr0 = prune_tickets_to(_)
; Uinstr0 = incr_sp(_, _, _)
; Uinstr0 = decr_sp(_)
; Uinstr0 = decr_sp_and_return(_)
; Uinstr0 = init_sync_term(_, _, _)
; Uinstr0 = fork_new_child(_, _)
; Uinstr0 = join_and_continue(_, _)
; Uinstr0 = arbitrary_c_code(_, _, _)
)
).
:- func assignment_updates_oldlval(lval, lval) = bool.
assignment_updates_oldlval(Lval, OldLval) =
( if Lval = OldLval then
% If we alter any lval that occurs in OldLval, we must stop the
% substitutions. At the moment, the only lval OldLval can contain
% is itself.
yes
else
no
).
:- func components_update_oldlval(list(foreign_proc_component), lval) = bool.
components_update_oldlval([], _Lval) = no.
components_update_oldlval([Component | Components], Lval) = Updates :-
ComponentUpdates = component_updates_oldlval(Component, Lval),
(
ComponentUpdates = yes,
Updates = yes
;
ComponentUpdates = no,
Updates = components_update_oldlval(Components, Lval)
).
:- func component_updates_oldlval(foreign_proc_component, lval) = bool.
component_updates_oldlval(Component, Lval) = Updates :-
(
Component = foreign_proc_outputs(Outputs),
( if
some [Output] (
list.member(Output, Outputs),
Output ^ out_arg_dest = Lval
)
then
Updates = yes
else
Updates = no
)
;
( Component = foreign_proc_inputs(_)
; Component = foreign_proc_fail_to(_)
; Component = foreign_proc_alloc_id(_)
; Component = foreign_proc_noop
; Component = foreign_proc_user_code(_, _, _)
; Component = foreign_proc_raw_code(_, _, _, _)
),
Updates = no
).
:- func components_affect_liveness(list(foreign_proc_component))
= bool.
components_affect_liveness([]) = no.
components_affect_liveness([Component | Components]) = Updates :-
ComponentUpdates = component_affects_liveness(Component),
(
ComponentUpdates = yes,
Updates = yes
;
ComponentUpdates = no,
Updates = components_affect_liveness(Components)
).
:- func component_affects_liveness(foreign_proc_component) = bool.
component_affects_liveness(Component) = Affects :-
(
( Component = foreign_proc_inputs(_)
; Component = foreign_proc_outputs(_)
; Component = foreign_proc_fail_to(_)
; Component = foreign_proc_alloc_id(_)
; Component = foreign_proc_noop
),
Affects = no
;
( Component = foreign_proc_user_code(_, AffectsLiveness, Code)
; Component = foreign_proc_raw_code(_, AffectsLiveness, _, Code)
),
(
AffectsLiveness = proc_affects_liveness,
Affects = yes
;
AffectsLiveness = proc_does_not_affect_liveness,
Affects = no
;
AffectsLiveness = proc_default_affects_liveness,
( if Code = "" then
Affects = no
else
Affects = yes
)
)
).
%-----------------------------------------------------------------------------%
:- func local_vars_proc_size_limit = int.
local_vars_proc_size_limit = 10000.
:- func local_vars_block_size_limit = int.
local_vars_block_size_limit = 200.
%-----------------------------------------------------------------------------%
:- end_module ll_backend.use_local_vars.
%-----------------------------------------------------------------------------%