CS 701, Program #4

CS 701, Program 4
Graph Coloring Register Allocation

Due: Friday, December 12, 2014 (by midnight)

Not accepted after 11:59 pm on Wednesday, December 17, 2014

Overview

For this project you'll write one LLVM pass that does register allocation via graph coloring. You only need to handle code for which all live ranges get colored; handling uncolored live ranges (i.e., spilling) will be worth extra credit.

The LLVM pass that you write will be a MachineFunction pass, with this header:

You can find skeleton files in

You should copy that directory tree to your own machine.

Type make to build the skeleton version of the register allocator. To run it (on a file called xxx.c), either type make XXX.reg, or:

    clang -emit-llvm -O0 -c xxx.c -o xxx.bc
    opt -mem2reg xxx.bc > xxx.ssa
    mv xxx.ssa xxx.bc
    llc -optimize-regalloc=0 -load Debug/lib/P4.so -regalloc=gc xxx.bc
Until you implement all of register allocation, running your register allocator will produce only debugging output (see below).

Your MachineFunction pass will include the following steps:

  1. For each register class RC, get the set of physical registers that can be allocated to RC.
  2. Do reaching-definitions and live-variable analyses.
  3. Compute initial and final live ranges.
  4. Build the interference graph.
  5. Color the nodes of the interference graph.
  6. For each live range that is for a virtual register V, and that has an allocated physical register P:
    1. replace all instances of V in the live range with P, and
    2. save and restore P across all calls in the live range.
  7. For extra credit:
Steps 1 and 2 are provided for you in the skeleton file. Steps 4-7 are explained in more detail below.

Step 4: Build the interference graph.

When building the interference graph, two (final) live ranges should be considered to interfere with each other iff

  1. Their sets of CFG nodes are non-disjoint, and
  2. Their sets of available registers plus the aliases of those registers are non-disjoint. Note that the live range for a physical register (a preg) has only itself as its "available register". The live range for a virtual register (a vreg) has as its "available registers" all of the physical registers that can be allocated to the register class of which that vreg is a member. (See Some Useful LLVM Operations below for how to get the register class of a vreg and how to find the aliases of a preg.)

Step 5: Color the interference graph.

Color the graph using the technique discussed in class: Iterate over all nodes of the graph (all final live ranges), pushing them onto a stack until all nodes are pushed, then pop all nodes, coloring them if possible.

Pushing nodes

Push all nodes that represent virtual registers before pushing any nodes that represent physical registers. First push all "easy" nodes: nodes that represent live ranges for virtual registers V whose number of unstacked neighbors is less than the number of registers available for V's register class.

NOTE: Do not double-count physical-register neighbors. If a virtual register has two or more neighbors that are live ranges for the same physical register, only count that as one unstacked neighbor.

If there are no "easy" nodes, push a node that represents a live range for a virtual register V that has a maximal number of unstacked neighbors -- don't do the extra work of computing the costs of not coloring live ranges. Again, don't double-count physical-register neighbors.

Note that pushing a node (whether easy or not) can cause other nodes to become easy.

Popping nodes

When a node that represents a physical register's live range is popped, its color should be the number of the physical register itself.

When a node N that represents a virtual register's live range is popped, its color should be a physical register number R such that the set that contains R and R's aliases does not overlap with the set that contains the registers allocated to N's popped neighbors and those registers' aliases. If there is no available register give an error message and quit. If you are doing extra credit, see below for how to handle uncolored live ranges.

Step 6(a): Replace instances of virtual registers with the appropriate physical register.

Given MachineFunction Fn and a pointer to an instruction inst whose k-th operand is a virtual register, replace that operand with physical register P using this code:

  const TargetRegisterInfo *TRI = Fn.getTarget().getRegisterInfo();
  MachineOperand &MOp = inst->getOperand(k);
  MOp.substPhysReg(P, *TRI);
(Note that TRI is constant across each function, and is used in several ways, so you might want to define it as a global and set it once at the start ofrunOnMachineFunction.)

Step 6(b): Handle Function Calls

You will need to add code to save the contents of physical registers across function calls. You can do this as follows:

  1. When creating a live range, keep track of whether the range includes a call instruction. Given variable in, a pointer to a MachineInstr, use the following code to test whether the instruction is a call:

    const MCInstrDesc &MCID = in->getDesc();
    if (MCID.isCall()) ...
    
  2. For each colored live range (for a virtual register V) such that the live range includes a call:

Step 7: Handling Uncolored Live Ranges (Extra Credit)

To earn extra credit, you may augment your register-allocation pass to handle uncolored live ranges. The necessary steps are described below.

Setting aside Spill Registers

This step must be done before coloring the interference graph. For each register class, you will need to find 3 physical registers that are in the set of available registers for that class and do not occur explicitly in the program. Those 3 registers and all of their aliases must be removed from the available sets for all register classes. To avoid very poor use of registers (and risking not being able to complete register allocation), when you choose a spill register for one class, you should try to choose aliases of that register as the spill registers of other classes.

The Spill Registers will be used to replace instances of virtual registers in uncolored live ranges (we will call those "uncolored virtual registers"). Note: This discussion assumes that no machine instruction includes more than 3 distinct virtual registers. We believe this to be true. If your code is run on an example for which it is not true, and it causes your register allocator to fail, print an error message and quit. If you find such an example, please inform us.

Handling instances of uncolored virtual registers

