riscv64: Implement "C" extension in assembler am: fddd6a4c78 am: 71321b13bb

Original change: https://android-review.googlesource.com/c/platform/art/+/2939939

Change-Id: Ib126c1ee780e5d71a899e031e71673cc1d03e92a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/compiler/utils/riscv64/assembler_riscv64.cc b/compiler/utils/riscv64/assembler_riscv64.cc
index 7011e9e..eeb4537 100644
--- a/compiler/utils/riscv64/assembler_riscv64.cc
+++ b/compiler/utils/riscv64/assembler_riscv64.cc
@@ -61,18 +61,6 @@
   finalized_ = true;
 }
 
-void Riscv64Assembler::Emit(uint32_t value) {
-  if (overwriting_) {
-    // Branches to labels are emitted into their placeholders here.
-    buffer_.Store<uint32_t>(overwrite_location_, value);
-    overwrite_location_ += sizeof(uint32_t);
-  } else {
-    // Other instructions are simply appended at the end here.
-    AssemblerBuffer::EnsureCapacity ensured(&buffer_);
-    buffer_.Emit<uint32_t>(value);
-  }
-}
-
 /////////////////////////////// RV64 VARIANTS extension ///////////////////////////////
 
 //////////////////////////////// RV64 "I" Instructions ////////////////////////////////
@@ -792,6 +780,270 @@
 
 /////////////////////////////// RV64 "FD" Instructions  END ///////////////////////////////
 
