+static inline void assertCodeLength(size_t code, uint8_t size)
+{
+ assert((code >> size) == 0);
+}
+
+/*****************************************************************************
+ *
+ * Emit a 32-bit RISCV64 R-Type instruction
+ *
+ * Note: Instruction types as per RISC-V Spec, Chapter 24 RV32/64G Instruction Set Listings
+ * R-Type layout:
+ * 31-------25-24---20-19--15-14------12-11-----------7-6------------0
+ * | funct7 | rs2 | rs1 | funct3 | rd | opcode |
+ * -------------------------------------------------------------------
+ */
+
+/*static*/ emitter::code_t emitter::insEncodeRTypeInstr(
+ unsigned opcode, unsigned rd, unsigned funct3, unsigned rs1, unsigned rs2, unsigned funct7)
+{
+ assertCodeLength(opcode, 7);
+ assertCodeLength(rd, 5);
+ assertCodeLength(funct3, 3);
+ assertCodeLength(rs1, 5);
+ assertCodeLength(rs2, 5);
+ assertCodeLength(funct7, 7);
+
+ return opcode | (rd << 7) | (funct3 << 12) | (rs1 << 15) | (rs2 << 20) | (funct7 << 25);
+}
+
+/*****************************************************************************
+ *
+ * Emit a 32-bit RISCV64 I-Type instruction
+ *
+ * Note: Instruction types as per RISC-V Spec, Chapter 24 RV32/64G Instruction Set Listings
+ * I-Type layout:
+ * 31------------20-19-----15-14------12-11-----------7-6------------0
+ * | imm[11:0] | rs1 | funct3 | rd | opcode |
+ * -------------------------------------------------------------------
+ */
+
+/*static*/ emitter::code_t emitter::insEncodeITypeInstr(
+ unsigned opcode, unsigned rd, unsigned funct3, unsigned rs1, unsigned imm12)
+{
+ assertCodeLength(opcode, 7);
+ assertCodeLength(rd, 5);
+ assertCodeLength(funct3, 3);
+ assertCodeLength(rs1, 5);
+ // This assert may be triggered by the untrimmed signed integers. Please refer to the TrimSigned helpers
+ assertCodeLength(imm12, 12);
+
+ return opcode | (rd << 7) | (funct3 << 12) | (rs1 << 15) | (imm12 << 20);
+}
+
+/*****************************************************************************
+ *
+ * Emit a 32-bit RISCV64 S-Type instruction
+ *
+ * Note: Instruction types as per RISC-V Spec, Chapter 24 RV32/64G Instruction Set Listings
+ * S-Type layout:
+ * 31-------25-24---20-19--15-14------12-11-----------7-6------------0
+ * |imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode |
+ * -------------------------------------------------------------------
+ */
+
+/*static*/ emitter::code_t emitter::insEncodeSTypeInstr(
+ unsigned opcode, unsigned funct3, unsigned rs1, unsigned rs2, unsigned imm12)
+{
+ static constexpr unsigned kLoMask = 0x1f; // 0b00011111
+ static constexpr unsigned kHiMask = 0x7f; // 0b01111111
+
+ assertCodeLength(opcode, 7);
+ assertCodeLength(funct3, 3);
+ assertCodeLength(rs1, 5);
+ assertCodeLength(rs2, 5);
+ // This assert may be triggered by the untrimmed signed integers. Please refer to the TrimSigned helpers
+ assertCodeLength(imm12, 12);
+
+ unsigned imm12Lo = imm12 & kLoMask;
+ unsigned imm12Hi = (imm12 >> 5) & kHiMask;
+
+ return opcode | (imm12Lo << 7) | (funct3 << 12) | (rs1 << 15) | (rs2 << 20) | (imm12Hi << 25);
+}
+
+/*****************************************************************************
+ *
+ * Emit a 32-bit RISCV64 U-Type instruction
+ *
+ * Note: Instruction types as per RISC-V Spec, Chapter 24 RV32/64G Instruction Set Listings
+ * U-Type layout:
+ * 31---------------------------------12-11-----------7-6------------0
+ * | imm[31:12] | rd | opcode |
+ * -------------------------------------------------------------------
+ */
+
+/*static*/ emitter::code_t emitter::insEncodeUTypeInstr(unsigned opcode, unsigned rd, unsigned imm20)
+{
+ assertCodeLength(opcode, 7);
+ assertCodeLength(rd, 5);
+ // This assert may be triggered by the untrimmed signed integers. Please refer to the TrimSigned helpers
+ assertCodeLength(imm20, 20);
+
+ return opcode | (rd << 7) | (imm20 << 12);
+}
+
+/*****************************************************************************
+ *
+ * Emit a 32-bit RISCV64 B-Type instruction
+ *
+ * Note: Instruction types as per RISC-V Spec, Chapter 24 RV32/64G Instruction Set Listings
+ * B-Type layout:
+ * 31-------30-----25-24-20-19-15-14--12-11-------8----7----6--------0
+ * |imm[12]|imm[10:5]| rs2 | rs1 |funct3| imm[4:1]|imm[11]| opcode |
+ * -------------------------------------------------------------------
+ */
+
+/*static*/ emitter::code_t emitter::insEncodeBTypeInstr(
+ unsigned opcode, unsigned funct3, unsigned rs1, unsigned rs2, unsigned imm13)
+{
+ static constexpr unsigned kLoSectionMask = 0x0f; // 0b00001111
+ static constexpr unsigned kHiSectionMask = 0x3f; // 0b00111111
+ static constexpr unsigned kBitMask = 0x01;
+
+ assertCodeLength(opcode, 7);
+ assertCodeLength(funct3, 3);
+ assertCodeLength(rs1, 5);
+ assertCodeLength(rs2, 5);
+ // This assert may be triggered by the untrimmed signed integers. Please refer to the TrimSigned helpers
+ assertCodeLength(imm13, 13);
+ assert((imm13 & 0x01) == 0);
+
+ unsigned imm12 = imm13 >> 1;
+ unsigned imm12LoSection = imm12 & kLoSectionMask;
+ unsigned imm12LoBit = (imm12 >> 10) & kBitMask;
+ unsigned imm12HiSection = (imm12 >> 4) & kHiSectionMask;
+ unsigned imm12HiBit = (imm12 >> 11) & kBitMask;
+
+ return opcode | (imm12LoBit << 7) | (imm12LoSection << 8) | (funct3 << 12) | (rs1 << 15) | (rs2 << 20) |
+ (imm12HiSection << 25) | (imm12HiBit << 31);
+}
+
+/*****************************************************************************
+ *
+ * Emit a 32-bit RISCV64 J-Type instruction
+ *
+ * Note: Instruction types as per RISC-V Spec, Chapter 24 RV32/64G Instruction Set Listings
+ * J-Type layout:
+ * 31-------30--------21----20---19----------12-11----7-6------------0
+ * |imm[20]| imm[10:1] |imm[11]| imm[19:12] | rd | opcode |
+ * -------------------------------------------------------------------
+ */
+
+/*static*/ emitter::code_t emitter::insEncodeJTypeInstr(unsigned opcode, unsigned rd, unsigned imm21)
+{
+ static constexpr unsigned kHiSectionMask = 0x3ff; // 0b1111111111
+ static constexpr unsigned kLoSectionMask = 0xff; // 0b11111111
+ static constexpr unsigned kBitMask = 0x01;
+
+ assertCodeLength(opcode, 7);
+ assertCodeLength(rd, 5);
+ // This assert may be triggered by the untrimmed signed integers. Please refer to the TrimSigned helpers
+ assertCodeLength(imm21, 21);
+ assert((imm21 & 0x01) == 0);
+
+ unsigned imm20 = imm21 >> 1;
+ unsigned imm20HiSection = imm20 & kHiSectionMask;
+ unsigned imm20HiBit = (imm20 >> 19) & kBitMask;
+ unsigned imm20LoSection = (imm20 >> 11) & kLoSectionMask;
+ unsigned imm20LoBit = (imm20 >> 10) & kBitMask;
+
+ return opcode | (rd << 7) | (imm20LoSection << 12) | (imm20LoBit << 20) | (imm20HiSection << 21) |
+ (imm20HiBit << 31);
+}
+
+static constexpr unsigned kInstructionOpcodeMask = 0x7f;
+static constexpr unsigned kInstructionFunct3Mask = 0x7000;
+static constexpr unsigned kInstructionFunct5Mask = 0xf8000000;
+static constexpr unsigned kInstructionFunct7Mask = 0xfe000000;
+static constexpr unsigned kInstructionFunct2Mask = 0x06000000;
+
+#ifdef DEBUG
+
+/*static*/ void emitter::emitOutput_RTypeInstr_SanityCheck(instruction ins, regNumber rd, regNumber rs1, regNumber rs2)
+{
+ switch (ins)
+ {
+ case INS_add:
+ case INS_sub:
+ case INS_sll:
+ case INS_slt:
+ case INS_sltu:
+ case INS_xor:
+ case INS_srl:
+ case INS_sra:
+ case INS_or:
+ case INS_and:
+ case INS_addw:
+ case INS_subw:
+ case INS_sllw:
+ case INS_srlw:
+ case INS_sraw:
+ case INS_mul:
+ case INS_mulh:
+ case INS_mulhsu:
+ case INS_mulhu:
+ case INS_div:
+ case INS_divu:
+ case INS_rem:
+ case INS_remu:
+ case INS_mulw:
+ case INS_divw:
+ case INS_divuw:
+ case INS_remw:
+ case INS_remuw:
+ assert(isGeneralRegisterOrR0(rd));
+ assert(isGeneralRegisterOrR0(rs1));
+ assert(isGeneralRegisterOrR0(rs2));
+ break;
+ case INS_fsgnj_s:
+ case INS_fsgnjn_s:
+ case INS_fsgnjx_s:
+ case INS_fmin_s:
+ case INS_fmax_s:
+ case INS_fsgnj_d:
+ case INS_fsgnjn_d:
+ case INS_fsgnjx_d:
+ case INS_fmin_d:
+ case INS_fmax_d:
+ assert(isFloatReg(rd));
+ assert(isFloatReg(rs1));
+ assert(isFloatReg(rs2));
+ break;
+ case INS_feq_s:
+ case INS_feq_d:
+ case INS_flt_d:
+ case INS_flt_s:
+ case INS_fle_s:
+ case INS_fle_d:
+ assert(isGeneralRegisterOrR0(rd));
+ assert(isFloatReg(rs1));
+ assert(isFloatReg(rs2));
+ break;
+ case INS_fmv_w_x:
+ case INS_fmv_d_x:
+ assert(isFloatReg(rd));
+ assert(isGeneralRegisterOrR0(rs1));
+ assert(rs2 == 0);
+ break;
+ case INS_fmv_x_d:
+ case INS_fmv_x_w:
+ case INS_fclass_s:
+ case INS_fclass_d:
+ assert(isGeneralRegisterOrR0(rd));
+ assert(isFloatReg(rs1));
+ assert(rs2 == 0);
+ break;
+ default:
+ NO_WAY("Illegal ins within emitOutput_RTypeInstr!");
+ break;
+ }
+}
+
+/*static*/ void emitter::emitOutput_ITypeInstr_SanityCheck(
+ instruction ins, regNumber rd, regNumber rs1, unsigned immediate, unsigned opcode)
+{
+ switch (ins)
+ {
+ case INS_mov:
+ case INS_jalr:
+ case INS_lb:
+ case INS_lh:
+ case INS_lw:
+ case INS_lbu:
+ case INS_lhu:
+ case INS_addi:
+ case INS_slti:
+ case INS_sltiu:
+ case INS_xori:
+ case INS_ori:
+ case INS_andi:
+ case INS_lwu:
+ case INS_ld:
+ case INS_addiw:
+ case INS_csrrw:
+ case INS_csrrs:
+ case INS_csrrc:
+ assert(isGeneralRegisterOrR0(rd));
+ assert(isGeneralRegisterOrR0(rs1));
+ assert((opcode & kInstructionFunct7Mask) == 0);
+ break;
+ case INS_flw:
+ case INS_fld:
+ assert(isFloatReg(rd));
+ assert(isGeneralRegisterOrR0(rs1));
+ assert((opcode & kInstructionFunct7Mask) == 0);
+ break;
+ case INS_slli:
+ case INS_srli:
+ case INS_srai:
+ assert(immediate < 64);
+ assert(isGeneralRegisterOrR0(rd));
+ assert(isGeneralRegisterOrR0(rs1));
+ break;
+ case INS_slliw:
+ case INS_srliw:
+ case INS_sraiw:
+ assert(immediate < 32);
+ assert(isGeneralRegisterOrR0(rd));
+ assert(isGeneralRegisterOrR0(rs1));
+ break;
+ case INS_csrrwi:
+ case INS_csrrsi:
+ case INS_csrrci:
+ assert(isGeneralRegisterOrR0(rd));
+ assert(rs1 < 32);
+ assert((opcode & kInstructionFunct7Mask) == 0);
+ break;
+ case INS_fence:
+ {
+ assert(rd == REG_ZERO);
+ assert(rs1 == REG_ZERO);
+ ssize_t format = immediate >> 8;
+ assert((format == 0) || (format == 0x8));
+ assert((opcode & kInstructionFunct7Mask) == 0);
+ }
+ break;
+ default:
+ NO_WAY("Illegal ins within emitOutput_ITypeInstr!");
+ break;
+ }
+}
+
+/*static*/ void emitter::emitOutput_STypeInstr_SanityCheck(instruction ins, regNumber rs1, regNumber rs2)
+{
+ switch (ins)
+ {
+ case INS_sb:
+ case INS_sh:
+ case INS_sw:
+ case INS_sd:
+ assert(isGeneralRegister(rs1));
+ assert(isGeneralRegisterOrR0(rs2));
+ break;
+ case INS_fsw:
+ case INS_fsd:
+ assert(isGeneralRegister(rs1));
+ assert(isFloatReg(rs2));
+ break;
+ default:
+ NO_WAY("Illegal ins within emitOutput_STypeInstr!");
+ break;
+ }
+}
+
+/*static*/ void emitter::emitOutput_UTypeInstr_SanityCheck(instruction ins, regNumber rd)
+{
+ switch (ins)
+ {
+ case INS_lui:
+ case INS_auipc:
+ assert(isGeneralRegisterOrR0(rd));
+ break;
+ default:
+ NO_WAY("Illegal ins within emitOutput_UTypeInstr!");
+ break;
+ }
+}
+
+/*static*/ void emitter::emitOutput_BTypeInstr_SanityCheck(instruction ins, regNumber rs1, regNumber rs2)
+{
+ switch (ins)
+ {
+ case INS_beqz:
+ case INS_bnez:
+ assert((rs1 == REG_ZERO) || (rs2 == REG_ZERO));
+ FALLTHROUGH;
+ case INS_beq:
+ case INS_bne:
+ case INS_blt:
+ case INS_bge:
+ case INS_bltu:
+ case INS_bgeu:
+ assert(isGeneralRegisterOrR0(rs1));
+ assert(isGeneralRegisterOrR0(rs2));
+ break;
+ default:
+ NO_WAY("Illegal ins within emitOutput_BTypeInstr!");
+ break;
+ }
+}
+
+/*static*/ void emitter::emitOutput_JTypeInstr_SanityCheck(instruction ins, regNumber rd)
+{
+ switch (ins)
+ {
+ case INS_j:
+ assert(rd == REG_ZERO);
+ break;
+ case INS_jal:
+ assert(isGeneralRegisterOrR0(rd));
+ break;
+ default:
+ NO_WAY("Illegal ins within emitOutput_JTypeInstr!");
+ break;
+ }
+}
+
+#endif // DEBUG
+
+/*****************************************************************************
+ *
+ * Casts an integral or float register from their identification number to
+ * theirs binary format. In case of the integral registers the encoded number
+ * is the register id. In case of the floating point registers the encoded
+ * number is shifted back by the floating point register base (32) (The
+ * instruction itself specifies whether the register contains floating
+ * point or integer, in their encoding they are indistinguishable)
+ *
+ */
+
+/*static*/ unsigned emitter::castFloatOrIntegralReg(regNumber reg)
+{
+ static constexpr unsigned kRegisterMask = 0x1f;
+
+ assert(isGeneralRegisterOrR0(reg) || isFloatReg(reg));
+
+ return reg & kRegisterMask;
+}
+
+/*****************************************************************************
+ *
+ * Emit a 32-bit RISCV64 R-Type instruction to the given buffer. Returns a
+ * length of an encoded instruction opcode
+ *
+ */
+
+unsigned emitter::emitOutput_RTypeInstr(BYTE* dst, instruction ins, regNumber rd, regNumber rs1, regNumber rs2) const
+{
+ unsigned insCode = emitInsCode(ins);
+#ifdef DEBUG
+ emitOutput_RTypeInstr_SanityCheck(ins, rd, rs1, rs2);
+#endif // DEBUG
+ unsigned opcode = insCode & kInstructionOpcodeMask;
+ unsigned funct3 = (insCode & kInstructionFunct3Mask) >> 12;
+ unsigned funct7 = (insCode & kInstructionFunct7Mask) >> 25;
+ return emitOutput_Instr(dst, insEncodeRTypeInstr(opcode, castFloatOrIntegralReg(rd), funct3,
+ castFloatOrIntegralReg(rs1), castFloatOrIntegralReg(rs2), funct7));
+}
+
+/*****************************************************************************
+ *
+ * Emit a 32-bit RISCV64 I-Type instruction to the given buffer. Returns a
+ * length of an encoded instruction opcode
+ *
+ */
+
+unsigned emitter::emitOutput_ITypeInstr(BYTE* dst, instruction ins, regNumber rd, regNumber rs1, unsigned imm12) const
+{
+ unsigned insCode = emitInsCode(ins);
+#ifdef DEBUG
+ emitOutput_ITypeInstr_SanityCheck(ins, rd, rs1, imm12, insCode);
+#endif // DEBUG
+ unsigned opcode = insCode & kInstructionOpcodeMask;
+ unsigned funct3 = (insCode & kInstructionFunct3Mask) >> 12;
+ unsigned funct7 = (insCode & kInstructionFunct7Mask) >> 20; // only used by some of the immediate shifts
+ return emitOutput_Instr(dst, insEncodeITypeInstr(opcode, castFloatOrIntegralReg(rd), funct3, rs1, imm12 | funct7));
+}
+
+/*****************************************************************************
+ *
+ * Emit a 32-bit RISCV64 S-Type instruction to the given buffer. Returns a
+ * length of an encoded instruction opcode
+ *
+ */
+
+unsigned emitter::emitOutput_STypeInstr(BYTE* dst, instruction ins, regNumber rs1, regNumber rs2, unsigned imm12) const
+{
+ unsigned insCode = emitInsCode(ins);
+#ifdef DEBUG
+ emitOutput_STypeInstr_SanityCheck(ins, rs1, rs2);
+#endif // DEBUG
+ unsigned opcode = insCode & kInstructionOpcodeMask;
+ unsigned funct3 = (insCode & kInstructionFunct3Mask) >> 12;
+ return emitOutput_Instr(dst, insEncodeSTypeInstr(opcode, funct3, rs1, castFloatOrIntegralReg(rs2), imm12));
+}
+
+/*****************************************************************************
+ *
+ * Emit a 32-bit RISCV64 U-Type instruction to the given buffer. Returns a
+ * length of an encoded instruction opcode
+ *
+ */
+
+unsigned emitter::emitOutput_UTypeInstr(BYTE* dst, instruction ins, regNumber rd, unsigned imm20) const
+{
+ unsigned insCode = emitInsCode(ins);
+#ifdef DEBUG
+ emitOutput_UTypeInstr_SanityCheck(ins, rd);
+#endif // DEBUG
+ return emitOutput_Instr(dst, insEncodeUTypeInstr(insCode, rd, imm20));
+}
+
+/*****************************************************************************
+ *
+ * Emit a 32-bit RISCV64 B-Type instruction to the given buffer. Returns a
+ * length of an encoded instruction opcode
+ *
+ */
+
+unsigned emitter::emitOutput_BTypeInstr(BYTE* dst, instruction ins, regNumber rs1, regNumber rs2, unsigned imm13) const
+{
+ unsigned insCode = emitInsCode(ins);
+#ifdef DEBUG
+ emitOutput_BTypeInstr_SanityCheck(ins, rs1, rs2);
+#endif // DEBUG
+ unsigned opcode = insCode & kInstructionOpcodeMask;
+ unsigned funct3 = (insCode & kInstructionFunct3Mask) >> 12;
+ return emitOutput_Instr(dst, insEncodeBTypeInstr(opcode, funct3, rs1, rs2, imm13));
+}
+
+/*****************************************************************************
+ *
+ * Emit a 32-bit RISCV64 B-Type instruction with inverted comparation to
+ * the given buffer. Returns a length of an encoded instruction opcode
+ *
+ * Note: Replaces:
+ * - beqz with bnez and vice versa
+ * - beq with bne and vice versa
+ * - blt with bge and vice versa
+ * - bltu with bgeu and vice versa
+ */
+
+unsigned emitter::emitOutput_BTypeInstr_InvertComparation(
+ BYTE* dst, instruction ins, regNumber rs1, regNumber rs2, unsigned imm13) const
+{
+ unsigned insCode = emitInsCode(ins) ^ 0x1000;
+#ifdef DEBUG
+ emitOutput_BTypeInstr_SanityCheck(ins, rs1, rs2);
+#endif // DEBUG
+ unsigned opcode = insCode & kInstructionOpcodeMask;
+ unsigned funct3 = (insCode & kInstructionFunct3Mask) >> 12;
+ return emitOutput_Instr(dst, insEncodeBTypeInstr(opcode, funct3, rs1, rs2, imm13));
+}
+
+/*****************************************************************************
+ *
+ * Emit a 32-bit RISCV64 J-Type instruction to the given buffer. Returns a
+ * length of an encoded instruction opcode
+ *
+ */
+
+unsigned emitter::emitOutput_JTypeInstr(BYTE* dst, instruction ins, regNumber rd, unsigned imm21) const
+{
+ unsigned insCode = emitInsCode(ins);
+#ifdef DEBUG
+ emitOutput_JTypeInstr_SanityCheck(ins, rd);
+#endif // JTypeInstructionSanityCheck
+ return emitOutput_Instr(dst, insEncodeJTypeInstr(insCode, rd, imm21));
+}
+