0% found this document useful (0 votes)
14 views35 pages

CS6109 Module 10

Module 10 of CS6109 covers key concepts in compiler design, including basic blocks, next-use information, register allocation, and DAG construction. It emphasizes the importance of optimizing code generation through techniques such as local optimization within basic blocks and the use of flow graphs. The module also discusses methods for efficient register allocation and the representation of basic blocks using DAGs to improve code performance.

Uploaded by

cip29.mit
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views35 pages

CS6109 Module 10

Module 10 of CS6109 covers key concepts in compiler design, including basic blocks, next-use information, register allocation, and DAG construction. It emphasizes the importance of optimizing code generation through techniques such as local optimization within basic blocks and the use of flow graphs. The module also discusses methods for efficient register allocation and the representation of basic blocks using DAGs to improve code performance.

Uploaded by

cip29.mit
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 35

CS6109 – COMPILER DESIGN

Module – 10

Presented By
Dr. S. Muthurajkumar,
Assistant Professor,
Dept. of CT, MIT Campus,
Anna University, Chennai.
MODULE - 10
 Basic blocks
 Next use Information
 Register allocation
 DAG construction
 Loops

2
BASIC BLOCKS
 This section introduces a graph representation of intermediate code that is helpful
for discussing code generation even if the graph is not constructed explicitly by a
code-generation algorithm. Code generation benefits from context.
 The representation is constructed as follows:
1. Partition the intermediate code into basic blocks, which are maximal sequences
of consecutive three-address instructions with the properties that
(a) The flow of control can only enter the basic block through the first
instruction in the block. That is, there are no jumps into the middle of the
block.
(b) Control will leave the block without halting or branching, except possibly at
the last instruction in the block.
2. The basic blocks become the nodes of a flow graph, whose edges indicate which
blocks can follow which other blocks.

3
BASIC BLOCKS
 Our first job is to partition a sequence of three-address instructions into basic
blocks.
 We begin a new basic block with the first instruction and keep adding
instructions until we meet either a jump, a conditional jump, or a label on the
following instruction.
 In the absence of jumps and labels, control proceeds sequentially from one
instruction to the next.

4
BASIC BLOCKS
 Algorithm: Partitioning three-address instructions into basic blocks.
 INPUT: A sequence of three-address instructions.
 OUTPUT: A list of the basic blocks for that sequence in which each instruction is
assigned to exactly one basic block.
 METHOD: First, we determine those instructions in the intermediate code that
are leaders, that is, the first instructions in some basic block. The instruction just
past the end of the intermediate program is not included as a leader. The rules for
finding leaders are:
1. The first three-address instruction in the intermediate code is a leader.
2. Any instruction that is the target of a conditional or unconditional jump is a
leader.
3. Any instruction that immediately follows a conditional or unconditional jump is a
leader.
5
BASIC BLOCKS
 Intermediate code to set a 10 x 10 matrix to an identity matrix

6
BASIC BLOCKS
 Source code for 10 x 10 matrix

7
NEXT-USE INFORMATION
 Knowing when the value of a variable will be used next is essential for
generating good code.
 If the value of a variable that is currently in a register will never be referenced
subsequently, then that register can be assigned to another variable.
 The use of a name in a three-address statement is defined as follows.
 Suppose three-address statement i assigns a value to x.
 If statement j has x as an operand, and control can flow from statement i to j
along a path that has no intervening assignments to x, then we say statement j
uses the value of x computed at statement i.
 We further say that x is live at statement i.

8
NEXT-USE INFORMATION
 Algorithm: Determining the liveness and next-use information for each statement
in a basic block.
 INPUT: A basic block B of three-address statements. We assume that the symbol
table initially shows all nontemporary variables in B as being live on exit.
 OUTPUT: At each statement i: x = y +z in B, we attach to i the liveness and next-
use information of x, y, and z.
 METHOD: We start at the last statement in B and scan backwards to the
beginning of B. At each statement i: x = y + z in B, we do the following:
1.Attach to statement i the information currently found in the symbol table
regarding the next use and liveness of x, y, and z.
2.In the symbol table, set x to “not live” and “no next use”.
3.In the symbol table, set y and z to “live” and the next uses of y and z to i.
9
FLOW GRAPHS
 Once an intermediate-code program is partitioned into basic blocks, we
