Introduction
A compressed instruction is 16 bits long (bits 1:0 are not 11) and does the work of one 32-bit
instruction, with fewer choices of register and a shorter immediate. The decoder writes it with a c.
name and the operands as the compressed form has them. The extension is split: Zca (the
integer instructions, and c.ebreak), Zcf (single-precision loads and stores, RV32 only),
Zcd (double precision), and C means Zca plus Zcf (RV32 with F) plus Zcd (with D).
Zcb, Zcmp and Zcmt use encodings the original C left reserved or
gave to the floating-point stack stores. The quadrant is bits 1:0 (0, 1, 2) and funct3 bits 15:13.
Encodings that overlap are chosen by the profile: c.jal (RV32) and c.addiw (RV64);
c.flw and c.fsw (RV32 with F) and c.ld and c.sd (RV64);
c.flwsp and c.ldsp, c.fswsp and c.sdsp likewise; and
c.fsdsp (Zcd) against cm.push, cm.pop, cm.popret,
cm.popretz, cm.mvsa01, cm.mva01s (Zcmp) and cm.jt,
cm.jalt (Zcmt), which are decoded only when the profile has no Zcd. (RV128 has c.lq
and c.sq in the places of c.fld and c.fsd; they are not decoded.)
Quadrant 0 (bits 1:0 = 00)
| Instruction | funct3 | Operands | Equivalent to | Extension |
|---|---|---|---|---|
c.addi4spn | 000 | rd′, sp, nzuimm (×4, up to 1020) | addi rd′, sp, nzuimm | Zca; nzuimm = 0 is reserved (all zero is the illegal instruction) |
c.fld | 001 | frd′, uimm(rs1′) (×8) | fld | Zcd |
c.lw | 010 | rd′, uimm(rs1′) (×4) | lw | Zca |
c.flw (rv32) | 011 | frd′, uimm(rs1′) (×4) | flw | Zcf |
c.ld (rv64) | 011 | rd′, uimm(rs1′) (×8) | ld | Zca |
c.lbu | 100, bits 12:10 = 000 | rd′, uimm(rs1′) (0 to 3) | lbu | Zcb |
c.lhu, c.lh | 100, bits 12:10 = 001; bit 6 = 0 / 1 | rd′, uimm(rs1′) (0 or 2) | lhu, lh | Zcb |
c.sb | 100, bits 12:10 = 010 | rs2′, uimm(rs1′) (0 to 3) | sb | Zcb |
c.sh | 100, bits 12:10 = 011, bit 6 = 0 | rs2′, uimm(rs1′) (0 or 2) | sh | Zcb |
c.fsd | 101 | frs2′, uimm(rs1′) (×8) | fsd | Zcd |
c.sw | 110 | rs2′, uimm(rs1′) (×4) | sw | Zca |
c.fsw (rv32) | 111 | frs2′, uimm(rs1′) (×4) | fsw | Zcf |
c.sd (rv64) | 111 | rs2′, uimm(rs1′) (×8) | sd | Zca |
Quadrant 1 (bits 1:0 = 01)
| Instruction | Encoding | Operands | Equivalent to | Extension and notes |
|---|---|---|---|---|
c.nop | 000, rd = 0, imm = 0 | addi x0, x0, 0 | Zca. With a non-zero immediate it is a HINT. | |
c.addi | 000 | rd, nzimm (6-bit signed) | addi rd, rd, nzimm | Zca. rd ≠ 0 with imm = 0 is a HINT. |
c.jal (rv32) | 001 | offset (12-bit, even) | jal ra, offset | Zca |
c.addiw (rv64) | 001 | rd, imm | addiw rd, rd, imm | Zca; rd = 0 is reserved |
c.li | 010 | rd, imm | addi rd, x0, imm | Zca; rd = 0 is a HINT |
c.addi16sp | 011, rd = 2 | sp, nzimm (×16, ±512) | addi sp, sp, nzimm | Zca; nzimm = 0 is reserved |
c.lui | 011, rd ≠ 0, 2 | rd, nzimm (6-bit, bits 17:12) | lui rd, nzimm | Zca; nzimm = 0 is reserved; rd = 0 with nzimm ≠ 0 is a HINT |
c.srli | 100, bits 11:10 = 00 | rd′, shamt | srli rd′, rd′, shamt | Zca; shamt = 0 is a HINT; on RV32 shamt bit 5 set is reserved |
c.srai | 100, bits 11:10 = 01 | rd′, shamt | srai | as c.srli |
c.andi | 100, bits 11:10 = 10 | rd′, imm | andi rd′, rd′, imm | Zca |
c.sub, c.xor, c.or, c.and | 100, bits 12:10 = 011, bits 6:5 = 00, 01, 10, 11 | rd′, rs2′ | sub, xor, or, and | Zca |
c.subw, c.addw (rv64) | 100, bits 12:10 = 111, bits 6:5 = 00, 01 | rd′, rs2′ | subw, addw | Zca |
c.zext.b, c.sext.b, c.zext.h, c.sext.h, c.zext.w (rv64), c.not | 100, bits 12:10 = 111, bits 6:5 = 11, bits 4:2 = 000, 001, 010, 011, 100, 101 | rd′ | andi rd′, rd′, 255, sext.b, zext.h, sext.h, add.uw rd′, rd′, x0, xori rd′, rd′, -1 | Zcb (sext.* also need Zbb, zext.h Zbb, zext.w Zba) |
c.mul | 100, bits 12:10 = 111, bits 6:5 = 10 | rd′, rs2′ | mul rd′, rd′, rs2′ | Zcb with M or Zmmul |
c.j | 101 | offset | jal x0, offset | Zca |
c.beqz, c.bnez | 110, 111 | rs1′, offset (±256) | beq rs1′, x0, offset, bne | Zca |
Quadrant 2 (bits 1:0 = 10)
| Instruction | Encoding | Operands | Equivalent to | Extension and notes |
|---|---|---|---|---|
c.slli | 000 | rd, shamt | slli rd, rd, shamt | Zca; rd = 0 or shamt = 0 is a HINT; on RV32 shamt bit 5 is reserved |
c.fldsp | 001 | frd, uimm(sp) (×8) | fld | Zcd |
c.lwsp | 010 | rd, uimm(sp) (×4) | lw | Zca; rd = 0 is reserved |
c.flwsp (rv32) | 011 | frd, uimm(sp) | flw | Zcf |
c.ldsp (rv64) | 011 | rd, uimm(sp) (×8) | ld | Zca; rd = 0 is reserved |
c.jr | 100, bit 12 = 0, rs2 = 0 | rs1 | jalr x0, 0(rs1) | Zca; rs1 = 0 is reserved |
c.mv | 100, bit 12 = 0, rs2 ≠ 0 | rd, rs2 | add rd, x0, rs2 | Zca; rd = 0 is a HINT |
c.ebreak | 0x9002 | ebreak | Zca | |
c.jalr | 100, bit 12 = 1, rs2 = 0 | rs1 | jalr ra, 0(rs1) | Zca; rs1 = 0 is c.ebreak |
c.add | 100, bit 12 = 1, rs2 ≠ 0 | rd, rs2 | add rd, rd, rs2 | Zca; rd = 0 is a HINT (the Zihintntl c.ntl.* are rd = 0 with rs2 = 2 to 5) |
c.fsdsp | 101 | frs2, uimm(sp) (×8) | fsd | Zcd |
c.swsp | 110 | rs2, uimm(sp) (×4) | sw | Zca |
c.fswsp (rv32) | 111 | frs2, uimm(sp) | fsw | Zcf |
c.sdsp (rv64) | 111 | rs2, uimm(sp) (×8) | sd | Zca |
Zcmp and Zcmt (quadrant 2, funct3 101)
These replace c.fsdsp and exist for processors without Zcd. In cm.push,
cm.pop, cm.popret and cm.popretz bits 7:4 are rlist
(4 = {ra}, 5 = {ra, s0} ... 14 = {ra, s0-s9}, 15 =
{ra, s0-s11}; 0 to 3 are reserved) and bits 3:2 are spimm. The stack space is the
registers rounded up to 16 bytes (RV32: 16, 32, 48 or 64; RV64: 16 to 112) plus 16 × spimm; the explorer writes
it as the adjustment (negative for cm.push).
| Instruction | Bits 12:8 / 12:10 | Operands | What it does |
|---|---|---|---|
cm.push | 11000 | {reg_list}, −stack_adj | Pushes ra and s-registers, decreases sp. |
cm.pop | 11010 | {reg_list}, stack_adj | Pops them and increases sp. |
cm.popretz | 11100 | {reg_list}, stack_adj | Pops, sets a0 to 0, increases sp and returns. |
cm.popret | 11110 | {reg_list}, stack_adj | Pops, increases sp and returns. |
cm.mvsa01 | 011, bits 6:5 = 01 | r1s′, r2s′ | Moves a0 and a1 to two different s-registers (equal numbers are reserved). |
cm.mva01s | 011, bits 6:5 = 11 | r1s′, r2s′ | Moves two s-registers to a0 and a1. |
cm.jt | 000, bits 9:7 = 000 | index (0 to 31) | Jumps to the address in the jump-vector table (CSR jvt) entry. |
cm.jalt | 000, bits 9:7 ≠ 000 | index (32 to 255) | As cm.jt, writing ra. |
The decoder does not follow a cm.jt jump (the target is read from memory at run time).
The illegal instruction
0x0000 is defined to be illegal (c.unimp), so that a jump into zeroed memory traps. The
explorer marks it illegal.