Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may...

12
Code Generation and Optimisation Optimisation = Analysis + Transformation Constant propagation Is the following transformation semantics-preserving? li k, 33 loop: mul j, n, i print j add i, i, k blt i, n, loop halt li k, 33 loop: mul j, n, i print j addi i, i, 33 blt i, n, loop halt (Assume that i and n are inputs to the program.) Dead-code elimination Is the following transformation semantics-preserving? li k, 33 loop: mul j, n, i print j addi i, i, 33 blt i, n, loop halt loop: mul j, n, i print j addi i, i, 33 blt i, n, loop halt (Assume that i and n are inputs to the program.) Enabling optimisations Constant propagation did not remove any instructions, but it enabled dead-code elimination which did remove instructions. Together, the optimisations made the code more efficient. Hence, constant propagation is known as an enabling optimisation.

Transcript of Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may...

Page 1: Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may also change the meaning of the code . Analysis determines when transformations can

Code Generation and Optimisation

Optimisation

=

Analysis + Transformation

Constant propagation

Is the following transformation semantics-preserving?

li k, 33 loop: mul j, n, i print j add i, i, k blt i, n, loop halt

li k, 33 loop: mul j, n, i print j addi i, i, 33 blt i, n, loop halt

(Assume that i and n are inputs to the program.)

Dead-code elimination

Is the following transformation semantics-preserving?

li k, 33 loop: mul j, n, i print j addi i, i, 33 blt i, n, loop halt

loop: mul j, n, i print j addi i, i, 33 blt i, n, loop halt

(Assume that i and n are inputs to the program.)

Enabling optimisations

Constant propagation did not remove any instructions, but it enabled dead-code elimination which did remove instructions.

Together, the optimisations made the code more efficient. Hence, constant propagation is known as an enabling optimisation.

Page 2: Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may also change the meaning of the code . Analysis determines when transformations can

Constant propagation

Is the following transformation semantics-preserving?

li i, 0 loop: mul j, n, i print j add i, i, k blt i, n, loop halt

li i, 0 loop: muli j, n, 0 print j add i, i, k blt i, n, loop halt

(Assume that k and n are inputs to the program.)

Dead-code elimination

Is the following transformation semantics-preserving?

li i, 0 loop: mul j, n, i print j add i, i, k blt i, n, loop halt

loop: mul j, n, i print j add i, i, k blt i, n, loop halt

(Assume that k and n are inputs to the program.)

Lesson

Transformation may make the code more efficient. But it may also change the meaning of the code. Analysis determines when transformations can be applied safely.

Opimisation

=

Analysis + Transformation

Outline

We will explore two analyses:

Liveness analysis

Can deduce when dead-code elimination

is safe. Also used in register allocation (next chapter). Is a “backward analysis”.

Reaching-definitions analysis

Can deduce when constant propagation

is safe. Is a “forward analysis”.

Page 3: Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may also change the meaning of the code . Analysis determines when transformations can

CONTROL-FLOW GRAPHS

Explicit modelling of execution order

Nodes are instructions.

Edges denote possible control-flow.

Control-flow graphs

li k, 33 loop: mul j, n, i print j add i, i, k blt i, n, loop halt

li k, 33

mul j, n, i

add i, i, k

blt i, n, loop

print j

Example code Control-flow graph

L1:

loop:

L2:

L3:

L5: halt

L4:

Draw a control-flow graph for the following code fragment.

Exercise 1

li r, 1 begin: bge o, n, end mul r, r, n addi n, n, -1 jump begin end: print r halt

LIVENESS ANALYSIS

live: of continuing or current interest

Webster’s Dictionary

Page 4: Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may also change the meaning of the code . Analysis determines when transformations can

Intuition

A variable is live at a point in the program if it holds a value that may be needed in future.

li k, 33 loop: mul j, n, i print j add i, i, k blt i, n, loop halt

Is n live here?

Is i live here?

Is j live here?

(Assume that i and n are inputs to the program.)

Definition

A variable v is live on an edge in the CFG if there is a directed path from that edge to a use of v that does not go through any definition of v.

A variable is live-in at a node if it is live on any of the in-edges to that node.

A variable is live-out at a node if it is live on any of the out-edges from that node.

Example 1

Is n live-out at L1? Is i live-in at L2? Is j live-in at L4?

li k, 33

mul j, n, i

add i, i, k

blt i, n, loop

print j

Control-flow graph

L1:

loop:

L2:

L3:

L5: halt

L4:

(Assume that i and n are inputs to the program.)

use and def sets

use :: Instr -> [Reg] use(MOVE(x, y)) = [y] use(OP(x, y, op, z)) = [y, z] use(OPI(x, y, op, i)) = [y] use(PRINT(x)) = [x] use(BRANCH(x, op, y, l)) = [x, y] use(other) = []

Registers read-from by an instruction

def :: Instr -> [Reg] def(LI(x, i)) = [x] def(MOVE(x, y)) = [x] def(OP(x, y, op, z)) = [x] def(OPI(x, y, op, i)) = [x] def(other) = []

Registers assigned-to by an instruction

