]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/asm,cmd/internal/obj/riscv: add support for riscv compressed instructions
authorJoel Sing <joel@sing.id.au>
Tue, 9 Sep 2025 14:17:00 +0000 (00:17 +1000)
committerJoel Sing <joel@sing.id.au>
Thu, 13 Nov 2025 12:17:37 +0000 (04:17 -0800)
Add support for compressed instructions in the RISC-V assembler. This
implements instruction validation and encoding for all instructions in
the "C" extension.

It is worth noting that the validation and encoding of these instructions
is far more convoluted then the typical instruction validation and
encoding. While the current model has been followed for now, it would be
worth revisiting this in the future and potentially switching to a table
based or even per-instruction implementation.

Additionally, the current instruction encoding is lacking some of the bits
needed for compressed instructions - this is solved by compressedEncoding,
which provides the missing information. This will also be addressed in the
future, likely by changing the instruction encoding format.

Updates #71105

Change-Id: I0f9359d63f93ebbdc6e708e79429b2d61eae220d
Reviewed-on: https://go-review.googlesource.com/c/go/+/713020
Reviewed-by: Mark Ryan <markdryan@rivosinc.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Meng Zhuo <mengzhuo1203@gmail.com>
src/cmd/asm/internal/arch/arch.go
src/cmd/asm/internal/asm/testdata/riscv64.s
src/cmd/asm/internal/asm/testdata/riscv64validation.s
src/cmd/internal/obj/riscv/obj.go

