Top Banner
Static Program Analysis Seidl/Wilhelm/Hack: Compiler Design – Analysis and Transformation, Springer Verlag, 2012 1
220

Static Program Analysis - Compiler Design Lab, Saarland ...

Apr 07, 2022

Download

Documents

dariahiddleston
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Static Program Analysis - Compiler Design Lab, Saarland ...

Static Program Analysis

Seidl/Wilhelm/Hack: Compiler Design – Analysis and

Transformation, Springer Verlag, 2012

1

Page 2: Static Program Analysis - Compiler Design Lab, Saarland ...

A Short History of Static Program Analysis

• Early high-level programming languages were implemented on very

small and very slow machines.

• Compilers needed to generate executables that were extremely

efficient in space and time.

• Compiler writers invented efficiency-increasing program

transformations, wrongly called optimizing transformations.

• Transformations must not change the semantics of programs.

• Enabling conditions guaranteed semantics preservation.

• Enabling conditions were checked by static analysis of programs.

2

Page 3: Static Program Analysis - Compiler Design Lab, Saarland ...

Theoretical Foundations of Static Program Analysis

• Theoretical foundations for the solution of recursive equations:

Kleene (1930s), Tarski (1955)

• Gary Kildall (1972) clarified the lattice-theoretic foundation of

data-flow analysis.

• Patrick Cousot (1974) established the relation to the

programming-language semantics.

3

Page 4: Static Program Analysis - Compiler Design Lab, Saarland ...

Static Program Analysis as a Verification Method

• Automatic method to derive invariants about program behavior,

answers questions about program behavior:

– will index always be within bounds at program point p?

– will memory access at p always hit the cache?

• answers of sound static analysis are correct, but approximate:

don’t know is a valid answer!

• analyses proved correct wrt. language semantics,

4

Page 5: Static Program Analysis - Compiler Design Lab, Saarland ...

Proposed Lectures Content:

1. Introductory example: rules-of-sign analysis

2. theoretical foundations: lattices

3. an operational semantics of the language

4. another example: constant propagation

5. relating the semantics to the analysis—correctness proofs

6. some further static analyses in compilers: Elimination of superfluous

computations

→ available expressions

→ live variables

→ array-bounds checks

5

Page 6: Static Program Analysis - Compiler Design Lab, Saarland ...

1 Introduction

... in this course and in the Seidl/Wilhelm/Hack book:

a simple imperative programming language with:

• variables // registers

• R = e; // assignments

• R = M [e]; // loads

• M [e1] = e2; // stores

• if (e) s1 else s2 // conditional branching

• goto L; // no loops

Intermediate language into which (almost) everything can be compiled.

However, no procedures. So, only intra-procedural analyses!

6

Page 7: Static Program Analysis - Compiler Design Lab, Saarland ...

2 Example: Rules-of-Sign Analysis

Starting Point: Questions about a program, mostly at a particular program

point:

• May variable x have value 0 when program execution reaches this

program point? −→ Attempt to exclude division by 0.

• May x have a negative value? −→ Attempt to exclude sqrt of a

negative number.

Solution: Determine at each program point the sign of the values of all

variables of numeric type.

Determines a sound, but maybe approximate answer.

7

Page 8: Static Program Analysis - Compiler Design Lab, Saarland ...

Example program represented as control-flow graph

1: x = 0;

2: y = 1;

3: while (y > 0) do

4: y = y + x;

5: x = x + (-1);

1

2

4 3

y = 1

0

x = 0

y = y+x

5

x = x+(-1)

true(y>0) false(y>0)

8

Page 9: Static Program Analysis - Compiler Design Lab, Saarland ...

All the ingredients:

• a set of information elements, each a set of possible signs,

• a partial order, “⊑”, on these elements, specifying the ”relative

strength” of two information elements,

• these together form the abstract domain, a lattice,

• functions describing how signs of variables change by the execution

of a statement, abstract edge effects,

• these need an abstract arithmetic, an arithmetic on signs.

9

Page 10: Static Program Analysis - Compiler Design Lab, Saarland ...

We construct the abstract domain for single variables starting with the

lattice Signs = 2{−,0,+} with the relation “⊑” =“⊆”.

{ }

{+}

{0,+}{-,0}

{-}

{-,0,+}

{-,+}

{0}

10

Page 11: Static Program Analysis - Compiler Design Lab, Saarland ...

The analysis should ”bind” program variables to elements in Signs .

So, the abstract domain is D = (Vars → Signs)⊥, a Sign-environment.

⊥ ∈ D is the function mapping all arguments to {}.

The partial order on D is D1 ⊑ D2 iff

D1 = ⊥ or

D1 x ⊇ D2 x (x ∈ Vars)

Intuition?

D1 is at least as precise as D2 since D2 admits at least as many signs as

D1

11

Page 12: Static Program Analysis - Compiler Design Lab, Saarland ...

The analysis should ”bind” program variables to elements in Signs .

So, the abstract domain is D = (Vars → Signs)⊥. a Sign-environment.

⊥ ∈ D is the function mapping all arguments to {}.

The partial order on D is D1 ⊑ D2 iff

D1 = ⊥ or

D1 x ⊇ D2 x (x ∈ Vars)

Intuition?

D1 is at least as precise as D2 since D2 admits at least as many signs as

D1

12

Page 13: Static Program Analysis - Compiler Design Lab, Saarland ...

How did we analyze the program?

1

2

4 3

y = 1

0

x = 0

y = y+x

5

x = x+(-1)

true(y>0) false(y>0)

In particular, how did we walk the

lattice for y at program point 5?

{ }

{+}

{0,+}{-,0}

{-}

{-,0,+}

{-,+}

{0}

13

Page 14: Static Program Analysis - Compiler Design Lab, Saarland ...

How is a solution found?

Iterating until a fixed-point is reached

1

2

4 3

y = 1

0

x = 0

y = y+x

5

x = x+(-1)

true(y>0) false(y>0)

0 1 2 3 4 5

x y x y x y x y x y x y

14

Page 15: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea:

• We want to determine the sign of the values of expressions.

15

Page 16: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea:

• We want to determine the sign of the values of expressions.

• For some sub-expressions, the analysis may yield {+,−, 0},

which means, it couldn’t find out.

16

Page 17: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea:

• We want to determine the signs of the values of expressions.

• For some sub-expressions, the analysis may yield {+,−, 0},

which means, it couldn’t find out.

• We replace the concrete operators ✷ working on values by

abstract operators ✷♯ working on signs:

17

Page 18: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea:

• We want to determine the signs of the values of expressions.

• For some sub-expressions, the analysis may yield {+,−, 0},

which means, it couldn’t find out.

• We replace the concrete operators ✷ working on values by

abstract operators ✷♯ working on signs:

• The abstract operators allow to define an abstract evaluation of

expressions:

[[e]]♯ : (Vars → Signs) → Signs

18

Page 19: Static Program Analysis - Compiler Design Lab, Saarland ...

Determining the sign of expressions in a Sign-environment is defined by

the function [[ ]] : Exp× SignEnv → V al

[[c]]♯ D =

{+} if c > 0

{−} if c < 0

{0} if c = 0

[[v]]♯ = D(v)

[[e1 ✷ e2]]♯ D = [[e1]]

♯ D✷♯ [[e2]]

♯ D

[[✷e]]♯ D = ✷♯[[e]]♯ D

A remark about the notation:

[[ ]] is given in a ”distributed” form; its first argument appears between the

brackets, the second follows the brackets.

19

Page 20: Static Program Analysis - Compiler Design Lab, Saarland ...

Abstract operators working on signs (Addition)

+# {0} {+} {-} {-, 0} {-, +} {0, +} {-, 0, +}

{0} {0} {+}

{+}

{-}

{-, 0}

{-, +}

{0, +}

{-, 0, +} {-, 0, +}

20

Page 21: Static Program Analysis - Compiler Design Lab, Saarland ...

Abstract operators working on signs (Multiplication)

×# {0} {+} {−} · · ·

{0} {0} {0} {0}

{+} {0} {+} {−}

{−} {0} {−} {+}

{−, 0} {0} {−, 0} {0, +}

{−, +} {0} {−, +} {−, +}

{0, +} {0} {0, +} {−, 0}

{−, 0, +} {0} {−, 0, +} {−, 0, +}

Abstract operators working on signs (unary minus)

−# {0} {+} {-} {-, 0} {-, +} {0, +} {-, 0, +}

{0} {-} {+} {+, 0} {-, +} {0, -} {-, 0, +}

21

Page 22: Static Program Analysis - Compiler Design Lab, Saarland ...

Working an example: D = {x 7→ {+}, y 7→ {+}}

[[x+ 7]]♯ D = [[x]]♯ D +♯ [[7]]♯ D

= {+} +♯ {+}

= {+}

[[x+ (−y)]]♯ D = {+} +♯ (−♯[[y]]♯ D )

= {+} +♯ (−♯{+})

= {+} +♯ {−}

= {+,−, 0}

22

Page 23: Static Program Analysis - Compiler Design Lab, Saarland ...

[[lab]]♯ is the abstract edge effects associated with edge k.

It depends only on the label lab:

[[;]]♯ D = D

[[true (e)]]♯ D = D

[[false (e)]]♯ D = D

[[x = e;]]♯ D = D ⊕ {x 7→ [[e]]♯ D}

[[x = M [e];]]♯ D = D ⊕ {x 7→ {+,−, 0}}

[[M [e1] = e2;]]♯ D = D

... whenever D 6= ⊥

These edge effects can be composed to the effect of a path π = k1 . . . kr:

[[π]]♯ = [[kr]]♯ ◦ . . . ◦ [[k1]]

23

Page 24: Static Program Analysis - Compiler Design Lab, Saarland ...

Consider a program node v:

→ For every path π from program entry start to v the analysis should

determine for each program variable x the set of all signs that the

values of x may have at v as a result of executing π.

→ Initially at program start, no information about signs is available.

→ The analysis computes a superset of the set of signs as safe

information.

==⇒ For each node v, we need the set:

S[v] =⋃

{[[π]]♯⊤ | π : start →∗ v}

where ⊤ is the function bindig all variables to {−, 0,+}.

This function describes that we don’t know the sign of any variable at

program entry.

24

Page 25: Static Program Analysis - Compiler Design Lab, Saarland ...

Question:

How do we compute S[u] for every program point u?

Idea:

Collect all constraints on the values of S[u] into a system of constraints:

S[start ] ⊇ ⊥

S[v] ⊇ [[k]]♯ (S[u]) k = (u, _, v) edge

25

Page 26: Static Program Analysis - Compiler Design Lab, Saarland ...

Question:

How can we compute S[u] for every program point u?

Idea:

Collect all constraints on the values of S[u] into a system of constraints:

S[start ] ⊇ ⊤

S[v] ⊇ [[k]]♯ (S[u]) k = (u, _, v) edge

Why ⊇?

26

Page 27: Static Program Analysis - Compiler Design Lab, Saarland ...

Wanted:

• a least solution (why least?)

• an algorithm that computes this solution

Example:

27

Page 28: Static Program Analysis - Compiler Design Lab, Saarland ...

1

2

4 3

y = 1

0

