CS 453 Programming Assignment #6 — Code Generation
Due Wednesday, May 7th (by 11:59pm)
early and late policy
Introduction
You will now make your MiniJava compiler generate MIPS code that is
wrapped in the Assem interface. Using the Assem interface makes it possible
to generate MIPS code where the temporaries have not yet been mapped to
registers. We will be providing you with a pass that handles register allocation.
You can generate the list of Assem instructions for the MiniJava program
anyway you want. The test will be whether the generated MIPS code generates
the same result as the java virtual machine run on the same MiniJava
input file.
CodeGenDriver
The CodeGenDriver creates a MipsCodeGen visitor that visits all of the nodes in the AST and generates MIPS code wrapped in the Assem interface,
which is then returned via a call to getInstrs().
These instructions are then printed to a MIPS assembly file along with
the MIPS instructions for the MiniJava runtime library (frame.programTail()).
Your compiler can generate the list of Assem.Instr in any way. You
can even modify the driver.
Getting Started
Download the MJCodeGenStart.tar
to get all the files that we are providing for this project.
- CodeGenTest/
- See the README in this directory to see how you can test your compiler against the Sun Java compiler automatically.
- src/CodeGenDriver.java
- Possible driver for PA6.
- src/Assem/*
- The Assem subdirectory contains all of the Assem instruction classes.
- src/Frame/Access.java
- This interface now includes genStore() and genLoad() methods for generating stores and loads of variables. If any VarSTE (MemberSTE or LocalSTE) contains an Access reference, then they can be asked to generate
code that loads the variable value into a register or stores a register value into the variable.
- src/Frame/Frame.java
- Abstract interface for architectural specific information and
stack frame info. Some suggested methods have been added to the interface.
- src/Mips/InFrame.java, InHeap.java
- These classes implement genStore() and genLoad().
- src/Mips/SpillAllRegAlloc.java
- The spillAll method will spill all Temps not in the provided spillSet.
The result is a new list of Assem instructions.
- src/symtable/
- All VarSTEs now have an Access instance. This includes MemberSTEs, which used to just have an offset.
- srcTestCases/*
- Some test cases and the reference compiler output.
We recommend the following steps for starting the code generation phase of the MiniJava compiler:
- Implement the run-time library routines in MIPS and test them. Some of them should look familiar. After they have been implemented and tested, have the programTail() routine in the MipsFrame class return a string with their implementations so they can be easily appended to the end of any MIPS file. Here are the comments for the runtime library routines.
#####################
# _initArray
# pass a ptr to array on stack at 0($fp)
# pass the size of the array in terms of elements on stack at -4($fp)
# This routine puts the number of elements in the first word of the array
# and then initializes all of the elements in the array to zero.
#####################
#####################
# _printint
# pass number to print on stack to be found at 0($fp)
#####################
#####################
# _halloc
# pass number of bytes to allocate on stack at 0($fp)
#####################
- Start with a MiniJava input program that only has a main and that main prints a constant.
class PrintConst {
public static void main(String[] args) {
System.out.println(42);
}
}
Figure out how to automatically generate the epilogue and prologue for main and then generalize that to any function. The prologue and epilogue should be added to the front and end of the list of statements for each function/method (i.e., in the outMethod() routine). Here is the MIPS epilogue and prologue from the reference compiler for the above example:
# prologue start
.text
main:
main_framesize=16
main_paramsNregsaves=8
sw $ra, 0($sp)
subu $sp, $sp, 4
sw $fp, 0($sp)
subu $sp, $sp, 4
addu $fp, $sp, main_paramsNregsaves
subu $sp, $fp, main_framesize
# prologue end
# ...
# epilogue start
done1:
lw $ra, 0($fp)
move $t0, $fp
lw $fp, -4($fp)
move $sp, $t0
jr $ra
# epilogue end
The above code runs in spim just fine.
- Implement the generation of Assem.Instr for integer expressions and
the print statement. Here are some examples of how to generate Assem.Instrs.
/**
* @param val Immediate value that should be loaded.
* @param comment Inline comment to put in the generated code.
* @param ilist List of instructions that the load instruction(s) are added to.
* @return The Temp where the loaded value will be put.
*/
private Temp genLoadImm(int val, String comment, List ilist) {
Temp retval = new Temp();
ArrayList destlist = new ArrayList();
destlist.add(retval);
ilist.add(new Assem.OPER("\tli `d0, " + val + "\t# " + comment + "\n",
destlist, null));
return retval;
}
- Next add some debugging output that prints the instructions before register allocation. For the above, the reference compiler generates the following debug output:
-------------------
main
li t32, 42 # IntegerExp
# PrintStatement
# push parameter onto stack
sw t32, 0($sp)
subu $sp, $sp, 4
jal _printint
move t33, $v0 # store return value to Temp
Notice the temporaries t32 and t33. Also notice that comments in the generated MIPS code could be quite helpful for debugging.
- Finally call the spill all register allocator on the instructions for the body of the function/method BEFORE adding the prologue and epilogue.
In spill all, the temporaries will be replaced with registers and everything spilled to memory.
// perform spillAll on body of the function
// need the method's frame
MethodSTE methodInfo = (MethodSTE)this.mCurrentST.lookup(node.getName().getText());
Set noSpillSet = new HashSet();
noSpillSet.add(mFrame.FP());
noSpillSet.add(mFrame.SP());
noSpillSet.add(mFrame.RV());
noSpillSet.add(mFrame.RA());
List instrs
= (new Mips.SpillAllRegAlloc((Mips.MipsFrame)methodInfo.getFrame())).spillAll(
noSpillSet, mFrame.tempRegSet(),this.mNoregInstrList);
# Body of main after spill all
li $t0, 42 # IntegerExp
sw $t0, -8($fp)
# PrintStatement
lw $t0, -8($fp)
# push parameter onto stack
sw $t0, 0($sp)
subu $sp, $sp, 4
jal _printint
move $t1, $v0 # store return value to Temp
sw $t1, -12($fp)
At this point, you should be able to add code generation for one ast node at
a time and test the MIPS output with spim as you go. We will also be covering various aspects of the code generation in class.
Here are the other MIPS instructions you will need.
.text
L1:
beq $t0, $t1, L1
bne $t0, $t1, L1
blt $t0, $t1, L1
j L1
jal L1
jr $ra
sw $t0, 4($t1)
lw $t0, -8($sp)
li $t0, 42
move $t0, $t1
subu $t0, $t1, 8
and $t0, $t1, $t2
sub, mul, xor, add
Extra Credit Possibilities
The MIPS code generated after spilling most of the temporaries
to memory will be very inefficient. Here are two possible things you can
do to reduce the number of instructions executed. Any extra points
earned from implementing one of these optimizations will be added to your
final exam. You need to indicate that you have implemented extra credit
in your README file and show how the optimization has reduced the number of static and dynamic instructions. You can do a dynamic instruction count
by using "spim -keepstats".
Removing Unnecessary Jumps
This optimization is worth 2 points. It could be done by doing a pass
over the list of Assem instructions before the code that prints the
instructions to a file. Any jump directly followed by the label it is
jumping to can be removed.
Register Allocation for Expression Trees
This optimization is worth 5 points.
Chapter 8.10.3 in the book describes how to do register allocation for
expression trees.
Other Optimizations
Propose an optimization in an email, and I will gladly assign a point value.
Implement Inherited Fields
This is worth 3 points. You need to include a test case that shows this works.
Implement Polymorphism
This is worth 5 points. You need to include a test case that shows this works.
Taking advantage of Early Policy and Extra Credit
If you do write a version of the code generator that removes unnecessary
jumps and/or does register allocation for expression trees, you should
turn in a generic version of the code generator as early as possible and
name it "PA6_username.tar", then later turn in a "PA6_username_extra.tar".
Make sure to indicate in your README file what optimizations you did and
where Alan should look in your code to see how the optimization is implemented.
The Assignment
Write a code generator that can generate MIPS Assem instructions
given the AST and a SymTable.
Use spillAll to generate MIPS code with
register assignments.
The resulting MIPS code should be in the file inputfile.s.
The output of "spim inputfile.s" should match the output of compiling and
running the inputfile with the normal JVM.
All the features in the MiniJava grammar should be implemented with the exception of inheritance: accesses to inherited member variables, assignment of a subclass
reference to a parent class reference, and virtual method calls.
The semantics of each statement in MiniJava is the same as Java except that
System.out.println() can only be applied to integers and the parameter to main
should be ignored.
Submitting the Assignment
-
Make sure you test your implementation thoroughly.
- Create a jar file that contains all the source code, the .class files, and a README file. We should be able to use the jar file to run your MiniJava compiler.
- Create a mainClass.txt file in MJCompiler-groupname/src/ with the following contents (make sure there is an newline after the second line in the file):
Main-Class: CodeGenDriver
Class-Path: java-cup-11a-runtime.jar
- Also put the subversion.txt and README files into the src/ subdirectory.
- While in the src/ subdirectory, do the following commands:
% javac -classpath .:mjparser/java-cup-11a-runtime.jar CodeGenDriver.java
% jar cmf mainClass.txt MJCompiler-groupname.jar *.class */*.class */*/*.class *.java */*.java */*/*.java README
-
The README file should include your username(s), emails(s), and any
information you want the TA to have before grading your assignment.
e.g., this part is not working due to..., a favorite quote or joke
to amuse the TA, etc.
Submit assignment using checkin utility
~cs453/bin/checkin PA6 MJCompiler-groupname.jar
Sanity Check (procedure TA will use to run your assignment):
% java -classpath java-cup-11a-runtime.jar -jar MJCompiler-groupname.jar filename.java
% mkdir MJCompiler-groupname/src/
% cd MJCompiler-groupname/src/
% cp ../../MJCompiler-groupname.jar .
% jar xf MJCompiler-groupname.jar
Note that you need to have a copy of the java-cup-11a-runtime.jar file in the same directory as the MJCompiler-groupname.jar file. We will provide our own copy of the runtime jar file for testing. We will be running your compiler on multiple test files. Also, the TA will be looking at the source files.
Early and Late Policy
For PA6, each day the assignment is turned in early, you will earn an extra 2% on the assignment.
Late assignments will be accepted up to 24 hours past the due
date for a deduction of
20% and will not accepted past this period.
mstrout@cs.colostate.edu
.... April 16, 2008