Commit 891adee4 authored by Christophe Dubach's avatar Christophe Dubach

Part 3 instructions released

parent 66e794ba
This diff is collapsed.
......@@ -6,7 +6,7 @@ Please note that the description of the course work might be updated from time t
1. [Part 1 (parser)](desc/part1/), Thursday 10 October 2019 at 11am, weight = 20%
2. [Part 2 (ast builder + semantic analyser)](desc/part2), Thursday 24 October 2019 at 11am, weight = 20%
3. Part 3 (code generator), Thursday 14 November 2019 at 11am, weight = 30%
3. [Part 3 (code generator)](desc/part3), Thursday 14 November 2019 at 11am, weight = 30%
4. Part 4 (LLVM-based compiler pass), Monday 13 January 2020, 10am, weight = 30%
Note that specific instructions for each part can be found above by clicking on the part name.
......
# Part III : Code Generation
The goal of part IV is to write the code generator, targeting MIPS32 assembly.
**Important**: the marking system will run the simulator from the command line which may change slightly the behaviour of your program (especially when it comes to handling input).
You should always make sure that all your tests execute correctly with the simulator run from the command line.
## 0. Setup and Learning
Your first task consist of setting up the MARS mips simulator.
First download the simulator [here](./Mars4_5.jar) and follow Part 1 of the [tutorial](http://courses.missouristate.edu/KenVollmar/mars/tutorial.htm) to learn how to use the simulator.
We also encourage you to have a look at the documentation provided by MARS which can be found [here](http://courses.missouristate.edu/KenVollmar/mars/Help/MarsHelpIntro.html) as well as the [MIPS 32 Instruction Set Quick Reference](./MD00565-2B-MIPS32-QRC-01.01-1.pdf).
For a more detailed explanation about the MIPS architecture, please have a look at the [MIPS Assembly WikiBooks](http://en.wikibooks.org/wiki/MIPS_Assembly)
## 1. Generating a simple program
Your next task should consists of producing an empty program (e.g. just an empty main function) and see that you can produce an assembly file.
Next, we suggest that you implement the print_i function using the corresponding system calls (check the lecture notes and the link above to the MARS documentation that explain how to do this).
You can then starts implementing simple arithmetic operations operating on literals, following the examples from the lectures.
Please note that we expect your programs to have one main function which should be the assembly entry point for the simulator.
## 2. Binary Operators
Your next task should be to add support for all the binary operators.
You should make use of the `getRegister` and `freeRegister` helper functions to allocate and free up registers as seen in the class.
Please note that the comparison operators as well as the `||` and `&&` should be implemented with control flow.
For the comparison operations, use the positional encoding as seen during the lecture (value 0 means false, any other value means true).
## 3. Variable allocations and uses
Your next task should be to implement allocations of global and local variables.
As seen during the course, the global variables all go in the static storage area (data section of the assembly file).
The local variables (variables inside a function) go onto the stack.
You should allocate them at a fix offset from the frame pointer ($fp) and store this offset either in a symbol table that you carry around or directly in the VarDecl AST node as a field.
Note that the only thing your compiler has to emit with respect to local variable is code to move the stack pointer ($sp) by an offset corresponding to the size of all the local variables declared on the stack.
Next you should implement the logic to read and write local or global variables.
You can use the `lw` and `sw` instruction to read from or write to a variable respectively.
### sizeof and data alignment
We will follow the following specification for the size of the different types:
`sizeof(char)==1`, `sizeof(int)==4`, `sizeof(int*)==4`
Also arrays should always be represented in a compact form but you may need to pad the end of the array to make sure it is aligned to a 4 byte boundary.
As seen during the lecture, in the case of structures, you should make sure all the field are aligned at a 4 byte boundary.
## 4. Branching (if-then-else, loop, logical operators)
We suggest that you then implement the loop and if-then-else control structures as seen during the course using the branch instructions.
The logical `||` and `&&` should also be implemented with control flow.
Note that in the following example
```C
int foo() {
print_i(2);
return 2;
}
if ((1==0) && foo() == 2)
...
```
the function foo is never called at runtime since the semantic `&&` imposes that if the left side is false, the right side expression should not be executed. A similar logic applies for `||`.
## 5. struct and array accesses
Next you should add support for struct and array accesses.
This can be implemented using the `lw` and `sw` instructions for struct and a combination of `add` instruction with the `lw` and `sw` instructions for array accesses.
The idea is to get the address of an array into a register, then add to it the index (keeping in mind that addresses are expressed in byte, so an access to `a[x]` where a is an int array means an offset of x*4 from the base address where the array is stored).
## 6. Function call
You can them move on to implementing function calls.
You can pass up to four arguments (no larger than 4 bytes each) through registers `$a0-$a3`, the rest or other arguments (e.g. struct) being passed on the stack.
Return value can be passed through register `$v0` or through the stack if they are bigger than 4 bytes (e.g. struct).
As seen during the lectures, when entering a function should make sure to save the content of the temporary registers, frame pointers `$fp` and return address `$ra`, and restored them when leaving the function.
You should also initialise the frame pointer to the value of the stack pointer.
## 7. stdLib functions
Finally, you should add support for all the standard library functions found in the file `minic-stdlib.h` provided in the tests folder.
These should all be implemented using [system calls](http://courses.missouristate.edu/KenVollmar/mars/Help/SyscallHelp.html).
## New Files
A new package has been added under `src/gen/`. This package should be used to store your code generator.
* The `gen.CodeGenerator` is the only class which `Main.java` directly interfaces with.
* The `gen.Register` class represents registers and contain a definition of most MIPS32 registers.
## Updated Files
* The `Main.java` has been updated to provide a new commandline pass `-gen` which runs your code generator.
import ast.ASTPrinter;
import ast.Program;
import gen.CodeGenerator;
import lexer.Scanner;
import lexer.Token;
import lexer.Tokeniser;
......@@ -67,19 +68,25 @@ public class Main {
if (mode == Mode.LEXER) {
for (Token t = tokeniser.nextToken(); t.tokenClass != Token.TokenClass.EOF; t = tokeniser.nextToken())
System.out.println(t);
if (tokeniser.getErrorCount() != 0)
System.out.println("Lexing: failed ("+tokeniser.getErrorCount()+" errors)");
if (tokeniser.getErrorCount() == 0)
System.out.println("Lexing: pass");
else
System.out.println("Lexing: failed ("+tokeniser.getErrorCount()+" errors)");
System.exit(tokeniser.getErrorCount() == 0 ? PASS : LEXER_FAIL);
} else if (mode == Mode.PARSER) {
Parser parser = new Parser(tokeniser);
parser.parse();
if (parser.getErrorCount() != 0)
if (parser.getErrorCount() == 0)
System.out.println("Parsing: pass");
else
System.out.println("Parsing: failed ("+parser.getErrorCount()+" errors)");
System.exit(parser.getErrorCount() == 0 ? PASS : PARSER_FAIL);
} else if (mode == Mode.AST) {
Parser parser = new Parser(tokeniser);
Program programAst = parser.parse();
if (parser.getErrorCount() == 0) {
if (parser.getErrorCount() == 0) {
System.out.println("Parsing: pass");
System.out.println("Printing out AST:");
PrintWriter writer;
StringWriter sw = new StringWriter();
try {
......@@ -108,9 +115,23 @@ public class Main {
} else
System.exit(PARSER_FAIL);
} else if (mode == Mode.GEN) {
System.exit(MODE_FAIL);
Parser parser = new Parser(tokeniser);
Program programAst = parser.parse();
if (parser.getErrorCount() > 0)
System.exit(PARSER_FAIL);
SemanticAnalyzer sem = new SemanticAnalyzer();
int errors = sem.analyze(programAst);
if (errors > 0)
System.exit(SEM_FAIL);
CodeGenerator codegen = new CodeGenerator();
try {
codegen.emitProgram(programAst, outputFile);
} catch (FileNotFoundException e) {
System.out.println("File "+outputFile.toString()+" does not exist.");
System.exit(FILE_NOT_FOUND);
}
} else {
System.exit(MODE_FAIL);
}
}
}
}
\ No newline at end of file
import ast.ASTPrinter;
import ast.Program;
import lexer.Scanner;
import lexer.Token;
import lexer.Tokeniser;
import parser.Parser;
import sem.SemanticAnalyzer;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* The Main file implies an interface for the subsequent components, e.g.
* * The Tokeniser must have a constructor which accepts a Scanner,
* moreover Tokeniser must provide a public method getErrorCount
* which returns the total number of lexing errors.
*/
public class MainPart2 {
private static final int FILE_NOT_FOUND = 2;
private static final int MODE_FAIL = 254;
private static final int LEXER_FAIL = 250;
private static final int PARSER_FAIL = 245;
private static final int SEM_FAIL = 240;
private static final int PASS = 0;
private enum Mode {
LEXER, PARSER, AST, SEMANTICANALYSIS, GEN
}
private static void usage() {
System.out.println("Usage: java "+Main.class.getSimpleName()+" pass inputfile outputfile");
System.out.println("where pass is either: -lexer, -parser, -ast, -sem or -gen");
System.exit(-1);
}
public static void main(String[] args) {
if (args.length != 3)
usage();
Mode mode = null;
switch (args[0]) {
case "-lexer": mode = Mode.LEXER; break; case "-parser": mode = Mode.PARSER; break;
case "-ast": mode = Mode.AST; break; case "-sem": mode = Mode.SEMANTICANALYSIS; break;
case "-gen": mode = Mode.GEN; break;
default:
usage();
break;
}
File inputFile = new File(args[1]);
File outputFile = new File(args[2]);
Scanner scanner;
try {
scanner = new Scanner(inputFile);
} catch (FileNotFoundException e) {
System.out.println("File "+inputFile.toString()+" does not exist.");
System.exit(FILE_NOT_FOUND);
return;
}
Tokeniser tokeniser = new Tokeniser(scanner);
if (mode == Mode.LEXER) {
for (Token t = tokeniser.nextToken(); t.tokenClass != Token.TokenClass.EOF; t = tokeniser.nextToken())
System.out.println(t);
if (tokeniser.getErrorCount() != 0)
System.out.println("Lexing: failed ("+tokeniser.getErrorCount()+" errors)");
System.exit(tokeniser.getErrorCount() == 0 ? PASS : LEXER_FAIL);
} else if (mode == Mode.PARSER) {
Parser parser = new Parser(tokeniser);
parser.parse();
if (parser.getErrorCount() != 0)
System.out.println("Parsing: failed ("+parser.getErrorCount()+" errors)");
System.exit(parser.getErrorCount() == 0 ? PASS : PARSER_FAIL);
} else if (mode == Mode.AST) {
Parser parser = new Parser(tokeniser);
Program programAst = parser.parse();
if (parser.getErrorCount() == 0) {
PrintWriter writer;
StringWriter sw = new StringWriter();
try {
writer = new PrintWriter(sw);
programAst.accept(new ASTPrinter(writer));
writer.flush();
System.out.print(sw.toString());
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
} else
System.out.println("Parsing: failed ("+parser.getErrorCount()+" errors)");
System.exit(parser.getErrorCount() == 0 ? PASS : PARSER_FAIL);
} else if (mode == Mode.SEMANTICANALYSIS) {
Parser parser = new Parser(tokeniser);
Program programAst = parser.parse();
if (parser.getErrorCount() == 0) {
SemanticAnalyzer sem = new SemanticAnalyzer();
int errors = sem.analyze(programAst);
if (errors == 0)
System.out.println("Semantic analysis: Pass");
else
System.out.println("Semantic analysis: Failed (" + errors + ")");
System.exit(errors == 0 ? PASS : SEM_FAIL);
} else
System.exit(PARSER_FAIL);
} else if (mode == Mode.GEN) {
System.exit(MODE_FAIL);
} else {
System.exit(MODE_FAIL);
}
}
}
package gen;
import ast.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.EmptyStackException;
import java.util.Stack;
public class CodeGenerator implements ASTVisitor<Register> {
/*
* Simple register allocator.
*/
// contains all the free temporary registers
private Stack<Register> freeRegs = new Stack<Register>();
public CodeGenerator() {
freeRegs.addAll(Register.tmpRegs);
}
private class RegisterAllocationError extends Error {}
private Register getRegister() {
try {
return freeRegs.pop();
} catch (EmptyStackException ese) {
throw new RegisterAllocationError(); // no more free registers, bad luck!
}
}
private void freeRegister(Register reg) {
freeRegs.push(reg);
}
private PrintWriter writer; // use this writer to output the assembly instructions
public void emitProgram(Program program, File outputFile) throws FileNotFoundException {
writer = new PrintWriter(outputFile);
visitProgram(program);
writer.close();
}
@Override
public Register visitBaseType(BaseType bt) {
return null;
}
@Override
public Register visitStructTypeDecl(StructTypeDecl st) {
return null;
}
@Override
public Register visitBlock(Block b) {
// TODO: to complete
return null;
}
@Override
public Register visitFunDecl(FunDecl p) {
// TODO: to complete
return null;
}
@Override
public Register visitProgram(Program p) {
// TODO: to complete
return null;
}
@Override
public Register visitVarDecl(VarDecl vd) {
// TODO: to complete
return null;
}
@Override
public Register visitVarExpr(VarExpr v) {
// TODO: to complete
return null;
}
}
package gen;
import java.util.ArrayList;
import java.util.List;
/**
* @author cdubach
*/
public class Register {
/*
* definition of registers
*/
public static final Register v0 = new Register(2,"v0");
public static final Register[] paramRegs = {
new Register(4,"a0"),
new Register(5,"a1"),
new Register(6,"a2"),
new Register(7,"a3")};
public static final List<Register> tmpRegs = new ArrayList<Register>();
static {
for (int i=8; i<=15; i++)
tmpRegs.add(new Register(i,"t"+(i-8)));
for (int i=16; i<=23; i++)
tmpRegs.add(new Register(i,"s"+(i-16)));
for (int i=24; i<=25; i++)
tmpRegs.add(new Register(i,"t"+(i-24+8)));
}
public static final Register gp = new Register(28,"gp");
public static final Register sp = new Register(29,"sp");
public static final Register fp = new Register(30,"fp");
public static final Register ra = new Register(31,"ra");
private final int num; // register number
private final String name; // register name
private Register(int num, String name) {
this.num = num;
this.name = name;
}
public String toString() {
return "$"+name;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment