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