x = 0

y = y+x

5

x = x+(-1)

true(y>0) false(y>0)

S[0] ⊇ ⊤

S[1] ⊇ S[0]⊕ {x 7→ {0}}

S[2] ⊇ S[1]⊕ {y 7→ {+}}

S[2] ⊇ S[5]⊕ {x 7→ [[x+ (−1)]]♯ S[5]}

S[3] ⊇ S[2]

S[4] ⊇ S[2]

S[5] ⊇ S[4]⊕ {y 7→ [[y + x]]♯ S[4]}

28

Page 29: Static Program Analysis - Compiler Design Lab, Saarland ...

3 An Operational Semantics

Programs are represented as control-flow graphs.

Example:

29

Page 30: Static Program Analysis - Compiler Design Lab, Saarland ...

void swap (int i, int j) {

int t;

if (a[i] > a[j]) {

t = a[j];

a[j] = a[i];

a[i] = t;

}

}

start

stop

A1 = A0 + 1 ∗ i;

R1 = M [A1];

A2 = A0 + 1 ∗ j;

R2 = M [A2];

A3 = A0 + 1 ∗ j;

Pos (R1 > R2)Neg (R1 > R2)

30

Page 31: Static Program Analysis - Compiler Design Lab, Saarland ...

Thereby, represent:

vertex program point

start program start

stop program exit

edge labeled with a statement or a condition

Edge labels:

Test : Pos (e) or Neg (e)

Assignment : R = e;

Load : R = M [e];

Store : M [e1] = e2;

Nop : ;

31

Page 32: Static Program Analysis - Compiler Design Lab, Saarland ...

Thereby, represent:

vertex program point

start program start

stop program exit

edge step of computation

Edge Labelings:

Test : Pos (e) or Neg (e) (better true(e) or false(e))

Assignment : R = e;

Load : R = M [e];

Store : M [e1] = e2;

Nop : ;

32

Page 33: Static Program Analysis - Compiler Design Lab, Saarland ...

Execution of a path is a computation.

A computation transforms a state s = (ρ, µ) where:

ρ : Vars → int values of variables (contents of symbolic registers)

µ : N → int contents of memory

Every edge k = (u, lab, v) defines a partial transformation

[[k]] = [[lab]]

of the state:

33

Page 34: Static Program Analysis - Compiler Design Lab, Saarland ...

[[; ]] (ρ, µ) = (ρ, µ)

[[true (e)]] (ρ, µ) = (ρ, µ) if [[e]] ρ 6= 0

[[false (e)]] (ρ, µ) = (ρ, µ) if [[e]] ρ = 0

// [[e]] : evaluation of the expression e, z.B.

// [[x+ y]] {x 7→ 7, y 7→ −1} = 6

// [[!(x == 4)]] {x 7→ 5} = 1

[[R = e; ]] (ρ, µ) = ( ρ⊕ {R 7→ [[e]] ρ} , mu)

// where “⊕” makes a function map a given argument to a given value

34

Page 35: Static Program Analysis - Compiler Design Lab, Saarland ...

[[; ]] (ρ, µ) = (ρ, µ)

[[true (e)]] (ρ, µ) = (ρ, µ) if [[e]] ρ 6= 0

[[false (e)]] (ρ, µ) = (ρ, µ) if [[e]] ρ = 0

// [[e]] : evaluation of the expression e, e.g.

// [[x+ y]] {x 7→ 7, y 7→ −1} = 6

// [[!(x == 4)]] {x 7→ 5} = 1

[[R = e; ]] (ρ, µ) = ( ρ⊕ {R 7→ [[e]] ρ} , µ)

// where “⊕” makes a function map a given argument to a given value

35

Page 36: Static Program Analysis - Compiler Design Lab, Saarland ...

[[; ]] (ρ, µ) = (ρ, µ)

[[true (e)]] (ρ, µ) = (ρ, µ) if [[e]] ρ 6= 0

[[false (e)]] (ρ, µ) = (ρ, µ) if [[e]] ρ = 0

// [[e]] : evaluation of the expression e, e.g.

// [[x+ y]] {x 7→ 7, y 7→ −1} = 6

// [[!(x == 4)]] {x 7→ 5} = 1

[[R = e; ]] (ρ, µ) = ( ρ⊕ {R 7→ [[e]] ρ} , µ)

// where “⊕” modifies a mapping at a given argument

36

Page 37: Static Program Analysis - Compiler Design Lab, Saarland ...

[[R = M [e]; ]] (ρ, µ) = ( ρ⊕ {R 7→ µ([[e]] ρ))} , µ)

[[M [e1] = e2; ]] (ρ, µ) = (ρ, µ⊕ {[[e1]] ρ 7→ [[e2]] ρ} )

Example:

[[x = x+ 1; ]] ({x 7→ 5}, µ) = (ρ, µ) where

ρ = {x 7→ 5} ⊕ {x 7→ [[x+ 1]] {x 7→ 5}}

= {x 7→ 5} ⊕ {x 7→ 6}

= {x 7→ 6}

A path π = k1k2 . . . km defines a computation in the state s if

s ∈ def ([[km]] ◦ . . . ◦ [[k1]])

The result of the computation is [[π]] s = ([[km]] ◦ . . . ◦ [[k1]]) s

37

Page 38: Static Program Analysis - Compiler Design Lab, Saarland ...

The approach:

A static analysis needs to collect correct and hopefully precise

information about a program in a terminating computation.

Concepts:

• partial orders relate information for their contents/quality/precision,

• least upper bounds combine information in the best possible way,

• monotonic functions preserve the order, prevent loss of collected

information, prevent oscillation.

38

Page 39: Static Program Analysis - Compiler Design Lab, Saarland ...

4 Complete Lattices

A set D together with a relation ⊑ ⊆ D× D is a partial order if for

all a, b, c ∈ D,

a ⊑ a reflexivity

a ⊑ b ∧ b ⊑ a =⇒ a = b anti−symmetry

a ⊑ b ∧ b ⊑ c =⇒ a ⊑ c transitivity

Intuition: ⊑ represents precision.

By convention: a ⊑ b means a is at least as precise as b.

39

Page 40: Static Program Analysis - Compiler Design Lab, Saarland ...

Examples:

1. D = 2{a,b,c} with the relation “⊆” :

a, b, c

a, b a, c b, c

a b c

40

Page 41: Static Program Analysis - Compiler Design Lab, Saarland ...

Examples:

1. The rules-of-sign analysis uses the following lattice D = 2{−,0,+}

with the relation “⊆” :

{ }

{+}

{0,+}{-,0}

{-}

{-,0,+}

{-,+}

{0}

41

Page 42: Static Program Analysis - Compiler Design Lab, Saarland ...

2. Z with the relation “=” :

210-1-2

2. Z with the relation “≤” :

0-1

12

3. Z⊥ = Z ∪ {⊥} with the ordering:

210-1-2

42

Page 43: Static Program Analysis - Compiler Design Lab, Saarland ...

d ∈ D is called upper bound for X ⊆ D if

x ⊑ d for all x ∈ X

d is called least upper bound (lub) if

1. d is an upper bound and

2. d ⊑ y for every upper bound y of X .

The least upper bound is the youngest common ancestor in the pictorial

representation of lattices.

It is the best combined information for X .

Caveat:

• has no upper bound!

• has the upper bounds

43

Page 44: Static Program Analysis - Compiler Design Lab, Saarland ...

d ∈ D is called upper bound for X ⊆ D if

x ⊑ d for all x ∈ X

d is called least upper bound (lub) if

1. d is an upper bound and

2. d ⊑ y for every upper bound y of X .

The least upper bound is the youngest common ancestor in the pictorial

representation of lattices.

It is the best combined information for X .

Caveat:

• has no upper bound!

• has the upper bounds

44

Page 45: Static Program Analysis - Compiler Design Lab, Saarland ...

d ∈ D is called upper bound for X ⊆ D if

x ⊑ d for all x ∈ X

d is called least upper bound (lub) if

1. d is an upper bound and

2. d ⊑ y for every upper bound y of X .

The least upper bound is the youngest common ancestor in the pictorial

representation of lattices.

Intuition: It is the best combined information for X .

Caveat:

• {0, 2, 4, . . .} ⊆ Z has no upper bound!

• {0, 2, 4} ⊆ Z has the upper bounds 4, 5, 6, . . .

45

Page 46: Static Program Analysis - Compiler Design Lab, Saarland ...

A partially ordered set D is a complete lattice (cl) if every subset

X ⊆ D has a least upper bound⊔

X ∈ D.

Note:

Every complete lattice has

→ a least element ⊥ =⊔

∅ ∈ D;

→ a greatest element ⊤ =⊔

D ∈ D.

46

Page 47: Static Program Analysis - Compiler Design Lab, Saarland ...

Examples:

1. D = 2{−,0,+} with ⊑ is a complete lattice

2. D = Z with “≤” is not a complete lattice.

3. D = Z⊥ is also not a complete lattice

4. With an extra element ⊤, we obtain the flat lattice

Z⊤⊥ = Z ∪ {⊥,⊤} :

210-1-2

47

Page 48: Static Program Analysis - Compiler Design Lab, Saarland ...

Theorem:

If D is a complete lattice, then every subset X ⊆ D has a greatest

lower bound⊔

X .

Proof:

48

Page 49: Static Program Analysis - Compiler Design Lab, Saarland ...

Back to the system of constraints for Rules-of-Signs Analysis!

S[start ] ⊒ ⊤

S[v] ⊒ [[k]]♯ (S[u]) k = (u, _, v) edge

Combine all constraints for a variable v by least-upper-bound operator⊔

:

S[v] ⊒⊔

{[[k]]♯ (S[u]) | k = (u, _, v) edge}

Our generic form of the systems of constraints:

xi ⊒ fi(x1, . . . , xn) (∗)

Relation to the running example:

xi unknown here: S[u]

D values here: Signs

⊑ ⊆ D × D ordering relation here: ⊆

fi: Dn → D constraint here: ...

49

Page 50: Static Program Analysis - Compiler Design Lab, Saarland ...

A mapping f : D1 → D2 is called monotonic (order preserving)

if f(a) ⊑ f(b) for all a ⊑ b.

50

Page 51: Static Program Analysis - Compiler Design Lab, Saarland ...

A mapping f : D1 → D2 is called monotonic (order preserving)

if f(a) ⊑ f(b) for all a ⊑ b.

Examples:

(1) D1 = D2 = 2U for a set U and f x = (x ∩ a) ∪ b.

Obviously, every such f is monotonic

51

Page 52: Static Program Analysis - Compiler Design Lab, Saarland ...

A mapping f : D1 → D2 is called monotonic (order preserving)

if f(a) ⊑ f(b) for all a ⊑ b.

Examples:

(1) D1 = D2 = 2U for a set U and f x = (x ∩ a) ∪ b.

Obviously, every such f is monotonic

(2) D1 = D2 = Z (with the ordering “≤”). Then:

• incx = x+ 1 is monotonic.

• decx = x− 1 is monotonic.

textbullet

52

Page 53: Static Program Analysis - Compiler Design Lab, Saarland ...

A mapping f : D1 → D2 is called monotonic (order preserving)

