assembler_compressed.cpp
1 #include <biscuit/assert.hpp> 2 #include <biscuit/assembler.hpp> 3 4 #include <array> 5 #include <cmath> 6 7 #include "assembler_util.hpp" 8 9 // RVC Extension Instructions 10 11 namespace biscuit { 12 namespace { 13 // Emits a compressed branch instruction. These consist of: 14 // funct3 | imm[8|4:3] | rs | imm[7:6|2:1|5] | op 15 void EmitCompressedBranch(CodeBuffer& buffer, uint32_t funct3, int32_t offset, GPR rs, uint32_t op) { 16 BISCUIT_ASSERT(IsValidCBTypeImm(offset)); 17 BISCUIT_ASSERT(IsValid3BitCompressedReg(rs)); 18 19 const auto transformed_imm = TransformToCBTypeImm(static_cast<uint32_t>(offset)); 20 const auto rs_san = CompressedRegTo3BitEncoding(rs); 21 buffer.Emit16(((funct3 & 0b111) << 13) | transformed_imm | (rs_san << 7) | (op & 0b11)); 22 } 23 24 // Emits a compressed jump instruction. These consist of: 25 // funct3 | imm | op 26 void EmitCompressedJump(CodeBuffer& buffer, uint32_t funct3, int32_t offset, uint32_t op) { 27 BISCUIT_ASSERT(IsValidCJTypeImm(offset)); 28 BISCUIT_ASSERT((offset % 2) == 0); 29 30 buffer.Emit16(TransformToCJTypeImm(static_cast<uint32_t>(offset)) | 31 ((funct3 & 0b111) << 13) | (op & 0b11)); 32 } 33 34 // Emits a compress immediate instruction. These consist of: 35 // funct3 | imm | rd | imm | op 36 void EmitCompressedImmediate(CodeBuffer& buffer, uint32_t funct3, uint32_t imm, GPR rd, uint32_t op) { 37 BISCUIT_ASSERT(rd != x0); 38 39 const auto new_imm = ((imm & 0b11111) << 2) | ((imm & 0b100000) << 7); 40 buffer.Emit16(((funct3 & 0b111) << 13) | new_imm | (rd.Index() << 7) | (op & 0b11)); 41 } 42 43 // Emits a compressed load instruction. These consist of: 44 // funct3 | imm | rs1 | imm | rd | op 45 void EmitCompressedLoad(CodeBuffer& buffer, uint32_t funct3, uint32_t imm, GPR rs, 46 Register rd, uint32_t op) { 47 BISCUIT_ASSERT(IsValid3BitCompressedReg(rs)); 48 BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); 49 50 imm &= 0xF8; 51 52 const auto imm_enc = ((imm & 0x38) << 7) | ((imm & 0xC0) >> 1); 53 const auto rd_san = CompressedRegTo3BitEncoding(rd); 54 const auto rs_san = CompressedRegTo3BitEncoding(rs); 55 buffer.Emit16(((funct3 & 0b111) << 13) | imm_enc | (rs_san << 7) | (rd_san << 2) | (op & 0b11)); 56 } 57 58 // Emits a compressed register arithmetic instruction. These consist of: 59 // funct6 | rd | funct2 | rs | op 60 void EmitCompressedRegArith(CodeBuffer& buffer, uint32_t funct6, GPR rd, uint32_t funct2, 61 GPR rs, uint32_t op) { 62 BISCUIT_ASSERT(IsValid3BitCompressedReg(rs)); 63 BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); 64 65 const auto rd_san = CompressedRegTo3BitEncoding(rd); 66 const auto rs_san = CompressedRegTo3BitEncoding(rs); 67 buffer.Emit16(((funct6 & 0b111111) << 10) | (rd_san << 7) | ((funct2 & 0b11) << 5) | 68 (rs_san << 2) | (op & 0b11)); 69 } 70 71 // Emits a compressed store instruction. These consist of: 72 // funct3 | imm | rs1 | imm | rs2 | op 73 void EmitCompressedStore(CodeBuffer& buffer, uint32_t funct3, uint32_t imm, GPR rs1, 74 Register rs2, uint32_t op) { 75 // This has the same format as a compressed load, with rs2 taking the place of rd. 76 // We can reuse the code we've already written to handle this. 77 EmitCompressedLoad(buffer, funct3, imm, rs1, rs2, op); 78 } 79 80 // Emits a compressed wide immediate instruction. These consist of: 81 // funct3 | imm | rd | opcode 82 void EmitCompressedWideImmediate(CodeBuffer& buffer, uint32_t funct3, uint32_t imm, 83 GPR rd, uint32_t op) { 84 BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); 85 86 const auto rd_sanitized = CompressedRegTo3BitEncoding(rd); 87 buffer.Emit16(((funct3 & 0b111) << 13) | ((imm & 0xFF) << 5) | 88 (rd_sanitized << 2) | (op & 0b11)); 89 } 90 91 void EmitCLBType(CodeBuffer& buffer, uint32_t funct6, GPR rs, uint32_t uimm, GPR rd, 92 uint32_t op, uint32_t b6) { 93 BISCUIT_ASSERT(IsValid3BitCompressedReg(rs)); 94 BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); 95 BISCUIT_ASSERT(uimm <= 3); 96 97 const auto rd_san = CompressedRegTo3BitEncoding(rd); 98 const auto rs_san = CompressedRegTo3BitEncoding(rs); 99 100 buffer.Emit16((funct6 << 10) | (rs_san << 7) | (b6 << 6) | (uimm << 5) | (rd_san << 2) | op); 101 } 102 103 void EmitCLHType(CodeBuffer& buffer, uint32_t funct6, GPR rs, uint32_t uimm, GPR rd, 104 uint32_t op, uint32_t b6) { 105 BISCUIT_ASSERT((uimm % 2) == 0); 106 BISCUIT_ASSERT(uimm <= 2); 107 108 // Only have 1 bit of encoding space for the immediate. 109 const uint32_t uimm_fixed = uimm >> 1; 110 EmitCLBType(buffer, funct6, rs, uimm_fixed, rd, op, b6); 111 } 112 113 // These have the same layout as the equivalent loads, we just essentially alias 114 // the name of those to provide better intent at the call site. 115 void EmitCSBType(CodeBuffer& buffer, uint32_t funct6, GPR rs, uint32_t uimm, GPR rd, uint32_t op) { 116 EmitCLBType(buffer, funct6, rs, uimm, rd, op, 0); 117 } 118 void EmitCSHType(CodeBuffer& buffer, uint32_t funct6, GPR rs, uint32_t uimm, GPR rd, uint32_t op) { 119 EmitCLHType(buffer, funct6, rs, uimm, rd, op, 0); 120 } 121 122 void EmitCUType(CodeBuffer& buffer, uint32_t funct6, GPR rd, uint32_t funct5, uint32_t op) { 123 BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); 124 const auto rd_san = CompressedRegTo3BitEncoding(rd); 125 126 buffer.Emit16((funct6 << 10) | (rd_san << 7) | (funct5 << 2) | op); 127 } 128 129 void EmitCMJTType(CodeBuffer& buffer, uint32_t funct6, uint32_t index, uint32_t op) { 130 buffer.Emit16((funct6 << 10) | (index << 2) | op); 131 } 132 133 void EmitCMMVType(CodeBuffer& buffer, uint32_t funct6, GPR r1s, uint32_t funct2, GPR r2s, uint32_t op) { 134 const auto is_valid_s_register = [](GPR reg) { 135 return reg == s0 || reg == s1 || (reg >= s2 && reg <= s7); 136 }; 137 138 BISCUIT_ASSERT(r1s != r2s); 139 BISCUIT_ASSERT(is_valid_s_register(r1s)); 140 BISCUIT_ASSERT(is_valid_s_register(r2s)); 141 142 const auto r1s_san = r1s.Index() & 0b111; 143 const auto r2s_san = r2s.Index() & 0b111; 144 145 buffer.Emit16((funct6 << 10) | (r1s_san << 7) | (funct2 << 5) | (r2s_san << 2) | op); 146 } 147 148 void EmitCMPPType(CodeBuffer& buffer, uint32_t funct6, uint32_t funct2, PushPopList reglist, 149 int32_t stack_adj, uint32_t op, ArchFeature feature) { 150 BISCUIT_ASSERT(stack_adj % 16 == 0); 151 152 static constexpr std::array stack_adj_bases_rv32{ 153 0U, 0U, 0U, 0U, 16U, 16U, 16U, 16U, 154 32U, 32U, 32U, 32U, 48U, 48U, 48U, 64U, 155 }; 156 static constexpr std::array stack_adj_bases_rv64{ 157 0U, 0U, 0U, 0U, 16U, 16U, 32U, 32U, 158 48U, 48U, 64U, 64U, 80U, 80U, 96U, 112U 159 }; 160 161 const auto bitmask = reglist.GetBitmask(); 162 const auto stack_adj_base = IsRV64(feature) ? stack_adj_bases_rv64[bitmask] 163 : stack_adj_bases_rv32[bitmask]; 164 const auto stack_adj_u = static_cast<uint32_t>(std::abs(stack_adj)); 165 const auto spimm = (stack_adj_u - stack_adj_base) / 16U; 166 167 // We can only encode up to three differenct values as the upper spimm bits. 168 // Ensure we catch any cases where we end up going outside of them. 169 BISCUIT_ASSERT(stack_adj_u == stack_adj_base || 170 stack_adj_u == stack_adj_base + 16 || 171 stack_adj_u == stack_adj_base + 32 || 172 stack_adj_u == stack_adj_base + 48); 173 174 buffer.Emit16((funct6 << 10) | (funct2 << 8) | (bitmask << 4) | (spimm << 2) | op); 175 } 176 } // Anonymous namespace 177 178 void Assembler::C_ADD(GPR rd, GPR rs) noexcept { 179 BISCUIT_ASSERT(rs != x0); 180 m_buffer.Emit16(0x9002 | (rd.Index() << 7) | (rs.Index() << 2)); 181 } 182 183 void Assembler::C_ADDI(GPR rd, int32_t imm) noexcept { 184 BISCUIT_ASSERT(imm != 0); 185 BISCUIT_ASSERT(IsValidSigned6BitImm(imm)); 186 EmitCompressedImmediate(m_buffer, 0b000, static_cast<uint32_t>(imm), rd, 0b01); 187 } 188 189 void Assembler::C_ADDIW(GPR rd, int32_t imm) noexcept { 190 BISCUIT_ASSERT(IsRV64OrRV128(m_features)); 191 BISCUIT_ASSERT(IsValidSigned6BitImm(imm)); 192 EmitCompressedImmediate(m_buffer, 0b001, static_cast<uint32_t>(imm), rd, 0b01); 193 } 194 195 void Assembler::C_ADDI4SPN(GPR rd, uint32_t imm) noexcept { 196 BISCUIT_ASSERT(imm != 0); 197 BISCUIT_ASSERT(imm <= 1020); 198 BISCUIT_ASSERT(imm % 4 == 0); 199 200 // clang-format off 201 const auto new_imm = ((imm & 0x030) << 2) | 202 ((imm & 0x3C0) >> 4) | 203 ((imm & 0x004) >> 1) | 204 ((imm & 0x008) >> 3); 205 // clang-format on 206 207 EmitCompressedWideImmediate(m_buffer, 0b000, new_imm, rd, 0b00); 208 } 209 210 void Assembler::C_ADDW(GPR rd, GPR rs) noexcept { 211 BISCUIT_ASSERT(IsRV64OrRV128(m_features)); 212 EmitCompressedRegArith(m_buffer, 0b100111, rd, 0b01, rs, 0b01); 213 } 214 215 void Assembler::C_ADDI16SP(int32_t imm) noexcept { 216 BISCUIT_ASSERT(imm != 0); 217 BISCUIT_ASSERT(imm >= -512 && imm <= 496); 218 BISCUIT_ASSERT(imm % 16 == 0); 219 220 // clang-format off 221 const auto uimm = static_cast<uint32_t>(imm); 222 const auto new_imm = ((uimm & 0x020) >> 3) | 223 ((uimm & 0x180) >> 4) | 224 ((uimm & 0x040) >> 1) | 225 ((uimm & 0x010) << 2) | 226 ((uimm & 0x200) << 3); 227 // clang-format on 228 229 m_buffer.Emit16(0x6000U | new_imm | (x2.Index() << 7) | 0b01U); 230 } 231 232 void Assembler::C_AND(GPR rd, GPR rs) noexcept { 233 EmitCompressedRegArith(m_buffer, 0b100011, rd, 0b11, rs, 0b01); 234 } 235 236 void Assembler::C_ANDI(GPR rd, uint32_t imm) noexcept { 237 BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); 238 239 constexpr auto base = 0x8801U; 240 const auto shift_enc = ((imm & 0b11111) << 2) | ((imm & 0b100000) << 7); 241 const auto reg = CompressedRegTo3BitEncoding(rd); 242 243 m_buffer.Emit16(base | shift_enc | (reg << 7)); 244 } 245 246 void Assembler::C_BEQZ(GPR rs, int32_t offset) noexcept { 247 EmitCompressedBranch(m_buffer, 0b110, offset, rs, 0b01); 248 } 249 250 void Assembler::C_BEQZ(GPR rs, Label* label) noexcept { 251 const auto address = LinkAndGetOffset(label); 252 C_BEQZ(rs, static_cast<int32_t>(address)); 253 } 254 255 void Assembler::C_BNEZ(GPR rs, int32_t offset) noexcept { 256 EmitCompressedBranch(m_buffer, 0b111, offset, rs, 0b01); 257 } 258 259 void Assembler::C_BNEZ(GPR rs, Label* label) noexcept { 260 const auto address = LinkAndGetOffset(label); 261 C_BNEZ(rs, static_cast<int32_t>(address)); 262 } 263 264 void Assembler::C_EBREAK() noexcept { 265 m_buffer.Emit16(0x9002); 266 } 267 268 void Assembler::C_FLD(FPR rd, uint32_t imm, GPR rs) noexcept { 269 BISCUIT_ASSERT(IsRV32OrRV64(m_features)); 270 BISCUIT_ASSERT(imm <= 248); 271 BISCUIT_ASSERT(imm % 8 == 0); 272 273 EmitCompressedLoad(m_buffer, 0b001, imm, rs, rd, 0b00); 274 } 275 276 void Assembler::C_FLDSP(FPR rd, uint32_t imm) noexcept { 277 BISCUIT_ASSERT(IsRV32OrRV64(m_features)); 278 BISCUIT_ASSERT(imm <= 504); 279 BISCUIT_ASSERT(imm % 8 == 0); 280 281 // clang-format off 282 const auto new_imm = ((imm & 0x018) << 2) | 283 ((imm & 0x1C0) >> 4) | 284 ((imm & 0x020) << 7); 285 // clang-format on 286 287 m_buffer.Emit16(0x2002U | (rd.Index() << 7) | new_imm); 288 } 289 290 void Assembler::C_FLW(FPR rd, uint32_t imm, GPR rs) noexcept { 291 BISCUIT_ASSERT(IsRV32(m_features)); 292 BISCUIT_ASSERT(imm <= 124); 293 BISCUIT_ASSERT(imm % 4 == 0); 294 295 imm &= 0x7C; 296 const auto new_imm = ((imm & 0b0100) << 5) | (imm & 0x78); 297 EmitCompressedLoad(m_buffer, 0b011, new_imm, rs, rd, 0b00); 298 } 299 300 void Assembler::C_FLWSP(FPR rd, uint32_t imm) noexcept { 301 BISCUIT_ASSERT(IsRV32(m_features)); 302 BISCUIT_ASSERT(imm <= 252); 303 BISCUIT_ASSERT(imm % 4 == 0); 304 305 // clang-format off 306 const auto new_imm = ((imm & 0x020) << 7) | 307 ((imm & 0x0C0) >> 4) | 308 ((imm & 0x01C) << 2); 309 // clang-format on 310 311 m_buffer.Emit16(0x6002U | (rd.Index() << 7) | new_imm); 312 } 313 314 void Assembler::C_FSD(FPR rs2, uint32_t imm, GPR rs1) noexcept { 315 BISCUIT_ASSERT(IsRV32OrRV64(m_features)); 316 BISCUIT_ASSERT(imm <= 248); 317 BISCUIT_ASSERT(imm % 8 == 0); 318 319 EmitCompressedStore(m_buffer, 0b101, imm, rs1, rs2, 0b00); 320 } 321 322 void Assembler::C_FSDSP(FPR rs, uint32_t imm) noexcept { 323 BISCUIT_ASSERT(IsRV32OrRV64(m_features)); 324 BISCUIT_ASSERT(imm <= 504); 325 BISCUIT_ASSERT(imm % 8 == 0); 326 327 // clang-format off 328 const auto new_imm = ((imm & 0x038) << 7) | 329 ((imm & 0x1C0) << 1); 330 // clang-format on 331 332 m_buffer.Emit16(0xA002U | (rs.Index() << 2) | new_imm); 333 } 334 335 void Assembler::C_J(Label* label) noexcept { 336 const auto address = LinkAndGetOffset(label); 337 C_J(static_cast<int32_t>(address)); 338 } 339 340 void Assembler::C_J(int32_t offset) noexcept { 341 EmitCompressedJump(m_buffer, 0b101, offset, 0b01); 342 } 343 344 void Assembler::C_JAL(Label* label) noexcept { 345 const auto address = LinkAndGetOffset(label); 346 C_JAL(static_cast<int32_t>(address)); 347 } 348 349 void Assembler::C_JAL(int32_t offset) noexcept { 350 BISCUIT_ASSERT(IsRV32(m_features)); 351 EmitCompressedJump(m_buffer, 0b001, offset, 0b01); 352 } 353 354 void Assembler::C_FSW(FPR rs2, uint32_t imm, GPR rs1) noexcept { 355 BISCUIT_ASSERT(IsRV32(m_features)); 356 BISCUIT_ASSERT(imm <= 124); 357 BISCUIT_ASSERT(imm % 4 == 0); 358 359 imm &= 0x7C; 360 const auto new_imm = ((imm & 0b0100) << 5) | (imm & 0x78); 361 EmitCompressedStore(m_buffer, 0b111, new_imm, rs1, rs2, 0b00); 362 } 363 364 void Assembler::C_FSWSP(FPR rs, uint32_t imm) noexcept { 365 BISCUIT_ASSERT(IsRV32(m_features)); 366 BISCUIT_ASSERT(imm <= 252); 367 BISCUIT_ASSERT(imm % 4 == 0); 368 369 // clang-format off 370 const auto new_imm = ((imm & 0x0C0) << 1) | 371 ((imm & 0x03C) << 7); 372 // clang-format on 373 374 m_buffer.Emit16(0xE002U | (rs.Index() << 2) | new_imm); 375 } 376 377 void Assembler::C_JALR(GPR rs) noexcept { 378 BISCUIT_ASSERT(rs != x0); 379 m_buffer.Emit16(0x9002 | (rs.Index() << 7)); 380 } 381 382 void Assembler::C_JR(GPR rs) noexcept { 383 BISCUIT_ASSERT(rs != x0); 384 m_buffer.Emit16(0x8002 | (rs.Index() << 7)); 385 } 386 387 void Assembler::C_LD(GPR rd, uint32_t imm, GPR rs) noexcept { 388 BISCUIT_ASSERT(IsRV64OrRV128(m_features)); 389 BISCUIT_ASSERT(imm <= 248); 390 BISCUIT_ASSERT(imm % 8 == 0); 391 392 EmitCompressedLoad(m_buffer, 0b011, imm, rs, rd, 0b00); 393 } 394 395 void Assembler::C_LDSP(GPR rd, uint32_t imm) noexcept { 396 BISCUIT_ASSERT(IsRV64OrRV128(m_features)); 397 BISCUIT_ASSERT(rd != x0); 398 BISCUIT_ASSERT(imm <= 504); 399 BISCUIT_ASSERT(imm % 8 == 0); 400 401 // clang-format off 402 const auto new_imm = ((imm & 0x018) << 2) | 403 ((imm & 0x1C0) >> 4) | 404 ((imm & 0x020) << 7); 405 // clang-format on 406 407 m_buffer.Emit16(0x6002U | (rd.Index() << 7) | new_imm); 408 } 409 410 void Assembler::C_LI(GPR rd, int32_t imm) noexcept { 411 BISCUIT_ASSERT(IsValidSigned6BitImm(imm)); 412 EmitCompressedImmediate(m_buffer, 0b010, static_cast<uint32_t>(imm), rd, 0b01); 413 } 414 415 void Assembler::C_LQ(GPR rd, uint32_t imm, GPR rs) noexcept { 416 BISCUIT_ASSERT(IsRV128(m_features)); 417 BISCUIT_ASSERT(imm <= 496); 418 BISCUIT_ASSERT(imm % 16 == 0); 419 420 imm &= 0x1F0; 421 const auto new_imm = ((imm & 0x100) >> 5) | (imm & 0xF0); 422 EmitCompressedLoad(m_buffer, 0b001, new_imm, rs, rd, 0b00); 423 } 424 425 void Assembler::C_LQSP(GPR rd, uint32_t imm) noexcept { 426 BISCUIT_ASSERT(IsRV128(m_features)); 427 BISCUIT_ASSERT(rd != x0); 428 BISCUIT_ASSERT(imm <= 1008); 429 BISCUIT_ASSERT(imm % 16 == 0); 430 431 // clang-format off 432 const auto new_imm = ((imm & 0x020) << 7) | 433 ((imm & 0x010) << 2) | 434 ((imm & 0x3C0) >> 4); 435 // clang-format on 436 437 m_buffer.Emit16(0x2002U | (rd.Index() << 7) | new_imm); 438 } 439 440 void Assembler::C_LUI(GPR rd, uint32_t imm) noexcept { 441 BISCUIT_ASSERT(imm != 0); 442 BISCUIT_ASSERT(rd != x0 && rd != x2); 443 444 const auto new_imm = (imm & 0x3F000) >> 12; 445 EmitCompressedImmediate(m_buffer, 0b011, new_imm, rd, 0b01); 446 } 447 448 void Assembler::C_LW(GPR rd, uint32_t imm, GPR rs) noexcept { 449 BISCUIT_ASSERT(imm <= 124); 450 BISCUIT_ASSERT(imm % 4 == 0); 451 452 imm &= 0x7C; 453 const auto new_imm = ((imm & 0b0100) << 5) | (imm & 0x78); 454 EmitCompressedLoad(m_buffer, 0b010, new_imm, rs, rd, 0b00); 455 } 456 457 void Assembler::C_LWSP(GPR rd, uint32_t imm) noexcept { 458 BISCUIT_ASSERT(rd != x0); 459 BISCUIT_ASSERT(imm <= 252); 460 BISCUIT_ASSERT(imm % 4 == 0); 461 462 // clang-format off 463 const auto new_imm = ((imm & 0x020) << 7) | 464 ((imm & 0x0C0) >> 4) | 465 ((imm & 0x01C) << 2); 466 // clang-format on 467 468 m_buffer.Emit16(0x4002U | (rd.Index() << 7) | new_imm); 469 } 470 471 void Assembler::C_MV(GPR rd, GPR rs) noexcept { 472 BISCUIT_ASSERT(rd != x0); 473 BISCUIT_ASSERT(rs != x0); 474 m_buffer.Emit16(0x8002 | (rd.Index() << 7) | (rs.Index() << 2)); 475 } 476 477 void Assembler::C_NOP() noexcept { 478 m_buffer.Emit16(1); 479 } 480 481 void Assembler::C_OR(GPR rd, GPR rs) noexcept { 482 EmitCompressedRegArith(m_buffer, 0b100011, rd, 0b10, rs, 0b01); 483 } 484 485 void Assembler::C_SD(GPR rs2, uint32_t imm, GPR rs1) noexcept { 486 BISCUIT_ASSERT(IsRV64OrRV128(m_features)); 487 BISCUIT_ASSERT(imm <= 248); 488 BISCUIT_ASSERT(imm % 8 == 0); 489 490 EmitCompressedLoad(m_buffer, 0b111, imm, rs1, rs2, 0b00); 491 } 492 493 void Assembler::C_SDSP(GPR rs, uint32_t imm) noexcept { 494 BISCUIT_ASSERT(IsRV64OrRV128(m_features)); 495 BISCUIT_ASSERT(imm <= 504); 496 BISCUIT_ASSERT(imm % 8 == 0); 497 498 // clang-format off 499 const auto new_imm = ((imm & 0x038) << 7) | 500 ((imm & 0x1C0) << 1); 501 // clang-format on 502 503 m_buffer.Emit16(0xE002U | (rs.Index() << 2) | new_imm); 504 } 505 506 void Assembler::C_SLLI(GPR rd, uint32_t shift) noexcept { 507 BISCUIT_ASSERT(rd != x0); 508 BISCUIT_ASSERT(IsValidCompressedShiftAmount(shift)); 509 510 // RV128C encodes a 64-bit shift with an encoding of 0. 511 if (shift == 64) { 512 BISCUIT_ASSERT(IsRV128(m_features)); 513 shift = 0; 514 } 515 516 const auto shift_enc = ((shift & 0b11111) << 2) | ((shift & 0b100000) << 7); 517 m_buffer.Emit16(0x0002U | shift_enc | (rd.Index() << 7)); 518 } 519 520 void Assembler::C_SQ(GPR rs2, uint32_t imm, GPR rs1) noexcept { 521 BISCUIT_ASSERT(IsRV128(m_features)); 522 BISCUIT_ASSERT(imm <= 496); 523 BISCUIT_ASSERT(imm % 16 == 0); 524 525 imm &= 0x1F0; 526 const auto new_imm = ((imm & 0x100) >> 5) | (imm & 0xF0); 527 EmitCompressedStore(m_buffer, 0b101, new_imm, rs1, rs2, 0b00); 528 } 529 530 void Assembler::C_SQSP(GPR rs, uint32_t imm) noexcept { 531 BISCUIT_ASSERT(IsRV128(m_features)); 532 BISCUIT_ASSERT(imm <= 1008); 533 BISCUIT_ASSERT(imm % 16 == 0); 534 535 // clang-format off 536 const auto new_imm = ((imm & 0x3C0) << 1) | 537 ((imm & 0x030) << 7); 538 // clang-format on 539 540 m_buffer.Emit16(0xA002U | (rs.Index() << 2) | new_imm); 541 } 542 543 void Assembler::C_SRAI(GPR rd, uint32_t shift) noexcept { 544 BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); 545 BISCUIT_ASSERT(IsValidCompressedShiftAmount(shift)); 546 547 // RV128C encodes a 64-bit shift with an encoding of 0. 548 if (shift == 64) { 549 BISCUIT_ASSERT(IsRV128(m_features)); 550 shift = 0; 551 } 552 553 constexpr auto base = 0x8401U; 554 const auto shift_enc = ((shift & 0b11111) << 2) | ((shift & 0b100000) << 7); 555 const auto reg = CompressedRegTo3BitEncoding(rd); 556 557 m_buffer.Emit16(base | shift_enc | (reg << 7)); 558 } 559 560 void Assembler::C_SRLI(GPR rd, uint32_t shift) noexcept { 561 BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); 562 BISCUIT_ASSERT(IsValidCompressedShiftAmount(shift)); 563 564 // RV128C encodes a 64-bit shift with an encoding of 0. 565 if (shift == 64) { 566 BISCUIT_ASSERT(IsRV128(m_features)); 567 shift = 0; 568 } 569 570 constexpr auto base = 0x8001U; 571 const auto shift_enc = ((shift & 0b11111) << 2) | ((shift & 0b100000) << 7); 572 const auto reg = CompressedRegTo3BitEncoding(rd); 573 574 m_buffer.Emit16(base | shift_enc | (reg << 7)); 575 } 576 577 void Assembler::C_SUB(GPR rd, GPR rs) noexcept { 578 EmitCompressedRegArith(m_buffer, 0b100011, rd, 0b00, rs, 0b01); 579 } 580 581 void Assembler::C_SUBW(GPR rd, GPR rs) noexcept { 582 BISCUIT_ASSERT(IsRV64OrRV128(m_features)); 583 EmitCompressedRegArith(m_buffer, 0b100111, rd, 0b00, rs, 0b01); 584 } 585 586 void Assembler::C_SW(GPR rs2, uint32_t imm, GPR rs1) noexcept { 587 BISCUIT_ASSERT(imm <= 124); 588 BISCUIT_ASSERT(imm % 4 == 0); 589 590 imm &= 0x7C; 591 const auto new_imm = ((imm & 0b0100) << 5) | (imm & 0x78); 592 EmitCompressedStore(m_buffer, 0b110, new_imm, rs1, rs2, 0b00); 593 } 594 595 void Assembler::C_SWSP(GPR rs, uint32_t imm) noexcept { 596 BISCUIT_ASSERT(imm <= 252); 597 BISCUIT_ASSERT(imm % 4 == 0); 598 599 // clang-format off 600 const auto new_imm = ((imm & 0x0C0) << 1) | 601 ((imm & 0x03C) << 7); 602 // clang-format on 603 604 m_buffer.Emit16(0xC002U | (rs.Index() << 2) | new_imm); 605 } 606 607 void Assembler::C_UNDEF() noexcept { 608 m_buffer.Emit16(0); 609 } 610 611 void Assembler::C_XOR(GPR rd, GPR rs) noexcept { 612 EmitCompressedRegArith(m_buffer, 0b100011, rd, 0b01, rs, 0b01); 613 } 614 615 // Zc Extension Instructions 616 617 void Assembler::C_LBU(GPR rd, uint32_t uimm, GPR rs) noexcept { 618 // C.LBU swaps the ordering of the immediate. 619 const auto uimm_fixed = ((uimm & 0b01) << 1) | ((uimm & 0b10) >> 1); 620 621 EmitCLBType(m_buffer, 0b100000, rs, uimm_fixed, rd, 0b00, 0); 622 } 623 void Assembler::C_LH(GPR rd, uint32_t uimm, GPR rs) noexcept { 624 EmitCLHType(m_buffer, 0b100001, rs, uimm, rd, 0b00, 1); 625 } 626 void Assembler::C_LHU(GPR rd, uint32_t uimm, GPR rs) noexcept { 627 EmitCLHType(m_buffer, 0b100001, rs, uimm, rd, 0b00, 0); 628 } 629 void Assembler::C_SB(GPR rs2, uint32_t uimm, GPR rs1) noexcept { 630 // C.SB swaps the ordering of the immediate. 631 const auto uimm_fixed = ((uimm & 0b01) << 1) | ((uimm & 0b10) >> 1); 632 633 EmitCSBType(m_buffer, 0b100010, rs1, uimm_fixed, rs2, 0b00); 634 } 635 void Assembler::C_SH(GPR rs2, uint32_t uimm, GPR rs1) noexcept { 636 EmitCSHType(m_buffer, 0b100011, rs1, uimm, rs2, 0b00); 637 } 638 639 void Assembler::C_SEXT_B(GPR rd) noexcept { 640 EmitCUType(m_buffer, 0b100111, rd, 0b11001, 0b01); 641 } 642 void Assembler::C_SEXT_H(GPR rd) noexcept { 643 EmitCUType(m_buffer, 0b100111, rd, 0b11011, 0b01); 644 } 645 void Assembler::C_ZEXT_B(GPR rd) noexcept { 646 EmitCUType(m_buffer, 0b100111, rd, 0b11000, 0b01); 647 } 648 void Assembler::C_ZEXT_H(GPR rd) noexcept { 649 EmitCUType(m_buffer, 0b100111, rd, 0b11010, 0b01); 650 } 651 void Assembler::C_ZEXT_W(GPR rd) noexcept { 652 BISCUIT_ASSERT(IsRV64(m_features)); 653 EmitCUType(m_buffer, 0b100111, rd, 0b11100, 0b01); 654 } 655 656 void Assembler::C_MUL(GPR rsd, GPR rs2) noexcept { 657 EmitCompressedRegArith(m_buffer, 0b100111, rsd, 0b10, rs2, 0b01); 658 } 659 void Assembler::C_NOT(GPR rd) noexcept { 660 EmitCUType(m_buffer, 0b100111, rd, 0b11101, 0b01); 661 } 662 663 void Assembler::CM_JALT(uint32_t index) noexcept { 664 BISCUIT_ASSERT(index >= 32 && index <= 255); 665 EmitCMJTType(m_buffer, 0b101000, index, 0b10); 666 } 667 void Assembler::CM_JT(uint32_t index) noexcept { 668 BISCUIT_ASSERT(index <= 31); 669 EmitCMJTType(m_buffer, 0b101000, index, 0b10); 670 } 671 672 void Assembler::CM_MVA01S(GPR r1s, GPR r2s) noexcept { 673 EmitCMMVType(m_buffer, 0b101011, r1s, 0b11, r2s, 0b10); 674 } 675 void Assembler::CM_MVSA01(GPR r1s, GPR r2s) noexcept { 676 EmitCMMVType(m_buffer, 0b101011, r1s, 0b01, r2s, 0b10); 677 } 678 679 void Assembler::CM_POP(PushPopList reg_list, int32_t stack_adj) noexcept { 680 BISCUIT_ASSERT(stack_adj > 0); 681 EmitCMPPType(m_buffer, 0b101110, 0b10, reg_list, stack_adj, 0b10, m_features); 682 } 683 void Assembler::CM_POPRET(PushPopList reg_list, int32_t stack_adj) noexcept { 684 BISCUIT_ASSERT(stack_adj > 0); 685 EmitCMPPType(m_buffer, 0b101111, 0b10, reg_list, stack_adj, 0b10, m_features); 686 } 687 void Assembler::CM_POPRETZ(PushPopList reg_list, int32_t stack_adj) noexcept { 688 BISCUIT_ASSERT(stack_adj > 0); 689 EmitCMPPType(m_buffer, 0b101111, 0b00, reg_list, stack_adj, 0b10, m_features); 690 } 691 void Assembler::CM_PUSH(PushPopList reg_list, int32_t stack_adj) noexcept { 692 BISCUIT_ASSERT(stack_adj < 0); 693 EmitCMPPType(m_buffer, 0b101110, 0b00, reg_list, stack_adj, 0b10, m_features); 694 } 695 696 } // namespace biscuit