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