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:
Unknown W. Brackets
2023-01-22 12:37:47 -08:00
committed by GitHub
parent 31d106b0c5
commit 3bc2450b5e
4 changed files with 424 additions and 17 deletions
+1
View File
@@ -104,6 +104,7 @@ struct CPUInfo {
bool RiscV_D;
bool RiscV_C;
bool RiscV_V;
bool RiscV_B;
// Quirks
struct {
+1
View File
@@ -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
View File
@@ -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_;
}
+44
View File
@@ -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);