You are on page 1of 53

Compiler Design

Data-Flow Analysis & Optimizations

Professor Yi-Ping You


Department of Computer Science
http://www.cs.nctu.edu.tw/~ypyou/

Compiler Design Page 1


The Structure of a Compiler
Source Code

Lexical Analyzer
(scanner)

Syntax Analyzer
(parser)
Front End
Semantic Analyzer
(Analysis)
Intermediate Code
Generator

Code Optimizer

Back End
Code Generator
(Synthesis)
Target Code
Compiler Design Page 2
Outline
Introduction to Dataflow Analysis
Dataflow Algorithms
Liveness Analysis
Reaching Definition Analysis
DU/UD Chains
Generalized Dataflow Algorithm
Available Definition
Available Expression
Optimizations

Compiler Design Page 3


Dataflow Analysis + Optimization
Control flow analysis
Treat BB as black box
Just care about branches
Now ... r1 = r2 + r3
Start looking at operations in BBs r6 = r4 – r5
What’s computed and where
Classical optimizations
Make the computation more efficient r4 = 4
Get rid of redundancy r6 = 8
Simplify
Ex: Common Subexpression Elimination
Is r2 + r3 redundant? r6 = r2 + r3
What about r4 - r5? r7 = r4 – r5
What if there were 1000 BB’s
Dataflow analysis !!

Compiler Design Page 4


Dataflow Analysis Introduction
Dataflow analysis – Collection of information
that summarizes the creation/destruction of
values in a program. Used to identify legal
optimization opportunities.
r1 = r2 + r3
r6 = r4 – r5
Pick an arbitrary point in the program
Which virtual registers contain
useful data values?
r4 = 4
(liveness or upward exposed uses)
r6 = 8
Which definitions may reach
this point? (reaching defns)
r6 = r2 + r3
r7 = r4 – r5 Which definitions are guaranteed
to reach this point? (available defns)

Compiler Design Page 5


Outline
Introduction to Dataflow Analysis
Dataflow Algorithms
Liveness Analysis
Reaching Definition Analysis
DU/UD Chains
Generalized Dataflow Algorithm
Available Definition
Available Expression
Optimizations

Compiler Design Page 6


Live Variable (Liveness) Analysis
Defn: For each point p in a program and each variable
y, determine whether y can be used before being
redefined starting at p
Algorithm sketch
For each BB, y is live if it is used before being defined in the
BB or it is live leaving the block
Backward dataflow analysis as propagation occurs from uses
upwards to defs
4 sets
USE = set of external variables consumed in the BB
DEF = set of variables defined in the BB
IN = set of variables that are live at the entry point of a BB
OUT = set of variables that are live at the exit point of a BB

Compiler Design Page 7


Liveness Example

r2, r3, r4, r5 are all live as they


are consumed later, r6 is dead
r1 = r2 + r3 as it is redefined later
r6 = r4 – r5
r4 is dead, as it is redefined.
So is r6. r2, r3, r5 are live

r4 = 4
r6 = 8

r6 = r2 + r3 What does this mean? r6 = r4 – r5 is


r7 = r4 – r5 useless, it produces a dead value !!
Get rid of it!

Compiler Design Page 8


Compute USE/DEF Sets For Each BB
for each basic block in the procedure, X, do
DEF(X) = 0
USE(X) = 0
for each operation in sequential order in X, op, do
for each source operand of op, src, do
if (src not in DEF(X)) then
USE(X) += src
endif
endfor
for each destination operand of op, dest, do
DEF(X) += dest
endfor
endfor def is the union of all the LHS’s
endfor use is all the VRs that are used before defined

Compiler Design Page 9


Example USE/DEF Calculation

r1 = MEM[r2+0]
r2 = r2 + 1 USE = {r2,r4}
r3 = r1 * r4 DEF = {r1,r2,r3}

r1 = r1 + 5 r2 = 0
USE = {r1,r5} USE = Æ
r3 = r5 – r1 r7 = 23
DEF = {r1,r3,r7} DEF = {r1,r2,r7}
r7 = r3 * 2 r1 = 4

r8 = r7 + 5
USE = {r3,r7}
r1 = r3 – r8
DEF = {r1,r3,r8}
r3 = r1 * 2

Compiler Design Page 10


