28.5-8RVC: Computation, Code-Point Reuse & the Opcode Map

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

  • rvc quadrant map
  • c.addi16sp / c.addi4spn

The computational half of RVC is a lesson in encoding frugality: nearly every “spare” operand combination — x0 destinations, zero immediates, special registers — is recycled into another instruction, a HINT, or reserved space. Decode is therefore a cascade of field checks, and this page is the map.

28.5 Computational instructions and their recycled code points

RVC integer computation (with the code-point fine print)
Expands toCode-point fine print
C.LI (CI)addi rd, x0, imm6rd=0 → HINT
C.LUI (CI)lui rd, nzimm (bits 17:12)rd∉{0,2}, imm≠0
C.ADDI16SPaddi x2, x2, nzimm×16Range [−512, 496]; nzimm=0 reserved
C.ADDI4SPN (CIW)addi rd′, x2, nzuimm×4nzuimm=0 reserved — pointers into the stack frame
C.ADDI (CI)addi rd, rd, nzimmrd=0 → C.NOP (its imm≠0 points are HINTs); imm=0 (rd≠0) → HINT
C.ADDIW (CI, RV64)addiw rd, rd, immimm MAY be 0 (= sext.w); rd=0 reserved. Same code point is C.JAL on RV32
C.SLLI (CI)slli rd, rd, shamtFull 5-bit rd; RV32: shamt[5]=1 → custom space; rd=0 or shamt=0 → HINT
C.SRLI / C.SRAI / C.ANDI (CB)srli/srai/andi rd′, rd′, immPrime registers; shifts: shamt=0 HINT, RV32 shamt[5] custom
C.MV (CR)add rd, x0, rs2rs2=0 → C.JR; rd=0 → HINT
C.ADD (CR)add rd, rd, rs2rs2=0 → C.JALR (rd≠0) or C.EBREAK (rd=0); rd=0 → HINT/C.NTL.*
C.AND/OR/XOR/SUB (CA)and/or/xor/sub rd′, rd′, rs2′Prime registers, funct2 selects
C.ADDW / C.SUBW (CA, RV64)addw/subw rd′, rd′, rs2′RV32: reserved
Dotted-underlined cells have explanations — click one.

Three special citizens: the all-zero 16-bit encoding is permanently illegal (traps jumps into zeroed memory — never redefine it, even in custom extensions); C.NOP = addi x0, x0, 0’s compressed twin; C.EBREAK hides in C.ADD’s rd=rs2=0 corner.

28.6 LR/SC loops, compressed

Constrained LR/SC loops (ch. I·13) may use the compressed forms of the permitted instructions. Claiming A and C means your eventuality guarantee holds for 16-bit-encoded loops too.

28.7 RVC HINTs

Same architecture as base HINTs (no state change but pc/counters, executed as dead ops), one twist: RVC HINT code points need not map to the corresponding RVI HINTs — the scarce space goes to popular, fusible hints instead. Already assigned: C.NTL.* = C.ADD x0, x2–x5 (Table 38: ~78% future-standard, custom rows in the degenerate shift/C.SLLI points).

28.8 The quadrant map

op[1:0] picks the quadrant ; funct3 (bits 15:13) picks the row (Table 39):

Table 39 — RVC opcode map (RV32 / RV64 differences marked)
Quadrant 00Quadrant 01Quadrant 10
000ADDI4SPNADDISLLI
001FLDJAL (RV32) / ADDIW (RV64)FLDSP
010LWLILWSP
011FLW (RV32) / LD (RV64)LUI / ADDI16SPFLWSP (RV32) / LDSP (RV64)
100ReservedMISC-ALU (SRLI/SRAI/ANDI/SUB/XOR/OR/AND/SUBW/ADDW)J[AL]R / MV / ADD / EBREAK
101FSDJFSDSP
110SWBEQZSWSP
111FSW (RV32) / SD (RV64)BNEZFSWSP (RV32) / SDSP (RV64)
Dotted-underlined cells have explanations — click one.

(Quadrant 11 = anything ≥ 32 bits — the base ISA’s world.)

Hardware Designer Notes

The decode cascade for quadrant 1’s funct3=011 and quadrant 2’s funct3=100 rows is where RVC decoders grow their bugs — write the truth table (rd=0? rd=2? rs2=0? bit 12?) directly from Figures 4/5 and let your DV cross every RES/HINT corner against a reference decoder. The listings’ RES points are your documented-choice space; the HINT points are not (they must execute).

Minimal Linux-boot hart MUST

  • Trap the all-zero 16-bit encoding as illegal — never recycle it
  • Decode the recycled corners exactly: C.LUI rd=2 → ADDI16SP; C.MV rs2=0 → JR; C.ADD rs2=0 → JALR/EBREAK
  • Execute RVC HINT points as dead ops (never trap); implement C.NTL.* only if you implement NTL
  • Honor LR/SC eventuality for compressed constrained loops if you claim A+C

MAY simplify / trap-and-emulate

  • Expand C.MV to the canonical MV (ADDI) instead of ADD when your rename stage pattern-matches moves
  • Ignore all HINTs; use the custom HINT rows (degenerate shifts) for your own hints

Check yourself — RVC computation & code points

1.How does the decoder tell C.LUI from C.ADDI16SP? They share funct3=011 in quadrant 1.

2.C.MV expands to ADD rd, x0, rs2 — not to ADDI rd, rs2, 0 (the canonical MV). Why does the spec call this out?

3.Must C.ADD x0, a0 behave identically to the RVI HINT ADD x0, x0, a0?

4.Your A+C core runs a constrained LR/SC loop written with compressed instructions. Does the forward-progress guarantee apply?

4 questions