Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/isa/riscv64/inst/encode.rs
1693 views
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
16
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 |
24
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.
36
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] |
57
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.
68
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] |
83
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
106
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
137
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
156
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
174
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
193
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
212
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.
239
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
254
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
293
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.
310
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.
316
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 |
330
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
343
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 |
351
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 |
367
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 |
396
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.
410
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
444
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 |
463
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 |
485
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 |
504
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 |
539
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 |
554
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 |
567
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 |
611
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 |
624
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
648
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
652
pub fn encode_zcbmem_store(op: ZcbMemOp, src: Reg, base: Reg, imm: Uimm2) -> u16 {
653
encode_zcbmem_bits(op, src, base, imm)
654
}
655
656
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
673
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
684
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
702
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
722