Page 5: Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may also change the meaning of the code . Analysis determines when transformations can

Exercise 2

Label use def

L1

loop

L2

L3

L4

L5

Complete the table by identifying the use and def sets of each instruction.

li k, 33

mul j, n, i

add i, i, k

blt i, n, loop

print j

Control-flow graph

L1:

loop:

L2:

L3:

L5: halt

L4:

Liveness criteria

Let i(l) be the instruction at the node labelled l in the control-flow graph and let succs(l) be the

node-labels of its successors.

A variable is live-in at node l if it is in use(i(l)).

A variable is live-in at l if it is live-out at l and not in def(i(l)).

A variable is live-out at l if it is live-in at any label in succs(l).

Data-flow equations

s ∈ succs(l) ⋃ in(s) out(l) =

in(l) = use(i(l)) ⋃ (out(l) – def(i(l)))

The liveness criteria are captured precisely by the following equations.

Data-flow equations for liveness analysis

Liveness analysis involves solving the equations to find the two unknowns in(l) and out(l) for all node labels l.

Algorithm

for each l in[l] ⟵ {} out[l] ⟵ {}

initialise

for each l

step

s ∈ succs(l) ⋃ in[s] out[l] ⟵

in[l] use(i(l)) ⋃ (out[l] – def(i(l))) ⟵

The equations can be solved by fixed-point iteration. Initially, let the sets of live variables be empty.

In each iteration, the sets are updated according to the equations.

Page 6: Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may also change the meaning of the code . Analysis determines when transformations can

Algorithm

solve

initialise repeat save step until ∀l. in[l] = in'[l] and out[l] = out'[l]

for each l in'[l] ⟵ in[l] out'[l] ⟵ out[l]

save

The algorithm stops when it reaches a fixed-point. That is, when an iteration fails to modify any set of live variables.

Exercise 3

Apply the algorithm to the flow-graph from Exercise 2. (Answer can be found at end of chapter).

(Note the unusual order of the rows in the table. Later we will see why this order is a good idea.)

Label use def out1 in1 out2 in2

L5

L4 i,n

L3 i,k i

L2 j

loop n,i j

L1 k

Order matters!

The order in which the nodes are visited does not affect the final result of the algorithm.

However, some orders allow a fixed-point to be reached much sooner than other orders.

Backward anaylsis

Since live variables at a node always depend on the live variables at its immediate successors, a good visiting order is backwards along the control-flow arrows.

It is also more efficient to compute the out-set at a node before the in-set because in[l] depends on out[l].

Page 7: Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may also change the meaning of the code . Analysis determines when transformations can

REACHING-DEFINITIONS ANALYSIS

reach: an extent or range especially of knowledge

Webster’s Dictionary

What is a reaching definition?

A node n1 in the CFG that assigns a value to a variable v reaches node n2 if there exists a path leaving n1 and entering n2 that does not pass through an assignment to v.

Exercise 4

Does L1 reach L3? Does loop reach L5? Does L3 reach L1?

li k, 33

mul j, n, i

addi i, i, k

blt i, n, loop

print j

Control-flow graph

L1:

loop:

L2:

L3:

L5: halt

L4:

gen and kill sets

gen :: Label -> [Label] gen(l) = case i(l) of LI(x, i) -> [l] MOVE(x, y) -> [l] OP(x, y, op, z) -> [l] OPI(x, y, op, i) -> [l]

other -> []

Node-labels “generated” by a node

A node labelled l generates l if it defines (assigns to) any variable.

Page 8: Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may also change the meaning of the code . Analysis determines when transformations can

gen and kill sets

kill :: Label -> [Label] kill (l) = case i(l) of LI(x, i) -> defs(x) \\ [l] MOVE(x, y) -> defs(x) \\ [l] OP(x, y, op, z) -> defs(x) \\ [l] OPI(x, y, op, i) -> defs(x) \\ [l]

other -> []

All node-labels “killed” by a node

A node labelled l that defines

register x kills all nodes that define x, except l.

Here, defs(x) denotes the labels of all nodes that define x in the control-flow graph.

Exercise 5

Label gen kill

L1

L2

L3

L4

L5

L6

L7

Give the gen and kill sets of each instruction in the given flow-graph.

li a, 5

li c, 1

add c, c, c

jump L3

blt a, c, L6

Control-flow graph

L1:

L2:

L3:

L4:

L6: mul a, c, a

L5:

L7: li c, 0

Reaching criteria

All node labels in gen(l) reach-out of l.

l1 reaches-out of l2 if it reaches-in to l2 and is not in kill(l2).

l1 reaches-in to l2 if l1 reaches-out of any label in preds(l2).

Let preds(l) be the node-labels of

the predecessors of the node labelled l.

Data-flow equations

p ∈ preds(l) ⋃ out(p) in(l) =

out(l) = gen(l) ⋃ (in(l) – kill(l))

The reaching criteria are captured precisely by the following equations.

Data-flow equations for liveness analysis

Reaching-definitions analysis involves solving the equations to find the two

unknowns in(l) and out(l) for all l.

Page 9: Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may also change the meaning of the code . Analysis determines when transformations can

