Skip to main content

ab_riscv_interpreter/v/zvexx/
fixed_point.rs

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