Skip to main content

2 Registers

Registers are the most important part of any processor. RISC-V defines various types, depending on which extensions are included: The general registers (with the program counter), control registers, floating point registers (F extension), and vector registers (V extension).

2.1 General registers

The RV32I base integer ISA includes 32 registers, named x0 to x31. The program counter PC is separate from these registers, in contrast to other processors such as the ARM-32. The first register, x0, has a special function: Reading it always returns 0 and writes to it are ignored. As we will see later, this allows various tricks and simplifications.

In practice, the programmer doesn’t use this notation for the registers. Though x1 to x31 are all equally general-use registers as far as the processor is concerned, by convention certain registers are used for special tasks. In assembler, they are given standardized names as part of the RISC-V application binary interface (ABI). This is what you will usually see in code listings. If you really want to see the numeric register names, the -Mnumeric argument to objdump will provide them.

Table 1. Registers of the RV32I. Based on RISC-V documentation and Patterson and Waterman "The RISC-V Reader" (2017)

| Register | ABI | Use by convention | Preserved? | | x0 | zero | hardwired to 0, ignores writes | n/a | | x1 | ra | return address for jumps | no | | x2 | sp | stack pointer | yes | | x3 | gp | global pointer | n/a | | x4 | tp | thread pointer | n/a | | x5 | t0 | temporary register 0 | no | | x6 | t1 | temporary register 1 | no | | x7 | t2 | temporary register 2 | no | | x8 | s0 or fp | saved register 0 or frame pointer | yes | | x9 | s1 | saved register 1 | yes | | x10 | a0 | return value or function argument 0 | no | | x11 | a1 | return value or function argument 1 | no | | x12 | a2 | function argument 2 | no | | x13 | a3 | function argument 3 | no | | x14 | a4 | function argument 4 | no | | x15 | a5 | function argument 5 | no | | x16 | a6 | function argument 6 | no | | x17 | a7 | function argument 7 | no | | x18 | s2 | saved register 2 | yes | | x19 | s3 | saved register 3 | yes | | x20 | s4 | saved register 4 | yes | | x21 | s5 | saved register 5 | yes | | x22 | s6 | saved register 6 | yes | | x23 | s7 | saved register 7 | yes | | x24 | s8 | saved register 8 | yes | | x25 | s9 | saved register 9 | yes | | x26 | s10 | saved register 10 | yes | | x27 | s11 | saved register 11 | yes | | x28 | t3 | temporary register 3 | no | | x29 | t4 | temporary register 4 | no | | x30 | t5 | temporary register 5 | no | | x31 | t6 | temporary register 6 | no | | pc | (none) | program counter | n/a |

As a general rule, the saved registers s0 to s11 are preserved across function calls, while the argument registers a0 to a7 and the temporary registers t0 to t6 are not. The use of the various specialized registers such as sp by convention will be discussed later in more detail.

2.2 Control registers

(TBA)

2.3 Floating Point registers (F extension)

The F extension adds 32 floating point registers, named f0 to f31, each 32 bits wide. The D extension widens the 32 registers to 64 bits. The Q extension widens the 32 registers to 128 bits.

Like the general registers, the floating point registers are given standardized names as part of the RISC-V application binary interface (ABI).

| Register | ABI | Use by convention | Preserved? | | f0 | ft0 | temporary register 0 | no | | f1 | ft1 | temporary register 1 | no | | f2 | ft2 | temporary register 2 | no | | f3 | ft3 | temporary register 3 | no | | f4 | ft4 | temporary register 4 | no | | f5 | ft5 | temporary register 5 | no | | f6 | ft6 | temporary register 6 | no | | f7 | ft7 | temporary register 7 | no | | f8 | fs0 | saved register 0 | yes | | f9 | fs1 | saved register 1 | yes | | f10 | fa0 | return value or function argument 0 | no | | f11 | fa1 | return value or function argument 1 | no | | f12 | fa2 | function argument 2 | no | | f13 | fa3 | function argument 3 | no | | f14 | fa4 | function argument 4 | no | | f15 | fa5 | function argument 5 | no | | f16 | fa6 | function argument 6 | no | | f17 | fa7 | function argument 7 | no | | f18 | fs2 | saved register 2 | yes | | f19 | fs3 | saved register 3 | yes | | f20 | fs4 | saved register 4 | yes | | f21 | fs5 | saved register 5 | yes | | f22 | fs6 | saved register 6 | yes | | f23 | fs7 | saved register 7 | yes | | f24 | fs8 | saved register 8 | yes | | f25 | fs9 | saved register 9 | yes | | f26 | fs10 | saved register 10 | yes | | f27 | fs11 | saved register 11 | yes | | f28 | ft8 | temporary register 8 | no | | f29 | ft9 | temporary register 9 | no | | f30 | ft10 | temporary register 10 | no | | f31 | ft11 | temporary register 11 | no |

Floating-point values in saved registers are only preserved across calls if they are no larger than the width of a floating-point register in the targeted ABI. These registers can be considered temporaries if targeting the base integer calling convention.

2.4 Vector registers (RV32V)

(TBA)