rvv_encode/
lib.rs

1#[macro_use]
2extern crate pest_derive;
3
4use anyhow::{anyhow, Error};
5use pest::Parser;
6
7#[allow(dead_code, clippy::type_complexity)]
8mod opcodes;
9
10mod asm_parser {
11    #[derive(Parser)]
12    #[grammar = "asm.pest"]
13    pub(crate) struct AsmParser;
14}
15use asm_parser::{AsmParser, Rule};
16
17// The last register must be `v0`
18const V0_TAIL_INSTS: [&str; 14] = [
19    "vfmerge.vfm",
20    "vadc.vxm",
21    "vmadc.vxm",
22    "vsbc.vxm",
23    "vmsbc.vxm",
24    "vmerge.vxm",
25    "vadc.vvm",
26    "vmadc.vvm",
27    "vsbc.vvm",
28    "vmsbc.vvm",
29    "vmerge.vvm",
30    "vadc.vim",
31    "vmadc.vim",
32    "vmerge.vim",
33];
34
35// https://github.com/riscv/riscv-v-spec/blob/master/v-spec.adoc#101-vector-arithmetic-instruction-encoding
36//
37// NOTE: For ternary multiply-add operations, the assembler syntax always places the
38// destination vector register first, followed by either rs1 or vs1, then vs2.
39// This ordering provides a more natural reading of the assembler for these
40// ternary operations, as the multiply operands are always next to each other.
41const VV_TERNARY_INSTS: [&str; 19] = [
42    "vmacc.vv",
43    "vnmsac.vv",
44    "vmadd.vv",
45    "vnmsub.vv",
46    "vwmaccu.vv",
47    "vwmacc.vv",
48    "vwmaccsu.vv",
49    "vfmacc.vv",
50    "vfnmacc.vv",
51    "vfmsac.vv",
52    "vfnmsac.vv",
53    "vfmadd.vv",
54    "vfnmadd.vv",
55    "vfmsub.vv",
56    "vfnmsub.vv",
57    "vfwmacc.vv",
58    "vfwnmacc.vv",
59    "vfwmsac.vv",
60    "vfwnmsac.vv",
61];
62const VX_VF_TERNARY_INSTS: [&str; 20] = [
63    "vmacc.vx",
64    "vnmsac.vx",
65    "vmadd.vx",
66    "vnmsub.vx",
67    "vwmaccu.vx",
68    "vwmacc.vx",
69    "vwmaccsu.vx",
70    "vwmaccus.vx",
71    "vfmacc.vf",
72    "vfnmacc.vf",
73    "vfmsac.vf",
74    "vfnmsac.vf",
75    "vfmadd.vf",
76    "vfnmadd.vf",
77    "vfmsub.vf",
78    "vfnmsub.vf",
79    "vfwmacc.vf",
80    "vfwnmacc.vf",
81    "vfwmsac.vf",
82    "vfwnmsac.vf",
83];
84
85/// Convert one RISC-V Vector Extension(RVV) instruction into code.
86///
87/// This function try to parse the instruction as normal asm instruction or
88/// standalone label ("1:"/"label:"). If it's a valid RVV instruction, it will
89/// try to encode it to target code. Otherwise, if it's a valid asm instruction
90/// it will return `Ok(None)`. If parse or encode failed, it will return error.
91///
92/// ## Example:
93/// ```
94/// assert_eq!(rvv_encode::encode("vle64.v v3, (a0), v0.t", false).unwrap(), Some(0b00000000000001010111000110000111));
95/// ```
96pub fn encode(inst: &str, reserved_only: bool) -> Result<Option<u32>, Error> {
97    let pairs = if let Ok(result) = AsmParser::parse(Rule::inst, inst.trim()) {
98        result
99    } else {
100        let _ = AsmParser::parse(Rule::label, inst.trim())
101            .map_err(|err| anyhow!("parse asm error: {}", err))?;
102        return Ok(None);
103    };
104    let mut name = "unknown";
105    let mut args = Vec::new();
106    for pair in pairs.clone() {
107        for inner1_pair in pair.into_inner() {
108            match inner1_pair.as_rule() {
109                Rule::inst_name => {
110                    name = inner1_pair.as_str();
111                }
112                Rule::inst_arg => {
113                    for inner2_pair in inner1_pair.into_inner() {
114                        match inner2_pair.as_rule() {
115                            Rule::inst_arg_mask | Rule::inst_arg_simple | Rule::integer => {
116                                args.push(inner2_pair.as_str());
117                            }
118                            _ => {
119                                return Ok(None);
120                            }
121                        }
122                    }
123                }
124                _ => {
125                    return Ok(None);
126                }
127            }
128        }
129    }
130    if reserved_only {
131        let mut is_reserved = false;
132        'outer: for width in [128, 256, 512, 1024] {
133            for prefix in ["i", "e"] {
134                let part = format!("{}{}", prefix, width);
135                if name.contains(part.as_str()) {
136                    is_reserved = true;
137                    break 'outer;
138                }
139                for arg in &args {
140                    if arg.contains(part.as_str()) {
141                        is_reserved = true;
142                        break 'outer;
143                    }
144                }
145            }
146        }
147        if !is_reserved {
148            return Ok(None);
149        }
150    }
151
152    opcodes::INSTRUCTIONS
153        .iter()
154        .find(|(inst_name, _, _)| *inst_name == name)
155        .map(|(_, base, args_cfg)| gen_inst_code(name, &args, *base, args_cfg))
156        .transpose()
157}
158
159fn gen_inst_code(
160    name: &str,
161    args: &[&str],
162    mut base: u32,
163    arg_cfg: &[(&str, usize)],
164) -> Result<u32, Error> {
165    // The order of `simm5`/`vs1` and `vs2` in opcodes-rvv is not the same with v-spec.adoc
166    let simm5_idx = arg_cfg.iter().position(|(name, _)| *name == "simm5");
167    let vs1_idx = arg_cfg.iter().position(|(name, _)| *name == "vs1");
168    let rs1_idx = arg_cfg.iter().position(|(name, _)| *name == "rs1");
169    let vs2_idx = arg_cfg.iter().position(|(name, _)| *name == "vs2");
170    let last_7bits = base & 0b1111111;
171    let mut arg_cfg_vec = arg_cfg.iter().collect::<Vec<_>>();
172    if let (Some(simm5_idx), Some(vs2_idx), 0x57) = (simm5_idx, vs2_idx, last_7bits) {
173        arg_cfg_vec.swap(simm5_idx, vs2_idx);
174    }
175    if let (Some(vs1_idx), Some(vs2_idx), 0x57) = (vs1_idx, vs2_idx, last_7bits) {
176        if !VV_TERNARY_INSTS.contains(&name) {
177            arg_cfg_vec.swap(vs1_idx, vs2_idx);
178        }
179    }
180    if let (Some(rs1_idx), Some(vs2_idx), 0x57) = (rs1_idx, vs2_idx, last_7bits) {
181        if !VX_VF_TERNARY_INSTS.contains(&name) {
182            arg_cfg_vec.swap(rs1_idx, vs2_idx);
183        }
184    }
185    let arg_cfg_final = &arg_cfg_vec;
186
187    let has_vm = arg_cfg_final.iter().any(|(arg_name, _)| *arg_name == "vm");
188    let has_nf = arg_cfg_final.iter().any(|(arg_name, _)| *arg_name == "nf");
189    let number = if has_nf && has_vm {
190        arg_cfg_final.len() - 2
191    } else if has_vm {
192        arg_cfg_final.len() - 1
193    } else {
194        arg_cfg_final.len()
195    };
196    check_args(name, args, number, has_vm)?;
197    for (idx, (arg_name, arg_pos)) in arg_cfg_final.iter().rev().enumerate() {
198        let value = match *arg_name {
199            "rs1" | "rs2" | "rd" => map_x_reg(args[idx], arg_name)?,
200            "vs1" | "vs2" | "vs3" | "vd" => map_v_reg(args[idx], arg_name)?,
201            "simm5" => {
202                let arg_current = args[idx].to_lowercase();
203                let value = if arg_current.starts_with('-') {
204                    let value = if let Some(content) = arg_current.strip_prefix("0x") {
205                        i8::from_str_radix(content, 16)
206                            .map_err(|_| anyhow!("Parse simm5 value failed: {}", arg_current))?
207                    } else {
208                        arg_current
209                            .parse::<i8>()
210                            .map_err(|_| anyhow!("Parse simm5 value failed: {}", arg_current))?
211                    };
212                    if value < -16 || value > 15 {
213                        return Err(anyhow!(
214                            "Simm5 value out of range: {} expected: [-16, 15]",
215                            value
216                        ));
217                    }
218                    value as u8
219                } else {
220                    let value = if let Some(content) = arg_current.strip_prefix("0x") {
221                        u8::from_str_radix(content, 16)
222                            .map_err(|_| anyhow!("Parse uimm5 value failed: {}", arg_current))?
223                    } else {
224                        arg_current
225                            .parse::<u8>()
226                            .map_err(|_| anyhow!("Parse uimm5 value failed: {}", arg_current))?
227                    };
228                    if value > 31 {
229                        return Err(anyhow!(
230                            "Uimm5 value out of range: {} expected: [0, 31]",
231                            value
232                        ));
233                    }
234                    value
235                };
236                (value & 0b00011111) as u32
237            }
238            "zimm" => {
239                let value = args[idx]
240                    .parse::<u8>()
241                    .map_err(|_| anyhow!("Parse zimm5 value failed: {}", args[idx]))?;
242                if value > 31 {
243                    return Err(anyhow!(
244                        "zimm5 value out of range: {} expected: [0, 31]",
245                        value
246                    ));
247                }
248                (value as u8 & 0b00011111) as u32
249            }
250            // NOTE: special case
251            "zimm10" | "zimm11" => {
252                let mut vsew: u8 = 0;
253                let mut lmul = Vlmul::M1;
254                let mut ta = false;
255                let mut ma = false;
256                let mut tu = false;
257                let mut mu = false;
258                for arg_str in &args[idx..] {
259                    if *arg_str == "ta" {
260                        ta = true;
261                    } else if *arg_str == "ma" {
262                        ma = true;
263                    } else if *arg_str == "tu" {
264                        tu = true;
265                    } else if *arg_str == "mu" {
266                        mu = true;
267                    } else if arg_str.as_bytes()[0] == b'e' {
268                        let sew = arg_str[1..]
269                            .parse::<u16>()
270                            .map_err(|_| anyhow!("Invalid SEW value format: {}", arg_str))?;
271                        vsew = match sew {
272                            8 => 0,
273                            16 => 1,
274                            32 => 2,
275                            64 => 3,
276                            128 => 4,
277                            256 => 5,
278                            512 => 6,
279                            1024 => 7,
280                            _ => {
281                                return Err(anyhow!(
282                                    "Invalid SEW value for vtypei: {}, arg: {}",
283                                    sew,
284                                    arg_str
285                                ))
286                            }
287                        };
288                    } else if arg_str.as_bytes()[0] == b'm' {
289                        lmul = Vlmul::from_str(arg_str)
290                            .ok_or_else(|| anyhow!("Invalid LMUL value format: {}", arg_str))?;
291                    } else {
292                        return Err(anyhow!(
293                            "Invalid argument for {}, expected: e{{n}}/m{{n}}/ta/ma/tu/mu, got: {}",
294                            name,
295                            arg_str
296                        ));
297                    }
298                }
299
300                if ta && tu {
301                    return Err(anyhow!("ta/tu can not both exists"));
302                }
303                if ma && mu {
304                    return Err(anyhow!("ma/mu can not both exists"));
305                }
306
307                let mut value = lmul as u8;
308                value |= vsew << 3;
309                if ta {
310                    value |= 1 << 6;
311                }
312                if ma {
313                    value |= 1 << 7;
314                }
315                value as u32
316            }
317            // See comment in asm.pest
318            "vm" => {
319                if args.get(idx) == Some(&"v0.t") {
320                    0
321                } else {
322                    1
323                }
324            }
325            // FIXME: support segment load/store
326            "nf" => 0,
327            // FIXME: support `Zvamo`
328            "wd" => return Err(anyhow!("Zvamo instructions are not supported.")),
329            _ => unreachable!(),
330        };
331        base |= value << arg_pos;
332    }
333    Ok(base)
334}
335
336#[repr(u8)]
337enum Vlmul {
338    // LMUL=1/8
339    Mf8 = 0b101,
340    // LMUL=1/4
341    Mf4 = 0b110,
342    // LMUL=1/2
343    Mf2 = 0b111,
344    // LMUL=1
345    M1 = 0b000,
346    // LMUL=2
347    M2 = 0b001,
348    // LMUL=4
349    M4 = 0b010,
350    // LMUL=8
351    M8 = 0b011,
352}
353
354impl Vlmul {
355    fn from_str(name: &str) -> Option<Vlmul> {
356        match name {
357            "mf8" => Some(Vlmul::Mf8),
358            "mf4" => Some(Vlmul::Mf4),
359            "mf2" => Some(Vlmul::Mf2),
360            "m1" => Some(Vlmul::M1),
361            "m2" => Some(Vlmul::M2),
362            "m4" => Some(Vlmul::M4),
363            "m8" => Some(Vlmul::M8),
364            _ => None,
365        }
366    }
367}
368
369fn check_args(name: &str, args: &[&str], number: usize, vm: bool) -> Result<(), Error> {
370    if V0_TAIL_INSTS.contains(&name) {
371        if args.len() != number + 1 {
372            return Err(anyhow!(
373                "Invalid number of arguments for {}, expected: {}, got: {}",
374                name,
375                number + 1,
376                args.len()
377            ));
378        }
379        if args[args.len() - 1] != "v0" {
380            return Err(anyhow!("The last argument of {} must be v0", name));
381        }
382        return Ok(());
383    }
384    let (expected, min, max) = if name == "vsetvli" || name == "vsetivli" {
385        ("3 <= n <=6".to_string(), 3, 6)
386    } else if !vm {
387        (number.to_string(), number, number)
388    } else {
389        (format!("{} or {}", number, number + 1), number, number + 1)
390    };
391    if args.len() < min || args.len() > max {
392        Err(anyhow!(
393            "Invalid number of arguments for {}, expected: {}, got: {}",
394            name,
395            expected,
396            args.len()
397        ))
398    } else {
399        Ok(())
400    }
401}
402
403fn map_v_reg(name: &str, label: &str) -> Result<u32, Error> {
404    if name.as_bytes()[0] != b'v' {
405        return Err(anyhow!("Invalid {} V register: {}", label, name));
406    }
407    let number = name[1..]
408        .parse::<u32>()
409        .map_err(|_| anyhow!("Invalid {} V register: {}", label, name))?;
410    if number > 31 {
411        return Err(anyhow!("Invalid {} V register: {}", label, name));
412    }
413    Ok(number)
414}
415
416fn map_x_reg(name: &str, label: &str) -> Result<u32, Error> {
417    match name {
418        "x0" | "zero" => Ok(0),
419        "x1" | "ra" => Ok(1),
420        "x2" | "sp" => Ok(2),
421        "x3" | "gp" => Ok(3),
422        "x4" | "tp" => Ok(4),
423        "x5" | "t0" => Ok(5),
424        "x6" | "t1" => Ok(6),
425        "x7" | "t2" => Ok(7),
426        "x8" | "s0" | "fp" => Ok(8),
427        "x9" | "s1" => Ok(9),
428        "x10" | "a0" => Ok(10),
429        "x11" | "a1" => Ok(11),
430        "x12" | "a2" => Ok(12),
431        "x13" | "a3" => Ok(13),
432        "x14" | "a4" => Ok(14),
433        "x15" | "a5" => Ok(15),
434        "x16" | "a6" => Ok(16),
435        "x17" | "a7" => Ok(17),
436        "x18" | "s2" => Ok(18),
437        "x19" | "s3" => Ok(19),
438        "x20" | "s4" => Ok(20),
439        "x21" | "s5" => Ok(21),
440        "x22" | "s6" => Ok(22),
441        "x23" | "s7" => Ok(23),
442        "x24" | "s8" => Ok(24),
443        "x25" | "s9" => Ok(25),
444        "x26" | "s10" => Ok(26),
445        "x27" | "s11" => Ok(27),
446        "x28" | "t3" => Ok(28),
447        "x29" | "t4" => Ok(29),
448        "x30" | "t5" => Ok(30),
449        "x31" | "t6" => Ok(31),
450        _ => Err(anyhow!("Invalid {} X register: {}", label, name)),
451    }
452}
453
454#[cfg(test)]
455mod tests {
456    use super::*;
457
458    #[test]
459    fn test_riscvgc() {
460        assert_eq!(encode("add {abc_t}, {abc}, 3", false).unwrap(), None);
461        assert_eq!(encode("add a1, a1, a2", false).unwrap(), None);
462        assert_eq!(encode("jr t0", false).unwrap(), None);
463        assert_eq!(encode("fence", false).unwrap(), None);
464    }
465
466    fn assert_inst(code: u32, inst: &str) {
467        let output_code = encode(inst, false).unwrap().unwrap();
468        assert_eq!(output_code, code, "0b{:032b} - {}", output_code, inst);
469    }
470
471    // # configuration setting
472    // # https://github.com/riscv/riscv-v-spec/blob/master/vcfg-format.adoc
473    //
474    // vsetivli     31=1 30=1 zimm10    zimm 14..12=0x7 rd 6..0=0x57
475    // vsetvli      31=0 zimm11          rs1 14..12=0x7 rd 6..0=0x57
476    // vsetvl       31=1 30..25=0x0 rs2  rs1 14..12=0x7 rd 6..0=0x57
477    #[test]
478    fn test_vset() {
479        for (code, inst) in [
480            (0b10000001111110011111001011010111, "vsetvl x5, s3, t6"),
481            (0b00000000000010011111001011010111, "vsetvli x5, s3, e8"),
482            (
483                0b00000010101010011111001011010111,
484                "vsetvli x5, s3, e256, m4",
485            ),
486            (
487                0b00001110101010011111001011010111,
488                "vsetvli x5, s3, e256, m4, ta, ma",
489            ),
490            (
491                0b11000010101010011111001011010111,
492                "vsetivli x5, 19, e256, m4",
493            ),
494            (
495                0b11000010101010011111001011010111,
496                "vsetivli x5, 19, e256, m4, tu, mu",
497            ),
498            (
499                0b11001010101010011111001011010111,
500                "vsetivli x5, 19, e256, m4, tu, ma",
501            ),
502            (
503                0b11000110101010011111001011010111,
504                "vsetivli x5, 19, e256, m4, ta, mu",
505            ),
506            (
507                0b11001110101010011111001011010111,
508                "vsetivli x5, 19, e256, m4, ta, ma",
509            ),
510        ] {
511            assert_inst(code, inst);
512        }
513    }
514
515    #[test]
516    fn test_simm5_range() {
517        for (code, inst) in [
518            (0b00000010001010000011000011010111, "vadd.vi  v1, v2, -16"),
519            (0b00000010001001111011000011010111, "vadd.vi  v1, v2, 15"),
520        ] {
521            assert_inst(code, inst);
522        }
523
524        assert!(encode("vadd.vi  v1, v2, -17", false).is_err());
525    }
526
527    // vlm.v          31..28=0      27..26=0 25=1 24..20=0xb  rs1 14..12=0x0  vd 6..0=0x07
528    // vle8.v         nf       28=0 27..26=0 vm   24..20=0    rs1 14..12=0x0  vd 6..0=0x07
529    // vle8ff.v       nf       28=0 27..26=0 vm   24..20=0x10 rs1 14..12=0x0  vd 6..0=0x07
530    // vl1re8.v       31..29=0 28=0 27..26=0 25=1 24..20=0x08 rs1 14..12=0x0  vd 6..0=0x07
531    #[test]
532    fn test_rs1_vd_0x07() {
533        for (code, inst) in [
534            // vlm.v     vd, (rs1)
535            (0b00000010101100101000000010000111, "vlm.v v1, (t0)"),
536            // vle8.v    vd, (rs1), vm
537            (0b00000010000000101000000010000111, "vle8.v v1, (t0)"),
538            // vle8ff.v  vd, (rs1), vm
539            (0b00000011000000101000000010000111, "vle8ff.v v1, (t0)"),
540            // vl1re8.v  vd, (rs1)
541            (0b00000010100000101000000010000111, "vl1re8.v v1, (t0)"),
542        ] {
543            assert_inst(code, inst);
544        }
545    }
546    // vsm.v          31..28=0      27..26=0 25=1 24..20=0xb  rs1 14..12=0x0 vs3 6..0=0x27
547    // vse8.v         nf       28=0 27..26=0 vm   24..20=0    rs1 14..12=0x0 vs3 6..0=0x27
548    // vs1r.v         31..29=0 28=0 27..26=0 25=1 24..20=0x08 rs1 14..12=0x0 vs3 6..0=0x27
549    #[test]
550    fn test_rs1_vs3_0x27() {
551        for (code, inst) in [
552            // vsm.v     vs3, (rs1)
553            (0b00000010101100101000000010100111, "vsm.v v1, (t0)"),
554            // vse8.v    vs3, (rs1), vm
555            (0b00000010000000101000000010100111, "vse8.v v1, (t0)"),
556            // vs1r.v    vs3, (rs1)
557            (0b00000010100000101000000010100111, "vs1r.v v1, (t0)"),
558        ] {
559            assert_inst(code, inst);
560        }
561    }
562
563    // vluxei8.v  nf 28=0 27..26=1 vm vs2 rs1 14..12=0x0  vd 6..0=0x07
564    // vloxei8.v  nf 28=0 27..26=3 vm vs2 rs1 14..12=0x0  vd 6..0=0x07
565    #[test]
566    fn test_vs2_rs1_vd_0x07() {
567        for (code, inst) in [
568            // vluxei8.v    vd, (rs1), vs2, vm
569            (0b00000110001000101000000010000111, "vluxei8.v v1, (t0), v2"),
570            // vloxei8.v    vd, (rs1), vs2, vm
571            (0b00001110001000101000000010000111, "vloxei8.v v1, (t0), v2"),
572        ] {
573            assert_inst(code, inst);
574        }
575    }
576    // vsuxei8.v      nf 28=0 27..26=1 vm vs2 rs1 14..12=0x0 vs3 6..0=0x27
577    // vsoxei8.v      nf 28=0 27..26=3 vm vs2 rs1 14..12=0x0 vs3 6..0=0x27
578    #[test]
579    fn test_vs2_rs1_vs3_0x27() {
580        for (code, inst) in [
581            // vsuxei8.v   vs3, (rs1), vs2, vm
582            (0b00000110001000101000000010100111, "vsuxei8.v v1, (t0), v2"),
583            // vsoxei8.v   vs3, (rs1), vs2, vm
584            (0b00001110001000101000000010100111, "vsoxei8.v v1, (t0), v2"),
585        ] {
586            assert_inst(code, inst);
587        }
588    }
589
590    // vlse8.v         nf 28=0 27..26=2 vm rs2 rs1 14..12=0x0  vd 6..0=0x07
591    #[test]
592    fn test_rs2_rs1_vd_0x07() {
593        // vlse8.v    vd, (rs1), rs2, vm
594        assert_inst(0b00001010011000101000000010000111, "vlse8.v v1, (t0), t1");
595    }
596
597    // vsse8.v         nf 28=0 27..26=2 vm rs2 rs1 14..12=0x0 vs3 6..0=0x27
598    #[test]
599    fn test_rs2_rs1_vs3_0x27() {
600        // vsse8.v    vs3, (rs1), rs2, vm
601        assert_inst(0b00001010011000101000000010100111, "vsse8.v v1, (t0), t1");
602    }
603
604    // vfadd.vf       31..26=0x00 vm   vs2 rs1 14..12=0x5 vd 6..0=0x57
605    // vfmerge.vfm    31..26=0x17 25=0 vs2 rs1 14..12=0x5 vd 6..0=0x57
606    // vmacc.vx       31..26=0x2d vm   vs2 rs1 14..12=0x6 vd 6..0=0x57
607    #[test]
608    fn test_vs2_rs1_vd_0x57() {
609        for (code, inst) in [
610            // vfadd.vf    vd, vs2, rs1, vm
611            (0b00000010001000101101000011010111, "vfadd.vf v1, v2, t0"),
612            // vfmerge.vfm vd, vs2, rs1, v0
613            (
614                0b01011100001000101101000011010111,
615                "vfmerge.vfm v1, v2, t0, v0",
616            ),
617            // vmacc.vx vd, rs1, vs2, vm
618            (0b10110110000101100110000111010111, "vmacc.vx v3, a2, v1"),
619        ] {
620            assert_inst(code, inst);
621        }
622    }
623
624    // vfmv.s.f       31..26=0x10 25=1 24..20=0 rs1 14..12=0x5 vd 6..0=0x57
625    // vmv.v.x        31..26=0x17 25=1 24..20=0 rs1 14..12=0x4 vd 6..0=0x57
626    #[test]
627    fn test_rs1_vd_0x57() {
628        for (code, inst) in [
629            // vfmv.s.f vd, rs1
630            (0b01000010000000101101000011010111, "vfmv.s.f v1, t0"),
631            // vmv.v.x  vd, rs1
632            (0b01011110000000101100000011010111, "vmv.v.x v1, t0"),
633        ] {
634            assert_inst(code, inst);
635        }
636    }
637
638    // vfadd.vv       31..26=0x00 vm   vs2 vs1 14..12=0x1 vd 6..0=0x57
639    // vadc.vvm       31..26=0x10 25=0 vs2 vs1 14..12=0x0 vd 6..0=0x57
640    // vmacc.vv       31..26=0x2d vm   vs2 vs1 14..12=0x2 vd 6..0=0x57
641    #[test]
642    fn test_vs2_vs1_vd_0x57() {
643        for (code, inst) in [
644            // vfadd.vv   vd, vs2, vs1, vm
645            (0b00000010001000011001000011010111, "vfadd.vv v1, v2, v3"),
646            // vadc.vvm   vd, vs2, vs1, v0
647            (
648                0b01000000001000011000000011010111,
649                "vadc.vvm v1, v2, v3, v0",
650            ),
651            // vmacc.vv vd, vs1, vs2, vm
652            (0b10110110001000001010000111010111, "vmacc.vv v3, v1, v2"),
653        ] {
654            assert_inst(code, inst);
655        }
656    }
657
658    // vfmv.f.s       31..26=0x10 25=1 vs2 19..15=0    14..12=0x1 rd 6..0=0x57
659    // vcpop.m        31..26=0x10 vm   vs2 19..15=0x10 14..12=0x2 rd 6..0=0x57
660    #[test]
661    fn test_vs2_rd_0x57() {
662        // vfmv.f.s rd, vs2
663        // vcpop.m  rd, vs2, vm
664        for (code, inst) in [
665            (0b01000010001000000001001011010111, "vfmv.f.s t0, v2"),
666            (0b01000010001010000010001011010111, "vcpop.m t0, v2"),
667        ] {
668            assert_inst(code, inst);
669        }
670    }
671
672    // vfcvt.xu.f.v     31..26=0x12 vm   vs2 19..15=0x00 14..12=0x1 vd 6..0=0x57
673    // vmv1r.v          31..26=0x27 25=1 vs2 19..15=0    14..12=0x3 vd 6..0=0x57
674    #[test]
675    fn test_vs2_vd_0x57() {
676        for (code, inst) in [
677            // vfcvt.xu.f.v vd, vs2, vm
678            (0b01001010001000000001000011010111, "vfcvt.xu.f.v v1, v2"),
679            // vmv<nr>r.v   vd, vs2
680            (0b10011110001000000011000011010111, "vmv1r.v v1, v2"),
681        ] {
682            assert_inst(code, inst);
683        }
684    }
685
686    // vmv.v.v        31..26=0x17 25=1 24..20=0 vs1 14..12=0x0 vd 6..0=0x57
687    #[test]
688    fn test_vs1_vd_0x57() {
689        // vmv.v.v vd, vs1
690        assert_inst(0b01011110000000010000000011010111, "vmv.v.v v1, v2");
691    }
692
693    // vadd.vi        31..26=0x00 vm   vs2 simm5 14..12=0x3 vd 6..0=0x57
694    // vadc.vim       31..26=0x10 25=0 vs2 simm5 14..12=0x3 vd 6..0=0x57
695    #[test]
696    fn test_vs2_simm5_vd_0x57() {
697        for (code, inst) in [
698            // vadd.vi    vd, vs2, imm, vm
699            (0b00000010001011011011000011010111, "vadd.vi  v1, v2, -5"),
700            // vadc.vim   vd, vs2, imm, v0
701            (0b01000000001000101011000011010111, "vadc.vim v1, v2, 5, v0"),
702        ] {
703            assert_inst(code, inst);
704        }
705    }
706
707    // vmv.v.i        31..26=0x17 25=1 24..20=0 simm5 14..12=0x3 vd 6..0=0x57
708    #[test]
709    fn test_simm5_vd_0x57() {
710        // vmv.v.i vd, imm
711        assert_inst(0b01011110000000101011000011010111, "vmv.v.i v1, 0x5");
712    }
713    // vid.v          31..26=0x14 vm 24..20=0 19..15=0x11 14..12=0x2 vd 6..0=0x57
714    #[test]
715    fn test_vd_0x57() {
716        // vid.v vd, vm
717        assert_inst(0b01010010000010001010000011010111, "vid.v v1");
718    }
719
720    // vmandn.mm      31..26=0x18 vm vs2 vs1 14..12=0x2 vd 6..0=0x57
721    // vmorn.mm       31..26=0x1c vm vs2 vs1 14..12=0x2 vd 6..0=0x57
722    #[test]
723    fn test_vmandn_vmorn() {
724        // vmandn.mm vd, vs2, vs1
725        assert_inst(0b01100010001000011010000011010111, "vmandn.mm v1, v2, v3");
726        // vmorn.mm  vd, vs2, vs1
727        assert_inst(0b01110010001000011010000011010111, "vmorn.mm v1, v2, v3");
728    }
729}