if f(a) ⊑ f(b) for all a ⊑ b.

Examples:

(1) D1 = D2 = 2U for a set U and f x = (x ∩ a) ∪ b.

Obviously, every such f is monotonic

(2) D1 = D2 = Z (with the ordering “≤”). Then:

• incx = x+ 1 is monotonic.

• decx = x− 1 is monotonic.

• inv x = −x is not monotonic

53

Page 54: Static Program Analysis - Compiler Design Lab, Saarland ...

Theorem:

If f1 : D1 → D2 and f2 : D2 → D3 are monotonic, then also

f2 ◦ f1 : D1 → D3

Theorem:

If D is a complete lattice, then the set [S → D] of functions

f : S → D is also a complete lattice where

f ⊑ g iff f x ⊑ g x for all x ∈ D1

54

Page 55: Static Program Analysis - Compiler Design Lab, Saarland ...

Theorem:

If f1 : D1 → D2 and f2 : D2 → D3 are monotonic, then also

f2 ◦ f1 : D1 → D3

Theorem:

If D is a complete lattice, then the set [S → D] of functions

f : S → D is also a complete lattice where

f ⊑ g iff f x ⊑ g x for all x ∈ D1

In particular for F ⊆ [S → D2],⊔

F = f with f x =⊔

{g x | g ∈ F}

55

Page 56: Static Program Analysis - Compiler Design Lab, Saarland ...

Wanted: least solution for:

xi ⊒ fi(x1, . . . , xn), i = 1, . . . , n (∗)

where all fi : Dn → D are monotonic.

56

Page 57: Static Program Analysis - Compiler Design Lab, Saarland ...

Wanted: least solution for:

xi ⊒ fi(x1, . . . , xn), i = 1, . . . , n (∗)

where all fi : Dn → D are monotonic.

Idea:

• Consider F : Dn → D

n where

F (x1, . . . , xn) = (y1, . . . , yn) with yi = fi(x1, . . . , xn).

57

Page 58: Static Program Analysis - Compiler Design Lab, Saarland ...

Wanted: least solution for:

xi ⊒ fi(x1, . . . , xn), i = 1, . . . , n (∗)

where all fi : Dn → D are monotonic.

Idea:

• Consider F : Dn → D

n where

F (x1, . . . , xn) = (y1, . . . , yn) with yi = fi(x1, . . . , xn).

• If all fi are monotonic, then also F

58

Page 59: Static Program Analysis - Compiler Design Lab, Saarland ...

Wanted: least solution for

xi ⊒ fi(x1, . . . , xn), i = 1, . . . , n (∗)

where all fi : Dn → D are monotonic.

Idea:

• Consider F : Dn → D

n where

F (x1, . . . , xn) = (y1, . . . , yn) with yi = fi(x1, . . . , xn).

• If all fi are monotonic, then also F

• We successively approximate a solution from below. We construct:

⊥, F ⊥, F 2 ⊥, F 3 ⊥, . . .

Intuition: This iteration eliminates unjustified assumptions.

Hope: We eventually reach a solution!

59

Page 60: Static Program Analysis - Compiler Design Lab, Saarland ...

Theorem

• ⊥, F ⊥, F 2 ⊥, . . . form an ascending chain :

⊥ ⊑ F ⊥ ⊑ F 2 ⊥ ⊑ . . .

• If F k ⊥ = F k+1⊥ , F k is the least solution.

• If all ascending chains are finite, such a k always exists.

60

Page 61: Static Program Analysis - Compiler Design Lab, Saarland ...

Proof

The first claim follows by induction:

Foundation: F 0 ⊥ = ⊥ ⊑ F 1 ⊥

Step: Assume F i−1⊥ ⊑ F i ⊥ . Then

F i ⊥ = F (F i−1⊥) ⊑ F (F i ⊥) = F i+1 ⊥

since F monotonic

61

Page 62: Static Program Analysis - Compiler Design Lab, Saarland ...

Step: Assume F i−1⊥ ⊑ F i ⊥ . Then

F i ⊥ = F (F i−1⊥) ⊑ F (F i ⊥) = F i+1 ⊥

since F monotonic

Conclusion:

If D is finite, a solution can be found that is definitely the least

solution.

Question: What, if D is not finite?

62

Page 63: Static Program Analysis - Compiler Design Lab, Saarland ...

Theorem Knaster – Tarski

Assume D is a complete lattice. Then every monotonic function

f : D → D has a least fixed point d0 ∈ D.

Remark:

The least fixed point d0 is in P and a lower bound

==⇒ d0 is the least value x

Application:

Assume xi ⊒ fi(x1, . . . , xn), i = 1, . . . , n (∗)

is a system of constraints where all fi : Dn → D are monotonic.

==⇒ least solution of (∗) == least fixed point of F

63

Page 64: Static Program Analysis - Compiler Design Lab, Saarland ...

Conclusion:

Systems of inequalities can be solved through fixed-point iteration, i.e.,

by repeated evaluation of right-hand sides

64

Page 65: Static Program Analysis - Compiler Design Lab, Saarland ...

Caveat: Naive fixed-point iteration is rather inefficient

Example:

1

2

4 3

y = 1

0

x = 0

y = y+x

5

x = x+(-1)

true(y>0) false(y>0)

0 1 2 3 4 5

x y x y x y x y x y x y

65

Page 66: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea: Round Robin Iteration

Instead of accessing the values of the last iteration, always use the current

values of unknowns

Example:

66

Page 67: Static Program Analysis - Compiler Design Lab, Saarland ...

1

2

4 3

y = 1

0

x = 0

y = y+x

5

x = x+(-1)

true(y>0) false(y>0)

0 1 2 3 4 5

x y x y x y x y x y x y

67

Page 68: Static Program Analysis - Compiler Design Lab, Saarland ...

The code for Round Robin Iteration in Java looks as follows:

for (i = 1; i ≤ n; i++) xi = ⊥;

do {

finished = true;

for (i = 1; i ≤ n; i++) {

new = fi(x1, . . . , xn);

if (!(xi ⊒ new)) {

finished = false;

xi = xi ⊔ new ;

}

}

} while (!finished);

68

Page 69: Static Program Analysis - Compiler Design Lab, Saarland ...

What we have learned:

• The information derived by static program analysis is partially

ordered in a complete lattice.

• the partial order represents information content/precision of the lattice

elements.

• least upper-bound combines information in the best possible way.

• Monotone functions prevent loss of information.

69

Page 70: Static Program Analysis - Compiler Design Lab, Saarland ...

For a complete lattice D, consider systems:

I[start ] ⊒ d0

I[v] ⊒ [[k]]♯ (I[u]) k = (u, _, v) edge

where d0 ∈ D and all [[k]]♯ : D → D are monotonic ...

Wanted: MOP (Merge Over all Paths)

I∗[v] =⊔

{[[π]]♯ d0 | π : start →∗ v}

Theorem Kam, Ullman 1975

Assume I is a solution of the constraint system. Then:

I[v] ⊒ I∗[v] for every v

In particular: I[v] ⊒ [[π]]♯ d0 for every π : start →∗ v

70

Page 71: Static Program Analysis - Compiler Design Lab, Saarland ...

Disappointment: Are solutions of the constraint system just upper

bounds?

Answer: In general: yes

Notable exception, if all functions [[k]]♯ are distributive.

The function f : D1 → D2 is called distributive, if

f (⊔

X) =⊔

{f x | x ∈ X} for all ∅ 6= X ⊆ D;

Remark: If f : D1 → D2 is distributive, then it is also monotonic

Theorem Kildall 1972

Assume all v are reachable from start.

Then: If all effects of edges [[k]]♯ are distributive, I∗[v] = I[v] holds for

all v.

Question: Are the edge effects of the Rules-of-Sign analysis distributive?

71

Page 72: Static Program Analysis - Compiler Design Lab, Saarland ...

5 Constant Propagation

Goal: Execute as much of the code at compile-time as possible!

Example:

x = 7;

if (x > 0)

M [A] = B;

2

1

3

4

5

x = 7;

Pos (x > 0)

M [A] = B;

Neg (x > 0)

;

72

Page 73: Static Program Analysis - Compiler Design Lab, Saarland ...

Obviously, x has always the value 7

Thus, the memory access is always executed

Goal:

2

1

3

4

5

x = 7;

Pos (x > 0)

M [A] = B;

Neg (x > 0)

;

73

Page 74: Static Program Analysis - Compiler Design Lab, Saarland ...

Obviously, x has always the value 7

Thus, the memory access is always executed

Goal:

2

1

3

4

5

2

1

3

4

5

;

M [A] = B;

;

;x = 7;

Pos (x > 0)

M [A] = B;

Neg (x > 0)

;

74

Page 75: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea:

Design an analysis that for every program point u determines the

values that variables definitely have at u;

As a side effect, it also tells whether u can be reached at all

75

Page 76: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea:

Design an analysis that for every program point u, determines the

values that variables definitely have at u;

As a side effect, it also tells whether u can be reached at all

We need to design a complete lattice and an abstract semantics for this

analysis.

It abstracts from the variable binding of the state, ρ : Vars → int, in a

similar way as the Rules-of-Sign analysis.

76

Page 77: Static Program Analysis - Compiler Design Lab, Saarland ...

As in the case of the Rules-of-Signs analysis the complete lattice is

constructed in two steps.

(1) The potential values of variables:

Z⊤ = Z ∪ {⊤} with x ⊑ y iff y = ⊤ or x = y

210-1-2

77

Page 78: Static Program Analysis - Compiler Design Lab, Saarland ...

Caveat: Z⊤ is not a complete lattice in itself

(2) D = (Vars → Z⊤)⊥ = (Vars → Z

⊤) ∪ {⊥}

// ⊥ denotes: “not reachable”

with D1 ⊑ D2 iff ⊥ = D1 or

D1 x ⊑ D2 x (x ∈ Vars)

Remark: D is a complete lattice

78

Page 79: Static Program Analysis - Compiler Design Lab, Saarland ...

For every edge k = (_, lab, _) , construct an effect function

[[k]]♯ = [[lab]]♯ : D → D which simulates the concrete computation.

Obviously, [[lab]]♯ ⊥ = ⊥ for all lab

Now let ⊥ 6= D ∈ Vars → Z⊤.

79

Page 80: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea:

• We use D to determine the values of expressions.

80

Page 81: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea:

• We use D to determine the values of expressions.

• For some sub-expressions, we obtain ⊤

81

Page 82: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea:

• We use D to determine the values of expressions.

• For some sub-expressions, we obtain ⊤

==⇒

As in the Rules-of-Sign analysis, we replace the concrete operators

✷ by abstract operators ✷♯ that can handle ⊤ :

a✷♯ b =

