Set up your GitHub classroom repository for step 3 using the link on Brightspace. Don't forget to use the same team name you used in step 2!
Then clone your repository as directed in the cloning and submitting instructions
We now have a compiler that can generate assembly for straight line code: a sequence of arithmetic operations, assignment statements, and input and output statements. But for a program to do anything useful, it cannot just be straight-line code. It needs to support control flow: the ability to change what code executes based on the dynamic execution of the program.
In this step, we will implement code generation for the two control flow constructs in uC: if statements and while loops.
The key to implementing control flow is being able to change the flow of execution of a program. Normally, the machine (or, in this case, our simulator) will execute a program by walking through the instructions one by one, in order. But there are several RISC-V instructions that can change this behavior. Consider the following code:
geti r1
geti r2
blt r1, r2, l0
puti r1
j l1
l0:
puti r2
l1:
This code implements, roughly, the following piece of uC:
read(x);
read(y);
if (x < y) {
print(x)
} else {
printy(y)
}
The way it does so is through the blt and j instructions. The blt instruction is a conditional branch: if r1 < r2, then the branch will jump to label l0 in the code (and in doing so, will skip executing puti r1). The j instruction is an unconditional jump: it will immediately jump to label l1 in the code (and in doing so, will skip executing puti r2).
Risc-V provides 6 different conditional branch instructions for integers:
bltfor "branch if less than"blefor "branch if less than or equal to"bgtfor "branch if greater than"bgefor "branch if greater than or equal to"beqfor "branch if equal to"bnefor "branch if not equal to"We have provided Java classes to model all of those instructions, as well as the unconditional jump,
j.Risc-V does not provide conditional branch instructions for floating point. Instead, it provides comparison operations that take the form:
<cmp> dst, src1, src2with the semantics:
dst = (src1 <cmp> src2) ? 1 : 0. This can be combined with integer branch instructions to branch based on floating point comparisons.The three comparison operators are:
feq.sfor==flt.sfor<fle.sfor<=Note that you can synthesize the other three comparisons by changing the polarity of the branch.
We already know how to generate code for a list of statements. Generating code for if statements and while loops involves stitching together the "bodies" of those constructs (the then/else blocks for if statements, and the loop body for while loops) with carefully chosen labels, branches, and jumps. See the lecture notes for an explanation of how this works.
We want you to fill in the rest of CodeGenerator and MicroC.g4 to build an AST for if statements and while loops, then generate code for them.
The key challenges you will have to face in this step are:
- Generating unique and appropriate labels for control constructs.
- Generating code for the conditional expressions (
CondNode) in the control constructs. You have to take care to generate the right kind of branch, and the right labels for those branches. The notes in the starter code give some explanation for how to do that.
Note: don't forget to handle the case where an
ifstatement does not have anelseblock!
To run your code, you can use the Risc simulator we have provided for you. This can be set up as a submodule in your git repo, which you can initialize by running:
> git submodule add https://github.com/milindkulkarni/RiscSim.git
Which will put the simulator code in the RiscSim directory.
Because we are adding this code as a submodule, if we ever update the simulator, you can simply run
git pullin theRiscSimdirectory to get the updated simulator
You can run an assembly file by running:
> python3 RiscSim/driver.py [assembly file]
The test inputs we will use to test your program are in tests. We also have sample outputs in outputs. While your compiler may not produce exactly the same assembly as in outputs, the code you generate should produce the same results.
-
All of the necessary code for building your compiler.
-
A Makefile with the following targets:
compiler: this target will build your compilerclean: this target will remove any intermediate files that were created to build the compiler
-
A shell script (this must be written in bash, which is located at
/bin/bashon the ecegrid machines) calledrunmethat runs your scanner. This script should take in two arguments: first, the input file to the compiler and second, the filename where you want to put the compiler's output. You can assume that we will have runmake clean; make compilerbefore running this script, and that we will invoke the script from the root directory of your compiler.
While you may create as many other directories as you would like to organize your code or any intermediate products of the compilation process, both your Makefile and your runme script should be in the root directory of your repository.
Do not submit any binaries. Your git repo should only contain source files; no products of compilation.
See the submission instructions document for instructions on how to submit. You should tag your step 2 submission as submission