Path: blob/main/cranelift/codegen/src/isa/riscv64/inst/encode.rs
1693 views
//! Contains the RISC-V instruction encoding logic.1//!2//! These formats are specified in the RISC-V specification in section 2.2.3//! See: <https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf>4//!5//! Some instructions especially in extensions have slight variations from6//! the base RISC-V specification.78use super::*;9use crate::isa::riscv64::lower::isle::generated_code::{10COpcodeSpace, CaOp, CbOp, CiOp, CiwOp, ClOp, CrOp, CsOp, CssOp, CsznOp, FpuOPWidth,11VecAluOpRImm5, VecAluOpRR, VecAluOpRRRImm5, VecAluOpRRRR, VecOpCategory, ZcbMemOp,12};13use crate::machinst::isle::WritableReg;1415fn unsigned_field_width(value: u32, width: u8) -> u32 {16debug_assert_eq!(value & (!0 << width), 0);17value18}1920/// Layout:21/// 0-------6-7-------11-12------14-15------19-20------24-25-------3122/// | Opcode | rd | funct3 | rs1 | rs2 | funct7 |23fn encode_r_type_bits(opcode: u32, rd: u32, funct3: u32, rs1: u32, rs2: u32, funct7: u32) -> u32 {24let mut bits = 0;25bits |= unsigned_field_width(opcode, 7);26bits |= unsigned_field_width(rd, 5) << 7;27bits |= unsigned_field_width(funct3, 3) << 12;28bits |= unsigned_field_width(rs1, 5) << 15;29bits |= unsigned_field_width(rs2, 5) << 20;30bits |= unsigned_field_width(funct7, 7) << 25;31bits32}3334/// Encode an R-type instruction.35pub fn encode_r_type(36opcode: u32,37rd: WritableReg,38funct3: u32,39rs1: Reg,40rs2: Reg,41funct7: u32,42) -> u32 {43encode_r_type_bits(44opcode,45reg_to_gpr_num(rd.to_reg()),46funct3,47reg_to_gpr_num(rs1),48reg_to_gpr_num(rs2),49funct7,50)51}5253/// Layout:54/// 0-------6-7-------11-12------14-15------19-20------------------3155/// | Opcode | rd | width | rs1 | Offset[11:0] |56fn encode_i_type_bits(opcode: u32, rd: u32, funct3: u32, rs1: u32, offset: u32) -> u32 {57let mut bits = 0;58bits |= unsigned_field_width(opcode, 7);59bits |= unsigned_field_width(rd, 5) << 7;60bits |= unsigned_field_width(funct3, 3) << 12;61bits |= unsigned_field_width(rs1, 5) << 15;62bits |= unsigned_field_width(offset, 12) << 20;63bits64}6566/// Encode an I-type instruction.67pub fn encode_i_type(opcode: u32, rd: WritableReg, width: u32, rs1: Reg, offset: Imm12) -> u32 {68encode_i_type_bits(69opcode,70reg_to_gpr_num(rd.to_reg()),71width,72reg_to_gpr_num(rs1),73offset.bits(),74)75}7677/// Encode an S-type instruction.78///79/// Layout:80/// 0-------6-7-------11-12------14-15------19-20---24-25-------------3181/// | Opcode | imm[4:0] | width | base | src | imm[11:5] |82pub fn encode_s_type(opcode: u32, width: u32, base: Reg, src: Reg, offset: Imm12) -> u32 {83let mut bits = 0;84bits |= unsigned_field_width(opcode, 7);85bits |= (offset.bits() & 0b11111) << 7;86bits |= unsigned_field_width(width, 3) << 12;87bits |= reg_to_gpr_num(base) << 15;88bits |= reg_to_gpr_num(src) << 20;89bits |= unsigned_field_width(offset.bits() >> 5, 7) << 25;90bits91}9293/// Encodes a Vector ALU instruction.94///95/// Fields:96/// - opcode (7 bits)97/// - vd (5 bits)98/// - funct3 (3 bits)99/// - vs1 (5 bits)100/// - vs2 (5 bits)101/// - vm (1 bit)102/// - funct6 (6 bits)103///104/// See: https://github.com/riscv/riscv-v-spec/blob/master/valu-format.adoc105pub fn encode_valu(106op: VecAluOpRRR,107vd: WritableReg,108vs1: Reg,109vs2: Reg,110masking: VecOpMasking,111) -> u32 {112let funct7 = (op.funct6() << 1) | masking.encode();113encode_r_type_bits(114op.opcode(),115reg_to_gpr_num(vd.to_reg()),116op.funct3(),117reg_to_gpr_num(vs1),118reg_to_gpr_num(vs2),119funct7,120)121}122123/// Encodes a Vector ALU+Imm instruction.124/// This is just a Vector ALU instruction with an immediate in the VS1 field.125///126/// Fields:127/// - opcode (7 bits)128/// - vd (5 bits)129/// - funct3 (3 bits)130/// - imm (5 bits)131/// - vs2 (5 bits)132/// - vm (1 bit)133/// - funct6 (6 bits)134///135/// See: https://github.com/riscv/riscv-v-spec/blob/master/valu-format.adoc136pub fn encode_valu_rr_imm(137op: VecAluOpRRImm5,138vd: WritableReg,139imm: Imm5,140vs2: Reg,141masking: VecOpMasking,142) -> u32 {143let funct7 = (op.funct6() << 1) | masking.encode();144let imm = imm.bits() as u32;145encode_r_type_bits(146op.opcode(),147reg_to_gpr_num(vd.to_reg()),148op.funct3(),149imm,150reg_to_gpr_num(vs2),151funct7,152)153}154155pub fn encode_valu_rrrr(156op: VecAluOpRRRR,157vd: WritableReg,158vs2: Reg,159vs1: Reg,160masking: VecOpMasking,161) -> u32 {162let funct7 = (op.funct6() << 1) | masking.encode();163encode_r_type_bits(164op.opcode(),165reg_to_gpr_num(vd.to_reg()),166op.funct3(),167reg_to_gpr_num(vs1),168reg_to_gpr_num(vs2),169funct7,170)171}172173pub fn encode_valu_rrr_imm(174op: VecAluOpRRRImm5,175vd: WritableReg,176imm: Imm5,177vs2: Reg,178masking: VecOpMasking,179) -> u32 {180let funct7 = (op.funct6() << 1) | masking.encode();181let imm = imm.bits() as u32;182encode_r_type_bits(183op.opcode(),184reg_to_gpr_num(vd.to_reg()),185op.funct3(),186imm,187reg_to_gpr_num(vs2),188funct7,189)190}191192pub fn encode_valu_rr(op: VecAluOpRR, vd: WritableReg, vs: Reg, masking: VecOpMasking) -> u32 {193let funct7 = (op.funct6() << 1) | masking.encode();194195let (vs1, vs2) = if op.vs_is_vs2_encoded() {196(op.aux_encoding(), reg_to_gpr_num(vs))197} else {198(reg_to_gpr_num(vs), op.aux_encoding())199};200201encode_r_type_bits(202op.opcode(),203reg_to_gpr_num(vd.to_reg()),204op.funct3(),205vs1,206vs2,207funct7,208)209}210211pub fn encode_valu_r_imm(212op: VecAluOpRImm5,213vd: WritableReg,214imm: Imm5,215masking: VecOpMasking,216) -> u32 {217let funct7 = (op.funct6() << 1) | masking.encode();218219// This is true for this opcode, not sure if there are any other ones.220debug_assert_eq!(op, VecAluOpRImm5::VmvVI);221let vs1 = imm.bits() as u32;222let vs2 = op.aux_encoding();223224encode_r_type_bits(225op.opcode(),226reg_to_gpr_num(vd.to_reg()),227op.funct3(),228vs1,229vs2,230funct7,231)232}233234/// Encodes a Vector CFG Imm instruction.235///236/// See: https://github.com/riscv/riscv-v-spec/blob/master/vcfg-format.adoc237// TODO: Check if this is any of the known instruction types in the spec.238pub fn encode_vcfg_imm(opcode: u32, rd: Reg, imm: UImm5, vtype: &VType) -> u32 {239let mut bits = 0;240bits |= unsigned_field_width(opcode, 7);241bits |= reg_to_gpr_num(rd) << 7;242bits |= VecOpCategory::OPCFG.encode() << 12;243bits |= unsigned_field_width(imm.bits(), 5) << 15;244bits |= unsigned_field_width(vtype.encode(), 10) << 20;245bits |= 0b11 << 30;246bits247}248249/// Encodes a Vector Mem Unit Stride Load instruction.250///251/// See: https://github.com/riscv/riscv-v-spec/blob/master/vmem-format.adoc252/// TODO: These instructions share opcode space with LOAD-FP and STORE-FP253pub fn encode_vmem_load(254opcode: u32,255vd: Reg,256width: VecElementWidth,257rs1: Reg,258lumop: u32,259masking: VecOpMasking,260mop: u32,261nf: u32,262) -> u32 {263// Width is encoded differently to avoid a clash with the FP load/store sizes.264let width = match width {265VecElementWidth::E8 => 0b000,266VecElementWidth::E16 => 0b101,267VecElementWidth::E32 => 0b110,268VecElementWidth::E64 => 0b111,269};270271let mut bits = 0;272bits |= unsigned_field_width(opcode, 7);273bits |= reg_to_gpr_num(vd) << 7;274bits |= width << 12;275bits |= reg_to_gpr_num(rs1) << 15;276bits |= unsigned_field_width(lumop, 5) << 20;277bits |= masking.encode() << 25;278bits |= unsigned_field_width(mop, 2) << 26;279280// The mew bit (inst[28]) when set is expected to be used to encode expanded281// memory sizes of 128 bits and above, but these encodings are currently reserved.282bits |= 0b0 << 28;283284bits |= unsigned_field_width(nf, 3) << 29;285bits286}287288/// Encodes a Vector Mem Unit Stride Load instruction.289///290/// See: https://github.com/riscv/riscv-v-spec/blob/master/vmem-format.adoc291/// TODO: These instructions share opcode space with LOAD-FP and STORE-FP292pub fn encode_vmem_store(293opcode: u32,294vs3: Reg,295width: VecElementWidth,296rs1: Reg,297sumop: u32,298masking: VecOpMasking,299mop: u32,300nf: u32,301) -> u32 {302// This is pretty much the same as the load instruction, just303// with different names on the fields.304encode_vmem_load(opcode, vs3, width, rs1, sumop, masking, mop, nf)305}306307// The CSR Reg instruction is really just an I type instruction with the CSR in308// the immediate field.309pub fn encode_csr_reg(op: CsrRegOP, rd: WritableReg, rs: Reg, csr: CSR) -> u32 {310encode_i_type(op.opcode(), rd, op.funct3(), rs, csr.bits())311}312313// The CSR Imm instruction is an I type instruction with the CSR in314// the immediate field and the value to be set in the `rs1` field.315pub fn encode_csr_imm(op: CsrImmOP, rd: WritableReg, csr: CSR, imm: UImm5) -> u32 {316encode_i_type_bits(317op.opcode(),318reg_to_gpr_num(rd.to_reg()),319op.funct3(),320imm.bits(),321csr.bits().bits(),322)323}324325// Encode a CR type instruction.326//327// 0--1-2-----6-7-------11-12-------15328// |op | rs2 | rd/rs1 | funct4 |329pub fn encode_cr_type(op: CrOp, rd: WritableReg, rs2: Reg) -> u16 {330let mut bits = 0;331bits |= unsigned_field_width(op.op().bits(), 2);332bits |= reg_to_gpr_num(rs2) << 2;333bits |= reg_to_gpr_num(rd.to_reg()) << 7;334bits |= unsigned_field_width(op.funct4(), 4) << 12;335bits.try_into().unwrap()336}337338// This isn't technically a instruction format that exists. It's just a CR type339// where the source is rs1, rs2 is zero. rs1 is never written to.340//341// Used for C.JR and C.JALR342pub fn encode_cr2_type(op: CrOp, rs1: Reg) -> u16 {343encode_cr_type(op, WritableReg::from_reg(rs1), zero_reg())344}345346// Encode a CA type instruction.347//348// 0--1-2-----4-5--------6-7--------9-10------15349// |op | rs2 | funct2 | rd/rs1 | funct6 |350pub fn encode_ca_type(op: CaOp, rd: WritableReg, rs2: Reg) -> u16 {351let mut bits = 0;352bits |= unsigned_field_width(op.op().bits(), 2);353bits |= reg_to_compressed_gpr_num(rs2) << 2;354bits |= unsigned_field_width(op.funct2(), 2) << 5;355bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 7;356bits |= unsigned_field_width(op.funct6(), 6) << 10;357bits.try_into().unwrap()358}359360// Encode a CJ type instruction.361//362// The imm field is a 11 bit signed immediate that is shifted left by 1.363//364// 0--1-2-----12-13--------15365// |op | imm | funct3 |366pub fn encode_cj_type(op: CjOp, imm: Imm12) -> u16 {367let imm = imm.bits();368debug_assert!(imm & 1 == 0);369370// The offset bits are in rather weird positions.371// [11|4|9:8|10|6|7|3:1|5]372let mut imm_field = 0;373imm_field |= ((imm >> 11) & 1) << 10;374imm_field |= ((imm >> 4) & 1) << 9;375imm_field |= ((imm >> 8) & 3) << 7;376imm_field |= ((imm >> 10) & 1) << 6;377imm_field |= ((imm >> 6) & 1) << 5;378imm_field |= ((imm >> 7) & 1) << 4;379imm_field |= ((imm >> 1) & 7) << 1;380imm_field |= ((imm >> 5) & 1) << 0;381382let mut bits = 0;383bits |= unsigned_field_width(op.op().bits(), 2);384bits |= unsigned_field_width(imm_field, 11) << 2;385bits |= unsigned_field_width(op.funct3(), 3) << 13;386bits.try_into().unwrap()387}388389// Encode a CI type instruction.390//391// The imm field is a 6 bit signed immediate.392//393// 0--1-2-------6-7-------11-12-----12-13-----15394// |op | imm[4:0] | src | imm[5] | funct3 |395pub fn encode_ci_type(op: CiOp, rd: WritableReg, imm: Imm6) -> u16 {396let imm = imm.bits();397398let mut bits = 0;399bits |= unsigned_field_width(op.op().bits(), 2);400bits |= unsigned_field_width((imm & 0x1f) as u32, 5) << 2;401bits |= reg_to_gpr_num(rd.to_reg()) << 7;402bits |= unsigned_field_width(((imm >> 5) & 1) as u32, 1) << 12;403bits |= unsigned_field_width(op.funct3(), 3) << 13;404bits.try_into().unwrap()405}406407// Stack-Pointer relative loads are regular CI instructions, but, the immediate408// is zero extended, and with a slightly different immediate field encoding.409pub fn encode_ci_sp_load(op: CiOp, rd: WritableReg, imm: Uimm6) -> u16 {410let imm = imm.bits();411412// These are the spec encoded offsets.413// LWSP: [5|4:2|7:6]414// LDSP: [5|4:3|8:6]415// FLDSP: [5|4:3|8:6]416//417// We don't receive the entire offset in `imm`, just a multiple of the load-size.418419// Number of bits in the lowest position of imm. 3 for lwsp, 2 for {f,}ldsp.420let low_bits = match op {421CiOp::CLwsp => 3, // [4:2]422CiOp::CLdsp | CiOp::CFldsp => 2, // [4:3]423_ => unreachable!(),424};425let high_bits = 6 - 1 - low_bits;426let mut enc_imm = 0;427428// Encode [7:6] at the bottom of imm429enc_imm |= imm >> (6 - high_bits);430431// Next place [4:2] in the middle432enc_imm |= (imm & ((1 << low_bits) - 1)) << high_bits;433434// Finally place [5] at the top435enc_imm |= ((imm >> low_bits) & 1) << 5;436437let enc_imm = Imm6::maybe_from_i16((enc_imm as i16) << 10 >> 10).unwrap();438439encode_ci_type(op, rd, enc_imm)440}441442/// c.addi16sp is a regular CI op, but the immediate field is encoded in a weird way443pub fn encode_c_addi16sp(imm: Imm6) -> u16 {444let imm = imm.bits();445446// [6|1|3|5:4|2]447let mut enc_imm = 0;448enc_imm |= ((imm >> 5) & 1) << 5;449enc_imm |= ((imm >> 0) & 1) << 4;450enc_imm |= ((imm >> 2) & 1) << 3;451enc_imm |= ((imm >> 3) & 3) << 1;452enc_imm |= ((imm >> 1) & 1) << 0;453let enc_imm = Imm6::maybe_from_i16((enc_imm as i16) << 10 >> 10).unwrap();454455encode_ci_type(CiOp::CAddi16sp, writable_stack_reg(), enc_imm)456}457458// Encode a CIW type instruction.459//460// 0--1-2------4-5------12-13--------15461// |op | rd | imm | funct3 |462pub fn encode_ciw_type(op: CiwOp, rd: WritableReg, imm: u8) -> u16 {463// [3:2|7:4|0|1]464let mut imm_field = 0;465imm_field |= ((imm >> 1) & 1) << 0;466imm_field |= ((imm >> 0) & 1) << 1;467imm_field |= ((imm >> 4) & 15) << 2;468imm_field |= ((imm >> 2) & 3) << 6;469470let mut bits = 0;471bits |= unsigned_field_width(op.op().bits(), 2);472bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 2;473bits |= unsigned_field_width(imm_field as u32, 8) << 5;474bits |= unsigned_field_width(op.funct3(), 3) << 13;475bits.try_into().unwrap()476}477478// Encode a CB type instruction.479//480// The imm field is a 6 bit signed immediate.481//482// 0--1-2-------6-7-------9-10-------11-12-------13--------15483// |op | imm[4:0] | dst | funct2 | imm[5] | funct3 |484pub fn encode_cb_type(op: CbOp, rd: WritableReg, imm: Imm6) -> u16 {485let imm = imm.bits();486487let mut bits = 0;488bits |= unsigned_field_width(op.op().bits(), 2);489bits |= unsigned_field_width((imm & 0x1f) as u32, 5) << 2;490bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 7;491bits |= unsigned_field_width(op.funct2(), 2) << 10;492bits |= unsigned_field_width(((imm >> 5) & 1) as u32, 1) << 12;493bits |= unsigned_field_width(op.funct3(), 3) << 13;494bits.try_into().unwrap()495}496497// Encode a CSS type instruction.498//499// The imm field is a 6 bit unsigned immediate.500//501// 0--1-2-------6-7--------12-13-------15502// |op | src | imm | funct3 |503pub fn encode_css_type(op: CssOp, src: Reg, imm: Uimm6) -> u16 {504let imm = imm.bits();505506// These are the spec encoded offsets.507// c.swsp: [5:2|7:6]508// c.sdsp: [5:3|8:6]509// c.fsdsp: [5:3|8:6]510//511// We don't receive the entire offset in `imm`, just a multiple of the load-size.512513// Number of bits in the lowest position of imm. 4 for c.swsp, 3 for c.{f,}sdsp.514let low_bits = match op {515CssOp::CSwsp => 4, // [5:2]516CssOp::CSdsp | CssOp::CFsdsp => 3, // [5:3]517};518let high_bits = 6 - low_bits;519520let mut enc_imm = 0;521enc_imm |= (imm & ((1 << low_bits) - 1)) << high_bits;522enc_imm |= imm >> low_bits;523524let mut bits = 0;525bits |= unsigned_field_width(op.op().bits(), 2);526bits |= reg_to_gpr_num(src) << 2;527bits |= unsigned_field_width(enc_imm as u32, 6) << 7;528bits |= unsigned_field_width(op.funct3(), 3) << 13;529bits.try_into().unwrap()530}531532// Encode a CS type instruction.533//534// The imm field is a 5 bit unsigned immediate.535//536// 0--1-2-----4-5----------6-7---------9-10----------12-13-----15537// |op | src | imm(2-bit) | base | imm(3-bit) | funct3 |538pub fn encode_cs_type(op: CsOp, src: Reg, base: Reg, imm: Uimm5) -> u16 {539let size = match op {540CsOp::CFsd | CsOp::CSd => 8,541CsOp::CSw => 4,542};543544encode_cs_cl_type_bits(op.op(), op.funct3(), size, src, base, imm)545}546547// Encode a CL type instruction.548//549// The imm field is a 5 bit unsigned immediate.550//551// 0--1-2------4-5----------6-7---------9-10----------12-13-----15552// |op | dest | imm(2-bit) | base | imm(3-bit) | funct3 |553pub fn encode_cl_type(op: ClOp, dest: WritableReg, base: Reg, imm: Uimm5) -> u16 {554let size = match op {555ClOp::CFld | ClOp::CLd => 8,556ClOp::CLw => 4,557};558559encode_cs_cl_type_bits(op.op(), op.funct3(), size, dest.to_reg(), base, imm)560}561562// CL and CS type instructions have the same physical layout.563//564// 0--1-2----------4-5----------6-7---------9-10----------12-13-----15565// |op | dest/src | imm(2-bit) | base | imm(3-bit) | funct3 |566fn encode_cs_cl_type_bits(567op: COpcodeSpace,568funct3: u32,569size: u32,570dest_src: Reg,571base: Reg,572imm: Uimm5,573) -> u16 {574let imm = imm.bits();575576// c.sw / c.lw: [2|6]577// c.sd / c.ld: [7:6]578// c.fsd / c.fld: [7:6]579//580// We differentiate these based on the operation size581let imm2 = match size {5824 => ((imm >> 4) & 1) | ((imm & 1) << 1),5838 => (imm >> 3) & 0b11,584_ => unreachable!(),585};586587// [5:3] on all opcodes588let imm3 = match size {5894 => (imm >> 1) & 0b111,5908 => (imm >> 0) & 0b111,591_ => unreachable!(),592};593594let mut bits = 0;595bits |= unsigned_field_width(op.bits(), 2);596bits |= reg_to_compressed_gpr_num(dest_src) << 2;597bits |= unsigned_field_width(imm2 as u32, 2) << 5;598bits |= reg_to_compressed_gpr_num(base) << 7;599bits |= unsigned_field_width(imm3 as u32, 3) << 10;600bits |= unsigned_field_width(funct3, 3) << 13;601bits.try_into().unwrap()602}603604// Encode a CSZN type instruction.605//606// This is an additional encoding format that is introduced in the Zcb extension.607//608// 0--1-2---------6-7--------9-10------15609// |op | funct5 | rd/rs1 | funct6 |610pub fn encode_cszn_type(op: CsznOp, rd: WritableReg) -> u16 {611let mut bits = 0;612bits |= unsigned_field_width(op.op().bits(), 2);613bits |= unsigned_field_width(op.funct5(), 5) << 2;614bits |= reg_to_compressed_gpr_num(rd.to_reg()) << 7;615bits |= unsigned_field_width(op.funct6(), 6) << 10;616bits.try_into().unwrap()617}618619// Encodes the various memory operations in the Zcb extension.620//621// 0--1-2----------4-5----------6-7---------9-10-------15622// |op | dest/src | imm(2-bit) | base | funct6 |623fn encode_zcbmem_bits(op: ZcbMemOp, dest_src: Reg, base: Reg, imm: Uimm2) -> u16 {624let imm = imm.bits();625626// For these ops, bit 6 is part of the opcode, and bit 5 encodes the imm offset.627let imm = match op {628ZcbMemOp::CLh | ZcbMemOp::CLhu | ZcbMemOp::CSh => {629debug_assert_eq!(imm & !1, 0);630// Only c.lh has this bit as 1631let opcode_bit = (op == ZcbMemOp::CLh) as u8;632imm | (opcode_bit << 1)633}634// In the rest of the ops the imm is reversed.635_ => ((imm & 1) << 1) | ((imm >> 1) & 1),636};637638let mut bits = 0;639bits |= unsigned_field_width(op.op().bits(), 2);640bits |= reg_to_compressed_gpr_num(dest_src) << 2;641bits |= unsigned_field_width(imm as u32, 2) << 5;642bits |= reg_to_compressed_gpr_num(base) << 7;643bits |= unsigned_field_width(op.funct6(), 6) << 10;644bits.try_into().unwrap()645}646647pub fn encode_zcbmem_load(op: ZcbMemOp, rd: WritableReg, base: Reg, imm: Uimm2) -> u16 {648encode_zcbmem_bits(op, rd.to_reg(), base, imm)649}650651pub fn encode_zcbmem_store(op: ZcbMemOp, src: Reg, base: Reg, imm: Uimm2) -> u16 {652encode_zcbmem_bits(op, src, base, imm)653}654655pub fn encode_fli(width: FpuOPWidth, imm: FliConstant, rd: WritableReg) -> u32 {656// FLI.{H,S,D} is encoded as a FMV.{H,W,D} instruction with rs2 set to the657// immediate value to be loaded.658let op = FpuOPRR::FmvFmtX;659let frm = 0; // FRM is hard coded to 0 in both instructions660let rs2 = 1; // rs2 set to 1 is what differentiates FLI from FMV661662let mut bits = 0;663bits |= unsigned_field_width(op.opcode(), 7);664bits |= reg_to_gpr_num(rd.to_reg()) << 7;665bits |= unsigned_field_width(frm, 3) << 12;666bits |= unsigned_field_width(imm.bits() as u32, 5) << 15;667bits |= unsigned_field_width(rs2, 6) << 20;668bits |= unsigned_field_width(op.funct7(width), 7) << 25;669bits670}671672pub fn encode_fp_rr(op: FpuOPRR, width: FpuOPWidth, frm: FRM, rd: WritableReg, rs: Reg) -> u32 {673encode_r_type_bits(674op.opcode(),675reg_to_gpr_num(rd.to_reg()),676frm.as_u32(),677reg_to_gpr_num(rs),678op.rs2(),679op.funct7(width),680)681}682683pub fn encode_fp_rrr(684op: FpuOPRRR,685width: FpuOPWidth,686frm: FRM,687rd: WritableReg,688rs1: Reg,689rs2: Reg,690) -> u32 {691encode_r_type_bits(692op.opcode(),693reg_to_gpr_num(rd.to_reg()),694frm.as_u32(),695reg_to_gpr_num(rs1),696reg_to_gpr_num(rs2),697op.funct7(width),698)699}700701pub fn encode_fp_rrrr(702op: FpuOPRRRR,703width: FpuOPWidth,704frm: FRM,705rd: WritableReg,706rs1: Reg,707rs2: Reg,708rs3: Reg,709) -> u32 {710let funct7 = (reg_to_gpr_num(rs3) << 2) | width.as_u32();711encode_r_type_bits(712op.opcode(),713reg_to_gpr_num(rd.to_reg()),714frm.as_u32(),715reg_to_gpr_num(rs1),716reg_to_gpr_num(rs2),717funct7,718)719}720721722