Skip to main content

ab_riscv_interpreter/v/zve64x/
fixed_point.rs

1//! Zve64x fixed-point arithmetic instructions
2
3#[cfg(test)]
4mod tests;
5pub mod zve64x_fixed_point_helpers;
6
7use crate::v::vector_registers::VectorRegistersExt;
8use crate::v::zve64x::zve64x_helpers;
9use crate::{
10    ExecutableInstruction, ExecutableInstructionCsr, ExecutableInstructionOperands, ExecutionError,
11    ProgramCounter, RegisterFile, Rs1Rs2OperandValues, Rs1Rs2Operands, VirtualMemory,
12};
13use ab_riscv_macros::instruction_execution;
14use ab_riscv_primitives::prelude::*;
15use core::fmt;
16use core::ops::ControlFlow;
17
18#[instruction_execution]
19impl<Reg> ExecutableInstructionOperands for Zve64xFixedPointInstruction<Reg> where Reg: Register {}
20
21#[instruction_execution]
22impl<Reg, ExtState, CustomError> ExecutableInstructionCsr<ExtState, CustomError>
23    for Zve64xFixedPointInstruction<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 Zve64xFixedPointInstruction<Reg>
33where
34    Reg: Register,
35    Regs: RegisterFile<Reg>,
36    ExtState: VectorRegistersExt<Reg, CustomError>,
37    [(); ExtState::ELEN as usize]:,
38    [(); ExtState::VLEN as usize]:,
39    [(); ExtState::VLENB as usize]:,
40    Memory: VirtualMemory,
41    PC: ProgramCounter<Reg::Type, Memory, CustomError>,
42    CustomError: fmt::Debug,
43{
44    #[inline(always)]
45    fn execute(
46        self,
47        Rs1Rs2OperandValues {
48            rs1_value,
49            rs2_value: _,
50        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
51        _regs: &mut Regs,
52        ext_state: &mut ExtState,
53        _memory: &mut Memory,
54        program_counter: &mut PC,
55        _system_instruction_handler: &mut InstructionHandler,
56    ) -> Result<
57        ControlFlow<(), (Self::Reg, <Self::Reg as Register>::Type)>,
58        ExecutionError<Reg::Type, CustomError>,
59    > {
60        match self {
61            // vsaddu.vv / vsaddu.vx / vsaddu.vi - saturating unsigned add
62            Self::VsadduVv { vd, vs2, vs1, vm } => {
63                if !ext_state.vector_instructions_allowed() {
64                    Err(ExecutionError::IllegalInstruction {
65                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
66                    })?;
67                }
68                let vtype = ext_state
69                    .vtype()
70                    .ok_or(ExecutionError::IllegalInstruction {
71                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
72                    })?;
73                let group_regs = vtype.vlmul().register_count();
74                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
75                    program_counter,
76                    vd,
77                    group_regs,
78                )?;
79                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
80                    program_counter,
81                    vs2,
82                    group_regs,
83                )?;
84                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
85                    program_counter,
86                    vs1,
87                    group_regs,
88                )?;
89                if !vm && vd.bits() == 0 {
90                    Err(ExecutionError::IllegalInstruction {
91                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
92                    })?;
93                }
94                let sew = vtype.vsew();
95                let vl = ext_state.vl();
96                let vstart = u32::from(ext_state.vstart());
97                // SAFETY: alignment checked above
98                unsafe {
99                    zve64x_fixed_point_helpers::execute_fixed_point_op(
100                        ext_state,
101                        vd,
102                        vs2,
103                        zve64x_fixed_point_helpers::OpSrc::Vreg(vs1.bits()),
104                        vm,
105                        vl,
106                        vstart,
107                        sew,
108                        |a, b, sew, _vxrm, vxsat| {
109                            zve64x_fixed_point_helpers::sat_addu(a, b, sew, vxsat)
110                        },
111                    );
112                }
113            }
114            Self::VsadduVx {
115                vd,
116                vs2,
117                rs1: _,
118                vm,
119            } => {
120                if !ext_state.vector_instructions_allowed() {
121                    Err(ExecutionError::IllegalInstruction {
122                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
123                    })?;
124                }
125                let vtype = ext_state
126                    .vtype()
127                    .ok_or(ExecutionError::IllegalInstruction {
128                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
129                    })?;
130                let group_regs = vtype.vlmul().register_count();
131                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
132                    program_counter,
133                    vd,
134                    group_regs,
135                )?;
136                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
137                    program_counter,
138                    vs2,
139                    group_regs,
140                )?;
141                if !vm && vd.bits() == 0 {
142                    Err(ExecutionError::IllegalInstruction {
143                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
144                    })?;
145                }
146                let sew = vtype.vsew();
147                let vl = ext_state.vl();
148                let vstart = u32::from(ext_state.vstart());
149                let scalar = rs1_value.as_u64();
150                // SAFETY: alignment checked above
151                unsafe {
152                    zve64x_fixed_point_helpers::execute_fixed_point_op(
153                        ext_state,
154                        vd,
155                        vs2,
156                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
157                        vm,
158                        vl,
159                        vstart,
160                        sew,
161                        |a, b, sew, _vxrm, vxsat| {
162                            zve64x_fixed_point_helpers::sat_addu(a, b, sew, vxsat)
163                        },
164                    );
165                }
166            }
167            Self::VsadduVi { vd, vs2, imm, vm } => {
168                if !ext_state.vector_instructions_allowed() {
169                    Err(ExecutionError::IllegalInstruction {
170                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
171                    })?;
172                }
173                let vtype = ext_state
174                    .vtype()
175                    .ok_or(ExecutionError::IllegalInstruction {
176                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
177                    })?;
178                let group_regs = vtype.vlmul().register_count();
179                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
180                    program_counter,
181                    vd,
182                    group_regs,
183                )?;
184                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
185                    program_counter,
186                    vs2,
187                    group_regs,
188                )?;
189                if !vm && vd.bits() == 0 {
190                    Err(ExecutionError::IllegalInstruction {
191                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
192                    })?;
193                }
194                let sew = vtype.vsew();
195                let vl = ext_state.vl();
196                let vstart = u32::from(ext_state.vstart());
197                // Per v-spec §12.1 / §11.1: the 5-bit immediate is sign-extended to SEW,
198                // then interpreted as an unsigned SEW-wide value for the saturating add.
199                // Sign-extend i8 -> i64 -> bit-cast to u64; sat_addu masks to SEW internally.
200                let scalar = i64::from(imm).cast_unsigned();
201                unsafe {
202                    zve64x_fixed_point_helpers::execute_fixed_point_op(
203                        ext_state,
204                        vd,
205                        vs2,
206                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
207                        vm,
208                        vl,
209                        vstart,
210                        sew,
211                        |a, b, sew, _vxrm, vxsat| {
212                            zve64x_fixed_point_helpers::sat_addu(a, b, sew, vxsat)
213                        },
214                    );
215                }
216            }
217            // vsadd.vv / vsadd.vx / vsadd.vi - saturating signed add
218            Self::VsaddVv { vd, vs2, vs1, vm } => {
219                if !ext_state.vector_instructions_allowed() {
220                    Err(ExecutionError::IllegalInstruction {
221                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
222                    })?;
223                }
224                let vtype = ext_state
225                    .vtype()
226                    .ok_or(ExecutionError::IllegalInstruction {
227                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
228                    })?;
229                let group_regs = vtype.vlmul().register_count();
230                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
231                    program_counter,
232                    vd,
233                    group_regs,
234                )?;
235                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
236                    program_counter,
237                    vs2,
238                    group_regs,
239                )?;
240                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
241                    program_counter,
242                    vs1,
243                    group_regs,
244                )?;
245                if !vm && vd.bits() == 0 {
246                    Err(ExecutionError::IllegalInstruction {
247                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
248                    })?;
249                }
250                let sew = vtype.vsew();
251                let vl = ext_state.vl();
252                let vstart = u32::from(ext_state.vstart());
253                // SAFETY: alignment checked above
254                unsafe {
255                    zve64x_fixed_point_helpers::execute_fixed_point_op(
256                        ext_state,
257                        vd,
258                        vs2,
259                        zve64x_fixed_point_helpers::OpSrc::Vreg(vs1.bits()),
260                        vm,
261                        vl,
262                        vstart,
263                        sew,
264                        |a, b, sew, _vxrm, vxsat| {
265                            zve64x_fixed_point_helpers::sat_add(a, b, sew, vxsat)
266                        },
267                    );
268                }
269            }
270            Self::VsaddVx {
271                vd,
272                vs2,
273                rs1: _,
274                vm,
275            } => {
276                if !ext_state.vector_instructions_allowed() {
277                    Err(ExecutionError::IllegalInstruction {
278                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
279                    })?;
280                }
281                let vtype = ext_state
282                    .vtype()
283                    .ok_or(ExecutionError::IllegalInstruction {
284                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
285                    })?;
286                let group_regs = vtype.vlmul().register_count();
287                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
288                    program_counter,
289                    vd,
290                    group_regs,
291                )?;
292                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
293                    program_counter,
294                    vs2,
295                    group_regs,
296                )?;
297                if !vm && vd.bits() == 0 {
298                    Err(ExecutionError::IllegalInstruction {
299                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
300                    })?;
301                }
302                let sew = vtype.vsew();
303                let vl = ext_state.vl();
304                let vstart = u32::from(ext_state.vstart());
305                let scalar = rs1_value.as_u64();
306                // SAFETY: alignment checked above
307                unsafe {
308                    zve64x_fixed_point_helpers::execute_fixed_point_op(
309                        ext_state,
310                        vd,
311                        vs2,
312                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
313                        vm,
314                        vl,
315                        vstart,
316                        sew,
317                        |a, b, sew, _vxrm, vxsat| {
318                            zve64x_fixed_point_helpers::sat_add(a, b, sew, vxsat)
319                        },
320                    );
321                }
322            }
323            Self::VsaddVi { vd, vs2, imm, vm } => {
324                if !ext_state.vector_instructions_allowed() {
325                    Err(ExecutionError::IllegalInstruction {
326                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
327                    })?;
328                }
329                let vtype = ext_state
330                    .vtype()
331                    .ok_or(ExecutionError::IllegalInstruction {
332                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
333                    })?;
334                let group_regs = vtype.vlmul().register_count();
335                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
336                    program_counter,
337                    vd,
338                    group_regs,
339                )?;
340                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
341                    program_counter,
342                    vs2,
343                    group_regs,
344                )?;
345                if !vm && vd.bits() == 0 {
346                    Err(ExecutionError::IllegalInstruction {
347                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
348                    })?;
349                }
350                let sew = vtype.vsew();
351                let vl = ext_state.vl();
352                let vstart = u32::from(ext_state.vstart());
353                // Sign-extend 5-bit immediate for signed sat add
354                let scalar = i64::from(imm).cast_unsigned();
355                // SAFETY: alignment checked above
356                unsafe {
357                    zve64x_fixed_point_helpers::execute_fixed_point_op(
358                        ext_state,
359                        vd,
360                        vs2,
361                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
362                        vm,
363                        vl,
364                        vstart,
365                        sew,
366                        |a, b, sew, _vxrm, vxsat| {
367                            zve64x_fixed_point_helpers::sat_add(a, b, sew, vxsat)
368                        },
369                    );
370                }
371            }
372            // vssubu.vv / vssubu.vx - saturating unsigned subtract
373            Self::VssubuVv { vd, vs2, vs1, vm } => {
374                if !ext_state.vector_instructions_allowed() {
375                    Err(ExecutionError::IllegalInstruction {
376                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
377                    })?;
378                }
379                let vtype = ext_state
380                    .vtype()
381                    .ok_or(ExecutionError::IllegalInstruction {
382                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
383                    })?;
384                let group_regs = vtype.vlmul().register_count();
385                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
386                    program_counter,
387                    vd,
388                    group_regs,
389                )?;
390                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
391                    program_counter,
392                    vs2,
393                    group_regs,
394                )?;
395                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
396                    program_counter,
397                    vs1,
398                    group_regs,
399                )?;
400                if !vm && vd.bits() == 0 {
401                    Err(ExecutionError::IllegalInstruction {
402                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
403                    })?;
404                }
405                let sew = vtype.vsew();
406                let vl = ext_state.vl();
407                let vstart = u32::from(ext_state.vstart());
408                // SAFETY: alignment checked above
409                unsafe {
410                    zve64x_fixed_point_helpers::execute_fixed_point_op(
411                        ext_state,
412                        vd,
413                        vs2,
414                        zve64x_fixed_point_helpers::OpSrc::Vreg(vs1.bits()),
415                        vm,
416                        vl,
417                        vstart,
418                        sew,
419                        |a, b, sew, _vxrm, vxsat| {
420                            zve64x_fixed_point_helpers::sat_subu(a, b, sew, vxsat)
421                        },
422                    );
423                }
424            }
425            Self::VssubuVx {
426                vd,
427                vs2,
428                rs1: _,
429                vm,
430            } => {
431                if !ext_state.vector_instructions_allowed() {
432                    Err(ExecutionError::IllegalInstruction {
433                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
434                    })?;
435                }
436                let vtype = ext_state
437                    .vtype()
438                    .ok_or(ExecutionError::IllegalInstruction {
439                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
440                    })?;
441                let group_regs = vtype.vlmul().register_count();
442                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
443                    program_counter,
444                    vd,
445                    group_regs,
446                )?;
447                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
448                    program_counter,
449                    vs2,
450                    group_regs,
451                )?;
452                if !vm && vd.bits() == 0 {
453                    Err(ExecutionError::IllegalInstruction {
454                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
455                    })?;
456                }
457                let sew = vtype.vsew();
458                let vl = ext_state.vl();
459                let vstart = u32::from(ext_state.vstart());
460                let scalar = rs1_value.as_u64();
461                // SAFETY: alignment checked above
462                unsafe {
463                    zve64x_fixed_point_helpers::execute_fixed_point_op(
464                        ext_state,
465                        vd,
466                        vs2,
467                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
468                        vm,
469                        vl,
470                        vstart,
471                        sew,
472                        |a, b, sew, _vxrm, vxsat| {
473                            zve64x_fixed_point_helpers::sat_subu(a, b, sew, vxsat)
474                        },
475                    );
476                }
477            }
478            // vssub.vv / vssub.vx - saturating signed subtract
479            Self::VssubVv { vd, vs2, vs1, vm } => {
480                if !ext_state.vector_instructions_allowed() {
481                    Err(ExecutionError::IllegalInstruction {
482                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
483                    })?;
484                }
485                let vtype = ext_state
486                    .vtype()
487                    .ok_or(ExecutionError::IllegalInstruction {
488                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
489                    })?;
490                let group_regs = vtype.vlmul().register_count();
491                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
492                    program_counter,
493                    vd,
494                    group_regs,
495                )?;
496                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
497                    program_counter,
498                    vs2,
499                    group_regs,
500                )?;
501                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
502                    program_counter,
503                    vs1,
504                    group_regs,
505                )?;
506                if !vm && vd.bits() == 0 {
507                    Err(ExecutionError::IllegalInstruction {
508                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
509                    })?;
510                }
511                let sew = vtype.vsew();
512                let vl = ext_state.vl();
513                let vstart = u32::from(ext_state.vstart());
514                // SAFETY: alignment checked above
515                unsafe {
516                    zve64x_fixed_point_helpers::execute_fixed_point_op(
517                        ext_state,
518                        vd,
519                        vs2,
520                        zve64x_fixed_point_helpers::OpSrc::Vreg(vs1.bits()),
521                        vm,
522                        vl,
523                        vstart,
524                        sew,
525                        |a, b, sew, _vxrm, vxsat| {
526                            zve64x_fixed_point_helpers::sat_sub(a, b, sew, vxsat)
527                        },
528                    );
529                }
530            }
531            Self::VssubVx {
532                vd,
533                vs2,
534                rs1: _,
535                vm,
536            } => {
537                if !ext_state.vector_instructions_allowed() {
538                    Err(ExecutionError::IllegalInstruction {
539                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
540                    })?;
541                }
542                let vtype = ext_state
543                    .vtype()
544                    .ok_or(ExecutionError::IllegalInstruction {
545                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
546                    })?;
547                let group_regs = vtype.vlmul().register_count();
548                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
549                    program_counter,
550                    vd,
551                    group_regs,
552                )?;
553                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
554                    program_counter,
555                    vs2,
556                    group_regs,
557                )?;
558                if !vm && vd.bits() == 0 {
559                    Err(ExecutionError::IllegalInstruction {
560                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
561                    })?;
562                }
563                let sew = vtype.vsew();
564                let vl = ext_state.vl();
565                let vstart = u32::from(ext_state.vstart());
566                let scalar = rs1_value.as_u64();
567                // SAFETY: alignment checked above
568                unsafe {
569                    zve64x_fixed_point_helpers::execute_fixed_point_op(
570                        ext_state,
571                        vd,
572                        vs2,
573                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
574                        vm,
575                        vl,
576                        vstart,
577                        sew,
578                        |a, b, sew, _vxrm, vxsat| {
579                            zve64x_fixed_point_helpers::sat_sub(a, b, sew, vxsat)
580                        },
581                    );
582                }
583            }
584            // vaaddu.vv / vaaddu.vx - averaging unsigned add
585            Self::VaadduVv { vd, vs2, vs1, vm } => {
586                if !ext_state.vector_instructions_allowed() {
587                    Err(ExecutionError::IllegalInstruction {
588                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
589                    })?;
590                }
591                let vtype = ext_state
592                    .vtype()
593                    .ok_or(ExecutionError::IllegalInstruction {
594                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
595                    })?;
596                let group_regs = vtype.vlmul().register_count();
597                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
598                    program_counter,
599                    vd,
600                    group_regs,
601                )?;
602                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
603                    program_counter,
604                    vs2,
605                    group_regs,
606                )?;
607                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
608                    program_counter,
609                    vs1,
610                    group_regs,
611                )?;
612                if !vm && vd.bits() == 0 {
613                    Err(ExecutionError::IllegalInstruction {
614                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
615                    })?;
616                }
617                let sew = vtype.vsew();
618                let vl = ext_state.vl();
619                let vstart = u32::from(ext_state.vstart());
620                // SAFETY: alignment checked above
621                unsafe {
622                    zve64x_fixed_point_helpers::execute_fixed_point_op(
623                        ext_state,
624                        vd,
625                        vs2,
626                        zve64x_fixed_point_helpers::OpSrc::Vreg(vs1.bits()),
627                        vm,
628                        vl,
629                        vstart,
630                        sew,
631                        |a, b, sew, vxrm, _vxsat| {
632                            zve64x_fixed_point_helpers::avg_addu(a, b, sew, vxrm)
633                        },
634                    );
635                }
636            }
637            Self::VaadduVx {
638                vd,
639                vs2,
640                rs1: _,
641                vm,
642            } => {
643                if !ext_state.vector_instructions_allowed() {
644                    Err(ExecutionError::IllegalInstruction {
645                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
646                    })?;
647                }
648                let vtype = ext_state
649                    .vtype()
650                    .ok_or(ExecutionError::IllegalInstruction {
651                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
652                    })?;
653                let group_regs = vtype.vlmul().register_count();
654                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
655                    program_counter,
656                    vd,
657                    group_regs,
658                )?;
659                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
660                    program_counter,
661                    vs2,
662                    group_regs,
663                )?;
664                if !vm && vd.bits() == 0 {
665                    Err(ExecutionError::IllegalInstruction {
666                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
667                    })?;
668                }
669                let sew = vtype.vsew();
670                let vl = ext_state.vl();
671                let vstart = u32::from(ext_state.vstart());
672                let scalar = rs1_value.as_u64();
673                // SAFETY: alignment checked above
674                unsafe {
675                    zve64x_fixed_point_helpers::execute_fixed_point_op(
676                        ext_state,
677                        vd,
678                        vs2,
679                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
680                        vm,
681                        vl,
682                        vstart,
683                        sew,
684                        |a, b, sew, vxrm, _vxsat| {
685                            zve64x_fixed_point_helpers::avg_addu(a, b, sew, vxrm)
686                        },
687                    );
688                }
689            }
690            // vaadd.vv / vaadd.vx - averaging signed add
691            Self::VaaddVv { vd, vs2, vs1, vm } => {
692                if !ext_state.vector_instructions_allowed() {
693                    Err(ExecutionError::IllegalInstruction {
694                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
695                    })?;
696                }
697                let vtype = ext_state
698                    .vtype()
699                    .ok_or(ExecutionError::IllegalInstruction {
700                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
701                    })?;
702                let group_regs = vtype.vlmul().register_count();
703                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
704                    program_counter,
705                    vd,
706                    group_regs,
707                )?;
708                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
709                    program_counter,
710                    vs2,
711                    group_regs,
712                )?;
713                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
714                    program_counter,
715                    vs1,
716                    group_regs,
717                )?;
718                if !vm && vd.bits() == 0 {
719                    Err(ExecutionError::IllegalInstruction {
720                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
721                    })?;
722                }
723                let sew = vtype.vsew();
724                let vl = ext_state.vl();
725                let vstart = u32::from(ext_state.vstart());
726                // SAFETY: alignment checked above
727                unsafe {
728                    zve64x_fixed_point_helpers::execute_fixed_point_op(
729                        ext_state,
730                        vd,
731                        vs2,
732                        zve64x_fixed_point_helpers::OpSrc::Vreg(vs1.bits()),
733                        vm,
734                        vl,
735                        vstart,
736                        sew,
737                        |a, b, sew, vxrm, _vxsat| {
738                            zve64x_fixed_point_helpers::avg_add(a, b, sew, vxrm)
739                        },
740                    );
741                }
742            }
743            Self::VaaddVx {
744                vd,
745                vs2,
746                rs1: _,
747                vm,
748            } => {
749                if !ext_state.vector_instructions_allowed() {
750                    Err(ExecutionError::IllegalInstruction {
751                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
752                    })?;
753                }
754                let vtype = ext_state
755                    .vtype()
756                    .ok_or(ExecutionError::IllegalInstruction {
757                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
758                    })?;
759                let group_regs = vtype.vlmul().register_count();
760                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
761                    program_counter,
762                    vd,
763                    group_regs,
764                )?;
765                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
766                    program_counter,
767                    vs2,
768                    group_regs,
769                )?;
770                if !vm && vd.bits() == 0 {
771                    Err(ExecutionError::IllegalInstruction {
772                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
773                    })?;
774                }
775                let sew = vtype.vsew();
776                let vl = ext_state.vl();
777                let vstart = u32::from(ext_state.vstart());
778                let scalar = rs1_value.as_u64();
779                // SAFETY: alignment checked above
780                unsafe {
781                    zve64x_fixed_point_helpers::execute_fixed_point_op(
782                        ext_state,
783                        vd,
784                        vs2,
785                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
786                        vm,
787                        vl,
788                        vstart,
789                        sew,
790                        |a, b, sew, vxrm, _vxsat| {
791                            zve64x_fixed_point_helpers::avg_add(a, b, sew, vxrm)
792                        },
793                    );
794                }
795            }
796            // vasubu.vv / vasubu.vx - averaging unsigned subtract
797            Self::VasubuVv { vd, vs2, vs1, vm } => {
798                if !ext_state.vector_instructions_allowed() {
799                    Err(ExecutionError::IllegalInstruction {
800                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
801                    })?;
802                }
803                let vtype = ext_state
804                    .vtype()
805                    .ok_or(ExecutionError::IllegalInstruction {
806                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
807                    })?;
808                let group_regs = vtype.vlmul().register_count();
809                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
810                    program_counter,
811                    vd,
812                    group_regs,
813                )?;
814                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
815                    program_counter,
816                    vs2,
817                    group_regs,
818                )?;
819                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
820                    program_counter,
821                    vs1,
822                    group_regs,
823                )?;
824                if !vm && vd.bits() == 0 {
825                    Err(ExecutionError::IllegalInstruction {
826                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
827                    })?;
828                }
829                let sew = vtype.vsew();
830                let vl = ext_state.vl();
831                let vstart = u32::from(ext_state.vstart());
832                // SAFETY: alignment checked above
833                unsafe {
834                    zve64x_fixed_point_helpers::execute_fixed_point_op(
835                        ext_state,
836                        vd,
837                        vs2,
838                        zve64x_fixed_point_helpers::OpSrc::Vreg(vs1.bits()),
839                        vm,
840                        vl,
841                        vstart,
842                        sew,
843                        |a, b, sew, vxrm, _vxsat| {
844                            zve64x_fixed_point_helpers::avg_subu(a, b, sew, vxrm)
845                        },
846                    );
847                }
848            }
849            Self::VasubuVx {
850                vd,
851                vs2,
852                rs1: _,
853                vm,
854            } => {
855                if !ext_state.vector_instructions_allowed() {
856                    Err(ExecutionError::IllegalInstruction {
857                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
858                    })?;
859                }
860                let vtype = ext_state
861                    .vtype()
862                    .ok_or(ExecutionError::IllegalInstruction {
863                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
864                    })?;
865                let group_regs = vtype.vlmul().register_count();
866                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
867                    program_counter,
868                    vd,
869                    group_regs,
870                )?;
871                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
872                    program_counter,
873                    vs2,
874                    group_regs,
875                )?;
876                if !vm && vd.bits() == 0 {
877                    Err(ExecutionError::IllegalInstruction {
878                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
879                    })?;
880                }
881                let sew = vtype.vsew();
882                let vl = ext_state.vl();
883                let vstart = u32::from(ext_state.vstart());
884                let scalar = rs1_value.as_u64();
885                // SAFETY: alignment checked above
886                unsafe {
887                    zve64x_fixed_point_helpers::execute_fixed_point_op(
888                        ext_state,
889                        vd,
890                        vs2,
891                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
892                        vm,
893                        vl,
894                        vstart,
895                        sew,
896                        |a, b, sew, vxrm, _vxsat| {
897                            zve64x_fixed_point_helpers::avg_subu(a, b, sew, vxrm)
898                        },
899                    );
900                }
901            }
902            // vasub.vv / vasub.vx - averaging signed subtract
903            Self::VasubVv { vd, vs2, vs1, vm } => {
904                if !ext_state.vector_instructions_allowed() {
905                    Err(ExecutionError::IllegalInstruction {
906                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
907                    })?;
908                }
909                let vtype = ext_state
910                    .vtype()
911                    .ok_or(ExecutionError::IllegalInstruction {
912                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
913                    })?;
914                let group_regs = vtype.vlmul().register_count();
915                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
916                    program_counter,
917                    vd,
918                    group_regs,
919                )?;
920                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
921                    program_counter,
922                    vs2,
923                    group_regs,
924                )?;
925                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
926                    program_counter,
927                    vs1,
928                    group_regs,
929                )?;
930                if !vm && vd.bits() == 0 {
931                    Err(ExecutionError::IllegalInstruction {
932                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
933                    })?;
934                }
935                let sew = vtype.vsew();
936                let vl = ext_state.vl();
937                let vstart = u32::from(ext_state.vstart());
938                // SAFETY: alignment checked above
939                unsafe {
940                    zve64x_fixed_point_helpers::execute_fixed_point_op(
941                        ext_state,
942                        vd,
943                        vs2,
944                        zve64x_fixed_point_helpers::OpSrc::Vreg(vs1.bits()),
945                        vm,
946                        vl,
947                        vstart,
948                        sew,
949                        |a, b, sew, vxrm, _vxsat| {
950                            zve64x_fixed_point_helpers::avg_sub(a, b, sew, vxrm)
951                        },
952                    );
953                }
954            }
955            Self::VasubVx {
956                vd,
957                vs2,
958                rs1: _,
959                vm,
960            } => {
961                if !ext_state.vector_instructions_allowed() {
962                    Err(ExecutionError::IllegalInstruction {
963                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
964                    })?;
965                }
966                let vtype = ext_state
967                    .vtype()
968                    .ok_or(ExecutionError::IllegalInstruction {
969                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
970                    })?;
971                let group_regs = vtype.vlmul().register_count();
972                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
973                    program_counter,
974                    vd,
975                    group_regs,
976                )?;
977                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
978                    program_counter,
979                    vs2,
980                    group_regs,
981                )?;
982                if !vm && vd.bits() == 0 {
983                    Err(ExecutionError::IllegalInstruction {
984                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
985                    })?;
986                }
987                let sew = vtype.vsew();
988                let vl = ext_state.vl();
989                let vstart = u32::from(ext_state.vstart());
990                let scalar = rs1_value.as_u64();
991                // SAFETY: alignment checked above
992                unsafe {
993                    zve64x_fixed_point_helpers::execute_fixed_point_op(
994                        ext_state,
995                        vd,
996                        vs2,
997                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
998                        vm,
999                        vl,
1000                        vstart,
1001                        sew,
1002                        |a, b, sew, vxrm, _vxsat| {
1003                            zve64x_fixed_point_helpers::avg_sub(a, b, sew, vxrm)
1004                        },
1005                    );
1006                }
1007            }
1008            // vsmul.vv / vsmul.vx - fractional multiply with rounding and saturation
1009            Self::VsmulVv { vd, vs2, vs1, vm } => {
1010                if !ext_state.vector_instructions_allowed() {
1011                    Err(ExecutionError::IllegalInstruction {
1012                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1013                    })?;
1014                }
1015                let vtype = ext_state
1016                    .vtype()
1017                    .ok_or(ExecutionError::IllegalInstruction {
1018                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1019                    })?;
1020                let group_regs = vtype.vlmul().register_count();
1021                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1022                    program_counter,
1023                    vd,
1024                    group_regs,
1025                )?;
1026                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
1027                    program_counter,
1028                    vs2,
1029                    group_regs,
1030                )?;
1031                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
1032                    program_counter,
1033                    vs1,
1034                    group_regs,
1035                )?;
1036                if !vm && vd.bits() == 0 {
1037                    Err(ExecutionError::IllegalInstruction {
1038                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1039                    })?;
1040                }
1041                let sew = vtype.vsew();
1042                let vl = ext_state.vl();
1043                let vstart = u32::from(ext_state.vstart());
1044                // SAFETY: alignment checked above
1045                unsafe {
1046                    zve64x_fixed_point_helpers::execute_fixed_point_op(
1047                        ext_state,
1048                        vd,
1049                        vs2,
1050                        zve64x_fixed_point_helpers::OpSrc::Vreg(vs1.bits()),
1051                        vm,
1052                        vl,
1053                        vstart,
1054                        sew,
1055                        |a, b, sew, vxrm, vxsat| {
1056                            zve64x_fixed_point_helpers::smul(a, b, sew, vxrm, vxsat)
1057                        },
1058                    );
1059                }
1060            }
1061            Self::VsmulVx {
1062                vd,
1063                vs2,
1064                rs1: _,
1065                vm,
1066            } => {
1067                if !ext_state.vector_instructions_allowed() {
1068                    Err(ExecutionError::IllegalInstruction {
1069                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1070                    })?;
1071                }
1072                let vtype = ext_state
1073                    .vtype()
1074                    .ok_or(ExecutionError::IllegalInstruction {
1075                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1076                    })?;
1077                let group_regs = vtype.vlmul().register_count();
1078                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1079                    program_counter,
1080                    vd,
1081                    group_regs,
1082                )?;
1083                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
1084                    program_counter,
1085                    vs2,
1086                    group_regs,
1087                )?;
1088                if !vm && vd.bits() == 0 {
1089                    Err(ExecutionError::IllegalInstruction {
1090                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1091                    })?;
1092                }
1093                let sew = vtype.vsew();
1094                let vl = ext_state.vl();
1095                let vstart = u32::from(ext_state.vstart());
1096                let scalar = rs1_value.as_u64();
1097                // SAFETY: alignment checked above
1098                unsafe {
1099                    zve64x_fixed_point_helpers::execute_fixed_point_op(
1100                        ext_state,
1101                        vd,
1102                        vs2,
1103                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
1104                        vm,
1105                        vl,
1106                        vstart,
1107                        sew,
1108                        |a, b, sew, vxrm, vxsat| {
1109                            zve64x_fixed_point_helpers::smul(a, b, sew, vxrm, vxsat)
1110                        },
1111                    );
1112                }
1113            }
1114            // vssrl.vv / vssrl.vx / vssrl.vi - scaling shift right logical
1115            Self::VssrlVv { vd, vs2, vs1, vm } => {
1116                if !ext_state.vector_instructions_allowed() {
1117                    Err(ExecutionError::IllegalInstruction {
1118                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1119                    })?;
1120                }
1121                let vtype = ext_state
1122                    .vtype()
1123                    .ok_or(ExecutionError::IllegalInstruction {
1124                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1125                    })?;
1126                let group_regs = vtype.vlmul().register_count();
1127                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1128                    program_counter,
1129                    vd,
1130                    group_regs,
1131                )?;
1132                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
1133                    program_counter,
1134                    vs2,
1135                    group_regs,
1136                )?;
1137                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
1138                    program_counter,
1139                    vs1,
1140                    group_regs,
1141                )?;
1142                if !vm && vd.bits() == 0 {
1143                    Err(ExecutionError::IllegalInstruction {
1144                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1145                    })?;
1146                }
1147                let sew = vtype.vsew();
1148                let vl = ext_state.vl();
1149                let vstart = u32::from(ext_state.vstart());
1150                // SAFETY: alignment checked above
1151                unsafe {
1152                    zve64x_fixed_point_helpers::execute_fixed_point_op(
1153                        ext_state,
1154                        vd,
1155                        vs2,
1156                        zve64x_fixed_point_helpers::OpSrc::Vreg(vs1.bits()),
1157                        vm,
1158                        vl,
1159                        vstart,
1160                        sew,
1161                        |a, b, sew, vxrm, _vxsat| {
1162                            // Shift amount masked to log2(SEW) bits per spec §12.7
1163                            let shamt = (b & u64::from(sew.bits() - 1)) as u32;
1164                            let masked_a = a & zve64x_fixed_point_helpers::sew_mask(sew);
1165                            zve64x_fixed_point_helpers::rounded_srl(masked_a, shamt, vxrm)
1166                                & zve64x_fixed_point_helpers::sew_mask(sew)
1167                        },
1168                    );
1169                }
1170            }
1171            Self::VssrlVx {
1172                vd,
1173                vs2,
1174                rs1: _,
1175                vm,
1176            } => {
1177                if !ext_state.vector_instructions_allowed() {
1178                    Err(ExecutionError::IllegalInstruction {
1179                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1180                    })?;
1181                }
1182                let vtype = ext_state
1183                    .vtype()
1184                    .ok_or(ExecutionError::IllegalInstruction {
1185                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1186                    })?;
1187                let group_regs = vtype.vlmul().register_count();
1188                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1189                    program_counter,
1190                    vd,
1191                    group_regs,
1192                )?;
1193                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
1194                    program_counter,
1195                    vs2,
1196                    group_regs,
1197                )?;
1198                if !vm && vd.bits() == 0 {
1199                    Err(ExecutionError::IllegalInstruction {
1200                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1201                    })?;
1202                }
1203                let sew = vtype.vsew();
1204                let vl = ext_state.vl();
1205                let vstart = u32::from(ext_state.vstart());
1206                let scalar = rs1_value.as_u64();
1207                // SAFETY: alignment checked above
1208                unsafe {
1209                    zve64x_fixed_point_helpers::execute_fixed_point_op(
1210                        ext_state,
1211                        vd,
1212                        vs2,
1213                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
1214                        vm,
1215                        vl,
1216                        vstart,
1217                        sew,
1218                        |a, b, sew, vxrm, _vxsat| {
1219                            let shamt = (b & u64::from(sew.bits() - 1)) as u32;
1220                            let masked_a = a & zve64x_fixed_point_helpers::sew_mask(sew);
1221                            zve64x_fixed_point_helpers::rounded_srl(masked_a, shamt, vxrm)
1222                                & zve64x_fixed_point_helpers::sew_mask(sew)
1223                        },
1224                    );
1225                }
1226            }
1227            Self::VssrlVi { vd, vs2, imm, vm } => {
1228                if !ext_state.vector_instructions_allowed() {
1229                    Err(ExecutionError::IllegalInstruction {
1230                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1231                    })?;
1232                }
1233                let vtype = ext_state
1234                    .vtype()
1235                    .ok_or(ExecutionError::IllegalInstruction {
1236                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1237                    })?;
1238                let group_regs = vtype.vlmul().register_count();
1239                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1240                    program_counter,
1241                    vd,
1242                    group_regs,
1243                )?;
1244                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
1245                    program_counter,
1246                    vs2,
1247                    group_regs,
1248                )?;
1249                if !vm && vd.bits() == 0 {
1250                    Err(ExecutionError::IllegalInstruction {
1251                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1252                    })?;
1253                }
1254                let sew = vtype.vsew();
1255                let vl = ext_state.vl();
1256                let vstart = u32::from(ext_state.vstart());
1257                // Immediate is unsigned 5-bit; mask to log2(SEW) here too
1258                let shamt = (u64::from(imm) & u64::from(sew.bits() - 1)) as u32;
1259                // SAFETY: alignment checked above
1260                unsafe {
1261                    zve64x_fixed_point_helpers::execute_fixed_point_op(
1262                        ext_state,
1263                        vd,
1264                        vs2,
1265                        zve64x_fixed_point_helpers::OpSrc::Scalar(u64::from(shamt)),
1266                        vm,
1267                        vl,
1268                        vstart,
1269                        sew,
1270                        |a, b, sew, vxrm, _vxsat| {
1271                            let shamt = b as u32;
1272                            let masked_a = a & zve64x_fixed_point_helpers::sew_mask(sew);
1273                            zve64x_fixed_point_helpers::rounded_srl(masked_a, shamt, vxrm)
1274                                & zve64x_fixed_point_helpers::sew_mask(sew)
1275                        },
1276                    );
1277                }
1278            }
1279            // vssra.vv / vssra.vx / vssra.vi - scaling shift right arithmetic
1280            Self::VssraVv { vd, vs2, vs1, vm } => {
1281                if !ext_state.vector_instructions_allowed() {
1282                    Err(ExecutionError::IllegalInstruction {
1283                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1284                    })?;
1285                }
1286                let vtype = ext_state
1287                    .vtype()
1288                    .ok_or(ExecutionError::IllegalInstruction {
1289                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1290                    })?;
1291                let group_regs = vtype.vlmul().register_count();
1292                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1293                    program_counter,
1294                    vd,
1295                    group_regs,
1296                )?;
1297                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
1298                    program_counter,
1299                    vs2,
1300                    group_regs,
1301                )?;
1302                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
1303                    program_counter,
1304                    vs1,
1305                    group_regs,
1306                )?;
1307                if !vm && vd.bits() == 0 {
1308                    Err(ExecutionError::IllegalInstruction {
1309                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1310                    })?;
1311                }
1312                let sew = vtype.vsew();
1313                let vl = ext_state.vl();
1314                let vstart = u32::from(ext_state.vstart());
1315                // SAFETY: alignment checked above
1316                unsafe {
1317                    zve64x_fixed_point_helpers::execute_fixed_point_op(
1318                        ext_state,
1319                        vd,
1320                        vs2,
1321                        zve64x_fixed_point_helpers::OpSrc::Vreg(vs1.bits()),
1322                        vm,
1323                        vl,
1324                        vstart,
1325                        sew,
1326                        |a, b, sew, vxrm, _vxsat| {
1327                            let shamt = (b & u64::from(sew.bits() - 1)) as u32;
1328                            zve64x_fixed_point_helpers::rounded_sra(a, shamt, vxrm, sew)
1329                                & zve64x_fixed_point_helpers::sew_mask(sew)
1330                        },
1331                    );
1332                }
1333            }
1334            Self::VssraVx {
1335                vd,
1336                vs2,
1337                rs1: _,
1338                vm,
1339            } => {
1340                if !ext_state.vector_instructions_allowed() {
1341                    Err(ExecutionError::IllegalInstruction {
1342                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1343                    })?;
1344                }
1345                let vtype = ext_state
1346                    .vtype()
1347                    .ok_or(ExecutionError::IllegalInstruction {
1348                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1349                    })?;
1350                let group_regs = vtype.vlmul().register_count();
1351                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1352                    program_counter,
1353                    vd,
1354                    group_regs,
1355                )?;
1356                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
1357                    program_counter,
1358                    vs2,
1359                    group_regs,
1360                )?;
1361                if !vm && vd.bits() == 0 {
1362                    Err(ExecutionError::IllegalInstruction {
1363                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1364                    })?;
1365                }
1366                let sew = vtype.vsew();
1367                let vl = ext_state.vl();
1368                let vstart = u32::from(ext_state.vstart());
1369                let scalar = rs1_value.as_u64();
1370                // SAFETY: alignment checked above
1371                unsafe {
1372                    zve64x_fixed_point_helpers::execute_fixed_point_op(
1373                        ext_state,
1374                        vd,
1375                        vs2,
1376                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
1377                        vm,
1378                        vl,
1379                        vstart,
1380                        sew,
1381                        |a, b, sew, vxrm, _vxsat| {
1382                            let shamt = (b & u64::from(sew.bits() - 1)) as u32;
1383                            zve64x_fixed_point_helpers::rounded_sra(a, shamt, vxrm, sew)
1384                                & zve64x_fixed_point_helpers::sew_mask(sew)
1385                        },
1386                    );
1387                }
1388            }
1389            Self::VssraVi { vd, vs2, imm, vm } => {
1390                if !ext_state.vector_instructions_allowed() {
1391                    Err(ExecutionError::IllegalInstruction {
1392                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1393                    })?;
1394                }
1395                let vtype = ext_state
1396                    .vtype()
1397                    .ok_or(ExecutionError::IllegalInstruction {
1398                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1399                    })?;
1400                let group_regs = vtype.vlmul().register_count();
1401                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1402                    program_counter,
1403                    vd,
1404                    group_regs,
1405                )?;
1406                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
1407                    program_counter,
1408                    vs2,
1409                    group_regs,
1410                )?;
1411                if !vm && vd.bits() == 0 {
1412                    Err(ExecutionError::IllegalInstruction {
1413                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1414                    })?;
1415                }
1416                let sew = vtype.vsew();
1417                let vl = ext_state.vl();
1418                let vstart = u32::from(ext_state.vstart());
1419                let shamt = (u64::from(imm) & u64::from(sew.bits() - 1)) as u32;
1420                // SAFETY: alignment checked above
1421                unsafe {
1422                    zve64x_fixed_point_helpers::execute_fixed_point_op(
1423                        ext_state,
1424                        vd,
1425                        vs2,
1426                        zve64x_fixed_point_helpers::OpSrc::Scalar(u64::from(shamt)),
1427                        vm,
1428                        vl,
1429                        vstart,
1430                        sew,
1431                        |a, b, sew, vxrm, _vxsat| {
1432                            let shamt = b as u32;
1433                            zve64x_fixed_point_helpers::rounded_sra(a, shamt, vxrm, sew)
1434                                & zve64x_fixed_point_helpers::sew_mask(sew)
1435                        },
1436                    );
1437                }
1438            }
1439            // vnclipu.wv / vnclipu.wx / vnclipu.wi - narrowing unsigned clip
1440            Self::VnclipuWv { vd, vs2, vs1, vm } => {
1441                if !ext_state.vector_instructions_allowed() {
1442                    Err(ExecutionError::IllegalInstruction {
1443                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1444                    })?;
1445                }
1446                let vtype = ext_state
1447                    .vtype()
1448                    .ok_or(ExecutionError::IllegalInstruction {
1449                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1450                    })?;
1451                // Destination SEW must be <= 32 so that 2*SEW fits in 64 bits
1452                let sew = vtype.vsew();
1453                zve64x_fixed_point_helpers::check_narrowing_sew::<Reg, _, _, _>(
1454                    program_counter,
1455                    sew,
1456                )?;
1457                let group_regs = vtype.vlmul().register_count();
1458                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1459                    program_counter,
1460                    vd,
1461                    group_regs,
1462                )?;
1463                // vs2 holds 2*SEW elements; its register group is double-width
1464                zve64x_fixed_point_helpers::check_vs2_narrowing_alignment::<Reg, _, _, _>(
1465                    program_counter,
1466                    vs2,
1467                    group_regs,
1468                )?;
1469                // vs1 is a normal SEW-wide source for the shift amount
1470                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
1471                    program_counter,
1472                    vs1,
1473                    group_regs,
1474                )?;
1475                if !vm && vd.bits() == 0 {
1476                    Err(ExecutionError::IllegalInstruction {
1477                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1478                    })?;
1479                }
1480                let vl = ext_state.vl();
1481                let vstart = u32::from(ext_state.vstart());
1482                // SAFETY: sew <= 32 checked; alignment checked above
1483                unsafe {
1484                    zve64x_fixed_point_helpers::execute_narrowing_clip_op(
1485                        ext_state,
1486                        vd,
1487                        vs2,
1488                        zve64x_fixed_point_helpers::OpSrc::Vreg(vs1.bits()),
1489                        vm,
1490                        vl,
1491                        vstart,
1492                        sew,
1493                        |wide, shamt, sew, vxrm, vxsat| {
1494                            zve64x_fixed_point_helpers::nclipu(wide, shamt, sew, vxrm, vxsat)
1495                        },
1496                    );
1497                }
1498            }
1499            Self::VnclipuWx {
1500                vd,
1501                vs2,
1502                rs1: _,
1503                vm,
1504            } => {
1505                if !ext_state.vector_instructions_allowed() {
1506                    Err(ExecutionError::IllegalInstruction {
1507                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1508                    })?;
1509                }
1510                let vtype = ext_state
1511                    .vtype()
1512                    .ok_or(ExecutionError::IllegalInstruction {
1513                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1514                    })?;
1515                let sew = vtype.vsew();
1516                zve64x_fixed_point_helpers::check_narrowing_sew::<Reg, _, _, _>(
1517                    program_counter,
1518                    sew,
1519                )?;
1520                let group_regs = vtype.vlmul().register_count();
1521                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1522                    program_counter,
1523                    vd,
1524                    group_regs,
1525                )?;
1526                zve64x_fixed_point_helpers::check_vs2_narrowing_alignment::<Reg, _, _, _>(
1527                    program_counter,
1528                    vs2,
1529                    group_regs,
1530                )?;
1531                if !vm && vd.bits() == 0 {
1532                    Err(ExecutionError::IllegalInstruction {
1533                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1534                    })?;
1535                }
1536                let vl = ext_state.vl();
1537                let vstart = u32::from(ext_state.vstart());
1538                let scalar = rs1_value.as_u64();
1539                // SAFETY: sew <= 32 checked; alignment checked above
1540                unsafe {
1541                    zve64x_fixed_point_helpers::execute_narrowing_clip_op(
1542                        ext_state,
1543                        vd,
1544                        vs2,
1545                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
1546                        vm,
1547                        vl,
1548                        vstart,
1549                        sew,
1550                        |wide, shamt, sew, vxrm, vxsat| {
1551                            zve64x_fixed_point_helpers::nclipu(wide, shamt, sew, vxrm, vxsat)
1552                        },
1553                    );
1554                }
1555            }
1556            Self::VnclipuWi { vd, vs2, imm, vm } => {
1557                if !ext_state.vector_instructions_allowed() {
1558                    Err(ExecutionError::IllegalInstruction {
1559                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1560                    })?;
1561                }
1562                let vtype = ext_state
1563                    .vtype()
1564                    .ok_or(ExecutionError::IllegalInstruction {
1565                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1566                    })?;
1567                let sew = vtype.vsew();
1568                zve64x_fixed_point_helpers::check_narrowing_sew::<Reg, _, _, _>(
1569                    program_counter,
1570                    sew,
1571                )?;
1572                let group_regs = vtype.vlmul().register_count();
1573                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1574                    program_counter,
1575                    vd,
1576                    group_regs,
1577                )?;
1578                zve64x_fixed_point_helpers::check_vs2_narrowing_alignment::<Reg, _, _, _>(
1579                    program_counter,
1580                    vs2,
1581                    group_regs,
1582                )?;
1583                if !vm && vd.bits() == 0 {
1584                    Err(ExecutionError::IllegalInstruction {
1585                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1586                    })?;
1587                }
1588                let vl = ext_state.vl();
1589                let vstart = u32::from(ext_state.vstart());
1590                // SAFETY: sew <= 32 checked; alignment checked above
1591                unsafe {
1592                    zve64x_fixed_point_helpers::execute_narrowing_clip_op(
1593                        ext_state,
1594                        vd,
1595                        vs2,
1596                        // Immediate is the shift amount directly; masking done inside the helper
1597                        zve64x_fixed_point_helpers::OpSrc::Scalar(u64::from(imm)),
1598                        vm,
1599                        vl,
1600                        vstart,
1601                        sew,
1602                        |wide, shamt, sew, vxrm, vxsat| {
1603                            zve64x_fixed_point_helpers::nclipu(wide, shamt, sew, vxrm, vxsat)
1604                        },
1605                    );
1606                }
1607            }
1608            // vnclip.wv / vnclip.wx / vnclip.wi - narrowing signed clip
1609            Self::VnclipWv { vd, vs2, vs1, vm } => {
1610                if !ext_state.vector_instructions_allowed() {
1611                    Err(ExecutionError::IllegalInstruction {
1612                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1613                    })?;
1614                }
1615                let vtype = ext_state
1616                    .vtype()
1617                    .ok_or(ExecutionError::IllegalInstruction {
1618                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1619                    })?;
1620                let sew = vtype.vsew();
1621                zve64x_fixed_point_helpers::check_narrowing_sew::<Reg, _, _, _>(
1622                    program_counter,
1623                    sew,
1624                )?;
1625                let group_regs = vtype.vlmul().register_count();
1626                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1627                    program_counter,
1628                    vd,
1629                    group_regs,
1630                )?;
1631                zve64x_fixed_point_helpers::check_vs2_narrowing_alignment::<Reg, _, _, _>(
1632                    program_counter,
1633                    vs2,
1634                    group_regs,
1635                )?;
1636                zve64x_fixed_point_helpers::check_vs::<Reg, _, _, _>(
1637                    program_counter,
1638                    vs1,
1639                    group_regs,
1640                )?;
1641                if !vm && vd.bits() == 0 {
1642                    Err(ExecutionError::IllegalInstruction {
1643                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1644                    })?;
1645                }
1646                let vl = ext_state.vl();
1647                let vstart = u32::from(ext_state.vstart());
1648                // SAFETY: sew <= 32 checked; alignment checked above
1649                unsafe {
1650                    zve64x_fixed_point_helpers::execute_narrowing_clip_op(
1651                        ext_state,
1652                        vd,
1653                        vs2,
1654                        zve64x_fixed_point_helpers::OpSrc::Vreg(vs1.bits()),
1655                        vm,
1656                        vl,
1657                        vstart,
1658                        sew,
1659                        |wide, shamt, sew, vxrm, vxsat| {
1660                            zve64x_fixed_point_helpers::nclip(wide, shamt, sew, vxrm, vxsat)
1661                        },
1662                    );
1663                }
1664            }
1665            Self::VnclipWx {
1666                vd,
1667                vs2,
1668                rs1: _,
1669                vm,
1670            } => {
1671                if !ext_state.vector_instructions_allowed() {
1672                    Err(ExecutionError::IllegalInstruction {
1673                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1674                    })?;
1675                }
1676                let vtype = ext_state
1677                    .vtype()
1678                    .ok_or(ExecutionError::IllegalInstruction {
1679                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1680                    })?;
1681                let sew = vtype.vsew();
1682                zve64x_fixed_point_helpers::check_narrowing_sew::<Reg, _, _, _>(
1683                    program_counter,
1684                    sew,
1685                )?;
1686                let group_regs = vtype.vlmul().register_count();
1687                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1688                    program_counter,
1689                    vd,
1690                    group_regs,
1691                )?;
1692                zve64x_fixed_point_helpers::check_vs2_narrowing_alignment::<Reg, _, _, _>(
1693                    program_counter,
1694                    vs2,
1695                    group_regs,
1696                )?;
1697                if !vm && vd.bits() == 0 {
1698                    Err(ExecutionError::IllegalInstruction {
1699                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1700                    })?;
1701                }
1702                let vl = ext_state.vl();
1703                let vstart = u32::from(ext_state.vstart());
1704                let scalar = rs1_value.as_u64();
1705                // SAFETY: sew <= 32 checked; alignment checked above
1706                unsafe {
1707                    zve64x_fixed_point_helpers::execute_narrowing_clip_op(
1708                        ext_state,
1709                        vd,
1710                        vs2,
1711                        zve64x_fixed_point_helpers::OpSrc::Scalar(scalar),
1712                        vm,
1713                        vl,
1714                        vstart,
1715                        sew,
1716                        |wide, shamt, sew, vxrm, vxsat| {
1717                            zve64x_fixed_point_helpers::nclip(wide, shamt, sew, vxrm, vxsat)
1718                        },
1719                    );
1720                }
1721            }
1722            Self::VnclipWi { vd, vs2, imm, vm } => {
1723                if !ext_state.vector_instructions_allowed() {
1724                    Err(ExecutionError::IllegalInstruction {
1725                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1726                    })?;
1727                }
1728                let vtype = ext_state
1729                    .vtype()
1730                    .ok_or(ExecutionError::IllegalInstruction {
1731                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1732                    })?;
1733                let sew = vtype.vsew();
1734                zve64x_fixed_point_helpers::check_narrowing_sew::<Reg, _, _, _>(
1735                    program_counter,
1736                    sew,
1737                )?;
1738                let group_regs = vtype.vlmul().register_count();
1739                zve64x_fixed_point_helpers::check_vd::<Reg, _, _, _>(
1740                    program_counter,
1741                    vd,
1742                    group_regs,
1743                )?;
1744                zve64x_fixed_point_helpers::check_vs2_narrowing_alignment::<Reg, _, _, _>(
1745                    program_counter,
1746                    vs2,
1747                    group_regs,
1748                )?;
1749                if !vm && vd.bits() == 0 {
1750                    Err(ExecutionError::IllegalInstruction {
1751                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
1752                    })?;
1753                }
1754                let vl = ext_state.vl();
1755                let vstart = u32::from(ext_state.vstart());
1756                // SAFETY: sew <= 32 checked; alignment checked above
1757                unsafe {
1758                    zve64x_fixed_point_helpers::execute_narrowing_clip_op(
1759                        ext_state,
1760                        vd,
1761                        vs2,
1762                        zve64x_fixed_point_helpers::OpSrc::Scalar(u64::from(imm)),
1763                        vm,
1764                        vl,
1765                        vstart,
1766                        sew,
1767                        |wide, shamt, sew, vxrm, vxsat| {
1768                            zve64x_fixed_point_helpers::nclip(wide, shamt, sew, vxrm, vxsat)
1769                        },
1770                    );
1771                }
1772            }
1773        }
1774
1775        Ok(ControlFlow::Continue(Default::default()))
1776    }
1777}