28.1-4"C": Compressed Instructions — Formats, Loads/Stores, Control Transfer

Part I Linux boot: required Vol. I (Unprivileged) pp. 152–158 · ~5 min read

  • rvc
  • prime registers
  • 1:1 expansion rule

RVC 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 .

Two ground rules define the whole extension:

  1. 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.
  2. The 1:1 expansion rule : 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

RVC instruction formats (Table 36)
RegistersUsed for
CR — registerfull 5-bit rd/rs1, rs2C.MV, C.ADD, C.JR, C.JALR, C.EBREAK
CI — immediatefull 5-bit rd/rs1C.LI, C.LUI, C.ADDI[W], C.SLLI, SP-loads
CSS — stack storefull 5-bit rs2C.SWSP / C.SDSP / C.FSWSP / C.FSDSP
CIW — wide immediate3-bit rd′C.ADDI4SPN (8-bit immediate)
CL / CS — load/store3-bit rs1′ + rd′/rs2′C.LW/LD/FLW/FLD, C.SW/SD/FSW/FSD
CA — arithmetic3-bit rd′/rs1′, rs2′C.AND/OR/XOR/SUB/ADDW/SUBW
CB — branch/arith3-bit rd′/rs1′C.BEQZ/BNEZ, C.SRLI/SRAI/ANDI
CJ — jumpnoneC.J, C.JAL (11-bit scrambled offset)
Dotted-underlined cells have explanations — click one.

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.

RVC loads/stores and their expansions
Expands toAvailability / constraints
C.LWSP / C.SWSPlw/sw reg, uimm×4(x2)Any data register; C.LWSP rd≠0 (rd=0 reserved)
C.LDSP / C.SDSPld/sd reg, uimm×8(x2)RV64C only; C.LDSP rd≠0
C.FLWSP / C.FSWSPflw/fsw reg, uimm×4(x2)RV32FC only
C.FLDSP / C.FSDSPfld/fsd reg, uimm×8(x2)RV32DC and RV64DC — doubles compress on both bases
C.LW / C.SWlw/sw rx′, uimm×4(rs1′)Prime registers only
C.LD / C.SDld/sd rx′, uimm×8(rs1′)RV64C (RV32’s same slots: C.FLW/C.FSW)
C.FLD / C.FSDfld/fsd fx′, uimm×8(rs1′)Both bases with D
Dotted-underlined cells have explanations — click one.

28.4 Control transfer

RVC control transfer
Expands toRange / notes
C.Jjal x0, offset±2 KiB, CJ format, scrambled offset[11|4|9:8|10|6|7|3:1|5]
C.JALjal x1, offsetRV32C only; link = pc+2
C.JRjalr x0, 0(rs1)rs1≠0 (rs1=0 reserved)
C.JALRjalr x1, 0(rs1) — but link = pc+2rs1≠0; rs1=0 is C.EBREAK
C.BEQZ / C.BNEZbeq/bne rs1′, x0, offset±256 B, prime rs1′ compared against zero
Dotted-underlined cells have explanations — click one.

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?

5 questions