CSE 378 Assignment 2

Winter 2001

Due: Wednesday 1/17/01

Purpose

The purpose of this homework is to familiarize yourself with the R2000/3000 simulator, xspim. You will learn how to use xspim to run assembly language programs and to extract information to debug your own programs. You will also try your hand at a little assembly programming by making minor modifications to a program we provide. Finally, you'll be asked to compare and contrast a few examples of compiler generated assembly code with hand written assembly code. Use this as an apportunity to also familiarize yourself with the instructional computing environment, UNIX, and emacs, if you have not already done so.

Introduction

For this assignment we'll use the following program, which computes the factorial of 8. The line numbers you see are for reference only, and are not part of the actual assembly program.
 1  # compute the factorial of 8, using the following algorithm:
 2  #
 3  #   N = 8;
 4  #   result = 1;
 5  #   for (counter = 1; counter <= N; counter++)
 6  #     result = result * counter 
 7  #
 8
 9          .data                           # begin data section
10  msg:    .ascii  "The factorial is: "    # message string
11          .text                           # begin program text
12          .globl  main                    # main must be a global symbol
13  main:
14          subu    $sp, 28                 # adjust stack pointer
15          sw      $ra, 20($sp)            # save return address
16          li      $t0, 1                  # $t0 will hold result, init 1
17          li      $t1, 8                  # $t1 will hold N, init 8
18          li      $t2, 1                  # $t2 will hold counter, init 1
19  top:
20          bgt     $t2, $t1, bottom        # if counter > N, exit loop
21          mul     $t0, $t0, $t2           # result = result * counter
22          addu    $t2, $t2, 1             # increment counter
23          j       top                     # goto top
24  bottom:
25          sw      $t0, 24($sp)            # we'd better save result
26          li      $v0, 4                  # finished w/ loop, now print out
27          la      $a0, msg                # message, by invoking syscall 4
28          syscall                         # (print_string)
29
30          li      $v0, 1                  # now print out result, by 
31          lw      $a0, 24($sp)            # invoking syscall 1 (print_int)
32          syscall
33
34          lw      $ra, 20($sp)            # restore return address
35          addu    $sp, 28                 # pop stack frame
36          jr      $31                     # return to caller
37          .end    main
Thankfully, you don't have to type the program in yourself, as it already resides in
/cse/courses/cse378/CurrentQtr/homework/hw2.spim
on the instructional machines. Simply copy this file to your home directory and use it.

Using the Simulator: Instructions and Questions

  1. You'll first want to read over the document, Getting Started with Spim.

  2. Start the simulator, load the program, and run it by single-stepping through it. What is printed on the spim console?
    
    
    
    
    
  3. List the differences between the provided program and the instructions being executed:
    
      
    
    
    
    
    
  4. Complete the following table by examining various registers and memory location contents after the appropriate instructions are executed. Using the breakpoint command may help in certain situations.

    Location Line 17 Line 22
    2nd iteration
    Line 22
    4th iteration
    Line 22
    7th iteration
    Line 34
    $t0




    $t1




    $t2




    20($sp)




    24($sp)




  5. In total, how many instructions (in the simulator) are executed to calculate the factorial of 8? The right way to answer this question is not to step the program through 8 loops and count instructions, but rather determine how many instructions are exectuted before the loop, during one iteration of the loop, and after the loop. Then you can give us an answer in terms of N, where N is the value we're calculating the factorial of (as well as the number of times the loop iterates).
     
    
    
    

Assembly Programming: Modifications

Modify the given code so that it reads a value of N from the user, and then computes its factorial. Do this by reading Appendix A in Patterson and Hennessy and the code to figure out how to make the appropriate syscall. Next, modify the program to continue prompting the user for values of N and computing their factorials. Exit the program when the user enters a negative number.

Compilers

Below is the C version of the factorial program. It can also be found in .../cse378/CurrentQtr/homework/hw2.c. Copy the program to your home directory; compile and run the program. Hopefully, you'll get the same result as above.
#include <stdio.h>

/* Compute the factorial of 8 */

void main ()
{
  int counter;
  int N;
  int result;
  N = 8;
  result = 1;
  for (counter=1; counter <= N; counter++)
    result = result * counter;
  printf("The factorial is: %d\n", result);
}
Now let's look at the MIPS assembly code generated by a typical C compiler. Doing this turns out to be a little tricky, because the C compilers on the Linux boxes (fiji, etc.) will generate x86 (not MIPS) assembly code. To get MIPS assembly, I've built a "cross-compiler", which is a compiler that runs on a machine of type A, but outputs code for machines of type B. (In this case, the compiler is a Linux x86 executable, but outputs MIPS Rx000 assembly code.)

The cross-compiler lives in /cse/courses/cse378/CurrentQtr/bin/mips-gcc. To use it, just type:

  mips-gcc hw2.c
at the prompt. The cross-compiler will deposit assembly code into a file called hw2.s (in the above example). It will also dump (perhaps) informative messages into a file called gc.err. Please use mips-gcc to answer the following questions:
  1. First, compile the program without optimizations: mips-gcc -O0 hw2.c. The -O flag (that's the letter "O", not zero) is the optimization flag for the compiler. -O0 ("oh-zero") turns off all optimizations. This will produce a file called hw2.s. Take a look at this file to see the relationship and differences between operations in high-level language and assembly language. You'll want to save a copy of this file before moving on to the next step. (Something like cp hw2.s hw2-unopt.s should do the trick.)

  2. Next, compile the program with (more or less) full optimization: mips-gcc -O2 hw2.c. This will produce a file called hw2.s. Save a copy of this program in hw2-opt.s. The compiler generated assembly programs may contain some syntax which you do not understand (particularly the assembler directives). Don't worry about this.

  3. Compare hw2.spim (the hand-coded assembly program we have provided) and hw2-unopt.s (the compiler-generated, unoptomized assembly program). What is the main difference between the main loop of hw2-unopt.s and hw2.spim? Which program do you think will execute faster? Why?
    
    
    
    
    
    
    
    
    
  4. Compare hw2.spim and hw2-opt.s (the compiler-generated, fully optimized assembly program). Which program do you think will execute faster? Why?
    
    
    
    
    
    
    
    
    
  5. Compare hw2-unopt.s and hw2-opt.s. Describe, briefly, what kind of instruction(s) the compiler has eliminated in order to optimize the program. (One sentence should suffice.)
    
    
    
    
    
    
    
    
    

Ask a Question (or Two)

Write two short questions you have about what we've been covering in class. We ask this to (1) get you to think a little deeply about the current material, and (2) help us learn what we're not getting across in lecture and section.

Deliverables

Please turn in the answers to the above questions (you can just turn in this handout, with your answers filled in), your two questions, as well as a printout of your modified assembly code (only the final version). We may announce additional turnin requirements (eg. via electronic turnin), so stay tuned.
dugan@cs.washington.edu