diff options
author | 2023-12-25 12:34:40 +0300 | |
---|---|---|
committer | 2024-02-20 14:57:03 +0000 | |
commit | fddd6a4c78ff8bee410e4e07a500e2495ee43dcf (patch) | |
tree | 0076a28b24659393791c1dd1dcbd3cf98a19ebb7 | |
parent | 3293b6bca259eb73789628e6cc948150396a113c (diff) |
riscv64: Implement "C" extension in assembler
Support Compressed extension in Masm
Implement tests for new instructions
Test: m art_compiler_host_tests
Test: $NATIVE_TESTS/art_compiler_host_tests_intermediates/art_compiler_host_tests
Change-Id: I370d3c98ffc4948d09c5a01665305ffdea7e5d54
-rw-r--r-- | compiler/utils/riscv64/assembler_riscv64.cc | 284 | ||||
-rw-r--r-- | compiler/utils/riscv64/assembler_riscv64.h | 405 | ||||
-rw-r--r-- | compiler/utils/riscv64/assembler_riscv64_test.cc | 657 |
3 files changed, 1316 insertions, 30 deletions
diff --git a/compiler/utils/riscv64/assembler_riscv64.cc b/compiler/utils/riscv64/assembler_riscv64.cc index 7011e9e616..eeb4537a31 100644 --- a/compiler/utils/riscv64/assembler_riscv64.cc +++ b/compiler/utils/riscv64/assembler_riscv64.cc @@ -61,18 +61,6 @@ void Riscv64Assembler::FinalizeCode() { 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 @@ void Riscv64Assembler::FClassD(XRegister rd, FRegister rs1) { /////////////////////////////// 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::FLoadd(FRegister rd, Literal* literal) { 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 @@ void Riscv64Assembler::FinalizeLabeledBranch(Riscv64Label* label) { // 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 @@ void Riscv64Assembler::PromoteBranches() { 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 @@ void Riscv64Assembler::EmitJumpTables() { 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 193585bc7e..1696251bf6 100644 --- a/compiler/utils/riscv64/assembler_riscv64.h +++ b/compiler/utils/riscv64/assembler_riscv64.h @@ -490,6 +490,64 @@ class Riscv64Assembler final : public Assembler { 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 @@ class Riscv64Assembler final : public Assembler { // 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 @@ class Riscv64Assembler final : public Assembler { 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 @@ class Riscv64Assembler final : public Assembler { 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 @@ class Riscv64Assembler final : public Assembler { 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 @@ class Riscv64Assembler final : public Assembler { 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 @@ class Riscv64Assembler final : public Assembler { 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 @@ class Riscv64Assembler final : public Assembler { 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 @@ class Riscv64Assembler final : public Assembler { 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 @@ class Riscv64Assembler final : public Assembler { 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 @@ class Riscv64Assembler final : public Assembler { 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 @@ class Riscv64Assembler final : public Assembler { 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 2229695a95..3f7d617789 100644 --- a/compiler/utils/riscv64/assembler_riscv64_test.cc +++ b/compiler/utils/riscv64/assembler_riscv64_test.cc @@ -59,6 +59,24 @@ class AssemblerRISCV64Test : public AssemblerTest<Riscv64Assembler, 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 @@ class AssemblerRISCV64Test : public AssemblerTest<Riscv64Assembler, 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 @@ class AssemblerRISCV64Test : public AssemblerTest<Riscv64Assembler, 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 @@ class AssemblerRISCV64Test : public AssemblerTest<Riscv64Assembler, 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 @@ class AssemblerRISCV64Test : public AssemblerTest<Riscv64Assembler, 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 @@ class AssemblerRISCV64Test : public AssemblerTest<Riscv64Assembler, 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 @@ class AssemblerRISCV64Test : public AssemblerTest<Riscv64Assembler, 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 @@ TEST_F(AssemblerRISCV64Test, FClassD) { 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"); } |