Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

1Learning Outcomes

We have previously discussed register names and register conventions:

Register names define convention—that is, specifying how assembly instructions should use specific registers for specific common functions. These restrictions help build “agreement” upon how to translate separate components of a program so that the assembly instructions slot together.

Consider the register convention table on the RISC-V green card. So far we have only discussed a few register conventions–namely, the stack pointer sp and the return address ra.

The remainder of the register conventions we will discuss in this course[1] pertain to how to use registers between procedure calls.

2Motivation

In the C code below, main calls sum_square, which makes two calls to mult.

1
2
3
4
5
6
7
8
9
10
11
12
int main() {
  int z = sum_quare(3, 4);
  ...
}

int mult(int x, int y) {
  return x * y;
}

int sum_square(int x, int y) {
  return mult(x, x) + mult(y, y);
}

As we discussed in a previous section, jal ra Label and jr ra are a common instruction pair that saves a return address into register ra. Register naming conventions mean that in both instructions, ra refers to the same register number x1. However, necessarily sum_square will want to jump back to some ra, but this will be overwritten by both calls to mult. We therefore need to save sum_square’s return address somewhere before the call to mult—let’s use the stack!

3Register Calling Convention

Consider the fundamental steps of function calls. As part of Step 2 (where a caller transfers control and execution to a callee), how might a caller “save” their curent registers?

Instead, RISC-V defines a calling convention:

A set of generally accepted rules as to which registers will be unchanged after a procedure call, and which registers may be changed.

In other words, to minimize expensive loads and stores between procedure calls, RISC-V calling convention divides registers into two categories:

There are multiple ways of specifying this convention. The ASM Manual specifies the convention as whether registers are preserved across a procedure call (“yes” or “no”). The convention in Table 1 specifies who must save the register values (“caller” or “callee”).

Table 1:RV32I Register Calling Convention. This table is also available on our course green card.

Register(s)NameDescriptionSaver
x0zeroConstant 0-
x1raReturn AddressCaller
x2spStack PointerCallee
x3gpGlobal Pointer[1]-
x4tpThread Pointer[1]-
x5-7t0-2Temporary RegistersCaller
x8s0 / fpSaved Register 0 / Frame PointerCallee
x9s1Saved RegisterCallee
x10-11a0-1Procedure Arguments / Return ValuesCaller
x12-17a2-7Procedure ArgumentsCaller
x18-x27s2-11Saved RegistersCallee
x28-31t3-6TemporariesCaller

4Fundamental Steps, Revisted

In light of calling convention, we revisit the Six Fundamental Steps to Procedure Calls from a previous section in more detail:

Footnotes
  1. Out of scope: gp (global pointer, used to store a reference to the heap) and tp (thread pointer, used to store separate stacks for threads). Consider these registers “off-limits”–using them violates register conventions!