After handling all colored live ranges (replacing all instances of the colored virtual register associated with that live range with the allocated physical register) you should handle all uncolored live ranges. To handle one uncolored live range whose associated virtual register is V, do the following steps:

  1. Get stack space for V as follows:

      const TargetRegisterClass *RC = MRI->getRegClass(V);
      int frameIndex = Fn.getFrameInfo()->CreateSpillStackObject(RC->getSize(), RC->getAlignment()); 
    
  2. For each instruction I in the uncolored live range that includes an instance of V, get a Spill Register R for V such that neither R nor any of its aliases already occur in I. If V occurs more than once in I, use the same Spill Register for all such occurrences.

  3. Replace each instance of V in I with Spill Register R using the same substPhysReg method used to replace each instance of a colored virtual register with the allocated physical register.

  4. If V occurs as a use in I (use MOp.isUse()to find out) you must generate code to load the value of V from the stack into your chosen Spill Register R. That code must come immediately before instruction I, and can be generated as follows:

    MachineBasicBlock *MBB = I->getParent();
    TII->loadRegFromStackSlot(*MBB, I, R, frameIndex, TRC, TRI);
    
    where TII and TRI are the same as those values used to save and restore registers across calls; I is (a pointer to) the instruction you're handling, R is the chosen Spill Register, TRC is the TargetRegisterClass for V (as above), and frameIndex is the stack space that you allocated for V.

  5. If V occurs as a def in I, you must generate code to store the defined value into the stack space that you allocated. This code must come immediately after instruction I; i.e., it must come immediately before the instruction that follows I. Here is the code that generates a spill of the value in register R immediately before instruction nextI:

    TII->storeRegToStackSlot(*MBB, nextI, R, true, frameIndex, TRC, TRI);
    

    See the explanation of how to save registers across calls for how to compute nextI.

Some Useful LLVM Operations

You can iterate over all basic blocks in the current MachineFunction Fn, and over all instructions in a basic block like this:

  for (MachineFunction::iterator MFIt = Fn.begin(); MFIt != Fn.end(); MFIt++) {
    for (MachineBasicBlock::iterator MBBIt = MFIt->begin(); MBBIt != MFIt-&ht;end(); MBBIt++) {
      // *MBBIt is a MachineInstr

You can look at all operands of a machine instruction like this:

      int numOp = MBBIt->getNumOperands();
      for (int i = 0; i < numOp; i++) {
        MachineOperand MOp = MBBIt->getOperand(i);

And you can print the whole machine instruction (e.g., for debugging) like this:

      MachineInstr *oneIns = MBBIt;
      oneIns->dump();

To print virtual register numbers that match the ones in the machine instructions, use

to map from the unsigned value of vreg to its index.

You can find out what class a virtual register V is in using the following code:

MachineRegisterInfo *MRI = &Fn.getRegInfo();
int vregClass = MRI->getRegClass(V)->getID() // what class is virtual reg V in

You can find all aliases of preg P in function Fn like this:

const TargetRegisterInfo *TRI = Fn.getTarget().getRegisterInfo();
MCRegAliasIterator *it = new MCRegAliasIterator(P, TRI, false);
while (it->isValid()) {
  // **it has type "unsigned"; i.e., it is a preg that is an alias of P
  ++(*it);
}

Here are more potentially useful methods:

Note that for some instructions, more than one operand is a def (and of course that is also true for uses).

Debugging Output

As usual, you should use the file flags.h to control what output is produced by your program. The skeleton version uses PRINTLIVE and PRINTRD to control whether the results of live analysis and/or reaching-definitions analysis are printed. It also includes a static field DEBUG that controls whether some tracing output and the machine instructions for each function are printed. This debugging output is for your use only; it will not be tested during grading.

Test code with expected outputs can be found in /p/course/cs701-fischer/public/proj4/TESTS.

Print Live Ranges

If PRINTRANGES is defined in flags.h, you should print the initial and final live ranges that you compute. As usual, you will need to format your output so that it is the same as ours when compared using diff -B -b. Note that the ranges for the physical registers are printed first, then the ranges for the virtual registers. Each list of ranges is ordered by the register number, and the sets of instructions within each range are also ordered by their numbers.

If there are multiple ranges for the same (physical) register, sort by the first instruction number in the range. For example, if there are two live ranges for R14, and one range starts with instruction %22 while the other starts with instruction %40, you should print the range that starts with instruction %22 first.

Print the Interference Graph

If PRINTGRAPH is defined in flags.h, you should print the interference graph. For each node (final range), first the nodes for physical registers then those for virtual ones, print the name of the register for that range and its neighbors in the graph. The list of neighbors should include all physical register neighbors first (in sorted order), then all virtual register neighbors (in sorted order).

When printing the interference graph, use the same ordering as above for physical registers that have multiple live ranges. Note that when you print the graph you don't print the instruction numbers, just the registers for the neighboring live ranges, so you won't "see" the sorted order, but it will be consistent with the order used for printing the live ranges themselves.

Clarifications

  1. To be sure that your output matches the expected output, either work on the macaroni machines, or change your (top-level) Makefile to use this call to llc:
      llc -mcpu=generic -optimize-regalloc=0 -load Debug/lib/P4.so -regalloc=gc $*.bc

  2. When computing initial live ranges, be sure to take into account registers used to pass parameters (those registers will be live at the start of the function). The skeleton code is designed so that the global variable argPregSet contains the physical registers used to pass parameters, and that is taken into account in the reaching definitions analysis that the skeleton code does. For example, if R47 is used to pass a parameter, then there will be a definition of R47 at (pseudo) instruction %0.

Submit Your Work

To submit your work, copy your complete development directory (after make clean) to your handin directory

~cs701-1/HANDIN/YOUR-LOGIN/P4

using your actual login in place of YOUR-LOGIN. If you are working with a partner, only one of you should submit your work. Inform us if you have changed who you're working with.