Compute IN/OUT Sets For All BBs
initialize IN(X) to 0 for all basic blocks X
change = 1
while (change) do
change = 0
for each basic block in procedure, X, do
old_IN = IN(X)
OUT(X) = Union(IN(Y)) for all successors Y of X
IN(X) = USE(X) + (OUT(X) – DEF(X))
if (old_IN != IN(X)) then
change = 1
endif
endfor
endfor IN = set of variables that are live when the BB is entered
OUT = set of variables that are live when the BB is exited

Compiler Design Page 11


Example IN/OUT Calculation
OUT(X) = Union(IN(Y)) for all successors Y of X
IN(X) = USE(X) + (OUT(X) – DEF(X)) IN = {r2,r4}È({r1,r3,r5}-{r1,r2,r3})
= {r2,r4,r5}
r1 = MEM[r2+0]
r2 = r2 + 1 USE = {r2,r4}
r3 = r1 * r4 DEF = {r1,r2,r3}
OUT = {r1,r5} È{r3}={r1,r3,r5}
IN = {r1,r5}È({r3,r7}-{r1,r3,r7})
= {r1,r5} IN = Æ È({r3,r7}-{r1,r2,r7})
r1 = r1 + 5 r2 = 0 = {r3}
USE = {r1,r5} USE = Æ
r3 = r5 – r1 r7 = 23
DEF = {r1,r3,r7} DEF = {r1,r2,r7}
r7 = r3 * 2 r1 = 4
OUT = {r3,r7} OUT = {r3,r7}

IN = {r3,r7}
r8 = r7 + 5
USE = {r3,r7}
r1 = r3 – r8
DEF = {r1,r3,r8}
r3 = r1 * 2
OUT = Æ

Compiler Design Page 12


Outline
Introduction to Dataflow Analysis
Dataflow Algorithms
Liveness Analysis
Reaching Definition Analysis
DU/UD Chains
Generalized Dataflow Algorithm
Available Definition
Available Expression
Optimizations

Compiler Design Page 13


Reaching Definition Analysis (rdefs)
A definition of a variable x is an operation that assigns,
or may assign, a value to x

A definition d reaches a point p if there is a path from


the point immediately following d to p such that d is
not “killed” along that path

A definition of a variable is killed between 2 points


when there is another definition of that variable along
the path
r1 = r2 + r3 kills previous definitions of r1

Compiler Design Page 14


Reaching Defs Example

defs 1 and 2 reach this point


1: r1 = r2 + r3
2: r6 = r4 – r5

3: r4 = 4 defs 1, 3, 4 reach this point


4: r6 = 8 def 2 is killed by 4

5: r6 = r2 + r3
6: r7 = r4 – r5
defs 1, 3, 5, 6 reach this point
defs 2, 4 are killed by 5

Compiler Design Page 15


Reaching Definition Analysis (rdefs)
Algorithm sketch
Forward dataflow analysis as propagation
occurs from defs downwards
4 sets
GEN = set of definitions generated in the BB
(operations not registers like liveness!!)
KILL = set of definitions killed in the BB
IN = set of definitions reaching the BB entry
OUT = set of definitions reaching the BB exit

Compiler Design Page 16


Compute Rdef GEN/KILL Sets For Each BB
for each basic block in the procedure, X, do
GEN(X) = 0
KILL(X) = 0
for each operation in sequential order in X, op, do
for each destination operand of op, dest, do
G = op
K = {all ops which define dest – op}
GEN(X) = G + (GEN(X) – K)
KILL(X) = K + (KILL(X) – G)
endfor gen = set of definitions created by an operation
endfor kill = set of definitions destroyed by an operation
endfor à Assume each operation only has 1 destination
so just keep track of “ops”.
Compiler Design Page 17
Example Rdef GEN/KILL Calculation
1: r1 = 3
GEN = 1,2,3
2: r2 = r3 KILL = 4,6,7
3: r3 = r4

4: r1 = r1 + 1 GEN = 4,5
5: r7 = r1 * r2 KILL = 1

GEN = 6 GEN = 7
KILL = 2,7 6: r2 = 0 7: r2 = r2 + 1 KILL = 2,6

8: r4 = r2 + r1 GEN = 8
KILL = Æ

9: r9 = r4 + r8 GEN = 9
KILL = Æ

Compiler Design Page 18


Compute Rdef IN/OUT Sets for all BBs
initialize IN(X) = 0 for all basic blocks X
initialize OUT(X) = GEN(X) (or simply 0) for all basic blocks X
change = 1
while (change) do
change = 0
for each basic block in procedure, X, do
old_OUT = OUT(X)
IN(X) = Union(OUT(Y)) for all predecessors Y of X
OUT(X) = GEN(X) + (IN(X) – KILL(X))
if (old_OUT != OUT(X)) then
change = 1
endif
endfor IN = set of definitions reaching the entry of BB
endfor OUT = set of definitions leaving BB

