MCS-388 Lab 5: Procedures (Spring 2006)

Due: May 3, 2006

Objective

You will extend your compiler to allow procedures other than main to be defined and procedures other than printInt to be called.

You should do the portion of this lab headed "the basics" first, after which you can work on the remaining parts in almost any order. I would like to see everyone achieve at least a couple of the non-basic parts.

The basics

Remember to test your work frequently. You don't need to complete this whole section in order to conduct useful tests; each paragraph can be tested.

If you haven't already done this, revise the printInt statements so they don't neeed to have that one specific name before the left parenthesis; they can have any name. Whatever name is used should wind up in the jal instruction in the generated assembly code. This gives you relatively general procedure call statements, though they all still require one integer argument.

Change the syntax of a program so that there can be any number of procedure definitions (each with any name), rather than always just a single definition of main. Be sure to reset the register allocators between generating code for each procedure and the next, so that each procedure has the full range of registers available.

Because each procedure call passes in one argument, it would be nice if each procedure were expecting an argument. The syntax of a procedure definition should no longer have an empty pair of parentheses. Instead, include the keyword int and a variable name. This should be treated just like the corresponding local variable declaration would, except that the assembly code for the procedure should also include a move instruction that copies the argument value from $a0 into the variable's $s register.

Allowing more or fewer arguments

Change procedure calls and procedure definitions to allow as few as zero arguments and as many as 4, passed in registers $a0 through $a3.

Return statements

Add a return statement, which can translate into a j instruction that jumps to a label just before the saved registers are restored from the stack. (Keep in mind that each procedure will need its own label.)

Checking calls and definitions

Add code to check that every procedure call uses a procedure name that is defined and passes in the same number of arguments as are shown in the procedure defintion. (Treat printInt as predefined with one argument.) Also, check that no two procedure definitions use the same name.

Allowing procedures to return integers

In order to allow procedures to return integers, you will likely want to make three changes:
  1. Unless you want every procedure to return an integer, you might want to differentiate those that do from those that don't by starting each definition with the keyword int or void.

  2. You will need a return statement that includes an expression whose value should be returned. This expression's value should be moved into the $v0 register. After that, the code should jump to the tail end of the procedure where registers are restored from the stack.

  3. You will want to allow procedure calls to serve as expressions. The generated assembly can be the same as for procedure call statements, provided that you limit the contexts in which procedure call expressions are allowed, as described in the following paragraph.

To keep the assembly code for procedure call expressions just as simple as for procedure call statements, you will need to set up your grammar so that calls can only appear as top-level expressions. That is, a procedure call can show up as the right hand side of an assignment statement, as the test expression in a while or if statement, or as the expression in a return statement. A procedure call can't show up as a subexpression inside an arithmetic expression, nor as an argument to another procedure call. (Actually, the first argument would be OK, but there is no point in getting too fancy.) Subject to this limitation, the same assembly language can be generated as for a call statement. The expression's value will wind up in $v0.

If you are really adventuresome, you could allow procedure call expressions to serve as subexpressions, rather than only as top-level expressions. However, this requires that you save any $t and $a registers before the call and restore them afterward. Moreover, you can't leave the return value in $v0 because the same overall expression might have more than one procedure call, each with its own return value.

Postlab

Write a lab report highlighting the differences from the code for your previous compiler. You should also describe the tests you ran and any problems uncovered.


Course web site: http://www.gac.edu/~max/courses/S2006/MCS-388/
Instructor: Max Hailperin <max@gustavus.edu>