mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-11 11:53:51 +00:00
Estimated hours taken: 1 Branches: main compiler/basic_block.m: Record the number of instructions in each basic block. compiler/use_local_vars.m: Do not perform this quadratic optimization on basic blocks on which it would take too long. compiler/dupelim.m: Don't try to detect duplicates in big blocks; the attempt is expensive, and also very likely to fail. (Big blocks are unlikely to duplicated; the optimization was meant for redundant copies of the procedure epilogue.) compiler/livemap.m: Put a limit on the number of iterations done by the fixpoint algorithm.
257 lines
10 KiB
Mathematica
257 lines
10 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1997-2001,2003-2007, 2010-2011 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: basic_block.m.
|
|
% Main author: zs.
|
|
%
|
|
% This module defines a representation for basic blocks, sequences of
|
|
% instructions with one entry and one exit, and provides predicates that
|
|
% convert a list of instructions into a list of basic blocks and vice versa.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend.basic_block.
|
|
:- interface.
|
|
|
|
:- import_module ll_backend.llds.
|
|
:- import_module mdbcomp.prim_data.
|
|
|
|
:- import_module bool.
|
|
:- import_module counter.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module set.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type block_map == map(label, block_info).
|
|
|
|
:- type block_info
|
|
---> block_info(
|
|
% The label starting the block.
|
|
bi_starting_label :: label,
|
|
|
|
% The instruction containing the label.
|
|
bi_label_instr :: instruction,
|
|
|
|
% The code of the block without the initial label.
|
|
bi_later_instrs :: list(instruction),
|
|
|
|
% The number of instructions in bi_later_instrs.
|
|
bi_num_later_instrs :: int,
|
|
|
|
% Does the previous block (if any) fall through to this block?
|
|
bi_fallen_into :: bool,
|
|
|
|
% The labels we can jump to (not falling through).
|
|
bi_jump_dests :: list(label),
|
|
|
|
% The label we fall through to (if there is one).
|
|
bi_fall_dest :: maybe(label)
|
|
).
|
|
|
|
% create_basic_blocks(ProcInstrs, Comments, ProcLabel, !C, NewLabels,
|
|
% LabelSeq, BlockMap):
|
|
%
|
|
% Given ProcInstrs, the instruction sequence of the procedure given by
|
|
% ProcLabel and whose label counter is currently !.C, create_basic_blocks
|
|
% will divide up ProcInstrs into a sequence of basic blocks, each
|
|
% identified by a label. The info on each basic block is returned in
|
|
% BlockMap, and the sequence of basic blocks is returned in LabelSeq.
|
|
% In the process, create_basic_blocks creates new labels for basic blocks
|
|
% that can be reached only by falling through. The set of these new labels
|
|
% is returned in NewLabels. Any initial comments are returned in Comments.
|
|
%
|
|
:- pred create_basic_blocks(list(instruction)::in, list(instruction)::out,
|
|
proc_label::in, counter::in, counter::out,
|
|
set(label)::out, list(label)::out, block_map::out) is det.
|
|
|
|
% extend_basic_blocks(!LabelSeq, !BlockMap, NewLabels):
|
|
%
|
|
% Given !.LabelSeq, a sequence of labels each referring to a basic block in
|
|
% !.BlockMap, and the set of labels NewLabels that are not the targets of
|
|
% gotos (e.g. because they were freshly created by create_basic_blocks),
|
|
% delete from !.LabelSeq each label in NewLabels, merging its basic block
|
|
% with the immediately previous basic block. As a result, each block in
|
|
% !:BlockMap is an extended basic block.
|
|
%
|
|
:- pred extend_basic_blocks(list(label)::in, list(label)::out,
|
|
block_map::in, block_map::out, set(label)::in) is det.
|
|
|
|
% flatten_basic_blocks(LabelSeq, BlockMap, Instrs, NumInstrs):
|
|
%
|
|
% Given LabelSeq, a sequence of labels each referring to a block in
|
|
% BlockMap, return the concatenation of the basic blocks referred to by
|
|
% the labels in LabelSeq.
|
|
%
|
|
:- pred flatten_basic_blocks(list(label)::in, block_map::in,
|
|
list(instruction)::out, int::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module ll_backend.opt_util.
|
|
|
|
:- import_module int.
|
|
:- import_module require.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
create_basic_blocks(Instrs0, Comments, ProcLabel, !C, NewLabels, LabelSeq,
|
|
BlockMap) :-
|
|
opt_util.get_prologue(Instrs0, LabelInstr, Comments, AfterLabelInstrs),
|
|
Instrs1 = [LabelInstr | AfterLabelInstrs],
|
|
build_block_map(Instrs1, LabelSeq, ProcLabel, no, map.init, BlockMap,
|
|
set.init, NewLabels, !C).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Build up the block map. As we go along, we add labels to the given
|
|
% instruction sequence so that every basic block has labels around it.
|
|
%
|
|
:- pred build_block_map(list(instruction)::in, list(label)::out,
|
|
proc_label::in, bool::in, block_map::in, block_map::out,
|
|
set(label)::in, set(label)::out, counter::in, counter::out) is det.
|
|
|
|
build_block_map([], [], _, _, !BlockMap, !NewLabels, !C).
|
|
build_block_map([OrigInstr0 | OrigInstrs0], LabelSeq, ProcLabel, FallInto,
|
|
!BlockMap, !NewLabels, !C) :-
|
|
( OrigInstr0 = llds_instr(label(OrigLabel), _) ->
|
|
Label = OrigLabel,
|
|
LabelInstr = OrigInstr0,
|
|
RestInstrs = OrigInstrs0
|
|
;
|
|
counter.allocate(N, !C),
|
|
Label = internal_label(N, ProcLabel),
|
|
set.insert(Label, !NewLabels),
|
|
LabelInstr = llds_instr(label(Label), ""),
|
|
RestInstrs = [OrigInstr0 | OrigInstrs0]
|
|
),
|
|
(
|
|
take_until_end_of_block(RestInstrs, [], RevBlockInstrs, Instrs1),
|
|
build_block_map(Instrs1, LabelSeq1, ProcLabel, NextFallInto, !BlockMap,
|
|
!NewLabels, !C),
|
|
(
|
|
RevBlockInstrs = [LastInstr | _],
|
|
reverse_and_count_list(RevBlockInstrs, [], BlockInstrs,
|
|
0, NumBlockInstrs),
|
|
LastInstr = llds_instr(LastUinstr, _),
|
|
opt_util.possible_targets(LastUinstr, SideLabels, _SideCodeAddrs),
|
|
opt_util.can_instr_fall_through(LastUinstr) = NextFallInto
|
|
;
|
|
RevBlockInstrs = [],
|
|
BlockInstrs = [],
|
|
NumBlockInstrs = 0,
|
|
SideLabels = [],
|
|
NextFallInto = yes
|
|
),
|
|
(
|
|
NextFallInto = yes,
|
|
get_fallthrough_from_seq(LabelSeq1, MaybeFallThrough)
|
|
;
|
|
NextFallInto = no,
|
|
MaybeFallThrough = no
|
|
),
|
|
BlockInfo = block_info(Label, LabelInstr, BlockInstrs, NumBlockInstrs,
|
|
FallInto, SideLabels, MaybeFallThrough),
|
|
map.det_insert(Label, BlockInfo, !BlockMap),
|
|
LabelSeq = [Label | LabelSeq1]
|
|
).
|
|
|
|
:- pred take_until_end_of_block(list(instruction)::in,
|
|
list(instruction)::in, list(instruction)::out,
|
|
list(instruction)::out) is det.
|
|
|
|
take_until_end_of_block([], !RevBlockInstrs, []).
|
|
take_until_end_of_block([Instr0 | Instrs0], !RevBlockInstrs, Rest) :-
|
|
Instr0 = llds_instr(Uinstr0, _Comment),
|
|
( Uinstr0 = label(_) ->
|
|
Rest = [Instr0 | Instrs0]
|
|
; opt_util.can_instr_branch_away(Uinstr0) = yes ->
|
|
!:RevBlockInstrs = [Instr0 | !.RevBlockInstrs],
|
|
Rest = Instrs0
|
|
;
|
|
!:RevBlockInstrs = [Instr0 | !.RevBlockInstrs],
|
|
take_until_end_of_block(Instrs0, !RevBlockInstrs, Rest)
|
|
).
|
|
|
|
:- pred reverse_and_count_list(list(T)::in, list(T)::in, list(T)::out,
|
|
int::in, int::out) is det.
|
|
|
|
reverse_and_count_list([], !List, !N).
|
|
reverse_and_count_list([X | Xs], !List, !N) :-
|
|
!:List = [X | !.List],
|
|
!:N = !.N + 1,
|
|
reverse_and_count_list(Xs, !List, !N).
|
|
|
|
:- pred get_fallthrough_from_seq(list(label)::in, maybe(label)::out) is det.
|
|
|
|
get_fallthrough_from_seq(LabelSeq, MaybeFallThrough) :-
|
|
(
|
|
LabelSeq = [NextLabel | _],
|
|
MaybeFallThrough = yes(NextLabel)
|
|
;
|
|
LabelSeq = [],
|
|
MaybeFallThrough = no
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
extend_basic_blocks([], [], !BlockMap, _NewLabels).
|
|
extend_basic_blocks([Label | Labels], LabelSeq, !BlockMap, NewLabels) :-
|
|
(
|
|
Labels = [NextLabel | RestLabels],
|
|
set.member(NextLabel, NewLabels)
|
|
->
|
|
map.lookup(!.BlockMap, Label, BlockInfo),
|
|
map.lookup(!.BlockMap, NextLabel, NextBlockInfo),
|
|
BlockInfo = block_info(BlockLabel, BlockLabelInstr, BlockInstrs,
|
|
NumBlockInstrs, BlockFallInto, BlockSideLabels,
|
|
BlockMaybeFallThrough),
|
|
NextBlockInfo = block_info(NextBlockLabel, _, NextBlockInstrs,
|
|
NumNextBlockInstrs, NextBlockFallInto, NextBlockSideLabels,
|
|
NextBlockMaybeFallThrough),
|
|
expect(unify(BlockLabel, Label), $module, $pred,
|
|
"block label mismatch"),
|
|
expect(unify(NextBlockLabel, NextLabel), $module, $pred,
|
|
"next block label mismatch"),
|
|
expect(unify(BlockMaybeFallThrough, yes(NextLabel)), $module, $pred,
|
|
"fall through mismatch"),
|
|
expect(unify(NextBlockFallInto, yes), $module, $pred,
|
|
"fall into mismatch"),
|
|
NewBlockInfo = block_info(BlockLabel, BlockLabelInstr,
|
|
BlockInstrs ++ NextBlockInstrs,
|
|
NumBlockInstrs + NumNextBlockInstrs, BlockFallInto,
|
|
BlockSideLabels ++ NextBlockSideLabels, NextBlockMaybeFallThrough),
|
|
map.det_update(Label, NewBlockInfo, !BlockMap),
|
|
map.delete(NextLabel, !BlockMap),
|
|
extend_basic_blocks([Label | RestLabels], LabelSeq, !BlockMap,
|
|
NewLabels)
|
|
;
|
|
extend_basic_blocks(Labels, LabelSeqTail, !BlockMap, NewLabels),
|
|
LabelSeq = [Label | LabelSeqTail]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
flatten_basic_blocks([], _, [], 0).
|
|
flatten_basic_blocks([Label | Labels], BlockMap, Instrs, NumInstrs) :-
|
|
flatten_basic_blocks(Labels, BlockMap, RestInstrs, RestNumInstrs),
|
|
map.lookup(BlockMap, Label, BlockInfo),
|
|
BlockInfo = block_info(_, BlockLabelInstr, BlockInstrs, NumBlockInstrs,
|
|
_, _, _),
|
|
Instrs = [BlockLabelInstr | BlockInstrs] ++ RestInstrs,
|
|
NumInstrs = NumBlockInstrs + RestNumInstrs.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module ll_backend.basic_block.
|
|
%-----------------------------------------------------------------------------%
|