Skip to main content

ab_riscv_interpreter/v/zvexx/
load.rs

1//! ZveXx vector load instructions
2
3#[cfg(test)]
4mod tests;
5pub mod zvexx_load_helpers;
6
7use crate::v::vector_registers::VectorRegistersExt;
8use crate::v::zvexx::zvexx_helpers;
9use crate::{
10    ExecutableInstruction, ExecutableInstructionCsr, ExecutableInstructionOperands, ExecutionError,
11    ExecutionResult, FetchInstructionResult, InstructionFetcher, OpaqueThreadedExecutionResult,
12    PackedAddress, ProgramCounter, RegisterFile, Rs1Rs2OperandValues, Rs1Rs2Operands,
13    ThreadedExecutableInstruction, ThreadedExecutionResult, VirtualMemory,
14};
15use ab_riscv_macros::instruction_execution;
16use ab_riscv_primitives::prelude::*;
17
18#[instruction_execution]
19const impl<Reg> ExecutableInstructionOperands for ZveXxLoadInstruction<Reg> where Reg: Register {}
20
21#[instruction_execution]
22const impl<Reg, Env> ExecutableInstructionCsr<Env> for ZveXxLoadInstruction<Reg> where Reg: Register {}
23
24#[instruction_execution]
25impl<Reg, Regs, Env, Memory, PC> ExecutableInstruction<Regs, Env, Memory, PC>
26    for ZveXxLoadInstruction<Reg>
27where
28    Reg: Register,
29    Regs: RegisterFile<Reg>,
30    Env: VectorRegistersExt<Reg>,
31    [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
32    Memory: VirtualMemory,
33    PC: ProgramCounter<Reg::Type, Memory>,
34{
35    #[inline(always)]
36    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
37    fn execute(
38        self,
39        Rs1Rs2OperandValues {
40            rs1_value,
41            rs2_value,
42        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
43        _regs: &mut Regs,
44        env: &mut Env,
45        memory: &mut Memory,
46        program_counter: &mut PC,
47    ) -> ExecutionResult<Self::Reg> {
48        match self {
49            // Whole-register load: loads `nreg` consecutive registers starting at `vd` directly
50            // from memory. `vd` must be aligned to `nreg`. Ignores vtype, vl, vstart, masking.
51            Self::Vlr {
52                vd,
53                rs1: _,
54                nreg,
55                eew: _,
56            } => {
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 !vd.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 base = rs1_value.as_u64();
75                for reg_off in 0..nreg {
76                    // SAFETY: the decoder guarantees nreg in {1,2,4,8} and vd is nreg-aligned
77                    // (checked above), so vd.to_bits() + nreg - 1 <= 31.
78                    let reg = unsafe { VReg::from_bits(vd.to_bits() + reg_off).unwrap_unchecked() };
79                    let bytes = memory
80                        .read_slice(
81                            base + u64::from(reg_off) * u64::from(Env::VLEN.bytes()),
82                            Env::VLEN.bytes(),
83                        )
84                        .inspect_err(|_error| {
85                            if reg_off > 0 {
86                                env.mark_vs_dirty();
87                                env.reset_vstart();
88                            }
89                        })?;
90                    env.write_vregs().get_mut(reg).copy_from_slice(bytes);
91                }
92                env.mark_vs_dirty();
93                env.reset_vstart();
94            }
95
96            // Mask load: loads ceil(vl / 8) bytes from base into vd with no masking applied.
97            // Does not require a valid vtype: when vill is set vl is 0, so zero bytes are read.
98            Self::Vlm { vd, rs1: _ } => {
99                if !env.vector_instructions_allowed() {
100                    ::core::hint::cold_path();
101                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
102                        address: PackedAddress::new(
103                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
104                        ),
105                    });
106                }
107                let vl = env.vl();
108                let byte_count = vl.bytes();
109                if byte_count > 0 {
110                    let base = rs1_value.as_u64();
111                    let bytes = memory.read_slice(base, u32::from(byte_count))?;
112                    // SAFETY: `bytes.len() == byte_count = vl.div_ceil(8) <= VLEN / 8 =
113                    // VLEN.bytes()` because `vl <= VLMAX <= VLEN`, so
114                    // `..bytes.len()` is in bounds within the
115                    // `VLEN.bytes()`-byte destination register.
116                    unsafe {
117                        env.write_vregs()
118                            .get_mut(vd)
119                            .get_unchecked_mut(..bytes.len())
120                            .copy_from_slice(bytes);
121                    }
122                }
123                env.mark_vs_dirty();
124                env.reset_vstart();
125            }
126
127            // Unit-stride load.
128            //
129            // Destination EMUL = EEW/SEW * LMUL, computed via `index_register_count`. This
130            // gives `group_regs` such that `VLMAX = group_regs * VLEN.bytes() / eew.bytes()`
131            // matches the architectural `vl`.
132            Self::Vle {
133                vd,
134                rs1: _,
135                vm,
136                eew,
137            } => {
138                if !env.vector_instructions_allowed() {
139                    ::core::hint::cold_path();
140                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
141                        address: PackedAddress::new(
142                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
143                        ),
144                    });
145                }
146                let Some(vtype) = env.vtype() else {
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 group_regs = vtype
155                    .vlmul()
156                    .index_register_count(eew, vtype.vsew())
157                    .ok_or(ExecutionError::IllegalInstruction {
158                        address: PackedAddress::new(
159                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
160                        ),
161                    })?;
162                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _>(
163                    program_counter,
164                    vd,
165                    group_regs,
166                )?;
167                if !vm
168                    && zvexx_load_helpers::groups_overlap(
169                        vd,
170                        group_regs,
171                        VReg::V0,
172                        ::core::num::NonZeroU8::new(1).expect("Not zero; qed"),
173                    )
174                {
175                    ::core::hint::cold_path();
176                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
177                        address: PackedAddress::new(
178                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
179                        ),
180                    });
181                }
182                // SAFETY:
183                // - alignment: `check_register_group_alignment` verified `vd % group_regs == 0` and
184                //   `vd + group_regs <= 32`, satisfying both the alignment and nf=1 bounds
185                //   preconditions
186                // - `vl <= group_regs * VLEN.bytes() / eew.bytes()`: `group_regs` is the EMUL
187                //   computed for this `eew` and `vtype`, so this VLMAX equals the architectural
188                //   VLMAX that bounds `vl`
189                // - mask overlap: checked above via `groups_overlap`
190                unsafe {
191                    zvexx_load_helpers::execute_unit_stride_load::<false, _, _, _>(
192                        env,
193                        memory,
194                        vd,
195                        vm,
196                        rs1_value.as_u64(),
197                        eew,
198                        group_regs,
199                        Nf::N1,
200                    )?;
201                }
202            }
203
204            // Fault-only-first unit-stride load. Preconditions identical to `Vle`.
205            Self::Vleff {
206                vd,
207                rs1: _,
208                vm,
209                eew,
210            } => {
211                if !env.vector_instructions_allowed() {
212                    ::core::hint::cold_path();
213                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
214                        address: PackedAddress::new(
215                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
216                        ),
217                    });
218                }
219                let Some(vtype) = env.vtype() else {
220                    ::core::hint::cold_path();
221                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
222                        address: PackedAddress::new(
223                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
224                        ),
225                    });
226                };
227                let group_regs = vtype
228                    .vlmul()
229                    .index_register_count(eew, vtype.vsew())
230                    .ok_or(ExecutionError::IllegalInstruction {
231                        address: PackedAddress::new(
232                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
233                        ),
234                    })?;
235                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _>(
236                    program_counter,
237                    vd,
238                    group_regs,
239                )?;
240                if !vm
241                    && zvexx_load_helpers::groups_overlap(
242                        vd,
243                        group_regs,
244                        VReg::V0,
245                        ::core::num::NonZeroU8::new(1).expect("Not zero; qed"),
246                    )
247                {
248                    ::core::hint::cold_path();
249                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
250                        address: PackedAddress::new(
251                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
252                        ),
253                    });
254                }
255                // SAFETY: preconditions identical to `Vle`; see that arm for the full argument.
256                unsafe {
257                    zvexx_load_helpers::execute_unit_stride_load::<true, _, _, _>(
258                        env,
259                        memory,
260                        vd,
261                        vm,
262                        rs1_value.as_u64(),
263                        eew,
264                        group_regs,
265                        Nf::N1,
266                    )?;
267                }
268            }
269
270            // Strided load. Destination EMUL = EEW/SEW * LMUL as for unit-stride.
271            Self::Vlse {
272                vd,
273                rs1: _,
274                rs2: _,
275                vm,
276                eew,
277            } => {
278                if !env.vector_instructions_allowed() {
279                    ::core::hint::cold_path();
280                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
281                        address: PackedAddress::new(
282                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
283                        ),
284                    });
285                }
286                let Some(vtype) = env.vtype() else {
287                    ::core::hint::cold_path();
288                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
289                        address: PackedAddress::new(
290                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
291                        ),
292                    });
293                };
294                let group_regs = vtype
295                    .vlmul()
296                    .index_register_count(eew, vtype.vsew())
297                    .ok_or(ExecutionError::IllegalInstruction {
298                        address: PackedAddress::new(
299                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
300                        ),
301                    })?;
302                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _>(
303                    program_counter,
304                    vd,
305                    group_regs,
306                )?;
307                if !vm
308                    && zvexx_load_helpers::groups_overlap(
309                        vd,
310                        group_regs,
311                        VReg::V0,
312                        ::core::num::NonZeroU8::new(1).expect("Not zero; qed"),
313                    )
314                {
315                    ::core::hint::cold_path();
316                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
317                        address: PackedAddress::new(
318                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
319                        ),
320                    });
321                }
322                // rs2 holds a signed stride; reinterpret the register value as signed
323                let stride = rs2_value.as_i64();
324                // SAFETY:
325                // - alignment and nf=1 bounds: `check_register_group_alignment` verified `vd %
326                //   group_regs == 0` and `vd + group_regs <= 32`
327                // - `vl <= group_regs * VLEN.bytes() / eew.bytes()`: `group_regs` is the EMUL for
328                //   this `eew` and `vtype`, so this VLMAX equals the architectural VLMAX bounding
329                //   `vl`
330                // - mask overlap: checked above via `groups_overlap`
331                unsafe {
332                    zvexx_load_helpers::execute_strided_load(
333                        env,
334                        memory,
335                        vd,
336                        vm,
337                        rs1_value.as_u64(),
338                        stride,
339                        eew,
340                        group_regs,
341                        Nf::N1,
342                    )?;
343                }
344            }
345
346            // Indexed-unordered load: eew is the index EEW; data EEW comes from vtype.vsew().
347            // The data destination uses the base LMUL (data EEW = SEW for indexed loads).
348            Self::Vluxei {
349                vd,
350                rs1: _,
351                vs2,
352                vm,
353                eew: index_eew,
354            } => {
355                if !env.vector_instructions_allowed() {
356                    ::core::hint::cold_path();
357                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
358                        address: PackedAddress::new(
359                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
360                        ),
361                    });
362                }
363                let Some(vtype) = env.vtype() else {
364                    ::core::hint::cold_path();
365                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
366                        address: PackedAddress::new(
367                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
368                        ),
369                    });
370                };
371                let data_group_regs = vtype.vlmul().register_count();
372                let index_group_regs = vtype
373                    .vlmul()
374                    .index_register_count(index_eew, vtype.vsew())
375                    .ok_or(ExecutionError::IllegalInstruction {
376                        address: PackedAddress::new(
377                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
378                        ),
379                    })?;
380                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _>(
381                    program_counter,
382                    vd,
383                    data_group_regs,
384                )?;
385                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _>(
386                    program_counter,
387                    vs2,
388                    index_group_regs,
389                )?;
390                // Non-segment indexed loads permit `vd`/`vs2` overlap under the general
391                // EEW-relative overlap rule (e.g. when the data and index EEW match); only
392                // disallowed overlaps are reserved.
393                if !zvexx_load_helpers::indexed_load_overlap_allowed(
394                    vd,
395                    data_group_regs,
396                    vs2,
397                    index_group_regs,
398                    index_eew,
399                    vtype.vsew(),
400                    vtype.vlmul(),
401                ) {
402                    ::core::hint::cold_path();
403                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
404                        address: PackedAddress::new(
405                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
406                        ),
407                    });
408                }
409                if !vm
410                    && zvexx_load_helpers::groups_overlap(
411                        vd,
412                        data_group_regs,
413                        VReg::V0,
414                        ::core::num::NonZeroU8::new(1).expect("Not zero; qed"),
415                    )
416                {
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                // SAFETY:
425                // - data alignment/nf=1 bounds: `check_register_group_alignment` on `vd`
426                // - index alignment/bounds: `check_register_group_alignment` on `vs2`
427                // - `vl <= data_group_regs * VLEN.bytes() / data_eew.bytes()`: data EEW = SEW and
428                //   `data_group_regs = LMUL`, so VLMAX = LMUL * VLEN / SEW, which bounds `vl`
429                // - `vl <= index_group_regs * VLEN.bytes() / index_eew.bytes()`: `index_group_regs`
430                //   is EMUL_index defined so this VLMAX_index equals the architectural VLMAX
431                // - `vd`/`vs2` overlap (if any) satisfies the general EEW overlap rule, checked
432                //   above; the in-order element loop reads index element `i` before writing data
433                //   element `i`, and that rule guarantees a data write never clobbers an index
434                //   element that has not yet been consumed
435                // - mask overlap: checked above via `groups_overlap`
436                unsafe {
437                    zvexx_load_helpers::execute_indexed_load(
438                        env,
439                        memory,
440                        vd,
441                        vs2,
442                        vm,
443                        rs1_value.as_u64(),
444                        vtype.vsew().as_eew(),
445                        index_eew,
446                        data_group_regs,
447                        Nf::N1,
448                    )?;
449                }
450            }
451
452            // Indexed-ordered load: functionally identical to `Vluxei` for a software
453            // interpreter; memory access ordering has no observable effect here.
454            Self::Vloxei {
455                vd,
456                rs1: _,
457                vs2,
458                vm,
459                eew: index_eew,
460            } => {
461                if !env.vector_instructions_allowed() {
462                    ::core::hint::cold_path();
463                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
464                        address: PackedAddress::new(
465                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
466                        ),
467                    });
468                }
469                let Some(vtype) = env.vtype() else {
470                    ::core::hint::cold_path();
471                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
472                        address: PackedAddress::new(
473                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
474                        ),
475                    });
476                };
477                let data_group_regs = vtype.vlmul().register_count();
478                let index_group_regs = vtype
479                    .vlmul()
480                    .index_register_count(index_eew, vtype.vsew())
481                    .ok_or(ExecutionError::IllegalInstruction {
482                        address: PackedAddress::new(
483                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
484                        ),
485                    })?;
486                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _>(
487                    program_counter,
488                    vd,
489                    data_group_regs,
490                )?;
491                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _>(
492                    program_counter,
493                    vs2,
494                    index_group_regs,
495                )?;
496                // Non-segment indexed loads permit `vd`/`vs2` overlap under the general
497                // EEW-relative overlap rule; see the `Vluxei` arm for details.
498                if !zvexx_load_helpers::indexed_load_overlap_allowed(
499                    vd,
500                    data_group_regs,
501                    vs2,
502                    index_group_regs,
503                    index_eew,
504                    vtype.vsew(),
505                    vtype.vlmul(),
506                ) {
507                    ::core::hint::cold_path();
508                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
509                        address: PackedAddress::new(
510                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
511                        ),
512                    });
513                }
514                if !vm
515                    && zvexx_load_helpers::groups_overlap(
516                        vd,
517                        data_group_regs,
518                        VReg::V0,
519                        ::core::num::NonZeroU8::new(1).expect("Not zero; qed"),
520                    )
521                {
522                    ::core::hint::cold_path();
523                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
524                        address: PackedAddress::new(
525                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
526                        ),
527                    });
528                }
529                // SAFETY: preconditions identical to `Vluxei`; see that arm for the full
530                // argument.
531                unsafe {
532                    zvexx_load_helpers::execute_indexed_load(
533                        env,
534                        memory,
535                        vd,
536                        vs2,
537                        vm,
538                        rs1_value.as_u64(),
539                        vtype.vsew().as_eew(),
540                        index_eew,
541                        data_group_regs,
542                        Nf::N1,
543                    )?;
544                }
545            }
546
547            // Unit-stride segment load. EMUL = EEW/SEW * LMUL per field group.
548            Self::Vlseg {
549                vd,
550                rs1: _,
551                eew,
552                vm_nf,
553            } => {
554                let vm = vm_nf.vm();
555                let nf = vm_nf.nf();
556                if !env.vector_instructions_allowed() {
557                    ::core::hint::cold_path();
558                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
559                        address: PackedAddress::new(
560                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
561                        ),
562                    });
563                }
564                let Some(vtype) = env.vtype() else {
565                    ::core::hint::cold_path();
566                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
567                        address: PackedAddress::new(
568                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
569                        ),
570                    });
571                };
572                let group_regs = vtype
573                    .vlmul()
574                    .index_register_count(eew, vtype.vsew())
575                    .ok_or(ExecutionError::IllegalInstruction {
576                        address: PackedAddress::new(
577                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
578                        ),
579                    })?;
580                zvexx_load_helpers::validate_segment_registers::<Reg, _, _>(
581                    program_counter,
582                    vd,
583                    vm,
584                    group_regs,
585                    nf,
586                )?;
587                // SAFETY:
588                // - alignment and nf-group bounds: `validate_segment_registers` verified `vd %
589                //   group_regs == 0` and `vd + nf * group_regs <= 32`
590                // - `vl <= group_regs * VLEN.bytes() / eew.bytes()`: `group_regs` is the EMUL for
591                //   this `eew` and `vtype`, so this VLMAX equals the architectural VLMAX bounding
592                //   `vl`
593                // - mask overlap with v0: `validate_segment_registers` checked `vd.to_bits() != 0`
594                //   when `vm=false`, ensuring no field group contains v0
595                unsafe {
596                    zvexx_load_helpers::execute_unit_stride_load::<false, _, _, _>(
597                        env,
598                        memory,
599                        vd,
600                        vm,
601                        rs1_value.as_u64(),
602                        eew,
603                        group_regs,
604                        nf,
605                    )?;
606                }
607            }
608
609            // Fault-only-first segment load. Preconditions identical to `Vlseg`.
610            Self::Vlsegff {
611                vd,
612                rs1: _,
613                eew,
614                vm_nf,
615            } => {
616                let vm = vm_nf.vm();
617                let nf = vm_nf.nf();
618                if !env.vector_instructions_allowed() {
619                    ::core::hint::cold_path();
620                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
621                        address: PackedAddress::new(
622                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
623                        ),
624                    });
625                }
626                let Some(vtype) = env.vtype() else {
627                    ::core::hint::cold_path();
628                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
629                        address: PackedAddress::new(
630                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
631                        ),
632                    });
633                };
634                let group_regs = vtype
635                    .vlmul()
636                    .index_register_count(eew, vtype.vsew())
637                    .ok_or(ExecutionError::IllegalInstruction {
638                        address: PackedAddress::new(
639                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
640                        ),
641                    })?;
642                zvexx_load_helpers::validate_segment_registers::<Reg, _, _>(
643                    program_counter,
644                    vd,
645                    vm,
646                    group_regs,
647                    nf,
648                )?;
649                // SAFETY: preconditions identical to `Vlseg`; see that arm for the full argument.
650                unsafe {
651                    zvexx_load_helpers::execute_unit_stride_load::<true, _, _, _>(
652                        env,
653                        memory,
654                        vd,
655                        vm,
656                        rs1_value.as_u64(),
657                        eew,
658                        group_regs,
659                        nf,
660                    )?;
661                }
662            }
663
664            // Strided segment load. EMUL = EEW/SEW * LMUL as for `Vlse`.
665            Self::Vlsseg {
666                vd,
667                rs1: _,
668                rs2: _,
669                eew,
670                vm_nf,
671            } => {
672                let vm = vm_nf.vm();
673                let nf = vm_nf.nf();
674                if !env.vector_instructions_allowed() {
675                    ::core::hint::cold_path();
676                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
677                        address: PackedAddress::new(
678                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
679                        ),
680                    });
681                }
682                let Some(vtype) = env.vtype() else {
683                    ::core::hint::cold_path();
684                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
685                        address: PackedAddress::new(
686                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
687                        ),
688                    });
689                };
690                let group_regs = vtype
691                    .vlmul()
692                    .index_register_count(eew, vtype.vsew())
693                    .ok_or(ExecutionError::IllegalInstruction {
694                        address: PackedAddress::new(
695                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
696                        ),
697                    })?;
698                zvexx_load_helpers::validate_segment_registers::<Reg, _, _>(
699                    program_counter,
700                    vd,
701                    vm,
702                    group_regs,
703                    nf,
704                )?;
705                let stride = rs2_value.as_i64();
706                // SAFETY:
707                // - alignment and nf-group bounds: `validate_segment_registers` verified `vd %
708                //   group_regs == 0` and `vd + nf * group_regs <= 32`
709                // - `vl <= group_regs * VLEN.bytes() / eew.bytes()`: `group_regs` is EMUL for this
710                //   `eew` and `vtype`
711                // - mask overlap: `validate_segment_registers` checked `vd.to_bits() != 0` when
712                //   `vm=false`
713                unsafe {
714                    zvexx_load_helpers::execute_strided_load(
715                        env,
716                        memory,
717                        vd,
718                        vm,
719                        rs1_value.as_u64(),
720                        stride,
721                        eew,
722                        group_regs,
723                        nf,
724                    )?;
725                }
726            }
727
728            // Indexed-unordered segment load
729            Self::Vluxseg {
730                vd,
731                rs1: _,
732                vs2,
733                eew: index_eew,
734                vm_nf,
735            } => {
736                let vm = vm_nf.vm();
737                let nf = vm_nf.nf();
738                if !env.vector_instructions_allowed() {
739                    ::core::hint::cold_path();
740                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
741                        address: PackedAddress::new(
742                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
743                        ),
744                    });
745                }
746                let Some(vtype) = env.vtype() else {
747                    ::core::hint::cold_path();
748                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
749                        address: PackedAddress::new(
750                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
751                        ),
752                    });
753                };
754                let data_group_regs = vtype.vlmul().register_count();
755                let index_group_regs = vtype
756                    .vlmul()
757                    .index_register_count(index_eew, vtype.vsew())
758                    .ok_or(ExecutionError::IllegalInstruction {
759                        address: PackedAddress::new(
760                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
761                        ),
762                    })?;
763                // `validate_segment_registers` is called before the per-field overlap loop so
764                // that `vd.to_bits() + f * data_group_regs < 32` is established for all `f < nf`,
765                // which is required by the `VReg::from_bits` call inside the loop.
766                zvexx_load_helpers::validate_segment_registers::<Reg, _, _>(
767                    program_counter,
768                    vd,
769                    vm,
770                    data_group_regs,
771                    nf,
772                )?;
773                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _>(
774                    program_counter,
775                    vs2,
776                    index_group_regs,
777                )?;
778                for f in 0..nf.fields_per_segment() {
779                    // SAFETY: `vd.to_bits() + f * data_group_regs < 32` because
780                    // `validate_segment_registers` established `vd.to_bits() + nf * data_group_regs
781                    // <= 32` and `f < nf`. The value is in [0, 31], so it is a valid `VReg`
782                    // encoding.
783                    let field_vd = unsafe {
784                        VReg::from_bits(vd.to_bits() + f * data_group_regs.get()).unwrap_unchecked()
785                    };
786                    if zvexx_load_helpers::groups_overlap(
787                        field_vd,
788                        data_group_regs,
789                        vs2,
790                        index_group_regs,
791                    ) {
792                        ::core::hint::cold_path();
793                        return ExecutionResult::Err(ExecutionError::IllegalInstruction {
794                            address: PackedAddress::new(
795                                program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
796                            ),
797                        });
798                    }
799                }
800                // SAFETY:
801                // - data alignment/nf-group bounds: `validate_segment_registers` verified `vd %
802                //   data_group_regs == 0` and `vd + nf * data_group_regs <= 32`
803                // - index alignment/bounds: `check_register_group_alignment` verified `vs2 %
804                //   EMUL_index == 0` and `vs2 + EMUL_index <= 32`
805                // - no field/index group overlap: verified by the loop above
806                // - `vl <= data_group_regs * VLEN.bytes() / data_eew.bytes()`: data EEW = SEW and
807                //   `data_group_regs = LMUL`, so VLMAX = LMUL * VLEN / SEW bounds `vl`
808                // - `vl <= EMUL_index * VLEN.bytes() / index_eew.bytes()`: `index_group_regs`
809                //   (EMUL_index) is defined so this VLMAX_index equals the architectural VLMAX
810                // - mask overlap: `validate_segment_registers` checked `vd.to_bits() != 0` when
811                //   `vm=false`, and no field group starts at 0 since groups are contiguous from
812                //   `vd` which is nonzero
813                unsafe {
814                    zvexx_load_helpers::execute_indexed_load(
815                        env,
816                        memory,
817                        vd,
818                        vs2,
819                        vm,
820                        rs1_value.as_u64(),
821                        vtype.vsew().as_eew(),
822                        index_eew,
823                        data_group_regs,
824                        nf,
825                    )?;
826                }
827            }
828
829            // Indexed-ordered segment load: functionally identical to `Vluxseg` for a software
830            // interpreter
831            Self::Vloxseg {
832                vd,
833                rs1: _,
834                vs2,
835                eew: index_eew,
836                vm_nf,
837            } => {
838                let vm = vm_nf.vm();
839                let nf = vm_nf.nf();
840                if !env.vector_instructions_allowed() {
841                    ::core::hint::cold_path();
842                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
843                        address: PackedAddress::new(
844                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
845                        ),
846                    });
847                }
848                let Some(vtype) = env.vtype() else {
849                    ::core::hint::cold_path();
850                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
851                        address: PackedAddress::new(
852                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
853                        ),
854                    });
855                };
856                let data_group_regs = vtype.vlmul().register_count();
857                let index_group_regs = vtype
858                    .vlmul()
859                    .index_register_count(index_eew, vtype.vsew())
860                    .ok_or(ExecutionError::IllegalInstruction {
861                        address: PackedAddress::new(
862                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
863                        ),
864                    })?;
865                zvexx_load_helpers::validate_segment_registers::<Reg, _, _>(
866                    program_counter,
867                    vd,
868                    vm,
869                    data_group_regs,
870                    nf,
871                )?;
872                zvexx_load_helpers::check_register_group_alignment::<Reg, _, _>(
873                    program_counter,
874                    vs2,
875                    index_group_regs,
876                )?;
877                for f in 0..nf.fields_per_segment() {
878                    // SAFETY: `vd.to_bits() + f * data_group_regs < 32` because
879                    // `validate_segment_registers` established `vd.to_bits() + nf * data_group_regs
880                    // <= 32` and `f < nf`. The value is in [0, 31], so it is a valid `VReg`
881                    // encoding.
882                    let field_vd = unsafe {
883                        VReg::from_bits(vd.to_bits() + f * data_group_regs.get()).unwrap_unchecked()
884                    };
885                    if zvexx_load_helpers::groups_overlap(
886                        field_vd,
887                        data_group_regs,
888                        vs2,
889                        index_group_regs,
890                    ) {
891                        ::core::hint::cold_path();
892                        return ExecutionResult::Err(ExecutionError::IllegalInstruction {
893                            address: PackedAddress::new(
894                                program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
895                            ),
896                        });
897                    }
898                }
899                // SAFETY: preconditions identical to `Vluxseg`; see that arm for the full
900                // argument
901                unsafe {
902                    zvexx_load_helpers::execute_indexed_load(
903                        env,
904                        memory,
905                        vd,
906                        vs2,
907                        vm,
908                        rs1_value.as_u64(),
909                        vtype.vsew().as_eew(),
910                        index_eew,
911                        data_group_regs,
912                        nf,
913                    )?;
914                }
915            }
916        }
917
918        ExecutionResult::ContinueNoWrite
919    }
920}