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