/ externals / biscuit / src / assembler_compressed.cpp
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