The optimisation of ALICE code Federico Carminati January 19, 2012 1.
Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may...
Transcript of Constant propagation Code Generation and Optimisation Dead ... · code more efficient . But it may...
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.
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”.
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
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
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.
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].
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.
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.
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
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.
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
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