Lab 7: Assembly Functions

You will complete some problems on a worksheet, followed by an implementation of recursive summation function. Please attend the section you are registered for to receive your lab checkoff.

For this week’s lab work, there are three files:

In the first part of the lab, you will review RISC-V Assembly and Calling Conventions concepts, completing the three worksheet problems to reinforce your understanding as you go along.

In the second half of the lab, we will write a recursive function that sums the integers from 1 through n. The function we want to implement would look something like this in C:

int sum(int n) {
  if (n == 0)
    return n;
  return n + sum(n - 1);
}

In assembly, recursive function calls work exactly the same way as any other function call—the caller and callee just happen to be the same function. We’ll follow the RISC-V calling conventions in both roles.

Start by writing the function body. The interesting part is implementing the function call. Take note of which caller-saved registers you need to save before the jal instruction and restore after the function returns.

Next, write the prologue and epilogue. You’ll want to start by making a list of all the values this function will ever need to store in its stack frame. Determine the stack frame layout, or the offsets you’ll store each value at. Lastly, follow the recipe from the add_one step in the lab slides to write the prologue and epilogue.

Copy the RISC-V assembly of the recsum function above into a file called recsum.s.

Trying It Out: Calling Your Function From C

We can’t run this assembly program yet as it lacks a main function. It also doesn’t print anything out, which makes it hard to tell what it’s doing (if anything). One way to test your assembly functions is to write a C program that calls your assembly function.

Make sure that your recsum.s implementation has an recsum: label. Now, at the top of the file add the following line:

.global recsum

This directive .global tells the assembler that the recsum label is a global symbol, meaning it’s accessible to other code.

We have provided a main function you can use to test your code in recsum.c

That recsum declaration is called a prototype header, which means it doesn’t have a function body. It just tells the C compiler that the function is implemented elsewhere—in your case, in an assembly file.

Now, let’s compile and link these two files together.

$ rv gcc recsum.s recsum.c -o recsum

Then, to run the program use rv qemu recsum n, where n is the integer to perform the recursive sum on.

This works thanks to the magic of calling conventions! You and GCC are both “assembly programmers”, and because you agree on the standard way to invoke functions, the assembly code you both write can interoperate.