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