diff --git a/Common/CPUDetect.h b/Common/CPUDetect.h index 36686f38f4..5d3dd0f3e2 100644 --- a/Common/CPUDetect.h +++ b/Common/CPUDetect.h @@ -104,6 +104,7 @@ struct CPUInfo { bool RiscV_D; bool RiscV_C; bool RiscV_V; + bool RiscV_B; // Quirks struct { diff --git a/Common/RiscVCPUDetect.cpp b/Common/RiscVCPUDetect.cpp index 6aeed2e117..912c4f3e49 100644 --- a/Common/RiscVCPUDetect.cpp +++ b/Common/RiscVCPUDetect.cpp @@ -173,6 +173,7 @@ void CPUInfo::Detect() RiscV_D = ExtensionSupported(hwcap, 'D'); RiscV_C = ExtensionSupported(hwcap, 'C'); RiscV_V = ExtensionSupported(hwcap, 'V'); + RiscV_B = ExtensionSupported(hwcap, 'B'); } // Turn the cpu info into a string we can show diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index b040dec92d..611c969889 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -40,7 +40,8 @@ static inline uint8_t FloatBitsSupported() { return 0; } -static inline bool SupportsMulDiv() { +static inline bool SupportsMulDiv(bool allowZmmul = false) { + // TODO allowZmmul? return cpu_info.RiscV_M; } @@ -57,6 +58,11 @@ static inline bool SupportsVector() { return cpu_info.RiscV_V; } +static inline bool SupportsBitmanip(char zbx) { + // TODO: Allow and detect sub-support? + return cpu_info.RiscV_B; +} + enum class Opcode32 { // Note: invalid, just used for FixupBranch. ZERO = 0b0000000, @@ -167,6 +173,25 @@ enum class Funct3 { VLS_32 = 0b110, VLS_64 = 0b111, + CLMUL = 0b001, + CLMULR = 0b010, + CLMULH = 0b011, + MIN = 0b100, + MINU = 0b101, + MAX = 0b110, + MAXU = 0b111, + + SH1ADD = 0b010, + SH2ADD = 0b100, + SH3ADD = 0b110, + + COUNT_SEXT_ROL = 0b001, + ZEXT = 0b100, + ROR = 0b101, + + BSET = 0b001, + BEXT = 0b101, + C_ADDI4SPN = 0b000, C_FLD = 0b001, C_LW = 0b010, @@ -225,6 +250,15 @@ enum class Funct7 { SRA = 0b0100000, MULDIV = 0b0000001, + + ADDUW_ZEXT = 0b0000100, + MINMAX_CLMUL = 0b0000101, + SH_ADD = 0b0010000, + BSET_ORC = 0b0010100, + NOT = 0b0100000, + BCLREXT = 0b0100100, + COUNT_SEXT_ROT = 0b0110000, + BINV_REV = 0b0110100, }; enum class Funct5 { @@ -297,6 +331,13 @@ enum class Funct5 { VMSIF = 0b00011, VIOTA = 0b10000, VID = 0b10001, + + CLZ = 0b00000, + CTZ = 0b00001, + CPOP = 0b00010, + SEXT_B = 0b00100, + SEXT_H = 0b00101, + ORC_B = 0b00111, }; enum class Funct4 { @@ -531,6 +572,12 @@ static inline u32 EncodeGR(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg return EncodeR(opcode, rd, funct3, rs1, rs2, funct7); } +static inline u32 EncodeGR(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, Funct5 funct5, Funct7 funct7) { + _assert_msg_(IsGPR(rd), "R instruction rd must be GPR"); + _assert_msg_(IsGPR(rs1), "R instruction rs1 must be GPR"); + return EncodeR(opcode, rd, funct3, rs1, (RiscVReg)funct5, funct7); +} + static inline u32 EncodeAtomicR(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, Atomic ordering, Funct5 funct5) { u32 funct7 = ((u32)funct5 << 2) | (u32)ordering; return EncodeGR(opcode, rd, funct3, rs1, rs2, (Funct7)funct7); @@ -570,6 +617,14 @@ static inline u32 EncodeGI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg return EncodeI(opcode, rd, funct3, rs1, simm12); } +static inline u32 EncodeGIShift(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, u32 shamt, Funct7 funct7) { + _assert_msg_(IsGPR(rd), "IShift instruction rd must be GPR"); + _assert_msg_(IsGPR(rs1), "IShift instruction rs1 must be GPR"); + _assert_msg_(shamt < BitsSupported(), "IShift instruction shift out of range %d", shamt); + // Low bits of funct7 must be 0 to allow for shift amounts. + return (u32)opcode | ((u32)DecodeReg(rd) << 7) | ((u32)funct3 << 12) | ((u32)DecodeReg(rs1) << 15) | ((u32)shamt << 20) | ((u32)funct7 << 25); +} + static inline u32 EncodeI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, Funct12 funct12) { return EncodeI(opcode, rd, funct3, rs1, SignReduce32((s32)funct12, 12)); } @@ -1452,12 +1507,12 @@ void RiscVEmitter::SLLI(RiscVReg rd, RiscVReg rs1, u32 shamt) { // Not sure if shamt=0 is legal or not, let's play it safe. _assert_msg_(shamt > 0 && shamt < BitsSupported(), "Shift out of range"); - if (AutoCompress() && rd == rs1 && shamt <= (u32)(BitsSupported() == 64 ? 63 : 31)) { + if (AutoCompress() && rd == rs1 && shamt != 0 && shamt <= (u32)(BitsSupported() == 64 ? 63 : 31)) { C_SLLI(rd, (u8)shamt); return; } - Write32(EncodeGI(Opcode32::OP_IMM, rd, Funct3::SLL, rs1, shamt)); + Write32(EncodeGIShift(Opcode32::OP_IMM, rd, Funct3::SLL, rs1, shamt, Funct7::ZERO)); } void RiscVEmitter::SRLI(RiscVReg rd, RiscVReg rs1, u32 shamt) { @@ -1470,7 +1525,7 @@ void RiscVEmitter::SRLI(RiscVReg rd, RiscVReg rs1, u32 shamt) { return; } - Write32(EncodeGI(Opcode32::OP_IMM, rd, Funct3::SRL, rs1, shamt)); + Write32(EncodeGIShift(Opcode32::OP_IMM, rd, Funct3::SRL, rs1, shamt, Funct7::ZERO)); } void RiscVEmitter::SRAI(RiscVReg rd, RiscVReg rs1, u32 shamt) { @@ -1483,7 +1538,7 @@ void RiscVEmitter::SRAI(RiscVReg rd, RiscVReg rs1, u32 shamt) { return; } - Write32(EncodeGI(Opcode32::OP_IMM, rd, Funct3::SRL, rs1, shamt | (1 << 10))); + Write32(EncodeGIShift(Opcode32::OP_IMM, rd, Funct3::SRL, rs1, shamt, Funct7::SRA)); } void RiscVEmitter::ADD(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { @@ -1665,7 +1720,7 @@ void RiscVEmitter::SLLIW(RiscVReg rd, RiscVReg rs1, u32 shamt) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); // Not sure if shamt=0 is legal or not, let's play it safe. _assert_msg_(shamt > 0 && shamt < 32, "Shift out of range"); - Write32(EncodeGI(Opcode32::OP_IMM_32, rd, Funct3::SLL, rs1, shamt)); + Write32(EncodeGIShift(Opcode32::OP_IMM_32, rd, Funct3::SLL, rs1, shamt, Funct7::ZERO)); } void RiscVEmitter::SRLIW(RiscVReg rd, RiscVReg rs1, u32 shamt) { @@ -1677,7 +1732,7 @@ void RiscVEmitter::SRLIW(RiscVReg rd, RiscVReg rs1, u32 shamt) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); // Not sure if shamt=0 is legal or not, let's play it safe. _assert_msg_(shamt > 0 && shamt < 32, "Shift out of range"); - Write32(EncodeGI(Opcode32::OP_IMM_32, rd, Funct3::SRL, rs1, shamt)); + Write32(EncodeGIShift(Opcode32::OP_IMM_32, rd, Funct3::SRL, rs1, shamt, Funct7::ZERO)); } void RiscVEmitter::SRAIW(RiscVReg rd, RiscVReg rs1, u32 shamt) { @@ -1689,7 +1744,7 @@ void RiscVEmitter::SRAIW(RiscVReg rd, RiscVReg rs1, u32 shamt) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); // Not sure if shamt=0 is legal or not, let's play it safe. _assert_msg_(shamt > 0 && shamt < 32, "Shift out of range"); - Write32(EncodeGI(Opcode32::OP_IMM_32, rd, Funct3::SRL, rs1, shamt | (1 << 10))); + Write32(EncodeGIShift(Opcode32::OP_IMM_32, rd, Funct3::SRL, rs1, shamt, Funct7::SRA)); } void RiscVEmitter::ADDW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { @@ -1753,63 +1808,63 @@ void RiscVEmitter::SRAW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { } void RiscVEmitter::MUL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { - _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + _assert_msg_(SupportsMulDiv(true), "%s instruction unsupported without M/Zmmul", __func__); // Not explicitly a HINT, but seems sensible to restrict just in case. _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); Write32(EncodeGR(Opcode32::OP, rd, Funct3::MUL, rs1, rs2, Funct7::MULDIV)); } void RiscVEmitter::MULH(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { - _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + _assert_msg_(SupportsMulDiv(true), "%s instruction unsupported without M/Zmmul", __func__); // Not explicitly a HINT, but seems sensible to restrict just in case. _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); Write32(EncodeGR(Opcode32::OP, rd, Funct3::MULH, rs1, rs2, Funct7::MULDIV)); } void RiscVEmitter::MULHSU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { - _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + _assert_msg_(SupportsMulDiv(true), "%s instruction unsupported without M/Zmmul", __func__); // Not explicitly a HINT, but seems sensible to restrict just in case. _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); Write32(EncodeGR(Opcode32::OP, rd, Funct3::MULHSU, rs1, rs2, Funct7::MULDIV)); } void RiscVEmitter::MULHU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { - _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + _assert_msg_(SupportsMulDiv(true), "%s instruction unsupported without M/Zmmul", __func__); // Not explicitly a HINT, but seems sensible to restrict just in case. _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); Write32(EncodeGR(Opcode32::OP, rd, Funct3::MULHU, rs1, rs2, Funct7::MULDIV)); } void RiscVEmitter::DIV(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { - _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + _assert_msg_(SupportsMulDiv(), "%s instruction unsupported without M", __func__); // Not explicitly a HINT, but seems sensible to restrict just in case. _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); Write32(EncodeGR(Opcode32::OP, rd, Funct3::DIV, rs1, rs2, Funct7::MULDIV)); } void RiscVEmitter::DIVU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { - _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + _assert_msg_(SupportsMulDiv(), "%s instruction unsupported without M", __func__); // Not explicitly a HINT, but seems sensible to restrict just in case. _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); Write32(EncodeGR(Opcode32::OP, rd, Funct3::DIVU, rs1, rs2, Funct7::MULDIV)); } void RiscVEmitter::REM(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { - _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + _assert_msg_(SupportsMulDiv(), "%s instruction unsupported without M", __func__); // Not explicitly a HINT, but seems sensible to restrict just in case. _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); Write32(EncodeGR(Opcode32::OP, rd, Funct3::REM, rs1, rs2, Funct7::MULDIV)); } void RiscVEmitter::REMU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { - _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + _assert_msg_(SupportsMulDiv(), "%s instruction unsupported without M", __func__); // Not explicitly a HINT, but seems sensible to restrict just in case. _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); Write32(EncodeGR(Opcode32::OP, rd, Funct3::REMU, rs1, rs2, Funct7::MULDIV)); } void RiscVEmitter::MULW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { - _assert_msg_(BitsSupported() >= 64 && SupportsMulDiv(), "%s is only valid with R64M", __func__); + _assert_msg_(BitsSupported() >= 64 && SupportsMulDiv(true), "%s is only valid with R64M", __func__); // Not explicitly a HINT, but seems sensible to restrict just in case. _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::MUL, rs1, rs2, Funct7::MULDIV)); @@ -3420,11 +3475,13 @@ void RiscVEmitter::VMXNOR_MM(RiscVReg vd, RiscVReg vs2, RiscVReg vs1) { void RiscVEmitter::VCPOP_M(RiscVReg rd, RiscVReg vs2, VUseMask vm) { _assert_msg_(IsGPR(rd), "%s instruction rd must be GPR", __func__); + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); Write32(EncodeV(rd, Funct3::OPMVV, (RiscVReg)Funct5::VPOPC, vs2, vm, Funct6::VRWUNARY0)); } void RiscVEmitter::VFIRST_M(RiscVReg rd, RiscVReg vs2, VUseMask vm) { _assert_msg_(IsGPR(rd), "%s instruction rd must be GPR", __func__); + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); Write32(EncodeV(rd, Funct3::OPMVV, (RiscVReg)Funct5::VFIRST, vs2, vm, Funct6::VRWUNARY0)); } @@ -3465,6 +3522,7 @@ void RiscVEmitter::VID_M(RiscVReg vd, VUseMask vm) { void RiscVEmitter::VMV_X_S(RiscVReg rd, RiscVReg vs2) { _assert_msg_(IsGPR(rd), "%s instruction rd must be GPR", __func__); + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); Write32(EncodeV(rd, Funct3::OPMVV, (RiscVReg)Funct5::VMV_S, vs2, VUseMask::NONE, Funct6::VRWUNARY0)); } @@ -3565,6 +3623,309 @@ void RiscVEmitter::VMVR_V(int regs, RiscVReg vd, RiscVReg vs2) { Write32(EncodeIVI(vd, regs - 1, vs2, VUseMask::NONE, Funct6::VSMUL_VMVR)); } +void RiscVEmitter::ADD_UW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + if (BitsSupported() == 32) { + ADD(rd, rs1, rs2); + return; + } + + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('a'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::ADD, rs1, rs2, Funct7::ADDUW_ZEXT)); +} + +void RiscVEmitter::SH_ADD(int shift, RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('a'), "%s instruction unsupported without B", __func__); + if (shift == 1) + Write32(EncodeGR(Opcode32::OP, rd, Funct3::SH1ADD, rs1, rs2, Funct7::SH_ADD)); + else if (shift == 2) + Write32(EncodeGR(Opcode32::OP, rd, Funct3::SH2ADD, rs1, rs2, Funct7::SH_ADD)); + else if (shift == 3) + Write32(EncodeGR(Opcode32::OP, rd, Funct3::SH3ADD, rs1, rs2, Funct7::SH_ADD)); + else + _assert_msg_(shift >= 1 && shift <= 3, "%s shift amount must be 1-3", __func__); +} + +void RiscVEmitter::SH_ADD_UW(int shift, RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + if (BitsSupported() == 32) { + SH_ADD(shift, rd, rs1, rs2); + return; + } + + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('a'), "%s instruction unsupported without B", __func__); + if (shift == 1) + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::SH1ADD, rs1, rs2, Funct7::SH_ADD)); + else if (shift == 2) + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::SH2ADD, rs1, rs2, Funct7::SH_ADD)); + else if (shift == 3) + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::SH3ADD, rs1, rs2, Funct7::SH_ADD)); + else + _assert_msg_(shift >= 1 && shift <= 3, "%s shift amount must be 1-3", __func__); +} + +void RiscVEmitter::SLLI_UW(RiscVReg rd, RiscVReg rs1, u32 shamt) { + if (BitsSupported() == 32) { + SLLI(rd, rs1, shamt); + return; + } + + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('a'), "%s instruction unsupported without B", __func__); + // Not sure if shamt=0 is legal or not, let's play it safe. + _assert_msg_(shamt > 0 && shamt < BitsSupported(), "Shift %d out of range", shamt); + Write32(EncodeGIShift(Opcode32::OP_IMM_32, rd, Funct3::SLL, rs1, shamt, Funct7::ADDUW_ZEXT)); +} + +void RiscVEmitter::ANDN(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::AND, rs1, rs2, Funct7::NOT)); +} + +void RiscVEmitter::ORN(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::OR, rs1, rs2, Funct7::NOT)); +} + +void RiscVEmitter::XNOR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::XOR, rs1, rs2, Funct7::NOT)); +} + +void RiscVEmitter::CLZ(RiscVReg rd, RiscVReg rs) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP_IMM, rd, Funct3::COUNT_SEXT_ROL, rs, Funct5::CLZ, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::CLZW(RiscVReg rd, RiscVReg rs) { + if (BitsSupported() == 32) { + CLZ(rd, rs); + return; + } + + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP_IMM_32, rd, Funct3::COUNT_SEXT_ROL, rs, Funct5::CLZ, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::CTZ(RiscVReg rd, RiscVReg rs) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP_IMM, rd, Funct3::COUNT_SEXT_ROL, rs, Funct5::CTZ, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::CTZW(RiscVReg rd, RiscVReg rs) { + if (BitsSupported() == 32) { + CTZ(rd, rs); + return; + } + + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP_IMM_32, rd, Funct3::COUNT_SEXT_ROL, rs, Funct5::CTZ, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::CPOP(RiscVReg rd, RiscVReg rs) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP_IMM, rd, Funct3::COUNT_SEXT_ROL, rs, Funct5::CPOP, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::CPOPW(RiscVReg rd, RiscVReg rs) { + if (BitsSupported() == 32) { + CPOP(rd, rs); + return; + } + + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP_IMM_32, rd, Funct3::COUNT_SEXT_ROL, rs, Funct5::CPOP, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::MAX(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::MAX, rs1, rs2, Funct7::MINMAX_CLMUL)); +} + +void RiscVEmitter::MAXU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::MAXU, rs1, rs2, Funct7::MINMAX_CLMUL)); +} + +void RiscVEmitter::MIN(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::MIN, rs1, rs2, Funct7::MINMAX_CLMUL)); +} + +void RiscVEmitter::MINU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::MINU, rs1, rs2, Funct7::MINMAX_CLMUL)); +} + +void RiscVEmitter::SEXT_B(RiscVReg rd, RiscVReg rs) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP_IMM, rd, Funct3::COUNT_SEXT_ROL, rs, Funct5::SEXT_B, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::SEXT_H(RiscVReg rd, RiscVReg rs) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP_IMM, rd, Funct3::COUNT_SEXT_ROL, rs, Funct5::SEXT_H, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::ZEXT_H(RiscVReg rd, RiscVReg rs) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + if (BitsSupported() == 32) + Write32(EncodeGR(Opcode32::OP, rd, Funct3::ZEXT, rs, R_ZERO, Funct7::ADDUW_ZEXT)); + else + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::ZEXT, rs, R_ZERO, Funct7::ADDUW_ZEXT)); +} + +void RiscVEmitter::ROL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::COUNT_SEXT_ROL, rs1, rs2, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::ROLW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + if (BitsSupported() == 32) { + ROL(rd, rs1, rs2); + return; + } + + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::COUNT_SEXT_ROL, rs1, rs2, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::ROR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::ROR, rs1, rs2, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::RORW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + if (BitsSupported() == 32) { + ROR(rd, rs1, rs2); + return; + } + + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::ROR, rs1, rs2, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::RORI(RiscVReg rd, RiscVReg rs1, u32 shamt) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + // Not sure if shamt=0 is legal or not, let's play it safe. + _assert_msg_(shamt > 0 && shamt < BitsSupported(), "Shift %d out of range", shamt); + Write32(EncodeGIShift(Opcode32::OP_IMM, rd, Funct3::ROR, rs1, shamt, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::RORIW(RiscVReg rd, RiscVReg rs1, u32 shamt) { + if (BitsSupported() == 32) { + RORI(rd, rs1, shamt); + return; + } + + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + // Not sure if shamt=0 is legal or not, let's play it safe. + _assert_msg_(shamt > 0 && shamt < 32, "Shift %d out of range", shamt); + Write32(EncodeGIShift(Opcode32::OP_IMM_32, rd, Funct3::ROR, rs1, shamt, Funct7::COUNT_SEXT_ROT)); +} + +void RiscVEmitter::ORC_B(RiscVReg rd, RiscVReg rs) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP_IMM, rd, Funct3::BEXT, rs, Funct5::ORC_B, Funct7::BSET_ORC)); +} + +void RiscVEmitter::REV8(RiscVReg rd, RiscVReg rs) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + const u32 shamt = BitsSupported() - 8; + Write32(EncodeGIShift(Opcode32::OP_IMM, rd, Funct3::ROR, rs, shamt, Funct7::BINV_REV)); +} + +void RiscVEmitter::CLMUL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::CLMUL, rs1, rs2, Funct7::MINMAX_CLMUL)); +} + +void RiscVEmitter::CLMULH(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::CLMULH, rs1, rs2, Funct7::MINMAX_CLMUL)); +} + +void RiscVEmitter::CLMULR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::CLMULR, rs1, rs2, Funct7::MINMAX_CLMUL)); +} + +void RiscVEmitter::BCLR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::BSET, rs1, rs2, Funct7::BCLREXT)); +} + +void RiscVEmitter::BCLRI(RiscVReg rd, RiscVReg rs1, u32 shamt) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGIShift(Opcode32::OP_IMM, rd, Funct3::BSET, rs1, shamt, Funct7::BCLREXT)); +} + +void RiscVEmitter::BEXT(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::BEXT, rs1, rs2, Funct7::BCLREXT)); +} + +void RiscVEmitter::BEXTI(RiscVReg rd, RiscVReg rs1, u32 shamt) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGIShift(Opcode32::OP_IMM, rd, Funct3::BEXT, rs1, shamt, Funct7::BCLREXT)); +} + +void RiscVEmitter::BINV(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::BSET, rs1, rs2, Funct7::BINV_REV)); +} + +void RiscVEmitter::BINVI(RiscVReg rd, RiscVReg rs1, u32 shamt) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGIShift(Opcode32::OP_IMM, rd, Funct3::BSET, rs1, shamt, Funct7::BINV_REV)); +} + +void RiscVEmitter::BSET(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::BSET, rs1, rs2, Funct7::BSET_ORC)); +} + +void RiscVEmitter::BSETI(RiscVReg rd, RiscVReg rs1, u32 shamt) { + _assert_msg_(rd != R_ZERO, "%s should avoid write to zero", __func__); + _assert_msg_(SupportsBitmanip('b'), "%s instruction unsupported without B", __func__); + Write32(EncodeGIShift(Opcode32::OP_IMM, rd, Funct3::BSET, rs1, shamt, Funct7::BSET_ORC)); +} + bool RiscVEmitter::AutoCompress() const { return SupportsCompressed() && autoCompress_; } diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index 8e48f5bdbf..bcdd2e9107 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -867,6 +867,50 @@ public: void VCOMPRESS_VM(RiscVReg vd, RiscVReg vs2, RiscVReg vs1); void VMVR_V(int regs, RiscVReg vd, RiscVReg vs2); + // Bitmanip instructions. + void ADD_UW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SH_ADD(int shift, RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SH_ADD_UW(int shift, RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SLLI_UW(RiscVReg rd, RiscVReg rs1, u32 shamt); + void ANDN(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void ORN(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void XNOR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void CLZ(RiscVReg rd, RiscVReg rs); + void CLZW(RiscVReg rd, RiscVReg rs); + void CTZ(RiscVReg rd, RiscVReg rs); + void CTZW(RiscVReg rd, RiscVReg rs); + void CPOP(RiscVReg rd, RiscVReg rs); + void CPOPW(RiscVReg rd, RiscVReg rs); + void MAX(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void MAXU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void MIN(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void MINU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SEXT_B(RiscVReg rd, RiscVReg rs); + void SEXT_H(RiscVReg rd, RiscVReg rs); + void ZEXT_H(RiscVReg rd, RiscVReg rs); + void ZEXT_W(RiscVReg rd, RiscVReg rs) { + ADD_UW(rd, rs, R_ZERO); + } + void ROL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void ROLW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void ROR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void RORW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void RORI(RiscVReg rd, RiscVReg rs1, u32 shamt); + void RORIW(RiscVReg rd, RiscVReg rs1, u32 shamt); + void ORC_B(RiscVReg rd, RiscVReg rs); + void REV8(RiscVReg rd, RiscVReg rs); + void CLMUL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void CLMULH(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void CLMULR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void BCLR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void BCLRI(RiscVReg rd, RiscVReg rs1, u32 shamt); + void BEXT(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void BEXTI(RiscVReg rd, RiscVReg rs1, u32 shamt); + void BINV(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void BINVI(RiscVReg rd, RiscVReg rs1, u32 shamt); + void BSET(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void BSETI(RiscVReg rd, RiscVReg rs1, u32 shamt); + // Compressed instructions. void C_ADDI4SPN(RiscVReg rd, u32 nzuimm10); void C_FLD(RiscVReg rd, RiscVReg addr, u8 uimm8);