loop_transforms

advertisement
Advanced Compiler Design and Implementation
Loop Transformations
Chapter 14
Dudu Ronen
Outline
This chapter is about optimizations done for loops. The assumption in this kind of optimizations is that
loops are performed many times. This makes it is worth while to reduce the cost of the code inside the
loop, even on account of the initialization code performed outside the loop. We covered two
optimizations: Strength Reductions and elimination of unnecessary array bounds checking.
In this lesson we cover:

Induction-Variable Optimizations

Strength Reduction

Live Variable Analysis

Elimination of unnecessary array bounds checking
Induction Variable Optimizations
What is an Induction Variable
An induction variable is a variable that changes in a constant way each iteration of the loop. For
example, if we have inside a loop the code x:=x+C where C is a loop invariant, than x is an induction
variable.
Example 1
Induction variables are not always obvious, as in the following example.
do i=1,100
a(i) = 202 – 2*i
enddo
In this case, the value put in a(i) decreases each iteration by 2. This means, that we can rewrite the code
as follows:
t1 = 202
do i=1,100
t1 = t1 - 2
a(i) = t1
enddo
Not that we have replaced the more complex expression “202-2*I” with the simpler “t1-2”.
Example 2
x := init
for ...
a := ...
x := x {a}
y := x z
od
In this example:

z is a loop invariant

x and y are induction variables
Advanced Compiler Design and Implementation
Intermediate Representations
Therefore, we can replace the above code with the following one:
x := init
y := initz
for ...
a :=
x := x {a}
if a z then
y := y
od
 {a}
Finding Induction Variables
The are two types of induction variables:

Basic – Variables that change each iteration by a constant (loop invariant) amount.

Dependant – A variable which depends on another induction variable.
Typically, induction variables are variables that change by a linear function.
Consider the following example:
1. Original code
t1 = 202
do i=1,100
t1 = t1 - 2
a(i) = t1
enddo
2. MIR code
t1  202
i  1
L1: t2  i >100
if t2 goto L2
t1  t1 - 2
t3  addr(a)
t4  t3 - 4
t5  4 * i
t6  t4 + t5
*t6  t1
i  i + 1
goto L1
L2:
2
Advanced Compiler Design and Implementation
Intermediate Representations
3. After Moving Loop Invariants
t1  202
i  1
t3  addr(a)
t4  t3 - 4
L1: t2  i >100
if t2 goto L2
t1  t1 - 2
t5  4 * i
t6  t4 + t5
*t6  t1
i  i + 1
goto L1
L2:
We can see that i and t1 are basic induction variables. t5 and t6 depend on i. We say that in this
example there are two classes of variables: those who depend on the basic variable i (i, t5 and t6) and
those who depend on the basic variable t1 (only t1).
4. Transforming the Induction Variables
In this transformation, we notice that t6 increases by 4 each iteration.
t1  202
i  1
t3  addr(a)
t4  t3 - 4
t6  t4
L1: t2  i >100
if t2 goto L2
t1  t1 - 2
t6  t6 + 4
*t6  t1
i  i + 1
goto L1
L2:
5. Transforming the loop condition
Since i is required only for the loop condition, we use t6 instead of i in the condition:
t1  202
i  1
t3  addr(a)
t4  t3 - 4
t5  4
t6  t4
t7  t3 - 396
L1: t2  t6 > t7
if t2 goto L2
t1  t1 - 2
t6  t6 + 4
*t6  t1
goto L1
L2:
All in all, we cut the 5 instructions we had inside the loop after we moved the loop invariants into 3.
3
Advanced Compiler Design and Implementation
Intermediate Representations
Note: consider the following example:
x1 = x1 + c1
x2 = x2 + c2
y = a1x1 + a2x2
We would like to replace the calculation of y into a calculation that relies on the value of y in the
previous iteration. Obviously, this can be done. However, the algorithm presented here does not handle
this type of situations.
Algorithm to Identify Induction Variables

Identify loops

Identify loop invariants and constants

Identify basic induction variables biv = biv + c

Inductively identify variables j with a unique assignment j = b * biv + c

