Skip to main content

ab_riscv_interpreter/v/zvexx/
load.rs

1//! ZveXx vector load instructions
2
3#[cfg(test)]
4pub(super) mod tests;
5pub mod zvexx_load_helpers;
6
7use crate::v::vector_registers::{VectorRegisters, 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, Hart> ExecutableInstructionOperands for ZveXxLoadInstruction<Hart>
20where
21    Reg: Register,
22    Hart: VectorHartConfig<Reg = Reg>,
23{
24}
25
26#[instruction_execution]
27const impl<Reg, Hart, Env> ExecutableInstructionCsr<Env> for ZveXxLoadInstruction<Hart>
28where
29    Reg: Register,
30    Hart: VectorHartConfig<Reg = Reg>,
31{
32}
33
34#[instruction_execution]
35impl<Reg, Hart, Regs, Env, Memory, PC> ExecutableInstruction<Regs, Env, Memory, PC>
36    for ZveXxLoadInstruction<Hart>
37where
38    Reg: Register,
39    Hart: VectorHartConfig<Reg = Reg>,
40    Regs: RegisterFile<Reg>,
41    Env: VectorRegistersExt<Hart = Hart>,
42    Memory: VirtualMemory,
43    PC: ProgramCounter<Reg::Type, Memory>,
44{
45    #[inline(always)]
46    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
47    fn execute(
48        self,
49        Rs1Rs2OperandValues {
50            rs1_value,
51            rs2_value,
52        }: Rs1Rs2OperandValues<Reg::Type>,
53        _regs: &mut Regs,
54        env: &mut Env,
55        memory: &mut Memory,
56        program_counter: &PC,
57    ) -> ExecutionResult<Reg> {
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 !env.vector_instructions_allowed() {
69                    ::core::hint::cold_path();
70                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
71                        address: PackedAddress::new(
72                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
73                        ),
74                    });
75                }
76                let Some(vd) = zvexx_load_helpers::VRegGroup::whole_registers(vd, nreg, eew) else {
77                    ::core::hint::cold_path();
78                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
79                        address: PackedAddress::new(
80                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
81                        ),
82                    });
83                };
84                // `evl = NREG * VLEN / EEW` elements, regardless of `vtype` and `vl`
85                let vl = vd.vl().get();
86                // `vstart >= evl` is reserved
87                if env.vstart() >= vl {
88                    ::core::hint::cold_path();
89                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
90                        address: PackedAddress::new(
91                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
92                        ),
93                    });
94                }
95                zvexx_load_helpers::execute_unit_stride_load(
96                    env,
97                    memory,
98                    zvexx_load_helpers::VRegSegmentGroup::single(vd),
99                    true,
100                    rs1_value.as_u64(),
101                    None,
102                )?;
103            }
104
105            // Mask load: loads ceil(vl / 8) bytes from base into vd with no masking applied.
106            // Illegal with vill set, it depends on vtype indirectly through its constraints on vl.
107            Self::Vlm { vd, rs1: _ } => {
108                if !env.vector_instructions_allowed() {
109                    ::core::hint::cold_path();
110                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
111                        address: PackedAddress::new(
112                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
113                        ),
114                    });
115                }
116                let Some(config) = env.vector_config() else {
117                    ::core::hint::cold_path();
118                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
119                        address: PackedAddress::new(
120                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
121                        ),
122                    });
123                };
124                // `evl = ceil(vl / 8)` elements with `EEW = 8`, which never exceeds a single
125                // register
126                let Some(vd) =
127                    zvexx_load_helpers::VRegGroup::whole_registers(vd, VRegGroupSize::R1, Eew::E8)
128                        .and_then(|vd| vd.with_vl(Vl::from(config.vl().bytes())))
129                else {
130                    ::core::hint::cold_path();
131                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
132                        address: PackedAddress::new(
133                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
134                        ),
135                    });
136                };
137                zvexx_load_helpers::execute_unit_stride_load(
138                    env,
139                    memory,
140                    zvexx_load_helpers::VRegSegmentGroup::single(vd),
141                    true,
142                    rs1_value.as_u64(),
143                    None,
144                )?;
145            }
146
147            // Unit-stride load.
148            //
149            // Destination EMUL = EEW/SEW * LMUL, computed via `eew_register_count`. This
150            // gives `group_regs` such that `VLMAX = group_regs * VLEN.bytes() / eew.bytes()`
151            // matches the architectural `vl`.
152            Self::Vle {
153                vd,
154                rs1: _,
155                vm,
156                eew,
157            } => {
158                if !env.vector_instructions_allowed() {
159                    ::core::hint::cold_path();
160                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
161                        address: PackedAddress::new(
162                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
163                        ),
164                    });
165                }
166                let Some(config) = env.vector_config() else {
167                    ::core::hint::cold_path();
168                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
169                        address: PackedAddress::new(
170                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
171                        ),
172                    });
173                };
174                let vd =
175                    zvexx_helpers::vreg_group::<Reg, Env, _, _>(program_counter, config, vd, eew)?;
176                zvexx_load_helpers::execute_unit_stride_load(
177                    env,
178                    memory,
179                    zvexx_load_helpers::VRegSegmentGroup::single(vd),
180                    vm,
181                    rs1_value.as_u64(),
182                    None,
183                )?;
184            }
185
186            // Fault-only-first unit-stride load. Preconditions identical to `Vle`.
187            Self::Vleff {
188                vd,
189                rs1: _,
190                vm,
191                eew,
192            } => {
193                if !env.vector_instructions_allowed() {
194                    ::core::hint::cold_path();
195                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
196                        address: PackedAddress::new(
197                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
198                        ),
199                    });
200                }
201                let Some(config) = env.vector_config() else {
202                    ::core::hint::cold_path();
203                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
204                        address: PackedAddress::new(
205                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
206                        ),
207                    });
208                };
209                let vd =
210                    zvexx_helpers::vreg_group::<Reg, Env, _, _>(program_counter, config, vd, eew)?;
211                zvexx_load_helpers::execute_unit_stride_load(
212                    env,
213                    memory,
214                    zvexx_load_helpers::VRegSegmentGroup::single(vd),
215                    vm,
216                    rs1_value.as_u64(),
217                    Some(config),
218                )?;
219            }
220
221            // Strided load. Destination EMUL = EEW/SEW * LMUL as for unit-stride.
222            Self::Vlse {
223                vd,
224                rs1: _,
225                rs2: _,
226                vm,
227                eew,
228            } => {
229                if !env.vector_instructions_allowed() {
230                    ::core::hint::cold_path();
231                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
232                        address: PackedAddress::new(
233                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
234                        ),
235                    });
236                }
237                let Some(config) = env.vector_config() else {
238                    ::core::hint::cold_path();
239                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
240                        address: PackedAddress::new(
241                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
242                        ),
243                    });
244                };
245                let vd =
246                    zvexx_helpers::vreg_group::<Reg, Env, _, _>(program_counter, config, vd, eew)?;
247                // rs2 holds a signed stride; reinterpret the register value as signed
248                let stride = rs2_value.as_i64();
249                zvexx_load_helpers::execute_strided_load(
250                    env,
251                    memory,
252                    zvexx_load_helpers::VRegSegmentGroup::single(vd),
253                    vm,
254                    rs1_value.as_u64(),
255                    stride,
256                )?;
257            }
258
259            // Indexed-unordered load: eew is the index EEW; data EEW comes from vtype.vsew().
260            // The data destination uses the base LMUL (data EEW = SEW for indexed loads).
261            Self::Vluxei {
262                vd,
263                rs1: _,
264                vs2,
265                vm,
266                eew: index_eew,
267            } => {
268                if !env.vector_instructions_allowed() {
269                    ::core::hint::cold_path();
270                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
271                        address: PackedAddress::new(
272                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
273                        ),
274                    });
275                }
276                let Some(config) = env.vector_config() else {
277                    ::core::hint::cold_path();
278                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
279                        address: PackedAddress::new(
280                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
281                        ),
282                    });
283                };
284                let sew = config.vtype().vsew();
285                let vd = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
286                    program_counter,
287                    config,
288                    vd,
289                    sew.as_eew(),
290                )?;
291                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
292                    program_counter,
293                    config,
294                    vs2,
295                    index_eew,
296                )?;
297                // Non-segment indexed loads permit `vd`/`vs2` overlap under the general
298                // EEW-relative overlap rule (e.g. when the data and index EEW
299                // match), unlike segment ones
300                zvexx_helpers::check_destination_overlap::<Reg, Env, _, _>(
301                    program_counter,
302                    vd,
303                    vs2,
304                )?;
305                zvexx_load_helpers::execute_indexed_load(
306                    env,
307                    memory,
308                    zvexx_load_helpers::VRegSegmentGroup::single(vd),
309                    vs2,
310                    vm,
311                    rs1_value.as_u64(),
312                )?;
313            }
314
315            // Indexed-ordered load: functionally identical to `Vluxei` for a software
316            // interpreter; memory access ordering has no observable effect here.
317            Self::Vloxei {
318                vd,
319                rs1: _,
320                vs2,
321                vm,
322                eew: index_eew,
323            } => {
324                if !env.vector_instructions_allowed() {
325                    ::core::hint::cold_path();
326                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
327                        address: PackedAddress::new(
328                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
329                        ),
330                    });
331                }
332                let Some(config) = env.vector_config() else {
333                    ::core::hint::cold_path();
334                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
335                        address: PackedAddress::new(
336                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
337                        ),
338                    });
339                };
340                let sew = config.vtype().vsew();
341                let vd = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
342                    program_counter,
343                    config,
344                    vd,
345                    sew.as_eew(),
346                )?;
347                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
348                    program_counter,
349                    config,
350                    vs2,
351                    index_eew,
352                )?;
353                // Non-segment indexed loads permit `vd`/`vs2` overlap under the general
354                // EEW-relative overlap rule (e.g. when the data and index EEW
355                // match), unlike segment ones
356                zvexx_helpers::check_destination_overlap::<Reg, Env, _, _>(
357                    program_counter,
358                    vd,
359                    vs2,
360                )?;
361                zvexx_load_helpers::execute_indexed_load(
362                    env,
363                    memory,
364                    zvexx_load_helpers::VRegSegmentGroup::single(vd),
365                    vs2,
366                    vm,
367                    rs1_value.as_u64(),
368                )?;
369            }
370
371            // Unit-stride segment load. EMUL = EEW/SEW * LMUL per field group.
372            Self::Vlseg {
373                vd,
374                rs1: _,
375                eew,
376                vm_nf,
377            } => {
378                let vm = vm_nf.vm();
379                let nf = vm_nf.nf();
380                if !env.vector_instructions_allowed() {
381                    ::core::hint::cold_path();
382                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
383                        address: PackedAddress::new(
384                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
385                        ),
386                    });
387                }
388                let Some(config) = env.vector_config() else {
389                    ::core::hint::cold_path();
390                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
391                        address: PackedAddress::new(
392                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
393                        ),
394                    });
395                };
396                let vd =
397                    zvexx_helpers::vreg_group::<Reg, Env, _, _>(program_counter, config, vd, eew)?;
398                let vd =
399                    zvexx_helpers::vreg_segment_group::<Reg, Env, _, _>(program_counter, vd, nf)?;
400                zvexx_load_helpers::execute_unit_stride_load(
401                    env,
402                    memory,
403                    vd,
404                    vm,
405                    rs1_value.as_u64(),
406                    None,
407                )?;
408            }
409
410            // Fault-only-first segment load. Preconditions identical to `Vlseg`.
411            Self::Vlsegff {
412                vd,
413                rs1: _,
414                eew,
415                vm_nf,
416            } => {
417                let vm = vm_nf.vm();
418                let nf = vm_nf.nf();
419                if !env.vector_instructions_allowed() {
420                    ::core::hint::cold_path();
421                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
422                        address: PackedAddress::new(
423                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
424                        ),
425                    });
426                }
427                let Some(config) = env.vector_config() else {
428                    ::core::hint::cold_path();
429                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
430                        address: PackedAddress::new(
431                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
432                        ),
433                    });
434                };
435                let vd =
436                    zvexx_helpers::vreg_group::<Reg, Env, _, _>(program_counter, config, vd, eew)?;
437                let vd =
438                    zvexx_helpers::vreg_segment_group::<Reg, Env, _, _>(program_counter, vd, nf)?;
439                zvexx_load_helpers::execute_unit_stride_load(
440                    env,
441                    memory,
442                    vd,
443                    vm,
444                    rs1_value.as_u64(),
445                    Some(config),
446                )?;
447            }
448
449            // Strided segment load. EMUL = EEW/SEW * LMUL as for `Vlse`.
450            Self::Vlsseg {
451                vd,
452                rs1: _,
453                rs2: _,
454                eew,
455                vm_nf,
456            } => {
457                let vm = vm_nf.vm();
458                let nf = vm_nf.nf();
459                if !env.vector_instructions_allowed() {
460                    ::core::hint::cold_path();
461                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
462                        address: PackedAddress::new(
463                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
464                        ),
465                    });
466                }
467                let Some(config) = env.vector_config() else {
468                    ::core::hint::cold_path();
469                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
470                        address: PackedAddress::new(
471                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
472                        ),
473                    });
474                };
475                let vd =
476                    zvexx_helpers::vreg_group::<Reg, Env, _, _>(program_counter, config, vd, eew)?;
477                let vd =
478                    zvexx_helpers::vreg_segment_group::<Reg, Env, _, _>(program_counter, vd, nf)?;
479                let stride = rs2_value.as_i64();
480                zvexx_load_helpers::execute_strided_load(
481                    env,
482                    memory,
483                    vd,
484                    vm,
485                    rs1_value.as_u64(),
486                    stride,
487                )?;
488            }
489
490            // Indexed-unordered segment load
491            Self::Vluxseg {
492                vd,
493                rs1: _,
494                vs2,
495                eew: index_eew,
496                vm_nf,
497            } => {
498                let vm = vm_nf.vm();
499                let nf = vm_nf.nf();
500                if !env.vector_instructions_allowed() {
501                    ::core::hint::cold_path();
502                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
503                        address: PackedAddress::new(
504                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
505                        ),
506                    });
507                }
508                let Some(config) = env.vector_config() else {
509                    ::core::hint::cold_path();
510                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
511                        address: PackedAddress::new(
512                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
513                        ),
514                    });
515                };
516                let sew = config.vtype().vsew();
517                let vd = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
518                    program_counter,
519                    config,
520                    vd,
521                    sew.as_eew(),
522                )?;
523                let vd =
524                    zvexx_helpers::vreg_segment_group::<Reg, Env, _, _>(program_counter, vd, nf)?;
525                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
526                    program_counter,
527                    config,
528                    vs2,
529                    index_eew,
530                )?;
531                // No field may overlap the index register group
532                if vd.fields().any(|field| field.overlaps(vs2)) {
533                    ::core::hint::cold_path();
534                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
535                        address: PackedAddress::new(
536                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
537                        ),
538                    });
539                }
540                zvexx_load_helpers::execute_indexed_load(
541                    env,
542                    memory,
543                    vd,
544                    vs2,
545                    vm,
546                    rs1_value.as_u64(),
547                )?;
548            }
549
550            // Indexed-ordered segment load: functionally identical to `Vluxseg` for a software
551            // interpreter
552            Self::Vloxseg {
553                vd,
554                rs1: _,
555                vs2,
556                eew: index_eew,
557                vm_nf,
558            } => {
559                let vm = vm_nf.vm();
560                let nf = vm_nf.nf();
561                if !env.vector_instructions_allowed() {
562                    ::core::hint::cold_path();
563                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
564                        address: PackedAddress::new(
565                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
566                        ),
567                    });
568                }
569                let Some(config) = env.vector_config() else {
570                    ::core::hint::cold_path();
571                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
572                        address: PackedAddress::new(
573                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
574                        ),
575                    });
576                };
577                let sew = config.vtype().vsew();
578                let vd = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
579                    program_counter,
580                    config,
581                    vd,
582                    sew.as_eew(),
583                )?;
584                let vd =
585                    zvexx_helpers::vreg_segment_group::<Reg, Env, _, _>(program_counter, vd, nf)?;
586                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
587                    program_counter,
588                    config,
589                    vs2,
590                    index_eew,
591                )?;
592                // No field may overlap the index register group
593                if vd.fields().any(|field| field.overlaps(vs2)) {
594                    ::core::hint::cold_path();
595                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
596                        address: PackedAddress::new(
597                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
598                        ),
599                    });
600                }
601                zvexx_load_helpers::execute_indexed_load(
602                    env,
603                    memory,
604                    vd,
605                    vs2,
606                    vm,
607                    rs1_value.as_u64(),
608                )?;
609            }
610        }
611
612        ExecutionResult::ContinueNoWrite
613    }
614}