Skip to main content

ab_riscv_interpreter/v/zvexx/
fixed_point.rs

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