+/////////////////////////////// RV64 "C" Instructions  START /////////////////////////////
+
+void Riscv64Assembler::CLwsp(XRegister rd, int32_t offset) {
+  DCHECK_NE(rd, Zero);
+
+  EmitCI(0b010u, rd, ExtractOffset52_76(offset), 0b10u);
+}
+
+void Riscv64Assembler::CLdsp(XRegister rd, int32_t offset) {
+  DCHECK_NE(rd, Zero);
+
+  EmitCI(0b011u, rd, ExtractOffset53_86(offset), 0b10u);
+}
+
+void Riscv64Assembler::CFLdsp(FRegister rd, int32_t offset) {
+  EmitCI(0b001u, rd, ExtractOffset53_86(offset), 0b10u);
+}
+
+void Riscv64Assembler::CSwsp(XRegister rs2, int32_t offset) {
+  EmitCSS(0b110u, ExtractOffset52_76(offset), rs2, 0b10u);
+}
+
+void Riscv64Assembler::CSdsp(XRegister rs2, int32_t offset) {
+  EmitCSS(0b111u, ExtractOffset53_86(offset), rs2, 0b10u);
+}
+
+void Riscv64Assembler::CFSdsp(FRegister rs2, int32_t offset) {
+  EmitCSS(0b101u, ExtractOffset53_86(offset), rs2, 0b10u);
+}
+
+void Riscv64Assembler::CLw(XRegister rd_s, XRegister rs1_s, int32_t offset) {
+  EmitCM(0b010u, ExtractOffset52_6(offset), rs1_s, rd_s, 0b00u);
+}
+
+void Riscv64Assembler::CLd(XRegister rd_s, XRegister rs1_s, int32_t offset) {
+  EmitCM(0b011u, ExtractOffset53_76(offset), rs1_s, rd_s, 0b00u);
+}
+
+void Riscv64Assembler::CFLd(FRegister rd_s, XRegister rs1_s, int32_t offset) {
+  EmitCM(0b001u, ExtractOffset53_76(offset), rs1_s, rd_s, 0b00u);
+}
+
+void Riscv64Assembler::CSw(XRegister rs2_s, XRegister rs1_s, int32_t offset) {
+  EmitCM(0b110u, ExtractOffset52_6(offset), rs1_s, rs2_s, 0b00u);
+}
+
+void Riscv64Assembler::CSd(XRegister rs2_s, XRegister rs1_s, int32_t offset) {
+  EmitCM(0b111u, ExtractOffset53_76(offset), rs1_s, rs2_s, 0b00u);
+}
+
+void Riscv64Assembler::CFSd(FRegister rs2_s, XRegister rs1_s, int32_t offset) {
+  EmitCM(0b101u, ExtractOffset53_76(offset), rs1_s, rs2_s, 0b00u);
+}
+
+void Riscv64Assembler::CLi(XRegister rd, int32_t imm) {
+  DCHECK_NE(rd, Zero);
+  DCHECK(IsInt<6>(imm));
+
+  EmitCI(0b010u, rd, EncodeInt6(imm), 0b01u);
+}
+
+void Riscv64Assembler::CLui(XRegister rd, uint32_t nzimm6) {
+  DCHECK_NE(rd, Zero);
+  DCHECK_NE(rd, SP);
+  DCHECK(IsImmCLuiEncodable(nzimm6));
+
+  EmitCI(0b011u, rd, nzimm6 & MaskLeastSignificant<uint32_t>(6), 0b01u);
+}
+
+void Riscv64Assembler::CAddi(XRegister rd, int32_t nzimm) {
+  DCHECK_NE(rd, Zero);
+  DCHECK_NE(nzimm, 0);
+
+  EmitCI(0b000u, rd, EncodeInt6(nzimm), 0b01u);
+}
+
+void Riscv64Assembler::CAddiw(XRegister rd, int32_t imm) {
+  DCHECK_NE(rd, Zero);
+
+  EmitCI(0b001u, rd, EncodeInt6(imm), 0b01u);
+}
+
+void Riscv64Assembler::CAddi16Sp(int32_t nzimm) {
+  DCHECK_NE(nzimm, 0);
+  DCHECK(IsAligned<16>(nzimm));
+
+  uint32_t unzimm = static_cast<uint32_t>(nzimm);
+
+  // nzimm[9]
+  uint32_t imms1 =  BitFieldExtract(unzimm, 9, 1);
+  // nzimm[4|6|8:7|5]
+  uint32_t imms0 = (BitFieldExtract(unzimm, 4, 1) << 4) |
+                   (BitFieldExtract(unzimm, 6, 1) << 3) |
+                   (BitFieldExtract(unzimm, 7, 2) << 1) |
+                    BitFieldExtract(unzimm, 5, 1);
+
+  EmitCI(0b011u, SP, BitFieldInsert(imms0, imms1, 5, 1), 0b01u);
+}
+
+void Riscv64Assembler::CAddi4Spn(XRegister rd_s, uint32_t nzuimm) {
+  DCHECK_NE(nzuimm, 0u);
+  DCHECK(IsAligned<4>(nzuimm));
+  DCHECK(IsUint<10>(nzuimm));
+
+  // nzuimm[5:4|9:6|2|3]
+  uint32_t uimm = (BitFieldExtract(nzuimm, 4, 2) << 6) |
+                  (BitFieldExtract(nzuimm, 6, 4) << 2) |
+                  (BitFieldExtract(nzuimm, 2, 1) << 1) |
+                   BitFieldExtract(nzuimm, 3, 1);
+
+  EmitCIW(0b000u, uimm, rd_s, 0b00u);
+}
+
+void Riscv64Assembler::CSlli(XRegister rd, int32_t shamt) {
+  DCHECK_NE(shamt, 0);
+  DCHECK_NE(rd, Zero);
+
+  EmitCI(0b000u, rd, shamt, 0b10u);
+}
+
+void Riscv64Assembler::CSrli(XRegister rd_s, int32_t shamt) {
+  DCHECK_NE(shamt, 0);
+  DCHECK(IsUint<6>(shamt));
+
+  EmitCBArithmetic(0b100u, 0b00u, shamt, rd_s, 0b01u);
+}
+
+void Riscv64Assembler::CSrai(XRegister rd_s, int32_t shamt) {
+  DCHECK_NE(shamt, 0);
+  DCHECK(IsUint<6>(shamt));
+
+  EmitCBArithmetic(0b100u, 0b01u, shamt, rd_s, 0b01u);
+}
+
+void Riscv64Assembler::CAndi(XRegister rd_s, int32_t imm) {
+  DCHECK(IsInt<6>(imm));
+
+  EmitCBArithmetic(0b100u, 0b10u, imm, rd_s, 0b01u);
+}
+
+void Riscv64Assembler::CMv(XRegister rd, XRegister rs2) {
+  DCHECK_NE(rd, Zero);
+  DCHECK_NE(rs2, Zero);
+
+  EmitCR(0b1000u, rd, rs2, 0b10u);
+}
+
+void Riscv64Assembler::CAdd(XRegister rd, XRegister rs2) {
+  DCHECK_NE(rd, Zero);
+  DCHECK_NE(rs2, Zero);
+
+  EmitCR(0b1001u, rd, rs2, 0b10u);
+}
+
+void Riscv64Assembler::CAnd(XRegister rd_s, XRegister rs2_s) {
+  EmitCAReg(0b100011u, rd_s, 0b11u, rs2_s, 0b01u);
+}
+
+void Riscv64Assembler::COr(XRegister rd_s, XRegister rs2_s) {
+  EmitCAReg(0b100011u, rd_s, 0b10u, rs2_s, 0b01u);
+}
+
+void Riscv64Assembler::CXor(XRegister rd_s, XRegister rs2_s) {
+  EmitCAReg(0b100011u, rd_s, 0b01u, rs2_s, 0b01u);
+}
+
+void Riscv64Assembler::CSub(XRegister rd_s, XRegister rs2_s) {
+  EmitCAReg(0b100011u, rd_s, 0b00u, rs2_s, 0b01u);
+}
+
+void Riscv64Assembler::CAddw(XRegister rd_s, XRegister rs2_s) {
+  EmitCAReg(0b100111u, rd_s, 0b01u, rs2_s, 0b01u);
+}
+
+void Riscv64Assembler::CSubw(XRegister rd_s, XRegister rs2_s) {
+  EmitCAReg(0b100111u, rd_s, 0b00u, rs2_s, 0b01u);
+}
+
+// "Zcb" Standard Extension, part of "C", opcode = 0b00, 0b01, funct3 = 0b100.
+
+void Riscv64Assembler::CLbu(XRegister rd_s, XRegister rs1_s, int32_t offset) {
+  EmitCAReg(0b100000u, rs1_s, EncodeOffset0_1(offset), rd_s, 0b00u);
+}
+
+void Riscv64Assembler::CLhu(XRegister rd_s, XRegister rs1_s, int32_t offset) {
+  DCHECK(IsUint<2>(offset));
+  DCHECK_ALIGNED(offset, 2);
+  EmitCAReg(0b100001u, rs1_s, BitFieldExtract<uint32_t>(offset, 1, 1), rd_s, 0b00u);
+}
+
+void Riscv64Assembler::CLh(XRegister rd_s, XRegister rs1_s, int32_t offset) {
+  DCHECK(IsUint<2>(offset));
+  DCHECK_ALIGNED(offset, 2);
+  EmitCAReg(0b100001u, rs1_s, 0b10 | BitFieldExtract<uint32_t>(offset, 1, 1), rd_s, 0b00u);
+}
+
+void Riscv64Assembler::CSb(XRegister rs2_s, XRegister rs1_s, int32_t offset) {
+  EmitCAReg(0b100010u, rs1_s, EncodeOffset0_1(offset), rs2_s, 0b00u);
+}
+
+void Riscv64Assembler::CSh(XRegister rs2_s, XRegister rs1_s, int32_t offset) {
+  DCHECK(IsUint<2>(offset));
+  DCHECK_ALIGNED(offset, 2);
+  EmitCAReg(0b100011u, rs1_s, BitFieldExtract<uint32_t>(offset, 1, 1), rs2_s, 0b00u);
+}
+
+void Riscv64Assembler::CZext_b(XRegister rd_rs1_s) {
+  EmitCAImm(0b100111u, rd_rs1_s, 0b11u, 0b000u, 0b01u);
+}
+
+void Riscv64Assembler::CSext_b(XRegister rd_rs1_s) {
+  EmitCAImm(0b100111u, rd_rs1_s, 0b11u, 0b001u, 0b01u);
+}
+
+void Riscv64Assembler::CZext_h(XRegister rd_rs1_s) {
+  EmitCAImm(0b100111u, rd_rs1_s, 0b11u, 0b010u, 0b01u);
+}
+
+void Riscv64Assembler::CSext_h(XRegister rd_rs1_s) {
+  EmitCAImm(0b100111u, rd_rs1_s, 0b11u, 0b011u, 0b01u);
+}
+
+void Riscv64Assembler::CZext_w(XRegister rd_rs1_s) {
+  EmitCAImm(0b100111u, rd_rs1_s, 0b11u, 0b100u, 0b01u);
+}
+
+void Riscv64Assembler::CNot(XRegister rd_rs1_s) {
+  EmitCAImm(0b100111u, rd_rs1_s, 0b11u, 0b101u, 0b01u);
+}
+
+void Riscv64Assembler::CMul(XRegister rd_s, XRegister rs2_s) {
+  EmitCAReg(0b100111u, rd_s, 0b10u, rs2_s, 0b01u);
+}
+
+void Riscv64Assembler::CJ(int32_t offset) { EmitCJ(0b101u, offset, 0b01u); }
+
+void Riscv64Assembler::CJr(XRegister rs1) {
+  DCHECK_NE(rs1, Zero);
+
+  EmitCR(0b1000u, rs1, Zero, 0b10u);
+}
+
+void Riscv64Assembler::CJalr(XRegister rs1) {
+  DCHECK_NE(rs1, Zero);
+
+  EmitCR(0b1001u, rs1, Zero, 0b10u);
+}
+
+void Riscv64Assembler::CBeqz(XRegister rs1_s, int32_t offset) {
+  EmitCBBranch(0b110u, offset, rs1_s, 0b01u);
+}
+
+void Riscv64Assembler::CBnez(XRegister rs1_s, int32_t offset) {
+  EmitCBBranch(0b111u, offset, rs1_s, 0b01u);
+}
+
+void Riscv64Assembler::CEbreak() { EmitCR(0b1001u, Zero, Zero, 0b10u); }
+
+void Riscv64Assembler::CNop() { EmitCI(0b000u, Zero, 0u, 0b01u); }
+
+void Riscv64Assembler::CUnimp() { Emit16(0x0u); }
+
+/////////////////////////////// RV64 "C" Instructions  END ///////////////////////////////
+
 ////////////////////////////// RV64 "Zba" Instructions  START /////////////////////////////
 
 void Riscv64Assembler::AddUw(XRegister rd, XRegister rs1, XRegister rs2) {
@@ -5072,7 +5324,7 @@
 
 void Riscv64Assembler::Unimp() {
   // TODO(riscv64): use 16-bit zero C.UNIMP once we support compression
-  Emit(0xC0001073);
+  Emit32(0xC0001073);
 }
 
 /////////////////////////////// RV64 MACRO Instructions END ///////////////////////////////
@@ -5554,7 +5806,7 @@
     // Branch forward (to a following label), distance is unknown.
     // The first branch forward will contain 0, serving as the terminator of
     // the list of forward-reaching branches.
-    Emit(label->position_);
+    Emit32(label->position_);
     length--;
     // Now make the label object point to this branch
     // (this forms a linked list of branches preceding this label).
@@ -5787,7 +6039,7 @@
       DCHECK(!overwriting_);
       overwriting_ = true;
       overwrite_location_ = first_literal_location;
-      Emit(0);  // Illegal instruction.
+      Emit32(0);  // Illegal instruction.
       overwriting_ = false;
       // Increase target addresses in literal and address loads by 4 bytes in order for correct
       // offsets from PC to be generated.
@@ -5850,7 +6102,7 @@
         CHECK_EQ(buffer_.Load<uint32_t>(overwrite_location_), 0x1abe1234u);
         // The table will contain target addresses relative to the table start.
         uint32_t offset = GetLabelLocation(target) - start;
-        Emit(offset);
+        Emit32(offset);
       }
     }
 
diff --git a/compiler/utils/riscv64/assembler_riscv64.h b/compiler/utils/riscv64/assembler_riscv64.h
index 193585b..1696251 100644
--- a/compiler/utils/riscv64/assembler_riscv64.h
+++ b/compiler/utils/riscv64/assembler_riscv64.h
@@ -490,6 +490,64 @@
   void FClassS(XRegister rd, FRegister rs1);
   void FClassD(XRegister rd, FRegister rs1);
 
