Académique Documents
Professionnel Documents
Culture Documents
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
int n;
struct node *l;
q = malloc(sizeof(struct node));
q->next=nil; q->value=n; p->next=q; } }
Procedure Integration
Some programming languages allow user annotations (C++, ada) How to decide when to inline:
Single vs multiple compilation units
Multiple programming languages
What about recursive procedures? Code size Cache effect and register pressure Other compiler assumptions
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
r1r8 + 2
r1 r1 + r2
restore r8-r15
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;
switch (t1) {
case APPLE: t3= Red; break; case BANANA: t3=Yellow; break; case ORANGE: t3=Orange; }} printf(%s\n, t3);}
snack.variety = APPLE;
snack.shape = ROUND; printf(%s\n, color(&snack));}
g=100;
p(); y = g;
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
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
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
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
2:
3: 4:
g(f2);
f3 := f2 ; g(f3);
end
begin (* main) 5: a := 5; 6: f(a, b); end.
b = & y;
if () y = & z; else y = &x;
c = &y ;
*c = t ;
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
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?