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