University of Arizona, Department of Computer Science

CSc 553 : Programming Assignment 1 (Code Generation)

Mon Sept 10, 2018

Due: 11:59 PM, Mon Oct 1, 2018

1. General

This assignment involves generating code for C--. You will write code to traverse the syntax tree for each function in a program and generate three-address statements, then translate these into MIPS assembly code and output the assembly code generated. Your code generator should reuse temporaries as far as possible.

For intermediate code generation, you can use the three-address instruction set given here; a translation from this instruction set to MIPS assembly code is given here. You are not required to adhere to this particular three-address instruction set, and are free to deviate from it if you want; however, if you do you will have to work out the mapping to MIPS assembly code yourself.

Note : Even though the intermediate code plays a relatively small role in this assignment, future assignments will involve dataflow analysis and machine-independent optimization using the intermediate code you will be generating in this assignment.

2. Behavior

The executable that impements your compiler should be called compile. It will take its input from stdin and generate MIPS assembly code to stdout. Error messages, if any, will be sent to stderr.

3. Turnin

You should turn in all the sources to your compiler (including all of the files for the front end). You should also turn in a Makefile that supports at least the following:
make compile
Creates an executable file in the current directory called compile that implements your compiler. Note that this should build your compiler from scratch, i.e. starting from the lex and yacc specifications.

make clean
deletes the following files from the current directory: lex.yy.c, y.tab.*, y.output, all object files (*.o) and the executable file compile.
To turn in your program, execute the command turnin cs553f18_assg1 files.

4. Special Routines

Your programs will print out values using the following routines:
void print_int(int val)
Prints out the integer val to stdout.
void print_string(char str[])
Prints out the string str to stdout.

To facilitate type checking by your compiler, these will be declared as externs in the input programs. For example:

extern void print_int(int x);
extern void print_string(char str[]);

int main(void) {
    int x;
    x = 123;
    print_int(x);
    print_string("\n");
    return 0;
}
Your compiler should generate additional code for these two routines, as described at the end of the Translating Three-Address Code to MIPS Assembly Code document.

5. Things to Watch Out For

  1. Variables and function names in the input program that conflict with MIPS opcodes, e.g., ‘b’.

    The simplest way to guard against such conflicts is to add an underscore "_" at the front of each program identifier. If you do this, however, you should keep in mind that execution still needs to begin at main (and not "_main"). The simplest way to handle this is to have the code generator add a label "main" whose code is a single unconditional jump to "_main".

  2. Large integer constants: Immediate operands can be at most 16 bits wide. Loading a constant more than 16 bits wide into a register requires two instructions.

6. Running the Generated MIPS Code

I will run the MIPS assembly code generated by your compiler using a version of SPIM that provides statistics on different kinds of instructions executed by a program. The source code for this version of SPIM is available here and also in /home/cs553/fall18/spim-7.2.1-keepstats; the executable for this version of SPIM is in
/home/cs553/fall18/bin/spim
To run SPIM on a file foo.s containing MIPS assembly code, use:
spim -file foo.s
To get execution count statistics, use:
spim -keepstats -file foo.s