1 //! Contains the RISC-V instruction encoding logic.
2 //!
3 //! These formats are specified in the RISC-V specification in section 2.2.
4 //! See: <https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf>
5 //!
6 //! Some instructions especially in extensions have slight variations from
7 //! the base RISC-V specification.
8 
9 use super::*;
10 use crate::isa::riscv64::lower::isle::generated_code::{
11     COpcodeSpace, CaOp, CbOp, CiOp, CiwOp, ClOp, CrOp, CsOp, CssOp, CsznOp, FpuOPWidth,
12     VecAluOpRImm5, VecAluOpRR, VecAluOpRRRImm5, VecAluOpRRRR, VecOpCategory, ZcbMemOp,
13 };
14 use crate::machinst::isle::WritableReg;
15 
unsigned_field_width(value: u32, width: u8) -> u3216 fn unsigned_field_width(value: u32, width: u8) -> u32 {
17     debug_assert_eq!(value & (!0 << width), 0);
18     value
19 }
20 
21 /// Layout:
22 /// 0-------6-7-------11-12------14-15------19-20------24-25-------31
23 /// | Opcode |   rd     |  funct3  |   rs1    |   rs2    |   funct7  |
encode_r_type_bits(opcode: u32, rd: u32, funct3: u32, rs1: u32, rs2: u32, funct7: u32) -> u3224 fn encode_r_type_bits(opcode: u32, rd: u32, funct3: u32, rs1: u32, rs2: u32, funct7: u32) -> u32 {
25     let mut bits = 0;
26     bits |= unsigned_field_width(opcode, 7);
27     bits |= unsigned_field_width(rd, 5) << 7;
28     bits |= unsigned_field_width(funct3, 3) << 12;
29     bits |= unsigned_field_width(rs1, 5) << 15;
30     bits |= unsigned_field_width(rs2, 5) << 20;
31     bits |= unsigned_field_width(funct7, 7) << 25;
32     bits
33 }
34 
35 /// Encode an R-type instruction.
encode_r_type( opcode: u32, rd: WritableReg, funct3: u32, rs1: Reg, rs2: Reg, funct7: u32, ) -> u3236 pub fn encode_r_type(
37     opcode: u32,
38     rd: WritableReg,
39     funct3: u32,
40     rs1: Reg,
41     rs2: Reg,
42     funct7: u32,
43 ) -> u32 {
44     encode_r_type_bits(
45         opcode,
46         reg_to_gpr_num(rd.to_reg()),
47         funct3,
48         reg_to_gpr_num(rs1),
49         reg_to_gpr_num(rs2),
50         funct7,
51     )
52 }
53 
54 /// Layout:
55 /// 0-------6-7-------11-12------14-15------19-20------------------31
56 /// | Opcode |   rd     |  width   |   rs1    |     Offset[11:0]    |
encode_i_type_bits(opcode: u32, rd: u32, funct3: u32, rs1: u32, offset: u32) -> u3257 fn encode_i_type_bits(opcode: u32, rd: u32, funct3: u32, rs1: u32, offset: u32) -> u32 {
58     let mut bits = 0;
59     bits |= unsigned_field_width(opcode, 7);
60     bits |= unsigned_field_width(rd, 5) << 7;
61     bits |= unsigned_field_width(funct3, 3) << 12;
62     bits |= unsigned_field_width(rs1, 5) << 15;
63     bits |= unsigned_field_width(offset, 12) << 20;
64     bits
65 }
66 
67 /// Encode an I-type instruction.
encode_i_type(opcode: u32, rd: WritableReg, width: u32, rs1: Reg, offset: Imm12) -> u3268 pub fn encode_i_type(opcode: u32, rd: WritableReg, width: u32, rs1: Reg, offset: Imm12) -> u32 {
69     encode_i_type_bits(
70         opcode,
71         reg_to_gpr_num(rd.to_reg()),
72         width,
73         reg_to_gpr_num(rs1),
74         offset.bits(),
75     )
76 }
77 
78 /// Encode an S-type instruction.
79 ///
80 /// Layout:
81 /// 0-------6-7-------11-12------14-15------19-20---24-25-------------31
82 /// | Opcode | imm[4:0] |  width   |   base   |  src  |    imm[11:5]   |
encode_s_type(opcode: u32, width: u32, base: Reg, src: Reg, offset: Imm12) -> u3283 pub fn encode_s_type(opcode: u32, width: u32, base: Reg, src: Reg, offset: Imm12) -> u32 {
84     let mut bits = 0;
85     bits |= unsigned_field_width(opcode, 7);
86     bits |= (offset.bits() & 0b11111) << 7;
87     bits |= unsigned_field_width(width, 3) << 12;
88     bits |= reg_to_gpr_num(base) << 15;
89     bits |= reg_to_gpr_num(src) << 20;
90     bits |= unsigned_field_width(offset.bits() >> 5, 7) << 25;
91     bits
92 }
93 
94 /// Encodes a Vector ALU instruction.
95 ///
96 /// Fields:
97 /// - opcode (7 bits)
98 /// - vd     (5 bits)
99 /// - funct3 (3 bits)
100 /// - vs1    (5 bits)
101 /// - vs2    (5 bits)
102 /// - vm     (1 bit)
103 /// - funct6 (6 bits)
104 ///
105 /// See: https://github.com/riscv/riscv-v-spec/blob/master/valu-format.adoc
encode_valu( op: VecAluOpRRR, vd: WritableReg, vs1: Reg, vs2: Reg, masking: VecOpMasking, ) -> u32106 pub fn encode_valu(
107     op: VecAluOpRRR,
108     vd: WritableReg,
109     vs1: Reg,
110     vs2: Reg,
111     masking: VecOpMasking,
112 ) -> u32 {
113     let funct7 = (op.funct6() << 1) | masking.encode();
114     encode_r_type_bits(
115         op.opcode(),
116         reg_to_gpr_num(vd.to_reg()),
117         op.funct3(),
118         reg_to_gpr_num(vs1),
119         reg_to_gpr_num(vs2),
120         funct7,
121     )
122 }
123 
124 /// Encodes a Vector ALU+Imm instruction.
125 /// This is just a Vector ALU instruction with an immediate in the VS1 field.
126 ///
127 /// Fields:
128 /// - opcode (7 bits)
129 /// - vd     (5 bits)
130 /// - funct3 (3 bits)
131 /// - imm    (5 bits)
132 /// - vs2    (5 bits)
133 /// - vm     (1 bit)
134 /// - funct6 (6 bits)
135 ///
136 /// See: https://github.com/riscv/riscv-v-spec/blob/master/valu-format.adoc
encode_valu_rr_imm( op: VecAluOpRRImm5, vd: WritableReg, imm: Imm5, vs2: Reg, masking: VecOpMasking, ) -> u32137 pub fn encode_valu_rr_imm(
138     op: VecAluOpRRImm5,
139     vd: WritableReg,
140     imm: Imm5,
141     vs2: Reg,
142     masking: VecOpMasking,
143 ) -> u32 {
144     let funct7 = (op.funct6() << 1) | masking.encode();
145     let imm = imm.bits() as u32;
146     encode_r_type_bits(
147         op.opcode(),
148         reg_to_gpr_num(vd.to_reg()),
149         op.funct3(),
150         imm,
151         reg_to_gpr_num(vs2),
152         funct7,
153     )
154 }
155 
encode_valu_rrrr( op: VecAluOpRRRR, vd: WritableReg, vs2: Reg, vs1: Reg, masking: VecOpMasking, ) -> u32156 pub fn encode_valu_rrrr(
157     op: VecAluOpRRRR,
158     vd: WritableReg,
159     vs2: Reg,
160     vs1: Reg,
161     masking: VecOpMasking,
162 ) -> u32 {
163     let funct7 = (op.funct6() << 1) | masking.encode();
164     encode_r_type_bits(
165         op.opcode(),
166         reg_to_gpr_num(vd.to_reg()),
167         op.funct3(),
168         reg_to_gpr_num(vs1),
169         reg_to_gpr_num(vs2),
170         funct7,
171     )
172 }
173 
encode_valu_rrr_imm( op: VecAluOpRRRImm5, vd: WritableReg, imm: Imm5, vs2: Reg, masking: VecOpMasking, ) -> u32174 pub fn encode_valu_rrr_imm(
175     op: VecAluOpRRRImm5,
176     vd: WritableReg,
177     imm: Imm5,
178     vs2: Reg,
179     masking: VecOpMasking,
180 ) -> u32 {
181     let funct7 = (op.funct6() << 1) | masking.encode();
182     let imm = imm.bits() as u32;
183     encode_r_type_bits(
184         op.opcode(),
185         reg_to_gpr_num(vd.to_reg()),
186         op.funct3(),
187         imm,
188         reg_to_gpr_num(vs2),
189         funct7,
190     )
191 }
192 
encode_valu_rr(op: VecAluOpRR, vd: WritableReg, vs: Reg, masking: VecOpMasking) -> u32193 pub fn encode_valu_rr(op: VecAluOpRR, vd: WritableReg, vs: Reg, masking: VecOpMasking) -> u32 {
194     let funct7 = (op.funct6() << 1) | masking.encode();
195 
196     let (vs1, vs2) = if op.vs_is_vs2_encoded() {
197         (op.aux_encoding(), reg_to_gpr_num(vs))
198     } else {
199         (reg_to_gpr_num(vs), op.aux_encoding())
200     };
201 
202     encode_r_type_bits(
203         op.opcode(),
204         reg_to_gpr_num(vd.to_reg()),
205         op.funct3(),
206         vs1,
207         vs2,
208         funct7,
209     )
210 }
211 
encode_valu_r_imm( op: VecAluOpRImm5, vd: WritableReg, imm: Imm5, masking: VecOpMasking, ) -> u32212 pub fn encode_valu_r_imm(
213     op: VecAluOpRImm5,
214     vd: WritableReg,
215     imm: Imm5,
216     masking: VecOpMasking,
217 ) -> u32 {
218     let funct7 = (op.funct6() << 1) | masking.encode();
219 
220     // This is true for this opcode, not sure if there are any other ones.
221     debug_assert_eq!(op, VecAluOpRImm5::VmvVI);
222     let vs1 = imm.bits() as u32;
223     let vs2 = op.aux_encoding();
224 
225     encode_r_type_bits(
226         op.opcode(),
227         reg_to_gpr_num(vd.to_reg()),
228         op.funct3(),
229         vs1,
230         vs2,
231         funct7,
232     )
233 }
234 
235 /// Encodes a Vector CFG Imm instruction.
236 ///
237 /// See: https://github.com/riscv/riscv-v-spec/blob/master/vcfg-format.adoc
238 // TODO: Check if this is any of the known instruction types in the spec.
encode_vcfg_imm(opcode: u32, rd: Reg, imm: UImm5, vtype: &VType) -> u32239 pub fn encode_vcfg_imm(opcode: u32, rd: Reg, imm: UImm5, vtype: &VType) -> u32 {
240     let mut bits = 0;
241     bits |= unsigned_field_width(opcode, 7);
242     bits |= reg_to_gpr_num(rd) << 7;
243     bits |= VecOpCategory::OPCFG.encode() << 12;
244     bits |= unsigned_field_width(imm.bits(), 5) << 15;
245     bits |= unsigned_field_width(vtype.encode(), 10) << 20;
246     bits |= 0b11 << 30;
247     bits
248 }
249 
250 /// Encodes a Vector Mem Unit Stride Load instruction.
251 ///
252 /// See: https://github.com/riscv/riscv-v-spec/blob/master/vmem-format.adoc
253 /// TODO: These instructions share opcode space with LOAD-FP and STORE-FP
encode_vmem_load( opcode: u32, vd: Reg, width: VecElementWidth, rs1: Reg, lumop: u32, masking: VecOpMasking, mop: u32, nf: u32, ) -> u32254 pub fn encode_vmem_load(
255     opcode: u32,
256     vd: Reg,
257     width: VecElementWidth,
258     rs1: Reg,
259     lumop: u32,
260     masking: VecOpMasking,
261     mop: u32,
262     nf: u32,
263 ) -> u32 {
264     // Width is encoded differently to avoid a clash with the FP load/store sizes.
265     let width = match width {
266         VecElementWidth::E8 => 0b000,
267         VecElementWidth::E16 => 0b101,
268         VecElementWidth::E32 => 0b110,
269         VecElementWidth::E64 => 0b111,
270     };
271 
272     let mut bits = 0;
273     bits |= unsigned_field_width(opcode, 7);
274     bits |= reg_to_gpr_num(vd) << 7;
275     bits |= width << 12;
276     bits |= reg_to_gpr_num(rs1) << 15;
277     bits |= unsigned_field_width(lumop, 5) << 20;
278     bits |= masking.encode() << 25;
279     bits |= unsigned_field_width(mop, 2) << 26;
280 
281     // The mew bit (inst[28]) when set is expected to be used to encode expanded
282     // memory sizes of 128 bits and above, but these encodings are currently reserved.
283     bits |= 0b0 << 28;
284 
285     bits |= unsigned_field_width(nf, 3) << 29;
286     bits
287 }
288 
289 /// Encodes a Vector Mem Unit Stride Load instruction.
290 ///
291 /// See: https://github.com/riscv/riscv-v-spec/blob/master/vmem-format.adoc
292 /// TODO: These instructions share opcode space with LOAD-FP and STORE-FP
encode_vmem_store( opcode: u32, vs3: Reg, width: VecElementWidth, rs1: Reg, sumop: u32, masking: VecOpMasking, mop: u32, nf: u32, ) -> u32293 pub fn encode_vmem_store(
294     opcode: u32,
295     vs3: Reg,
296     width: VecElementWidth,
297     rs1: Reg,
298     sumop: u32,
299     masking: VecOpMasking,
300     mop: u32,
301     nf: u32,
302 ) -> u32 {
303     // This is pretty much the same as the load instruction, just
304     // with different names on the fields.
305     encode_vmem_load(opcode, vs3, width, rs1, sumop, masking, mop, nf)
306 }
307 
308 // The CSR Reg instruction is really just an I type instruction with the CSR in
309 // the immediate field.
encode_csr_reg(op: CsrRegOP, rd: WritableReg, rs: Reg, csr: CSR) -> u32310 pub fn encode_csr_reg(op: CsrRegOP, rd: WritableReg, rs: Reg, csr: CSR) -> u32 {
311     encode_i_type(op.opcode(), rd, op.funct3(), rs, csr.bits())
312 }
313 
314 // The CSR Imm instruction is an I type instruction with the CSR in
315 // the immediate field and the value to be set in the `rs1` field.
encode_csr_imm(op: CsrImmOP, rd: WritableReg, csr: CSR, imm: UImm5) -> u32316 pub fn encode_csr_imm(op: CsrImmOP, rd: WritableReg, csr: CSR, imm: UImm5) -> u32 {
317     encode_i_type_bits(
318         op.opcode(),
319         reg_to_gpr_num(rd.to_reg()),
320         op.funct3(),
321         imm.bits(),
322         csr.bits().bits(),
323     )
324 }
325 
326 // Encode a CR type instruction.
327 //
328 // 0--1-2-----6-7-------11-12-------15
329 // |op |  rs2  |  rd/rs1  |  funct4  |
encode_cr_type(op: CrOp, rd: WritableReg, rs2: Reg) -> u16330 pub fn encode_cr_type(op: CrOp, rd: WritableReg, rs2: Reg) -> u16 {
331     let mut bits = 0;
332     bits |= unsigned_field_width(op.op().bits(), 2);
333     bits |= reg_to_gpr_num(rs2) << 2;
334     bits |= reg_to_gpr_num(rd.to_reg()) << 7;
335     bits |= unsigned_field_width(op.funct4(), 4) << 12;
336     bits.try_into().unwrap()
337 }
338 
339 // This isn't technically a instruction format that exists. It's just a CR type
340 // where the source is rs1, rs2 is zero. rs1 is never written to.
341 //
342 // Used for C.JR and C.JALR
encode_cr2_type(op: CrOp, rs1: Reg) -> u16343 pub fn encode_cr2_type(op: CrOp, rs1: Reg) -> u16 {
344     encode_cr_type(op, WritableReg::from_reg(rs1), zero_reg())
345 }
346 
347 // Encode a CA type instruction.
348 //
349 // 0--1-2-----4-5--------6-7--------9-10------15
350 // |op |  rs2  |  funct2  |  rd/rs1  | funct6 |
encode_ca_type(op: CaOp, rd: WritableReg, rs2: Reg) -> u16351 pub fn encode_ca_type(op: CaOp, rd: WritableReg, rs2: Reg) -> u16 {
352     let mut bits = 0;
353     bits |= unsigned_field_width(op.op().bits(), 2);
354     bits |= reg_to_compressed_gpr_num(rs2) << 2;
355     bits |= unsigned_field_width(op.funct2(), 2) << 5;
356     bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 7;
357     bits |= unsigned_field_width(op.funct6(), 6) << 10;
358     bits.try_into().unwrap()
359 }
360 
361 // Encode a CJ type instruction.
362 //
363 // The imm field is a 11 bit signed immediate that is shifted left by 1.
364 //
365 // 0--1-2-----12-13--------15
366 // |op |  imm   |  funct3  |
encode_cj_type(op: CjOp, imm: Imm12) -> u16367 pub fn encode_cj_type(op: CjOp, imm: Imm12) -> u16 {
368     let imm = imm.bits();
369     debug_assert!(imm & 1 == 0);
370 
371     // The offset bits are in rather weird positions.
372     // [11|4|9:8|10|6|7|3:1|5]
373     let mut imm_field = 0;
374     imm_field |= ((imm >> 11) & 1) << 10;
375     imm_field |= ((imm >> 4) & 1) << 9;
376     imm_field |= ((imm >> 8) & 3) << 7;
377     imm_field |= ((imm >> 10) & 1) << 6;
378     imm_field |= ((imm >> 6) & 1) << 5;
379     imm_field |= ((imm >> 7) & 1) << 4;
380     imm_field |= ((imm >> 1) & 7) << 1;
381     imm_field |= ((imm >> 5) & 1) << 0;
382 
383     let mut bits = 0;
384     bits |= unsigned_field_width(op.op().bits(), 2);
385     bits |= unsigned_field_width(imm_field, 11) << 2;
386     bits |= unsigned_field_width(op.funct3(), 3) << 13;
387     bits.try_into().unwrap()
388 }
389 
390 // Encode a CI type instruction.
391 //
392 // The imm field is a 6 bit signed immediate.
393 //
394 // 0--1-2-------6-7-------11-12-----12-13-----15
395 // |op | imm[4:0] |   src   | imm[5]  | funct3  |
encode_ci_type(op: CiOp, rd: WritableReg, imm: Imm6) -> u16396 pub fn encode_ci_type(op: CiOp, rd: WritableReg, imm: Imm6) -> u16 {
397     let imm = imm.bits();
398 
399     let mut bits = 0;
400     bits |= unsigned_field_width(op.op().bits(), 2);
401     bits |= unsigned_field_width((imm & 0x1f) as u32, 5) << 2;
402     bits |= reg_to_gpr_num(rd.to_reg()) << 7;
403     bits |= unsigned_field_width(((imm >> 5) & 1) as u32, 1) << 12;
404     bits |= unsigned_field_width(op.funct3(), 3) << 13;
405     bits.try_into().unwrap()
406 }
407 
408 // Stack-Pointer relative loads are regular CI instructions, but, the immediate
409 // is zero extended, and with a slightly different immediate field encoding.
encode_ci_sp_load(op: CiOp, rd: WritableReg, imm: Uimm6) -> u16410 pub fn encode_ci_sp_load(op: CiOp, rd: WritableReg, imm: Uimm6) -> u16 {
411     let imm = imm.bits();
412 
413     // These are the spec encoded offsets.
414     // LWSP:  [5|4:2|7:6]
415     // LDSP:  [5|4:3|8:6]
416     // FLDSP: [5|4:3|8:6]
417     //
418     // We don't receive the entire offset in `imm`, just a multiple of the load-size.
419 
420     // Number of bits in the lowest position of imm. 3 for lwsp, 2 for {f,}ldsp.
421     let low_bits = match op {
422         CiOp::CLwsp => 3,                // [4:2]
423         CiOp::CLdsp | CiOp::CFldsp => 2, // [4:3]
424         _ => unreachable!(),
425     };
426     let high_bits = 6 - 1 - low_bits;
427     let mut enc_imm = 0;
428 
429     // Encode [7:6] at the bottom of imm
430     enc_imm |= imm >> (6 - high_bits);
431 
432     // Next place [4:2] in the middle
433     enc_imm |= (imm & ((1 << low_bits) - 1)) << high_bits;
434 
435     // Finally place [5] at the top
436     enc_imm |= ((imm >> low_bits) & 1) << 5;
437 
438     let enc_imm = Imm6::maybe_from_i16((enc_imm as i16) << 10 >> 10).unwrap();
439 
440     encode_ci_type(op, rd, enc_imm)
441 }
442 
443 /// c.addi16sp is a regular CI op, but the immediate field is encoded in a weird way
encode_c_addi16sp(imm: Imm6) -> u16444 pub fn encode_c_addi16sp(imm: Imm6) -> u16 {
445     let imm = imm.bits();
446 
447     // [6|1|3|5:4|2]
448     let mut enc_imm = 0;
449     enc_imm |= ((imm >> 5) & 1) << 5;
450     enc_imm |= ((imm >> 0) & 1) << 4;
451     enc_imm |= ((imm >> 2) & 1) << 3;
452     enc_imm |= ((imm >> 3) & 3) << 1;
453     enc_imm |= ((imm >> 1) & 1) << 0;
454     let enc_imm = Imm6::maybe_from_i16((enc_imm as i16) << 10 >> 10).unwrap();
455 
456     encode_ci_type(CiOp::CAddi16sp, writable_stack_reg(), enc_imm)
457 }
458 
459 // Encode a CIW type instruction.
460 //
461 // 0--1-2------4-5------12-13--------15
462 // |op |   rd   |   imm   |  funct3  |
encode_ciw_type(op: CiwOp, rd: WritableReg, imm: u8) -> u16463 pub fn encode_ciw_type(op: CiwOp, rd: WritableReg, imm: u8) -> u16 {
464     // [3:2|7:4|0|1]
465     let mut imm_field = 0;
466     imm_field |= ((imm >> 1) & 1) << 0;
467     imm_field |= ((imm >> 0) & 1) << 1;
468     imm_field |= ((imm >> 4) & 15) << 2;
469     imm_field |= ((imm >> 2) & 3) << 6;
470 
471     let mut bits = 0;
472     bits |= unsigned_field_width(op.op().bits(), 2);
473     bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 2;
474     bits |= unsigned_field_width(imm_field as u32, 8) << 5;
475     bits |= unsigned_field_width(op.funct3(), 3) << 13;
476     bits.try_into().unwrap()
477 }
478 
479 // Encode a CB type instruction.
480 //
481 // The imm field is a 6 bit signed immediate.
482 //
483 // 0--1-2-------6-7-------9-10-------11-12-------13--------15
484 // |op | imm[4:0] |   dst  |  funct2   |  imm[5]  | funct3 |
encode_cb_type(op: CbOp, rd: WritableReg, imm: Imm6) -> u16485 pub fn encode_cb_type(op: CbOp, rd: WritableReg, imm: Imm6) -> u16 {
486     let imm = imm.bits();
487 
488     let mut bits = 0;
489     bits |= unsigned_field_width(op.op().bits(), 2);
490     bits |= unsigned_field_width((imm & 0x1f) as u32, 5) << 2;
491     bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 7;
492     bits |= unsigned_field_width(op.funct2(), 2) << 10;
493     bits |= unsigned_field_width(((imm >> 5) & 1) as u32, 1) << 12;
494     bits |= unsigned_field_width(op.funct3(), 3) << 13;
495     bits.try_into().unwrap()
496 }
497 
498 // Encode a CSS type instruction.
499 //
500 // The imm field is a 6 bit unsigned immediate.
501 //
502 // 0--1-2-------6-7--------12-13-------15
503 // |op |   src   |    imm    |  funct3  |
encode_css_type(op: CssOp, src: Reg, imm: Uimm6) -> u16504 pub fn encode_css_type(op: CssOp, src: Reg, imm: Uimm6) -> u16 {
505     let imm = imm.bits();
506 
507     // These are the spec encoded offsets.
508     // c.swsp:  [5:2|7:6]
509     // c.sdsp:  [5:3|8:6]
510     // c.fsdsp: [5:3|8:6]
511     //
512     // We don't receive the entire offset in `imm`, just a multiple of the load-size.
513 
514     // Number of bits in the lowest position of imm. 4 for c.swsp, 3 for c.{f,}sdsp.
515     let low_bits = match op {
516         CssOp::CSwsp => 4,                 // [5:2]
517         CssOp::CSdsp | CssOp::CFsdsp => 3, // [5:3]
518     };
519     let high_bits = 6 - low_bits;
520 
521     let mut enc_imm = 0;
522     enc_imm |= (imm & ((1 << low_bits) - 1)) << high_bits;
523     enc_imm |= imm >> low_bits;
524 
525     let mut bits = 0;
526     bits |= unsigned_field_width(op.op().bits(), 2);
527     bits |= reg_to_gpr_num(src) << 2;
528     bits |= unsigned_field_width(enc_imm as u32, 6) << 7;
529     bits |= unsigned_field_width(op.funct3(), 3) << 13;
530     bits.try_into().unwrap()
531 }
532 
533 // Encode a CS type instruction.
534 //
535 // The imm field is a 5 bit unsigned immediate.
536 //
537 // 0--1-2-----4-5----------6-7---------9-10----------12-13-----15
538 // |op |  src  | imm(2-bit) |   base    |  imm(3-bit)  | funct3  |
encode_cs_type(op: CsOp, src: Reg, base: Reg, imm: Uimm5) -> u16539 pub fn encode_cs_type(op: CsOp, src: Reg, base: Reg, imm: Uimm5) -> u16 {
540     let size = match op {
541         CsOp::CFsd | CsOp::CSd => 8,
542         CsOp::CSw => 4,
543     };
544 
545     encode_cs_cl_type_bits(op.op(), op.funct3(), size, src, base, imm)
546 }
547 
548 // Encode a CL type instruction.
549 //
550 // The imm field is a 5 bit unsigned immediate.
551 //
552 // 0--1-2------4-5----------6-7---------9-10----------12-13-----15
553 // |op |  dest  | imm(2-bit) |   base    |  imm(3-bit)  | funct3  |
encode_cl_type(op: ClOp, dest: WritableReg, base: Reg, imm: Uimm5) -> u16554 pub fn encode_cl_type(op: ClOp, dest: WritableReg, base: Reg, imm: Uimm5) -> u16 {
555     let size = match op {
556         ClOp::CFld | ClOp::CLd => 8,
557         ClOp::CLw => 4,
558     };
559 
560     encode_cs_cl_type_bits(op.op(), op.funct3(), size, dest.to_reg(), base, imm)
561 }
562 
563 // CL and CS type instructions have the same physical layout.
564 //
565 // 0--1-2----------4-5----------6-7---------9-10----------12-13-----15
566 // |op |  dest/src  | imm(2-bit) |   base    |  imm(3-bit)  | funct3  |
encode_cs_cl_type_bits( op: COpcodeSpace, funct3: u32, size: u32, dest_src: Reg, base: Reg, imm: Uimm5, ) -> u16567 fn encode_cs_cl_type_bits(
568     op: COpcodeSpace,
569     funct3: u32,
570     size: u32,
571     dest_src: Reg,
572     base: Reg,
573     imm: Uimm5,
574 ) -> u16 {
575     let imm = imm.bits();
576 
577     // c.sw  / c.lw:  [2|6]
578     // c.sd  / c.ld:  [7:6]
579     // c.fsd / c.fld: [7:6]
580     //
581     // We differentiate these based on the operation size
582     let imm2 = match size {
583         4 => ((imm >> 4) & 1) | ((imm & 1) << 1),
584         8 => (imm >> 3) & 0b11,
585         _ => unreachable!(),
586     };
587 
588     // [5:3] on all opcodes
589     let imm3 = match size {
590         4 => (imm >> 1) & 0b111,
591         8 => (imm >> 0) & 0b111,
592         _ => unreachable!(),
593     };
594 
595     let mut bits = 0;
596     bits |= unsigned_field_width(op.bits(), 2);
597     bits |= reg_to_compressed_gpr_num(dest_src) << 2;
598     bits |= unsigned_field_width(imm2 as u32, 2) << 5;
599     bits |= reg_to_compressed_gpr_num(base) << 7;
600     bits |= unsigned_field_width(imm3 as u32, 3) << 10;
601     bits |= unsigned_field_width(funct3, 3) << 13;
602     bits.try_into().unwrap()
603 }
604 
605 // Encode a CSZN type instruction.
606 //
607 // This is an additional encoding format that is introduced in the Zcb extension.
608 //
609 // 0--1-2---------6-7--------9-10------15
610 // |op |   funct5  |  rd/rs1  | funct6 |
encode_cszn_type(op: CsznOp, rd: WritableReg) -> u16611 pub fn encode_cszn_type(op: CsznOp, rd: WritableReg) -> u16 {
612     let mut bits = 0;
613     bits |= unsigned_field_width(op.op().bits(), 2);
614     bits |= unsigned_field_width(op.funct5(), 5) << 2;
615     bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 7;
616     bits |= unsigned_field_width(op.funct6(), 6) << 10;
617     bits.try_into().unwrap()
618 }
619 
620 // Encodes the various memory operations in the Zcb extension.
621 //
622 // 0--1-2----------4-5----------6-7---------9-10-------15
623 // |op |  dest/src  | imm(2-bit) |   base    |  funct6  |
encode_zcbmem_bits(op: ZcbMemOp, dest_src: Reg, base: Reg, imm: Uimm2) -> u16624 fn encode_zcbmem_bits(op: ZcbMemOp, dest_src: Reg, base: Reg, imm: Uimm2) -> u16 {
625     let imm = imm.bits();
626 
627     // For these ops, bit 6 is part of the opcode, and bit 5 encodes the imm offset.
628     let imm = match op {
629         ZcbMemOp::CLh | ZcbMemOp::CLhu | ZcbMemOp::CSh => {
630             debug_assert_eq!(imm & !1, 0);
631             // Only c.lh has this bit as 1
632             let opcode_bit = (op == ZcbMemOp::CLh) as u8;
633             imm | (opcode_bit << 1)
634         }
635         // In the rest of the ops the imm is reversed.
636         _ => ((imm & 1) << 1) | ((imm >> 1) & 1),
637     };
638 
639     let mut bits = 0;
640     bits |= unsigned_field_width(op.op().bits(), 2);
641     bits |= reg_to_compressed_gpr_num(dest_src) << 2;
642     bits |= unsigned_field_width(imm as u32, 2) << 5;
643     bits |= reg_to_compressed_gpr_num(base) << 7;
644     bits |= unsigned_field_width(op.funct6(), 6) << 10;
645     bits.try_into().unwrap()
646 }
647 
encode_zcbmem_load(op: ZcbMemOp, rd: WritableReg, base: Reg, imm: Uimm2) -> u16648 pub fn encode_zcbmem_load(op: ZcbMemOp, rd: WritableReg, base: Reg, imm: Uimm2) -> u16 {
649     encode_zcbmem_bits(op, rd.to_reg(), base, imm)
650 }
651 
encode_zcbmem_store(op: ZcbMemOp, src: Reg, base: Reg, imm: Uimm2) -> u16652 pub fn encode_zcbmem_store(op: ZcbMemOp, src: Reg, base: Reg, imm: Uimm2) -> u16 {
653     encode_zcbmem_bits(op, src, base, imm)
654 }
655 
encode_fli(width: FpuOPWidth, imm: FliConstant, rd: WritableReg) -> u32656 pub fn encode_fli(width: FpuOPWidth, imm: FliConstant, rd: WritableReg) -> u32 {
657     // FLI.{H,S,D} is encoded as a FMV.{H,W,D} instruction with rs2 set to the
658     // immediate value to be loaded.
659     let op = FpuOPRR::FmvFmtX;
660     let frm = 0; // FRM is hard coded to 0 in both instructions
661     let rs2 = 1; // rs2 set to 1 is what differentiates FLI from FMV
662 
663     let mut bits = 0;
664     bits |= unsigned_field_width(op.opcode(), 7);
665     bits |= reg_to_gpr_num(rd.to_reg()) << 7;
666     bits |= unsigned_field_width(frm, 3) << 12;
667     bits |= unsigned_field_width(imm.bits() as u32, 5) << 15;
668     bits |= unsigned_field_width(rs2, 6) << 20;
669     bits |= unsigned_field_width(op.funct7(width), 7) << 25;
670     bits
671 }
672 
encode_fp_rr(op: FpuOPRR, width: FpuOPWidth, frm: FRM, rd: WritableReg, rs: Reg) -> u32673 pub fn encode_fp_rr(op: FpuOPRR, width: FpuOPWidth, frm: FRM, rd: WritableReg, rs: Reg) -> u32 {
674     encode_r_type_bits(
675         op.opcode(),
676         reg_to_gpr_num(rd.to_reg()),
677         frm.as_u32(),
678         reg_to_gpr_num(rs),
679         op.rs2(),
680         op.funct7(width),
681     )
682 }
683 
encode_fp_rrr( op: FpuOPRRR, width: FpuOPWidth, frm: FRM, rd: WritableReg, rs1: Reg, rs2: Reg, ) -> u32684 pub fn encode_fp_rrr(
685     op: FpuOPRRR,
686     width: FpuOPWidth,
687     frm: FRM,
688     rd: WritableReg,
689     rs1: Reg,
690     rs2: Reg,
691 ) -> u32 {
692     encode_r_type_bits(
693         op.opcode(),
694         reg_to_gpr_num(rd.to_reg()),
695         frm.as_u32(),
696         reg_to_gpr_num(rs1),
697         reg_to_gpr_num(rs2),
698         op.funct7(width),
699     )
700 }
701 
encode_fp_rrrr( op: FpuOPRRRR, width: FpuOPWidth, frm: FRM, rd: WritableReg, rs1: Reg, rs2: Reg, rs3: Reg, ) -> u32702 pub fn encode_fp_rrrr(
703     op: FpuOPRRRR,
704     width: FpuOPWidth,
705     frm: FRM,
706     rd: WritableReg,
707     rs1: Reg,
708     rs2: Reg,
709     rs3: Reg,
710 ) -> u32 {
711     let funct7 = (reg_to_gpr_num(rs3) << 2) | width.as_u32();
712     encode_r_type_bits(
713         op.opcode(),
714         reg_to_gpr_num(rd.to_reg()),
715         frm.as_u32(),
716         reg_to_gpr_num(rs1),
717         reg_to_gpr_num(rs2),
718         funct7,
719     )
720 }
721