Skip to main content

ab_riscv_interpreter/v/zve64x/
fixed_point.rs

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