CS553 Colorado State University ========================================== Intro to data-flow analysis: Liveness and Intro to register allocation ========================================== 9/10/09 -------------------- Data-flow analysis - how many registers needed? --> have students determine fewest number of names needed for values - assumptions? - values are read before being written ---------------------- Liveness analysis - things that need to be defined (start list on the board) program point future program point - idea: profile all variable defines and uses on a previous run and then use that information to allocate registers - why won't this work? - We perform liveness analysis on a control flow graph of each procedure. ---------------------- Control Flow Graphs - control flow graphs represent possible execution order between statements --> what loop construct did this control flow graph come from? ---------------------- Liveness by Example - discuss the live range for b --> what is the live range for a and c? When we see that a variable is used, we know that itŐs live as we move BACKWARDS in the flow graph. When we see that a variable is assigned into, we know that itŐs not live as we move BACKWARDS in the flow graph. ------------------------- More precise definition of liveness (Slides 8-11) The concept of uses and defs provides a more precise definition of liveness. --> what about dereferences to pointers and array accesses? ------------------------- Computing Liveness (Slide 11-13) - there is a set of data-flow equations that describe the whole program - they should all be satisfied - want to minimize size of in and out sets --> write set of liveness data-flow equations for the example --> use the iterative solver algorithm to solve the equations ------------------------- Liveness Analysis in the MiniJava compiler Current steps -parse into an AST -visit AST to allocate space on the stack for locals and space in the heap for member variables -visit AST to generate MIPS code To perform data-flow analysis ... -need 3 address code like intermediate representation -each instruction will need to know when defining or using a temporary, local, or parameter -have expressions read from and assign to temporaries instead of using the stack -will need to create control flow graph with each 3-address code instruction as a node Example of MiniJava AST to 3-address code --> see Expr3AddressExample.java and AST prologue param = *p where p points at location on stack for param and param is now considered a temporary possible representation: "ld `d0, XX($fp)", deflist: param body of method t1 = 5 possible representation: "li `d0, 5", deflist: t1 local = t1 where local is now considered a temporary possible representation: "move `d0, `s0", deflist: local uselist: t1 t2 = 2 ... ------------------------- Register Allocation --> show architectural registers (ISA) for MIPS - loads and stores lw $t0, -12($fp) add $t0, $t0, $t0 sw $t0, -12($fp) - moves, if a variable is assigned to one register in part of the program and another register somewhere else, then might require a move --------------------------- Scope of register allocation Expression a = x + y + 1 li t80, 1 add t81, t63, t80 add t82, t71, t81 move t72, t82 - can use single register $t0 in place of all temporaries t8* Local, within a basic block - any upward exposed uses must be loaded from memory - any downward exposed defs must be stored to memory a = x + y b = a ... b used later in flow graph, but a not defined later in flow graph Loop, all the basic blocks of a loop - good to keep iterator in register, for example - again need to determine upward exposed uses and downward exposed defs ----------------------------- Granularity --> make three diagrams of different symbolic registers - we are going to focus on variable granularity even though the other two are probably more common, variable is easier to implement ----------------------------- Global register allocation - interference graph is one way to represent a relation that contains node pairs, (n1,n2) in relation --> what does symmetric, reflexive and transitive mean? --> how does knowing these help in implementation? ---------------------------- Interference graph example (slide 19) --> when are the different variables live? --> which variables have interfering live ranges? - var being defined interferes with all variables that are live out ---------------------------- Computing the Interference Graph --> why is the special treatment of moves better? ---------------------------- Allocating Registers using the Interference Graph Assign a single register to all nodes with the same color. --> after constructing the whole interference graph, what could we do with variables involved in copies that could improve register allocation even further? ---------------------------- Spilling (slides 22-24) Spilling means the symbolic register or temporary being spilled will be allocated a spot in memory (e.g. the stack). When used that variable will be loaded from memory and placed into the symbolic register. When defined the symbolic register will be written to memory. In example: After spilling b have two options: - recreate graph (option shown here) and attempt to recolor - keep some registers back to implement spill code and only color graph once ---------------------------- Greedy algorithm for coloring (slides 25-27) The number of registers needed for spills depends on the number of register operands required for a given instruction. ------------------------ mstrout@cs.colostate.edu, 9/10/09