Compiler Design Page 19


Example Rdef GEN/KILL Calculation
IN(X) = Union(OUT(Y)) for all predecessors Y of X
OUT(X) = GEN(X) + (IN(X) – KILL(X))
1: r1 = 3 IN = Æ
2: r2 = r3 GEN = 1,2,3
KILL = 4,6,7
3: r3 = r4 OUT = 1,2,3

IN = 1,2,3 à 1,2,3,4,5,6,7,8
4: r1 = r1 + 1 GEN = 4,5
5: r7 = r1 * r2 KILL = 1

IN = 2,3,4,5 OUT = 2,3,4,5 à 2,3,4,5,6,7,8


à 2,3,4,5,6,7,8 IN = 2,3,4,5
GEN = 6 à 2,3,4,5,6,7,8
KILL = 2,7 6: r2 = 0 7: r2 = r2 + 1 GEN = 7
KILL = 2,6
OUT = 3,4,5,6
IN = 3,4,5,6,7 OUT = 3,4,5,7
à 3,4,5,6,8
à 3,4,5,6,7,8 à 3,4,5,7,8
8: r4 = r2 + r1 GEN = 8
KILL = Æ
OUT = 3,4,5,6,7,8 à 3,4,5,6,7,8
IN = 3,4,5,6,7,8
9: r9 = r4 + r8 GEN = 9
KILL = Æ
OUT = 3,4,5,6,7,8,9

Compiler Design Page 20


Outline
Introduction to Dataflow Analysis
Dataflow Algorithms
Liveness Analysis
Reaching Definition Analysis
DU/UD Chains
Generalized Dataflow Algorithm
Available Definition
Available Expression
Optimizations

Compiler Design Page 21


DU/UD Chains
Convenient way to access/use reaching
defs info
Def-Use chains
Given a def, what are all the possible
consumers of the operand produced
Maybe consumer
Use-Def chains
Given a use, what are all the possible
producers of the operand consumed
Maybe producer

Compiler Design Page 22


Example: DU Chains

(1) x=1 DU={4,9}


(2) y=2 DU={7,9}
(3) if (n>1) DU={}

(4) t1= x*x DU={5}


(5) x = n+t1 DU={9}

(6) if (n>1) DU={}

(7) y = y*n DU={7,9}


(8) n = n-1 DU={6,8}

(9) t2 = x+y DU={10}


(10) return t2 DU={}

Compiler Design Page 23


Outline
Introduction to Dataflow Analysis
Dataflow Algorithms
Liveness Analysis
Reaching Definition Analysis
DU/UD Chains
Generalized Dataflow Algorithm
Available Definition
Available Expression
Optimizations

Compiler Design Page 24


Some Things to Think About
Liveness and reaching defs are basically
the same thing!!!!!!!!!!!!!!!!!!
All dataflow is basically the same with a few
parameters
Meaning of gen/kill (use/def)
Backward / Forward
All paths / some paths (must/may)
– So far, we have looked at may analysis algorithms
– How do you adjust to do must algorithms?

Compiler Design Page 25


Generalizing Dataflow Analysis
Transfer function
How information is changed by “something” (BB)
OUT = GEN + (IN – KILL) forward analysis
IN = GEN + (OUT – KILL) backward analysis
Meet function
How information from multiple paths is combined
IN = Union(OUT(predecessors)) forward analysis
OUT = Union(IN(successors)) backward analysis
Note, this is only for “any” path

Compiler Design Page 26


Generalized Dataflow Algorithm
while (change) {
change = false
for each BB {
apply meet function
apply transfer function
if any changes à change = true
}
}

Compiler Design Page 27


A Taxonomy of Data-Flow Problems

Forward-Flow Backward-Flow
Any in[B] = Èp Î pred(B) out[p] out[B] = Ès Î succ(B) in[s]
out[B] = gen[B] È (in[B] - kill[B]) in[B] = gen[B] È (out[B] - kill[B])
path
All in[B] = Çp Î pred(B) out[p] out[B] = Çs Î succ(B) in[s]
out[B] = gen[B] È (in[B] - kill[B]) in[B] = gen[B] È (out[B] - kill[B])
path

Compiler Design Page 28


