assembler_util.hpp
1 #pragma once 2 3 #include <biscuit/assert.hpp> 4 #include <biscuit/code_buffer.hpp> 5 #include <biscuit/registers.hpp> 6 7 #include <cstddef> 8 #include <cstdint> 9 10 // Generic internal utility header for various helper functions related 11 // to encoding instructions. 12 13 namespace biscuit { 14 // Determines if a value lies within the range of a 6-bit immediate. 15 [[nodiscard]] constexpr bool IsValidSigned6BitImm(ptrdiff_t value) { 16 return value >= -32 && value <= 31; 17 } 18 19 // S-type and I-type immediates are 12 bits in size 20 [[nodiscard]] constexpr bool IsValidSigned12BitImm(ptrdiff_t value) { 21 return value >= -2048 && value <= 2047; 22 } 23 24 // B-type immediates only provide -4KiB to +4KiB range branches. 25 [[nodiscard]] constexpr bool IsValidBTypeImm(ptrdiff_t value) { 26 return value >= -4096 && value <= 4095; 27 } 28 29 // J-type immediates only provide -1MiB to +1MiB range branches. 30 [[nodiscard]] constexpr bool IsValidJTypeImm(ptrdiff_t value) { 31 return value >= -0x80000 && value <= 0x7FFFF; 32 } 33 34 // CB-type immediates only provide -256B to +256B range branches. 35 [[nodiscard]] constexpr bool IsValidCBTypeImm(ptrdiff_t value) { 36 return value >= -256 && value <= 255; 37 } 38 39 // CJ-type immediates only provide -2KiB to +2KiB range branches. 40 [[nodiscard]] constexpr bool IsValidCJTypeImm(ptrdiff_t value) { 41 return IsValidSigned12BitImm(value); 42 } 43 44 // Determines whether or not the register fits in 3-bit compressed encoding. 45 [[nodiscard]] constexpr bool IsValid3BitCompressedReg(Register reg) { 46 const auto index = reg.Index(); 47 return index >= 8 && index <= 15; 48 } 49 50 // Determines whether or not the given shift amount is valid for a compressed shift instruction 51 [[nodiscard]] constexpr bool IsValidCompressedShiftAmount(uint32_t shift) { 52 return shift > 0 && shift <= 64; 53 } 54 55 // Turns a compressed register into its encoding. 56 [[nodiscard]] constexpr uint32_t CompressedRegTo3BitEncoding(Register reg) { 57 return reg.Index() - 8; 58 } 59 60 // Transforms a regular value into an immediate encoded in a B-type instruction. 61 [[nodiscard]] constexpr uint32_t TransformToBTypeImm(uint32_t imm) { 62 // clang-format off 63 return ((imm & 0x07E0) << 20) | 64 ((imm & 0x1000) << 19) | 65 ((imm & 0x001E) << 7) | 66 ((imm & 0x0800) >> 4); 67 // clang-format on 68 } 69 70 // Transforms a regular value into an immediate encoded in a J-type instruction. 71 [[nodiscard]] constexpr uint32_t TransformToJTypeImm(uint32_t imm) { 72 // clang-format off 73 return ((imm & 0x0FF000) >> 0) | 74 ((imm & 0x000800) << 9) | 75 ((imm & 0x0007FE) << 20) | 76 ((imm & 0x100000) << 11); 77 // clang-format on 78 } 79 80 // Transforms a regular value into an immediate encoded in a CB-type instruction. 81 [[nodiscard]] constexpr uint32_t TransformToCBTypeImm(uint32_t imm) { 82 // clang-format off 83 return ((imm & 0x0C0) >> 1) | 84 ((imm & 0x006) << 2) | 85 ((imm & 0x020) >> 3) | 86 ((imm & 0x018) << 7) | 87 ((imm & 0x100) << 4); 88 // clang-format on 89 } 90 91 // Transforms a regular value into an immediate encoded in a CJ-type instruction. 92 [[nodiscard]] constexpr uint32_t TransformToCJTypeImm(uint32_t imm) { 93 // clang-format off 94 return ((imm & 0x800) << 1) | 95 ((imm & 0x010) << 7) | 96 ((imm & 0x300) << 1) | 97 ((imm & 0x400) >> 2) | 98 ((imm & 0x040) << 1) | 99 ((imm & 0x080) >> 1) | 100 ((imm & 0x00E) << 4) | 101 ((imm & 0x020) >> 3); 102 // clang-format on 103 } 104 105 // Emits a B type RISC-V instruction. These consist of: 106 // imm[12|10:5] | rs2 | rs1 | funct3 | imm[4:1] | imm[11] | opcode 107 inline void EmitBType(CodeBuffer& buffer, uint32_t imm, GPR rs2, GPR rs1, 108 uint32_t funct3, uint32_t opcode) { 109 imm &= 0x1FFE; 110 111 buffer.Emit32(TransformToBTypeImm(imm) | (rs2.Index() << 20) | (rs1.Index() << 15) | 112 ((funct3 & 0b111) << 12) | (opcode & 0x7F)); 113 } 114 115 // Emits a I type RISC-V instruction. These consist of: 116 // imm[11:0] | rs1 | funct3 | rd | opcode 117 inline void EmitIType(CodeBuffer& buffer, uint32_t imm, Register rs1, uint32_t funct3, 118 Register rd, uint32_t opcode) { 119 imm &= 0xFFF; 120 121 buffer.Emit32((imm << 20) | (rs1.Index() << 15) | ((funct3 & 0b111) << 12) | 122 (rd.Index() << 7) | (opcode & 0x7F)); 123 } 124 125 // Emits a J type RISC-V instruction. These consist of: 126 // imm[20|10:1|11|19:12] | rd | opcode 127 inline void EmitJType(CodeBuffer& buffer, uint32_t imm, GPR rd, uint32_t opcode) { 128 imm &= 0x1FFFFE; 129 130 buffer.Emit32(TransformToJTypeImm(imm) | rd.Index() << 7 | (opcode & 0x7F)); 131 } 132 133 // Emits a R type RISC instruction. These consist of: 134 // funct7 | rs2 | rs1 | funct3 | rd | opcode 135 inline void EmitRType(CodeBuffer& buffer, uint32_t funct7, Register rs2, Register rs1, 136 uint32_t funct3, Register rd, uint32_t opcode) { 137 // clang-format off 138 const auto value = ((funct7 & 0xFF) << 25) | 139 (rs2.Index() << 20) | 140 (rs1.Index() << 15) | 141 ((funct3 & 0b111) << 12) | 142 (rd.Index() << 7) | 143 (opcode & 0x7F); 144 // clang-format off 145 146 buffer.Emit32(value); 147 } 148 149 // Emits a R type RISC instruction. These consist of: 150 // funct7 | rs2 | rs1 | funct3 | rd | opcode 151 inline void EmitRType(CodeBuffer& buffer, uint32_t funct7, FPR rs2, FPR rs1, RMode funct3, 152 FPR rd, uint32_t opcode) { 153 EmitRType(buffer, funct7, rs2, rs1, static_cast<uint32_t>(funct3), rd, opcode); 154 } 155 156 // Emits a R4 type RISC instruction. These consist of: 157 // rs3 | funct2 | rs2 | rs1 | funct3 | rd | opcode 158 inline void EmitR4Type(CodeBuffer& buffer, FPR rs3, uint32_t funct2, FPR rs2, FPR rs1, 159 RMode funct3, FPR rd, uint32_t opcode) { 160 const auto reg_bits = (rs3.Index() << 27) | (rs2.Index() << 20) | (rs1.Index() << 15) | (rd.Index() << 7); 161 const auto funct_bits = ((funct2 & 0b11) << 25) | (static_cast<uint32_t>(funct3) << 12); 162 buffer.Emit32(reg_bits | funct_bits | (opcode & 0x7F)); 163 } 164 165 // Emits a S type RISC-V instruction. These consist of: 166 // imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode 167 inline void EmitSType(CodeBuffer& buffer, uint32_t imm, Register rs2, GPR rs1, 168 uint32_t funct3, uint32_t opcode) { 169 imm &= 0xFFF; 170 171 // clang-format off 172 const auto new_imm = ((imm & 0x01F) << 7) | 173 ((imm & 0xFE0) << 20); 174 // clang-format on 175 176 buffer.Emit32(new_imm | (rs2.Index() << 20) | (rs1.Index() << 15) | 177 ((funct3 & 0b111) << 12) | (opcode & 0x7F)); 178 } 179 180 // Emits a U type RISC-V instruction. These consist of: 181 // imm[31:12] | rd | opcode 182 inline void EmitUType(CodeBuffer& buffer, uint32_t imm, GPR rd, uint32_t opcode) { 183 buffer.Emit32((imm & 0x000FFFFF) << 12 | rd.Index() << 7 | (opcode & 0x7F)); 184 } 185 186 // Emits an atomic instruction. 187 inline void EmitAtomic(CodeBuffer& buffer, uint32_t funct5, Ordering ordering, GPR rs2, GPR rs1, 188 uint32_t funct3, GPR rd, uint32_t opcode) noexcept { 189 const auto funct7 = (funct5 << 2) | static_cast<uint32_t>(ordering); 190 EmitRType(buffer, funct7, rs2, rs1, funct3, rd, opcode); 191 } 192 193 // Emits a fence instruction 194 inline void EmitFENCE(CodeBuffer& buffer, uint32_t fm, FenceOrder pred, FenceOrder succ, 195 GPR rs, uint32_t funct3, GPR rd, uint32_t opcode) noexcept { 196 // clang-format off 197 buffer.Emit32(((fm & 0b1111) << 28) | 198 (static_cast<uint32_t>(pred) << 24) | 199 (static_cast<uint32_t>(succ) << 20) | 200 (rs.Index() << 15) | 201 ((funct3 & 0b111) << 12) | 202 (rd.Index() << 7) | 203 (opcode & 0x7F)); 204 // clang-format on 205 } 206 207 // Internal helpers for siloing away particular comparisons for behavior. 208 constexpr bool IsRV32(ArchFeature feature) { 209 return feature == ArchFeature::RV32; 210 } 211 constexpr bool IsRV64(ArchFeature feature) { 212 return feature == ArchFeature::RV64; 213 } 214 constexpr bool IsRV128(ArchFeature feature) { 215 return feature == ArchFeature::RV128; 216 } 217 constexpr bool IsRV32OrRV64(ArchFeature feature) { 218 return IsRV32(feature) || IsRV64(feature); 219 } 220 constexpr bool IsRV64OrRV128(ArchFeature feature) { 221 return IsRV64(feature) || IsRV128(feature); 222 } 223 224 } // namespace biscuit