Skip to main content

ab_riscv_interpreter/v/zvexx/
store.rs

1//! ZveXx vector store instructions
2
3#[cfg(test)]
4mod tests;
5pub mod zvexx_store_helpers;
6
7use crate::v::vector_registers::VectorRegistersExt;
8use crate::v::zvexx::load::zvexx_load_helpers;
9use crate::v::zvexx::zvexx_helpers;
10use crate::{
11    ExecutableInstruction, ExecutableInstructionCsr, ExecutableInstructionOperands, ExecutionError,
12    ProgramCounter, RegisterFile, Rs1Rs2OperandValues, Rs1Rs2Operands, VirtualMemory,
13};
14use ab_riscv_macros::instruction_execution;
15use ab_riscv_primitives::prelude::*;
16use core::fmt;
17use core::ops::ControlFlow;
18
19#[instruction_execution]
20impl<Reg> ExecutableInstructionOperands for ZveXxStoreInstruction<Reg> where Reg: Register {}
21
22#[instruction_execution]
23impl<Reg, ExtState, CustomError> ExecutableInstructionCsr<ExtState, CustomError>
24    for ZveXxStoreInstruction<Reg>
25where
26    Reg: Register,
27{
28}
29
30#[instruction_execution]
31impl<Reg, Regs, ExtState, Memory, PC, InstructionHandler, CustomError>
32    ExecutableInstruction<Regs, ExtState, Memory, PC, InstructionHandler, CustomError>
33    for ZveXxStoreInstruction<Reg>
34where
35    Reg: Register,
36    Regs: RegisterFile<Reg>,
37    ExtState: VectorRegistersExt<Reg, CustomError>,
38    [(); ExtState::ELEN as usize]:,
39    [(); ExtState::VLEN as usize]:,
40    [(); ExtState::VLENB as usize]:,
41    Memory: VirtualMemory,
42    PC: ProgramCounter<Reg::Type, Memory, CustomError>,
43    CustomError: fmt::Debug,
44{
45    #[inline(always)]
46    fn execute(
47        self,
48        Rs1Rs2OperandValues {
49            rs1_value,
50            rs2_value,
51        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
52        _regs: &mut Regs,
53        ext_state: &mut ExtState,
54        memory: &mut Memory,
55        program_counter: &mut PC,
56        _system_instruction_handler: &mut InstructionHandler,
57    ) -> Result<
58        ControlFlow<(), (Self::Reg, <Self::Reg as Register>::Type)>,
59        ExecutionError<Reg::Type, CustomError>,
60    > {
61        match self {
62            // Whole-register store: stores `nreg` consecutive registers starting at `vs3` directly
63            // to memory as a flat byte array of `EVL = nreg * VLENB` bytes. `vs3` must be aligned
64            // to `nreg`. Ignores vtype, vl, masking. Honors `vstart` in byte units: the first
65            // `vstart` bytes are skipped. If `vstart >= EVL`, the instruction is a no-op.
66            Self::Vsr { vs3, rs1: _, nreg } => {
67                let nreg = nreg.num_registers();
68                if !ext_state.vector_instructions_allowed() {
69                    return Err(ExecutionError::IllegalInstruction {
70                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
71                    });
72                }
73                if vs3.to_bits() % nreg != 0 {
74                    return Err(ExecutionError::IllegalInstruction {
75                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
76                    });
77                }
78                let vlenb = u64::from(ExtState::VLENB);
79                let evl = u64::from(nreg) * vlenb;
80                let vstart = ext_state.vstart();
81                if u64::from(vstart) < evl {
82                    let base = rs1_value.as_u64();
83                    let mut byte_off = u64::from(vstart);
84                    while byte_off < evl {
85                        let reg_off = byte_off / vlenb;
86                        let in_reg = (byte_off % vlenb) as usize;
87                        // SAFETY: the decoder guarantees `nreg` in {1,2,4,8} and `vs3` is
88                        // `nreg`-aligned (checked above), so `vs3.to_bits() + nreg - 1 <= 31`
89                        let reg = unsafe {
90                            VReg::from_bits(vs3.to_bits() + reg_off as u8).unwrap_unchecked()
91                        };
92                        // SAFETY: `in_reg < VLENB` by construction
93                        let src =
94                            unsafe { ext_state.read_vregs().get(reg).get_unchecked(in_reg..) };
95                        if let Err(error) = memory.write_slice(base + byte_off, src) {
96                            ext_state.set_vstart(byte_off as u16);
97                            return Err(ExecutionError::MemoryAccess(error));
98                        }
99                        byte_off += src.len() as u64;
100                    }
101                }
102                ext_state.reset_vstart();
103            }
104            // Mask store: stores `ceil(vl / 8)` bytes from `vs3` to memory with no masking.
105            // Does not require a valid vtype: when vill is set vl is 0, so zero bytes are written.
106            // Honors `vstart` at byte granularity: the first `vstart / 8` bytes are skipped.
107            Self::Vsm { vs3, rs1: _ } => {
108                if !ext_state.vector_instructions_allowed() {
109                    return Err(ExecutionError::IllegalInstruction {
110                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
111                    });
112                }
113                let vl = ext_state.vl();
114                let evl_bytes = vl.div_ceil(u8::BITS);
115                let start_byte = ext_state.vstart();
116                if u32::from(start_byte) < evl_bytes {
117                    let base = rs1_value.as_u64();
118                    // SAFETY: `evl_bytes = vl.div_ceil(8) <= VLEN / 8 = VLENB` because
119                    // `vl <= VLMAX <= VLEN`, so the slice `start_byte..evl_bytes` is in bounds of
120                    // the `VLENB`-byte source register
121                    let src = unsafe {
122                        ext_state
123                            .read_vregs()
124                            .get(vs3)
125                            .get_unchecked(usize::from(start_byte)..evl_bytes as usize)
126                    };
127                    memory
128                        .write_slice(base + u64::from(start_byte), src)
129                        .map_err(ExecutionError::MemoryAccess)?;
130                }
131                ext_state.reset_vstart();
132            }
133            // Unit-stride store.
134            //
135            // Source EMUL = EEW/SEW * LMUL, computed via `data_register_count`. This gives
136            // `group_regs` such that `VLMAX = group_regs * VLENB / eew.bytes()` matches the
137            // architectural `vl`.
138            Self::Vse {
139                vs3,
140                rs1: _,
141                vm,
142                eew,
143            } => {
144                if !ext_state.vector_instructions_allowed() {
145                    return Err(ExecutionError::IllegalInstruction {
146                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
147                    });
148                }
149                let vtype = ext_state
150                    .vtype()
151                    .ok_or(ExecutionError::IllegalInstruction {
152                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
153                    })?;
154                let group_regs = vtype.vlmul().data_register_count(eew, vtype.vsew()).ok_or(
155                    ExecutionError::IllegalInstruction {
156                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
157                    },
158                )?;
159                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
160                    program_counter,
161                    vs3,
162                    group_regs,
163                )?;
164                // SAFETY:
165                // - alignment: `check_register_group_alignment` verified `vs3 % group_regs == 0`
166                //   and `vs3 + group_regs <= 32`
167                // - `vl <= group_regs * VLENB / eew.bytes()`: `group_regs` is the EMUL computed for
168                //   this `eew` and `vtype`, so this VLMAX equals the architectural VLMAX that
169                //   bounds `vl`
170                // - vs3/v0 overlap: stores read vs3 as a source; the spec does not restrict
171                //   source/v0 overlap
172                unsafe {
173                    zvexx_store_helpers::execute_unit_stride_store(
174                        ext_state,
175                        memory,
176                        vs3,
177                        vm,
178                        rs1_value.as_u64(),
179                        eew,
180                        group_regs,
181                        Nf::N1,
182                    )?;
183                }
184            }
185            // Strided store
186            Self::Vsse {
187                vs3,
188                rs1: _,
189                rs2: _,
190                vm,
191                eew,
192            } => {
193                if !ext_state.vector_instructions_allowed() {
194                    return Err(ExecutionError::IllegalInstruction {
195                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
196                    });
197                }
198                let vtype = ext_state
199                    .vtype()
200                    .ok_or(ExecutionError::IllegalInstruction {
201                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
202                    })?;
203                let group_regs = vtype.vlmul().data_register_count(eew, vtype.vsew()).ok_or(
204                    ExecutionError::IllegalInstruction {
205                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
206                    },
207                )?;
208                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
209                    program_counter,
210                    vs3,
211                    group_regs,
212                )?;
213                let stride = rs2_value.as_i64();
214                // SAFETY: same preconditions as `Vse`.
215                unsafe {
216                    zvexx_store_helpers::execute_strided_store(
217                        ext_state,
218                        memory,
219                        vs3,
220                        vm,
221                        rs1_value.as_u64(),
222                        stride,
223                        eew,
224                        group_regs,
225                        Nf::N1,
226                    )?;
227                }
228            }
229            // Indexed-unordered store. Ordering between elements is not guaranteed.
230            Self::Vsuxei {
231                vs3,
232                rs1: _,
233                vs2,
234                vm,
235                eew: index_eew,
236            } => {
237                if !ext_state.vector_instructions_allowed() {
238                    return Err(ExecutionError::IllegalInstruction {
239                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
240                    });
241                }
242                let vtype = ext_state
243                    .vtype()
244                    .ok_or(ExecutionError::IllegalInstruction {
245                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
246                    })?;
247                let data_eew = vtype.vsew().as_eew();
248                let data_group_regs = vtype.vlmul().register_count();
249                let index_group_regs = vtype
250                    .vlmul()
251                    .index_register_count(index_eew, vtype.vsew())
252                    .ok_or(ExecutionError::IllegalInstruction {
253                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
254                    })?;
255                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
256                    program_counter,
257                    vs3,
258                    data_group_regs,
259                )?;
260                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
261                    program_counter,
262                    vs2,
263                    index_group_regs,
264                )?;
265                // SAFETY:
266                // - `vs3` alignment/bounds: `check_register_group_alignment` verified both
267                // - `vs2` alignment/bounds: `check_register_group_alignment` verified both
268                // - `vl <= data_group_regs * VLENB / data_eew.bytes()`: `data_group_regs` is the
269                //   EMUL that bounds `vl`
270                // - `vl <= index_group_regs * VLENB / index_eew.bytes()`: `index_register_count`
271                //   returns the EMUL for the index group, which by the same argument bounds `vl`
272                // - vs3/v0 overlap: stores read vs3 as a source; no restriction
273                unsafe {
274                    zvexx_store_helpers::execute_indexed_store(
275                        ext_state,
276                        memory,
277                        vs3,
278                        vs2,
279                        vm,
280                        rs1_value.as_u64(),
281                        data_eew,
282                        index_eew,
283                        data_group_regs,
284                        Nf::N1,
285                    )?;
286                }
287            }
288            // Indexed-ordered store. Elements must be written in element order.
289            // The ordering constraint is visible only to other harts/devices; the implementation
290            // here is already sequential, so no additional logic is needed.
291            Self::Vsoxei {
292                vs3,
293                rs1: _,
294                vs2,
295                vm,
296                eew: index_eew,
297            } => {
298                if !ext_state.vector_instructions_allowed() {
299                    return Err(ExecutionError::IllegalInstruction {
300                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
301                    });
302                }
303                let vtype = ext_state
304                    .vtype()
305                    .ok_or(ExecutionError::IllegalInstruction {
306                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
307                    })?;
308                let data_eew = vtype.vsew().as_eew();
309                let data_group_regs = vtype.vlmul().register_count();
310                let index_group_regs = vtype
311                    .vlmul()
312                    .index_register_count(index_eew, vtype.vsew())
313                    .ok_or(ExecutionError::IllegalInstruction {
314                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
315                    })?;
316                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
317                    program_counter,
318                    vs3,
319                    data_group_regs,
320                )?;
321                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
322                    program_counter,
323                    vs2,
324                    index_group_regs,
325                )?;
326                // SAFETY: identical precondition argument to `Vsuxei`
327                unsafe {
328                    zvexx_store_helpers::execute_indexed_store(
329                        ext_state,
330                        memory,
331                        vs3,
332                        vs2,
333                        vm,
334                        rs1_value.as_u64(),
335                        data_eew,
336                        index_eew,
337                        data_group_regs,
338                        Nf::N1,
339                    )?;
340                }
341            }
342            // Unit-stride segment store: `nf` fields per element, stored contiguously
343            Self::Vsseg {
344                vs3,
345                rs1: _,
346                eew,
347                vm_nf,
348            } => {
349                let vm = vm_nf.vm();
350                let nf = vm_nf.nf();
351                if !ext_state.vector_instructions_allowed() {
352                    return Err(ExecutionError::IllegalInstruction {
353                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
354                    });
355                }
356                let vtype = ext_state
357                    .vtype()
358                    .ok_or(ExecutionError::IllegalInstruction {
359                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
360                    })?;
361                let group_regs = vtype.vlmul().data_register_count(eew, vtype.vsew()).ok_or(
362                    ExecutionError::IllegalInstruction {
363                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
364                    },
365                )?;
366                zvexx_store_helpers::validate_segment_store_registers::<Reg, _, _, _>(
367                    program_counter,
368                    vs3,
369                    group_regs,
370                    nf,
371                )?;
372                // SAFETY:
373                // - `validate_segment_store_registers` guarantees `vs3 % group_regs == 0` and `vs3
374                //   + nf * group_regs <= 32`
375                // - `vl <= group_regs * VLENB / eew.bytes()`: same EMUL argument as `Vse`
376                // - vs3/v0 overlap: stores read vs3 as a source; no restriction
377                unsafe {
378                    zvexx_store_helpers::execute_unit_stride_store(
379                        ext_state,
380                        memory,
381                        vs3,
382                        vm,
383                        rs1_value.as_u64(),
384                        eew,
385                        group_regs,
386                        nf,
387                    )?;
388                }
389            }
390            // Strided segment store
391            Self::Vssseg {
392                vs3,
393                rs1: _,
394                rs2: _,
395                eew,
396                vm_nf,
397            } => {
398                let vm = vm_nf.vm();
399                let nf = vm_nf.nf();
400                if !ext_state.vector_instructions_allowed() {
401                    return Err(ExecutionError::IllegalInstruction {
402                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
403                    });
404                }
405                let vtype = ext_state
406                    .vtype()
407                    .ok_or(ExecutionError::IllegalInstruction {
408                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
409                    })?;
410                let group_regs = vtype.vlmul().data_register_count(eew, vtype.vsew()).ok_or(
411                    ExecutionError::IllegalInstruction {
412                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
413                    },
414                )?;
415                zvexx_store_helpers::validate_segment_store_registers::<Reg, _, _, _>(
416                    program_counter,
417                    vs3,
418                    group_regs,
419                    nf,
420                )?;
421                let stride = rs2_value.as_i64();
422                // SAFETY: same as `Vsseg`.
423                unsafe {
424                    zvexx_store_helpers::execute_strided_store(
425                        ext_state,
426                        memory,
427                        vs3,
428                        vm,
429                        rs1_value.as_u64(),
430                        stride,
431                        eew,
432                        group_regs,
433                        nf,
434                    )?;
435                }
436            }
437            // Indexed-unordered segment store
438            Self::Vsuxseg {
439                vs3,
440                rs1: _,
441                vs2,
442                eew: index_eew,
443                vm_nf,
444            } => {
445                let vm = vm_nf.vm();
446                let nf = vm_nf.nf();
447                if !ext_state.vector_instructions_allowed() {
448                    return Err(ExecutionError::IllegalInstruction {
449                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
450                    });
451                }
452                let vtype = ext_state
453                    .vtype()
454                    .ok_or(ExecutionError::IllegalInstruction {
455                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
456                    })?;
457                let data_eew = vtype.vsew().as_eew();
458                let data_group_regs = vtype.vlmul().register_count();
459                let index_group_regs = vtype
460                    .vlmul()
461                    .index_register_count(index_eew, vtype.vsew())
462                    .ok_or(ExecutionError::IllegalInstruction {
463                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
464                    })?;
465                zvexx_store_helpers::validate_segment_store_registers::<Reg, _, _, _>(
466                    program_counter,
467                    vs3,
468                    data_group_regs,
469                    nf,
470                )?;
471                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
472                    program_counter,
473                    vs2,
474                    index_group_regs,
475                )?;
476                // SAFETY:
477                // - `validate_segment_store_registers` covers `vs3` alignment/bounds
478                // - `check_register_group_alignment` covers `vs2` alignment/bounds
479                // - `vl` bounded by both EMUL groups as in `Vsuxei`
480                // - vs3/v0 overlap: stores read vs3 as a source; no restriction
481                unsafe {
482                    zvexx_store_helpers::execute_indexed_store(
483                        ext_state,
484                        memory,
485                        vs3,
486                        vs2,
487                        vm,
488                        rs1_value.as_u64(),
489                        data_eew,
490                        index_eew,
491                        data_group_regs,
492                        nf,
493                    )?;
494                }
495            }
496            // Indexed-ordered segment store. Sequential iteration satisfies the ordering
497            // requirement.
498            Self::Vsoxseg {
499                vs3,
500                rs1: _,
501                vs2,
502                eew: index_eew,
503                vm_nf,
504            } => {
505                let vm = vm_nf.vm();
506                let nf = vm_nf.nf();
507                if !ext_state.vector_instructions_allowed() {
508                    return Err(ExecutionError::IllegalInstruction {
509                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
510                    });
511                }
512                let vtype = ext_state
513                    .vtype()
514                    .ok_or(ExecutionError::IllegalInstruction {
515                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
516                    })?;
517                let data_eew = vtype.vsew().as_eew();
518                let data_group_regs = vtype.vlmul().register_count();
519                let index_group_regs = vtype
520                    .vlmul()
521                    .index_register_count(index_eew, vtype.vsew())
522                    .ok_or(ExecutionError::IllegalInstruction {
523                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
524                    })?;
525                zvexx_store_helpers::validate_segment_store_registers::<Reg, _, _, _>(
526                    program_counter,
527                    vs3,
528                    data_group_regs,
529                    nf,
530                )?;
531                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
532                    program_counter,
533                    vs2,
534                    index_group_regs,
535                )?;
536                // SAFETY: identical precondition argument to `Vsuxseg`
537                unsafe {
538                    zvexx_store_helpers::execute_indexed_store(
539                        ext_state,
540                        memory,
541                        vs3,
542                        vs2,
543                        vm,
544                        rs1_value.as_u64(),
545                        data_eew,
546                        index_eew,
547                        data_group_regs,
548                        nf,
549                    )?;
550                }
551            }
552        }
553
554        Ok(ControlFlow::Continue(Default::default()))
555    }
556}