CSE 401, Winter 2014

Robert R. Henry

Assignment 5

Expressions and Function Calls

Due via dropbox Monday February 10 by the end of the day (midnight).

Goals

By the end of this assignment you will be able to execute a compiled Mini Java program that manipulates integer constants. You will be able to call member functions (with constant expressions for arguments), call library functions from boot.c, return from functions, and implement if/then/else and while.  Your source programs aren’t yet type checked, so you need to be careful to only try to compile correct programs.

At this stage of development, our compiler can’t do the semantic analysis of variables, and doesn’t have a storage allocation model, so we won’t be dealing with variables, data members, parameters, method receivers, “this”, etc.  You will happily be able to add booleans to ints, compare booleans, etc.

Expressions

  1. We’ve provided a CodeGeneratorVisitor to crawl the AST.  We’ve supplied a CodeGenerator class which knows how to generate code for the x86_64.  You’ll be extending both hand-in-hand.
  2. Use a stack machine model of evaluation: all operands to operators get pushed onto the stack using a “pushq” instruction.  The operator itself pops the operands off the stack into convenient or mandatory registers, does the operation, and pushes the result back onto the stack. Function calls are k-ary operators. Be careful not to interchange operands.
  3. The generated code will be verbose and inefficient.  Who cares at this stage?
  4. Implement all binary and unary operators by following what is done already for the “plus/add”.
  5. Implement comparison operators by evaluating to a 1 or a 0, and pushing that value back onto the stack.
  6. Write small experimental programs in C and run these experiments through gcc -S to examine the assembly code gcc produces, especially for the hairy operators involving multiplication or division.  Remember, a good engineer borrows (and credits) as much as possible!
  7. Be wary of cut and paste errors.
  8. For now, all operators consume 64-bit integers (Java long) (“q” suffix on GNU assembly code), and produce 64-bit integers, including Booleans.  Who cares if we are wasting space? (Obviously, doubles will eventually consume 64-bit doubles.)

Function Call/Return

  1. You must stick to the ABI for function calls on the x86_64.  Actual parameters go into specific registers, and the return value into a specific register.  Give up if there are too many parameters.
  2. Make sure you keep the stack aligned, per requirements of the underlying C runtime.  Macosx is more picky than linux.
  3. The callee should move incoming formal parameters onto the stack.  Since we haven’t implemented variables or storage model yet, we can’t actually use the parameters in Mini Java programs just yet, but we can still call library functions.
  4. Don’t worry about the receiver or the type of the receiver; just treat every function call at this stage as if it were a function call to a static function whose name is known at compile time.
  5. Name functions using a mangled name, combining the class and method name into a valid assembler name.
  6. Call functions directly; we’ll add indirect function calls when we implement vtbls (virtual tables) and dynamic dispatch later on in the quarter.
  7. Make sure you can call library functions in the Mini Java Run Time, as implemented by stock C functions in boot.c, such as the function “put” or “put_double”.   See what is already done for the. You may have to add some library functions to test out the functionality of both calling and returning, taking more than 1 argument, etc.

Control Flow

  1. Figure out how turn a boolean on the top of the expression evaluation stack into a “branch if true” or a “branch if false”.
  2. Implement code to model branch destinations, and to generate and print assembly code labels.
  3. Implement if/then/else.
  4. Implement while statements.
  5. Implement the short circuit && and ||; this is a little trickier than doing if/then/else, but not much.

Debugging

  1. You will go insane reading the generated assembly code unless you put markers (aka fiducials) into the code.  We carbon-based human computers are not patient and meticulous enough to be a very good computer! Without markers, all you’ll see is pushes, pops, and some other glop, and you won’t be able to figure out what assembly code comes from which source statement.
  2. Markers should be pairs of bracketing comments that contain at the very least the source line number of the corresponding mini Java program, plus whatever information you think is relevant to reading and understanding the generated assembly code.

Discussion

  1. Brush up on your skills with gdb.
  2. Common mistakes include pushing more operands onto the stack than you pop off, or vice-versa.  This makes the stack get skewed, so the next function return picks up an invalid return address and goes off into some bogus place.

What to turn in

  1. Turn in via dropbox a tar file containing your entire project.  Make sure both files have a comment at the top with your group name and members’ names.
  2. Turn in via dropbox the assembly code produced by a Mini Java program we supply you.  This will be a different MJ program than the one we had you pretty print. Details to be worked out in section or via email as the due date approaches.