Prev Up Next
Go backward to Announcements
Go up to Top
Go forward to Some Implementation Details

Flow graphs and Basic Blocks

  1. The value numbering scheme does not work in situations where there are loops or conditionals:
    x[i+1] = y;
    while ( i < max ) {
       x [ i+1 ] = x[ i+1 ] + z;
       i = i+1;
    }
    
    In this example, we would assign the same value number to all four instances of i+1, but the assignment statement at the end of the loop means that the instance of i+1 outside the loop will not have the same value as those inside in all cases.
    w = 2*x + 1;
    if ( x > 0 )
       { z = 2*x }
    y = z + 1;
    
    In this example, 2*x + 1 and z + 1 might be common subexpressions, but we can't be sure unless we know whether or not x will be positive.

  2. Examples like this make this notion of "straight line code" important enough to deserve a name. We will call sequences of staight line code "basic blocks".

  3. Giving this term a precise definition in our context is actually a bit difficult.

  4. Our immediate interest in basic blocks will be that they provide a program unit larger than a single statement or expression to which we can apply optimization techniques without expending the effort to deal with control structures.

  5. In the real world, however, basic blocks are also typically used by compilers that do perform global analysis.

  6. In the real world, before optimization, a compiler usually would rewrite the program into a form in which basic blocks and the flow graph were represented explicitly. We don't have the time to do this. Luckly, since we are only interested in using basic blocks to identify sequences of straight line code we can take a simpler approach.

  7. The approach I want you to use depends on two facts:

  8. All we need to do to apply a local optimization algorithm to basic blocks is take the code used to do a "standard" traversal of the syntax tree and figure out where to put the "initialize various data structures" steps.

  9. To make this precise, here are some examples:
    
    void optimizeLogicalExpr( node * expr) {
    
    	optimizeExpr( expr->internal.child[0] );	
    	startNewBlock();
    	optimizeExpr( expr->internal.child[1] );
    	startNewBlock();
    }
    
    void optimizeIf( node * stmt ) {
    	optimizeExpr( stmt->internal.child[0] );
    	startNewBlock();
    	optimizeStmtList( stmt->internal.child[1] );	
    	startNewBlock();
    	optimizeStmtList( stmt->internal.child[2]);
    	startNewBlock();
    }
    
    
    
    void optimizeStmt( node * stmt ) {
    
        switch (stmt->internal.type ) {
        case Nif:
    	optimizeIf( stmt );
    	break;
        case Nwhile:
    	optimizeWhile( stmt );
    	break;
        ...
    }
    
    
    void optimizeStmtList( node * slist ) {
    	visitlist( slist, optimizeStmt, 0);
    }
    
    

  10. We can also take advantage of the fact that our goal is local optimization by working with a slightly looser definition of basic blocks.

Computer Science 434
Department of Computer Science
Williams College

Prev Up Next