Skip to main content

ab_riscv_primitives/instructions/v/zvexx/
load.rs

1//! ZveXx vector load instructions
2
3#[cfg(test)]
4mod tests;
5
6use crate::instructions::Instruction;
7use crate::instructions::v::{Eew, V, VRegGroupSize};
8use crate::registers::general_purpose::Register;
9use crate::registers::vector::VReg;
10use ab_riscv_macros::instruction;
11use core::fmt;
12
13/// Number of fields per segment for load/store instructions
14#[derive(Debug, Clone, Copy)]
15#[derive_const(PartialEq, Eq)]
16#[repr(u8)]
17pub enum Nf {
18    /// 1 field per segment
19    N1 = 1,
20    /// 2 fields per segment
21    N2 = 2,
22    /// 3 fields per segment
23    N3 = 3,
24    /// 4 fields per segment
25    N4 = 4,
26    /// 5 fields per segment
27    N5 = 5,
28    /// 6 fields per segment
29    N6 = 6,
30    /// 7 fields per segment
31    N7 = 7,
32    /// 8 fields per segment
33    N8 = 8,
34}
35
36impl fmt::Display for Nf {
37    #[inline]
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        fmt::Display::fmt(&self.fields_per_segment(), f)
40    }
41}
42
43impl Nf {
44    /// Maximum allowed value for `Nf`
45    pub const MAX: Self = Nf::N8;
46
47    /// Create a new instance.
48    ///
49    /// `nf` must be in the range `1..=8` or `None` is returned.
50    #[inline(always)]
51    pub const fn new(nf: u8) -> Option<Self> {
52        match nf {
53            1 => Some(Nf::N1),
54            2 => Some(Nf::N2),
55            3 => Some(Nf::N3),
56            4 => Some(Nf::N4),
57            5 => Some(Nf::N5),
58            6 => Some(Nf::N6),
59            7 => Some(Nf::N7),
60            8 => Some(Nf::N8),
61            _ => None,
62        }
63    }
64
65    /// Returns the number of fields per segment for the load/store instruction.
66    ///
67    /// Always in `1..=8` range.
68    #[inline(always)]
69    pub const fn fields_per_segment(&self) -> u8 {
70        *self as u8
71    }
72}
73
74/// `vm` and `nf` fields for segmented load/store instructions.
75///
76/// This is a more compact representation that fits within a single byte rather than two when
77/// storing these separately.
78#[derive(Debug, Clone, Copy)]
79#[derive_const(PartialEq, Eq)]
80pub struct SegVmNf(u8);
81
82impl SegVmNf {
83    /// Create a new instance
84    #[inline(always)]
85    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
86    pub const fn new(vm: bool, nf: Nf) -> Self {
87        Self((nf.fields_per_segment() << 1) | u8::from(vm))
88    }
89
90    /// Extracts the `vm` field from the `SegVmNf` representation
91    #[inline(always)]
92    pub const fn vm(&self) -> bool {
93        self.0 & 1 == 1
94    }
95
96    /// Extracts the `nf` field from the `SegVmNf` representation
97    #[inline(always)]
98    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
99    pub const fn nf(&self) -> Nf {
100        // SAFETY: Protected internal invariant
101        unsafe { Nf::new(self.0 >> 1).unwrap_unchecked() }
102    }
103}
104
105/// `nreg` field for load/store instructions
106#[derive(Debug, Clone, Copy)]
107#[derive_const(PartialEq, Eq)]
108#[repr(u8)]
109pub enum LoadStoreNreg {
110    /// 1 register
111    N1 = 1,
112    /// 2 registers
113    N2 = 2,
114    /// 4 registers
115    N4 = 4,
116    /// 8 registers
117    N8 = 8,
118}
119
120impl fmt::Display for LoadStoreNreg {
121    #[inline]
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        fmt::Display::fmt(&self.num_registers(), f)
124    }
125}
126
127impl LoadStoreNreg {
128    /// Create a new instance
129    #[inline(always)]
130    pub const fn new(n: u8) -> Option<Self> {
131        match n {
132            1 => Some(Self::N1),
133            2 => Some(Self::N2),
134            4 => Some(Self::N4),
135            8 => Some(Self::N8),
136            _ => None,
137        }
138    }
139
140    /// Get the number of registers
141    #[inline(always)]
142    pub const fn num_registers(&self) -> VRegGroupSize {
143        match self {
144            Self::N1 => VRegGroupSize::R1,
145            Self::N2 => VRegGroupSize::R2,
146            Self::N4 => VRegGroupSize::R4,
147            Self::N8 => VRegGroupSize::R8,
148        }
149    }
150}
151
152/// RISC-V ZveXx vector load instruction.
153///
154/// Encoded under the LOAD-FP major opcode (0x07). All loads use rs1 (GPR) as a base address and vd
155/// (vector register) as a destination.
156#[instruction]
157#[derive(Debug, Clone, Copy)]
158#[derive_const(PartialEq, Eq)]
159#[rustfmt::skip]
160#[doc(hidden)]
161pub enum ZveXxLoadInstruction<Reg> {
162    /// Unit-stride load: `vle{eew}.v vd, (rs1), vm`
163    ///
164    /// mop=00, lumop=00000, nf=000
165    Vle { vd: VReg, rs1: Reg, vm: bool, eew: Eew },
166    /// Unit-stride fault-only-first load: `vle{eew}ff.v vd, (rs1), vm`
167    ///
168    /// mop=00, lumop=10000, nf=000
169    Vleff { vd: VReg, rs1: Reg, vm: bool, eew: Eew },
170    /// Unit-stride mask load: `vlm.v vd, (rs1)`
171    ///
172    /// mop=00, lumop=01011, nf=000, eew=e8, vm=1
173    Vlm { vd: VReg, rs1: Reg },
174    /// Strided load: `vlse{eew}.v vd, (rs1), rs2, vm`
175    ///
176    /// mop=10, nf=000
177    Vlse { vd: VReg, rs1: Reg, rs2: Reg, vm: bool, eew: Eew },
178    /// Indexed-unordered load: `vluxei{eew}.v vd, (rs1), vs2, vm`
179    ///
180    /// mop=01, nf=000. eew is the index element width.
181    Vluxei { vd: VReg, rs1: Reg, vs2: VReg, vm: bool, eew: Eew },
182    /// Indexed-ordered load: `vloxei{eew}.v vd, (rs1), vs2, vm`
183    ///
184    /// mop=11, nf=000. eew is the index element width.
185    Vloxei { vd: VReg, rs1: Reg, vs2: VReg, vm: bool, eew: Eew },
186    /// Whole-register load: `vl{nreg}re{eew}.v vd, (rs1)`
187    ///
188    /// mop=00, lumop=01000, vm=1. nreg must be 1, 2, 4, or 8.
189    Vlr { vd: VReg, rs1: Reg, nreg: LoadStoreNreg, eew: Eew },
190    /// Unit-stride segment load: `vlseg{nf}e{eew}.v vd, (rs1), vm`
191    ///
192    /// mop=00, lumop=00000, nf>0
193    Vlseg { vd: VReg, rs1: Reg, eew: Eew, vm_nf: SegVmNf },
194    /// Unit-stride fault-only-first segment load: `vlseg{nf}e{eew}ff.v vd, (rs1), vm`
195    ///
196    /// mop=00, lumop=10000, nf>0
197    Vlsegff { vd: VReg, rs1: Reg, eew: Eew, vm_nf: SegVmNf },
198    /// Strided segment load: `vlsseg{nf}e{eew}.v vd, (rs1), rs2, vm`
199    ///
200    /// mop=10, nf>0
201    Vlsseg { vd: VReg, rs1: Reg, rs2: Reg, eew: Eew, vm_nf: SegVmNf },
202    /// Indexed-unordered segment load: `vluxseg{nf}ei{eew}.v vd, (rs1), vs2, vm`
203    ///
204    /// mop=01, nf>0
205    Vluxseg { vd: VReg, rs1: Reg, vs2: VReg, eew: Eew, vm_nf: SegVmNf },
206    /// Indexed-ordered segment load: `vloxseg{nf}ei{eew}.v vd, (rs1), vs2, vm`
207    ///
208    /// mop=11, nf>0
209    Vloxseg { vd: VReg, rs1: Reg, vs2: VReg, eew: Eew, vm_nf: SegVmNf },
210}
211
212#[instruction]
213const impl<Reg> Instruction for ZveXxLoadInstruction<Reg>
214where
215    Reg: [const] Register,
216{
217    const ALIGNMENT: u8 = align_of::<u32>() as u8;
218
219    type Reg = Reg;
220
221    #[inline(always)]
222    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
223    fn try_decode(instruction: u32) -> Option<Self> {
224        let opcode = (instruction & 0b111_1111) as u8;
225
226        // LOAD-FP major opcode
227        if opcode != 0b000_0111 {
228            None?;
229        }
230
231        let vd_bits = ((instruction >> 7) & 0x1f) as u8;
232        let width = ((instruction >> 12) & 0b111) as u8;
233        let rs1_bits = ((instruction >> 15) & 0x1f) as u8;
234        let rs2_bits = ((instruction >> 20) & 0x1f) as u8;
235        let vm = ((instruction >> 25) & 1) != 0;
236        let mop = ((instruction >> 26) & 0b11) as u8;
237        let mew = ((instruction >> 28) & 1) as u8;
238        let nf = ((instruction >> 29) & 0b111) as u8;
239
240        // mew must be 0 (reserved for >=128-bit)
241        if mew != 0 {
242            None?;
243        }
244
245        let vd = VReg::from_bits(vd_bits)?;
246        let rs1 = Reg::from_bits(rs1_bits)?;
247
248        // nf encodes number of fields minus 1 (nf=0 means 1 field)
249        let nf_val = nf + 1;
250
251        match mop {
252            // Unit-stride
253            0b00 => {
254                let lumop = rs2_bits;
255                match lumop {
256                    // Regular unit-stride load
257                    0b0_0000 => {
258                        let eew = Eew::from_width(width)?;
259                        if nf == 0 {
260                            Some(Self::Vle { vd, rs1, vm, eew })
261                        } else {
262                            Some(Self::Vlseg {
263                                vd,
264                                rs1,
265                                eew,
266                                vm_nf: SegVmNf::new(vm, Nf::new(nf_val)?),
267                            })
268                        }
269                    }
270                    // Whole-register load
271                    0b0_1000 => {
272                        // vm must be 1 (unmasked)
273                        if !vm {
274                            None?;
275                        }
276                        let eew = Eew::from_width(width)?;
277                        let nreg = LoadStoreNreg::new(nf_val)?;
278                        Some(Self::Vlr { vd, rs1, nreg, eew })
279                    }
280                    // Mask load
281                    0b0_1011 => {
282                        // Must be eew=e8, vm=1, nf=0
283                        if width != 0b000 || !vm || nf != 0 {
284                            None?;
285                        }
286                        Some(Self::Vlm { vd, rs1 })
287                    }
288                    // Fault-only-first
289                    0b1_0000 => {
290                        let eew = Eew::from_width(width)?;
291                        if nf == 0 {
292                            Some(Self::Vleff { vd, rs1, vm, eew })
293                        } else {
294                            Some(Self::Vlsegff {
295                                vd,
296                                rs1,
297                                eew,
298                                vm_nf: SegVmNf::new(vm, Nf::new(nf_val)?),
299                            })
300                        }
301                    }
302                    _ => None,
303                }
304            }
305            // Indexed-unordered
306            0b01 => {
307                let eew = Eew::from_width(width)?;
308                let vs2 = VReg::from_bits(rs2_bits)?;
309
310                if !Self::implements_extension::<V<_>>()
311                    && Reg::XLEN == u32::BITS as u8
312                    && eew == Eew::E64
313                {
314                    None?;
315                }
316
317                if nf == 0 {
318                    Some(Self::Vluxei {
319                        vd,
320                        rs1,
321                        vs2,
322                        vm,
323                        eew,
324                    })
325                } else {
326                    Some(Self::Vluxseg {
327                        vd,
328                        rs1,
329                        vs2,
330                        eew,
331                        vm_nf: SegVmNf::new(vm, Nf::new(nf_val)?),
332                    })
333                }
334            }
335            // Strided
336            0b10 => {
337                let eew = Eew::from_width(width)?;
338                let rs2 = Reg::from_bits(rs2_bits)?;
339                if nf == 0 {
340                    Some(Self::Vlse {
341                        vd,
342                        rs1,
343                        rs2,
344                        vm,
345                        eew,
346                    })
347                } else {
348                    Some(Self::Vlsseg {
349                        vd,
350                        rs1,
351                        rs2,
352                        eew,
353                        vm_nf: SegVmNf::new(vm, Nf::new(nf_val)?),
354                    })
355                }
356            }
357            // Indexed-ordered
358            0b11 => {
359                let eew = Eew::from_width(width)?;
360                let vs2 = VReg::from_bits(rs2_bits)?;
361
362                if !Self::implements_extension::<V<_>>()
363                    && Reg::XLEN == u32::BITS as u8
364                    && eew == Eew::E64
365                {
366                    None?;
367                }
368
369                if nf == 0 {
370                    Some(Self::Vloxei {
371                        vd,
372                        rs1,
373                        vs2,
374                        vm,
375                        eew,
376                    })
377                } else {
378                    Some(Self::Vloxseg {
379                        vd,
380                        rs1,
381                        vs2,
382                        eew,
383                        vm_nf: SegVmNf::new(vm, Nf::new(nf_val)?),
384                    })
385                }
386            }
387            _ => None,
388        }
389    }
390
391    #[inline(always)]
392    fn size(&self) -> u8 {
393        size_of::<u32>() as u8
394    }
395}
396
397#[instruction]
398impl<Reg> fmt::Display for ZveXxLoadInstruction<Reg>
399where
400    Reg: fmt::Display,
401{
402    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403        #[rustfmt::skip]
404        match self {
405            Self::Vle { vd, rs1, vm, eew } => write!(f, "vle{eew}.v {vd}, ({rs1}){}", mask_suffix(vm)),
406            Self::Vleff { vd, rs1, vm, eew } => write!(f, "vle{eew}ff.v {vd}, ({rs1}){}", mask_suffix(vm)),
407            Self::Vlm { vd, rs1 } => write!(f, "vlm.v {vd}, ({rs1})"),
408            Self::Vlse { vd, rs1, rs2, vm, eew } => write!(f, "vlse{eew}.v {vd}, ({rs1}), {rs2}{}", mask_suffix(vm)),
409            Self::Vluxei { vd, rs1, vs2, vm, eew } => write!(f, "vluxei{eew}.v {vd}, ({rs1}), {vs2}{}", mask_suffix(vm)),
410            Self::Vloxei { vd, rs1, vs2, vm, eew } => write!(f, "vloxei{eew}.v {vd}, ({rs1}), {vs2}{}", mask_suffix(vm)),
411            Self::Vlr { vd, rs1, nreg, eew } => write!(f, "vl{nreg}re{eew}.v {vd}, ({rs1})"),
412            Self::Vlseg { vd, rs1, eew, vm_nf } => write!(f, "vlseg{}e{eew}.v {vd}, ({rs1}){}", vm_nf.nf(), mask_suffix(&vm_nf.vm())),
413            Self::Vlsegff { vd, rs1, eew, vm_nf } => write!(f, "vlseg{}e{eew}ff.v {vd}, ({rs1}){}", vm_nf.nf(), mask_suffix(&vm_nf.vm())),
414            Self::Vlsseg { vd, rs1, rs2, eew, vm_nf } => write!(f, "vlsseg{}e{eew}.v {vd}, ({rs1}), {rs2}{}", vm_nf.nf(), mask_suffix(&vm_nf.vm())),
415            Self::Vluxseg { vd, rs1, vs2, eew, vm_nf } => write!(f, "vluxseg{}ei{eew}.v {vd}, ({rs1}), {vs2}{}", vm_nf.nf(), mask_suffix(&vm_nf.vm())),
416            Self::Vloxseg { vd, rs1, vs2, eew, vm_nf } => write!(f, "vloxseg{}ei{eew}.v {vd}, ({rs1}), {vs2}{}", vm_nf.nf(), mask_suffix(&vm_nf.vm())),
417        }
418    }
419}
420
421/// Format mask suffix for display
422#[inline(always)]
423fn mask_suffix(vm: &bool) -> &'static str {
424    if *vm { "" } else { ", v0.t" }
425}