Vous êtes sur la page 1sur 28

Procedure Optimizations and Interprocedural Analysis

Outline
Modularity Issues Procedure optimizations Interprocedural Optimizations Challenges The Call Graph Flow insensitive information Flow sensitive information Conclusions

Modularity Issues
Procedures provide a mechanism for modularity Procedure bodies become smaller Machines becomes stronger Often procedures implement general algorithms How to achieve performance of a single procedure in a complex software?
procedure integration/inline/tail call elimination interprocedural analysis

Two solutions:

Procedure Optimizations
Improve the code of a procedure
Reduce procedure overhead Enables other intraprocedural optimizations

Examples
Tail-call elimination and Tail-Recursion elimination Procedure integration In-Line Expansion Leaf-Routine Optimization Shrink Wrapping

Tail-Call and Tail-Recursive Elimination


void make_node(p, n) struct node *p; int n; { struct node *q; { if (n>l->value) if (l->next==nil) make_node(l, n); void insert_node(n, l)

int n;
struct node *l;

q = malloc(sizeof(struct node));
q->next=nil; q->value=n; p->next=q; } }

else insert_node(n, l->next);

Procedure Integration
Some programming languages allow user annotations (C++, ada) How to decide when to inline:
Single vs multiple compilation units
Multiple programming languages

Intermediate level Code improvement criteria


Call cites in nested loops
Enables other optimizations

Profiling Constant parameters

What about recursive procedures? Code size Cache effect and register pressure Other compiler assumptions

Typical Heuristics for Procedure Integration


The size of procedure body The number of calls to this procedure Is the procedure is called inside loop? Whether a call includes constant parameters? Use both static analysis and profiling (if available) Interprcedural analysis will lead to better results

Name Capture in Procedure integration


g(b, c) int b, c; { int a, d; f() { int a, e, d; a = 2; a = b + c;

a = b + c;
d= b * c; return d; } f() { int a, e; a = 2; e = g(3, 4); printf(%d\n, a); }

d = b * c;
e = d; printf(%d\n, a);

In-Line Expansion
Substitute low level code (assembly level) Increase opportunity for using machine capabilities Poor mans procedure integration Two essential mechanisms:
Define templates of machine sequences A compiler inliner

Leaf-Routine Optimization
Do not call other routines In practice half of the routines! Eliminate prolog and epilog

Shrink Wrapping
Move prolog and epilog into the place where it is necessary or remove The general idea
Move prolog forward Move epilog backward

Requires data-flow analysis

Shrink Wrapping (Example)


save r8-r15 r2 <= 10 Y r1 r1 + 1 N r8 r2 1

r1r8 + 2
r1 r1 + r2

restore r8-r15

Wrap-Up (Chapter 15)


Whole procedure optimization Applied early Allows other optimization Most do not require data-flow analysis

Interprocedural Optimizations
Can be used for procedure integration Constant propagation can be used to optimize procedure bodies Constant propagation can be used to clone procedures Call-by-value parameters can be passed by reference (if they dont change) Register allocation Improve intraprocedural information

char * Red = red; char * Yellow = yellow; char * Orange = orange; char * color(FRUIT CurrentFruit); { switch (currentFruit->variety) { case APPLE: return Red; break;

char * Red = red; char * Yellow = yellow; char * Orange = orange; main() { FRUIT snack;

VARIETY t1; SHAPE t2; COLOR t3;


t1 = APPLE;

case BANANA: return Yellow; t2 = ROUND; break;

switch (t1) {
case APPLE: t3= Red; break; case BANANA: t3=Yellow; break; case ORANGE: t3=Orange; }} printf(%s\n, t3);}

case ORANGE: return Orange; }} main() { FRUIT snack;

snack.variety = APPLE;
snack.shape = ROUND; printf(%s\n, color(&snack));}

Pascal Example with value parameters


type vector = array[11000] of integer procedure p(v: vector); procedure q; var a: vector; p(a);

C Example For Constant Propagation


int g; p() { } q(){

g=100;
p(); y = g;

Challenges in Interprocedral Analysis


Handling recursion Parameter passing mechanisms Hidden calls
Virtual methods Function pointers Procedural parameters Higher order functions

Scalability Supporting separate compilation mode

The Call Graph


A finite directed multi-graph A node per procedure A labeled edge per call site Can be constructed incrementally to support separate compilation mode Difficult to construct in the presence of hidden calls

Example for Call Graph Construction


1: void f() { 2: 3: 4: g(); g(); h();}

5: void g() {
6: h(); 7: i(); } 8: void h() {} 9: void i() { 10: g() ;}

Obstacles
Procedural parameters (P-SPACE hard) Higher order functions Virtual methods Solutions
Conservative approximation Data-Flow information Specialization

Flow insensitive side effect analysis


Ignore control flow Compute for every call site:
MOD - the variables that may be modified DEF - the variables must be defined USE - the set of variables that may be used before set

Can be computed efficiently for programs with small number of parameters (Cooper & Kennedy) Can be used for:
program understanding replacing value by reference parameter improving intraprocedural information

Becomes tricky with nesting and aliases

program test; var a. b: integer; procedure g(var f1: integer) begin 1: f1 := f1 + 1; end procedure f(var f2, f3: integer) begin

site f, 2 f, 4 main, 6

mod f2,a f3, b a, b

2:
3: 4:

g(f2);
f3 := f2 ; g(f3);

end
begin (* main) 5: a := 5; 6: f(a, b); end.

program test; var a. b: integer; procedure g(var f1: integer) begin 1: f1 := f1 + 1; end procedure f(var f2, f3: integer) begin

site f, 2 f, 4 main, 6

use f2,a f3, b a, b

2:
3: 4:

g(f2);
f3 := f2 ; g(f3);

end
begin (* main) 5: a := 5; 6: f(a, b); end.

Flow insensitive points-to analysis


Analyze C pointers Find if a pointer a may-point to a pointer b Efficient conservative solutions exit
a = &x;

b = & y;
if () y = & z; else y = &x;

c = &y ;
*c = t ;

Flow Sensitive Data-Flow Information

Integrate control flow graph and call graph (the program supergraph) In the presence of reference parameters even bit vector problems are hard Two main solutions:
call strings functional

Scaling is an issue

Non trivial constants


int x void p(int a) { int c; scanf(%\d, &c); if (c > 0) { a = a -2; p(a); a := a +2; } x := -2 * a + 5 printf(%s\n, x);} void main() { p(7); printf(%s\n, x) ; }

Conclusion
Interprocedural analysis will be integrated into future compilers Can be implemented at link time
Register allocation

Will lead to simpler programming Flow insensitive analysis scales But what about flow sensitive?

Vous aimerez peut-être aussi