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