+  // "C" Standard Extension, Compresseed Instructions
+  void CLwsp(XRegister rd, int32_t offset);
+  void CLdsp(XRegister rd, int32_t offset);
+  void CFLdsp(FRegister rd, int32_t offset);
+  void CSwsp(XRegister rs2, int32_t offset);
+  void CSdsp(XRegister rs2, int32_t offset);
+  void CFSdsp(FRegister rs2, int32_t offset);
+
+  void CLw(XRegister rd_s, XRegister rs1_s, int32_t offset);
+  void CLd(XRegister rd_s, XRegister rs1_s, int32_t offset);
+  void CFLd(FRegister rd_s, XRegister rs1_s, int32_t offset);
+  void CSw(XRegister rs2_s, XRegister rs1_s, int32_t offset);
+  void CSd(XRegister rs2_s, XRegister rs1_s, int32_t offset);
+  void CFSd(FRegister rs2_s, XRegister rs1_s, int32_t offset);
+
+  void CLi(XRegister rd, int32_t imm);
+  void CLui(XRegister rd, uint32_t nzimm6);
+  void CAddi(XRegister rd, int32_t nzimm);
+  void CAddiw(XRegister rd, int32_t imm);
+  void CAddi16Sp(int32_t nzimm);
+  void CAddi4Spn(XRegister rd_s, uint32_t nzuimm);
+  void CSlli(XRegister rd, int32_t shamt);
+  void CSrli(XRegister rd_s, int32_t shamt);
+  void CSrai(XRegister rd_s, int32_t shamt);
+  void CAndi(XRegister rd_s, int32_t imm);
+  void CMv(XRegister rd, XRegister rs2);
+  void CAdd(XRegister rd, XRegister rs2);
+  void CAnd(XRegister rd_s, XRegister rs2_s);
+  void COr(XRegister rd_s, XRegister rs2_s);
+  void CXor(XRegister rd_s, XRegister rs2_s);
+  void CSub(XRegister rd_s, XRegister rs2_s);
+  void CAddw(XRegister rd_s, XRegister rs2_s);
+  void CSubw(XRegister rd_s, XRegister rs2_s);
+
+  // "Zcb" Standard Extension, part of "C", opcode = 0b00, 0b01, funct3 = 0b100.
+  void CLbu(XRegister rd_s, XRegister rs1_s, int32_t offset);
+  void CLhu(XRegister rd_s, XRegister rs1_s, int32_t offset);
+  void CLh(XRegister rd_s, XRegister rs1_s, int32_t offset);
+  void CSb(XRegister rd_s, XRegister rs1_s, int32_t offset);
+  void CSh(XRegister rd_s, XRegister rs1_s, int32_t offset);
+  void CZext_b(XRegister rd_rs1_s);
+  void CSext_b(XRegister rd_rs1_s);
+  void CZext_h(XRegister rd_rs1_s);
+  void CSext_h(XRegister rd_rs1_s);
+  void CZext_w(XRegister rd_rs1_s);
+  void CNot(XRegister rd_rs1_s);
+  void CMul(XRegister rd_s, XRegister rs2_s);
+
+  void CJ(int32_t offset);
+  void CJr(XRegister rs1);
+  void CJalr(XRegister rs1);
+  void CBeqz(XRegister rs1_s, int32_t offset);
+  void CBnez(XRegister rs1_s, int32_t offset);
+
+  void CEbreak();
+  void CNop();
+  void CUnimp();
+
   // "Zba" Standard Extension, opcode = 0x1b, 0x33 or 0x3b, funct3 and funct7 varies.
   void AddUw(XRegister rd, XRegister rs1, XRegister rs2);
   void Sh1Add(XRegister rd, XRegister rs1, XRegister rs2);
@@ -1761,6 +1819,13 @@
   // and emit branches.
   void FinalizeCode() override;
 
+  template <typename Reg>
+  static inline bool IsShortReg(Reg reg) {
+    static_assert(std::is_same_v<Reg, XRegister> || std::is_same_v<Reg, FRegister>);
+    uint32_t uv = enum_cast<uint32_t>(reg) - 8u;
+    return IsUint<3>(uv);
+  }
+
   // Returns the current location of a label.
   //
   // This function must be used instead of `Riscv64Label::GetPosition()`
@@ -1955,7 +2020,23 @@
   void PatchCFI();
 
   // Emit data (e.g. encoded instruction or immediate) to the instruction stream.
-  void Emit(uint32_t value);
+  template <typename T>
+  void Emit(T value) {
+    static_assert(std::is_same_v<T, uint32_t> || std::is_same_v<T, uint16_t>,
+                  "Only Integer types are allowed");
+    if (overwriting_) {
+      // Branches to labels are emitted into their placeholders here.
+      buffer_.Store<T>(overwrite_location_, value);
+      overwrite_location_ += sizeof(T);
+    } else {
+      // Other instructions are simply appended at the end here.
+      AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+      buffer_.Emit<T>(value);
+    }
+  }
+
+  void Emit16(uint32_t value) { Emit(dchecked_integral_cast<uint16_t>(value)); }
+  void Emit32(uint32_t value) { Emit(value); }
 
   // Adjust base register and offset if needed for load/store with a large offset.
   void AdjustBaseAndOffset(XRegister& base, int32_t& offset, ScratchRegisterScope& srs);
@@ -2033,9 +2114,92 @@
     return funct6 << 1 | enum_cast<uint32_t>(vm);
   }
 
-  static constexpr uint32_t EncodeInt5(const int32_t imm) {
-    DCHECK(IsInt<5>(imm));
-    return static_cast<uint32_t>(imm) & 0b11111u;
+  template <unsigned kWidth>
+  static constexpr uint32_t EncodeIntWidth(const int32_t imm) {
+    DCHECK(IsInt<kWidth>(imm));
+    return static_cast<uint32_t>(imm) & MaskLeastSignificant<uint32_t>(kWidth);
+  }
+
+  static constexpr uint32_t EncodeInt5(const int32_t imm) { return EncodeIntWidth<5>(imm); }
+  static constexpr uint32_t EncodeInt6(const int32_t imm) { return EncodeIntWidth<6>(imm); }
+
+  template <typename Reg>
+  static constexpr uint32_t EncodeShortReg(const Reg reg) {
+    DCHECK(IsShortReg(reg));
+    return enum_cast<uint32_t>(reg) - 8u;
+  }
+
+  // Rearrange given offset in the way {offset[0] | offset[1]}
+  static constexpr uint32_t EncodeOffset0_1(int32_t offset) {
+    uint32_t u_offset = static_cast<uint32_t>(offset);
+    DCHECK(IsUint<2>(u_offset));
+
+    return u_offset >> 1 | (u_offset & 1u) << 1;
+  }
+
+  // Rearrange given offset, scaled by 4, in the way {offset[5:2] | offset[7:6]}
+  static constexpr uint32_t ExtractOffset52_76(int32_t offset) {
+    DCHECK(IsAligned<4>(offset)) << "Offset should be scalable by 4";
+
+    uint32_t u_offset = static_cast<uint32_t>(offset);
+    DCHECK(IsUint<6 + 2>(u_offset));
+
+    uint32_t imm_52 = BitFieldExtract(u_offset, 2, 4);
+    uint32_t imm_76 = BitFieldExtract(u_offset, 6, 2);
+
+    return BitFieldInsert(imm_76, imm_52, 2, 4);
+  }
+
+  // Rearrange given offset, scaled by 8, in the way {offset[5:3] | offset[8:6]}
+  static constexpr uint32_t ExtractOffset53_86(int32_t offset) {
+    DCHECK(IsAligned<8>(offset)) << "Offset should be scalable by 8";
+
+    uint32_t u_offset = static_cast<uint32_t>(offset);
+    DCHECK(IsUint<6 + 3>(u_offset));
+
+    uint32_t imm_53 = BitFieldExtract(u_offset, 3, 3);
+    uint32_t imm_86 = BitFieldExtract(u_offset, 6, 3);
+
+    return BitFieldInsert(imm_86, imm_53, 3, 3);
+  }
+
+  // Rearrange given offset, scaled by 4, in the way {offset[5:2] | offset[6]}
+  static constexpr uint32_t ExtractOffset52_6(int32_t offset) {
+    DCHECK(IsAligned<4>(offset)) << "Offset should be scalable by 4";
+
+    uint32_t u_offset = static_cast<uint32_t>(offset);
+    DCHECK(IsUint<5 + 2>(u_offset));
+
+    uint32_t imm_52 = BitFieldExtract(u_offset, 2, 4);
+    uint32_t imm_6  = BitFieldExtract(u_offset, 6, 1);
+
+    return BitFieldInsert(imm_6, imm_52, 1, 4);
+  }
+
+  // Rearrange given offset, scaled by 8, in the way {offset[5:3], offset[7:6]}
+  static constexpr uint32_t ExtractOffset53_76(int32_t offset) {
+    DCHECK(IsAligned<8>(offset)) << "Offset should be scalable by 4";
+
+    uint32_t u_offset = static_cast<uint32_t>(offset);
+    DCHECK(IsUint<5 + 3>(u_offset));
+
+    uint32_t imm_53 = BitFieldExtract(u_offset, 3, 3);
+    uint32_t imm_76 = BitFieldExtract(u_offset, 6, 2);
+
+    return BitFieldInsert(imm_76, imm_53, 2, 3);
+  }
+
+  static constexpr bool IsImmCLuiEncodable(uint32_t uimm) {
+    // Instruction c.lui is odd and its immediate value is a bit tricky
+    // Its value is not a full 32 bits value, but its bits [31:12]
+    // (where the bit 17 marks the sign bit) shifted towards the bottom i.e. bits [19:0]
+    // are the meaningful ones. Since that we want a signed non-zero 6-bit immediate to
+    // keep values in the range [0, 0x1f], and the range [0xfffe0, 0xfffff] for negative values
+    // since the sign bit was bit 17 (which is now bit 5 and replicated in the higher bits too)
+    // Also encoding with immediate = 0 is reserved
+    // For more details please see 16.5 chapter is the specification
+
+    return uimm != 0u && (IsUint<5>(uimm) || IsUint<5>(uimm - 0xfffe0u));
   }
 
   // Emit helpers.
@@ -2056,7 +2220,7 @@
     DCHECK(IsUint<7>(opcode));
     uint32_t encoding = static_cast<uint32_t>(imm12) << 20 | static_cast<uint32_t>(rs1) << 15 |
                         funct3 << 12 | static_cast<uint32_t>(rd) << 7 | opcode;
-    Emit(encoding);
+    Emit32(encoding);
   }
 
   // R-type instruction:
