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:
bool runOnMachineFunction(MachineFunction &Fn)
You can find skeleton files in
/p/course/cs701-fischer/public/proj4/SKELETON
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:
When building the interference graph, two (final) live ranges should be considered to interfere with each other iff
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.
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.
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.
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
.)
You will need to add code to save the contents of physical registers across function calls. You can do this as follows:
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()) ...
For each colored live range (for a virtual register
V
) such that the live range includes a call:
Allocate stack space in which to save the allocated physical
register R
across the call (do this just once for
each live range that includes a call):
MachineRegisterInfo *MRI = &Fn.getRegInfo(); const TargetRegisterClass *RC = MRI->getRegClass(V); int frameIndex = Fn.getFrameInfo()->CreateSpillStackObject(RC->getSize(), RC->getAlignment());
For each call instruction I
in the live range:
Generate code to store the current contents of the allocated preg
R
before the call
// do these two assignments just once per function const TargetRegisterInfo *TRI = Fn.getTarget().getRegisterInfo(); const TargetInstrInfo *TII = Fn.getTarget().getInstrInfo(); // do this for each call instruction I MachineBasicBlock *MBB = I->getParent(); TII->storeRegToStackSlot(*MBB, I, R, true, frameIndex, RC, TRI);
MachineBasicBlock::iterator IT = I; IT++; TII->loadRegFromStackSlot(*MBB, IT, R, frameIndex, RC, TRI);Note that this works even if
I
is the last instruction
in its basic block.
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.
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:
const TargetRegisterClass *RC = MRI->getRegClass(V); int frameIndex = Fn.getFrameInfo()->CreateSpillStackObject(RC->getSize(), RC->getAlignment());
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.
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.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.
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
.
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
TargetRegisterInfo::virtReg2Index(vreg)
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:
bool MOp.isReg() // true iff this operand is a register (physical or virtual)
unsigned MOp.getReg() // get the register
bool MOp.isDef() // true iff the operand is a def
bool MOp.isUse() // true iff the operand is a use
bool TargetRegisterInfo::isVirtualRegister(reg) // true iff reg is a vreg
bool TargetRegisterInfo::isPhysicalRegister(reg) // true iff reg is a preg
Note that for some instructions, more than one operand is a def (and of course that is also true for uses).
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
.
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.
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.
llc -mcpu=generic -optimize-regalloc=0 -load Debug/lib/P4.so -regalloc=gc $*.bc
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.
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.