represent the flow of control between them by a flow graph.
 The nodes of the flow graph are the basic blocks.
 There is an edge from block B to block C if and only if it is possible for the
first instruction in block C to immediately follow the last instruction in block
B.
 There are two ways that such an edge could be justified:
 There is a conditional or unconditional jump from the end of B to the
beginning of C.
 C immediately follows B in the original order of the three-address
instructions, and B does not end in an unconditional jump.
 We say that B is a predecessor of C, and C is a successor of B.
10
FLOW GRAPHS
 Often we add two nodes, called the entry and exit, that do not correspond to
executable intermediate instructions.
 There is an edge from the entry to the first executable node of the flow graph,
that is, to the basic block that comes from the first instruction of the
intermediate code.
 There is an edge to the exit from any basic block that contains an instruction
that could be the last executed instruction of the program.
 If the final instruction of the program is not an unconditional jump, then the
block containing the final instruction of the program is one predecessor of the
exit, but so is any basic block that has a jump to code that is not part of the
program.

11
FLOW GRAPHS

12
REPRESENTATION OF FLOW GRAPHS
 The flow graph, it is normal to replace the jumps to instruction numbers or
labels by jumps to basic blocks.
 Recall that every conditional or unconditional jump is to the leader of some
basic block, and it is to this block that the jump will now refer.
 The reason for this change is that after constructing the flow graph, it is
common to make substantial changes to the instructions in the various basic
blocks.
 If jumps were to instructions, we would have to fix the targets of the jumps
every time one of the target instructions was changed.
 Flow graphs, being quite ordinary graphs, can be represented by any of the data
structures appropriate for graphs.
 The content of nodes (basic blocks) need their own representation.
13
REGISTER ALLOCATION AND
ASSIGNMENT
 Instructions involving only register operands are faster than those involving
memory operands.
 On modern machines, processor speeds are often an order of magnitude or
more faster than memory speeds.
 Therefore, efficient utilization of registers is vitally important in generating
good code.
 Global Register Allocation
 Usage Counts
 Register Assignment for Outer Loops
 Register Allocation by Graph Coloring

14
REGISTER ALLOCATION AND
ASSIGNMENT
 Global Register Allocation
 The code generation algorithm used registers to hold values for the duration of
a single basic block.
 However, all live variables were stored at the end of each block.
 To save some of these stores and corresponding loads, we might arrange to
assign registers to frequently used variables and keep these registers consistent
across block boundaries (globally).
 Since programs spend most of their time in inner loops, a natural approach to
global register assignment is to try to keep a frequently used value in a fixed
register throughout a loop.

15
REGISTER ALLOCATION AND
ASSIGNMENT
 Usage Counts
 We shall assume that the savings to be realized by keeping a variable x in a
register for the duration of a loop L is one unit of cost for each reference to x if
x is already in a register.
 However, if we use the approach to generate code for a block, there is a good
chance that after x has been computed in a block it will remain in a register if
there are subsequent uses of x in that block.
 Thus we count a savings of one for each use of x in loop L that is not preceded
by an assignment to x in the same block.
 We also save two units if we can avoid a store of x at the end of a block.
 Thus, if x is allocated a register, we count a savings of two for each block in
loop L for which x is live on exit and in which x is assigned a value.
16
REGISTER ALLOCATION AND
ASSIGNMENT

Flow graph of an inner loop


17
REGISTER ALLOCATION AND
ASSIGNMENT

Code sequence using global register assignment


18
REGISTER ALLOCATION AND
ASSIGNMENT
 Register Assignment for Outer Loops
 Having assigned registers and generated code for inner loops, we may apply
the same idea to progressively larger enclosing loops.
 If an outer loop L1 contains an inner loop L2, the names allocated registers in L 2
need not be allocated registers in L1 - L2.
 However, if we choose to allocate x a register in L 2 but not L1, we must load x
on entrance to L2 and store x on exit from L2.
 We leave as an exercise the derivation of a criterion for selecting names to be
allocated registers in an outer loop L, given that choices have already been
made for all loops nested within L.

