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}