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}