index 8481a8f378f2cb01445032e47ea2cce25d73e92c..fb9e78511108a8e97d0ce088e7d8a745613508f4 100644 (file)
@@ -92,7 +92,8 @@ func jumpX86(word string) bool {
 func jumpRISCV(word string) bool {
        switch word {
        case "BEQ", "BEQZ", "BGE", "BGEU", "BGEZ", "BGT", "BGTU", "BGTZ", "BLE", "BLEU", "BLEZ",
-               "BLT", "BLTU", "BLTZ", "BNE", "BNEZ", "CALL", "JAL", "JALR", "JMP":
+               "BLT", "BLTU", "BLTZ", "BNE", "BNEZ", "CALL", "CBEQZ", "CBNEZ", "CJ", "CJALR", "CJR",
+               "JAL", "JALR", "JMP":
                return true
        }
        return false
index 702b82223b36afabe6d0a32c6139dad64678bc2d..4615119af00fd9d89983c51d58f34aac32b57731 100644 (file)
@@ -372,6 +372,76 @@ start:
        // 21.7: Double-Precision Floating-Point Classify Instruction
        FCLASSD F0, X5                                  // d31200e2
 
+       //
+       // "C" Extension for Compressed Instructions, Version 2.0
+       //
+
+       // 26.3.1: Compressed Stack-Pointer-Based Loads and Stores
+       CLWSP   20(SP), X10                             // 5245
+       CLDSP   24(SP), X10                             // 6265
+       CFLDSP  32(SP), F10                             // 0235
+       CSWSP   X10, 20(SP)                             // 2aca
+       CSDSP   X10, 24(SP)                             // 2aec
+       CFSDSP  F10, 32(SP)                             // 2ab0
+
+       // 26.3.2: Compressed Register-Based Loads and Stores
+       CLW     20(X10), X11                            // 4c49
+       CLD     24(X10), X11                            // 0c6d
+       CFLD    32(X10), F11                            // 0c31
+       CSW     X11, 20(X10)                            // 4cc9
+       CSD     X11, 24(X10)                            // 0ced
+       CFSD    F11, 32(X10)                            // 0cb1
+
+       // 26.4: Compressed Control Transfer Instructions
+       CJ      1(PC)                                   // 09a0
+       CJR     X5                                      // 8282
+       CJALR   X5                                      // 8292
+       CBEQZ   X10, 1(PC)                              // 09c1
+       CBNEZ   X10, 1(PC)                              // 09e1
+
+       // 26.5.1: Compressed Integer Constant-Generation Instructions
+       CLI     $-32, X5                                // 8152
+       CLI     $31, X5                                 // fd42
+       CLUI    $-32, X5                                // 8172
+       CLUI    $31, X5                                 // fd62
+
+       // 26.5.2: Compressed Integer Register-Immediate Operations
+       CADD    $-32, X5                                // 8112
+       CADD    $31, X5                                 // fd02
+       CADDI   $-32, X5                                // 8112
+       CADDI   $31, X5                                 // fd02
+       CADDW   $-32, X5                                // 8132
+       CADDW   $31, X5                                 // fd22
+       CADDIW  $-32, X5                                // 8132
+       CADDIW  $31, X5                                 // fd22
+       CADDI16SP $-512, SP                             // 0171
+       CADDI16SP $496, SP                              // 7d61
+       CADDI4SPN $4, SP, X10                           // 4800
+       CADDI4SPN $1020, SP, X10                        // e81f
+       CSLLI   $63, X5                                 // fe12
+       CSRLI   $63, X10                                // 7d91
+       CSRAI   $63, X10                                // 7d95
+       CAND    $-32, X10                               // 0199
+       CAND    $31, X10                                // 7d89
+       CANDI   $-32, X10                               // 0199
+       CANDI   $31, X10                                // 7d89
+
+       // 26.5.3: Compressed Integer Register-Register Operations
+       CMV     X6, X5                                  // 9a82
+       CADD    X9, X8                                  // 2694
+       CAND    X9, X8                                  // 658c
+       COR     X9, X8                                  // 458c
+       CXOR    X9, X8                                  // 258c
+       CSUB    X9, X8                                  // 058c
+       CADDW   X9, X8                                  // 259c
+       CSUBW   X9, X8                                  // 059c
+
+       // 26.5.5: Compressed NOP Instruction
+       CNOP                                            // 0100
+
+       // 26.5.6: Compressed Breakpoint Instruction
+       CEBREAK                                         // 0290
+
        // 28.4.1: Address Generation Instructions (Zba)
        ADDUW           X10, X11, X12                   // 3b86a508
        ADDUW           X10, X11                        // bb85a508
index c8ae4c92114bbc396964154764b7c949108390ba..6a2e5f92dee297e769693badc9fb88bb47b5558f 100644 (file)
@@ -15,6 +15,144 @@ TEXT validation(SB),$0
        WORD    $-1                             // ERROR "must be in range [0x0, 0xffffffff]"
        WORD    $0x100000000                    // ERROR "must be in range [0x0, 0xffffffff]"
 
+       //
+       // "C" Extension for Compressed Instructions, Version 2.0
+       //
+       CLWSP   20(X5), X10                             // ERROR "rs2 must be SP/X2"
+       CLWSP   20(SP), X0                              // ERROR "cannot use register X0"
+       CLWSP   20(SP), F10                             // ERROR "expected integer register in rd position"
+       CLWSP   22(SP), X10                             // ERROR "must be a multiple of 4"
+       CLDSP   24(X5), X10                             // ERROR "rs2 must be SP/X2"
+       CLDSP   24(SP), X0                              // ERROR "cannot use register X0"
+       CLDSP   24(SP), F10                             // ERROR "expected integer register in rd position"
+       CLDSP   28(SP), X10                             // ERROR "must be a multiple of 8"
+       CFLDSP  32(X5), F10                             // ERROR "rs2 must be SP/X2"
+       CFLDSP  32(SP), X10                             // ERROR "expected float register in rd position"
+       CFLDSP  36(SP), F10                             // ERROR "must be a multiple of 8"
+       CSWSP   X10, 20(X5)                             // ERROR "rd must be SP/X2"
+       CSWSP   F10, 20(SP)                             // ERROR "expected integer register in rs2 position"
+       CSWSP   X10, 22(SP)                             // ERROR "must be a multiple of 4"
+       CSDSP   X10, 24(X5)                             // ERROR "rd must be SP/X2"
+       CSDSP   F10, 24(SP)                             // ERROR "expected integer register in rs2 position"
+       CSDSP   X10, 28(SP)                             // ERROR "must be a multiple of 8"
+       CFSDSP  F10, 32(X5)                             // ERROR "rd must be SP/X2"
+       CFSDSP  X10, 32(SP)                             // ERROR "expected float register in rs2 position"
+       CFSDSP  F10, 36(SP)                             // ERROR "must be a multiple of 8"
+       CLW     20(X10), F11                            // ERROR "expected integer prime register in rd position"
+       CLW     20(X5), X11                             // ERROR "expected integer prime register in rs1 position"
+       CLW     20(X10), X5                             // ERROR "expected integer prime register in rd position"
+       CLW     -1(X10), X11                            // ERROR "must be in range [0, 127]"
+       CLW     22(X10), X11                            // ERROR "must be a multiple of 4"
+       CLW     128(X10), X11                           // ERROR "must be in range [0, 127]"
+       CLD     24(X10), F11                            // ERROR "expected integer prime register in rd position"
+       CLD     24(X5), X11                             // ERROR "expected integer prime register in rs1 position"
+       CLD     -1(X10), X11                            // ERROR "must be in range [0, 255]"
+       CLD     30(X10), X11                            // ERROR "must be a multiple of 8"
+       CLD     256(X10), X11                           // ERROR "must be in range [0, 255]"
+       CFLD    32(X10), X11                            // ERROR "expected float prime register in rd position"
+       CFLD    32(X5), F11                             // ERROR "expected integer prime register in rs1 position"
+       CFLD    -1(X10), F11                            // ERROR "must be in range [0, 255]"
+       CFLD    34(X10), F11                            // ERROR "must be a multiple of 8"
+       CFLD    256(X10), F11                           // ERROR "must be in range [0, 255]"
+       CSW     F11, 20(X10)                            // ERROR "expected integer prime register in rs2 position"
+       CSW     X11, -1(X10)                            // ERROR "must be in range [0, 127]"
+       CSW     X11, 22(X10)                            // ERROR "must be a multiple of 4"
+       CSW     X11, 128(X10)                           // ERROR "must be in range [0, 127]"
+       CSD     F11, 24(X10)                            // ERROR "expected integer prime register in rs2 position"
+       CSD     X11, -1(X10)                            // ERROR "must be in range [0, 255]"
+       CSD     X11, 28(X10)                            // ERROR "must be a multiple of 8"
+       CSD     X11, 256(X10)                           // ERROR "must be in range [0, 255]"
+       CFSD    X11, 32(X10)                            // ERROR "expected float prime register in rs2 position"
+       CFSD    F11, -1(X10)                            // ERROR "must be in range [0, 255]"
+       CFSD    F11, 36(X10)                            // ERROR "must be a multiple of 8"
+       CFSD    F11, 256(X10)                           // ERROR "must be in range [0, 255]"
+       CJR     X0                                      // ERROR "cannot use register X0 in rs1"
+       CJR     X10, X11                                // ERROR "expected no register in rs2"
+       CJALR   X0                                      // ERROR "cannot use register X0 in rs1"
+       CJALR   X10, X11                                // ERROR "expected no register in rd"
+       CBEQZ   X5, 1(PC)                               // ERROR "expected integer prime register in rs1"
+       CBNEZ   X5, 1(PC)                               // ERROR "expected integer prime register in rs1"
+       CLI     $3, X0                                  // ERROR "cannot use register X0 in rd"
+       CLI     $-33, X5                                // ERROR "must be in range [-32, 31]"
+       CLI     $32, X5                                 // ERROR "must be in range [-32, 31]"
+       CLUI    $0, X5                                  // ERROR "immediate cannot be zero"
+       CLUI    $3, X0                                  // ERROR "cannot use register X0 in rd"
+       CLUI    $3, X2                                  // ERROR "cannot use register SP/X2 in rd"
+       CLUI    $-33, X5                                // ERROR "must be in range [-32, 31]"
+       CLUI    $32, X5                                 // ERROR "must be in range [-32, 31]"
+       CADD    $31, X5, X6                             // ERROR "rd must be the same as rs1"
+       CADD    $-33, X5                                // ERROR "must be in range [-32, 31]"
+       CADD    $32, X5                                 // ERROR "must be in range [-32, 31]"
+       CADDI   $0, X5                                  // ERROR "immediate cannot be zero"
+       CADDI   $31, X5, X6                             // ERROR "rd must be the same as rs1"
+       CADDI   $-33, X5                                // ERROR "must be in range [-32, 31]"
+       CADDI   $32, X5                                 // ERROR "must be in range [-32, 31]"
+       CADDW   $-33, X5                                // ERROR "must be in range [-32, 31]"
+       CADDW   $32, X5                                 // ERROR "must be in range [-32, 31]"
+       CADDIW  $-33, X5                                // ERROR "must be in range [-32, 31]"
+       CADDIW  $32, X5                                 // ERROR "must be in range [-32, 31]"
+       CADDI16SP $0, SP                                // ERROR "immediate cannot be zero"
+       CADDI16SP $16, X5                               // ERROR "rd must be SP/X2"
+       CADDI16SP $-513, SP                             // ERROR "must be in range [-512, 511]"
+       CADDI16SP $20, SP                               // ERROR "must be a multiple of 16"
+       CADDI16SP $512, SP                              // ERROR "must be in range [-512, 511]"
+       CADDI4SPN $4, SP, X5                            // ERROR "expected integer prime register in rd"
+       CADDI4SPN $4, X5, X10                           // ERROR "SP/X2 must be in rs1"
+       CADDI4SPN $-1, SP, X10                          // ERROR "must be in range [0, 1023]"
+       CADDI4SPN $0, SP, X10                           // ERROR "immediate cannot be zero"
+       CADDI4SPN $6, SP, X10                           // ERROR "must be a multiple of 4"
+       CADDI4SPN $1024, SP, X10                        // ERROR "must be in range [0, 1023]"
+       CSLLI   $63, X5, X6                             // ERROR "rd must be the same as rs1"
+       CSLLI   $-1, X5                                 // ERROR "must be in range [0, 63]"
+       CSLLI   $0, X5                                  // ERROR "immediate cannot be zero"
+       CSLLI   $64, X5                                 // ERROR "must be in range [0, 63]"
+       CSRLI   $63, X10, X11                           // ERROR "rd must be the same as rs1"
+       CSRLI   $63, X5                                 // ERROR "expected integer prime register in rd"
+       CSRLI   $-1, X10                                // ERROR "must be in range [0, 63]"
+       CSRLI   $0, X10                                 // ERROR "immediate cannot be zero"
+       CSRLI   $64, X10                                // ERROR "must be in range [0, 63]"
+       CSRAI   $63, X10, X11                           // ERROR "rd must be the same as rs1"
+       CSRAI   $63, X5                                 // ERROR "expected integer prime register in rd"
+       CSRAI   $-1, X10                                // ERROR "must be in range [0, 63]"
+       CSRAI   $0, X10                                 // ERROR "immediate cannot be zero"
+       CSRAI   $64, X10                                // ERROR "must be in range [0, 63]"
+       CAND    $1, X10, X11                            // ERROR "rd must be the same as rs1"
+       CAND    $1, X5                                  // ERROR "expected integer prime register in rd"
+       CAND    $-64, X10                               // ERROR "must be in range [-32, 31]"
+       CAND    $63, X10                                // ERROR "must be in range [-32, 31]"
+       CANDI   $1, X10, X11                            // ERROR "rd must be the same as rs1"
+       CANDI   $1, X5                                  // ERROR "expected integer prime register in rd"
+       CANDI   $-64, X10                               // ERROR "must be in range [-32, 31]"
+       CANDI   $63, X10                                // ERROR "must be in range [-32, 31]"
+       CMV     X0, X5                                  // ERROR "cannot use register X0 in rs2"
+       CMV     X5, X6, X7                              // ERROR "expected no register in rs1"
+       CMV     X5, X0                                  // ERROR "cannot use register X0 in rd"
+       CMV     F1, X5                                  // ERROR "expected integer register in rs2"
+       CMV     X5, F1                                  // ERROR "expected integer register in rd"
+       CADD    X5, X6, X7                              // ERROR "rd must be the same as rs1"
+       CADD    X0, X8                                  // ERROR "cannot use register X0 in rs2"
+       CADD    X8, X0                                  // ERROR "cannot use register X0 in rd"
+       CAND    X10, X11, X12                           // ERROR "rd must be the same as rs1"
+       CAND    X5, X11                                 // ERROR "expected integer prime register in rs2"
+       CAND    X10, X5                                 // ERROR "expected integer prime register in rd"
+       COR     X10, X11, X12                           // ERROR "rd must be the same as rs1"
+       COR     X5, X11                                 // ERROR "expected integer prime register in rs2"
+       COR     X10, X5                                 // ERROR "expected integer prime register in rd"
+       CXOR    X10, X11, X12                           // ERROR "rd must be the same as rs1"
+       CXOR    X5, X11                                 // ERROR "expected integer prime register in rs2"
+       CXOR    X10, X5                                 // ERROR "expected integer prime register in rd"
+       CSUB    X10, X11, X12                           // ERROR "rd must be the same as rs1"
+       CSUB    X5, X11                                 // ERROR "expected integer prime register in rs2"
+       CSUB    X10, X5                                 // ERROR "expected integer prime register in rd"
+       CADDW   X10, X11, X12                           // ERROR "rd must be the same as rs1"
+       CADDW   X5, X11                                 // ERROR "expected integer prime register in rs2"
+       CADDW   X10, X5                                 // ERROR "expected integer prime register in rd"
+       CSUBW   X10, X11, X12                           // ERROR "rd must be the same as rs1"
+       CSUBW   X5, X11                                 // ERROR "expected integer prime register in rs2"
+       CSUBW   X10, X5                                 // ERROR "expected integer prime register in rd"
+       CNOP    X10                                     // ERROR "expected no register in rs2"
+       CEBREAK X10                                     // ERROR "expected no register in rs2"
+
        //
        // "V" Standard Extension for Vector Operations, Version 1.0
        //
index f4a7f0b1e7942786b132d30546757580b5f6aa0c..3deab34d312eab354748529fb0e0b117fd24f5b4 100644 (file)
@@ -70,6 +70,10 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
        // form of the instruction.
        if p.From.Type == obj.TYPE_CONST {
                switch p.As {
+               case ACSUB:
+                       p.As, p.From.Offset = ACADDI, -p.From.Offset
+               case ACSUBW:
+                       p.As, p.From.Offset = ACADDIW, -p.From.Offset
                case ASUB:
                        p.As, p.From.Offset = AADDI, -p.From.Offset
                case ASUBW:
@@ -381,6 +385,10 @@ func InvertBranch(as obj.As) obj.As {
                return ABEQ
        case ABNEZ:
                return ABEQZ
+       case ACBEQZ:
+               return ACBNEZ
+       case ACBNEZ:
+               return ACBEQZ
        default:
                panic("InvertBranch: not a branch")
        }
@@ -394,7 +402,7 @@ func containsCall(sym *obj.LSym) bool {
                switch p.As {
                case obj.ACALL:
                        return true
-               case AJAL, AJALR:
+               case ACJALR, AJAL, AJALR:
                        if p.From.Type == obj.TYPE_REG && p.From.Reg == REG_LR {
                                return true
                        }
@@ -670,7 +678,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
 
                for p := cursym.Func().Text; p != nil; p = p.Link {
                        switch p.As {
-                       case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ:
+                       case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ, ACBEQZ, ACBNEZ, ACJ:
                                if p.To.Type != obj.TYPE_BRANCH {
                                        ctxt.Diag("%v: instruction with branch-like opcode lacks destination", p)
                                        break
@@ -752,7 +760,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
        // instructions will break everything--don't do it!
        for p := cursym.Func().Text; p != nil; p = p.Link {
                switch p.As {
-               case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ:
+               case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ, ACBEQZ, ACBNEZ, ACJ:
                        switch p.To.Type {
                        case obj.TYPE_BRANCH:
                                p.To.Type, p.To.Offset = obj.TYPE_CONST, p.To.Target().Pc-p.Pc
@@ -1042,6 +1050,16 @@ func regVal(r, min, max uint32) uint32 {
        return r - min
 }
 
+// regCI returns an integer register for use in a compressed instruction.
+func regCI(r uint32) uint32 {
+       return regVal(r, REG_X8, REG_X15)
+}
+
+// regCF returns a float register for use in a compressed instruction.
+func regCF(r uint32) uint32 {
+       return regVal(r, REG_F8, REG_F15)
+}
+
 // regI returns an integer register.
 func regI(r uint32) uint32 {
        return regVal(r, REG_X0, REG_X31)
@@ -1123,6 +1141,24 @@ func wantImmU(ctxt *obj.Link, ins *instruction, imm int64, nbits uint) {
        }
 }
 
+func wantScaledImm(ctxt *obj.Link, ins *instruction, imm int64, nbits uint, scale int64, signed bool) {
+       if err := immFits(imm, nbits, signed); err != nil {
+               ctxt.Diag("%v: %v", ins, err)
+               return
+       }
+       if imm%scale != 0 {
+               ctxt.Diag("%v: unsigned immediate %d must be a multiple of %d", ins, imm, scale)
+       }
+}
+
+func wantScaledImmI(ctxt *obj.Link, ins *instruction, imm int64, nbits uint, scale int64) {
+       wantScaledImm(ctxt, ins, imm, nbits, scale, true)
+}
+
+func wantScaledImmU(ctxt *obj.Link, ins *instruction, imm int64, nbits uint, scale int64) {
+       wantScaledImm(ctxt, ins, imm, nbits, scale, false)
+}
+
 func wantReg(ctxt *obj.Link, ins *instruction, pos string, descr string, r, min, max uint32) {
        if r < min || r > max {
                var suffix string
@@ -1144,11 +1180,23 @@ func wantIntReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
        wantReg(ctxt, ins, pos, "integer", r, REG_X0, REG_X31)
 }
 
+// wantIntPrimeReg checks that r is an integer register that can be used
+// in a prime register field of a compressed instruction.
+func wantIntPrimeReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
+       wantReg(ctxt, ins, pos, "integer prime", r, REG_X8, REG_X15)
+}
+
 // wantFloatReg checks that r is a floating-point register.
 func wantFloatReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
        wantReg(ctxt, ins, pos, "float", r, REG_F0, REG_F31)
 }
 
+// wantFloatPrimeReg checks that r is an floating-point register that can
+// be used in a prime register field of a compressed instruction.
+func wantFloatPrimeReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
+       wantReg(ctxt, ins, pos, "float prime", r, REG_F8, REG_F15)
+}
+
 // wantVectorReg checks that r is a vector register.
 func wantVectorReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
        wantReg(ctxt, ins, pos, "vector", r, REG_V0, REG_V31)
@@ -1161,6 +1209,206 @@ func wantEvenOffset(ctxt *obj.Link, ins *instruction, offset int64) {
        }
 }
 
+func validateCA(ctxt *obj.Link, ins *instruction) {
+       wantIntPrimeReg(ctxt, ins, "rd", ins.rd)
+       if ins.rd != ins.rs1 {
+               ctxt.Diag("%v: rd must be the same as rs1", ins)
+       }
+       wantIntPrimeReg(ctxt, ins, "rs2", ins.rs2)
+}
+
+func validateCB(ctxt *obj.Link, ins *instruction) {
+       if (ins.as == ACSRAI || ins.as == ACSRLI) && ins.imm == 0 {
+               ctxt.Diag("%v: immediate cannot be zero", ins)
+       } else if ins.as == ACSRAI || ins.as == ACSRLI {
+               wantImmU(ctxt, ins, ins.imm, 6)
+       } else if ins.as == ACBEQZ || ins.as == ACBNEZ {
+               wantImmI(ctxt, ins, ins.imm, 9)
+       } else {
+               wantImmI(ctxt, ins, ins.imm, 6)
+       }
+       if ins.as == ACBEQZ || ins.as == ACBNEZ {
+               wantNoneReg(ctxt, ins, "rd", ins.rd)
+               wantIntPrimeReg(ctxt, ins, "rs1", ins.rs1)
+       } else {
+               wantIntPrimeReg(ctxt, ins, "rd", ins.rd)
+               if ins.rd != ins.rs1 {
+                       ctxt.Diag("%v: rd must be the same as rs1", ins)
+               }
+       }
+       wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+}
+
+func validateCI(ctxt *obj.Link, ins *instruction) {
+       if ins.as != ACNOP && ins.rd == REG_X0 {
+               ctxt.Diag("%v: cannot use register X0 in rd", ins)
+       }
+       if ins.as == ACLUI && ins.rd == REG_X2 {
+               ctxt.Diag("%v: cannot use register SP/X2 in rd", ins)
+       }
+       if ins.as != ACLI && ins.as != ACLUI && ins.as != ACLWSP && ins.as != ACLDSP && ins.as != ACFLDSP && ins.rd != ins.rs1 {
+               ctxt.Diag("%v: rd must be the same as rs1", ins)
+       }
+       if ins.as == ACADDI16SP && ins.rd != REG_SP {
+               ctxt.Diag("%v: rd must be SP/X2", ins)
+       }
+       if (ins.as == ACLWSP || ins.as == ACLDSP || ins.as == ACFLDSP) && ins.rs2 != REG_SP {
+               ctxt.Diag("%v: rs2 must be SP/X2", ins)
+       }
+       if (ins.as == ACADDI || ins.as == ACADDI16SP || ins.as == ACLUI || ins.as == ACSLLI) && ins.imm == 0 {
+               ctxt.Diag("%v: immediate cannot be zero", ins)
+       } else if ins.as == ACSLLI {
+               wantImmU(ctxt, ins, ins.imm, 6)
+       } else if ins.as == ACLWSP {
+               wantScaledImmU(ctxt, ins, ins.imm, 8, 4)
+       } else if ins.as == ACLDSP || ins.as == ACFLDSP {
+               wantScaledImmU(ctxt, ins, ins.imm, 9, 8)
+       } else if ins.as == ACADDI16SP {
+               wantScaledImmI(ctxt, ins, ins.imm, 10, 16)
+       } else {
+               wantImmI(ctxt, ins, ins.imm, 6)
+       }
+       switch ins.as {
+       case ACNOP, ACADDI, ACADDIW, ACSLLI:
+               wantIntReg(ctxt, ins, "rd", ins.rd)
+               wantIntReg(ctxt, ins, "rs1", ins.rs1)
+               wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+       case ACLWSP, ACLDSP:
+               wantIntReg(ctxt, ins, "rd", ins.rd)
+               wantNoneReg(ctxt, ins, "rs1", ins.rs1)
+               wantIntReg(ctxt, ins, "rs2", ins.rs2)
+       case ACFLDSP:
+               wantFloatReg(ctxt, ins, "rd", ins.rd)
+               wantNoneReg(ctxt, ins, "rs1", ins.rs1)
+               wantIntReg(ctxt, ins, "rs2", ins.rs2)
+       case ACADDI16SP:
+               wantIntReg(ctxt, ins, "rd", ins.rd)
+               wantIntReg(ctxt, ins, "rs1", ins.rs1)
+               wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+       default:
+               wantIntReg(ctxt, ins, "rd", ins.rd)
+               wantNoneReg(ctxt, ins, "rs1", ins.rs1)
+               wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+       }
+}
+
+func validateCIW(ctxt *obj.Link, ins *instruction) {
+       wantScaledImmU(ctxt, ins, ins.imm, 10, 4)
+       wantIntPrimeReg(ctxt, ins, "rd", ins.rd)
+       wantIntReg(ctxt, ins, "rs1", ins.rs1)
+       wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+       if ins.imm == 0 {
+               ctxt.Diag("%v: immediate cannot be zero", ins)
+       }
+       if ins.rs1 != REG_SP {
+               ctxt.Diag("%v: SP/X2 must be in rs1", ins)
+       }
+}
+
+func validateCJ(ctxt *obj.Link, ins *instruction) {
+       wantEvenOffset(ctxt, ins, ins.imm)
+       wantImmI(ctxt, ins, ins.imm, 12)
+       if ins.as != ACJ {
+               wantNoneReg(ctxt, ins, "rd", ins.rd)
+               wantIntReg(ctxt, ins, "rs1", ins.rs1)
+               wantIntReg(ctxt, ins, "rs2", ins.rs2)
+               if ins.rs1 == REG_X0 {
+                       ctxt.Diag("%v: cannot use register X0 in rs1", ins)
+               }
+       }
+}
+
+func validateCL(ctxt *obj.Link, ins *instruction) {
+       if ins.as == ACLW {
+               wantScaledImmU(ctxt, ins, ins.imm, 7, 4)
+       } else if ins.as == ACLD || ins.as == ACFLD {
+               wantScaledImmU(ctxt, ins, ins.imm, 8, 8)
+       } else {
+               wantImmI(ctxt, ins, ins.imm, 5)
+       }
+       if ins.as == ACFLD {
+               wantFloatPrimeReg(ctxt, ins, "rd", ins.rd)
+       } else {
+               wantIntPrimeReg(ctxt, ins, "rd", ins.rd)
+       }
+       wantIntPrimeReg(ctxt, ins, "rs1", ins.rs1)
+       wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+}
+
+func validateCR(ctxt *obj.Link, ins *instruction) {
+       switch ins.as {
+       case ACJR, ACJALR:
+               wantNoneReg(ctxt, ins, "rd", ins.rd)
+               wantIntReg(ctxt, ins, "rs1", ins.rs1)
+               wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+               if ins.rs1 == REG_X0 {
+                       ctxt.Diag("%v: cannot use register X0 in rs1", ins)
+               }
+       case ACMV:
+               wantIntReg(ctxt, ins, "rd", ins.rd)
+               wantNoneReg(ctxt, ins, "rs1", ins.rs1)
+               wantIntReg(ctxt, ins, "rs2", ins.rs2)
+               if ins.rd == REG_X0 {
+                       ctxt.Diag("%v: cannot use register X0 in rd", ins)
+               }
+               if ins.rs2 == REG_X0 {
+                       ctxt.Diag("%v: cannot use register X0 in rs2", ins)
+               }
+       case ACEBREAK:
+               wantNoneReg(ctxt, ins, "rd", ins.rd)
+               wantNoneReg(ctxt, ins, "rs1", ins.rs1)
+               wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+       case ACADD:
+               wantIntReg(ctxt, ins, "rd", ins.rd)
+               if ins.rd == REG_X0 {
+                       ctxt.Diag("%v: cannot use register X0 in rd", ins)
+               }
+               if ins.rd != ins.rs1 {
+                       ctxt.Diag("%v: rd must be the same as rs1", ins)
+               }
+               wantIntReg(ctxt, ins, "rs2", ins.rs2)
+               if ins.rs2 == REG_X0 {
+                       ctxt.Diag("%v: cannot use register X0 in rs2", ins)
+               }
+       }
+}
+
+func validateCS(ctxt *obj.Link, ins *instruction) {
+       if ins.as == ACSW {
+               wantScaledImmU(ctxt, ins, ins.imm, 7, 4)
+       } else if ins.as == ACSD || ins.as == ACFSD {
+               wantScaledImmU(ctxt, ins, ins.imm, 8, 8)
+       } else {
+               wantImmI(ctxt, ins, ins.imm, 5)
+       }
+       wantNoneReg(ctxt, ins, "rd", ins.rd)
+       wantIntPrimeReg(ctxt, ins, "rs1", ins.rs1)
+       if ins.as == ACFSD {
+               wantFloatPrimeReg(ctxt, ins, "rs2", ins.rs2)
+       } else {
+               wantIntPrimeReg(ctxt, ins, "rs2", ins.rs2)
+       }
+}
+
+func validateCSS(ctxt *obj.Link, ins *instruction) {
+       if ins.rd != REG_SP {
+               ctxt.Diag("%v: rd must be SP/X2", ins)
+       }
+       if ins.as == ACSWSP {
+               wantScaledImmU(ctxt, ins, ins.imm, 8, 4)
+       } else if ins.as == ACSDSP || ins.as == ACFSDSP {
+               wantScaledImmU(ctxt, ins, ins.imm, 9, 8)
+       } else {
+               wantImmI(ctxt, ins, ins.imm, 6)
+       }
+       wantNoneReg(ctxt, ins, "rs1", ins.rs1)
+       if ins.as == ACFSDSP {
+               wantFloatReg(ctxt, ins, "rs2", ins.rs2)
+       } else {
+               wantIntReg(ctxt, ins, "rs2", ins.rs2)
+       }
+}
+
 func validateRII(ctxt *obj.Link, ins *instruction) {
        wantIntReg(ctxt, ins, "rd", ins.rd)
        wantIntReg(ctxt, ins, "rs1", ins.rs1)
@@ -1422,6 +1670,91 @@ func validateRaw(ctxt *obj.Link, ins *instruction) {
        wantImmU(ctxt, ins, ins.imm, 32)
 }
 
+// compressedEncoding returns the fixed instruction encoding for a compressed
+// instruction.
+func compressedEncoding(as obj.As) uint32 {
+       enc := encode(as)
+       if enc == nil {
+               panic("compressedEncoding: could not encode instruction")
+       }
+
+       // TODO: this can be removed once encode is reworked to return the
+       // necessary bits.
+       op := uint32(0)
+       switch as {
+       case ACSUB:
+               op = 0b100011<<10 | 0b00<<5
+       case ACXOR:
+               op = 0b100011<<10 | 0b01<<5
+       case ACOR:
+               op = 0b100011<<10 | 0b10<<5
+       case ACAND:
+               op = 0b100011<<10 | 0b11<<5
+       case ACSUBW:
+               op = 0b100111<<10 | 0b00<<5
+       case ACADDW:
+               op = 0b100111<<10 | 0b01<<5
+       case ACBEQZ:
+               op = 0b110 << 13
+       case ACBNEZ:
+               op = 0b111 << 13
+       case ACANDI:
+               op = 0b100<<13 | 0b10<<10
+       case ACSRAI:
+               op = 0b100<<13 | 0b01<<10
+       case ACSRLI:
+               op = 0b100<<13 | 0b00<<10
+       case ACLI:
+               op = 0b010 << 13
+       case ACLUI:
+               op = 0b011 << 13
+       case ACLWSP:
+               op = 0b010 << 13
+       case ACLDSP:
+               op = 0b011 << 13
+       case ACFLDSP:
+               op = 0b001 << 13
+       case ACADDIW:
+               op = 0b001 << 13
+       case ACADDI16SP:
+               op = 0b011 << 13
+       case ACADDI4SPN:
+               op = 0b000 << 13
+       case ACJ:
+               op = 0b101 << 13
+       case ACLW:
+               op = 0b010 << 13
+       case ACLD:
+               op = 0b011 << 13
+       case ACFLD:
+               op = 0b001 << 13
+       case ACJR:
+               op = 0b1000 << 12
+       case ACMV:
+               op = 0b1000 << 12
+       case ACEBREAK:
+               op = 0b1001 << 12
+       case ACJALR:
+               op = 0b1001 << 12
+       case ACADD:
+               op = 0b1001 << 12
+       case ACSW:
+               op = 0b110 << 13
+       case ACSD:
+               op = 0b111 << 13
+       case ACFSD:
+               op = 0b101 << 13
+       case ACSWSP:
+               op = 0b110 << 13
+       case ACSDSP:
+               op = 0b111 << 13
+       case ACFSDSP:
+               op = 0b101 << 13
+       }
+
+       return op | enc.opcode
+}
+
 // encodeBitPattern encodes an immediate value by extracting the specified
 // bit pattern from the given immediate. Each value in the pattern specifies
 // the position of the bit to extract from the immediate, which are then
@@ -1434,6 +1767,149 @@ func encodeBitPattern(imm uint32, pattern []int) uint32 {
        return outImm
 }
 
+// encodeCA encodes a compressed arithmetic (CA-type) instruction.
+func encodeCA(ins *instruction) uint32 {
+       return compressedEncoding(ins.as) | regCI(ins.rd)<<7 | regCI(ins.rs2)<<2
+}
+
+// encodeCBImmediate encodes an immediate for a CB-type RISC-V instruction.
+func encodeCBImmediate(imm uint32) uint32 {
+       // Bit order - [8|4:3|7:6|2:1|5]
+       bits := encodeBitPattern(imm, []int{8, 4, 3, 7, 6, 2, 1, 5})
+       return (bits>>5)<<10 | (bits&0x1f)<<2
+}
+
+// encodeCB encodes a compressed branch (CB-type) instruction.
+func encodeCB(ins *instruction) uint32 {
+       imm := uint32(0)
+       if ins.as == ACBEQZ || ins.as == ACBNEZ {
+               imm = immI(ins.as, ins.imm, 9)
+               imm = encodeBitPattern(imm, []int{8, 4, 3, 7, 6, 2, 1, 5})
+       } else if ins.as == ACANDI {
+               imm = immI(ins.as, ins.imm, 6)
+               imm = (imm>>5)<<7 | imm&0x1f
+       } else if ins.as == ACSRAI || ins.as == ACSRLI {
+               imm = immU(ins.as, ins.imm, 6)
+               imm = (imm>>5)<<7 | imm&0x1f
+       }
+       return compressedEncoding(ins.as) | (imm>>5)<<10 | regCI(ins.rs1)<<7 | (imm&0x1f)<<2
+}
+
+// encodeCI encodes a compressed immediate (CI-type) instruction.
+func encodeCI(ins *instruction) uint32 {
+       imm := uint32(ins.imm)
+       if ins.as == ACLWSP {
+               // Bit order [5:2|7:6]
+               imm = encodeBitPattern(imm, []int{5, 4, 3, 2, 7, 6})
+       } else if ins.as == ACLDSP || ins.as == ACFLDSP {
+               // Bit order [5:3|8:6]
+               imm = encodeBitPattern(imm, []int{5, 4, 3, 8, 7, 6})
+       } else if ins.as == ACADDI16SP {
+               // Bit order [9|4|6|8:7|5]
+               imm = encodeBitPattern(imm, []int{9, 4, 6, 8, 7, 5})
+       }
+       rd := uint32(0)
+       if ins.as == ACFLDSP {
+               rd = regF(ins.rd)
+       } else {
+               rd = regI(ins.rd)
+       }
+       return compressedEncoding(ins.as) | ((imm>>5)&0x1)<<12 | rd<<7 | (imm&0x1f)<<2
+}
+
+// encodeCIW encodes a compressed immediate wide (CIW-type) instruction.
+func encodeCIW(ins *instruction) uint32 {
+       imm := uint32(ins.imm)
+       if ins.as == ACADDI4SPN {
+               // Bit order [5:4|9:6|2|3]
+               imm = encodeBitPattern(imm, []int{5, 4, 9, 8, 7, 6, 2, 3})
+       }
+       return compressedEncoding(ins.as) | imm<<5 | regCI(ins.rd)<<2
+}
+
+// encodeCJImmediate encodes an immediate for a CJ-type RISC-V instruction.
+func encodeCJImmediate(imm uint32) uint32 {
+       // Bit order - [11|4|9:8|10|6|7|3:1|5]
+       bits := encodeBitPattern(imm, []int{11, 4, 9, 8, 10, 6, 7, 3, 2, 1, 5})
+       return bits << 2
+}
+
+// encodeCJ encodes a compressed jump (CJ-type) instruction.
+func encodeCJ(ins *instruction) uint32 {
+       return compressedEncoding(ins.as) | encodeCJImmediate(uint32(ins.imm))
+}
+
+// encodeCL encodes a compressed load (CL-type) instruction.
+func encodeCL(ins *instruction) uint32 {
+       imm := uint32(ins.imm)
+       if ins.as == ACLW {
+               // Bit order [5:2|6]
+               imm = encodeBitPattern(imm, []int{5, 4, 3, 2, 6})
+       } else if ins.as == ACLD || ins.as == ACFLD {
+               // Bit order [5:3|7:6]
+               imm = encodeBitPattern(imm, []int{5, 4, 3, 7, 6})
+       }
+       rd := uint32(0)
+       if ins.as == ACFLD {
+               rd = regCF(ins.rd)
+       } else {
+               rd = regCI(ins.rd)
+       }
+       return compressedEncoding(ins.as) | (imm>>2)<<10 | regCI(ins.rs1)<<7 | (imm&0x3)<<5 | rd<<2
+}
+
+// encodeCR encodes a compressed register (CR-type) instruction.
+func encodeCR(ins *instruction) uint32 {
+       rs1, rs2 := uint32(0), uint32(0)
+       switch ins.as {
+       case ACJR, ACJALR:
+               rs1 = regI(ins.rs1)
+       case ACMV:
+               rs1, rs2 = regI(ins.rd), regI(ins.rs2)
+       case ACADD:
+               rs1, rs2 = regI(ins.rs1), regI(ins.rs2)
+       }
+       return compressedEncoding(ins.as) | rs1<<7 | rs2<<2
+}
+
+// encodeCS encodes a compressed store (CS-type) instruction.
+func encodeCS(ins *instruction) uint32 {
+       imm := uint32(ins.imm)
+       if ins.as == ACSW {
+               // Bit order [5:3|2|6]
+               imm = encodeBitPattern(imm, []int{5, 4, 3, 2, 6})
+       } else if ins.as == ACSD || ins.as == ACFSD {
+               // Bit order [5:3|7:6]
+               imm = encodeBitPattern(imm, []int{5, 4, 3, 7, 6})
+       }
+       rs2 := uint32(0)
+       if ins.as == ACFSD {
+               rs2 = regCF(ins.rs2)
+       } else {
+               rs2 = regCI(ins.rs2)
+       }
+       return compressedEncoding(ins.as) | ((imm>>2)&0x7)<<10 | regCI(ins.rs1)<<7 | (imm&3)<<5 | rs2<<2
+}
+
+// encodeCSS encodes a compressed stack-relative store (CSS-type) instruction.
+func encodeCSS(ins *instruction) uint32 {
+       imm := uint32(ins.imm)
+       if ins.as == ACSWSP {
+               // Bit order [5:2|7:6]
+               imm = encodeBitPattern(imm, []int{5, 4, 3, 2, 7, 6})
+       } else if ins.as == ACSDSP || ins.as == ACFSDSP {
+               // Bit order [5:3|8:6]
+               imm = encodeBitPattern(imm, []int{5, 4, 3, 8, 7, 6})
+       }
+       rs2 := uint32(0)
+       if ins.as == ACFSDSP {
+               rs2 = regF(ins.rs2)
+       } else {
+               rs2 = regI(ins.rs2)
+       }
+       return compressedEncoding(ins.as) | imm<<7 | rs2<<2
+}
+
 // encodeR encodes an R-type RISC-V instruction.
 func encodeR(as obj.As, rs1, rs2, rd, funct3, funct7 uint32) uint32 {
        enc := encode(as)
@@ -1653,20 +2129,6 @@ func encodeJ(ins *instruction) uint32 {
        return encodeJImmediate(imm) | rd<<7 | enc.opcode
 }
 
-// encodeCBImmediate encodes an immediate for a CB-type RISC-V instruction.
-func encodeCBImmediate(imm uint32) uint32 {
-       // Bit order - [8|4:3|7:6|2:1|5]
-       bits := encodeBitPattern(imm, []int{8, 4, 3, 7, 6, 2, 1, 5})
-       return (bits>>5)<<10 | (bits&0x1f)<<2
-}
-
-// encodeCJImmediate encodes an immediate for a CJ-type RISC-V instruction.
-func encodeCJImmediate(imm uint32) uint32 {
-       // Bit order - [11|4|9:8|10|6|7|3:1|5]
-       bits := encodeBitPattern(imm, []int{11, 4, 9, 8, 10, 6, 7, 3, 2, 1, 5})
-       return bits << 2
-}
-
 func encodeVset(as obj.As, rs1, rs2, rd uint32) uint32 {
        enc := encode(as)
        if enc == nil {
@@ -1781,7 +2243,7 @@ func EncodeVectorType(vsew, vlmul, vtail, vmask int64) (int64, error) {
 type encoding struct {
        encode   func(*instruction) uint32     // encode returns the machine code for an instruction
        validate func(*obj.Link, *instruction) // validate validates an instruction
-       length   int                           // length of encoded instruction; 0 for pseudo-ops, 4 otherwise
+       length   int                           // length of encoded instruction; 0 for pseudo-ops, 2 for compressed instructions, 4 otherwise
 }
 
 var (
@@ -1831,6 +2293,17 @@ var (
        uEncoding = encoding{encode: encodeU, validate: validateU, length: 4}
        jEncoding = encoding{encode: encodeJ, validate: validateJ, length: 4}
 
+       // Compressed encodings.
+       caEncoding  = encoding{encode: encodeCA, validate: validateCA, length: 2}
+       cbEncoding  = encoding{encode: encodeCB, validate: validateCB, length: 2}
+       ciEncoding  = encoding{encode: encodeCI, validate: validateCI, length: 2}
+       ciwEncoding = encoding{encode: encodeCIW, validate: validateCIW, length: 2}
+       cjEncoding  = encoding{encode: encodeCJ, validate: validateCJ, length: 2}
+       clEncoding  = encoding{encode: encodeCL, validate: validateCL, length: 2}
+       crEncoding  = encoding{encode: encodeCR, validate: validateCR, length: 2}
+       csEncoding  = encoding{encode: encodeCS, validate: validateCS, length: 2}
+       cssEncoding = encoding{encode: encodeCSS, validate: validateCSS, length: 2}
+
        // Encodings for vector configuration setting instruction.
        vsetvliEncoding  = encoding{encode: encodeVsetvli, validate: validateVsetvli, length: 4}
        vsetivliEncoding = encoding{encode: encodeVsetivli, validate: validateVsetivli, length: 4}
@@ -2059,6 +2532,63 @@ var instructions = [ALAST & obj.AMask]instructionData{
        // 21.7: Double-Precision Floating-Point Classify Instruction
        AFCLASSD & obj.AMask: {enc: rFIEncoding},
 
+       //
+       // "C" Extension for Compressed Instructions, Version 2.0
+       //
+
+       // 26.3.1: Compressed Stack-Pointer-Based Loads and Stores
+       ACLWSP & obj.AMask:  {enc: ciEncoding},
+       ACLDSP & obj.AMask:  {enc: ciEncoding},
+       ACFLDSP & obj.AMask: {enc: ciEncoding},
+       ACSWSP & obj.AMask:  {enc: cssEncoding},
+       ACSDSP & obj.AMask:  {enc: cssEncoding},
+       ACFSDSP & obj.AMask: {enc: cssEncoding},
+
+       // 26.3.2: Compressed Register-Based Loads and Stores
+       ACLW & obj.AMask:  {enc: clEncoding},
+       ACLD & obj.AMask:  {enc: clEncoding},
+       ACFLD & obj.AMask: {enc: clEncoding},
+       ACSW & obj.AMask:  {enc: csEncoding},
+       ACSD & obj.AMask:  {enc: csEncoding},
+       ACFSD & obj.AMask: {enc: csEncoding},
+
+       // 26.4: Compressed Control Transfer Instructions
+       ACJ & obj.AMask:    {enc: cjEncoding},
+       ACJR & obj.AMask:   {enc: crEncoding},
+       ACJALR & obj.AMask: {enc: crEncoding},
+       ACBEQZ & obj.AMask: {enc: cbEncoding},
+       ACBNEZ & obj.AMask: {enc: cbEncoding},
+
+       // 26.5.1: Compressed Integer Constant-Generation Instructions
+       ACLI & obj.AMask:  {enc: ciEncoding},
+       ACLUI & obj.AMask: {enc: ciEncoding},
+
+       // 26.5.2: Compressed Integer Register-Immediate Operations
+       ACADDI & obj.AMask:     {enc: ciEncoding, ternary: true},
+       ACADDIW & obj.AMask:    {enc: ciEncoding, ternary: true},
+       ACADDI16SP & obj.AMask: {enc: ciEncoding, ternary: true},
+       ACADDI4SPN & obj.AMask: {enc: ciwEncoding, ternary: true},
+       ACSLLI & obj.AMask:     {enc: ciEncoding, ternary: true},
+       ACSRLI & obj.AMask:     {enc: cbEncoding, ternary: true},
+       ACSRAI & obj.AMask:     {enc: cbEncoding, ternary: true},
+       ACANDI & obj.AMask:     {enc: cbEncoding, ternary: true},
+
+       // 26.5.3: Compressed Integer Register-Register Operations
+       ACMV & obj.AMask:   {enc: crEncoding},
+       ACADD & obj.AMask:  {enc: crEncoding, immForm: ACADDI, ternary: true},
+       ACAND & obj.AMask:  {enc: caEncoding, immForm: ACANDI, ternary: true},
+       ACOR & obj.AMask:   {enc: caEncoding, ternary: true},
+       ACXOR & obj.AMask:  {enc: caEncoding, ternary: true},
+       ACSUB & obj.AMask:  {enc: caEncoding, ternary: true},
+       ACADDW & obj.AMask: {enc: caEncoding, immForm: ACADDIW, ternary: true},
+       ACSUBW & obj.AMask: {enc: caEncoding, ternary: true},
+
+       // 26.5.5: Compressed NOP Instruction
+       ACNOP & obj.AMask: {enc: ciEncoding},
+
+       // 26.5.6: Compressed Breakpoint Instruction
+       ACEBREAK & obj.AMask: {enc: crEncoding},
+
        //
        // "B" Extension for Bit Manipulation, Version 1.0.0
        //
@@ -3542,7 +4072,7 @@ func instructionsForProg(p *obj.Prog) []*instruction {
        }
 
        switch ins.as {
-       case AJAL, AJALR:
+       case ACJALR, AJAL, AJALR:
                ins.rd, ins.rs1, ins.rs2 = uint32(p.From.Reg), uint32(p.To.Reg), obj.REG_NONE
                ins.imm = p.To.Offset
 
@@ -3753,6 +4283,32 @@ func instructionsForProg(p *obj.Prog) []*instruction {
                ins.as = AFSGNJND
                ins.rs1 = uint32(p.From.Reg)
 
+       case ACLW, ACLD, ACFLD:
+               ins.rs1, ins.rs2 = ins.rs2, obj.REG_NONE
+
+       case ACSW, ACSD, ACFSD:
+               ins.rs1, ins.rd = ins.rd, obj.REG_NONE
+               ins.imm = p.To.Offset
+
+       case ACSWSP, ACSDSP, ACFSDSP:
+               ins.imm = p.To.Offset
+
+       case ACANDI, ACSRLI, ACSRAI:
+               ins.rs1, ins.rd = ins.rd, ins.rs1
+
+       case ACBEQZ, ACBNEZ:
+               ins.rd, ins.rs1, ins.rs2 = obj.REG_NONE, uint32(p.From.Reg), obj.REG_NONE
+               ins.imm = p.To.Offset
+
+       case ACJR:
+               ins.rd, ins.rs1 = obj.REG_NONE, uint32(p.To.Reg)
+
+       case ACJ:
+               ins.imm = p.To.Offset
+
+       case ACNOP:
+               ins.rd, ins.rs1 = REG_ZERO, REG_ZERO
+
        case AROL, AROLW, AROR, ARORW:
                inss = instructionsForRotate(p, ins)
 
@@ -4187,7 +4743,8 @@ func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                                        Add:  p.To.Offset,
                                })
                        }
-               case AJALR:
+
+               case ACJALR, AJALR:
                        if p.To.Sym != nil {
                                ctxt.Diag("%v: unexpected AJALR with to symbol", p)
                        }