What About All Path Problems?
Up to this point
Any path problems (maybe relations)
Definition reaches along some path
Some sequence of branches in which def reaches
Lots of defs of the same variable may reach a point
Use of Union operator in meet function
All-path: Definition guaranteed to reach
Regardless of sequence of branches taken, def reaches
Can always count on this
Only 1 def can be guaranteed to reach
Availability (as opposed to reaching)
Available definitions
Available expressions (could also have reaching expressions, but not
that useful)

Compiler Design Page 29


Reaching vs Available Definitions

1: r1 = r2 + r3 1,2 reach
2: r6 = r4 – r5 1,2 available

1,2 reach 3: r4 = 4
1,2 available 4: r6 = 8

1,3,4 reach
1,3,4 available
5: r6 = r2 + r3
6: r7 = r4 – r5 1,2,3,4 reach
1 available

Compiler Design Page 30


Outline
Introduction to Dataflow Analysis
Dataflow Algorithms
Liveness Analysis
Reaching Definition Analysis
DU/UD Chains
Generalized Dataflow Algorithm
Available Definition
Available Expression
Optimizations

Compiler Design Page 31


Available Definition Analysis (Adefs)
A definition d is available at a point p if along all paths
from d to p, d is not killed
Remember, a definition of a variable is killed between
2 points when there is another definition of that
variable along the path
r1 = r2 + r3 kills previous definitions of r1
Algorithm
Forward dataflow analysis as propagation occurs from defs
downwards
Use the Intersect function as the meet operator to guarantee
the all-path requirement
GEN/KILL/IN/OUT similar to reaching defs
Initialization of IN/OUT is the tricky part

Compiler Design Page 32


Compute Adef GEN/KILL Sets
for each basic block in the procedure, X, do
GEN(X) = 0
KILL(X) = 0
for each operation in sequential order in X, op, do
for each destination operand of op, dest, do
G = op
K = {all ops which define dest – op}
GEN(X) = G + (GEN(X) – K)
KILL(X) = K + (KILL(X) – G)
endfor
endfor
endfor Exactly the same as reaching defs!!!!!!!

Compiler Design Page 33


Compute Adef IN/OUT Sets
U = universal set of all operations in the Procedure
IN(0) = 0
OUT(0) = GEN(0)
for each basic block in procedure, W, (W != 0), do
IN(W) = 0
OUT(W) = U – KILL(W)

change = 1
while (change) do
change = 0
for each basic block in procedure, X, do
old_OUT = OUT(X)
IN(X) = Intersect(OUT(Y)) for all predecessors Y of X
OUT(X) = GEN(X) + (IN(X) – KILL(X))
if (old_OUT != OUT(X)) then
change = 1
endif
endfor
endfor
Compiler Design Page 34
Outline
Introduction to Dataflow Analysis
Dataflow Algorithms
Liveness Analysis
Reaching Definition Analysis
DU/UD Chains
Generalized Dataflow Algorithm
Available Definition
Available Expression
Optimizations

Compiler Design Page 35


Available Expression Analysis (Aexprs)
An expression is a RHS of an operation
r2 = r3 + r4, r3+r4 is an expression
An expression e is available at a point p if along all
paths from e to p, e is not killed
An expression is killed between 2 points when one of its
source operands are redefined
r1 = r2 + r3 kills all expressions involving r1
Algorithm
Forward dataflow analysis
Use the Intersect function as the meet operator to guarantee
the all-path requirement
Looks exactly like adefs, except GEN/KILL/IN/OUT are the
RHS’s of operations rather than the LHS’s

Compiler Design Page 36


Outline
Introduction to Dataflow Analysis
Dataflow Algorithms
Liveness Analysis
Reaching Definition Analysis
DU/UD Chains
Generalized Dataflow Algorithm
Available Definition
Available Expression
Optimizations

Compiler Design Page 37


Optimization – Put Dataflow To Work!
Make the code run faster on the target processor
Anything goes
Look at benchmark kernels, what’s the bottleneck??
Invent your own optis
Classes of optimization
1. Classical (machine independent)
Reducing operation count (redundancy elimination)
Simplifying operations
2. Machine specific
Peephole optimizations
Take advantage of specialized hardware features
3. ILP enhancing
Increasing parallelism
Possibly increase instructions

Compiler Design Page 38


