RISC-V Calling Conventions and Stack Frames
C -> Asm / processors
- Every processor has an associated instruction set architecture (ISA)
- C -> Asm (.S) -> .o files -> executables
RISC-V & x86-64
x86-64
- 3 full books
- 3 instr/month (15k instr)
RISC-V
- Fewer instructions
- Simple instructions
- Open source
- Integrated devices
ARM (RISC)
- Qualcomm snapdragon (Android)
- iOS
Registers
There are basically two types of registers: integer registers and floating-point registers. Listed below.
| Register | ABI name | Description | Saver |
|---|---|---|---|
| x0 | zero | Hard-wired zero | — |
| x1 | ra | Return address | Caller |
| x2 | sp | Stack pointer | Callee |
| x3 | gp | Global pointer | — |
| x4 | tp | Thread pointer | — |
| x5–7 | t0–2 | Temporaries | Caller |
| x8 | s0/fp | Saved register/frame pointer | Callee |
| x9 | s1 | Saved register | Callee |
| x10–11 | a0–1 | Function arguments/return values | Caller |
| x12–17 | a2–7 | Function arguments | Caller |
| x18–27 | s2–11 | Saved registers | Callee |
| x28–31 | t3–6 | Temporaries | Caller |
| f0–7 | ft0–7 | FP temporaries | Caller |
| f8–9 | fs0–1 | FP saved registers | Callee |
| f10–11 | fa0–1 | FP arguments/return values | Caller |
| f12–17 | fa2–7 | FP arguments | Caller |
| f18–27 | fs2–11 | FP saved registers | Callee |
| f28–31 | ft8–11 | FP temporaries | Caller |
ABI name is much important than those noted by number, except when compressed version of RISC-V code.
Saver
When calling a function, registers may be overwritten by the callee. So RISC-V gives a calling convention to decide who saves what, so that register values are preserved correctly across function calls.
Caller saved registers
- Also called volatile or scratch registers.
- The caller (the function making the call) must save them before calling another function if it wants to keep their values.
Callee-saved registers
- Also called non-volatile or preserved registers.
- The callee (the function being called) must save their contents before using them and restore them before returning.
- That way, the caller can safely assume their values stay the same across function calls.
When using compressed version of RISC-V, you can only use register 8 to 15. So
axregisters are inserted intosxregisters.
Stack & calling conventions
Stack
Stack keeps functions organized and sane. It’s what makes functions work, it’s what makes return work, and it’s also where often we will find ourselves saving our registers and things likes that.
Here is what a stack looks like.
Each of these boxes is what we refer to as a stack frame, from current return address to
return address of deeper function (the latter not inclueded). And stack frames are generated by
function calls. It’s done by moving around the stack pointer (sp register). And stack frames for
functions contain registers, local variables; if you run out of argument registers, additional
arguments will show up on the stack.
In each stack frame, the first thing is always return address and the second thing is previous
frame pointer.
sp points to the bottom of stack, and fp points to the top of current frame.
It’s worth noting that stack always grows downwards.
