Skip to main content

ab_riscv_interpreter/v/vector_registers/
segment.rs

1//! Register groups of segment loads and stores, and their elements and fields
2
3use crate::v::vector_registers::{VRegGroup, VectorRegisterFile};
4use ab_riscv_primitives::prelude::*;
5use core::hint::cold_path;
6
7/// Register group of a segment load or store: `nf` fields, each a [`VRegGroup`], in consecutive
8/// register groups starting with the first one.
9#[derive(Debug, Clone, Copy)]
10pub struct VRegSegmentGroup<Hart>
11where
12    Hart: VectorHartConfig,
13{
14    // Invariant: all `nf` field groups lie within the register file
15    first: VRegGroup<Hart>,
16    nf: Nf,
17}
18
19impl<Hart> VRegSegmentGroup<Hart>
20where
21    Hart: VectorHartConfig,
22{
23    /// Segment group of `nf` fields, the first of which is `first`.
24    ///
25    /// Returns `None` if `NFIELDS * EMUL` exceeds 8 or the fields don't fit into the register
26    /// file, each of which makes an instruction illegal.
27    #[inline(always)]
28    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
29    pub const fn new(first: VRegGroup<Hart>, nf: Nf) -> Option<Self> {
30        let regs = u32::from(nf.fields_per_segment()) * u32::from(first.group_regs().get());
31        // Per spec, `NFIELDS * EMUL` must not exceed 8 for segment loads/stores, regardless of
32        // whether the field groups would otherwise fit within the 32 vector registers
33        if regs > 8 || u32::from(first.base.to_bits()) + regs > 32 {
34            cold_path();
35            return None;
36        }
37        Some(Self { first, nf })
38    }
39
40    /// Group of a single field, as used by loads and stores that are not segment ones
41    #[inline(always)]
42    pub const fn single(group: VRegGroup<Hart>) -> Self {
43        Self {
44            first: group,
45            nf: Nf::N1,
46        }
47    }
48
49    /// The first field
50    #[inline(always)]
51    pub const fn first(self) -> VRegGroup<Hart> {
52        self.first
53    }
54
55    /// Number of fields
56    #[inline(always)]
57    pub const fn nf(self) -> Nf {
58        self.nf
59    }
60
61    /// Register group of field `field`, `None` if it is not below the number of fields
62    #[inline(always)]
63    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
64    pub const fn field(self, field: u8) -> Option<VRegGroup<Hart>> {
65        if field >= self.nf.fields_per_segment() {
66            cold_path();
67            return None;
68        }
69        // SAFETY: Checked above
70        let base = unsafe { self.field_base(field) };
71        Some(VRegGroup { base, ..self.first })
72    }
73
74    /// First register of field `field`.
75    ///
76    /// # Safety
77    /// `field` must be below the number of fields.
78    #[inline(always)]
79    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
80    const unsafe fn field_base(self, field: u8) -> VReg {
81        // SAFETY: Field `field < nf` lies within the register file by the invariant and the
82        // caller's precondition
83        unsafe {
84            VReg::from_bits(self.first.base.to_bits() + field * self.first.group_regs().get())
85                .unwrap_unchecked()
86        }
87    }
88
89    /// Register groups of all fields in order
90    #[inline(always)]
91    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
92    pub fn fields(self) -> impl Iterator<Item = VRegGroup<Hart>> {
93        (0..self.nf.fields_per_segment()).filter_map(move |field| self.field(field))
94    }
95
96    /// Body elements `start..vl`, each of which gives access to its fields.
97    ///
98    /// The index of an element is below `vl` by construction, so access to its fields doesn't
99    /// need the compiler to prove anything about indices, unlike [`VectorRegisterFile::read()`].
100    #[inline(always)]
101    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
102    pub fn elements(self, start: u16) -> impl Iterator<Item = SegmentElement<Hart>> {
103        // `vl <= VLEN <= 65536`, so every index below it fits into `u16`
104        (u32::from(start)..u32::from(self.first.vl.get())).map(move |index| SegmentElement {
105            group: self,
106            index: u32::truncate(index),
107        })
108    }
109
110    /// This group together with the group of indices of an indexed load or store.
111    ///
112    /// Returns `None` if `index` has a different `vl`, which never happens for groups created from
113    /// the same configuration.
114    #[inline(always)]
115    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
116    pub fn with_index(self, index: VRegGroup<Hart>) -> Option<IndexedSegmentGroup<Hart>> {
117        if index.vl != self.first.vl {
118            cold_path();
119            return None;
120        }
121        Some(IndexedSegmentGroup { data: self, index })
122    }
123}
124
125/// Body element of a [`VRegSegmentGroup`], see [`VRegSegmentGroup::elements()`]
126#[derive(Debug, Clone, Copy)]
127pub struct SegmentElement<Hart>
128where
129    Hart: VectorHartConfig,
130{
131    group: VRegSegmentGroup<Hart>,
132    // Invariant: below `vl` of `group`
133    index: u16,
134}
135
136impl<Hart> SegmentElement<Hart>
137where
138    Hart: VectorHartConfig,
139{
140    /// Element index
141    #[inline(always)]
142    pub const fn index(self) -> u16 {
143        self.index
144    }
145
146    /// All fields of this element in order
147    #[inline(always)]
148    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
149    pub fn fields(self) -> impl Iterator<Item = SegmentField<Hart>> {
150        (0..self.group.nf.fields_per_segment()).map(move |field| SegmentField {
151            element: self,
152            field,
153        })
154    }
155
156    /// Read all fields of this element, zero-extended, into the first `nf` entries, the rest are
157    /// zero
158    #[inline(always)]
159    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
160    pub fn read_fields(
161        self,
162        vregs: &VectorRegisterFile<Hart>,
163    ) -> [u64; const { usize::from(Nf::MAX.fields_per_segment()) }] {
164        let mut fields = [0; _];
165        for (field, value) in (0..self.group.nf.fields_per_segment()).zip(&mut fields) {
166            // SAFETY: `field < nf`, the field has the same layout as the first one by the invariant
167            // of `VRegSegmentGroup` and the element index is below `vl <= vlmax` by the invariant
168            // of `SegmentElement`
169            unsafe {
170                let base = self.group.field_base(field);
171                *value = vregs.read_element(base, self.index, self.group.first.eew);
172            }
173        }
174        fields
175    }
176
177    /// Write the low bits of `fields[f]` into field `f` of this element, for each field that
178    /// exists, the rest of `fields` is ignored
179    #[inline(always)]
180    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
181    pub fn write_fields(self, vregs: &mut VectorRegisterFile<Hart>, fields: &[u64]) {
182        for (field, &value) in (0..self.group.nf.fields_per_segment()).zip(fields) {
183            // SAFETY: Same as in `Self::read_fields()`
184            unsafe {
185                let base = self.group.field_base(field);
186                vregs.write_element(base, self.index, self.group.first.eew, value);
187            }
188        }
189    }
190}
191
192/// Field of a [`SegmentElement`], see [`SegmentElement::fields()`]
193#[derive(Debug, Clone, Copy)]
194pub struct SegmentField<Hart>
195where
196    Hart: VectorHartConfig,
197{
198    element: SegmentElement<Hart>,
199    // Invariant: below the number of fields of the group
200    field: u8,
201}
202
203impl<Hart> SegmentField<Hart>
204where
205    Hart: VectorHartConfig,
206{
207    /// Field index
208    #[inline(always)]
209    pub const fn index(self) -> u8 {
210        self.field
211    }
212
213    /// Write the low bits of `value` into this field
214    #[inline(always)]
215    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
216    pub fn write(self, vregs: &mut VectorRegisterFile<Hart>, value: u64) {
217        let element = self.element;
218        // SAFETY: `field < nf` by the invariant, the field has the same layout as the first one by
219        // the invariant of `VRegSegmentGroup` and the element index is below `vl <= vlmax` by the
220        // invariant of `SegmentElement`
221        unsafe {
222            let base = element.group.field_base(self.field);
223            vregs.write_element(base, element.index, element.group.first.eew, value);
224        }
225    }
226}
227
228/// Segment group of an indexed load or store together with its group of indices, see
229/// [`VRegSegmentGroup::with_index()`]
230#[derive(Debug, Clone, Copy)]
231pub struct IndexedSegmentGroup<Hart>
232where
233    Hart: VectorHartConfig,
234{
235    data: VRegSegmentGroup<Hart>,
236    // Invariant: same `vl` as the first field of `data`
237    index: VRegGroup<Hart>,
238}
239
240impl<Hart> IndexedSegmentGroup<Hart>
241where
242    Hart: VectorHartConfig,
243{
244    /// Body elements `start..vl`, see [`VRegSegmentGroup::elements()`]
245    #[inline(always)]
246    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
247    pub fn elements(self, start: u16) -> impl Iterator<Item = IndexedSegmentElement<Hart>> {
248        self.data
249            .elements(start)
250            .map(move |element| IndexedSegmentElement {
251                element,
252                index: self.index,
253            })
254    }
255}
256
257/// Body element of an [`IndexedSegmentGroup`], see [`IndexedSegmentGroup::elements()`]
258#[derive(Debug, Clone, Copy)]
259pub struct IndexedSegmentElement<Hart>
260where
261    Hart: VectorHartConfig,
262{
263    element: SegmentElement<Hart>,
264    // Invariant: same `vl` as the segment group of `element`
265    index: VRegGroup<Hart>,
266}
267
268impl<Hart> IndexedSegmentElement<Hart>
269where
270    Hart: VectorHartConfig,
271{
272    /// The element of the segment group
273    #[inline(always)]
274    pub const fn element(self) -> SegmentElement<Hart> {
275        self.element
276    }
277
278    /// The index of this element, zero-extended, which is the offset of indexed loads and stores
279    #[inline(always)]
280    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
281    pub fn read_index(self, vregs: &VectorRegisterFile<Hart>) -> u64 {
282        // SAFETY: The element index is below `vl` of its segment group by the invariant of
283        // `SegmentElement`, which is `vl <= vlmax` of the group of indices by the invariant, and
284        // those elements lie within the register file by the invariant of `VRegGroup`
285        unsafe { vregs.read_element(self.index.base, self.element.index, self.index.eew) }
286    }
287}