19
REGISTER ALLOCATION AND
ASSIGNMENT
 Register Allocation by Graph Coloring
 When a register is needed for a computation but all available registers are in
use, the contents of one of the used registers must be stored (spilled) into a
memory location in order to free up a register.
 Graph coloring is a simple, systematic technique for allocating registers and
managing register spills. In the method, two passes are used.
 In the first, target-machine instructions are selected as though there are an
infinite number of symbolic registers; in effect, names used in the intermediate
code become names of registers and the three-address instructions become
machine-language instructions.
 In the second pass, for each procedure a register-interference graph is
constructed in which the nodes are symbolic registers and an edge connects two
nodes if one is live at a point where the other is defined.
20
OPTIMIZATION OF BASIC BLOCKS
 We can often obtain a substantial improvement in the running time of code
merely by performing local optimization within each basic block by itself.
 More thorough global optimization, which looks at how information flows
among the basic blocks of a program.
 The DAG Representation of Basic Blocks
 Finding Local Common Subexpressions
 Dead Code Elimination
 The Use of Algebraic Identities
 Representation of Array References
 Pointer Assignments and Procedure Calls
 Reassembling Basic Blocks From DAG's

21
THE DAG REPRESENTATION OF BASIC
BLOCKS
 The idea extends naturally to the collection of expressions that are created
within one basic block. We construct a DAG for a basic block as follows:
1. There is a node in the DAG for each of the initial values of the variables
appearing in the basic block.
2.There is a node N associated with each statement s within the block.
 The children of N are those nodes corresponding to statements that are the last
definitions, prior to s, of the operands used by s.
§ Node N is labeled by the operator applied at s, and also attached to N is the list
of variables for which it is the last definition within the block.
§ Certain nodes are designated output nodes. These are the nodes whose
variables are live on exit from the block; that is, their values may be used later,
in another block of the flow graph.
22
THE DAG REPRESENTATION OF BASIC
BLOCKS
 The DAG representation of a basic block lets us perform several code
improving transformations on the code represented by the block.
a)We can eliminate local common subexpressions, that is, instructions that
compute a value that has already been computed.
b)We can eliminate dead code, that is, instructions that compute a value that is
never used.
c)We can reorder statements that do not depend on one another; such reordering
may reduce the time a temporary value needs to be preserved in a register.
d)We can apply algebraic laws to reorder operands of three-address instructions,
and sometimes thereby simplify the computation.

23
FINDING LOCAL COMMON
SUBEXPRESSIONS
 Common subexpressions can be detected by noticing, as a new node M is about to
be added, whether there is an existing node N with the same children, in the same
order, and with the same operator.
 If so, N computes the same value as M and may be used in its place.
 A DAG for the block
a=b+c
b=a-d
c=b+c
d=a-d
 When we construct the node for the third statement c = b+c, we know that the use
of b in b+c refers to the node of labeled -, because that is the most recent definition
of b.
 Thus, we do not confuse the values computed at statements one and three.
24
DEAD CODE ELIMINATION
 The operation on DAG's that corresponds to dead-code elimination can be
implemented as follows.
 We delete from a DAG any root (node with no ancestors) that has no live
variables attached.
 Repeated application of this transformation will remove all nodes from the
DAG that correspond to dead code.
 a and b are live but c and e are not, we can
immediately remove the root labeled e.
 Then, the node labeled c becomes a root and
can be removed.
 The roots labeled a and b remain, since they
each have live variables attached.
25
THE USE OF ALGEBRAIC IDENTITIES
 Algebraic identities represent another important class of optimizations on basic
blocks.
 For example, we may apply arithmetic identities, such as
x+0=0+x=x x-0=x
x*1=1*x=x x/1=x
 to eliminate computations from a basic block.
 Another class of algebraic optimizations includes local reduction in strength,
that is, replacing a more expensive operator by a cheaper one as in:
EXPENSIVE CHEAPER
x2 = x*x
2*x = x+x
x/2 = x * 0.5
26
REPRESENTATION OF ARRAY
REFERENCES
 At first glance, it might appear that the array-indexing instructions can be
treated like any other operator.
 Consider for instance the sequence of three-address statements:
