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::{VectorRegisters, 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, Hart> ExecutableInstructionOperands for ZveXxStoreInstruction<Hart>
21where
22    Reg: Register,
23    Hart: VectorHartConfig<Reg = Reg>,
24{
25}
26
27#[instruction_execution]
28const impl<Reg, Hart, Env> ExecutableInstructionCsr<Env> for ZveXxStoreInstruction<Hart>
29where
30    Reg: Register,
31    Hart: VectorHartConfig<Reg = Reg>,
32{
33}
34
35#[instruction_execution]
36impl<Reg, Hart, Regs, Env, Memory, PC> ExecutableInstruction<Regs, Env, Memory, PC>
37    for ZveXxStoreInstruction<Hart>
38where
39    Reg: Register,
40    Hart: VectorHartConfig<Reg = Reg>,
41    Regs: RegisterFile<Reg>,
42    Env: VectorRegistersExt<Hart = Hart>,
43    Memory: VirtualMemory,
44    PC: ProgramCounter<Reg::Type, Memory>,
45{
46    #[inline(always)]
47    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
48    fn execute(
49        self,
50        Rs1Rs2OperandValues {
51            rs1_value,
52            rs2_value,
53        }: Rs1Rs2OperandValues<Reg::Type>,
54        _regs: &mut Regs,
55        env: &mut Env,
56        memory: &mut Memory,
57        program_counter: &PC,
58    ) -> ExecutionResult<Reg> {
59        match self {
60            // Whole-register store: stores `nreg` consecutive registers starting at `vs3` directly
61            // to memory as a flat byte array of `EVL = nreg * VLEN.bytes()` bytes. `vs3` must be
62            // aligned to `nreg`. Ignores vtype, vl, masking. Honors `vstart` in byte
63            // units: the first `vstart` bytes are skipped. If `vstart >= EVL`, the
64            // instruction is a no-op.
65            Self::Vsr { vs3, rs1: _, nreg } => {
66                let nreg = nreg.num_registers();
67                if !env.vector_instructions_allowed() {
68                    ::core::hint::cold_path();
69                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
70                        address: PackedAddress::new(
71                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
72                        ),
73                    });
74                }
75                let Some(vs3) = zvexx_load_helpers::VRegGroup::whole_registers(vs3, nreg, Eew::E8)
76                else {
77                    ::core::hint::cold_path();
78                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
79                        address: PackedAddress::new(
80                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
81                        ),
82                    });
83                };
84                // `evl = NREG * VLEN / 8` elements with `EEW = 8`, regardless of `vtype` and `vl`
85                let vl = vs3.vl().get();
86                // `vstart >= evl` is reserved
87                if env.vstart() >= vl {
88                    ::core::hint::cold_path();
89                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
90                        address: PackedAddress::new(
91                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
92                        ),
93                    });
94                }
95                zvexx_store_helpers::execute_unit_stride_store(
96                    env,
97                    memory,
98                    zvexx_load_helpers::VRegSegmentGroup::single(vs3),
99                    true,
100                    rs1_value.as_u64(),
101                )?;
102            }
103            // Mask store: stores `ceil(vl / 8)` bytes from `vs3` to memory with no masking.
104            // Illegal with vill set, it depends on vtype indirectly through its constraints on vl.
105            // Honors `vstart` at byte granularity: the first `vstart / 8` bytes are skipped.
106            Self::Vsm { vs3, rs1: _ } => {
107                if !env.vector_instructions_allowed() {
108                    ::core::hint::cold_path();
109                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
110                        address: PackedAddress::new(
111                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
112                        ),
113                    });
114                }
115                let Some(config) = env.vector_config() else {
116                    ::core::hint::cold_path();
117                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
118                        address: PackedAddress::new(
119                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
120                        ),
121                    });
122                };
123                // `evl = ceil(vl / 8)` elements with `EEW = 8`, which never exceeds a single
124                // register
125                let Some(vs3) =
126                    zvexx_load_helpers::VRegGroup::whole_registers(vs3, VRegGroupSize::R1, Eew::E8)
127                        .and_then(|vs3| vs3.with_vl(Vl::from(config.vl().bytes())))
128                else {
129                    ::core::hint::cold_path();
130                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
131                        address: PackedAddress::new(
132                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
133                        ),
134                    });
135                };
136                zvexx_store_helpers::execute_unit_stride_store(
137                    env,
138                    memory,
139                    zvexx_load_helpers::VRegSegmentGroup::single(vs3),
140                    true,
141                    rs1_value.as_u64(),
142                )?;
143            }
144            // Unit-stride store.
145            //
146            // Source EMUL = EEW/SEW * LMUL, computed via `eew_register_count`. This gives
147            // `group_regs` such that `VLMAX = group_regs * VLEN.bytes() / eew.bytes()` matches the
148            // architectural `vl`.
149            Self::Vse {
150                vs3,
151                rs1: _,
152                vm,
153                eew,
154            } => {
155                if !env.vector_instructions_allowed() {
156                    ::core::hint::cold_path();
157                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
158                        address: PackedAddress::new(
159                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
160                        ),
161                    });
162                }
163                let Some(config) = env.vector_config() else {
164                    ::core::hint::cold_path();
165                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
166                        address: PackedAddress::new(
167                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
168                        ),
169                    });
170                };
171                let vs3 =
172                    zvexx_helpers::vreg_group::<Reg, Env, _, _>(program_counter, config, vs3, eew)?;
173                zvexx_store_helpers::execute_unit_stride_store(
174                    env,
175                    memory,
176                    zvexx_load_helpers::VRegSegmentGroup::single(vs3),
177                    vm,
178                    rs1_value.as_u64(),
179                )?;
180            }
181            // Strided store
182            Self::Vsse {
183                vs3,
184                rs1: _,
185                rs2: _,
186                vm,
187                eew,
188            } => {
189                if !env.vector_instructions_allowed() {
190                    ::core::hint::cold_path();
191                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
192                        address: PackedAddress::new(
193                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
194                        ),
195                    });
196                }
197                let Some(config) = env.vector_config() else {
198                    ::core::hint::cold_path();
199                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
200                        address: PackedAddress::new(
201                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
202                        ),
203                    });
204                };
205                let vs3 =
206                    zvexx_helpers::vreg_group::<Reg, Env, _, _>(program_counter, config, vs3, eew)?;
207                let stride = rs2_value.as_i64();
208                zvexx_store_helpers::execute_strided_store(
209                    env,
210                    memory,
211                    zvexx_load_helpers::VRegSegmentGroup::single(vs3),
212                    vm,
213                    rs1_value.as_u64(),
214                    stride,
215                )?;
216            }
217            // Indexed-unordered store. Ordering between elements is not guaranteed.
218            Self::Vsuxei {
219                vs3,
220                rs1: _,
221                vs2,
222                vm,
223                eew: index_eew,
224            } => {
225                if !env.vector_instructions_allowed() {
226                    ::core::hint::cold_path();
227                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
228                        address: PackedAddress::new(
229                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
230                        ),
231                    });
232                }
233                let Some(config) = env.vector_config() else {
234                    ::core::hint::cold_path();
235                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
236                        address: PackedAddress::new(
237                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
238                        ),
239                    });
240                };
241                let data_eew = config.vtype().vsew().as_eew();
242                let vs3 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
243                    program_counter,
244                    config,
245                    vs3,
246                    data_eew,
247                )?;
248                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
249                    program_counter,
250                    config,
251                    vs2,
252                    index_eew,
253                )?;
254                // The index `vs2` and the data `vs3` are both sources, with different EEWs unless
255                // they happen to match
256                if index_eew != data_eew {
257                    zvexx_helpers::check_groups_disjoint::<Reg, Env, _, _>(
258                        program_counter,
259                        vs2,
260                        vs3,
261                    )?;
262                }
263                zvexx_store_helpers::execute_indexed_store(
264                    env,
265                    memory,
266                    zvexx_load_helpers::VRegSegmentGroup::single(vs3),
267                    vs2,
268                    vm,
269                    rs1_value.as_u64(),
270                )?;
271            }
272            // Indexed-ordered store. Elements must be written in element order.
273            // The ordering constraint is visible only to other harts/devices; the implementation
274            // here is already sequential, so no additional logic is needed.
275            Self::Vsoxei {
276                vs3,
277                rs1: _,
278                vs2,
279                vm,
280                eew: index_eew,
281            } => {
282                if !env.vector_instructions_allowed() {
283                    ::core::hint::cold_path();
284                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
285                        address: PackedAddress::new(
286                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
287                        ),
288                    });
289                }
290                let Some(config) = env.vector_config() else {
291                    ::core::hint::cold_path();
292                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
293                        address: PackedAddress::new(
294                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
295                        ),
296                    });
297                };
298                let data_eew = config.vtype().vsew().as_eew();
299                let vs3 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
300                    program_counter,
301                    config,
302                    vs3,
303                    data_eew,
304                )?;
305                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
306                    program_counter,
307                    config,
308                    vs2,
309                    index_eew,
310                )?;
311                // The index `vs2` and the data `vs3` are both sources, with different EEWs unless
312                // they happen to match
313                if index_eew != data_eew {
314                    zvexx_helpers::check_groups_disjoint::<Reg, Env, _, _>(
315                        program_counter,
316                        vs2,
317                        vs3,
318                    )?;
319                }
320                zvexx_store_helpers::execute_indexed_store(
321                    env,
322                    memory,
323                    zvexx_load_helpers::VRegSegmentGroup::single(vs3),
324                    vs2,
325                    vm,
326                    rs1_value.as_u64(),
327                )?;
328            }
329            // Unit-stride segment store: `nf` fields per element, stored contiguously
330            Self::Vsseg {
331                vs3,
332                rs1: _,
333                eew,
334                vm_nf,
335            } => {
336                let vm = vm_nf.vm();
337                let nf = vm_nf.nf();
338                if !env.vector_instructions_allowed() {
339                    ::core::hint::cold_path();
340                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
341                        address: PackedAddress::new(
342                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
343                        ),
344                    });
345                }
346                let Some(config) = env.vector_config() else {
347                    ::core::hint::cold_path();
348                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
349                        address: PackedAddress::new(
350                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
351                        ),
352                    });
353                };
354                let vs3 =
355                    zvexx_helpers::vreg_group::<Reg, Env, _, _>(program_counter, config, vs3, eew)?;
356                let vs3 =
357                    zvexx_helpers::vreg_segment_group::<Reg, Env, _, _>(program_counter, vs3, nf)?;
358                zvexx_store_helpers::execute_unit_stride_store(
359                    env,
360                    memory,
361                    vs3,
362                    vm,
363                    rs1_value.as_u64(),
364                )?;
365            }
366            // Strided segment store
367            Self::Vssseg {
368                vs3,
369                rs1: _,
370                rs2: _,
371                eew,
372                vm_nf,
373            } => {
374                let vm = vm_nf.vm();
375                let nf = vm_nf.nf();
376                if !env.vector_instructions_allowed() {
377                    ::core::hint::cold_path();
378                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
379                        address: PackedAddress::new(
380                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
381                        ),
382                    });
383                }
384                let Some(config) = env.vector_config() else {
385                    ::core::hint::cold_path();
386                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
387                        address: PackedAddress::new(
388                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
389                        ),
390                    });
391                };
392                let vs3 =
393                    zvexx_helpers::vreg_group::<Reg, Env, _, _>(program_counter, config, vs3, eew)?;
394                let vs3 =
395                    zvexx_helpers::vreg_segment_group::<Reg, Env, _, _>(program_counter, vs3, nf)?;
396                let stride = rs2_value.as_i64();
397                zvexx_store_helpers::execute_strided_store(
398                    env,
399                    memory,
400                    vs3,
401                    vm,
402                    rs1_value.as_u64(),
403                    stride,
404                )?;
405            }
406            // Indexed-unordered segment store
407            Self::Vsuxseg {
408                vs3,
409                rs1: _,
410                vs2,
411                eew: index_eew,
412                vm_nf,
413            } => {
414                let vm = vm_nf.vm();
415                let nf = vm_nf.nf();
416                if !env.vector_instructions_allowed() {
417                    ::core::hint::cold_path();
418                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
419                        address: PackedAddress::new(
420                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
421                        ),
422                    });
423                }
424                let Some(config) = env.vector_config() else {
425                    ::core::hint::cold_path();
426                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
427                        address: PackedAddress::new(
428                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
429                        ),
430                    });
431                };
432                let data_eew = config.vtype().vsew().as_eew();
433                let vs3 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
434                    program_counter,
435                    config,
436                    vs3,
437                    data_eew,
438                )?;
439                let vs3 =
440                    zvexx_helpers::vreg_segment_group::<Reg, Env, _, _>(program_counter, vs3, nf)?;
441                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
442                    program_counter,
443                    config,
444                    vs2,
445                    index_eew,
446                )?;
447                // The index `vs2` and the data `vs3` are both sources, with different EEWs unless
448                // they happen to match across all fields
449                if index_eew != data_eew && vs3.fields().any(|field| field.overlaps(vs2)) {
450                    ::core::hint::cold_path();
451                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
452                        address: PackedAddress::new(
453                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
454                        ),
455                    });
456                }
457                zvexx_store_helpers::execute_indexed_store(
458                    env,
459                    memory,
460                    vs3,
461                    vs2,
462                    vm,
463                    rs1_value.as_u64(),
464                )?;
465            }
466            // Indexed-ordered segment store. Sequential iteration satisfies the ordering
467            // requirement.
468            Self::Vsoxseg {
469                vs3,
470                rs1: _,
471                vs2,
472                eew: index_eew,
473                vm_nf,
474            } => {
475                let vm = vm_nf.vm();
476                let nf = vm_nf.nf();
477                if !env.vector_instructions_allowed() {
478                    ::core::hint::cold_path();
479                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
480                        address: PackedAddress::new(
481                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
482                        ),
483                    });
484                }
485                let Some(config) = env.vector_config() else {
486                    ::core::hint::cold_path();
487                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
488                        address: PackedAddress::new(
489                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
490                        ),
491                    });
492                };
493                let data_eew = config.vtype().vsew().as_eew();
494                let vs3 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
495                    program_counter,
496                    config,
497                    vs3,
498                    data_eew,
499                )?;
500                let vs3 =
501                    zvexx_helpers::vreg_segment_group::<Reg, Env, _, _>(program_counter, vs3, nf)?;
502                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
503                    program_counter,
504                    config,
505                    vs2,
506                    index_eew,
507                )?;
508                // The index `vs2` and the data `vs3` are both sources, with different EEWs unless
509                // they happen to match across all fields
510                if index_eew != data_eew && vs3.fields().any(|field| field.overlaps(vs2)) {
511                    ::core::hint::cold_path();
512                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
513                        address: PackedAddress::new(
514                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
515                        ),
516                    });
517                }
518                zvexx_store_helpers::execute_indexed_store(
519                    env,
520                    memory,
521                    vs3,
522                    vs2,
523                    vm,
524                    rs1_value.as_u64(),
525                )?;
526            }
527        }
528
529        ExecutionResult::ContinueNoWrite
530    }
531}