Split multiple assignments with the same induction variables into different induction
variables. For example, if we have:
x = x + 5
…
x = x + 7
we will replace it with
x1 = x1 + 5
…
x2 = x1 + 7
(If we use SSA, then this would already be done)
Strength Reduction
General
Strength reduction is about changing an expression with a simpler expression. The general idea is to
examine the sequence of differences, instead the sequence of the original values (examples below).
The usual case is to replace “j = b * biv + c” in a loop by “j = j + c1” and appropriate initialization. The
original sequence looks like b, b+c, 2*b+c, 3*b+c,… while the differences sequence is c, c, c, … . This
allows us to replace the calculation of “b * biv + c” inside the loop with the addition of c.
We can generalize this reduction as shown in the following example. The sequence i² has a differences
sequence of 2i+1. This means, that we may replace calculation of i² inside a loop with a calculation of
2i-1. After we do that, the calculation of 2i-1 yields another induction, this time it changes by 2.
We get something like the following code:
1. Original Code
for i=2,n do
t= i²
endfor
2. After one reduction
t1=1
t=1
for i=2,n do
t1=2*i-1
t= t+t1
endfor
4
Advanced Compiler Design and Implementation
Intermediate Representations
2. After the second reduction
t1=1
t=1
for i=2,n do
t1=t1+2
t= t+t1
endfor
Notes:
1.Like many other optimizations, strength reduction is not correct for floating point.
2. Linear reduction is useful for calculating addresses.
Algorithm for Strength Reduction
For every induction variable j = b * biv + c :

Allocate a new temporary variable tj and replace the (single) assignment to j by: j  tj

After assignments biv  biv + c0 insert an assignment tj  tj + b * c0

Put an assignment tj b * biv + c in the preheader

Replace j by tj
If we have nested loops, we do this bottom up.
Elimination of Induction Variables
Note that in the above algorithm, tj has now become an induction variable by itself Luckily, we can
sometimes eliminate some of the induction variable we created during the transformations. We do that
by the following techniques:
1.
Use a “live” algorithm. The the induction variable is not “live”, we can eliminate it. In Pascal,
for example, we cannot use the loop variable after the loop, and it may become “dead”.
2.
If the only thing that keep a loop variable “live” is the condition of the loop, we can replace it
with a linear function (e.g. replace i with tj). Then, we may eliminate the loop variable.
Live Variable Analysis
Live variable analysis is brought here to complete the picture. A variable is live at a program point if it
may be used before set in a path from this point, i.e. the value currently held by the variable may be
still valid in a future reference to the variable.
The following algorithm finds live variables.
Local information
USE - variables that may be used before set in a block
DEF - variables that must be assigned in the block
Iterative solution:
LVout(EXIT) = 
LVout(B) = B ’ Succ(B) LVin(B’)
LVin(B) = USE(B)  (LVout(B)-DEF(B))
5
Advanced Compiler Design and Implementation
Intermediate Representations
Other uses for live variables include:

Register allocation: two variables that are “live” at the same time will use two different
registers.

Software defects 1: uninitialized variables are “live” at the beginning of a procedure.

Software defects 2: unused parameters

Garbage Collection: a variable that is not “live” may be released.
Eliminating Unnecessary Array Bounds Checking
General
Exceed array bounds may cause severe problems. However, checking array bounds at runtime takes a
long time (30% - 500%), and some compilers even allow to disable array bounds checking (e.g. ADA)
using a pragma.
A compiler can eliminate most of the checks, or at least move them out of the loop. Actually, when a
compiler fails to move an array bounds check, it may indicate an “unreasonable” program, and the
compiler may issue a warning.
Example
In the following example “trap 6” means we exceeded array bounds. In this case, we can remove all the
checks from the loop: i is always >= 1, and the test for n > 100 can be done outside the loop.
Pascal code:
var b: array[1…100] of integer;
…
for i := 1 to n do
….
b[i] ….
MIR code:
L1:
...
if
if
t3
t4
t5
t6
t1
goto L1
L2:
1 > i trap 6
i > 100 trap 6
 addr(b)
 t3 - 4
 4 * i
 t4 + t5
 *t6
Algorithm
Assume that we need to show that some expression e inside the loop is within the bounds (lo  e  hi).
lo and hi are assumed to be loop invariants.
1. If e is loop invariant  move the check to preheader block.
2. If e is a basic induction variable  replace the check by checking that lo  init and fin < hi in
the preheader.
This algorithm can be generalized using partial redundancy elimination Kolte & Wolf 1995. We insert
an extra check for the bounds in the preheader. Then, we will eliminate the checks inside the loop using
algorithm based on the partial redundancy algorithm (it requires some modifications, since we wish to
remove the checks and not only avoid recalculation the expression).
Summary
We have seen two optimizations for loops:
6
Advanced Compiler Design and Implementation
1.
Strength reduction
2.
Elimination of array bound checks
Intermediate Representations
References
S. S. Muchnick, Advanced Compiler Design and Implementation, Chapter 14
7
Download