Types of Classical Optimizations
Operation-level – 1 operation in isolation
Constant folding, strength reduction
Dead code elimination (global, but 1 op at a
time)
Local – Pairs of operations in same BB
May or may not use dataflow analysis
Global – Again pairs of operations
But, operations in different BBs
Dataflow analysis necessary here
Loop – Body of a loop

Compiler Design Page 39


Constant Folding
Simplify operation based on values of src operands
Constant propagation creates opportunities for this
All constant operands
Evaluate the op, replace with a move
r1 = 3 * 4 à r1 = 12
Evaluate conditional branch, replace with jump or noop
if (1 < 2) goto BB2 à jump BB2
if (1 > 2) goto BB2 à convert to a noop
Algebraic identities
r1 = r2 + 0, r2 – 0, r2 | 0, r2 ^ 0, r2 << 0, r2 >> 0 à r1 = r2
r1 = 0 * r2, 0 / r2, 0 & r2 à r1 = 0
r1 = r2 * 1, r2 / 1 à r1 = r2

Compiler Design Page 40


Strength Reduction
Replace expensive ops with cheaper ones
Constant propagation creates opportunities for this
Power of 2 constants
Mpy by power of 2: r1 = r2 * 8 à r1 = r2 << 3
Div by power of 2: r1 = r2 / 4 à r1 = r2 >> 2
Rem by power of 2: r1 = r2 REM 16 à r1 = r2 & 15
More exotic
Replace multiply by constant by sequence of shift and
adds/subs
r1 = r2 * 6
– r100 = r2 << 2; r101 = r2 << 1; r1 = r100 + r101
r1 = r2 * 7
– r100 = r2 << 3; r1 = r100 – r2

Compiler Design Page 41


Dead Code Elimination
Remove any operation
who’s result is never
consumed r1 = 3
r2 = 10
Rules
X can be deleted
no stores or branches r4 = r4 + 1
r7 = r1 * r4
DU chain empty or dest not live

r2 = 0 r3 = r3 + 1

r3 = r2 + r1

store (r1, r3)

Compiler Design Page 42


Constant Propagation
Forward propagation of
moves of the form
rx = L (where L is a literal) r1 = 5
r2 = r1 + r3
Maximally propagate

When is it legal? r1 = r1 + r2 r7 = r1 + r4
SRC: Literal is a hard coded
constant, so never a
problem r8 = r1 + 3
DEST: Must be available
Guaranteed to reach r9 = r1 + r11
May reach not good enough

Compiler Design Page 43


Local Constant Propagation
Consider 2 ops, X and Y in a
BB, X is before Y
1: r1 = 5
1. X is a move
2: r2 = 6
2. src1(X) is a literal
3: r3 = 7
3. Y consumes dest(X)
4: r4 = r4 + r1
4. There is no definition of
dest(X) between X and Y
5: r1 = r1 + r2
Defn is locally available! 6: r1 = r1 + 1
5. Be careful if dest(X) is SP, FP 7: r3 = 12
or some other special register – If 8: r8 = r1 - r2
so, no subroutine calls between X 9: r9 = r3 + r5
and Y 10: r3 = r2 + 1
11: r10 = r3 – r1

Compiler Design Page 44


Local Constant Propagation
Consider 2 ops, X and Y in a
BB, X is before Y
1. X is a move
1: r1 = 5
2. src1(X) is a literal
2: r2 = 6
3: r3 = 7
3. Y consumes dest(X)
r1
4: r4 = r4 + 5
4. There is no definition of
dest(X) between X and Y
r1 + r2
5: r1 = 5
Defn is locally available! 6: r1 = r1 + 1
5. Be careful if dest(X) is SP, FP 7: r3 = 12
or some other special register – If 8: r8 = r1 - r2
so, no subroutine calls between X 9: r9 = r3 + r5
and Y 10: r3 = r2 + 1
11: r10 = r3 – r1

Compiler Design Page 45


Local Constant Propagation
Consider 2 ops, X and Y in a
BB, X is before Y
1. X is a move
1: r1 = 5
2. src1(X) is a literal
2: r2 = 6
3: r3 = 7
3. Y consumes dest(X)
r1
4: r4 = r4 + 5
4. There is no definition of
dest(X) between X and Y
r1 + 6
5: r1 = 5 r2
Defn is locally available! 6: r1 = r1 + 1
5. Be careful if dest(X) is SP, FP 7: r3 = 12
or some other special register – If 8: r8 = r1 - 6
r2
so, no subroutine calls between X 9: r9 = r3 + r5
and Y 10: r3 = 6
r2 + 1
11: r10 = r3 – r1

