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