@@ -2077,7 +2241,7 @@
     uint32_t encoding = funct7 << 25 | static_cast<uint32_t>(rs2) << 20 |
                         static_cast<uint32_t>(rs1) << 15 | funct3 << 12 |
                         static_cast<uint32_t>(rd) << 7 | opcode;
-    Emit(encoding);
+    Emit32(encoding);
   }
 
   // R-type instruction variant for floating-point fused multiply-add/sub (F[N]MADD/ F[N]MSUB):
@@ -2101,7 +2265,7 @@
                         static_cast<uint32_t>(rs2) << 20 | static_cast<uint32_t>(rs1) << 15 |
                         static_cast<uint32_t>(funct3) << 12 | static_cast<uint32_t>(rd) << 7 |
                         opcode;
-    Emit(encoding);
+    Emit32(encoding);
   }
 
   // S-type instruction:
@@ -2122,7 +2286,7 @@
                         static_cast<uint32_t>(rs2) << 20 | static_cast<uint32_t>(rs1) << 15 |
                         static_cast<uint32_t>(funct3) << 12 |
                         (static_cast<uint32_t>(imm12) & 0x1F) << 7 | opcode;
-    Emit(encoding);
+    Emit32(encoding);
   }
 
   // I-type instruction variant for shifts (SLLI / SRLI / SRAI):
@@ -2147,7 +2311,7 @@
     uint32_t encoding = funct6 << 26 | static_cast<uint32_t>(imm6) << 20 |
                         static_cast<uint32_t>(rs1) << 15 | funct3 << 12 |
                         static_cast<uint32_t>(rd) << 7 | opcode;
-    Emit(encoding);
+    Emit32(encoding);
   }
 
   // B-type instruction:
@@ -2169,7 +2333,7 @@
                         static_cast<uint32_t>(rs2) << 20 | static_cast<uint32_t>(rs1) << 15 |
                         static_cast<uint32_t>(funct3) << 12 |
                         (imm12 & 0xfu) << 8 | (imm12 & 0x400u) >> (10 - 7) | opcode;
-    Emit(encoding);
+    Emit32(encoding);
   }
 
   // U-type instruction:
@@ -2184,7 +2348,7 @@
     DCHECK(IsUint<5>(static_cast<uint32_t>(rd)));
     DCHECK(IsUint<7>(opcode));
     uint32_t encoding = imm20 << 12 | static_cast<uint32_t>(rd) << 7 | opcode;
-    Emit(encoding);
+    Emit32(encoding);
   }
 
   // J-type instruction:
@@ -2203,7 +2367,224 @@
     uint32_t encoding = (imm20 & 0x80000u) << (31 - 19) | (imm20 & 0x03ffu) << 21 |
                         (imm20 & 0x400u) << (20 - 10) | (imm20 & 0x7f800u) << (12 - 11) |
                         static_cast<uint32_t>(rd) << 7 | opcode;