x = a[i]
a[j] = y
z = a[i]
 If we think of a[i] as an operation involving a and i, similar to a + i, then it
might appear as if the two uses of a[i] were a common subexpression.
 In that case, we might be tempted to \optimize" by replacing the third
instruction z = a[i] by the simpler z = x.

27
POINTER ASSIGNMENTS AND
PROCEDURE CALLS
 When we assign indirectly through a pointer, as in the assignments
x = *p
*q = y
 we do not know what p or q point to. In effect, x = *p is a use of every variable
whatsoever, and *q = y is a possible assignment to every variable.
 As a consequence, the operator = * must take all nodes that are currently
associated with identifiers as arguments, which is relevant for dead-code
elimination.
 More importantly, the * = operator kills all other nodes so far constructed in the
DAG.

28
REASSEMBLING BASIC BLOCKS FROM
DAG'S
 After we perform whatever optimizations are possible while constructing the
DAG or by manipulating the DAG once constructed, we may reconstitute the
three-address code for the basic block from which we built the DAG.
 For each node that has one or more attached variables, we construct a three-
address statement that computes the value of one of those variables.
 We prefer to compute the result into a variable that is live on exit from the
block.
 Decided that if b is not live on exit from the block, then the three statements
a=b+c
d=a-d
c=d+c
 suffice to reconstruct the basic block.
29
REASSEMBLING BASIC BLOCKS FROM
DAG'S
 The third instruction, c = d+c, must use d as an operand rather than b, because
the optimized block never computes b.
 If both b and d are live on exit, or if we are not sure whether or not they are live
on exit, then we need to compute b as well as d.
 We can do so with the sequence
a=b+c
d=a-d
b=d
c=d+c
 This basic block is still more efficient than the original.
 Although the number of instructions is the same, we have replaced a
subtraction by a copy, which tends to be less expensive on most machines.
30
LOOPS
 Programming-language constructs like while-statements, do-while-statements,
and for-statements naturally give rise to loops in programs.
 Since virtually every program spends most of its time in executing its loops, it
is especially important for a compiler to generate good code for loops.
 Many code transformations depend upon the identification of “loops” in a flow
graph.
 We say that a set of nodes L in a flow graph is a loop if L contains a node e
called the loop entry, such that:
§ e is not ENTRY, the entry of the entire flow graph.
§ No node in L besides e has a predecessor outside L. That is, every path from
ENTRY to any node in L goes through e.
§ Every node in L has a nonempty path, completely within L, to e.
31
LOOPS
 Three loops:
1.B3 by itself.
2.B6 by itself.
3.{B2, B3, B4}.
 The first two are single nodes with an edge to the
node itself. For instance, B3 forms a loop with
B3 as its entry.
 Note that the last requirement for a loop is that
there be a nonempty path from B3 to itself.
 Thus, a single node like B2, which does not have
an edge B2  B2, is not a loop, since there is no
nonempty path from B2 to itself within {B2}.
32
LOOPS
 The third loop, L = {B2, B3, B4}, has B2 as its
loop entry.
 Note that among these three nodes, only B2 has
a predecessor, B1, that is not in L.
 Further, each of the three nodes has a nonempty
path to B2 staying within L.
 For instance, B2 has the path B2  B3  B4
 B2.

33
REFERENCE
1. Alfred V. Aho, Monica S. Lam, Ravi Sethi, Jeffrey D. Ullman, “Compilers:
Principles, Techniques and Tools”, Second Edition, Pearson Education Limited,
2014.
2. Randy Allen, Ken Kennedy, “Optimizing Compilers for Modern Architectures:
A Dependence-based Approach”, Morgan Kaufmann Publishers, 2002.
3. Steven S. Muchnick, “Advanced Compiler Design and Implementation”,
Morgan Kaufmann Publishers - Elsevier Science, India, Indian Reprint, 2003.
4. Keith D Cooper and Linda Torczon, “Engineering a Compiler”, Morgan
Kaufmann Publishers, Elsevier Science, 2004.
5. V. Raghavan, “Principles of Compiler Design”, Tata McGraw Hill Education
Publishers, 2010.
6. Allen I. Holub, “Compiler Design in C”, Prentice-Hall Software Series, 1993.

34
35

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy