You could have a look at Siskind's paper, Flow-Directed Lightweight Closure Conversion. Warning, it's not exactly light reading:
ftp://ftp.ecn.purdue.edu/qobi/fdlcc.pdf
Although the subject is nominally closure conversion, he describes how flow analysis is used to propagate information about values around the compiler's model of the program. Because the analysis is on a whole program basis it need not be very conservative - in a whole program compiler you never have to give up and do something the slow way because a value might be used in a place you can't see.
This aggressive flow analysis gives the compiler enough information to pick nice flat C-like representations, inline objects inside other objects, use more specialised calling conventions, etc.
For example, a whole program compiler might determine that the elements of an array are never compared with eq or have their mutable parts assigned to, which could allow the elements to be inlined into the array. Doing this is tough or impossible in a traditional compiler because the contents of files that have yet to be compiled is unknown, forcing the compiler to assume the worst.
This aggressive flow analysis gives the compiler enough information to pick nice flat C-like representations, inline objects inside other objects, use more specialised calling conventions, etc.
For example, a whole program compiler might determine that the elements of an array are never compared with eq or have their mutable parts assigned to, which could allow the elements to be inlined into the array. Doing this is tough or impossible in a traditional compiler because the contents of files that have yet to be compiled is unknown, forcing the compiler to assume the worst.
Pretty neat stuff.