Skip to main content

ab_riscv_interpreter/v/zve64x/
load.rs

1//! Zve64x vector load instructions
2
3#[cfg(test)]
4mod tests;
5pub mod zve64x_load_helpers;
6
7use crate::v::vector_registers::VectorRegistersExt;
8use crate::v::zve64x::zve64x_helpers;
9use crate::{
10    ExecutableInstruction, ExecutableInstructionCsr, ExecutableInstructionOperands, ExecutionError,
11    ProgramCounter, RegisterFile, Rs1Rs2OperandValues, Rs1Rs2Operands, VirtualMemory,
12};
13use ab_riscv_macros::instruction_execution;
14use ab_riscv_primitives::prelude::*;
15use core::fmt;
16use core::ops::ControlFlow;
17
18#[instruction_execution]
19impl<Reg> ExecutableInstructionOperands for Zve64xLoadInstruction<Reg> where Reg: Register {}
20
21#[instruction_execution]
22impl<Reg, ExtState, CustomError> ExecutableInstructionCsr<ExtState, CustomError>
23    for Zve64xLoadInstruction<Reg>
24where
25    Reg: Register,
26{
27}
28
29#[instruction_execution]
30impl<Reg, Regs, ExtState, Memory, PC, InstructionHandler, CustomError>
31    ExecutableInstruction<Regs, ExtState, Memory, PC, InstructionHandler, CustomError>
32    for Zve64xLoadInstruction<Reg>
33where
34    Reg: Register,
35    Regs: RegisterFile<Reg>,
36    ExtState: VectorRegistersExt<Reg, CustomError>,
37    [(); ExtState::ELEN as usize]:,
38    [(); ExtState::VLEN as usize]:,
39    [(); ExtState::VLENB as usize]:,
40    Memory: VirtualMemory,
41    PC: ProgramCounter<Reg::Type, Memory, CustomError>,
42    CustomError: fmt::Debug,
43{
44    #[inline(always)]
45    fn execute(
46        self,
47        Rs1Rs2OperandValues {
48            rs1_value,
49            rs2_value,
50        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
51        _regs: &mut Regs,
52        ext_state: &mut ExtState,
53        memory: &mut Memory,
54        program_counter: &mut PC,
55        _system_instruction_handler: &mut InstructionHandler,
56    ) -> Result<
57        ControlFlow<(), (Self::Reg, <Self::Reg as Register>::Type)>,
58        ExecutionError<Reg::Type, CustomError>,
59    > {
60        match self {
61            // Whole-register load: loads `nreg` consecutive registers starting at `vd` directly
62            // from memory. `vd` must be aligned to `nreg`. Ignores vtype, vl, vstart, masking.
63            Self::Vlr {
64                vd,
65                rs1: _,
66                nreg,
67                eew: _,
68            } => {
69                if !ext_state.vector_instructions_allowed() {
70                    Err(ExecutionError::IllegalInstruction {
71                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
72                    })?;
73                }
74                if u32::from(vd.bits()) % u32::from(nreg) != 0 {
75                    Err(ExecutionError::IllegalInstruction {
76                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
77                    })?;
78                }
79                let base = rs1_value.as_u64();
80                let vlenb = u64::from(ExtState::VLENB);
81                for reg_off in 0..u64::from(nreg) {
82                    let reg_idx = u64::from(vd.bits()) + reg_off;
83                    let bytes = match memory.read_slice(base + reg_off * vlenb, ExtState::VLENB) {
84                        Ok(bytes) => bytes,
85                        Err(error) => {
86                            if reg_off > 0 {
87                                ext_state.mark_vs_dirty();
88                                ext_state.reset_vstart();
89                            }
90                            Err(ExecutionError::MemoryAccess(error))?
91                        }
92                    };
93                    // SAFETY: `reg_idx < 32` because the decoder guarantees nreg in {1,2,4,8}
94                    // and vd is nreg-aligned (checked above), so vd.bits() + nreg - 1 <= 31.
95                    // `read_slice` returns a slice of exactly `ExtState::VLENB` bytes on success,
96                    // matching `dst`'s length, so `copy_from_slice` cannot panic.
97                    let dst = unsafe { ext_state.write_vreg().get_unchecked_mut(reg_idx as usize) };
98                    dst.copy_from_slice(bytes);
99                }
100                ext_state.mark_vs_dirty();
101                ext_state.reset_vstart();
102            }
103
104            // Mask load: loads ceil(vl / 8) bytes from base into vd with no masking applied.
105            // Does not require a valid vtype: when vill is set vl is 0, so zero bytes are read.
106            Self::Vlm { vd, rs1: _ } => {
107                if !ext_state.vector_instructions_allowed() {
108                    Err(ExecutionError::IllegalInstruction {
109                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
110                    })?;
111                }
112                let vl = ext_state.vl();
113                let byte_count = vl.div_ceil(u8::BITS);
114                if byte_count > 0 {
115                    let base = rs1_value.as_u64();
116                    let bytes = memory.read_slice(base, byte_count)?;
117                    // SAFETY: `vd.bits() < 32` is guaranteed by the `VReg` type.
118                    // `bytes.len() == byte_count = vl.div_ceil(8) <= VLEN / 8 = VLENB` because
119                    // `vl <= VLMAX <= VLEN`, so `..bytes.len()` is in bounds within the
120                    // `VLENB`-byte destination register.
121                    unsafe {
122                        ext_state
123                            .write_vreg()
124                            .get_unchecked_mut(usize::from(vd.bits()))
125                            .get_unchecked_mut(..bytes.len())
126                            .copy_from_slice(bytes);
127                    }
128                }
129                ext_state.mark_vs_dirty();
130                ext_state.reset_vstart();
131            }
132
133            // Unit-stride load.
134            //
135            // Destination EMUL = EEW/SEW * LMUL, computed via `index_register_count`. This
136            // gives `group_regs` such that `VLMAX = group_regs * VLENB / eew.bytes()` matches
137            // the architectural `vl`.
138            Self::Vle {
139                vd,
140                rs1: _,
141                vm,
142                eew,
143            } => {
144                if !ext_state.vector_instructions_allowed() {
145                    Err(ExecutionError::IllegalInstruction {
146                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
147                    })?;
148                }
149                let vtype = ext_state
150                    .vtype()
151                    .ok_or(ExecutionError::IllegalInstruction {
152                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
153                    })?;
154                let group_regs = vtype
155                    .vlmul()
156                    .index_register_count(eew, vtype.vsew())
157                    .ok_or(ExecutionError::IllegalInstruction {
158                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
159                    })?;
160                zve64x_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
161                    program_counter,
162                    vd,
163                    group_regs,
164                )?;
165                if !vm && zve64x_load_helpers::groups_overlap(vd, group_regs, VReg::V0, 1) {
166                    Err(ExecutionError::IllegalInstruction {
167                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
168                    })?;
169                }
170                // SAFETY:
171                // - 1 <= MAX_NF
172                // - alignment: `check_register_group_alignment` verified `vd % group_regs == 0` and
173                //   `vd + group_regs <= 32`, satisfying both the alignment and nf=1 bounds
174                //   preconditions
175                // - `vl <= group_regs * VLENB / eew.bytes()`: `group_regs` is the EMUL computed for
176                //   this `eew` and `vtype`, so this VLMAX equals the architectural VLMAX that
177                //   bounds `vl`
178                // - mask overlap: checked above via `groups_overlap`
179                unsafe {
180                    zve64x_load_helpers::execute_unit_stride_load(
181                        ext_state,
182                        memory,
183                        vd,
184                        vm,
185                        ext_state.vl(),
186                        u32::from(ext_state.vstart()),
187                        rs1_value.as_u64(),
188                        eew,
189                        group_regs,
190                        1,
191                        false,
192                    )?;
193                }
194            }
195
196            // Fault-only-first unit-stride load. Preconditions identical to `Vle`.
197            Self::Vleff {
198                vd,
199                rs1: _,
200                vm,
201                eew,
202            } => {
203                if !ext_state.vector_instructions_allowed() {
204                    Err(ExecutionError::IllegalInstruction {
205                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
206                    })?;
207                }
208                let vtype = ext_state
209                    .vtype()
210                    .ok_or(ExecutionError::IllegalInstruction {
211                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
212                    })?;
213                let group_regs = vtype
214                    .vlmul()
215                    .index_register_count(eew, vtype.vsew())
216                    .ok_or(ExecutionError::IllegalInstruction {
217                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
218                    })?;
219                zve64x_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
220                    program_counter,
221                    vd,
222                    group_regs,
223                )?;
224                if !vm && zve64x_load_helpers::groups_overlap(vd, group_regs, VReg::V0, 1) {
225                    Err(ExecutionError::IllegalInstruction {
226                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
227                    })?;
228                }
229                // SAFETY: preconditions identical to `Vle`; see that arm for the full argument.
230                unsafe {
231                    zve64x_load_helpers::execute_unit_stride_load(
232                        ext_state,
233                        memory,
234                        vd,
235                        vm,
236                        ext_state.vl(),
237                        u32::from(ext_state.vstart()),
238                        rs1_value.as_u64(),
239                        eew,
240                        group_regs,
241                        1,
242                        true,
243                    )?;
244                }
245            }
246
247            // Strided load. Destination EMUL = EEW/SEW * LMUL as for unit-stride.
248            Self::Vlse {
249                vd,
250                rs1: _,
251                rs2: _,
252                vm,
253                eew,
254            } => {
255                if !ext_state.vector_instructions_allowed() {
256                    Err(ExecutionError::IllegalInstruction {
257                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
258                    })?;
259                }
260                let vtype = ext_state
261                    .vtype()
262                    .ok_or(ExecutionError::IllegalInstruction {
263                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
264                    })?;
265                let group_regs = vtype
266                    .vlmul()
267                    .index_register_count(eew, vtype.vsew())
268                    .ok_or(ExecutionError::IllegalInstruction {
269                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
270                    })?;
271                zve64x_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
272                    program_counter,
273                    vd,
274                    group_regs,
275                )?;
276                if !vm && zve64x_load_helpers::groups_overlap(vd, group_regs, VReg::V0, 1) {
277                    Err(ExecutionError::IllegalInstruction {
278                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
279                    })?;
280                }
281                // rs2 holds a signed stride; reinterpret the register value as signed.
282                let stride = rs2_value.as_u64().cast_signed();
283                // SAFETY:
284                // - alignment and nf=1 bounds: `check_register_group_alignment` verified `vd %
285                //   group_regs == 0` and `vd + group_regs <= 32`
286                // - `vl <= group_regs * VLENB / eew.bytes()`: `group_regs` is the EMUL for this
287                //   `eew` and `vtype`, so this VLMAX equals the architectural VLMAX bounding `vl`
288                // - mask overlap: checked above via `groups_overlap`
289                unsafe {
290                    zve64x_load_helpers::execute_strided_load(
291                        ext_state,
292                        memory,
293                        vd,
294                        vm,
295                        ext_state.vl(),
296                        u32::from(ext_state.vstart()),
297                        rs1_value.as_u64(),
298                        stride,
299                        eew,
300                        group_regs,
301                        1,
302                    )?;
303                }
304            }
305
306            // Indexed-unordered load: eew is the index EEW; data EEW comes from vtype.vsew().
307            // The data destination uses the base LMUL (data EEW = SEW for indexed loads).
308            Self::Vluxei {
309                vd,
310                rs1: _,
311                vs2,
312                vm,
313                eew: index_eew,
314            } => {
315                if !ext_state.vector_instructions_allowed() {
316                    Err(ExecutionError::IllegalInstruction {
317                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
318                    })?;
319                }
320                let vtype = ext_state
321                    .vtype()
322                    .ok_or(ExecutionError::IllegalInstruction {
323                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
324                    })?;
325                let data_group_regs = vtype.vlmul().register_count();
326                let index_group_regs = vtype
327                    .vlmul()
328                    .index_register_count(index_eew, vtype.vsew())
329                    .ok_or(ExecutionError::IllegalInstruction {
330                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
331                    })?;
332                zve64x_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
333                    program_counter,
334                    vd,
335                    data_group_regs,
336                )?;
337                zve64x_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
338                    program_counter,
339                    vs2,
340                    index_group_regs,
341                )?;
342                if zve64x_load_helpers::groups_overlap(vd, data_group_regs, vs2, index_group_regs) {
343                    Err(ExecutionError::IllegalInstruction {
344                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
345                    })?;
346                }
347                if !vm && zve64x_load_helpers::groups_overlap(vd, data_group_regs, VReg::V0, 1) {
348                    Err(ExecutionError::IllegalInstruction {
349                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
350                    })?;
351                }
352                // SAFETY:
353                // - data alignment/nf=1 bounds: `check_register_group_alignment` on `vd`
354                // - index alignment/bounds: `check_register_group_alignment` on `vs2`
355                // - `vl <= data_group_regs * VLENB / data_eew.bytes()`: data EEW = SEW and
356                //   `data_group_regs = LMUL`, so VLMAX = LMUL * VLEN / SEW, which bounds `vl`
357                // - `vl <= index_group_regs * VLENB / index_eew.bytes()`: `index_group_regs` is
358                //   EMUL_index defined so this VLMAX_index equals the architectural VLMAX
359                // - no overlap between data and index groups: checked above
360                // - mask overlap: checked above via `groups_overlap`
361                unsafe {
362                    zve64x_load_helpers::execute_indexed_load(
363                        ext_state,
364                        memory,
365                        vd,
366                        vs2,
367                        vm,
368                        ext_state.vl(),
369                        u32::from(ext_state.vstart()),
370                        rs1_value.as_u64(),
371                        vtype.vsew().as_eew(),
372                        index_eew,
373                        data_group_regs,
374                        1,
375                    )?;
376                }
377            }
378
379            // Indexed-ordered load: functionally identical to `Vluxei` for a software
380            // interpreter; memory access ordering has no observable effect here.
381            Self::Vloxei {
382                vd,
383                rs1: _,
384                vs2,
385                vm,
386                eew: index_eew,
387            } => {
388                if !ext_state.vector_instructions_allowed() {
389                    Err(ExecutionError::IllegalInstruction {
390                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
391                    })?;
392                }
393                let vtype = ext_state
394                    .vtype()
395                    .ok_or(ExecutionError::IllegalInstruction {
396                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
397                    })?;
398                let data_group_regs = vtype.vlmul().register_count();
399                let index_group_regs = vtype
400                    .vlmul()
401                    .index_register_count(index_eew, vtype.vsew())
402                    .ok_or(ExecutionError::IllegalInstruction {
403                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
404                    })?;
405                zve64x_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
406                    program_counter,
407                    vd,
408                    data_group_regs,
409                )?;
410                zve64x_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
411                    program_counter,
412                    vs2,
413                    index_group_regs,
414                )?;
415                if zve64x_load_helpers::groups_overlap(vd, data_group_regs, vs2, index_group_regs) {
416                    Err(ExecutionError::IllegalInstruction {
417                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
418                    })?;
419                }
420                if !vm && zve64x_load_helpers::groups_overlap(vd, data_group_regs, VReg::V0, 1) {
421                    Err(ExecutionError::IllegalInstruction {
422                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
423                    })?;
424                }
425                // SAFETY: preconditions identical to `Vluxei`; see that arm for the full
426                // argument.
427                unsafe {
428                    zve64x_load_helpers::execute_indexed_load(
429                        ext_state,
430                        memory,
431                        vd,
432                        vs2,
433                        vm,
434                        ext_state.vl(),
435                        u32::from(ext_state.vstart()),
436                        rs1_value.as_u64(),
437                        vtype.vsew().as_eew(),
438                        index_eew,
439                        data_group_regs,
440                        1,
441                    )?;
442                }
443            }
444
445            // Unit-stride segment load. EMUL = EEW/SEW * LMUL per field group.
446            Self::Vlseg {
447                vd,
448                rs1: _,
449                vm,
450                eew,
451                nf,
452            } => {
453                if !ext_state.vector_instructions_allowed() {
454                    Err(ExecutionError::IllegalInstruction {
455                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
456                    })?;
457                }
458                let vtype = ext_state
459                    .vtype()
460                    .ok_or(ExecutionError::IllegalInstruction {
461                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
462                    })?;
463                let group_regs = vtype
464                    .vlmul()
465                    .index_register_count(eew, vtype.vsew())
466                    .ok_or(ExecutionError::IllegalInstruction {
467                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
468                    })?;
469                zve64x_load_helpers::validate_segment_registers::<Reg, _, _, _>(
470                    program_counter,
471                    vd,
472                    vm,
473                    group_regs,
474                    nf,
475                )?;
476                if nf > zve64x_load_helpers::MAX_NF {
477                    Err(ExecutionError::IllegalInstruction {
478                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
479                    })?;
480                }
481                // SAFETY:
482                // - `nf <= MAX_NF` checked above
483                // - alignment and nf-group bounds: `validate_segment_registers` verified `vd %
484                //   group_regs == 0` and `vd + nf * group_regs <= 32`
485                // - `vl <= group_regs * VLENB / eew.bytes()`: `group_regs` is the EMUL for this
486                //   `eew` and `vtype`, so this VLMAX equals the architectural VLMAX bounding `vl`
487                // - mask overlap with v0: `validate_segment_registers` checked `vd.bits() != 0`
488                //   when `vm=false`, ensuring no field group contains v0
489                unsafe {
490                    zve64x_load_helpers::execute_unit_stride_load(
491                        ext_state,
492                        memory,
493                        vd,
494                        vm,
495                        ext_state.vl(),
496                        u32::from(ext_state.vstart()),
497                        rs1_value.as_u64(),
498                        eew,
499                        group_regs,
500                        nf,
501                        false,
502                    )?;
503                }
504            }
505
506            // Fault-only-first segment load. Preconditions identical to `Vlseg`.
507            Self::Vlsegff {
508                vd,
509                rs1: _,
510                vm,
511                eew,
512                nf,
513            } => {
514                if !ext_state.vector_instructions_allowed() {
515                    Err(ExecutionError::IllegalInstruction {
516                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
517                    })?;
518                }
519                let vtype = ext_state
520                    .vtype()
521                    .ok_or(ExecutionError::IllegalInstruction {
522                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
523                    })?;
524                let group_regs = vtype
525                    .vlmul()
526                    .index_register_count(eew, vtype.vsew())
527                    .ok_or(ExecutionError::IllegalInstruction {
528                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
529                    })?;
530                zve64x_load_helpers::validate_segment_registers::<Reg, _, _, _>(
531                    program_counter,
532                    vd,
533                    vm,
534                    group_regs,
535                    nf,
536                )?;
537                if nf > zve64x_load_helpers::MAX_NF {
538                    Err(ExecutionError::IllegalInstruction {
539                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
540                    })?;
541                }
542                // SAFETY: preconditions identical to `Vlseg`; see that arm for the full argument.
543                unsafe {
544                    zve64x_load_helpers::execute_unit_stride_load(
545                        ext_state,
546                        memory,
547                        vd,
548                        vm,
549                        ext_state.vl(),
550                        u32::from(ext_state.vstart()),
551                        rs1_value.as_u64(),
552                        eew,
553                        group_regs,
554                        nf,
555                        true,
556                    )?;
557                }
558            }
559
560            // Strided segment load. EMUL = EEW/SEW * LMUL as for `Vlse`.
561            Self::Vlsseg {
562                vd,
563                rs1: _,
564                rs2: _,
565                vm,
566                eew,
567                nf,
568            } => {
569                if !ext_state.vector_instructions_allowed() {
570                    Err(ExecutionError::IllegalInstruction {
571                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
572                    })?;
573                }
574                let vtype = ext_state
575                    .vtype()
576                    .ok_or(ExecutionError::IllegalInstruction {
577                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
578                    })?;
579                let group_regs = vtype
580                    .vlmul()
581                    .index_register_count(eew, vtype.vsew())
582                    .ok_or(ExecutionError::IllegalInstruction {
583                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
584                    })?;
585                zve64x_load_helpers::validate_segment_registers::<Reg, _, _, _>(
586                    program_counter,
587                    vd,
588                    vm,
589                    group_regs,
590                    nf,
591                )?;
592                let stride = rs2_value.as_u64().cast_signed();
593                // SAFETY:
594                // - alignment and nf-group bounds: `validate_segment_registers` verified `vd %
595                //   group_regs == 0` and `vd + nf * group_regs <= 32`
596                // - `vl <= group_regs * VLENB / eew.bytes()`: `group_regs` is EMUL for this `eew`
597                //   and `vtype`
598                // - mask overlap: `validate_segment_registers` checked `vd.bits() != 0` when
599                //   `vm=false`
600                unsafe {
601                    zve64x_load_helpers::execute_strided_load(
602                        ext_state,
603                        memory,
604                        vd,
605                        vm,
606                        ext_state.vl(),
607                        u32::from(ext_state.vstart()),
608                        rs1_value.as_u64(),
609                        stride,
610                        eew,
611                        group_regs,
612                        nf,
613                    )?;
614                }
615            }
616
617            // Indexed-unordered segment load
618            Self::Vluxseg {
619                vd,
620                rs1: _,
621                vs2,
622                vm,
623                eew: index_eew,
624                nf,
625            } => {
626                if !ext_state.vector_instructions_allowed() {
627                    Err(ExecutionError::IllegalInstruction {
628                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
629                    })?;
630                }
631                let vtype = ext_state
632                    .vtype()
633                    .ok_or(ExecutionError::IllegalInstruction {
634                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
635                    })?;
636                let data_group_regs = vtype.vlmul().register_count();
637                let index_group_regs = vtype
638                    .vlmul()
639                    .index_register_count(index_eew, vtype.vsew())
640                    .ok_or(ExecutionError::IllegalInstruction {
641                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
642                    })?;
643                // `validate_segment_registers` is called before the per-field overlap loop so
644                // that `vd.bits() + f * data_group_regs < 32` is established for all `f < nf`,
645                // which is required by the `VReg::from_bits` call inside the loop.
646                zve64x_load_helpers::validate_segment_registers::<Reg, _, _, _>(
647                    program_counter,
648                    vd,
649                    vm,
650                    data_group_regs,
651                    nf,
652                )?;
653                zve64x_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
654                    program_counter,
655                    vs2,
656                    index_group_regs,
657                )?;
658                for f in 0..nf {
659                    // SAFETY: `vd.bits() + f * data_group_regs < 32` because
660                    // `validate_segment_registers` established `vd.bits() + nf * data_group_regs
661                    // <= 32` and `f < nf`. The value is in [0, 31], so it is a valid `VReg`
662                    // encoding.
663                    let field_vd = unsafe {
664                        VReg::from_bits(vd.bits() + f * data_group_regs).unwrap_unchecked()
665                    };
666                    if zve64x_load_helpers::groups_overlap(
667                        field_vd,
668                        data_group_regs,
669                        vs2,
670                        index_group_regs,
671                    ) {
672                        Err(ExecutionError::IllegalInstruction {
673                            address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
674                        })?;
675                    }
676                }
677                // SAFETY:
678                // - data alignment/nf-group bounds: `validate_segment_registers` verified `vd %
679                //   data_group_regs == 0` and `vd + nf * data_group_regs <= 32`
680                // - index alignment/bounds: `check_register_group_alignment` verified `vs2 %
681                //   EMUL_index == 0` and `vs2 + EMUL_index <= 32`
682                // - no field/index group overlap: verified by the loop above
683                // - `vl <= data_group_regs * VLENB / data_eew.bytes()`: data EEW = SEW and
684                //   `data_group_regs = LMUL`, so VLMAX = LMUL * VLEN / SEW bounds `vl`
685                // - `vl <= EMUL_index * VLENB / index_eew.bytes()`: `index_group_regs` (EMUL_index)
686                //   is defined so this VLMAX_index equals the architectural VLMAX
687                // - mask overlap: `validate_segment_registers` checked `vd.bits() != 0` when
688                //   `vm=false`, and no field group starts at 0 since groups are contiguous from
689                //   `vd` which is nonzero
690                unsafe {
691                    zve64x_load_helpers::execute_indexed_load(
692                        ext_state,
693                        memory,
694                        vd,
695                        vs2,
696                        vm,
697                        ext_state.vl(),
698                        u32::from(ext_state.vstart()),
699                        rs1_value.as_u64(),
700                        vtype.vsew().as_eew(),
701                        index_eew,
702                        data_group_regs,
703                        nf,
704                    )?;
705                }
706            }
707
708            // Indexed-ordered segment load: functionally identical to `Vluxseg` for a software
709            // interpreter
710            Self::Vloxseg {
711                vd,
712                rs1: _,
713                vs2,
714                vm,
715                eew: index_eew,
716                nf,
717            } => {
718                if !ext_state.vector_instructions_allowed() {
719                    Err(ExecutionError::IllegalInstruction {
720                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
721                    })?;
722                }
723                let vtype = ext_state
724                    .vtype()
725                    .ok_or(ExecutionError::IllegalInstruction {
726                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
727                    })?;
728                let data_group_regs = vtype.vlmul().register_count();
729                let index_group_regs = vtype
730                    .vlmul()
731                    .index_register_count(index_eew, vtype.vsew())
732                    .ok_or(ExecutionError::IllegalInstruction {
733                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
734                    })?;
735                zve64x_load_helpers::validate_segment_registers::<Reg, _, _, _>(
736                    program_counter,
737                    vd,
738                    vm,
739                    data_group_regs,
740                    nf,
741                )?;
742                zve64x_load_helpers::check_register_group_alignment::<Reg, _, _, _>(
743                    program_counter,
744                    vs2,
745                    index_group_regs,
746                )?;
747                for f in 0..nf {
748                    // SAFETY: `vd.bits() + f * data_group_regs < 32` because
749                    // `validate_segment_registers` established `vd.bits() + nf * data_group_regs
750                    // <= 32` and `f < nf`. The value is in [0, 31], so it is a valid `VReg`
751                    // encoding.
752                    let field_vd = unsafe {
753                        VReg::from_bits(vd.bits() + f * data_group_regs).unwrap_unchecked()
754                    };
755                    if zve64x_load_helpers::groups_overlap(
756                        field_vd,
757                        data_group_regs,
758                        vs2,
759                        index_group_regs,
760                    ) {
761                        Err(ExecutionError::IllegalInstruction {
762                            address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
763                        })?;
764                    }
765                }
766                // SAFETY: preconditions identical to `Vluxseg`; see that arm for the full
767                // argument
768                unsafe {
769                    zve64x_load_helpers::execute_indexed_load(
770                        ext_state,
771                        memory,
772                        vd,
773                        vs2,
774                        vm,
775                        ext_state.vl(),
776                        u32::from(ext_state.vstart()),
777                        rs1_value.as_u64(),
778                        vtype.vsew().as_eew(),
779                        index_eew,
780                        data_group_regs,
781                        nf,
782                    )?;
783                }
784            }
785        }
786
787        Ok(ControlFlow::Continue(Default::default()))
788    }
789}