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