Files
mercury/compiler/rl_loop.m
Fergus Henderson f3e08a26d5 Another step towards supporting accurate GC for the LLDs back-end:
Estimated hours taken: 3
Branches: main

Another step towards supporting accurate GC for the LLDs back-end:
modify the LLDS code generator to allocate the proper forwarding pointer
slots for type_infos and typeclass_infos.

compiler/unify_gen.m:
compiler/code_info.m:
compiler/var_locn.m:
	When allocating type_infos or typeclass_infos on the heap,
	if accurate garbage collection is enabled then ensure that we
	reserve a word before the start of the object.  This word is
	used by the collector to store a forwarding pointer.
	For ordinary objects, we store the forwarding pointer in the
	old copy of the object, but we can't clobber the old copies of
	type_infos and typeclass_infos, since they might be used _during_
	garbage collection.
2004-01-08 06:52:36 +00:00

454 lines
16 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1998, 2003-2004 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: rl_loop.m
% Main author: stayl
%
% Detect and move loop invariant instructions.
% As described in the dragon book -
% Aho, Sethi and Ullman,
% Compilers: principles, techniques and tools,
% Addison-Wesley, 1986.
%-----------------------------------------------------------------------------%
:- module aditi_backend__rl_loop.
:- interface.
:- import_module aditi_backend__rl_block.
% Given the flow graph for a procedure, return a new flow
% graph with loop invariant instructions moved out of loops.
:- pred rl_loop__shift_invariants(rl_opt_info::in, rl_opt_info::out) is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module aditi_backend__rl.
:- import_module assoc_list, bool, int, list, map, queue, relation.
:- import_module string, require, set, std_util.
rl_loop__shift_invariants -->
rl_opt_info_get_loops(Loops0),
% Do the outer loops first, so that we move instructions as far
% as they can go in one jump. If a variable is an invariant of
% an outer loop, it must be invariant in any inner loops of that
% outer loop.
{ CompareLoops = (pred(Loop1::in, Loop2::in, Compare::out) is det :-
Loop1 = loop(_, Nodes1),
Loop2 = loop(_, Nodes2),
set__to_sorted_list(Nodes1, Nodes1List),
list__length(Nodes1List, Size1),
set__to_sorted_list(Nodes2, Nodes2List),
list__length(Nodes2List, Size2),
% Sort in descending order on size.
compare(Compare, Size2, Size1)
) },
{ list__sort(CompareLoops, Loops0, SortedLoops) },
rl_loop__shift_invariants_2(SortedLoops, [], Loops),
rl_opt_info_set_loops(Loops).
:- pred rl_loop__shift_invariants_2(list(loop)::in, list(loop)::in,
list(loop)::out, rl_opt_info::in, rl_opt_info::out) is det.
rl_loop__shift_invariants_2([], Loops, Loops) --> [].
rl_loop__shift_invariants_2([Loop0 | LoopsToProcess0],
ProcessedLoops0, ProcessedLoops) -->
{ Loop0 = loop(EntryNode, Nodes) },
% We can only remove invariant computation if it is performed
% on every execution of the loop, i.e. the block containing
% it dominates all the exit points.
rl_opt_info_get_flow_graph(FlowGraph),
{ IsExitPoint =
(pred(Node::in) is semidet :-
relation__lookup_element(FlowGraph, Node, NodeKey),
relation__lookup_from(FlowGraph, NodeKey,
CalledNodeKeys),
set__member(CalledNodeKey, CalledNodeKeys),
relation__lookup_key(FlowGraph,
CalledNodeKey, CalledNode),
\+ set__member(CalledNode, Nodes)
) },
{ set__to_sorted_list(Nodes, NodeList) },
{ list__filter(IsExitPoint, NodeList, ExitNodes) },
rl_opt_info_get_dominator_info(DominatorInfo),
{
ExitNodes = [ExitNode | ExitNodes1],
map__lookup(DominatorInfo, ExitNode, ExitDominatingNodes0),
IntersectDominators =
(pred(N::in, Inter0::in, Inter::out) is det :-
map__lookup(DominatorInfo, N, DominatingNodes1),
set__intersect(Inter0, DominatingNodes1, Inter)
),
list__foldl(IntersectDominators, ExitNodes1,
ExitDominatingNodes0, ExitDominatingNodes)
;
ExitNodes = [],
error("rl_loop__shift_invariants: loop has no exit nodes")
},
{ set__init(OneBlockRels0) },
{ set__init(ManyBlockRels0) },
% Pick out relations which are only used in one block within the
% loop. Since rl_gen.m only generates loops with one block,
% this isn't a problem.
rl_loop__get_nonlocal_rels(NodeList, OneBlockRels0,
_OneBlockRels, ManyBlockRels0, ManyBlockRels),
rl_loop__shift_invariants_loop(NodeList, NodeList, ExitDominatingNodes,
ManyBlockRels, _, no, MaybeHeader),
rl_loop__maybe_add_header(MaybeHeader, Nodes, EntryNode,
ProcessedLoops0, ProcessedLoops1,
LoopsToProcess0, LoopsToProcess),
rl_loop__shift_invariants_2(LoopsToProcess,
[Loop0 | ProcessedLoops1], ProcessedLoops).
%-----------------------------------------------------------------------------%
% Given a list of blocks, pick out the relations that occur
% in more than one of the blocks.
:- pred rl_loop__get_nonlocal_rels(list(block_id)::in,
set(relation_id)::in, set(relation_id)::out, set(relation_id)::in,
set(relation_id)::out, rl_opt_info::in, rl_opt_info::out) is det.
rl_loop__get_nonlocal_rels([], OneBlock, OneBlock,
ManyBlock, ManyBlock) --> [].
rl_loop__get_nonlocal_rels([Block | Blocks], OneBlock0, OneBlock,
ManyBlock0, ManyBlock) -->
rl_opt_info_get_block(Block, block(_, Instrs, _, _)),
{ GetInstrRelations =
(pred(Instr::in, Rels0::in, Rels::out) is det :-
rl__instr_relations(Instr, Inputs, Outputs),
set__insert_list(Rels0, Inputs, Rels1),
set__insert_list(Rels1, Outputs, Rels)
) },
{ set__init(BlockRels0) },
{ list__foldl(GetInstrRelations, Instrs, BlockRels0, BlockRels1) },
{ Update = (pred(Rel::in, Changed0::in, Changed::out) is det :-
Changed0 = One0 - Many0,
( set__member(Rel, Many0) ->
Many = Many0,
One = One0
; set__member(Rel, One0) ->
set__delete(One0, Rel, One),
set__insert(Many0, Rel, Many)
;
set__insert(One0, Rel, One),
Many = Many0
),
Changed = One - Many
) },
{ set__to_sorted_list(BlockRels1, BlockRels) },
{ list__foldl(Update, BlockRels,
OneBlock0 - ManyBlock0, OneBlock1 - ManyBlock1) },
rl_loop__get_nonlocal_rels(Blocks, OneBlock1, OneBlock,
ManyBlock1, ManyBlock).
%-----------------------------------------------------------------------------%
:- pred rl_loop__shift_invariants_loop(list(block_id)::in, list(block_id)::in,
set(block_id)::in, set(relation_id)::in, set(relation_id)::out,
maybe(block)::in, maybe(block)::out, rl_opt_info::in,
rl_opt_info::out) is det.
rl_loop__shift_invariants_loop([], _, _, N, N, H, H) --> [].
rl_loop__shift_invariants_loop([LoopNode | LoopNodes], AllLoopNodes,
ExitDominatingNodes, NonLocalRels0, NonLocalRels,
MaybeHeader0, MaybeHeader) -->
rl_opt_info_get_block(LoopNode, Block0),
{ Block0 = block(Label, Instrs0, Branch, BlockInfo) },
{ GetChangedRels =
(pred(Instr::in, Rels0::in, Rels::out) is det :-
rl__instr_relations(Instr, _, Outputs),
set__insert_list(Rels0, Outputs, Rels)
) },
{ set__init(BlockChangedRels0) },
{ list__foldl(GetChangedRels, Instrs0,
BlockChangedRels0, BlockChangedRels) },
(
% Can't shift invariants if this block doesn't dominate
% all the exits to the loop (i.e. isn't executed on every
% pass through the loop).
{ set__member(LoopNode, ExitDominatingNodes) },
% Don't bother processing the block if it doesn't contain
% any relations which are changed in only one block within
% the loop.
{ set__difference(BlockChangedRels, NonLocalRels0,
ThisBlockRels) },
{ \+ set__empty(ThisBlockRels) }
->
{ set__init(SeenRels) },
{ set__init(ShiftedRels0) },
rl_loop__shift_invariants_block(Instrs0, Instrs, LoopNode,
AllLoopNodes, ExitDominatingNodes, BlockChangedRels,
NonLocalRels0, NonLocalRels1, SeenRels,
ShiftedRels0, _ShiftedRels, MaybeHeader0, MaybeHeader1),
rl_opt_info_set_block(LoopNode,
block(Label, Instrs, Branch, BlockInfo))
;
{ MaybeHeader1 = MaybeHeader0 },
{ NonLocalRels1 = NonLocalRels0 }
),
rl_loop__shift_invariants_loop(LoopNodes, AllLoopNodes,
ExitDominatingNodes, NonLocalRels1, NonLocalRels,
MaybeHeader1, MaybeHeader).
%-----------------------------------------------------------------------------%
:- pred rl_loop__shift_invariants_block(list(rl_instruction)::in,
list(rl_instruction)::out, block_id::in, list(block_id)::in,
set(block_id)::in, set(relation_id)::in, set(relation_id)::in,
set(relation_id)::out, set(relation_id)::in,
set(relation_id)::in, set(relation_id)::out, maybe(block)::in,
maybe(block)::out, rl_opt_info::in, rl_opt_info::out) is det.
rl_loop__shift_invariants_block([], [], _, _, _, _, NL, NL, _,
S, S, H, H) --> [].
rl_loop__shift_invariants_block([Instr | Instrs0], Instrs, LoopNode,
AllLoopNodes, ExitDominatingNodes, BlockChangedRels,
NonLocalRels0, NonLocalRels, SeenRels0,
ShiftedRels0, ShiftedRels, MaybeHeader0, MaybeHeader) -->
{ rl__instr_relations(Instr, InputRels, OutputRels) },
(
%
% Check if this instruction is invariant.
%
{ \+ rl_loop__unmovable(Instr) },
% Are all the inputs produced outside the loop.
{ \+ (
list__member(InputRel, InputRels),
\+ set__member(InputRel, ShiftedRels0),
( set__member(InputRel, BlockChangedRels)
; set__member(InputRel, NonLocalRels0)
; set__member(InputRel, SeenRels0)
)
) },
% Is there no use of one of the output relations within
% the block which could be reached by some other definition
% of the output.
{ \+ (
list__member(OutputRel, OutputRels),
set__member(OutputRel, SeenRels0)
) },
% Check that the outputs of this instruction are the only
% values of those output relations used by other instructions
% within this block.
{ set__list_to_set(OutputRels, OutputRelSet) },
{ rl_loop__check_later_uses_in_block(OutputRelSet,
Instrs0, Instrs1) }
->
{ rl_loop__add_instruction_to_pre_header(MaybeHeader0,
Instr, MaybeHeader1) },
{ set__insert_list(SeenRels0, InputRels, SeenRels1) },
{ set__insert_list(SeenRels1, OutputRels, SeenRels) },
{ set__insert_list(ShiftedRels0, InputRels, ShiftedRels1) },
{ set__union(ShiftedRels1, OutputRelSet, ShiftedRels2) },
rl_loop__shift_invariants_block(Instrs1, Instrs, LoopNode,
AllLoopNodes, ExitDominatingNodes, BlockChangedRels,
NonLocalRels0, NonLocalRels, SeenRels,
ShiftedRels2, ShiftedRels, MaybeHeader1, MaybeHeader)
;
{ set__insert_list(SeenRels0, InputRels, SeenRels1) },
{ set__insert_list(SeenRels1, OutputRels, SeenRels) },
rl_loop__shift_invariants_block(Instrs0, Instrs1, LoopNode,
AllLoopNodes, ExitDominatingNodes, BlockChangedRels,
NonLocalRels0, NonLocalRels, SeenRels,
ShiftedRels0, ShiftedRels, MaybeHeader0, MaybeHeader),
{ Instrs = [Instr | Instrs1] }
).
%-----------------------------------------------------------------------------%
:- pred rl_loop__unmovable(rl_instruction::in) is semidet.
rl_loop__unmovable(label(_) - _).
rl_loop__unmovable(goto(_) - _).
rl_loop__unmovable(conditional_goto(_, _) - _).
rl_loop__unmovable(clear(_) - _).
rl_loop__unmovable(unset(_) - _).
%-----------------------------------------------------------------------------%
% Check that all instructions in the current block that
% use any of the outputs of the instruction we are trying to
% move only use the version produced by that instruction.
:- pred rl_loop__check_later_uses_in_block(set(relation_id)::in,
list(rl_instruction)::in, list(rl_instruction)::out) is semidet.
rl_loop__check_later_uses_in_block(_, [], []).
rl_loop__check_later_uses_in_block(OutputRels, [Instr | Instrs0], Instrs) :-
rl__instr_relations(Instr, _, InstrOutputs),
set__list_to_set(InstrOutputs, InstrOutputSet),
set__intersect(OutputRels, InstrOutputSet, RedefinedRels),
set__empty(RedefinedRels),
rl_loop__check_later_uses_in_block(OutputRels, Instrs0, Instrs1),
Instrs = [Instr | Instrs1].
%-----------------------------------------------------------------------------%
:- pred rl_loop__add_instruction_to_pre_header(maybe(block)::in,
rl_instruction::in, maybe(block)::out) is det.
rl_loop__add_instruction_to_pre_header(no, Instr, yes(Block)) :-
block_info_init(BlockInfo),
Block = block(no, [Instr], no, BlockInfo).
rl_loop__add_instruction_to_pre_header(yes(Block0), Instr, yes(Block)) :-
Block0 = block(Label, RevInstrs0, Branch, BlockInfo),
RevInstrs = [Instr | RevInstrs0],
Block = block(Label, RevInstrs, Branch, BlockInfo).
%-----------------------------------------------------------------------------%
:- pred rl_loop__maybe_add_header(maybe(block)::in, set(block_id)::in,
block_id::in, list(loop)::in, list(loop)::out, list(loop)::in,
list(loop)::out, rl_opt_info::in, rl_opt_info::out) is det.
rl_loop__maybe_add_header(no, _, _, P, P, L, L) --> [].
rl_loop__maybe_add_header(yes(HeaderBlock), LoopNodes, EntryNode,
ProcessedLoops0, ProcessedLoops,
LoopsToProcess0, LoopsToProcess) -->
rl_opt_info_get_new_label(HeaderLabel, HeaderBlockId),
% Add a new block.
{ HeaderBlock = block(Label, RevInstrs, Branch, BlockInfo) },
{ list__reverse(RevInstrs, Instrs) },
{ HeaderBlock1 = block(Label, Instrs, Branch, BlockInfo) },
rl_opt_info_set_block(HeaderBlockId, HeaderBlock1),
rl_opt_info_get_block(EntryNode, EntryBlock),
% Add the new block to the ordering.
rl_opt_info_get_rev_block_order(RevOrder0),
{ rl_loop__insert_into_order(RevOrder0, HeaderBlockId,
EntryNode, RevOrder) },
rl_opt_info_set_rev_block_order(RevOrder),
% Fix up the flow graph.
rl_opt_info_get_flow_graph(FlowGraph0),
{ relation__lookup_element(FlowGraph0, EntryNode, EntryKey) },
{ relation__add_element(FlowGraph0, HeaderBlockId,
HeaderKey, FlowGraph1) },
{ relation__add(FlowGraph1, HeaderKey, EntryKey, FlowGraph) },
rl_opt_info_set_flow_graph(FlowGraph),
% Add the new block to all loops containing the old header.
( { EntryBlock = block(yes(EntryLabel), _, _, _) } ->
{ UpdateLoop = (pred(L0::in, L::out) is det :-
L0 = loop(LEntry0, LNodes0),
( set__member(EntryNode, LNodes0) ->
set__insert(LNodes0, HeaderBlockId, LNodes)
;
LNodes = LNodes0
),
L = loop(LEntry0, LNodes)
) },
{ list__map(UpdateLoop, ProcessedLoops0, ProcessedLoops) },
{ list__map(UpdateLoop, LoopsToProcess0, LoopsToProcess) },
rl_loop__update_gotos(LoopNodes, EntryLabel, HeaderLabel,
EntryNode, HeaderBlockId)
;
{ error("rl_loop__maybe_add_header: " ++
"loop entry does not have a label") }
).
%-----------------------------------------------------------------------------%
:- pred rl_loop__insert_into_order(list(block_id)::in, block_id::in,
block_id::in, list(block_id)::out) is det.
rl_loop__insert_into_order([], _, _, _) :-
error("rl_loop__insert_into_order").
rl_loop__insert_into_order([Block | RevOrder0],
HeaderBlockId, EntryNode, RevOrder) :-
( Block = EntryNode ->
RevOrder = [EntryNode, HeaderBlockId | RevOrder0]
;
rl_loop__insert_into_order(RevOrder0, HeaderBlockId,
EntryNode, RevOrder1),
RevOrder = [Block | RevOrder1]
).
%-----------------------------------------------------------------------------%
:- pred rl_loop__update_gotos(set(block_id)::in, label_id::in, label_id::in,
block_id::in, block_id::in, rl_opt_info::in, rl_opt_info::out) is det.
rl_loop__update_gotos(LoopNodes, OldLabel, NewLabel,
OldEntryBlock, NewEntryBlock) -->
%
% Fix up gotos to the loop entry from outside the loop.
%
rl_opt_info_get_block_map(BlockMap0),
{ map__to_assoc_list(BlockMap0, BlockAL0) },
{ UpdateBlock = (pred(BlockIdAndBlock0::in, BlockIdAndBlock::out)
is det :-
BlockIdAndBlock0 = BlockId - block(A, B, MaybeBranch0, D),
( set__member(BlockId, LoopNodes) ->
MaybeBranch = MaybeBranch0
;
(
MaybeBranch0 = yes(goto(OldLabel) - Comment)
->
MaybeBranch = yes(goto(NewLabel) - Comment)
;
MaybeBranch0 =
yes(conditional_goto(Cond, OldLabel)
- Comment)
->
MaybeBranch =
yes(conditional_goto(Cond, NewLabel)
- Comment)
;
MaybeBranch = MaybeBranch0
)
),
BlockIdAndBlock = BlockId - block(A, B, MaybeBranch, D)
) },
{ list__map(UpdateBlock, BlockAL0, BlockAL) },
{ map__from_assoc_list(BlockAL, BlockMap) },
rl_opt_info_set_block_map(BlockMap),
%
% Fix up the flow graph.
%
rl_opt_info_get_flow_graph(FlowGraph0),
{ relation__lookup_element(FlowGraph0,
OldEntryBlock, OldEntryBlockKey) },
{ relation__lookup_element(FlowGraph0,
NewEntryBlock, NewEntryBlockKey) },
{ relation__lookup_to(FlowGraph0, OldEntryBlockKey,
CallingBlockKeys) },
{ set__to_sorted_list(LoopNodes, NodeList) },
{ list__map(relation__lookup_element(FlowGraph0),
NodeList, NodeKeys) },
{ set__delete_list(CallingBlockKeys, NodeKeys,
OutsideLoopCallingBlocks) },
{ set__to_sorted_list(OutsideLoopCallingBlocks, OutsideLoopBlocks) },
{ list__length(OutsideLoopBlocks, NumBlocks) },
{ list__duplicate(NumBlocks, OldEntryBlockKey, OldEntryList) },
{ assoc_list__from_corresponding_lists(OutsideLoopBlocks, OldEntryList,
OldEntryAssocList) },
{ relation__remove_assoc_list(FlowGraph0,
OldEntryAssocList, FlowGraph1) },
{ list__duplicate(NumBlocks, NewEntryBlockKey, NewEntryList) },
{ assoc_list__from_corresponding_lists(OutsideLoopBlocks, NewEntryList,
NewEntryAssocList) },
{ relation__add_assoc_list(FlowGraph1,
NewEntryAssocList, FlowGraph) },
rl_opt_info_set_flow_graph(FlowGraph).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%