{

⊤ if a = ⊤ or b = ⊤

a✷ b otherwise

82

Page 83: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea:

• We use D to determine the values of expressions.

• For some sub-expressions, we obtain ⊤

==⇒

As in the Rules-of-Sign analysis, we replace the concrete operators

✷ by abstract operators ✷♯ that can handle ⊤ :

a✷♯ b =

{

⊤ if a = ⊤ or b = ⊤

a✷ b otherwise

• The abstract operators allow to define an abstract evaluation of

expressions:

[[e]]♯ : (Vars → Z⊤) → Z

83

Page 84: Static Program Analysis - Compiler Design Lab, Saarland ...

Abstract evaluation of expressions is like the concrete evaluation — but

with abstract values and operators. Here:

[[c]]♯ D = c

[[e1 ✷ e2]]♯ D = [[e1]]

♯ D✷♯ [[e2]]

♯ D

... analogously for unary operators

84

Page 85: Static Program Analysis - Compiler Design Lab, Saarland ...

Abstract evaluation of expressions is like the concrete evaluation — but

with abstract values and operators. Here:

[[c]]♯ D = c

[[e1 ✷ e2]]♯ D = [[e1]]

♯ D✷♯ [[e2]]

♯ D

... analogously for unary operators

Example: D = {x 7→ 2, y 7→ ⊤}

[[x+ 7]]♯ D = [[x]]♯ D +♯ [[7]]♯ D

= 2 +♯ 7

= 9

[[x− y]]♯ D = 2 −♯ ⊤

= ⊤

85

Page 86: Static Program Analysis - Compiler Design Lab, Saarland ...

Thus, we obtain the following abstract edge effects [[lab]]♯ :

[[;]]♯ D = D

[[true (e)]]♯ D =

{

⊥ if 0 = [[e]]♯ D definitely false

D otherwise possibly true

[[false (e)]]♯ D =

{

D if 0 ⊑ [[e]]♯ D possibly false

⊥ otherwise definitely true

[[x = e;]]♯ D = D ⊕ {x 7→ [[e]]♯ D}

[[x = M [e];]]♯ D = D ⊕ {x 7→ ⊤}

[[M [e1] = e2;]]♯ D = D

... whenever D 6= ⊥

86

Page 87: Static Program Analysis - Compiler Design Lab, Saarland ...

At start, we have D⊤ = {x 7→ ⊤ | x ∈ Vars} .

Example:

2

1

3

4

5

x = 7;

Pos (x > 0)

M [A] = B;

Neg (x > 0)

;

87

Page 88: Static Program Analysis - Compiler Design Lab, Saarland ...

At start, we have D⊤ = {x 7→ ⊤ | x ∈ Vars} .

Example:

2

1

3

4

5

x = 7;

Pos (x > 0)

M [A] = B;

Neg (x > 0)

;

1 {x 7→ ⊤}

2 {x 7→ 7}

3 {x 7→ 7}

4 {x 7→ 7}

5 ⊥ ⊔ {x 7→ 7} = {x 7→ 7}

The abstract effects of edges [[k]]♯ are again composed to form the

effects of paths π = k1 . . . kr by:

[[π]]♯ = [[kr]]♯ ◦ . . . ◦ [[k1]]

♯ : D → D

88

Page 89: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea for Correctness: Abstract Interpretation

Cousot, Cousot 1977

Establish a description relation ∆ between the concrete values and

their descriptions with:

x∆ a1 ∧ a1 ⊑ a2 ==⇒ x∆ a2

Concretization: γ a = {x | x∆ a}

// returns the set of described values

89

Page 90: Static Program Analysis - Compiler Design Lab, Saarland ...

(1) Values: ∆ ⊆ Z× Z⊤

z∆ a iff z = a ∨ a = ⊤

Concretization:

γ a =

{

{a} if a ⊏ ⊤

Z if a = ⊤

90

Page 91: Static Program Analysis - Compiler Design Lab, Saarland ...

(1) Values: ∆ ⊆ Z× Z⊤

z∆ a iff z = a ∨ a = ⊤

Concretization:

γ a =

{

{a} if a ⊏ ⊤

Z if a = ⊤

(2) Variable Bindings: ∆ ⊆ (Vars → Z)× (Vars → Z⊤)⊥

ρ ∆ D iff D 6= ⊥ ∧ ρ x ⊑ Dx (x ∈ Vars)

Concretization:

γ D =

{

∅ if D = ⊥

{ρ | ∀ x : (ρ x) ∆ (Dx)} otherwise

91

Page 92: Static Program Analysis - Compiler Design Lab, Saarland ...

Example: {x 7→ 1, y 7→ −7} ∆ {x 7→ ⊤, y 7→ −7}

(3) States:

∆ ⊆ ((Vars → Z)× (N → Z))× (Vars → Z⊤)⊥

(ρ, µ) ∆ D iff ρ ∆ D

Concretization:

γ D =

{

∅ if D = ⊥

{(ρ, µ) | ∀ x : (ρ x) ∆ (Dx)} otherwise

92

Page 93: Static Program Analysis - Compiler Design Lab, Saarland ...

We show correctness:

(∗) If s ∆ D and [[π]] s is defined, then:

([[π]] s) ∆ ([[π]]♯ D)

s

D D1

s1

∆ ∆

[[π]]

[[π]]♯

93

Page 94: Static Program Analysis - Compiler Design Lab, Saarland ...

(∗) The abstract semantics simulates the concrete semantics

In particular:

[[π]] s ∈ γ ([[π]]♯ D)

94

Page 95: Static Program Analysis - Compiler Design Lab, Saarland ...

(∗) The abstract semantics simulates the concrete semantics

In particular:

[[π]] s ∈ γ ([[π]]♯ D)

In practice, this means for example that Dx = −7 implies:

ρ′ x = −7 for all ρ′ ∈ γ D

==⇒ ρ1 x = −7 for (ρ1, _) = [[π]] s

95

Page 96: Static Program Analysis - Compiler Design Lab, Saarland ...

The MOP-Solution:

D∗[v] =⊔

{[[π]]♯ D⊤ | π : start →∗ v}

where D⊤ x = ⊤ (x ∈ Vars) .

In order to approximate the MOP, we use our constraint system

96

Page 97: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

7x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

36

4

5

2

y = 1;

1

0

M [R] = y;

x = 10;

97

Page 98: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

7x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

36

4

5

2

y = 1;

1

0

M [R] = y;

x = 10;1

x y

0 ⊤ ⊤

1 10 ⊤

2 10 1

3 10 1

4 10 10

5 9 10

6 ⊥

7 ⊥

98

Page 99: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

7x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

36

4

5

2

y = 1;

1

0

M [R] = y;

x = 10;1 2

x y x y

0 ⊤ ⊤ ⊤ ⊤

1 10 ⊤ 10 ⊤

2 10 1 ⊤ ⊤

3 10 1 ⊤ ⊤

4 10 10 ⊤ ⊤

5 9 10 ⊤ ⊤

6 ⊥ ⊤ ⊤

7 ⊥ ⊤ ⊤

99

Page 100: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

7x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

36

4

5

2

y = 1;

1

0

M [R] = y;

x = 10;1 2 3

x y x y x y

0 ⊤ ⊤ ⊤ ⊤

1 10 ⊤ 10 ⊤

2 10 1 ⊤ ⊤

3 10 1 ⊤ ⊤

4 10 10 ⊤ ⊤ dito

5 9 10 ⊤ ⊤

6 ⊥ ⊤ ⊤

7 ⊥ ⊤ ⊤

100

Page 101: Static Program Analysis - Compiler Design Lab, Saarland ...

Concrete vs. Abstract Execution:

Although program and all initial values are given, abstract execution does

not compute the result!

On the other hand, fixed-point iteration is guaranteed to terminate:

For n program points and m variables, we maximally need:

n · (m+ 1) rounds

Observation: The effects of edges are not distributive!

101

Page 102: Static Program Analysis - Compiler Design Lab, Saarland ...

Counterexample: f = [[x = x+ y;]]♯

Let D1 = {x 7→ 2, y 7→ 3}

D2 = {x 7→ 3, y 7→ 2}

Then f D1 ⊔ f D2 = {x 7→ 5, y 7→ 3} ⊔ {x 7→ 5, y 7→ 2}

= {x 7→ 5, y 7→ ⊤}

6= {x 7→ ⊤, y 7→ ⊤}

= f {x 7→ ⊤, y 7→ ⊤}

= f (D1 ⊔D2)

102

Page 103: Static Program Analysis - Compiler Design Lab, Saarland ...

We conclude:

The least solution D of the constraint system in general yields only an

upper approximation of the MOP, i.e.,

D∗[v] ⊑ D[v]

103

Page 104: Static Program Analysis - Compiler Design Lab, Saarland ...

We conclude:

The least solution D of the constraint system in general yields only an

upper approximation of the MOP, i.e.,

D∗[v] ⊑ D[v]

As an upper approximation, D[v] nonetheless describes the result of

every program execution π that reaches v :

([[π]] (ρ, µ)) ∆ (D[v])

whenever [[π]] (ρ, µ) is defined

104

Page 105: Static Program Analysis - Compiler Design Lab, Saarland ...

6 Removing superfluous computations

A computation may be superfluous because

• the result is already available, −→ available-expression analysis, or

• the result is not needed −→ live-variable analysis.

105

Page 106: Static Program Analysis - Compiler Design Lab, Saarland ...

6.1 Redundant computations

Idea:

If an expression at a program point is guaranteed to be computed to the

value it had before, then

→ store this value after the first computation;

→ replace every further computation through a look-up

Question to be answered by static analysis: Is an expression

available?

106

Page 107: Static Program Analysis - Compiler Design Lab, Saarland ...

Problem: Identify sources of redundant computations!

Example:

z = 1;

y = M [17];

A : x1 = y + z ;

. . .

B : x2 = y + z ;

B is a redundant computation of the value of y + z , if

(1) A is always executed before B; and

(2) y and z at B have the same values as at A

107

Page 108: Static Program Analysis - Compiler Design Lab, Saarland ...

Situation: The value of x+ y is computed at program point u

u vx+y

π

and a computation along path π reaches v where it evaluates again x+ y

....

If x and y have not been modified in π, then evaluation of x+ y at v

returns the same value as evaluation at u.

This property can be checked at every edge in π.

More efficient: Do this check for all expressions occurring in the

program in parallel.

Assume that the expressions A = {e1, . . . , er} are available at u.

Every edge k transforms this set into a set [[k]]♯ A of expressions

whose values are available after execution of k ...

108

Page 109: Static Program Analysis - Compiler Design Lab, Saarland ...

Situation: The value of x+ y is computed at program point u

u vx+y

π

and a computation along path π reaches v where it evaluates again x+ y

.... If x and y have not been modified in π, then evaluation of x+ y at v is

known to return the same value as evaluation at u

This property can be checked at every edge in π.

More efficient: Do this check for all expressions occurring in the

program in parallel.

Assume that the expressions A = {e1, . . . , er} are available at u.

Every edge k transforms this set into a set [[k]]♯ A of expressions

whose values are available after execution of k ....

109

Page 110: Static Program Analysis - Compiler Design Lab, Saarland ...

Situation: The value of x+ y is computed at program point u

u vx+y

π

and a computation along path π reaches v where it evaluates again x+ y

.... If x and y have not been modified in π, then evaluation of x+ y at v

must return the same value as evaluation at u.

This property can be checked at every edge in π.

More efficient: Do this check for all expressions occurring in the

program in parallel.

Assume that the expressions A = {e1, . . . , er} are available at u.

Every edge k transforms this set into a set [[k]]♯ A of expressions

whose values are available after execution of k.

[[k]]♯ A is the (abstract) edge effect associated with k

110

Page 111: Static Program Analysis - Compiler Design Lab, Saarland ...

These edge effects can be composed to the effect of a path π = k1 . . . kr:

[[π]]♯ = [[kr]]♯ ◦ . . . ◦ [[k1]]

The effect [[k]]♯ of an edge k = (u, lab, v) only depends on the

label lab, i.e., [[k]]♯ = [[lab]]♯ where:

[[;]]♯ A = A

[[true(e)]]♯ A = [[false(e)]]♯ A = A ∪ {e}

[[x = e;]]♯ A = (A ∪ {e})\ itExprx where

Exprx all expressions which contain x

111

Page 112: Static Program Analysis - Compiler Design Lab, Saarland ...

These edge effects can be composed to the effect of a path π = k1 . . . kr:

[[π]]♯ = [[kr]]♯ ◦ . . . ◦ [[k1]]

The effect [[k]]♯ of an edge k = (u, lab, v) only depends on the

label lab, i.e., [[k]]♯ = [[lab]]♯ where:

[[;]]♯ A = A

[[true(e)]]♯ A = [[false(e)]]♯ A = A ∪ {e}

[[x = e;]]♯ A = (A ∪ {e})\Exprx where

Exprx are all expressions that contain x

112

Page 113: Static Program Analysis - Compiler Design Lab, Saarland ...

These edge effects can be composed to the effect of a path π = k1 . . . kr:

[[π]]♯ = [[kr]]♯ ◦ . . . ◦ [[k1]]

The effect [[k]]♯ of an edge k = (u, lab, v) only depends on the

label lab, i.e., [[k]]♯ = [[lab]]♯ where:

[[;]]♯ A = A

[[true(e)]]♯ A = [[false(e)]]♯ A = A ∪ {e}

[[x = e;]]♯ A = (A ∪ {e})\Exprx where

Exprx all expressions that contain x

[[x = M [e];]]♯ A = (A ∪ {e})\Exprx

[[M [e1] = e2;]]♯ A = A ∪ {e1, e2}

113

Page 114: Static Program Analysis - Compiler Design Lab, Saarland ...

→ An expression is available at v if it is available along all paths π to

v.

→ For every such path π, the analysis determines the set of

expressions that are available along π.

→ Initially at program start, nothing is available.

→ The analysis computes the intersection of the availability sets as

safe information.

==⇒ For each node v, we need the set:

A[v] =⋂

{[[π]]♯∅ | π : start →∗ v}

114

Page 115: Static Program Analysis - Compiler Design Lab, Saarland ...

How does a compiler exploit this information?

Transformation UT (unique temporaries):

We provide a novel register Te as storage for the values of e:

v

u

v

u

Te = e;

x = Te;

x = e;

115

Page 116: Static Program Analysis - Compiler Design Lab, Saarland ...

Transformation UT (unique temporaries):

We provide novel registers Te as storage for the value of e:

v

u

u

v v

Pos (e)

v

u

v

u

Te = e;

x = Te;

Neg (e)

x = e;

Te = e;

v

Pos (Te)Neg (Te)

... analogously for R = M [e]; and M [e1] = e2;.

116

Page 117: Static Program Analysis - Compiler Design Lab, Saarland ...

Transformation AEE (available expression elimination):

If e is available at program point u, then e need not be re-evaluated:

u u

Te = e; ;

e ∈ A[u]

We replace the assignment with Nop.

117

Page 118: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

x = y + 3;

x = 7;

z = y + 3;

x = 7;

z = y + 3;

x = y + 3;

118

Page 119: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

x = y + 3;

x = 7;

z = y + 3;

x = 7;

T = y + 3;

x = T ;

T = y + 3;

z = T ;

119

Page 120: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

x = y + 3;

x = 7;

z = y + 3;

x = 7;

z = T ;

T = y + 3;

x = T ;

T = y + 3;

{y + 3}

{y + 3}

{y + 3}

{y + 3}

{y + 3}

120

Page 121: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

x = y + 3;

x = 7;

z = y + 3;

x = 7;

T = y + 3;

x = T ;

;

z = T ;

{y + 3}

{y + 3}

{y + 3}

{y + 3}

{y + 3}

121

Page 122: Static Program Analysis - Compiler Design Lab, Saarland ...

Warning:

Transformation UT is only meaningful for assignments x = e; where:

→ x 6∈ Vars(e); why?

→ e 6∈ Vars; why?

→ the evaluation of e is non-trivial; why?

Which leaves open whether ...

122

Page 123: Static Program Analysis - Compiler Design Lab, Saarland ...

Warning:

Transformation UT is only meaningful for assignments x = e; where:

→ x 6∈ Vars(e); otherwise e is not available afterwards.

→ e 6∈ Vars; otherwise values are shuffled around

→ the evaluation of e is non-trivial; otherwise the efficiency of the

code is decreased.

Open question ...

123

Page 124: Static Program Analysis - Compiler Design Lab, Saarland ...

Question:

How do we compute A[u] for every program point u?

Idea:

We collect all constraints on the values of A[u] into a system of

constraints:

A[start ] ⊆ ∅

A[v] ⊆ [[k]]♯ (A[u]) k = (u, _, v) edge

124

Page 125: Static Program Analysis - Compiler Design Lab, Saarland ...

Question:

How can we compute A[u] for every program point? u

Idea:

We collect all constraints on the values of A[u] into a system of

constraints:

A[start ] ⊆ ∅

A[v] ⊆ [[k]]♯ (A[u]) k = (u, _, v) edge

Why ⊆?

125

Page 126: Static Program Analysis - Compiler Design Lab, Saarland ...

Question:

How can we compute A[u] for every program point? u

Idea:

We collect all constraints on the values of A[u] into a system of

constraints:

A[start ] ⊆ ∅

A[v] ⊆ [[k]]♯ (A[u]) k = (u, _, v) edge

Why ⊆?

Then combine all constraints for each variable v by applying the

least-upper-bound operator −→

A[v] ⊆⋂

{[[k]]♯ (A[u]) | k = (u, _, v) edge}

126

Page 127: Static Program Analysis - Compiler Design Lab, Saarland ...

Wanted:

• a greatest solution (why greatest?)

• an algorithm that computes this solution

Example:

3

2

4

5

0

1

y = 1;

x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

127

Page 128: Static Program Analysis - Compiler Design Lab, Saarland ...

Wanted:

• a greatest solution (why greatest?)

• an algorithm that computes this solution

Example:

3

2

4

5

0

1

y = 1;

x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

A[0] ⊆ ∅

128

Page 129: Static Program Analysis - Compiler Design Lab, Saarland ...

Wanted:

• a greatest solution (why greatest?)

• an algorithm that computes this solution

Example:

3

2

4

5

0

1

y = 1;

x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

A[0] ⊆ ∅

A[1] ⊆ (A[0] ∪ {1})\Expry

A[1] ⊆ A[4]

129

Page 130: Static Program Analysis - Compiler Design Lab, Saarland ...

Wanted:

• a greatest solution (why greatest?)

• an algorithm that computes this solution

Example:

3

2

4

5

0

1

y = 1;

x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

A[0] ⊆ ∅

A[1] ⊆ (A[0] ∪ {1})\Expry

A[1] ⊆ A[4]

A[2] ⊆ A[1] ∪ {x > 1}

130

Page 131: Static Program Analysis - Compiler Design Lab, Saarland ...

Wanted:

• a greatest solution (why greatest?)

• an algorithm that computes this solution

Example:

3

2

4

5

0

1

y = 1;

x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

A[0] ⊆ ∅

A[1] ⊆ (A[0] ∪ {1})\Expry

A[1] ⊆ A[4]

A[2] ⊆ A[1] ∪ {x > 1}

A[3] ⊆ (A[2] ∪ {x ∗ y})\Expry

131

Page 132: Static Program Analysis - Compiler Design Lab, Saarland ...

Wanted:

• a greatest solution (why greatest?)

• an algorithm that computes this solution

Example:

3

2

4

5

0

1

y = 1;

x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

A[0] ⊆ ∅

A[1] ⊆ (A[0] ∪ {1})\Expry

A[1] ⊆ A[4]

A[2] ⊆ A[1] ∪ {x > 1}

A[3] ⊆ (A[2] ∪ {x ∗ y})\Expry

A[4] ⊆ (A[3] ∪ {x− 1})\Exprx

132

Page 133: Static Program Analysis - Compiler Design Lab, Saarland ...

Wanted:

• a greatest solution (why greatest?)

• an algorithm that computes this solution

Example:

3

2

4

5

0

1

y = 1;

x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

A[0] ⊆ ∅

A[1] ⊆ (A[0] ∪ {1})\Expry

A[1] ⊆ A[4]

A[2] ⊆ A[1] ∪ {x > 1}

A[3] ⊆ (A[2] ∪ {x ∗ y})\Expry

A[4] ⊆ (A[3] ∪ {x− 1})\Exprx

A[5] ⊆ A[1] ∪ {x > 1}

133

Page 134: Static Program Analysis - Compiler Design Lab, Saarland ...

Wanted:

• a greatest solution,

• an algorithm that computes this solution.

Example:

3

2

4

5

0

1

y = 1;

x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

Solution:

A[0] = ∅

A[1] = {1}

A[2] = {1, x > 1}

A[3] = {1, x > 1}

A[4] = {1}

A[5] = {1, x > 1}

134

Page 135: Static Program Analysis - Compiler Design Lab, Saarland ...

Observation:

• Again, the possible values for A[u] form a complete lattice:

D = 2Expr with B1 ⊑ B2 iff B1 ⊇ B2

• The order on the lattice elements indicates what is better

information,

more available expressions may allow more optimizations

• The functions [[k]]♯ : D → D are monotonic, i.e.,

[[k]]♯(B1) ⊑ [[k]]♯(B2) gdw. B1 ⊑ B2

135

Page 136: Static Program Analysis - Compiler Design Lab, Saarland ...

Observation:

• Again, the possible values for A[u] form a complete lattice:

D = 2Expr with B1 ⊑ B2 iff B1 ⊇ B2

• The order on the lattice elements indicates what is better

information,

more available expressions may allow more optimizations

• The functions [[k]]♯ : D → D have the form fi x = ai ∩ x ∪ bi.

They are called gen/kill functions — ∩ kills, ∪ generates.

• they are monotonic, i.e.,

[[k]]♯(B1) ⊑ [[k]]♯(B2) iff B1 ⊑ B2

136

Page 137: Static Program Analysis - Compiler Design Lab, Saarland ...

The operations “◦”, “⊔” and “⊓” can be explicitly defined by:

(f2 ◦ f1) x = a1 ∩ a2 ∩ x ∪ a2 ∩ b1 ∪ b2

(f1 ⊔ f2) x = (a1 ∪ a2) ∩ x ∪ b1 ∪ b2

(f1 ⊓ f2) x = (a1 ∪ b1) ∩ (a2 ∪ b2) ∩ x ∪ b1 ∩ b2

137

Page 138: Static Program Analysis - Compiler Design Lab, Saarland ...

6.2 Removing Assignments to Dead Variables

Example:

1 : x = y + 2;

2 : y = 5;

3 : x = y + 3;

The value of x at program points 1, 2 is overwritten before it can

be used.

Therefore, we call the variable x dead at these program points.

138

Page 139: Static Program Analysis - Compiler Design Lab, Saarland ...

Note:

→ Assignments to dead variables can be removed.

→ Such inefficiencies may originate from other transformations.

139

Page 140: Static Program Analysis - Compiler Design Lab, Saarland ...

Note:

→ Assignments to dead variables can be removed.

→ Such inefficiencies may originate from other transformations.

Formal Definition:

The variable x is called live at u along a path π starting at u

if π can be decomposed into π = π1 k π2 such that:

• k is a use of x and

• π1 does not contain a definition of x.

140

Page 141: Static Program Analysis - Compiler Design Lab, Saarland ...

uπ1

k

Thereby, the set of all defined or used variables at an edge

k = (_, lab, _) is defined by

lab used defined

; ∅ ∅

true (e) Vars (e) ∅

false (e) Vars (e) ∅

x = e; Vars (e) {x}

x = M [e]; Vars (e) {x}

M [e1] = e2; Vars (e1) ∪ Vars (e2) ∅

141

Page 142: Static Program Analysis - Compiler Design Lab, Saarland ...

A variable x which is not live at u along π is called dead at

u along π .

Example:

10 2 3

x = y + 2; y = 5; x = y + 3;

Then we observe:

live dead

0 {y} {x}

1 ∅ {x, y}

2 {y} {x}

3 ∅ {x, y}

142

Page 143: Static Program Analysis - Compiler Design Lab, Saarland ...

The variable x is live at u if x is live at u along some path to

the exit . Otherwise, x is called dead at u .

143

Page 144: Static Program Analysis - Compiler Design Lab, Saarland ...

The variable x is live at u if x is live at u along some path

to the exit. Otherwise, x is called dead at u.

Question:

How can the sets of all dead/live variables be computed for every u?

144

Page 145: Static Program Analysis - Compiler Design Lab, Saarland ...

The variable x is live at u if x is live at u along some path

to the exit. Otherwise, x is called dead at u .

Question:

How can the sets of all dead/live variables be computed for every u?

Idea:

For every edge k = (u, _, v) , define a function [[k]]♯ which transforms

the set of variables that are live at v into the set of variables that are

live at u.

Note: Edge transformers go "backwards"!

145

Page 146: Static Program Analysis - Compiler Design Lab, Saarland ...

Let L = 2Vars .

For k = (_, lab, _) , define [[k]]♯ = [[lab]]♯ by:

[[;]]♯ L = L

[[true(e)]]♯ L = [[false(e)]]♯ L = L ∪ Vars(e)

[[x = e;]]♯ L = (L\{x}) ∪ Vars(e)

[[x = M [e];]]♯ L = (L\{x}) ∪ Vars(e)

[[M [e1] = e2;]]♯ L = L ∪ Vars(e1) ∪ Vars(e2)

146

Page 147: Static Program Analysis - Compiler Design Lab, Saarland ...

Let L = 2Vars .

For k = (_, lab, _) , define [[k]]♯ = [[lab]]♯ by:

[[;]]♯ L = L

[[true(e)]]♯ L = [[false(e)]]♯ L = L ∪ Vars(e)

[[x = e;]]♯ L = (L\{x}) ∪ Vars(e)

[[x = M [e];]]♯ L = (L\{x}) ∪ Vars(e)

[[M [e1] = e2;]]♯ L = L ∪ Vars(e1) ∪ Vars(e2)

[[k]]♯ can again be composed to the effects of [[π]]♯ of paths

π = k1 . . . kr by:

[[π]]♯ = [[k1]]♯ ◦ . . . ◦ [[kr]]

147

Page 148: Static Program Analysis - Compiler Design Lab, Saarland ...

We verify that these definitions are meaningful

4 5321

M [y] = x;x = y + 2;y = 5;x = y + 2;

148

Page 149: Static Program Analysis - Compiler Design Lab, Saarland ...

We verify that these definitions are meaningful

4 5321

M [y] = x;x = y + 2;y = 5;x = y + 2;

149

Page 150: Static Program Analysis - Compiler Design Lab, Saarland ...

We verify that these definitions are meaningful

4 5321

M [y] = x;x = y + 2;y = 5;x = y + 2;

∅{x, y}

150

Page 151: Static Program Analysis - Compiler Design Lab, Saarland ...

We verify that these definitions are meaningful

4 5321

M [y] = x;x = y + 2;y = 5;x = y + 2;

∅{x, y}{y}

151

Page 152: Static Program Analysis - Compiler Design Lab, Saarland ...

We verify that these definitions are meaningful

4 5321

M [y] = x;x = y + 2;y = 5;x = y + 2;

∅{x, y}{y}∅

152

Page 153: Static Program Analysis - Compiler Design Lab, Saarland ...

We verify that these definitions are meaningful

4 5321

M [y] = x;x = y + 2;y = 5;x = y + 2;

∅{x, y}{y}∅{y}

153

Page 154: Static Program Analysis - Compiler Design Lab, Saarland ...

A variable is live at a program point u if there is at least one path

from u to program exit on which it is live.

The set of variables which are live at u therefore is given by:

L∗[u] =⋃

{[[π]]♯∅ | π : u →∗ stop}

No variables are assumed to be live at program exit.

As partial order for L we use ⊑ = ⊆. why?

So, the least upper bound is⋃

. why?

154

Page 155: Static Program Analysis - Compiler Design Lab, Saarland ...

Transformation DE (Dead assignment Elimination):

;

v v

x = e;

x 6∈ L∗[v]

;

v v

x 6∈ L∗[v]

x = M [e];

155

Page 156: Static Program Analysis - Compiler Design Lab, Saarland ...

Correctness Proof:

→ Correctness of the effects of edges: If L is the set of variables

which are live at the exit of the path π , then [[π]]♯ L is the set

of variables which are live at the beginning of π.

→ Correctness of the transformation along a path: If the value of a

variable is accessed, this variable is necessarily live. The value of

dead variables thus is irrelevant.

→ Correctness of the transformation: In any execution of the

transformed programs, the live variables always receive the same

values as in the original program.

156

Page 157: Static Program Analysis - Compiler Design Lab, Saarland ...

Computation of the sets L∗[u] :

(1) Collecting constraints:

L[stop] ⊇ ∅

L[u] ⊇ [[k]]♯ (L[v]) k = (u, _, v) edge

(2) Solving the constraint system by means of RR iteration.

Since L is finite, the iteration will terminate

(3) If the exit is (formally) reachable from every program

point, then the least solution L of the constraint

system equals L∗ since all [[k]]♯ are distributive

157

Page 158: Static Program Analysis - Compiler Design Lab, Saarland ...

Computation of the sets L∗[u] :

(1) Collecting constraints:

L[stop] ⊇ ∅

L[u] ⊇ [[k]]♯ (L[v]) k = (u, _, v) edge

(2) Solving the constraint system by means of RR iteration.

Since L is finite, the iteration will terminate

(3) If the exit is (formally) reachable from every program

point, then the least solution L of the constraint system equals

L∗ since all [[k]]♯ are distributive.

Note: The information is propagated backwards!

158

Page 159: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

7x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

36

4

5

2

y = 1;

1

x = M [I];

0

M [R] = y;

L[0] ⊇ (L[1]\{x}) ∪ {I}

L[1] ⊇ L[2]\{y}

L[2] ⊇ (L[6] ∪ {x}) ∪ (L[3] ∪ {x})

L[3] ⊇ (L[4]\{y}) ∪ {x, y}

L[4] ⊇ (L[5]\{x}) ∪ {x}

L[5] ⊇ L[2]

L[6] ⊇ L[7] ∪ {y,R}

L[7] ⊇ ∅

159

Page 160: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

7x = x− 1;

y = x ∗ y;

Pos(x > 1)Neg(x > 1)

36

4

5

2

y = 1;

1

x = M [I];

0

M [R] = y;

1 2

7 ∅

6 {y,R}

2 {x, y, R} dito

5 {x, y, R}

4 {x, y, R}

3 {x, y, R}

1 {x,R}

0 {I, R}

160

Page 161: Static Program Analysis - Compiler Design Lab, Saarland ...

The left-hand side of no assignment is dead

Caveat:

Removal of assignments to dead variables may kill further variables:

2

3

1

4

x = y + 1;

z = 2 ∗ x;

M [R] = y;

161

Page 162: Static Program Analysis - Compiler Design Lab, Saarland ...

The left-hand side of no assignment is dead

Caveat:

Removal of assignments to dead variables may kill further variables:

2

3

1

4

x = y + 1;

z = 2 ∗ x;

M [R] = y;

y,R

162

Page 163: Static Program Analysis - Compiler Design Lab, Saarland ...

The left-hand side of no assignment is dead

Caveat:

Removal of assignments to dead variables may kill further variables:

2

3

1

4

x = y + 1;

z = 2 ∗ x;

M [R] = y;

y,R

x, y, R

163

Page 164: Static Program Analysis - Compiler Design Lab, Saarland ...

The left-hand side of no assignment is dead

Caveat:

Removal of assignments to dead variables may kill further variables:

2

3

1

4

x = y + 1;

z = 2 ∗ x;

M [R] = y;

y,R

y,R

x, y, R

164

Page 165: Static Program Analysis - Compiler Design Lab, Saarland ...

The left-hand side of no assignment is dead

Caveat:

Removal of assignments to dead variables may kill further variables:

2

3

1

4

2

3

1

4

x = y + 1;

z = 2 ∗ x;

M [R] = y;

y,R

y,R

x, y, R

x = y + 1;

;

M [R] = y;

165

Page 166: Static Program Analysis - Compiler Design Lab, Saarland ...

The left-hand side of no assignment is dead

Caveat:

Removal of assignments to dead variables may kill further variables:

2

3

1

4

2

3

1

4

x = y + 1;

z = 2 ∗ x;

M [R] = y;

y,R

y,R

x, y, R

x = y + 1;

;

M [R] = y;

y,R

y,R

y,R

166

Page 167: Static Program Analysis - Compiler Design Lab, Saarland ...

The left-hand side of no assignment is dead

Caveat:

Removal of assignments to dead variables may kill further variables:

2

3

1

4

2

3

1

4

2

3

1

4

x = y + 1;

z = 2 ∗ x;

M [R] = y;

y,R

y,R

x, y, R

x = y + 1;

;

M [R] = y;

y,R

y,R

y,R

;

;

M [R] = y;

167

Page 168: Static Program Analysis - Compiler Design Lab, Saarland ...

Re-analyzing the program is inconvenient

Idea: Analyze true liveness!

x is called truly live at u along a path π, either

if π can be decomposed into π = π1 k π2 such that:

• k is a true use of x ;

• π1 does not contain any definition of x.

168

Page 169: Static Program Analysis - Compiler Design Lab, Saarland ...

u vkπ2

The set of truly used variables at an edge k = (_, lab, v) is defined as:

lab truly used

; ∅

true (e) Vars (e)

false (e) Vars (e)

x = e; Vars (e) (∗)

x = M [e]; Vars (e) (∗)

M [e1] = e2; Vars(e1) ∪ Vars(e2)

(∗) – given that x is truly live at v

169

Page 170: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

2

3

1

4

x = y + 1;

z = 2 ∗ x;

M [R] = y;

170

Page 171: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

2

3

1

4

x = y + 1;

z = 2 ∗ x;

M [R] = y;

y,R

171

Page 172: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

2

3

1

4

x = y + 1;

z = 2 ∗ x;

M [R] = y;

y,R

y,R

172

Page 173: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

2

3

1

4

x = y + 1;

z = 2 ∗ x;

M [R] = y;

y,R

y,R

y,R

173

Page 174: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

2

3

1

4

2

3

1

4

x = y + 1;

z = 2 ∗ x;

M [R] = y;

;

;

M [R] = y;

y,R

y,R

y,R

174

Page 175: Static Program Analysis - Compiler Design Lab, Saarland ...

The Effects of Edges:

[[;]]♯ L = L

[[true(e)]]♯ L = [[false(e)]]♯ L = L ∪ Vars(e)

[[x = e;]]♯ L = (L\{x}) ∪ (x ∈ L) ?Vars(e) : ∅

[[x = M [e];]]♯ L = (L\{x}) ∪ Vars(e)

[[M [e1] = e2;]]♯ L = L ∪ Vars(e1) ∪ Vars(e2)

175

Page 176: Static Program Analysis - Compiler Design Lab, Saarland ...

The Effects of Edges:

[[;]]♯ L = L

[[true(e)]]♯ L = [[false(e)]]♯ L = L ∪ Vars(e)

[[x = e;]]♯ L = (L\{x}) ∪ (x ∈ L) ?Vars(e) : ∅

[[x = M [e];]]♯ L = (L\{x}) ∪ (x ∈ L) ?Vars(e) : ∅

[[M [e1] = e2;]]♯ L = L ∪ Vars(e1) ∪ Vars(e2)

176

Page 177: Static Program Analysis - Compiler Design Lab, Saarland ...

Note:

• The effects of edges for truly live variables are more complicated

than for live variables

• Nonetheless, they are distributive !!

177

Page 178: Static Program Analysis - Compiler Design Lab, Saarland ...

Note:

• The effects of edges for truly live variables are more complicated

than for live variables

• Nonetheless, they are distributive !!

To see this, consider for D = 2U , f y = (u ∈ y) ? b : ∅ We

verify:

f (y1 ∪ y2) = (u ∈ y1 ∪ y2) ? b : ∅

= (u ∈ y1 ∨ u ∈ y2) ? b : ∅

= (u ∈ y1) ? b : ∅ ∪ (u ∈ y2) ? b : ∅

= f y1 ∪ f y2

178

Page 179: Static Program Analysis - Compiler Design Lab, Saarland ...

Note:

• The effects of edges for truly live variables are more complicated

than for live variables

• Nonetheless, they are distributive !!

To see this, consider for D = 2U , f y = (u ∈ y) ? b : ∅ We

verify:

f (y1 ∪ y2) = (u ∈ y1 ∪ y2) ? b : ∅

= (u ∈ y1 ∨ u ∈ y2) ? b : ∅

= (u ∈ y1) ? b : ∅ ∪ (u ∈ y2) ? b : ∅

= f y1 ∪ f y2

==⇒ the constraint system yields the MOP

179

Page 180: Static Program Analysis - Compiler Design Lab, Saarland ...

• True liveness detects more superfluous assignments than repeated

liveness !!!

True Liveness:

x = x− 1;

;

180

Page 181: Static Program Analysis - Compiler Design Lab, Saarland ...

• True liveness detects more superfluous assignments than repeated

liveness !!!

True Liveness:

x = x− 1;

;

{x}

181

Page 182: Static Program Analysis - Compiler Design Lab, Saarland ...

• True liveness detects more superfluous assignments than repeated

liveness !!!

True Liveness:

x = x− 1;

;

182

Page 183: Static Program Analysis - Compiler Design Lab, Saarland ...

7 Interval Analysis

Constant propagation attempts to determine values of variables.

However, variables may take on several values during program execution.

So, the value of a variable will often be unknown.

Next attempt: determine an interval enclosing all possible values that a

variable may take on during program execution at a program point.

183

Page 184: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

for (i = 0; i < 42; i++)

if (0 ≤ i ∧ i < 42){

A1 = A+ i;

M [A1] = i;

}

// A start address of an array

// if-statement does array-bounds check

Obviously, the inner check is superfluous.

184

Page 185: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea 1:

Determine for every variable x the tightest possible interval of

potential values.

Abstract domain:

I = {[l, u] | l ∈ Z ∪ {−∞}, u ∈ Z ∪ {+∞}, l ≤ u}

Partial order:

[l1, u1] ⊑ [l2, u2] iff l2 ≤ l1 ∧ u1 ≤ u2

l1 u1

l2 u2

185

Page 186: Static Program Analysis - Compiler Design Lab, Saarland ...

Thus:

[l1, u1] ⊔ [l2, u2] = [l1 ⊓ l2, u1 ⊔ u2]

[l1, u1] ⊓ [l2, u2] = [l1 ⊔ l2, u1 ⊓ u2] whenever (l1 ⊔ l2) ≤ (u1 ⊓ u2)

l1 u1

l2 u2

186

Page 187: Static Program Analysis - Compiler Design Lab, Saarland ...

Thus:

[l1, u1] ⊔ [l2, u2] = [l1 ⊓ l2, u1 ⊔ u2]

[l1, u1] ⊓ [l2, u2] = [l1 ⊔ l2, u1 ⊓ u2] whenever (l1 ⊔ l2) ≤ (u1 ⊓ u2)

l1 u1

l2 u2

187

Page 188: Static Program Analysis - Compiler Design Lab, Saarland ...

Caveat:

→ I is not a complete lattice,

→ I has infinite ascending chains, e.g.,

[0, 0] ⊏ [0, 1] ⊏ [−1, 1] ⊏ [−1, 2] ⊏ . . .

188

Page 189: Static Program Analysis - Compiler Design Lab, Saarland ...

Caveat:

→ I is not a complete lattice,

→ I has infinite ascending chains, e.g.,

[0, 0] ⊏ [0, 1] ⊏ [−1, 1] ⊏ [−1, 2] ⊏ . . .

Description Relation:

z ∆ [l, u] iff l ≤ z ≤ u

Concretization:

γ [l, u] = {z ∈ Z | l ≤ z ≤ u}

189

Page 190: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

γ [0, 7] = {0, . . . , 7}

γ [0,∞] = {0, 1, 2, . . . , }

Computing with intervals: Interval Arithmetic.

Addition:

[l1, u1] +♯ [l2, u2] = [l1 + l2, u1 + u2] where

−∞+ _ = −∞

+∞+ _ = +∞

// −∞+∞ cannot occur

190

Page 191: Static Program Analysis - Compiler Design Lab, Saarland ...

Negation:

−♯ [l, u] = [−u,−l]

Multiplication:

[l1, u1] ∗♯ [l2, u2] = [a, b] where

a = l1l2 ⊓ l1u2 ⊓ u1l2 ⊓ u1u2

b = l1l2 ⊔ l1u2 ⊔ u1l2 ⊔ u1u2

Example:

[0, 2] ∗♯ [3, 4] = [0, 8]

[−1, 2] ∗♯ [3, 4] = [−4, 8]

[−1, 2] ∗♯ [−3, 4] = [−6, 8]

[−1, 2] ∗♯ [−4,−3] = [−8, 4]

191

Page 192: Static Program Analysis - Compiler Design Lab, Saarland ...

Division: [l1, u1] /♯ [l2, u2] = [a, b]

• If 0 is not contained in the interval of the denominator, then:

a = l1/l2 ⊓ l1/u2 ⊓ u1/l2 ⊓ u1/u2

b = l1/l2 ⊔ l1/u2 ⊔ u1/l2 ⊔ u1/u2

• If: l2 ≤ 0 ≤ u2 , we define:

[a, b] = [−∞,+∞]

192

Page 193: Static Program Analysis - Compiler Design Lab, Saarland ...

Equality:

[l1, u1] ==♯ [l2, u2] =

true if l1 = u1 = l2 = u2

false if u1 < l2 ∨ u2 < l1

⊤ otherwise

193

Page 194: Static Program Analysis - Compiler Design Lab, Saarland ...

Equality:

[l1, u1] ==♯ [l2, u2] =

true if l1 = u1 = l2 = u2

false if u1 < l2 ∨ u2 < l1

⊤ otherwise

Example:

[42, 42]==♯[42, 42] = true

[0, 7]==♯ [0, 7] = ⊤

[1, 2]==♯ [3, 4] = false

194

Page 195: Static Program Analysis - Compiler Design Lab, Saarland ...

Less:

[l1, u1] <♯ [l2, u2] =

true if u1 < l2

false if u2 ≤ l1

⊤ otherwise

195

Page 196: Static Program Analysis - Compiler Design Lab, Saarland ...

Less:

[l1, u1] <♯ [l2, u2] =

true if u1 < l2

false if u2 ≤ l1

⊤ otherwise

Example:

[1, 2] <♯ [9, 42] = true

[0, 7] <♯ [0, 7] = ⊤

[3, 4] <♯ [1, 2] = false

196

Page 197: Static Program Analysis - Compiler Design Lab, Saarland ...

By means of I we construct the complete lattice:

DI = (Vars → I)⊥

Description Relation:

ρ ∆ D iff D 6= ⊥ ∧ ∀ x ∈ Vars : (ρ x) ∆ (D x)

The abstract evaluation of expressions is defined analogously to constant

propagation. We have:

([[e]] ρ) ∆ ([[e]]♯ D) whenever ρ ∆ D

197

Page 198: Static Program Analysis - Compiler Design Lab, Saarland ...

The Effects of Edges:

[[;]]♯ D = D

[[x = e;]]♯ D = D ⊕ {x 7→ [[e]]♯ D}

[[x = M [e];]]♯ D = D ⊕ {x 7→ ⊤}

[[M [e1] = e2;]]♯ D = D

[[true (e)]]♯ D =

{

⊥ if false = [[e]]♯ Ddefinitely false

D otherwise possibly true

[[false (e)]]♯ D =

{

D if false ⊑ [[e]]♯ Dpossibly false

⊥ otherwise definitely true

... given that D 6= ⊥

198

Page 199: Static Program Analysis - Compiler Design Lab, Saarland ...

Better Exploitation of Conditions:

[[Pos (e)]]♯ D =

{

⊥ if false = [[e]]♯ D

D1 otherwise

where :

D1 =

D ⊕ {x 7→ (Dx) ⊓ ([[e1]]♯ D)} if e ≡ x== e1

D ⊕ {x 7→ (Dx) ⊓ [−∞, u]} if e ≡ x ≤ e1, [[e1]]♯ D = [_, u]

D ⊕ {x 7→ (Dx) ⊓ [l,∞]} if e ≡ x ≥ e1, [[e1]]♯ D = [l, _]

199

Page 200: Static Program Analysis - Compiler Design Lab, Saarland ...

Better Exploitation of Conditions (cont.):

[[Neg (e)]]♯ D =

{

⊥ if false 6⊑ [[e]]♯ D

D1 otherwise

where :

D1 =

D ⊕ {x 7→ (Dx) ⊓ ([[e1]]♯ D)} if e ≡ x 6= e1

D ⊕ {x 7→ (Dx) ⊓ [−∞, u]} if e ≡ x > e1, [[e1]]♯ D = [_, u]

D ⊕ {x 7→ (Dx) ⊓ [l,∞]} if e ≡ x < e1, [[e1]]♯ D = [l, _]

200

Page 201: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

0

1

7

8

6

5

4

i = 0;

Pos(i < 42)

Neg(0 ≤ i < 42)

i = i+ 1;

Neg(i < 42)

M [A1] = i;

A1 = A+ i;

2

3

Pos(0 ≤ i < 42)

i

l u

0 −∞ +∞

1 0 42

2 0 41

3 0 41

4 0 41

5 0 41

6 1 42

7 ⊥

8 42 42

201

Page 202: Static Program Analysis - Compiler Design Lab, Saarland ...

Problem:

→ The solution can be computed with RR-iteration —

after about 42 rounds.

→ On some programs, iteration may never terminate.

Idea: Widening

Accelerate the iteration — at the cost of precision

202

Page 203: Static Program Analysis - Compiler Design Lab, Saarland ...

Formalization of the Approach:

Let xi ⊒ fi (x1, . . . , xn) , i = 1, . . . , n (1)

denote a system of constraints over D

Define an accumulating iteration:

xi = xi ⊔ fi (x1, . . . , xn) , i = 1, . . . , n (2)

We obviously have:

(a) x is a solution of (1) iff x is a solution of (2).

(b) The function G : Dn → D

n with

G (x1, . . . , xn) = (y1, . . . , yn) , yi = xi ⊔ fi (x1, . . . , xn)

is increasing, i.e., x ⊑ Gx for all x ∈ Dn .

203

Page 204: Static Program Analysis - Compiler Design Lab, Saarland ...

(c) The sequence Gk ⊥ , k ≥ 0, is an ascending chain:

⊥ ⊑ G⊥ ⊑ . . . ⊑ Gk ⊥ ⊑ . . .

(d) If Gk ⊥ = Gk+1⊥ = y , then y is a solution of (1).

(e) If D has infinite strictly ascending chains, then (d) is not yet

sufficient ...

but: we could consider the modified system of equations:

xi = xi ⊔– fi(x1, . . . , xn) , i = 1, . . . , n (3)

for a binary operation widening:

⊔– : D2 → D with v1 ⊔ v2 ⊑ v1 ⊔– v2

(RR)-iteration for (3) still will compute a solution of (1)

204

Page 205: Static Program Analysis - Compiler Design Lab, Saarland ...

... for Interval Analysis:

• The complete lattice is: DI = (Vars → I)⊥

• the widening ⊔– is defined by:

⊥⊔– D = D⊔– ⊥ = D and for D1 6= ⊥ 6= D2:

(D1 ⊔– D2) x = (D1 x)⊔– (D2 x) where

[l1, u1]⊔– [l2, u2] = [l, u] with

l =

{

l1 if l1 ≤ l2

−∞ otherwise

u =

{

u1 if u1 ≥ u2

+∞ otherwise

==⇒ ⊔– is not commutative !!!

205

Page 206: Static Program Analysis - Compiler Design Lab, Saarland ...

Example:

[0, 2]⊔– [1, 2] = [0, 2]

[1, 2]⊔– [0, 2] = [−∞, 2]

[1, 5]⊔– [3, 7] = [1,+∞]

→ Widening returns larger values more quickly.

→ It should be constructed in such a way that termination of iteration

is guaranteed.

→ For interval analysis, widening bounds the number of iterations by:

#points · (1 + 2 ·#Vars)

206

Page 207: Static Program Analysis - Compiler Design Lab, Saarland ...

Conclusion:

• In order to determine a solution of (1) over a complete lattice

with infinite ascending chains, we define a suitable widening and

then solve (3)

• Caveat: The construction of suitable widenings is a dark art !!!

Often ⊔– is chosen dynamically during iteration such that

→ the abstract values do not get too complicated;

→ the number of updates remains bounded ...

207

Page 208: Static Program Analysis - Compiler Design Lab, Saarland ...

Our Example:

0

1

7

8

6

5

4

i = 0;

Pos(i < 42)

Neg(0 ≤ i < 42)

i = i+ 1;

Neg(i < 42)

M [A1] = i;

A1 = A+ i;

2

3

Pos(0 ≤ i < 42)

1

l u

0 −∞ +∞

1 0 0

2 0 0

3 0 0

4 0 0

5 0 0

6 1 1

7 ⊥

8 ⊥

208

Page 209: Static Program Analysis - Compiler Design Lab, Saarland ...

Our Example:

0

1

7

8

6

5

4

i = 0;

Pos(i < 42)

Neg(0 ≤ i < 42)

i = i+ 1;

Neg(i < 42)

M [A1] = i;

A1 = A+ i;

2

3

Pos(0 ≤ i < 42)

1 2 3

l u l u l u

0 −∞ +∞ −∞ +∞

1 0 0 0 +∞

2 0 0 0 +∞

3 0 0 0 +∞

4 0 0 0 +∞ dito

5 0 0 0 +∞

6 1 1 1 +∞

7 ⊥ 42 +∞

8 ⊥ 42 +∞

209

Page 210: Static Program Analysis - Compiler Design Lab, Saarland ...

... obviously, the result is disappointing.

Idea 2:

In fact, acceleration with ⊔– need only be applied at sufficiently many

places!

A set I is a loop separator, if every loop contains at least one point

from I

If we apply widening only at program points from such a set I , then

RR-iteration still terminates !!!

210

Page 211: Static Program Analysis - Compiler Design Lab, Saarland ...

In our Example:

0

1

7

8

6

5

4

i = 0;

Pos(i < 42)

Neg(0 ≤ i < 42)

i = i+ 1;

Neg(i < 42)

M [A1] = i;

A1 = A+ i;

2

3

Pos(0 ≤ i < 42)

I1 = {1} or:

I2 = {2} or:

I3 = {3}

211

Page 212: Static Program Analysis - Compiler Design Lab, Saarland ...

The Analysis with I = {1} :

0

1

7

8

6

5

4

i = 0;

Pos(i < 42)

Neg(0 ≤ i < 42)

i = i+ 1;

Neg(i < 42)

M [A1] = i;

A1 = A+ i;

2

3

Pos(0 ≤ i < 42)

1 2 3

l u l u l u

0 −∞ +∞ −∞ +∞

1 0 0 0 +∞

2 0 0 0 41

3 0 0 0 41

4 0 0 0 41 dito

5 0 0 0 41

6 1 1 1 42

7 ⊥ ⊥

8 ⊥ 42 +∞

212

Page 213: Static Program Analysis - Compiler Design Lab, Saarland ...

The Analysis with I = {2} :

0

1

7

8

6

5

4

i = 0;

Pos(i < 42)

Neg(0 ≤ i < 42)

i = i+ 1;

Neg(i < 42)

M [A1] = i;

A1 = A+ i;

2

3

Pos(0 ≤ i < 42)

1 2 3 4

l u l u l u

0 −∞ +∞ −∞ +∞ −∞ +∞

1 0 0 0 1 0 42

2 0 0 0 +∞ 0 +∞

3 0 0 0 41 0 41

4 0 0 0 41 0 41 dito

5 0 0 0 41 0 41

6 1 1 1 42 1 42

7 ⊥ 42 +∞ 42 +∞

8 ⊥ ⊥ 42 42

213

Page 214: Static Program Analysis - Compiler Design Lab, Saarland ...

Discussion:

• Both runs of the analysis determine interesting information,

• The run with I = {2} proves that always i = 42 after

leaving the loop.

• Only the run with I = {1} finds, however, that the outer check

makes the inner check superfluous.

How can we find a suitable loop separator I ???

214

Page 215: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea 3: Narrowing

Let x denote any solution of (1) , i.e.,

xi ⊒ fi x , i = 1, . . . , n

Then for monotonic fi ,

x ⊒ F x ⊒ F 2 x ⊒ . . . ⊒ F k x ⊒ . . .

// Narrowing Iteration

215

Page 216: Static Program Analysis - Compiler Design Lab, Saarland ...

Idea 3: Narrowing

Let x denote any solution of (1) , i.e.,

xi ⊒ fi x , i = 1, . . . , n

Then for monotonic fi ,

x ⊒ F x ⊒ F 2 x ⊒ . . . ⊒ F k x ⊒ . . .

// Narrowing Iteration

Every tuple F k x is a solution of (1)

==⇒

Termination is no problem anymore:

we stop whenever we want

// The same also holds for RR-iteration.

216

Page 217: Static Program Analysis - Compiler Design Lab, Saarland ...

Narrowing Iteration in the Example:

0

1

7

8

6

5

4

i = 0;

Pos(i < 42)

Neg(0 ≤ i < 42)

i = i+ 1;

Neg(i < 42)

M [A1] = i;

A1 = A+ i;

2

3

Pos(0 ≤ i < 42)

0

l u

0 −∞ +∞

1 0 +∞

2 0 +∞

3 0 +∞

4 0 +∞

5 0 +∞

6 1 +∞

7 42 +∞

8 42 +∞

217

Page 218: Static Program Analysis - Compiler Design Lab, Saarland ...

Narrowing Iteration in the Example:

0

1

7

8

6

5

4

i = 0;

Pos(i < 42)

Neg(0 ≤ i < 42)

i = i+ 1;

Neg(i < 42)

M [A1] = i;

A1 = A+ i;

2

3

Pos(0 ≤ i < 42)

0 1

l u l u

0 −∞ +∞ −∞ +∞

1 0 +∞ 0 +∞

2 0 +∞ 0 41

3 0 +∞ 0 41

4 0 +∞ 0 41

5 0 +∞ 0 41

6 1 +∞ 1 42

7 42 +∞ ⊥

8 42 +∞ 42 +∞

218

Page 219: Static Program Analysis - Compiler Design Lab, Saarland ...

Narrowing Iteration in the Example:

0

1

7

8

6

5

4

i = 0;

Pos(i < 42)

Neg(0 ≤ i < 42)

i = i+ 1;

Neg(i < 42)

M [A1] = i;

A1 = A+ i;

2

3

Pos(0 ≤ i < 42)

0 1 2

l u l u l u

0 −∞ +∞ −∞ +∞ −∞ +∞

1 0 +∞ 0 +∞ 0 42

2 0 +∞ 0 41 0 41

3 0 +∞ 0 41 0 41

4 0 +∞ 0 41 0 41

5 0 +∞ 0 41 0 41

6 1 +∞ 1 42 1 42

7 42 +∞ ⊥ ⊥

8 42 +∞ 42 +∞ 42 42

219

Page 220: Static Program Analysis - Compiler Design Lab, Saarland ...

Discussion:

→ We start with a safe approximation.

→ We find that the inner check is redundant :-)

→ We find that at exit from the loop, always i = 42

→ It was not necessary to construct an optimal loop separator

Last Question:

220