Compiler Design Page 46


Local Constant Propagation
Consider 2 ops, X and Y in a
BB, X is before Y
1. X is a move
1: r1 = 5
2. src1(X) is a literal
2: r2 = 6
3: r3 = 7
3. Y consumes dest(X)
r1
4: r4 = r4 + 5
4. There is no definition of
dest(X) between X and Y
r1 + 6
5: r1 = 5 r2
Defn is locally available! 6: r1 = r1 + 1
5. Be careful if dest(X) is SP, FP 7: r3 = 12
or some other special register – If 8: r8 = r1 - 6
r2
so, no subroutine calls between X r3 + r5
9: r9 = 7
and Y r2 + 1
10: r3 = 6
11: r10 = r3 – r1

Compiler Design Page 47


Global Constant Propagation
Consider 2 ops, X and Y in
different BBs
1. X is a move r1 = 5
r2 = ‘_x’
2. src1(X) is a literal
3. Y consumes dest(X)
4. X is in adef_IN(BB(Y))
r1 = r1 + r2 r7 = r1 – r2
5. dest(X) is not modified
between the top of BB(Y)
and Y r8 = r1 * r2
Rules 4/5 guarantee X is
available
r9 = r1 + r2
6. If dest(X) is SP/FP/..., no
subroutine call between X
and Y Note: checks for subroutine calls whenever SP/FP/etc.
are involved is required for all optis

Compiler Design Page 48


Forward Copy Propagation
Forward propagation of the RHS of moves
X: r1 = r2

Y: r4 = r1 + 1 à r4 = r2 + 1
r1 = r2
Benefits r3 = r4
Reduce chain of dependences
Possibly eliminate the move
Rules (ops X and Y) r2 = 0 r6 = r3 + 1
X is a move
src1(X) is a register
Y consumes dest(X) r5 = r2 + r3
X.dest is an available def at Y
X.src1 is an available expr at Y

Compiler Design Page 49


Forward Copy Propagation
Forward propagation of the RHS of moves
X: r1 = r2

Y: r4 = r1 + 1 à r4 = r2 + 1
r1 = r2
Benefits r3 = r4
Reduce chain of dependences
Possibly eliminate the move
Rules (ops X and Y) r2 = 0 r6 = r4
r3 + 1
X is a move
src1(X) is a register
Y consumes dest(X) r5 = r2 + r4
r3
X.dest is an available def at Y
X.src1 is an available expr at Y

Compiler Design Page 50


Backward Copy Propagation
Backward prop. of the LHS of moves
X: r1 = r2 + r3 à r4 = r2 + r3

r5 = r1 + r6 à r5 = r4 + r6

Y: r4 = r1 à noop
Rules (ops X and Y in same BB)
dest(X) is a register
dest(X) not live out of BB(X)
Y is a move
dest(Y) is a register
Y consumes dest(X)
dest(Y) not consumed in (X…Y)
dest(Y) not defined in (X…Y)
There are no uses of dest(X) after the first redefinition of dest(Y)

Compiler Design Page 51


Local Common Subexpression Elimination (CSE)
Eliminate recomputation of an expr
X: r1 = r2 * r3
à r100 = r1
r1 = r2 + r3 r1 = r2 + r3

r4 = r4 +1 r100 = r1
Y: r4 = r2 * r3 à r4 = r100
r1 = 6 r4 = r4 +1
Benefits r6 = r2 + r3 r1 = 6
Reduce work r2 = r1 -1 r6 = r100
Moves can get copy propagated r6 = r4 + 1 r2 = r1 -1
Rules (ops X and Y) r7 = r2 + r3 r6 = r4 + 1
X and Y have the same opcode r7 = r2 + r3
src(X) = src(Y), for all srcs
for all srcs(X) no defs of srci in [X ... Y)
if X is a load, then there is no store that may write to address(X) between
X and Y

Compiler Design Page 52


Global Common Subexpression Elimination (CSE)

Rules (ops X and Y)


X and Y have the same
opcode
r1 = r2 * r6
src(X) = src(Y), for all srcs r3 = r4 / r7
expr(X) is available at Y
if X is a load, then there is no
store that may write to r2 = r2 + 1 r1 = r3 * 7
address(X) along any path
between X and Y
r5 = r2 * r6
r8 = r4 / r7
r9 = r3 * 7

if op is a load, call it redundant


load elimination rather than CSE

Compiler Design Page 53

You might also like