RVC rvc The compressed 16-bit instruction set (C extension on any base). Every RVC instruction expands 1:1 to a single base/F/D 32-bit instruction at decode; IALIGN becomes 16 and instruction-address-misaligned exceptions cease to exist. defined in ch. I·28 — open in glossary adds 16-bit encodings for the operations programs actually execute: typically 50–60% of instructions compress, cutting code size 25–30% — and the fetch-bandwidth savings reduce icache misses about as much as doubling the icache. Compression comes from four regularities: small immediates; operands implied to be x0, x1 (ra), or x2 (sp); rd = rs1; or operands drawn from the eight hot registers x8–x15 prime registers The 3-bit rs1'/rs2'/rd' fields of CIW/CL/CS/CA/CB formats: x8-x15 (s0,s1,a0-a5) and f8-f15 - the ABI's hot registers, made contiguous specifically for cheap decompression (value = field + 8). defined in ch. I·28 — open in glossary .
Two ground rules define the whole extension:
- IALIGN becomes 16. 32-bit instructions may start on any 16-bit boundary — and since every transfer offset is a multiple of 2, instruction-address-misaligned exceptions cease to exist.
- The 1:1 expansion rule 1:1 expansion rule Design constraint of RVC: every 16-bit instruction maps to exactly one 32-bit base/F/D instruction, so decoders expand early and compilers may ignore C entirely. Sole semantic wrinkle: C.JAL/C.JALR write pc+2 as the link value. defined in ch. I·28 — open in glossary : every RVC instruction expands to exactly one base/F/D instruction. Decoders expand early; compilers may not even know C exists (assembler/linker compress). The lone semantic wrinkle: C.JAL/C.JALR write pc+2 as the link value.
28.2 Nine formats, eight hot registers
| Registers | Used for | |
|---|---|---|
| CR — register | full 5-bit rd/rs1, rs2 | C.MV, C.ADD, C.JR, C.JALR, C.EBREAK |
| CI — immediate | full 5-bit rd/rs1 | C.LI, C.LUI, C.ADDI[W], C.SLLI, SP-loads |
| CSS — stack store | full 5-bit rs2 | C.SWSP / C.SDSP / C.FSWSP / C.FSDSP |
| CIW — wide immediate | 3-bit rd′ | C.ADDI4SPN (8-bit immediate) |
| CL / CS — load/store | 3-bit rs1′ + rd′/rs2′ | C.LW/LD/FLW/FLD, C.SW/SD/FSW/FSD |
| CA — arithmetic | 3-bit rd′/rs1′, rs2′ | C.AND/OR/XOR/SUB/ADDW/SUBW |
| CB — branch/arith | 3-bit rd′/rs1′ | C.BEQZ/BNEZ, C.SRLI/SRAI/ANDI |
| CJ — jump | none | C.J, C.JAL (11-bit scrambled offset) |
Design regularities worth wiring into your decoder: source specifiers sit in fixed positions; when a full 5-bit rd is present it aligns with the base ISA’s rd field; sign extension is always from bit 12; immediates are scrambled (as in the base ISA) to share bit positions across formats. Many code points exclude zero immediates or x0 — the freed space encodes other instructions.
28.3 Loads and stores: two addressing flavors
Offsets are zero-extended and scaled by the access size (×4 words, ×8 doublewords) — negative offsets are rare in practice and scaling extends reach.
| Expands to | Availability / constraints | |
|---|---|---|
| C.LWSP / C.SWSP | lw/sw reg, uimm×4(x2) | Any data register; C.LWSP rd≠0 (rd=0 reserved) |
| C.LDSP / C.SDSP | ld/sd reg, uimm×8(x2) | RV64C only; C.LDSP rd≠0 |
| C.FLWSP / C.FSWSP | flw/fsw reg, uimm×4(x2) | RV32FC only |
| C.FLDSP / C.FSDSP | fld/fsd reg, uimm×8(x2) | RV32DC and RV64DC — doubles compress on both bases |
| C.LW / C.SW | lw/sw rx′, uimm×4(rs1′) | Prime registers only |
| C.LD / C.SD | ld/sd rx′, uimm×8(rs1′) | RV64C (RV32’s same slots: C.FLW/C.FSW) |
| C.FLD / C.FSD | fld/fsd fx′, uimm×8(rs1′) | Both bases with D |
28.4 Control transfer
| Expands to | Range / notes | |
|---|---|---|
| C.J | jal x0, offset | ±2 KiB, CJ format, scrambled offset[11|4|9:8|10|6|7|3:1|5] |
| C.JAL | jal x1, offset | RV32C only; link = pc+2 |
| C.JR | jalr x0, 0(rs1) | rs1≠0 (rs1=0 reserved) |
| C.JALR | jalr x1, 0(rs1) — but link = pc+2 | rs1≠0; rs1=0 is C.EBREAK |
| C.BEQZ / C.BNEZ | beq/bne rs1′, x0, offset | ±256 B, prime rs1′ compared against zero |
Hardware Designer Notes
The expander itself is a few hundred gates; the real cost of C is in fetch: instructions now straddle everything (buffers, cachelines, pages). Budget for a straddle register in the fetch unit and precise fault attribution when the second half of a 32-bit instruction page faults (the reported pc is the instruction start — Vol II’s mtval/mepc semantics assume it). Linux requires C (RV64GC), so IALIGN=16 and the straddle logic are non-negotiable for the boot target.
Minimal Linux-boot hart MUST
- Expand 16→32 in decode and reuse the base datapath — but link values for C.JAL/C.JALR are pc+2
- Handle a 32-bit instruction straddling a cacheline/page boundary: two fetches, possibly two TLB lookups, and fault attribution to the correct half
- Switch the XLEN-shared code points (FLW/LD, FSW/SD, JAL/ADDIW) at decode by base
- Provide compressed FP loads/stores whenever C meets F/D
MAY simplify / trap-and-emulate
- Keep the fetch buffer 16-bit granular and length-decode from bits [1:0] alone (ch. I·1 guarantees it)
- Treat the C decoder as pure combinational expansion — no new architectural state exists in this extension
Check yourself — RVC basics
1.With the C extension implemented, when does an instruction-address-misaligned exception occur?
2.Why were the ABI's hot registers moved to x8–x15?
3.C.JALR x5 executes at address A. What link value is written to x1?
4.Why do C.LW-family offsets zero-extend and scale by the access size, when base-ISA load offsets are signed and unscaled?
5.Your RV64 decoder sees op=00, funct3=011 (the RV32 C.FLW slot). What is it?