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");
}