cranelift_codegen/isa/riscv64/inst/
encode.rs

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
9use super::*;
10use 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};
14use crate::machinst::isle::WritableReg;
15
16fn 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  |
24fn 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.
36pub 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]    |
57fn 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.
68pub 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]   |
83pub 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
106pub 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
137pub 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
156pub 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
174pub 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
193pub 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
212pub 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.
239pub 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
254pub 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
293pub 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.
310pub 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.
316pub 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  |
330pub 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
343pub 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 |
351pub 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  |
367pub 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  |
396pub 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.
410pub 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
444pub 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  |
463pub 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 |
485pub 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  |
504pub 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  |
539pub 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  |
554pub 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  |
567fn 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 |
611pub 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  |
624fn 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
648pub 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
652pub fn encode_zcbmem_store(op: ZcbMemOp, src: Reg, base: Reg, imm: Uimm2) -> u16 {
653    encode_zcbmem_bits(op, src, base, imm)
654}
655
656pub fn encode_fli(ty: Type, imm: FliConstant, rd: WritableReg) -> u32 {
657    // FLI.{S,D} is encoded as a FMV.{W,D} instruction with rs2 set to the
658    // immediate value to be loaded.
659    let op = FpuOPRR::FmvFmtX;
660    let width = FpuOPWidth::try_from(ty).unwrap();
661    let frm = 0; // FRM is hard coded to 0 in both instructions
662    let rs2 = 1; // rs2 set to 1 is what differentiates FLI from FMV
663
664    let mut bits = 0;
665    bits |= unsigned_field_width(op.opcode(), 7);
666    bits |= reg_to_gpr_num(rd.to_reg()) << 7;
667    bits |= unsigned_field_width(frm, 3) << 12;
668    bits |= unsigned_field_width(imm.bits() as u32, 5) << 15;
669    bits |= unsigned_field_width(rs2, 6) << 20;
670    bits |= unsigned_field_width(op.funct7(width), 7) << 25;
671    bits
672}
673
674pub fn encode_fp_rr(op: FpuOPRR, width: FpuOPWidth, frm: FRM, rd: WritableReg, rs: Reg) -> u32 {
675    encode_r_type_bits(
676        op.opcode(),
677        reg_to_gpr_num(rd.to_reg()),
678        frm.as_u32(),
679        reg_to_gpr_num(rs),
680        op.rs2(),
681        op.funct7(width),
682    )
683}
684
685pub fn encode_fp_rrr(
686    op: FpuOPRRR,
687    width: FpuOPWidth,
688    frm: FRM,
689    rd: WritableReg,
690    rs1: Reg,
691    rs2: Reg,
692) -> u32 {
693    encode_r_type_bits(
694        op.opcode(),
695        reg_to_gpr_num(rd.to_reg()),
696        frm.as_u32(),
697        reg_to_gpr_num(rs1),
698        reg_to_gpr_num(rs2),
699        op.funct7(width),
700    )
701}
702
703pub fn encode_fp_rrrr(
704    op: FpuOPRRRR,
705    width: FpuOPWidth,
706    frm: FRM,
707    rd: WritableReg,
708    rs1: Reg,
709    rs2: Reg,
710    rs3: Reg,
711) -> u32 {
712    let funct7 = (reg_to_gpr_num(rs3) << 2) | width.as_u32();
713    encode_r_type_bits(
714        op.opcode(),
715        reg_to_gpr_num(rd.to_reg()),
716        frm.as_u32(),
717        reg_to_gpr_num(rs1),
718        reg_to_gpr_num(rs2),
719        funct7,
720    )
721}