-    Emit(encoding);
+    Emit32(encoding);
+  }
+
+  // Compressed Instruction Encodings
+
+  // CR-type instruction:
+  //
+  //   15    12 11      7 6       2 1 0
+  //   ---------------------------------
+  //   [ . . . | . . . . | . . . . | . ]
+  //   [ func4   rd/rs1      rs2    op ]
+  //   ---------------------------------
+  //
+  void EmitCR(uint32_t funct4, XRegister rd_rs1, XRegister rs2, uint32_t opcode) {
+    DCHECK(IsUint<4>(funct4));
+    DCHECK(IsUint<5>(static_cast<uint32_t>(rd_rs1)));
+    DCHECK(IsUint<5>(static_cast<uint32_t>(rs2)));
+    DCHECK(IsUint<2>(opcode));
+
+    uint32_t encoding = funct4 << 12 | static_cast<uint32_t>(rd_rs1) << 7 |
+                        static_cast<uint32_t>(rs2) << 2 | opcode;
+    Emit16(encoding);
+  }
+
+  // CI-type instruction:
+  //
+  //   15  13   11      7 6       2 1 0
+  //   ---------------------------------
+  //   [ . . | | . . . . | . . . . | . ]
+  //   [func3 imm rd/rs1     imm    op ]
+  //   ---------------------------------
+  //
+  template <typename Reg>
+  void EmitCI(uint32_t funct3, Reg rd_rs1, uint32_t imm6, uint32_t opcode) {
+    DCHECK(IsUint<3>(funct3));
+    DCHECK(IsUint<5>(static_cast<uint32_t>(rd_rs1)));
+    DCHECK(IsUint<6>(imm6));
+    DCHECK(IsUint<2>(opcode));
+
+    uint32_t immH1 = BitFieldExtract(imm6, 5, 1);
+    uint32_t immL5 = BitFieldExtract(imm6, 0, 5);
+
+    uint32_t encoding =
+        funct3 << 13 | immH1 << 12 | static_cast<uint32_t>(rd_rs1) << 7 | immL5 << 2 | opcode;
+    Emit16(encoding);
+  }
+
+  // CSS-type instruction:
+  //
+  //   15  13 12        7 6       2 1 0
+  //   ---------------------------------
+  //   [ . . | . . . . . | . . . . | . ]
+  //   [func3     imm6      rs2     op ]
+  //   ---------------------------------
+  //
+  template <typename Reg>
+  void EmitCSS(uint32_t funct3, uint32_t offset6, Reg rs2, uint32_t opcode) {
+    DCHECK(IsUint<3>(funct3));
+    DCHECK(IsUint<6>(offset6));
+    DCHECK(IsUint<5>(static_cast<uint32_t>(rs2)));
+    DCHECK(IsUint<2>(opcode));
+
+    uint32_t encoding = funct3 << 13 | offset6 << 7 | static_cast<uint32_t>(rs2) << 2 | opcode;
+    Emit16(encoding);
+  }
+
+  // CIW-type instruction:
+  //
+  //   15  13 12            5 4   2 1 0
+  //   ---------------------------------
+  //   [ . . | . . . . . . . | . . | . ]
+  //   [func3     imm8         rd'  op ]
+  //   ---------------------------------
+  //
+  void EmitCIW(uint32_t funct3, uint32_t imm8, XRegister rd_s, uint32_t opcode) {
+    DCHECK(IsUint<3>(funct3));
+    DCHECK(IsUint<8>(imm8));
+    DCHECK(IsShortReg(rd_s)) << rd_s;
+    DCHECK(IsUint<2>(opcode));
+
+    uint32_t encoding = funct3 << 13 | imm8 << 5 | EncodeShortReg(rd_s) << 2 | opcode;
+    Emit16(encoding);
+  }
+
+  // CL/S-type instruction:
+  //
+  //   15  13 12  10 9  7 6 5 4   2 1 0
+  //   ---------------------------------
+  //   [ . . | . . | . . | . | . . | . ]
+  //   [func3  imm   rs1' imm rds2' op ]
+  //   ---------------------------------
+  //
+  template <typename Reg>
+  void EmitCM(uint32_t funct3, uint32_t imm5, XRegister rs1_s, Reg rd_rs2_s, uint32_t opcode) {
+    DCHECK(IsUint<3>(funct3));
+    DCHECK(IsUint<5>(imm5));
+    DCHECK(IsShortReg(rs1_s)) << rs1_s;
+    DCHECK(IsShortReg(rd_rs2_s)) << rd_rs2_s;
+    DCHECK(IsUint<2>(opcode));
+
+    uint32_t immH3 = BitFieldExtract(imm5, 2, 3);
+    uint32_t immL2 = BitFieldExtract(imm5, 0, 2);
+
+    uint32_t encoding = funct3 << 13 | immH3 << 10 | EncodeShortReg(rs1_s) << 7 | immL2 << 5 |
+                        EncodeShortReg(rd_rs2_s) << 2 | opcode;
+    Emit16(encoding);
+  }
+
+  // CA-type instruction:
+  //
+  //   15         10 9  7 6 5 4   2 1 0
+  //   ---------------------------------
+  //   [ . . . . . | . . | . | . . | . ]
+  //   [    funct6 rds1' funct2 rs2' op]
+  //   ---------------------------------
+  //
+  void EmitCA(
+      uint32_t funct6, XRegister rd_rs1_s, uint32_t funct2, uint32_t rs2_v, uint32_t opcode) {
+    DCHECK(IsUint<6>(funct6));
+    DCHECK(IsShortReg(rd_rs1_s)) << rd_rs1_s;
+    DCHECK(IsUint<2>(funct2));
+    DCHECK(IsUint<3>(rs2_v));
+    DCHECK(IsUint<2>(opcode));
+
+    uint32_t encoding =
+        funct6 << 10 | EncodeShortReg(rd_rs1_s) << 7 | funct2 << 5  | rs2_v << 2 | opcode;
+    Emit16(encoding);
+  }
+
+  void EmitCAReg(
+      uint32_t funct6, XRegister rd_rs1_s, uint32_t funct2, XRegister rs2_s, uint32_t opcode) {
+    DCHECK(IsShortReg(rs2_s)) << rs2_s;
+    EmitCA(funct6, rd_rs1_s, funct2, EncodeShortReg(rs2_s), opcode);
+  }
+
+  void EmitCAImm(
+      uint32_t funct6, XRegister rd_rs1_s, uint32_t funct2, uint32_t funct3, uint32_t opcode) {
+    EmitCA(funct6, rd_rs1_s, funct2, funct3, opcode);
+  }
+
+  // CB-type instruction:
+  //
+  //   15  13 12  10 9  7 6       2 1 0
+  //   ---------------------------------
+  //   [ . . | . . | . . | . . . . | . ]
+  //   [func3 offset rs1'   offset  op ]
+  //   ---------------------------------
+  //
+  void EmitCB(uint32_t funct3, int32_t offset8, XRegister rd_rs1_s, uint32_t opcode) {
+    DCHECK(IsUint<3>(funct3));
+    DCHECK(IsUint<8>(offset8));
+    DCHECK(IsShortReg(rd_rs1_s)) << rd_rs1_s;
+    DCHECK(IsUint<2>(opcode));
+
+    uint32_t offsetH3 = BitFieldExtract<uint32_t>(offset8, 5, 3);
+    uint32_t offsetL5 = BitFieldExtract<uint32_t>(offset8, 0, 5);
+
+    uint32_t encoding =
+        funct3 << 13 | offsetH3 << 10 | EncodeShortReg(rd_rs1_s) << 7 | offsetL5 << 2 | opcode;
+    Emit16(encoding);
+  }
+
+  // Wrappers for EmitCB with different imm bit permutation
+  void EmitCBBranch(uint32_t funct3, int32_t offset, XRegister rs1_s, uint32_t opcode) {
+    DCHECK(IsInt<9>(offset));
+    DCHECK_ALIGNED(offset, 2);
+
+    uint32_t u_offset = static_cast<uint32_t>(offset);
+
+    // offset[8|4:3]
+    uint32_t offsetH3 = (BitFieldExtract(u_offset, 8, 1) << 2) |
+                         BitFieldExtract(u_offset, 3, 2);
+    // offset[7:6|2:1|5]
+    uint32_t offsetL5 = (BitFieldExtract(u_offset, 6, 2) << 3) |
+                        (BitFieldExtract(u_offset, 1, 2) << 1) |
+                         BitFieldExtract(u_offset, 5, 1);
+
+    EmitCB(funct3, BitFieldInsert(offsetL5, offsetH3, 5, 3), rs1_s, opcode);
+  }
+
+  void EmitCBArithmetic(
+      uint32_t funct3, uint32_t funct2, uint32_t imm, XRegister rd_s, uint32_t opcode) {
+    uint32_t imm_5 = BitFieldExtract(imm, 5, 1);
+    uint32_t immH3 = BitFieldInsert(funct2, imm_5, 2, 1);
+    uint32_t immL5 = BitFieldExtract(imm, 0, 5);
+
+    EmitCB(funct3, BitFieldInsert(immL5, immH3, 5, 3), rd_s, opcode);
+  }
+
+  // CJ-type instruction:
+  //
+  //   15  13 12                  2 1 0
+  //   ---------------------------------
+  //   [ . . | . . . . . . . . . . | . ]
+  //   [func3    jump target 11     op ]
+  //   ---------------------------------
+  //
+  void EmitCJ(uint32_t funct3, int32_t offset, uint32_t opcode) {
+    DCHECK_ALIGNED(offset, 2);
+    DCHECK(IsInt<12>(offset)) << offset;
+    DCHECK(IsUint<3>(funct3));
+    DCHECK(IsUint<2>(opcode));
+
+    uint32_t uoffset = static_cast<uint32_t>(offset);
+    // offset[11|4|9:8|10|6|7|3:1|5]
+    uint32_t jumpt = (BitFieldExtract(uoffset, 11, 1) << 10) |
+                     (BitFieldExtract(uoffset, 4, 1) << 9)   |
+                     (BitFieldExtract(uoffset, 8, 2) << 7)   |
+                     (BitFieldExtract(uoffset, 10, 1) << 6)  |
+                     (BitFieldExtract(uoffset, 6, 1) << 5)   |
+                     (BitFieldExtract(uoffset, 7, 1) << 4)   |
+                     (BitFieldExtract(uoffset, 1, 3) << 1)   |
+                      BitFieldExtract(uoffset, 5, 1);
+
+    DCHECK(IsUint<11>(jumpt));
+
+    uint32_t encoding = funct3 << 13 | jumpt << 2 | opcode;
+    Emit16(encoding);
   }
 
   ArenaVector<Branch> branches_;
diff --git a/compiler/utils/riscv64/assembler_riscv64_test.cc b/compiler/utils/riscv64/assembler_riscv64_test.cc
index 2229695..3f7d617 100644
--- a/compiler/utils/riscv64/assembler_riscv64_test.cc
+++ b/compiler/utils/riscv64/assembler_riscv64_test.cc
@@ -59,6 +59,24 @@
     use_simple_march_ = value;
   }
 
