目录 ← 首页
CS61C

Calling Convention

Learning Outcomes

  • Explain why register calling convention helps implement procedure calls in RISC-V.
  • Identify whether the caller or callee is responsible for each of the six fundamental steps to procedure calls.
  • Use the RISC-V green card to determine whether registers are caller-saved, callee-saved, or neither.

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 course1 pertain to how to use registers between procedure calls.

Motivation

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


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!

Register 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:

  • Volatile, temporary registers that are not preserved across a procedure call.
  • Saved registers that are preserved across the procedure call.

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 #tab-calling-convention-copy specifies who must save the register values (“caller” or “callee”).

  • Caller-saved volatile registers. The caller is responsible for saving these register values for itself before calling a callee. After the callee returns, the caller can then restore these values if needed.
  • Callee-saved saved registers. The callee is responsible for saving and restoring these register values. These two steps are often performed in the prologue and epilogue, respectively.

Fundamental 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! 2 3