mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
riscv: Add bitmanip instructions to emitter (#16832)
* riscv: Cleanup emitter, add bitmanip detect. Better to encode using Funct7::ZERO, and obviously for SRA. * riscv: Add bitmanip instructions to emitter.
This commit is contained in:
committed by
GitHub
parent
31d106b0c5
commit
3bc2450b5e
@@ -104,6 +104,7 @@ struct CPUInfo {
|
||||
bool RiscV_D;
|
||||
bool RiscV_C;
|
||||
bool RiscV_V;
|
||||
bool RiscV_B;
|
||||
|
||||
// Quirks
|
||||
struct {
|
||||
|
||||
@@ -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
|
||||
|
||||
+378
-17
@@ -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_;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user