From c66d02d4db5bb65f343bcc5bad3acf6e1d0448e2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 24 Aug 2022 21:05:19 -0700 Subject: [PATCH] riscv: Emit mul/div instructions. --- Common/RiscVEmitter.cpp | 108 ++++++++++++++++++++++++++++++++++++++++ Common/RiscVEmitter.h | 16 ++++++ 2 files changed, 124 insertions(+) diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index 48eda83ee0..3e4d65605c 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -30,6 +30,11 @@ static inline uint8_t BitsSupported() { return 64; } +static inline bool SupportsMulDiv() { + // TODO + return true; +} + enum class Opcode32 { // Note: invalid, just used for FixupBranch. ZERO = 0b0000000, @@ -55,6 +60,7 @@ enum class Funct3 { PRIV = 0b000, FENCE = 0b000, + FENCE_I = 0b001, BEQ = 0b000, BNE = 0b001, @@ -79,6 +85,15 @@ enum class Funct3 { SRL = 0b101, OR = 0b110, AND = 0b111, + + MUL = 0b000, + MULH = 0b001, + MULHSU = 0b010, + MULHU = 0b011, + DIV = 0b100, + DIVU = 0b101, + REM = 0b110, + REMU = 0b111, }; enum class Funct2 { @@ -90,6 +105,8 @@ enum class Funct7 { SUB = 0b0100000, SRA = 0b0100000, + + MULDIV = 0b0000001, }; enum class Funct12 { @@ -635,4 +652,95 @@ void RiscVEmitter::SRAW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::SRL, rs1, rs2, Funct7::SRA)); } +void RiscVEmitter::MUL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __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__); + // 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__); + // 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__); + // 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__); + // 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__); + // 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__); + // 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__); + // 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__); + // 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)); +} + +void RiscVEmitter::DIVW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64 && SupportsMulDiv(), "%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::DIV, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::DIVUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64 && SupportsMulDiv(), "%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::DIVU, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::REMW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64 && SupportsMulDiv(), "%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::REM, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::REMUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64 && SupportsMulDiv(), "%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::REMU, rs1, rs2, Funct7::MULDIV)); +} + }; diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index 1b35097c12..8cf27ad2a1 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -188,6 +188,22 @@ public: SUBW(rd, R_ZERO, rs); } + // Integer multiplication and division. + void MUL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void MULH(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void MULHSU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void MULHU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void DIV(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void DIVU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void REM(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void REMU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + // 64-bit only multiply and divide. + void MULW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void DIVW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void DIVUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void REMW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void REMUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + private: void SetJumpTarget(const FixupBranch &branch, const void *dst); bool BInRange(const void *src, const void *dst) const;