Algorithm

for each l

step

The equations can be solved by fixed-point iteration. The algorithm is the same as that for liveness analysis, except that we use different data-flow equations.

p ∈ preds(l) ⋃ out[p] in[l] ⟵

out[l] ⟵ gen(l) ⋃ (in[l] – kill(l))

Note that we now compute in before out. Can you see why?

Forward analysis

Since labels reaching a node always depend on the labels reaching its immediate predecessors, a good visiting order is forwards along the control-flow arrows.

It is also more efficient to compute the in-set at a node before the out-set because out[l] depends on in[l].

Exercise 6

Apply the reaching-definitions algorithm to the control-flow graph from Exercise 5. (Show the labels that reach-in and reach-out at each node on each iteration of the algorithm.)

OPTIMISATIONS

Safe application of performance-improving transformations

Page 10: Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may also change the meaning of the code . Analysis determines when transformations can

Constant propagation

Suppose we have two nodes in a CFG:

node l1 containing an instruction

LI(z, i)

node l2 containing an instruction

OP(x, y, op, z) for any op

Answer: when node l1 is the only

definition of z that reaches node l2.

Question: when is it safe to do the

following transformation at node l2?

OP(x, y, op, z) → OPI(x, y, op, i)

Dead-code elimination

Suppose a node labelled l in a CFG

contains an instruction

LI(x, i), or

MOVE(x, y), or

OP(x, y, op, z), or

OPI(x, y, op, z).

Question: when is it safe to delete the node l?

Answer: when x is not live-out at node l.

SUMMARY

What have we learnt?

What have we learnt?

Optimisation =

Analysis + Transformation

To construct control flow graphs.

Algorithms for computing

reaching-definitions (forward),

live variables (backward),

that solve dataflow equations by fixed-point iteration.

Using the results to precisely and safely define the optimisations

constant propagation;

dead-code elimination.

Page 11: Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may also change the meaning of the code . Analysis determines when transformations can

Acknowledgements

Some ideas in this chapter are inspired by:

Tom Stuart: See “Optimising Compilers” course at Cambridge University.

Andrew Appel: See “Modern Compiler Implementation in ML”.

http://www.cl.cam.ac.uk/teaching/2005/OptComp

APPENDIX

Haskell Implementation of Liveness Analysis

CFGs in Haskell (1)

A control-flow graph is a mapping from node-labels to nodes.

A node contains an instruction and the

node-labels of its successors.

Here is an example value of type CFG:

type CFG = [(Label, Node)]

data Node = Node(Instr, [Label])

* ("$1” , Node(LI(“k", 33), *“$2"+)) , (“$2” , Node(LABEL(“loop”), *“loop”+)) , (“loop” , Node(OP(“j”, “n”, Mul, “i”), *“$3”+)) , ("$3" , Node(PRINT(“j”), *“$4”+)) , ("$4" , Node(OP(“i”, “i”, Add, “k”), *“$5”+)) , ("$5" , Node(BRANCH(“i”, “n”, *“loop”, “$6”+)) , ("$6" , Node(HALT, [])) ]

CFGs in Haskell (2)

We can compute a control-flow graph for any MIPS2 code sequence, given a label for the root node.

cfg :: ([Instr], Label) -> CFG cfg([], root) = [] cfg(i:is, root) = (root, Node(i, succs)) : cfg(is, next) where next = case i of LABEL(l) -> l other -> fresh() succs = case i of BRANCH(x, op, y, l) -> [next, l] JUMP(l) -> [l] HALT -> [] other -> [next]

Control-flow graph construction

Page 12: Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may also change the meaning of the code . Analysis determines when transformations can

Liveness in Haskell (1)

step :: (CFG, Liveness) -> Liveness step([], live) = live step((l, Node(i, succs)):g, live) = step(g, live') where liveOut = bigUnion([live!s | s <- succs]) liveIn = union(use(i), liveOut \\ def(i)) live' = insert(live, l, liveIn)

One iteration of the analysis

Each node label in a CFG is mapped to a list of registers live-in to that node.

type Liveness = Map Label [Reg]

The step function applies one iteration of the data-flow equations.

Liveness in Haskell (2)

The solve function iterates until a fixed-point is reached, computing the live-in variables at every node in the given control-flow graph.

solve :: (CFG, Liveness) -> Liveness solve(g, live) | live == live' = live | otherwise = solve(g, live') where live' = step(g, live)

Fixed-point iteration

liveness :: CFG -> Liveness liveness(g) = solve(g, init) where init = [(l, []) | (l, node) <- g]

Implementation of liveness analysis

Answer to Exercise 3

Here are the live-in and live-out variables for each node in Example 1.

The third iteration leads to the discovery of a fixed-point.

Label use def out1 in1 out2 in2

L5

L4 i,n i,n i,k,n i,k,n

L3 i,k i i,n i,k,n i,k,n i,k,n

L2 j i,k,n j,i,n,k i,k,n j,i,n,k

loop n,i j j,i,n,k n,i,k j,i,n,k n,i,k

L1 k n,i,k n,i n,i,k n,i