+  void SetCompressedMode(bool value) { compressed_mode_ = value; }
+
+  struct ScopedCompressedMode {
+    AssemblerRISCV64Test* test_;
+    bool old_cm_, old_sa_;
+
+    explicit ScopedCompressedMode(AssemblerRISCV64Test* test)
+        : test_(test), old_cm_(test->compressed_mode_), old_sa_(test->use_simple_march_) {
+      test->SetUseSimpleMarch(true);
+      test->SetCompressedMode(true);
+    }
+
+    ~ScopedCompressedMode() {
+      test_->SetCompressedMode(old_cm_);
+      test_->SetUseSimpleMarch(old_sa_);
+    }
+  };
+
   std::vector<std::string> GetAssemblerCommand() override {
     std::vector<std::string> result = Base::GetAssemblerCommand();
     if (use_simple_march_) {
@@ -66,7 +84,7 @@
                              result.end(),
                              [](const std::string& s) { return StartsWith(s, "-march="); });
       CHECK(it != result.end());
-      *it = "-march=rv64imafdv";
+      *it = compressed_mode_ ? "-march=rv64imafdcv_zca_zcb_zba_zbb" : "-march=rv64imafdv";
     }
     return result;
   }
@@ -78,7 +96,7 @@
                              result.end(),
                              [](const std::string& s) { return StartsWith(s, "--mattr="); });
       CHECK(it != result.end());
-      *it = "--mattr=+F,+D,+A,+V";
+      *it = compressed_mode_ ? "--mattr=+F,+D,+A,+V,+C" : "--mattr=+F,+D,+A,+V";
     }
     return result;
   }
@@ -167,6 +185,20 @@
     return ArrayRef<const XRegister>(kXRegisters);
   }
 
+  ArrayRef<const XRegister> GetRegistersShort() {
+    static constexpr XRegister kXRegistersShort[] = {
+        S0,
+        S1,
+        A0,
+        A1,
+        A2,
+        A3,
+        A4,
+        A5,
+    };
+    return ArrayRef<const XRegister>(kXRegistersShort);
+  }
+
   ArrayRef<const FRegister> GetFPRegisters() override {
     static constexpr FRegister kFRegisters[] = {
         FT0,
@@ -213,6 +245,20 @@
     return ArrayRef<const VRegister>(kVRegisters);
   }
 
+  ArrayRef<const FRegister> GetFPRegistersShort() {
+    static constexpr FRegister kFRegistersShort[] = {
+        FS0,
+        FS1,
+        FA0,
+        FA1,
+        FA2,
+        FA3,
+        FA4,
+        FA5,
+    };
+    return ArrayRef<const FRegister>(kFRegistersShort);
+  }
+
   std::string GetSecondaryRegisterName(const XRegister& reg) override {
     CHECK(secondary_register_names_.find(reg) != secondary_register_names_.end());
     return secondary_register_names_[reg];
@@ -954,6 +1000,186 @@
     return str;
   }
 
+  template <typename Reg, typename Imm>
+  std::string RepeatCTemplateRegImm(void (Riscv64Assembler::*f)(Reg, Imm),
+                                    ArrayRef<const Reg> registers,
+                                    std::string (Base::*GetName)(const Reg&),
+                                    int imm_bits,
+                                    int shift,
+                                    bool no_zero_imm,
+                                    const std::string& fmt) {
+    auto imms = CreateImmediateValuesBits(abs(imm_bits), /*as_uint=*/imm_bits > 0, shift);
+
+    CHECK(f != nullptr);
+    std::string str;
+    for (Reg reg : registers) {
+      for (int64_t imm_raw : imms) {
+        if (no_zero_imm && imm_raw == 0) {
+          continue;
+        }
+
+        Imm imm = CreateImmediate(imm_raw);
+        (GetAssembler()->*f)(reg, imm);
+
+        std::string base = fmt;
+        ReplaceReg(REG_TOKEN, (this->*GetName)(reg), &base);
+        ReplaceImm(imm, /*bias=*/0, /*multiplier=*/1, &base);
+        str += base;
+        str += "\n";
+      }
+    }
+    return str;
+  }
+
+  template <typename Imm>
+  std::string RepeatCRImm(void (Riscv64Assembler::*f)(XRegister, Imm),
+                          bool is_short,
+                          bool no_zero_reg,
+                          bool no_zero_imm,
+                          int imm_bits,
+                          int shift,
+                          const std::string& fmt) {
+    auto regs = is_short ? GetRegistersShort() : GetRegisters();
+    if (no_zero_reg) {
+      CHECK(!is_short);
+      CHECK_EQ(regs[0], Zero);
+      regs = regs.SubArray(1);
+    }
+    return RepeatCTemplateRegImm(
+        f, regs, &AssemblerRISCV64Test::GetRegisterName, imm_bits, shift, no_zero_imm, fmt);
+  }
+
+  template <typename Imm>
+  std::string RepeatCFImm(void (Riscv64Assembler::*f)(FRegister, Imm),
+                          int imm_bits,
+                          int shift,
+                          const std::string& fmt) {
+    auto regs = GetFPRegisters();
+    return RepeatCTemplateRegImm(
+        f, regs, &AssemblerRISCV64Test::GetFPRegName, imm_bits, shift, /*no_zero_imm=*/false, fmt);
+  }
+
+  template <typename Reg1>
+  std::string RepeatTemplatedShortRegistersImm(void (Riscv64Assembler::*f)(Reg1,
+                                                                           XRegister,
+                                                                           int32_t),
+                                               ArrayRef<const Reg1> reg1_registers,
+                                               std::string (Base::*GetName1)(const Reg1&),
+                                               int imm_bits,
+                                               int shift,
+                                               bool no_zero_imm,
+                                               const std::string& fmt) {
+    CHECK(f != nullptr);
+    auto imms = CreateImmediateValuesBits(abs(imm_bits), imm_bits > 0, shift);
+    std::string str;
+    for (Reg1 reg1 : reg1_registers) {
+      for (XRegister reg2 : GetRegistersShort()) {
+        for (int64_t imm_raw : imms) {
+          if (no_zero_imm && imm_raw == 0) {
+            continue;
+          }
+
+          int32_t imm = CreateImmediate(imm_raw);
+          (GetAssembler()->*f)(reg1, reg2, imm);
+
+          std::string base = fmt;
+          ReplaceReg(REG1_TOKEN, (this->*GetName1)(reg1), &base);
+          ReplaceReg(REG2_TOKEN, GetRegisterName(reg2), &base);
+          ReplaceImm(imm, /*bias=*/0, /*multiplier=*/1, &base);
+          str += base;
+          str += "\n";
+        }
+      }
+    }
+    return str;
+  }
+
+  std::string RepeatCRRImm(void (Riscv64Assembler::*f)(XRegister, XRegister, int32_t),
+                           int imm_bits,
+                           int shift,
+                           const std::string& fmt) {
+    return RepeatTemplatedShortRegistersImm(f,
+                                            GetRegistersShort(),
+                                            &AssemblerRISCV64Test::GetRegisterName,
+                                            imm_bits,
+                                            shift,
+                                            /*no_zero_imm=*/false,
+                                            fmt);
+  }
+
+  std::string RepeatCFRImm(void (Riscv64Assembler::*f)(FRegister, XRegister, int32_t),
+                           int imm_bits,
+                           int shift,
+                           const std::string& fmt) {
+    return RepeatTemplatedShortRegistersImm(f,
+                                            GetFPRegistersShort(),
+                                            &AssemblerRISCV64Test::GetFPRegName,
+                                            imm_bits,
+                                            shift,
+                                            /*no_zero_imm=*/false,
+                                            fmt);
+  }
+
+  std::string RepeatCRRShort(void (Riscv64Assembler::*f)(XRegister, XRegister),
+                             const std::string& fmt) {
+    return RepeatTemplatedRegisters(f,
+                                    GetRegistersShort(),
+                                    GetRegistersShort(),
+                                    &AssemblerRISCV64Test::GetRegisterName,
+                                    &AssemblerRISCV64Test::GetRegisterName,
+                                    fmt);
+  }
+
+  std::string RepeatCRRNonZero(void (Riscv64Assembler::*f)(XRegister, XRegister),
+                               const std::string& fmt) {
+    auto regs = GetRegisters();
+    CHECK_EQ(regs[0], Zero);
+    auto regs_no_zero = regs.SubArray(1);
+    return RepeatTemplatedRegisters(f,
+                                    regs_no_zero,
+                                    regs_no_zero,
+                                    &AssemblerRISCV64Test::GetRegisterName,
+                                    &AssemblerRISCV64Test::GetRegisterName,
+                                    fmt);
+  }
+
+  std::string RepeatCRShort(void (Riscv64Assembler::*f)(XRegister), const std::string& fmt) {
+    return RepeatTemplatedRegister(
+        f, GetRegistersShort(), &AssemblerRISCV64Test::GetRegisterName, fmt);
+  }
+
+  template <typename Imm>
+  std::string RepeatImm(void (Riscv64Assembler::*f)(Imm),
+                        bool no_zero_imm,
+                        int imm_bits,
+                        int shift,
+                        const std::string& fmt) {
+    auto imms = CreateImmediateValuesBits(abs(imm_bits), imm_bits > 0, shift);
+    std::string str;
+    for (int64_t imm_raw : imms) {
+      if (no_zero_imm && imm_raw == 0) {
+        continue;
+      }
+
+      Imm imm = CreateImmediate(imm_raw);
+      (GetAssembler()->*f)(imm);
+
+      std::string base = fmt;
+      ReplaceImm(imm, /*bias=*/0, /*multiplier=*/1, &base);
+      str += base;
+      str += "\n";
+    }
+
+    return str;
+  }
+
+  std::string RepeatRNoZero(void (Riscv64Assembler::*f)(XRegister), const std::string& fmt) {
+    auto regs = GetRegisters();
+    CHECK_EQ(regs[0], Zero);
+    return RepeatTemplatedRegister(
+        f, regs.SubArray(1), &AssemblerRISCV64Test::GetRegisterName, fmt);
+  }
+
   template <typename Reg1, typename Reg2>
   std::string RepeatTemplatedRegistersRoundingMode(
       void (Riscv64Assembler::*f)(Reg1, Reg2, FPRoundingMode),
@@ -1974,6 +2200,7 @@
 
   std::unique_ptr<const Riscv64InstructionSetFeatures> instruction_set_features_;
   bool use_simple_march_ = false;
+  bool compressed_mode_ = false;
 };
 
 TEST_F(AssemblerRISCV64Test, Toolchain) { EXPECT_TRUE(CheckTools()); }
@@ -2874,6 +3101,432 @@
   DriverStr(RepeatrF(&Riscv64Assembler::FClassD, "fclass.d {reg1}, {reg2}"), "FClassD");
 }
 
