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