+TEST_F(AssemblerRISCV64Test, CLwsp) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CLwsp,
+                        /*is_short=*/false,
+                        /*no_zero_reg=*/true,
+                        /*no_zero_imm=*/false,
+                        /*imm_bits=*/6,
+                        /*shift=*/2,
+                        "c.lwsp {reg}, {imm}(sp)"),
+            "CLwsp");
+}
+
+TEST_F(AssemblerRISCV64Test, CLdsp) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CLdsp,
+                        /*is_short=*/false,
+                        /*no_zero_reg=*/true,
+                        /*no_zero_imm=*/false,
+                        /*imm_bits=*/6,
+                        /*shift=*/3,
+                        "c.ldsp {reg}, {imm}(sp)"),
+            "CLdsp");
+}
+
+TEST_F(AssemblerRISCV64Test, CFLdsp) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCFImm(
+                &Riscv64Assembler::CFLdsp, /*imm_bits=*/6, /*shift=*/3, "c.fldsp {reg}, {imm}(sp)"),
+            "CFLdsp");
+}
+
+TEST_F(AssemblerRISCV64Test, CSwsp) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CSwsp,
+                        /*is_short=*/false,
+                        /*no_zero_reg=*/false,
+                        /*no_zero_imm=*/false,
+                        /*imm_bits=*/6,
+                        /*shift=*/2,
+                        "c.swsp {reg}, {imm}(sp)"),
+            "CLwsp");
+}
+
+TEST_F(AssemblerRISCV64Test, CSdsp) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CSdsp,
+                        /*is_short=*/false,
+                        /*no_zero_reg=*/false,
+                        /*no_zero_imm=*/false,
+                        /*imm_bits=*/6,
+                        /*shift=*/3,
+                        "c.sdsp {reg}, {imm}(sp)"),
+            "CLdsp");
+}
+
+TEST_F(AssemblerRISCV64Test, CFSdsp) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCFImm(
+                &Riscv64Assembler::CFSdsp, /*imm_bits=*/6, /*shift=*/3, "c.fsdsp {reg}, {imm}(sp)"),
+            "CFLdsp");
+}
+
+TEST_F(AssemblerRISCV64Test, CLw) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRImm(
+                &Riscv64Assembler::CLw, /*imm_bits=*/5, /*shift=*/2, "c.lw {reg1}, {imm}({reg2})"),
+            "CLw");
+}
+
+TEST_F(AssemblerRISCV64Test, CLd) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRImm(
+                &Riscv64Assembler::CLd, /*imm_bits=*/5, /*shift=*/3, "c.ld {reg1}, {imm}({reg2})"),
+            "CLd");
+}
+
+TEST_F(AssemblerRISCV64Test, CFLd) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCFRImm(&Riscv64Assembler::CFLd,
+                         /*imm_bits=*/5,
+                         /*shift=*/3,
+                         "c.fld {reg1}, {imm}({reg2})"),
+            "CFLd");
+}
+
+TEST_F(AssemblerRISCV64Test, CSw) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRImm(
+                &Riscv64Assembler::CSw, /*imm_bits=*/5, /*shift=*/2, "c.sw {reg1}, {imm}({reg2})"),
+            "CSw");
+}
+
+TEST_F(AssemblerRISCV64Test, CSd) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRImm(
+                &Riscv64Assembler::CSd, /*imm_bits=*/5, /*shift=*/3, "c.sd {reg1}, {imm}({reg2})"),
+            "CSd");
+}
+
+TEST_F(AssemblerRISCV64Test, CFSd) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCFRImm(&Riscv64Assembler::CFSd,
+                         /*imm_bits=*/5,
+                         /*shift=*/3,
+                         "c.fsd {reg1}, {imm}({reg2})"),
+            "CFSd");
+}
+
+TEST_F(AssemblerRISCV64Test, CLi) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CLi,
+                        /*is_short=*/false,
+                        /*no_zero_reg=*/true,
+                        /*no_zero_imm=*/false,
+                        /*imm_bits=*/-6,
+                        /*shift=*/0,
+                        "c.li {reg}, {imm}"),
+            "CLi");
+}
+
+TEST_F(AssemblerRISCV64Test, CLui) {
+  ScopedCompressedMode cm(this);
+  std::string str;
+  auto imms = CreateImmediateValuesBits(/*imm_bits=*/5, /*as_uint=*/true);
+  for (uint32_t v = 0xfffe0; v <= 0xfffff; ++v) {
+    imms.push_back(v);
+  }
+
+  for (XRegister reg : GetRegisters()) {
+    for (int64_t imm_raw : imms) {
+      if (imm_raw == 0) {
+        continue;
+      }
+
+      if (reg == Zero || reg == SP) {
+        continue;
+      }
+
+      uint32_t imm = CreateImmediate(imm_raw);
+      GetAssembler()->CLui(reg, imm);
+
+      std::string base = "c.lui {reg}, {imm}";
+      ReplaceReg(REG_TOKEN, GetRegisterName(reg), &base);
+      ReplaceImm(imm, /*bias=*/0, /*multiplier=*/1, &base);
+      str += base;
+      str += "\n";
+    }
+  }
+
+  DriverStr(str, "CLui");
+}
+
+TEST_F(AssemblerRISCV64Test, CAddi) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CAddi,
+                        /*is_short=*/false,
+                        /*no_zero_reg=*/true,
+                        /*no_zero_imm=*/true,
+                        /*imm_bits=*/-6,
+                        /*shift=*/0,
+                        "c.addi {reg}, {imm}"),
+            "CAddi");
+}
+
+TEST_F(AssemblerRISCV64Test, CAddiw) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CAddiw,
+                        /*is_short=*/false,
+                        /*no_zero_reg=*/true,
+                        /*no_zero_imm=*/false,
+                        /*imm_bits=*/-6,
+                        /*shift=*/0,
+                        "c.addiw {reg}, {imm}"),
+            "CAddiw");
+}
+
+TEST_F(AssemblerRISCV64Test, CAddi16Sp) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatImm(&Riscv64Assembler::CAddi16Sp,
+                      /*no_zero_imm=*/true,
+                      /*imm_bits=*/-6,
+                      /*shift=*/4,
+                      "c.addi16sp sp, {imm}"),
+            "CAddi16Sp");
+}
+
+TEST_F(AssemblerRISCV64Test, CAddi4Spn) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CAddi4Spn,
+                        /*is_short=*/true,
+                        /*no_zero_reg=*/false,
+                        /*no_zero_imm=*/true,
+                        /*imm_bits=*/8,
+                        /*shift=*/2,
+                        "c.addi4spn {reg}, sp, {imm}"),
+            "CAddi4Spn");
+}
+
+TEST_F(AssemblerRISCV64Test, CSlli) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CSlli,
+                        /*is_short=*/false,
+                        /*no_zero_reg=*/true,
+                        /*no_zero_imm=*/true,
+                        /*imm_bits=*/6,
+                        /*shift=*/0,
+                        "c.slli {reg}, {imm}"),
+            "CSlli");
+}
+
+TEST_F(AssemblerRISCV64Test, CSRli) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CSrli,
+                        /*is_short=*/true,
+                        /*no_zero_reg=*/false,
+                        /*no_zero_imm=*/true,
+                        /*imm_bits=*/6,
+                        /*shift=*/0,
+                        "c.srli {reg}, {imm}"),
+            "CSRli");
+}
+
+TEST_F(AssemblerRISCV64Test, CSRai) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CSrai,
+                        /*is_short=*/true,
+                        /*no_zero_reg=*/false,
+                        /*no_zero_imm=*/true,
+                        /*imm_bits=*/6,
+                        /*shift=*/0,
+                        "c.srai {reg}, {imm}"),
+            "CSRai");
+}
+
+TEST_F(AssemblerRISCV64Test, CAndi) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CAndi,
+                        /*is_short=*/true,
+                        /*no_zero_reg=*/false,
+                        /*no_zero_imm=*/false,
+                        /*imm_bits=*/-6,
+                        /*shift=*/0,
+                        "c.andi {reg}, {imm}"),
+            "CAndi");
+}
+
+TEST_F(AssemblerRISCV64Test, CMv) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRNonZero(&Riscv64Assembler::CMv, "c.mv {reg1}, {reg2}"), "CMv");
+}
+
+TEST_F(AssemblerRISCV64Test, CAdd) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRNonZero(&Riscv64Assembler::CAdd, "c.add {reg1}, {reg2}"), "CAdd");
+}
+
+TEST_F(AssemblerRISCV64Test, CAnd) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRShort(&Riscv64Assembler::CAnd, "c.and {reg1}, {reg2}"), "CAnd");
+}
+
+TEST_F(AssemblerRISCV64Test, COr) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRShort(&Riscv64Assembler::COr, "c.or {reg1}, {reg2}"), "COr");
+}
+
+TEST_F(AssemblerRISCV64Test, CXor) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRShort(&Riscv64Assembler::CXor, "c.xor {reg1}, {reg2}"), "CXor");
+}
+
+TEST_F(AssemblerRISCV64Test, CSub) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRShort(&Riscv64Assembler::CSub, "c.sub {reg1}, {reg2}"), "CSub");
+}
+
+TEST_F(AssemblerRISCV64Test, CAddw) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRShort(&Riscv64Assembler::CAddw, "c.addw {reg1}, {reg2}"), "CAddw");
+}
+
+TEST_F(AssemblerRISCV64Test, CSubw) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRShort(&Riscv64Assembler::CSubw, "c.subw {reg1}, {reg2}"), "CSubw");
+}
+
+TEST_F(AssemblerRISCV64Test, CLbu) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRImm(&Riscv64Assembler::CLbu,
+                         /*imm_bits=*/2,
+                         /*shift=*/0,
+                         "c.lbu {reg1}, {imm}({reg2})"),
+            "CLbu");
+}
+
+TEST_F(AssemblerRISCV64Test, CLhu) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRImm(&Riscv64Assembler::CLhu,
+                         /*imm_bits=*/1,
+                         /*shift=*/1,
+                         "c.lhu {reg1}, {imm}({reg2})"),
+            "CLhu");
+}
+
+TEST_F(AssemblerRISCV64Test, CLh) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRImm(&Riscv64Assembler::CLh,
+                         /*imm_bits=*/1,
+                         /*shift=*/1,
+                         "c.lh {reg1}, {imm}({reg2})"),
+            "CLh");
+}
+
+TEST_F(AssemblerRISCV64Test, CSb) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRImm(&Riscv64Assembler::CSb,
+                         /*imm_bits=*/2,
+                         /*shift=*/0,
+                         "c.sb {reg1}, {imm}({reg2})"),
+            "CSb");
+}
+
+TEST_F(AssemblerRISCV64Test, CSh) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRImm(&Riscv64Assembler::CSh,
+                         /*imm_bits=*/1,
+                         /*shift=*/1,
+                         "c.sh {reg1}, {imm}({reg2})"),
+            "CSh");
+}
+
+TEST_F(AssemblerRISCV64Test, CZext_b) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRShort(&Riscv64Assembler::CZext_b, "c.zext.b {reg}"), "CZext_b");
+}
+
+TEST_F(AssemblerRISCV64Test, CSext_b) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRShort(&Riscv64Assembler::CSext_b, "c.sext.b {reg}"), "CSext_b");
+}
+
+TEST_F(AssemblerRISCV64Test, CZext_h) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRShort(&Riscv64Assembler::CZext_h, "c.zext.h {reg}"), "CZext_h");
+}
+
+TEST_F(AssemblerRISCV64Test, CSext_h) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRShort(&Riscv64Assembler::CSext_h, "c.sext.h {reg}"), "CSext_h");
+}
+
+TEST_F(AssemblerRISCV64Test, CZext_w) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRShort(&Riscv64Assembler::CZext_w, "c.zext.w {reg}"), "CZext_w");
+}
+
+TEST_F(AssemblerRISCV64Test, CNot) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRShort(&Riscv64Assembler::CNot, "c.not {reg}"), "CNot");
+}
+
+TEST_F(AssemblerRISCV64Test, CMul) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRRShort(&Riscv64Assembler::CMul, "c.mul {reg1}, {reg2}"), "CMul");
+}
+
+TEST_F(AssemblerRISCV64Test, CJ) {
+  ScopedCompressedMode cm(this);
+  DriverStr(
+      RepeatImm(
+          &Riscv64Assembler::CJ, /*no_zero_imm=*/false, /*imm_bits=*/-11, /*shift=*/1, "c.j {imm}"),
+      "CJ");
+}
+
+TEST_F(AssemblerRISCV64Test, CJr) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatRNoZero(&Riscv64Assembler::CJr, "c.jr {reg}"), "CJr");
+}
+
+TEST_F(AssemblerRISCV64Test, CJalr) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatRNoZero(&Riscv64Assembler::CJalr, "c.jalr {reg}"), "CJalr");
+}
+
+TEST_F(AssemblerRISCV64Test, CBeqz) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CBeqz,
+                        /*is_short=*/true,
+                        /*no_zero_reg=*/false,
+                        /*no_zero_imm=*/false,
+                        /*imm_bits=*/-8,
+                        /*shift=*/1,
+                        "c.beqz {reg}, {imm}"),
+            "CBeqz");
+}
+
+TEST_F(AssemblerRISCV64Test, CBnez) {
+  ScopedCompressedMode cm(this);
+  DriverStr(RepeatCRImm(&Riscv64Assembler::CBnez,
+                        /*is_short=*/true,
+                        /*no_zero_reg=*/false,
+                        /*no_zero_imm=*/false,
+                        /*imm_bits=*/-8,
+                        /*shift=*/1,
+                        "c.bnez {reg}, {imm}"),
+            "CBnez");
+}
+
+TEST_F(AssemblerRISCV64Test, CEbreak) {
+  ScopedCompressedMode cm(this);
+  __ CEbreak();
+  DriverStr("c.ebreak", "CEbreak");
+}
+
+TEST_F(AssemblerRISCV64Test, CNop) {
+  ScopedCompressedMode cm(this);
+  __ CNop();
+  DriverStr("c.nop", "CNop");
+}
+
+TEST_F(AssemblerRISCV64Test, CUnimp) {
+  ScopedCompressedMode cm(this);
+  __ CUnimp();
+  DriverStr("c.unimp", "CUnimp");
+}
+
 TEST_F(AssemblerRISCV64Test, AddUw) {
   DriverStr(RepeatRRR(&Riscv64Assembler::AddUw, "add.uw {reg1}, {reg2}, {reg3}"), "AddUw");
 }