Skip to main content

ab_riscv_interpreter/v/zvexx/
muldiv.rs

1//! ZveXx multiply and divide instructions
2
3#[cfg(test)]
4mod tests;
5pub mod zvexx_muldiv_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> ExecutableInstructionOperands for ZveXxMulDivInstruction<Reg> where Reg: Register {}
20
21#[instruction_execution]
22const impl<Reg, Env> ExecutableInstructionCsr<Env> for ZveXxMulDivInstruction<Reg> where
23    Reg: Register
24{
25}
26
27#[instruction_execution]
28impl<Reg, Regs, Env, Memory, PC> ExecutableInstruction<Regs, Env, Memory, PC>
29    for ZveXxMulDivInstruction<Reg>
30where
31    Reg: Register,
32    Regs: RegisterFile<Reg>,
33    Env: VectorRegistersExt<Reg>,
34    [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
35    Memory: VirtualMemory,
36    PC: ProgramCounter<Reg::Type, Memory>,
37{
38    #[inline(always)]
39    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
40    fn execute(
41        self,
42        Rs1Rs2OperandValues {
43            rs1_value,
44            rs2_value: _,
45        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
46        _regs: &mut Regs,
47        env: &mut Env,
48        _memory: &mut Memory,
49        program_counter: &mut PC,
50    ) -> ExecutionResult<Self::Reg> {
51        match self {
52            // vmul.vv / vmul.vx - signed multiply, low half
53            Self::VmulVv { vd, vs2, vs1, vm } => {
54                if !env.vector_instructions_allowed() {
55                    ::core::hint::cold_path();
56                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
57                        address: PackedAddress::new(
58                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
59                        ),
60                    });
61                }
62                let Some(vtype) = env.vtype() else {
63                    ::core::hint::cold_path();
64                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
65                        address: PackedAddress::new(
66                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
67                        ),
68                    });
69                };
70                let group_regs = vtype.vlmul().register_count();
71                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
72                    program_counter,
73                    vd,
74                    group_regs,
75                )?;
76                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
77                    program_counter,
78                    vs2,
79                    group_regs,
80                )?;
81                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
82                    program_counter,
83                    vs1,
84                    group_regs,
85                )?;
86                if !vm && vd == VReg::V0 {
87                    ::core::hint::cold_path();
88                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
89                        address: PackedAddress::new(
90                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
91                        ),
92                    });
93                }
94                let sew = vtype.vsew();
95                // SAFETY: alignment checked above
96                unsafe {
97                    zvexx_muldiv_helpers::execute_arith_op(
98                        env,
99                        vd,
100                        vs2,
101                        zvexx_muldiv_helpers::OpSrc::Vreg(vs1),
102                        vm,
103                        sew,
104                        |a, b, _| a.wrapping_mul(b),
105                    );
106                }
107            }
108            Self::VmulVx {
109                vd,
110                vs2,
111                rs1: _,
112                vm,
113            } => {
114                if !env.vector_instructions_allowed() {
115                    ::core::hint::cold_path();
116                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
117                        address: PackedAddress::new(
118                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
119                        ),
120                    });
121                }
122                let Some(vtype) = env.vtype() else {
123                    ::core::hint::cold_path();
124                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
125                        address: PackedAddress::new(
126                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
127                        ),
128                    });
129                };
130                let group_regs = vtype.vlmul().register_count();
131                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
132                    program_counter,
133                    vd,
134                    group_regs,
135                )?;
136                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
137                    program_counter,
138                    vs2,
139                    group_regs,
140                )?;
141                if !vm && vd == VReg::V0 {
142                    ::core::hint::cold_path();
143                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
144                        address: PackedAddress::new(
145                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
146                        ),
147                    });
148                }
149                let sew = vtype.vsew();
150                let scalar = rs1_value.as_i64().cast_unsigned();
151                // SAFETY: alignment checked above
152                unsafe {
153                    zvexx_muldiv_helpers::execute_arith_op(
154                        env,
155                        vd,
156                        vs2,
157                        zvexx_muldiv_helpers::OpSrc::Scalar(scalar),
158                        vm,
159                        sew,
160                        |a, b, _| a.wrapping_mul(b),
161                    );
162                }
163            }
164            // vmulh.vv / vmulh.vx - signed×signed multiply, high half
165            Self::VmulhVv { vd, vs2, vs1, vm } => {
166                if !env.vector_instructions_allowed() {
167                    ::core::hint::cold_path();
168                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
169                        address: PackedAddress::new(
170                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
171                        ),
172                    });
173                }
174                let Some(vtype) = env.vtype() else {
175                    ::core::hint::cold_path();
176                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
177                        address: PackedAddress::new(
178                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
179                        ),
180                    });
181                };
182                // Zve64x excludes the high-half multiplies at SEW=64 (spec §18.2). The full "V"
183                // extension includes them; the arithmetic itself is width-complete because the
184                // 2*SEW product is formed in i128/u128
185                if !Self::implements_extension::<V<_>>() && vtype.vsew() == Vsew::E64 {
186                    ::core::hint::cold_path();
187                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
188                        address: PackedAddress::new(
189                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
190                        ),
191                    });
192                }
193                let group_regs = vtype.vlmul().register_count();
194                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
195                    program_counter,
196                    vd,
197                    group_regs,
198                )?;
199                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
200                    program_counter,
201                    vs2,
202                    group_regs,
203                )?;
204                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
205                    program_counter,
206                    vs1,
207                    group_regs,
208                )?;
209                if !vm && vd == VReg::V0 {
210                    ::core::hint::cold_path();
211                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
212                        address: PackedAddress::new(
213                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
214                        ),
215                    });
216                }
217                let sew = vtype.vsew();
218                // SAFETY: alignment checked above
219                unsafe {
220                    zvexx_muldiv_helpers::execute_arith_op(
221                        env,
222                        vd,
223                        vs2,
224                        zvexx_muldiv_helpers::OpSrc::Vreg(vs1),
225                        vm,
226                        sew,
227                        zvexx_muldiv_helpers::mulh_ss,
228                    );
229                }
230            }
231            Self::VmulhVx {
232                vd,
233                vs2,
234                rs1: _,
235                vm,
236            } => {
237                if !env.vector_instructions_allowed() {
238                    ::core::hint::cold_path();
239                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
240                        address: PackedAddress::new(
241                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
242                        ),
243                    });
244                }
245                let Some(vtype) = env.vtype() else {
246                    ::core::hint::cold_path();
247                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
248                        address: PackedAddress::new(
249                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
250                        ),
251                    });
252                };
253                // Zve64x excludes the high-half multiplies at SEW=64 (spec §18.2). The full "V"
254                // extension includes them; the arithmetic itself is width-complete because the
255                // 2*SEW product is formed in i128/u128
256                if !Self::implements_extension::<V<_>>() && vtype.vsew() == Vsew::E64 {
257                    ::core::hint::cold_path();
258                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
259                        address: PackedAddress::new(
260                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
261                        ),
262                    });
263                }
264                let group_regs = vtype.vlmul().register_count();
265                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
266                    program_counter,
267                    vd,
268                    group_regs,
269                )?;
270                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
271                    program_counter,
272                    vs2,
273                    group_regs,
274                )?;
275                if !vm && vd == VReg::V0 {
276                    ::core::hint::cold_path();
277                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
278                        address: PackedAddress::new(
279                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
280                        ),
281                    });
282                }
283                let sew = vtype.vsew();
284                let scalar = rs1_value.as_u64();
285                // SAFETY: alignment checked above
286                unsafe {
287                    zvexx_muldiv_helpers::execute_arith_op(
288                        env,
289                        vd,
290                        vs2,
291                        zvexx_muldiv_helpers::OpSrc::Scalar(scalar),
292                        vm,
293                        sew,
294                        zvexx_muldiv_helpers::mulh_ss,
295                    );
296                }
297            }
298            // vmulhu.vv / vmulhu.vx - unsigned×unsigned multiply, high half
299            Self::VmulhuVv { vd, vs2, vs1, vm } => {
300                if !env.vector_instructions_allowed() {
301                    ::core::hint::cold_path();
302                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
303                        address: PackedAddress::new(
304                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
305                        ),
306                    });
307                }
308                let Some(vtype) = env.vtype() else {
309                    ::core::hint::cold_path();
310                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
311                        address: PackedAddress::new(
312                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
313                        ),
314                    });
315                };
316                // Zve64x excludes the high-half multiplies at SEW=64 (spec §18.2). The full "V"
317                // extension includes them; the arithmetic itself is width-complete because the
318                // 2*SEW product is formed in i128/u128
319                if !Self::implements_extension::<V<_>>() && vtype.vsew() == Vsew::E64 {
320                    ::core::hint::cold_path();
321                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
322                        address: PackedAddress::new(
323                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
324                        ),
325                    });
326                }
327                let group_regs = vtype.vlmul().register_count();
328                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
329                    program_counter,
330                    vd,
331                    group_regs,
332                )?;
333                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
334                    program_counter,
335                    vs2,
336                    group_regs,
337                )?;
338                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
339                    program_counter,
340                    vs1,
341                    group_regs,
342                )?;
343                if !vm && vd == VReg::V0 {
344                    ::core::hint::cold_path();
345                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
346                        address: PackedAddress::new(
347                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
348                        ),
349                    });
350                }
351                let sew = vtype.vsew();
352                // SAFETY: alignment checked above
353                unsafe {
354                    zvexx_muldiv_helpers::execute_arith_op(
355                        env,
356                        vd,
357                        vs2,
358                        zvexx_muldiv_helpers::OpSrc::Vreg(vs1),
359                        vm,
360                        sew,
361                        zvexx_muldiv_helpers::mulhu_uu,
362                    );
363                }
364            }
365            Self::VmulhuVx {
366                vd,
367                vs2,
368                rs1: _,
369                vm,
370            } => {
371                if !env.vector_instructions_allowed() {
372                    ::core::hint::cold_path();
373                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
374                        address: PackedAddress::new(
375                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
376                        ),
377                    });
378                }
379                let Some(vtype) = env.vtype() else {
380                    ::core::hint::cold_path();
381                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
382                        address: PackedAddress::new(
383                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
384                        ),
385                    });
386                };
387                // Zve64x excludes the high-half multiplies at SEW=64 (spec §18.2). The full "V"
388                // extension includes them; the arithmetic itself is width-complete because the
389                // 2*SEW product is formed in i128/u128
390                if !Self::implements_extension::<V<_>>() && vtype.vsew() == Vsew::E64 {
391                    ::core::hint::cold_path();
392                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
393                        address: PackedAddress::new(
394                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
395                        ),
396                    });
397                }
398                let group_regs = vtype.vlmul().register_count();
399                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
400                    program_counter,
401                    vd,
402                    group_regs,
403                )?;
404                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
405                    program_counter,
406                    vs2,
407                    group_regs,
408                )?;
409                if !vm && vd == VReg::V0 {
410                    ::core::hint::cold_path();
411                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
412                        address: PackedAddress::new(
413                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
414                        ),
415                    });
416                }
417                let sew = vtype.vsew();
418                let scalar = rs1_value.as_u64();
419                // SAFETY: alignment checked above
420                unsafe {
421                    zvexx_muldiv_helpers::execute_arith_op(
422                        env,
423                        vd,
424                        vs2,
425                        zvexx_muldiv_helpers::OpSrc::Scalar(scalar),
426                        vm,
427                        sew,
428                        zvexx_muldiv_helpers::mulhu_uu,
429                    );
430                }
431            }
432            // vmulhsu.vv / vmulhsu.vx - signed×unsigned multiply, high half
433            Self::VmulhsuVv { vd, vs2, vs1, vm } => {
434                if !env.vector_instructions_allowed() {
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 Some(vtype) = env.vtype() else {
443                    ::core::hint::cold_path();
444                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
445                        address: PackedAddress::new(
446                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
447                        ),
448                    });
449                };
450                // Zve64x excludes the high-half multiplies at SEW=64 (spec §18.2). The full "V"
451                // extension includes them; the arithmetic itself is width-complete because the
452                // 2*SEW product is formed in i128/u128
453                if !Self::implements_extension::<V<_>>() && vtype.vsew() == Vsew::E64 {
454                    ::core::hint::cold_path();
455                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
456                        address: PackedAddress::new(
457                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
458                        ),
459                    });
460                }
461                let group_regs = vtype.vlmul().register_count();
462                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
463                    program_counter,
464                    vd,
465                    group_regs,
466                )?;
467                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
468                    program_counter,
469                    vs2,
470                    group_regs,
471                )?;
472                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
473                    program_counter,
474                    vs1,
475                    group_regs,
476                )?;
477                if !vm && vd == VReg::V0 {
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 sew = vtype.vsew();
486                // SAFETY: alignment checked above
487                unsafe {
488                    zvexx_muldiv_helpers::execute_arith_op(
489                        env,
490                        vd,
491                        vs2,
492                        zvexx_muldiv_helpers::OpSrc::Vreg(vs1),
493                        vm,
494                        sew,
495                        // vs2 is signed, vs1 is unsigned
496                        zvexx_muldiv_helpers::mulhsu_su,
497                    );
498                }
499            }
500            Self::VmulhsuVx {
501                vd,
502                vs2,
503                rs1: _,
504                vm,
505            } => {
506                if !env.vector_instructions_allowed() {
507                    ::core::hint::cold_path();
508                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
509                        address: PackedAddress::new(
510                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
511                        ),
512                    });
513                }
514                let Some(vtype) = env.vtype() else {
515                    ::core::hint::cold_path();
516                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
517                        address: PackedAddress::new(
518                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
519                        ),
520                    });
521                };
522                // Zve64x excludes the high-half multiplies at SEW=64 (spec §18.2). The full "V"
523                // extension includes them; the arithmetic itself is width-complete because the
524                // 2*SEW product is formed in i128/u128
525                if !Self::implements_extension::<V<_>>() && vtype.vsew() == Vsew::E64 {
526                    ::core::hint::cold_path();
527                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
528                        address: PackedAddress::new(
529                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
530                        ),
531                    });
532                }
533                let group_regs = vtype.vlmul().register_count();
534                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
535                    program_counter,
536                    vd,
537                    group_regs,
538                )?;
539                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
540                    program_counter,
541                    vs2,
542                    group_regs,
543                )?;
544                if !vm && vd == VReg::V0 {
545                    ::core::hint::cold_path();
546                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
547                        address: PackedAddress::new(
548                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
549                        ),
550                    });
551                }
552                let sew = vtype.vsew();
553                // scalar from rs1 is the unsigned operand; vs2 elements are signed
554                let scalar = rs1_value.as_u64();
555                // SAFETY: alignment checked above
556                unsafe {
557                    zvexx_muldiv_helpers::execute_arith_op(
558                        env,
559                        vd,
560                        vs2,
561                        zvexx_muldiv_helpers::OpSrc::Scalar(scalar),
562                        vm,
563                        sew,
564                        // vs2 is signed, scalar (rs1) is unsigned
565                        zvexx_muldiv_helpers::mulhsu_su,
566                    );
567                }
568            }
569            // vdivu.vv / vdivu.vx - unsigned divide
570            Self::VdivuVv { vd, vs2, vs1, vm } => {
571                if !env.vector_instructions_allowed() {
572                    ::core::hint::cold_path();
573                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
574                        address: PackedAddress::new(
575                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
576                        ),
577                    });
578                }
579                let Some(vtype) = env.vtype() else {
580                    ::core::hint::cold_path();
581                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
582                        address: PackedAddress::new(
583                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
584                        ),
585                    });
586                };
587                let group_regs = vtype.vlmul().register_count();
588                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
589                    program_counter,
590                    vd,
591                    group_regs,
592                )?;
593                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
594                    program_counter,
595                    vs2,
596                    group_regs,
597                )?;
598                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
599                    program_counter,
600                    vs1,
601                    group_regs,
602                )?;
603                if !vm && vd == VReg::V0 {
604                    ::core::hint::cold_path();
605                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
606                        address: PackedAddress::new(
607                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
608                        ),
609                    });
610                }
611                let sew = vtype.vsew();
612                // SAFETY: alignment checked above
613                unsafe {
614                    zvexx_muldiv_helpers::execute_arith_op(
615                        env,
616                        vd,
617                        vs2,
618                        zvexx_muldiv_helpers::OpSrc::Vreg(vs1),
619                        vm,
620                        sew,
621                        // Division by zero: quotient = all-ones for the SEW width (spec §12.11)
622                        |a, b, sew| {
623                            let mask = zvexx_muldiv_helpers::sew_mask(sew);
624                            let dividend = a & mask;
625                            let divisor = b & mask;
626                            dividend.checked_div(divisor).unwrap_or(mask)
627                        },
628                    );
629                }
630            }
631            Self::VdivuVx {
632                vd,
633                vs2,
634                rs1: _,
635                vm,
636            } => {
637                if !env.vector_instructions_allowed() {
638                    ::core::hint::cold_path();
639                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
640                        address: PackedAddress::new(
641                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
642                        ),
643                    });
644                }
645                let Some(vtype) = env.vtype() else {
646                    ::core::hint::cold_path();
647                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
648                        address: PackedAddress::new(
649                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
650                        ),
651                    });
652                };
653                let group_regs = vtype.vlmul().register_count();
654                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
655                    program_counter,
656                    vd,
657                    group_regs,
658                )?;
659                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
660                    program_counter,
661                    vs2,
662                    group_regs,
663                )?;
664                if !vm && vd == VReg::V0 {
665                    ::core::hint::cold_path();
666                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
667                        address: PackedAddress::new(
668                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
669                        ),
670                    });
671                }
672                let sew = vtype.vsew();
673                let scalar = rs1_value.as_i64().cast_unsigned();
674                // SAFETY: alignment checked above
675                unsafe {
676                    zvexx_muldiv_helpers::execute_arith_op(
677                        env,
678                        vd,
679                        vs2,
680                        zvexx_muldiv_helpers::OpSrc::Scalar(scalar),
681                        vm,
682                        sew,
683                        |a, b, sew| {
684                            let mask = zvexx_muldiv_helpers::sew_mask(sew);
685                            let dividend = a & mask;
686                            let divisor = b & mask;
687                            dividend.checked_div(divisor).unwrap_or(mask)
688                        },
689                    );
690                }
691            }
692            // vdiv.vv / vdiv.vx - signed divide
693            Self::VdivVv { vd, vs2, vs1, vm } => {
694                if !env.vector_instructions_allowed() {
695                    ::core::hint::cold_path();
696                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
697                        address: PackedAddress::new(
698                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
699                        ),
700                    });
701                }
702                let Some(vtype) = env.vtype() else {
703                    ::core::hint::cold_path();
704                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
705                        address: PackedAddress::new(
706                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
707                        ),
708                    });
709                };
710                let group_regs = vtype.vlmul().register_count();
711                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
712                    program_counter,
713                    vd,
714                    group_regs,
715                )?;
716                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
717                    program_counter,
718                    vs2,
719                    group_regs,
720                )?;
721                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
722                    program_counter,
723                    vs1,
724                    group_regs,
725                )?;
726                if !vm && vd == VReg::V0 {
727                    ::core::hint::cold_path();
728                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
729                        address: PackedAddress::new(
730                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
731                        ),
732                    });
733                }
734                let sew = vtype.vsew();
735                // SAFETY: alignment checked above
736                unsafe {
737                    zvexx_muldiv_helpers::execute_arith_op(
738                        env,
739                        vd,
740                        vs2,
741                        zvexx_muldiv_helpers::OpSrc::Vreg(vs1),
742                        vm,
743                        sew,
744                        zvexx_muldiv_helpers::sdiv,
745                    );
746                }
747            }
748            Self::VdivVx {
749                vd,
750                vs2,
751                rs1: _,
752                vm,
753            } => {
754                if !env.vector_instructions_allowed() {
755                    ::core::hint::cold_path();
756                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
757                        address: PackedAddress::new(
758                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
759                        ),
760                    });
761                }
762                let Some(vtype) = env.vtype() else {
763                    ::core::hint::cold_path();
764                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
765                        address: PackedAddress::new(
766                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
767                        ),
768                    });
769                };
770                let group_regs = vtype.vlmul().register_count();
771                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
772                    program_counter,
773                    vd,
774                    group_regs,
775                )?;
776                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
777                    program_counter,
778                    vs2,
779                    group_regs,
780                )?;
781                if !vm && vd == VReg::V0 {
782                    ::core::hint::cold_path();
783                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
784                        address: PackedAddress::new(
785                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
786                        ),
787                    });
788                }
789                let sew = vtype.vsew();
790                let scalar = rs1_value.as_i64().cast_unsigned();
791                // SAFETY: alignment checked above
792                unsafe {
793                    zvexx_muldiv_helpers::execute_arith_op(
794                        env,
795                        vd,
796                        vs2,
797                        zvexx_muldiv_helpers::OpSrc::Scalar(scalar),
798                        vm,
799                        sew,
800                        zvexx_muldiv_helpers::sdiv,
801                    );
802                }
803            }
804            // vremu.vv / vremu.vx - unsigned remainder
805            Self::VremuVv { vd, vs2, vs1, vm } => {
806                if !env.vector_instructions_allowed() {
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 Some(vtype) = env.vtype() else {
815                    ::core::hint::cold_path();
816                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
817                        address: PackedAddress::new(
818                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
819                        ),
820                    });
821                };
822                let group_regs = vtype.vlmul().register_count();
823                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
824                    program_counter,
825                    vd,
826                    group_regs,
827                )?;
828                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
829                    program_counter,
830                    vs2,
831                    group_regs,
832                )?;
833                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
834                    program_counter,
835                    vs1,
836                    group_regs,
837                )?;
838                if !vm && vd == VReg::V0 {
839                    ::core::hint::cold_path();
840                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
841                        address: PackedAddress::new(
842                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
843                        ),
844                    });
845                }
846                let sew = vtype.vsew();
847                // SAFETY: alignment checked above
848                unsafe {
849                    zvexx_muldiv_helpers::execute_arith_op(
850                        env,
851                        vd,
852                        vs2,
853                        zvexx_muldiv_helpers::OpSrc::Vreg(vs1),
854                        vm,
855                        sew,
856                        // Division by zero: remainder = dividend (spec §12.11)
857                        |a, b, sew| {
858                            let mask = zvexx_muldiv_helpers::sew_mask(sew);
859                            let dividend = a & mask;
860                            let divisor = b & mask;
861                            if divisor == 0 {
862                                dividend
863                            } else {
864                                dividend % divisor
865                            }
866                        },
867                    );
868                }
869            }
870            Self::VremuVx {
871                vd,
872                vs2,
873                rs1: _,
874                vm,
875            } => {
876                if !env.vector_instructions_allowed() {
877                    ::core::hint::cold_path();
878                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
879                        address: PackedAddress::new(
880                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
881                        ),
882                    });
883                }
884                let Some(vtype) = env.vtype() else {
885                    ::core::hint::cold_path();
886                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
887                        address: PackedAddress::new(
888                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
889                        ),
890                    });
891                };
892                let group_regs = vtype.vlmul().register_count();
893                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
894                    program_counter,
895                    vd,
896                    group_regs,
897                )?;
898                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
899                    program_counter,
900                    vs2,
901                    group_regs,
902                )?;
903                if !vm && vd == VReg::V0 {
904                    ::core::hint::cold_path();
905                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
906                        address: PackedAddress::new(
907                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
908                        ),
909                    });
910                }
911                let sew = vtype.vsew();
912                let scalar = rs1_value.as_i64().cast_unsigned();
913                // SAFETY: alignment checked above
914                unsafe {
915                    zvexx_muldiv_helpers::execute_arith_op(
916                        env,
917                        vd,
918                        vs2,
919                        zvexx_muldiv_helpers::OpSrc::Scalar(scalar),
920                        vm,
921                        sew,
922                        |a, b, sew| {
923                            let mask = zvexx_muldiv_helpers::sew_mask(sew);
924                            let dividend = a & mask;
925                            let divisor = b & mask;
926                            if divisor == 0 {
927                                dividend
928                            } else {
929                                dividend % divisor
930                            }
931                        },
932                    );
933                }
934            }
935            // vrem.vv / vrem.vx - signed remainder
936            Self::VremVv { vd, vs2, vs1, vm } => {
937                if !env.vector_instructions_allowed() {
938                    ::core::hint::cold_path();
939                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
940                        address: PackedAddress::new(
941                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
942                        ),
943                    });
944                }
945                let Some(vtype) = env.vtype() else {
946                    ::core::hint::cold_path();
947                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
948                        address: PackedAddress::new(
949                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
950                        ),
951                    });
952                };
953                let group_regs = vtype.vlmul().register_count();
954                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
955                    program_counter,
956                    vd,
957                    group_regs,
958                )?;
959                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
960                    program_counter,
961                    vs2,
962                    group_regs,
963                )?;
964                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
965                    program_counter,
966                    vs1,
967                    group_regs,
968                )?;
969                if !vm && vd == VReg::V0 {
970                    ::core::hint::cold_path();
971                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
972                        address: PackedAddress::new(
973                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
974                        ),
975                    });
976                }
977                let sew = vtype.vsew();
978                // SAFETY: alignment checked above
979                unsafe {
980                    zvexx_muldiv_helpers::execute_arith_op(
981                        env,
982                        vd,
983                        vs2,
984                        zvexx_muldiv_helpers::OpSrc::Vreg(vs1),
985                        vm,
986                        sew,
987                        zvexx_muldiv_helpers::srem,
988                    );
989                }
990            }
991            Self::VremVx {
992                vd,
993                vs2,
994                rs1: _,
995                vm,
996            } => {
997                if !env.vector_instructions_allowed() {
998                    ::core::hint::cold_path();
999                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1000                        address: PackedAddress::new(
1001                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1002                        ),
1003                    });
1004                }
1005                let Some(vtype) = env.vtype() else {
1006                    ::core::hint::cold_path();
1007                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1008                        address: PackedAddress::new(
1009                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1010                        ),
1011                    });
1012                };
1013                let group_regs = vtype.vlmul().register_count();
1014                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1015                    program_counter,
1016                    vd,
1017                    group_regs,
1018                )?;
1019                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1020                    program_counter,
1021                    vs2,
1022                    group_regs,
1023                )?;
1024                if !vm && vd == VReg::V0 {
1025                    ::core::hint::cold_path();
1026                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1027                        address: PackedAddress::new(
1028                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1029                        ),
1030                    });
1031                }
1032                let sew = vtype.vsew();
1033                let scalar = rs1_value.as_i64().cast_unsigned();
1034                // SAFETY: alignment checked above
1035                unsafe {
1036                    zvexx_muldiv_helpers::execute_arith_op(
1037                        env,
1038                        vd,
1039                        vs2,
1040                        zvexx_muldiv_helpers::OpSrc::Scalar(scalar),
1041                        vm,
1042                        sew,
1043                        zvexx_muldiv_helpers::srem,
1044                    );
1045                }
1046            }
1047            // vwmulu.vv / vwmulu.vx - unsigned widening multiply
1048            Self::VwmuluVv { vd, vs2, vs1, vm } => {
1049                if !env.vector_instructions_allowed() {
1050                    ::core::hint::cold_path();
1051                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1052                        address: PackedAddress::new(
1053                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1054                        ),
1055                    });
1056                }
1057                let Some(vtype) = env.vtype() else {
1058                    ::core::hint::cold_path();
1059                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1060                        address: PackedAddress::new(
1061                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1062                        ),
1063                    });
1064                };
1065                // Widening produces a 2*SEW result; an EEW above ELEN is reserved for every
1066                // implementation, so this is not a Zve64x-specific restriction
1067                if !zvexx_muldiv_helpers::widening_eew_supported(vtype.vsew(), Env::ELEN) {
1068                    ::core::hint::cold_path();
1069                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1070                        address: PackedAddress::new(
1071                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1072                        ),
1073                    });
1074                }
1075                let group_regs = vtype.vlmul().register_count();
1076                // dest_group_regs encodes EMUL=2*LMUL; None means EMUL>8, which is illegal
1077                let dest_group_regs = zvexx_muldiv_helpers::widening_dest_register_count(
1078                    vtype.vlmul(),
1079                )
1080                .ok_or(ExecutionError::IllegalInstruction {
1081                    address: PackedAddress::new(
1082                        program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1083                    ),
1084                })?;
1085                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1086                    program_counter,
1087                    vd,
1088                    dest_group_regs,
1089                )?;
1090                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1091                    program_counter,
1092                    vs2,
1093                    group_regs,
1094                )?;
1095                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1096                    program_counter,
1097                    vs1,
1098                    group_regs,
1099                )?;
1100                if !vm && vd == VReg::V0 {
1101                    ::core::hint::cold_path();
1102                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1103                        address: PackedAddress::new(
1104                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1105                        ),
1106                    });
1107                }
1108                // vd and vs2/vs1 must not overlap
1109                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
1110                    program_counter,
1111                    vd,
1112                    vs2,
1113                    dest_group_regs,
1114                    group_regs,
1115                )?;
1116                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
1117                    program_counter,
1118                    vd,
1119                    vs1,
1120                    dest_group_regs,
1121                    group_regs,
1122                )?;
1123                let sew = vtype.vsew();
1124                // SAFETY: alignment and overlap checked above; 2*SEW <= ELEN checked above
1125                unsafe {
1126                    zvexx_muldiv_helpers::execute_widening_op(
1127                        env,
1128                        vd,
1129                        vs2,
1130                        zvexx_muldiv_helpers::OpSrc::Vreg(vs1),
1131                        vm,
1132                        sew,
1133                        |a, b, sew| {
1134                            let mask = zvexx_muldiv_helpers::sew_mask(sew);
1135                            (a & mask).wrapping_mul(b & mask)
1136                        },
1137                    );
1138                }
1139            }
1140            Self::VwmuluVx {
1141                vd,
1142                vs2,
1143                rs1: _,
1144                vm,
1145            } => {
1146                if !env.vector_instructions_allowed() {
1147                    ::core::hint::cold_path();
1148                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1149                        address: PackedAddress::new(
1150                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1151                        ),
1152                    });
1153                }
1154                let Some(vtype) = env.vtype() else {
1155                    ::core::hint::cold_path();
1156                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1157                        address: PackedAddress::new(
1158                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1159                        ),
1160                    });
1161                };
1162                // Widening produces a 2*SEW result; an EEW above ELEN is reserved for every
1163                // implementation, so this is not a Zve64x-specific restriction
1164                if !zvexx_muldiv_helpers::widening_eew_supported(vtype.vsew(), Env::ELEN) {
1165                    ::core::hint::cold_path();
1166                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1167                        address: PackedAddress::new(
1168                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1169                        ),
1170                    });
1171                }
1172                let group_regs = vtype.vlmul().register_count();
1173                let dest_group_regs = zvexx_muldiv_helpers::widening_dest_register_count(
1174                    vtype.vlmul(),
1175                )
1176                .ok_or(ExecutionError::IllegalInstruction {
1177                    address: PackedAddress::new(
1178                        program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1179                    ),
1180                })?;
1181                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1182                    program_counter,
1183                    vd,
1184                    dest_group_regs,
1185                )?;
1186                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1187                    program_counter,
1188                    vs2,
1189                    group_regs,
1190                )?;
1191                if !vm && vd == VReg::V0 {
1192                    ::core::hint::cold_path();
1193                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1194                        address: PackedAddress::new(
1195                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1196                        ),
1197                    });
1198                }
1199                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
1200                    program_counter,
1201                    vd,
1202                    vs2,
1203                    dest_group_regs,
1204                    group_regs,
1205                )?;
1206                let sew = vtype.vsew();
1207                let scalar = rs1_value.as_u64();
1208                // SAFETY: alignment and overlap checked above; 2*SEW <= ELEN checked above
1209                unsafe {
1210                    zvexx_muldiv_helpers::execute_widening_op(
1211                        env,
1212                        vd,
1213                        vs2,
1214                        zvexx_muldiv_helpers::OpSrc::Scalar(scalar),
1215                        vm,
1216                        sew,
1217                        |a, b, sew| {
1218                            let mask = zvexx_muldiv_helpers::sew_mask(sew);
1219                            (a & mask).wrapping_mul(b & mask)
1220                        },
1221                    );
1222                }
1223            }
1224            // vwmulsu.vv / vwmulsu.vx - signed×unsigned widening multiply
1225            Self::VwmulsuVv { vd, vs2, vs1, vm } => {
1226                if !env.vector_instructions_allowed() {
1227                    ::core::hint::cold_path();
1228                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1229                        address: PackedAddress::new(
1230                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1231                        ),
1232                    });
1233                }
1234                let Some(vtype) = env.vtype() else {
1235                    ::core::hint::cold_path();
1236                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1237                        address: PackedAddress::new(
1238                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1239                        ),
1240                    });
1241                };
1242                // Widening produces a 2*SEW result; an EEW above ELEN is reserved for every
1243                // implementation, so this is not a Zve64x-specific restriction
1244                if !zvexx_muldiv_helpers::widening_eew_supported(vtype.vsew(), Env::ELEN) {
1245                    ::core::hint::cold_path();
1246                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1247                        address: PackedAddress::new(
1248                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1249                        ),
1250                    });
1251                }
1252                let group_regs = vtype.vlmul().register_count();
1253                let dest_group_regs = zvexx_muldiv_helpers::widening_dest_register_count(
1254                    vtype.vlmul(),
1255                )
1256                .ok_or(ExecutionError::IllegalInstruction {
1257                    address: PackedAddress::new(
1258                        program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1259                    ),
1260                })?;
1261                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1262                    program_counter,
1263                    vd,
1264                    dest_group_regs,
1265                )?;
1266                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1267                    program_counter,
1268                    vs2,
1269                    group_regs,
1270                )?;
1271                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1272                    program_counter,
1273                    vs1,
1274                    group_regs,
1275                )?;
1276                if !vm && vd == VReg::V0 {
1277                    ::core::hint::cold_path();
1278                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1279                        address: PackedAddress::new(
1280                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1281                        ),
1282                    });
1283                }
1284                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
1285                    program_counter,
1286                    vd,
1287                    vs2,
1288                    dest_group_regs,
1289                    group_regs,
1290                )?;
1291                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
1292                    program_counter,
1293                    vd,
1294                    vs1,
1295                    dest_group_regs,
1296                    group_regs,
1297                )?;
1298                let sew = vtype.vsew();
1299                // SAFETY: alignment and overlap checked above; 2*SEW <= ELEN checked above
1300                unsafe {
1301                    zvexx_muldiv_helpers::execute_widening_op(
1302                        env,
1303                        vd,
1304                        vs2,
1305                        zvexx_muldiv_helpers::OpSrc::Vreg(vs1),
1306                        vm,
1307                        sew,
1308                        // vs2 is signed, vs1 is unsigned; widen both to full u64 before multiply
1309                        |a, b, sew| {
1310                            let sa = zvexx_muldiv_helpers::sign_extend(a, sew);
1311                            let ub = b & zvexx_muldiv_helpers::sew_mask(sew);
1312                            sa.cast_unsigned().wrapping_mul(ub)
1313                        },
1314                    );
1315                }
1316            }
1317            Self::VwmulsuVx {
1318                vd,
1319                vs2,
1320                rs1: _,
1321                vm,
1322            } => {
1323                if !env.vector_instructions_allowed() {
1324                    ::core::hint::cold_path();
1325                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1326                        address: PackedAddress::new(
1327                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1328                        ),
1329                    });
1330                }
1331                let Some(vtype) = env.vtype() else {
1332                    ::core::hint::cold_path();
1333                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1334                        address: PackedAddress::new(
1335                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1336                        ),
1337                    });
1338                };
1339                // Widening produces a 2*SEW result; an EEW above ELEN is reserved for every
1340                // implementation, so this is not a Zve64x-specific restriction
1341                if !zvexx_muldiv_helpers::widening_eew_supported(vtype.vsew(), Env::ELEN) {
1342                    ::core::hint::cold_path();
1343                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1344                        address: PackedAddress::new(
1345                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1346                        ),
1347                    });
1348                }
1349                let group_regs = vtype.vlmul().register_count();
1350                let dest_group_regs = zvexx_muldiv_helpers::widening_dest_register_count(
1351                    vtype.vlmul(),
1352                )
1353                .ok_or(ExecutionError::IllegalInstruction {
1354                    address: PackedAddress::new(
1355                        program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1356                    ),
1357                })?;
1358                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1359                    program_counter,
1360                    vd,
1361                    dest_group_regs,
1362                )?;
1363                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1364                    program_counter,
1365                    vs2,
1366                    group_regs,
1367                )?;
1368                if !vm && vd == VReg::V0 {
1369                    ::core::hint::cold_path();
1370                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1371                        address: PackedAddress::new(
1372                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1373                        ),
1374                    });
1375                }
1376                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
1377                    program_counter,
1378                    vd,
1379                    vs2,
1380                    dest_group_regs,
1381                    group_regs,
1382                )?;
1383                let sew = vtype.vsew();
1384                // scalar from rs1 is the unsigned operand; vs2 elements are signed
1385                let scalar = rs1_value.as_u64();
1386                // SAFETY: alignment and overlap checked above; 2*SEW <= ELEN checked above
1387                unsafe {
1388                    zvexx_muldiv_helpers::execute_widening_op(
1389                        env,
1390                        vd,
1391                        vs2,
1392                        zvexx_muldiv_helpers::OpSrc::Scalar(scalar),
1393                        vm,
1394                        sew,
1395                        |a, b, sew| {
1396                            let sa = zvexx_muldiv_helpers::sign_extend(a, sew);
1397                            let ub = b & zvexx_muldiv_helpers::sew_mask(sew);
1398                            sa.cast_unsigned().wrapping_mul(ub)
1399                        },
1400                    );
1401                }
1402            }
1403            // vwmul.vv / vwmul.vx - signed widening multiply
1404            Self::VwmulVv { vd, vs2, vs1, vm } => {
1405                if !env.vector_instructions_allowed() {
1406                    ::core::hint::cold_path();
1407                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1408                        address: PackedAddress::new(
1409                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1410                        ),
1411                    });
1412                }
1413                let Some(vtype) = env.vtype() else {
1414                    ::core::hint::cold_path();
1415                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1416                        address: PackedAddress::new(
1417                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1418                        ),
1419                    });
1420                };
1421                // Widening produces a 2*SEW result; an EEW above ELEN is reserved for every
1422                // implementation, so this is not a Zve64x-specific restriction
1423                if !zvexx_muldiv_helpers::widening_eew_supported(vtype.vsew(), Env::ELEN) {
1424                    ::core::hint::cold_path();
1425                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1426                        address: PackedAddress::new(
1427                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1428                        ),
1429                    });
1430                }
1431                let group_regs = vtype.vlmul().register_count();
1432                let dest_group_regs = zvexx_muldiv_helpers::widening_dest_register_count(
1433                    vtype.vlmul(),
1434                )
1435                .ok_or(ExecutionError::IllegalInstruction {
1436                    address: PackedAddress::new(
1437                        program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1438                    ),
1439                })?;
1440                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1441                    program_counter,
1442                    vd,
1443                    dest_group_regs,
1444                )?;
1445                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1446                    program_counter,
1447                    vs2,
1448                    group_regs,
1449                )?;
1450                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1451                    program_counter,
1452                    vs1,
1453                    group_regs,
1454                )?;
1455                if !vm && vd == VReg::V0 {
1456                    ::core::hint::cold_path();
1457                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1458                        address: PackedAddress::new(
1459                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1460                        ),
1461                    });
1462                }
1463                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
1464                    program_counter,
1465                    vd,
1466                    vs2,
1467                    dest_group_regs,
1468                    group_regs,
1469                )?;
1470                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
1471                    program_counter,
1472                    vd,
1473                    vs1,
1474                    dest_group_regs,
1475                    group_regs,
1476                )?;
1477                let sew = vtype.vsew();
1478                // SAFETY: alignment and overlap checked above; 2*SEW <= ELEN checked above
1479                unsafe {
1480                    zvexx_muldiv_helpers::execute_widening_op(
1481                        env,
1482                        vd,
1483                        vs2,
1484                        zvexx_muldiv_helpers::OpSrc::Vreg(vs1),
1485                        vm,
1486                        sew,
1487                        // Both operands sign-extended; full 2*SEW product fits in u64
1488                        |a, b, sew| {
1489                            let sa = zvexx_muldiv_helpers::sign_extend(a, sew);
1490                            let sb = zvexx_muldiv_helpers::sign_extend(b, sew);
1491                            sa.cast_unsigned().wrapping_mul(sb.cast_unsigned())
1492                        },
1493                    );
1494                }
1495            }
1496            Self::VwmulVx {
1497                vd,
1498                vs2,
1499                rs1: _,
1500                vm,
1501            } => {
1502                if !env.vector_instructions_allowed() {
1503                    ::core::hint::cold_path();
1504                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1505                        address: PackedAddress::new(
1506                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1507                        ),
1508                    });
1509                }
1510                let Some(vtype) = env.vtype() else {
1511                    ::core::hint::cold_path();
1512                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1513                        address: PackedAddress::new(
1514                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1515                        ),
1516                    });
1517                };
1518                // Widening produces a 2*SEW result; an EEW above ELEN is reserved for every
1519                // implementation, so this is not a Zve64x-specific restriction
1520                if !zvexx_muldiv_helpers::widening_eew_supported(vtype.vsew(), Env::ELEN) {
1521                    ::core::hint::cold_path();
1522                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1523                        address: PackedAddress::new(
1524                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1525                        ),
1526                    });
1527                }
1528                let group_regs = vtype.vlmul().register_count();
1529                let dest_group_regs = zvexx_muldiv_helpers::widening_dest_register_count(
1530                    vtype.vlmul(),
1531                )
1532                .ok_or(ExecutionError::IllegalInstruction {
1533                    address: PackedAddress::new(
1534                        program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1535                    ),
1536                })?;
1537                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1538                    program_counter,
1539                    vd,
1540                    dest_group_regs,
1541                )?;
1542                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1543                    program_counter,
1544                    vs2,
1545                    group_regs,
1546                )?;
1547                if !vm && vd == VReg::V0 {
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                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
1556                    program_counter,
1557                    vd,
1558                    vs2,
1559                    dest_group_regs,
1560                    group_regs,
1561                )?;
1562                let sew = vtype.vsew();
1563                // scalar from rs1 is sign-extended to XLEN; treat as signed SEW-wide
1564                let scalar = rs1_value.as_u64();
1565                // SAFETY: alignment and overlap checked above; 2*SEW <= ELEN checked above
1566                unsafe {
1567                    zvexx_muldiv_helpers::execute_widening_op(
1568                        env,
1569                        vd,
1570                        vs2,
1571                        zvexx_muldiv_helpers::OpSrc::Scalar(scalar),
1572                        vm,
1573                        sew,
1574                        |a, b, sew| {
1575                            let sa = zvexx_muldiv_helpers::sign_extend(a, sew);
1576                            let sb = zvexx_muldiv_helpers::sign_extend(b, sew);
1577                            sa.cast_unsigned().wrapping_mul(sb.cast_unsigned())
1578                        },
1579                    );
1580                }
1581            }
1582            // vmacc.vv / vmacc.vx - vd = vd + vs1 * vs2
1583            Self::VmaccVv { vd, vs1, vs2, vm } => {
1584                if !env.vector_instructions_allowed() {
1585                    ::core::hint::cold_path();
1586                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1587                        address: PackedAddress::new(
1588                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1589                        ),
1590                    });
1591                }
1592                let Some(vtype) = env.vtype() else {
1593                    ::core::hint::cold_path();
1594                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1595                        address: PackedAddress::new(
1596                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1597                        ),
1598                    });
1599                };
1600                let group_regs = vtype.vlmul().register_count();
1601                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1602                    program_counter,
1603                    vd,
1604                    group_regs,
1605                )?;
1606                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1607                    program_counter,
1608                    vs2,
1609                    group_regs,
1610                )?;
1611                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1612                    program_counter,
1613                    vs1,
1614                    group_regs,
1615                )?;
1616                if !vm && vd == VReg::V0 {
1617                    ::core::hint::cold_path();
1618                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1619                        address: PackedAddress::new(
1620                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1621                        ),
1622                    });
1623                }
1624                let sew = vtype.vsew();
1625                // SAFETY: alignment checked above
1626                unsafe {
1627                    zvexx_muldiv_helpers::execute_muladd_op(
1628                        env,
1629                        vd,
1630                        vs1,
1631                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
1632                        vm,
1633                        sew,
1634                        // vmacc: vd[i] = vd[i] + vs1[i] * vs2[i]
1635                        |acc, a, b, _| acc.wrapping_add(a.wrapping_mul(b)),
1636                    );
1637                }
1638            }
1639            Self::VmaccVx {
1640                vd,
1641                rs1: _,
1642                vs2,
1643                vm,
1644            } => {
1645                if !env.vector_instructions_allowed() {
1646                    ::core::hint::cold_path();
1647                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1648                        address: PackedAddress::new(
1649                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1650                        ),
1651                    });
1652                }
1653                let Some(vtype) = env.vtype() else {
1654                    ::core::hint::cold_path();
1655                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1656                        address: PackedAddress::new(
1657                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1658                        ),
1659                    });
1660                };
1661                let group_regs = vtype.vlmul().register_count();
1662                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1663                    program_counter,
1664                    vd,
1665                    group_regs,
1666                )?;
1667                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1668                    program_counter,
1669                    vs2,
1670                    group_regs,
1671                )?;
1672                if !vm && vd == VReg::V0 {
1673                    ::core::hint::cold_path();
1674                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1675                        address: PackedAddress::new(
1676                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1677                        ),
1678                    });
1679                }
1680                let sew = vtype.vsew();
1681                let scalar = rs1_value.as_i64().cast_unsigned();
1682                // SAFETY: alignment checked above
1683                unsafe {
1684                    zvexx_muldiv_helpers::execute_muladd_scalar_op(
1685                        env,
1686                        vd,
1687                        scalar,
1688                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
1689                        vm,
1690                        sew,
1691                        |acc, a, b, _| acc.wrapping_add(a.wrapping_mul(b)),
1692                    );
1693                }
1694            }
1695            // vnmsac.vv / vnmsac.vx - vd = vd - vs1 * vs2
1696            Self::VnmsacVv { vd, vs1, vs2, vm } => {
1697                if !env.vector_instructions_allowed() {
1698                    ::core::hint::cold_path();
1699                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1700                        address: PackedAddress::new(
1701                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1702                        ),
1703                    });
1704                }
1705                let Some(vtype) = env.vtype() else {
1706                    ::core::hint::cold_path();
1707                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1708                        address: PackedAddress::new(
1709                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1710                        ),
1711                    });
1712                };
1713                let group_regs = vtype.vlmul().register_count();
1714                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1715                    program_counter,
1716                    vd,
1717                    group_regs,
1718                )?;
1719                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1720                    program_counter,
1721                    vs2,
1722                    group_regs,
1723                )?;
1724                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1725                    program_counter,
1726                    vs1,
1727                    group_regs,
1728                )?;
1729                if !vm && vd == VReg::V0 {
1730                    ::core::hint::cold_path();
1731                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1732                        address: PackedAddress::new(
1733                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1734                        ),
1735                    });
1736                }
1737                let sew = vtype.vsew();
1738                // SAFETY: alignment checked above
1739                unsafe {
1740                    zvexx_muldiv_helpers::execute_muladd_op(
1741                        env,
1742                        vd,
1743                        vs1,
1744                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
1745                        vm,
1746                        sew,
1747                        // vnmsac: vd[i] = vd[i] - vs1[i] * vs2[i]
1748                        |acc, a, b, _| acc.wrapping_sub(a.wrapping_mul(b)),
1749                    );
1750                }
1751            }
1752            Self::VnmsacVx {
1753                vd,
1754                rs1: _,
1755                vs2,
1756                vm,
1757            } => {
1758                if !env.vector_instructions_allowed() {
1759                    ::core::hint::cold_path();
1760                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1761                        address: PackedAddress::new(
1762                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1763                        ),
1764                    });
1765                }
1766                let Some(vtype) = env.vtype() else {
1767                    ::core::hint::cold_path();
1768                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1769                        address: PackedAddress::new(
1770                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1771                        ),
1772                    });
1773                };
1774                let group_regs = vtype.vlmul().register_count();
1775                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1776                    program_counter,
1777                    vd,
1778                    group_regs,
1779                )?;
1780                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1781                    program_counter,
1782                    vs2,
1783                    group_regs,
1784                )?;
1785                if !vm && vd == VReg::V0 {
1786                    ::core::hint::cold_path();
1787                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1788                        address: PackedAddress::new(
1789                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1790                        ),
1791                    });
1792                }
1793                let sew = vtype.vsew();
1794                let scalar = rs1_value.as_i64().cast_unsigned();
1795                // SAFETY: alignment checked above
1796                unsafe {
1797                    zvexx_muldiv_helpers::execute_muladd_scalar_op(
1798                        env,
1799                        vd,
1800                        scalar,
1801                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
1802                        vm,
1803                        sew,
1804                        |acc, a, b, _| acc.wrapping_sub(a.wrapping_mul(b)),
1805                    );
1806                }
1807            }
1808            // vmadd.vv / vmadd.vx - vd = vs1 * vd + vs2
1809            Self::VmaddVv { vd, vs1, vs2, vm } => {
1810                if !env.vector_instructions_allowed() {
1811                    ::core::hint::cold_path();
1812                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1813                        address: PackedAddress::new(
1814                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1815                        ),
1816                    });
1817                }
1818                let Some(vtype) = env.vtype() else {
1819                    ::core::hint::cold_path();
1820                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1821                        address: PackedAddress::new(
1822                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1823                        ),
1824                    });
1825                };
1826                let group_regs = vtype.vlmul().register_count();
1827                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1828                    program_counter,
1829                    vd,
1830                    group_regs,
1831                )?;
1832                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1833                    program_counter,
1834                    vs2,
1835                    group_regs,
1836                )?;
1837                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1838                    program_counter,
1839                    vs1,
1840                    group_regs,
1841                )?;
1842                if !vm && vd == VReg::V0 {
1843                    ::core::hint::cold_path();
1844                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1845                        address: PackedAddress::new(
1846                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1847                        ),
1848                    });
1849                }
1850                let sew = vtype.vsew();
1851                // SAFETY: alignment checked above
1852                unsafe {
1853                    zvexx_muldiv_helpers::execute_muladd_op(
1854                        env,
1855                        vd,
1856                        vs1,
1857                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
1858                        vm,
1859                        sew,
1860                        // vmadd: vd[i] = vs1[i] * vd[i] + vs2[i]; acc=vd, a=vs1, b=vs2
1861                        |acc, a, b, _| a.wrapping_mul(acc).wrapping_add(b),
1862                    );
1863                }
1864            }
1865            Self::VmaddVx {
1866                vd,
1867                rs1: _,
1868                vs2,
1869                vm,
1870            } => {
1871                if !env.vector_instructions_allowed() {
1872                    ::core::hint::cold_path();
1873                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1874                        address: PackedAddress::new(
1875                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1876                        ),
1877                    });
1878                }
1879                let Some(vtype) = env.vtype() else {
1880                    ::core::hint::cold_path();
1881                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1882                        address: PackedAddress::new(
1883                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1884                        ),
1885                    });
1886                };
1887                let group_regs = vtype.vlmul().register_count();
1888                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1889                    program_counter,
1890                    vd,
1891                    group_regs,
1892                )?;
1893                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1894                    program_counter,
1895                    vs2,
1896                    group_regs,
1897                )?;
1898                if !vm && vd == VReg::V0 {
1899                    ::core::hint::cold_path();
1900                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1901                        address: PackedAddress::new(
1902                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1903                        ),
1904                    });
1905                }
1906                let sew = vtype.vsew();
1907                let scalar = rs1_value.as_i64().cast_unsigned();
1908                // SAFETY: alignment checked above
1909                unsafe {
1910                    zvexx_muldiv_helpers::execute_muladd_scalar_op(
1911                        env,
1912                        vd,
1913                        scalar,
1914                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
1915                        vm,
1916                        sew,
1917                        // vmadd: vd[i] = rs1 * vd[i] + vs2[i]
1918                        |acc, a, b, _| a.wrapping_mul(acc).wrapping_add(b),
1919                    );
1920                }
1921            }
1922            // vnmsub.vv / vnmsub.vx - vd = -(vs1 * vd) + vs2
1923            Self::VnmsubVv { vd, vs1, vs2, vm } => {
1924                if !env.vector_instructions_allowed() {
1925                    ::core::hint::cold_path();
1926                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1927                        address: PackedAddress::new(
1928                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1929                        ),
1930                    });
1931                }
1932                let Some(vtype) = env.vtype() else {
1933                    ::core::hint::cold_path();
1934                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1935                        address: PackedAddress::new(
1936                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1937                        ),
1938                    });
1939                };
1940                let group_regs = vtype.vlmul().register_count();
1941                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1942                    program_counter,
1943                    vd,
1944                    group_regs,
1945                )?;
1946                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1947                    program_counter,
1948                    vs2,
1949                    group_regs,
1950                )?;
1951                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
1952                    program_counter,
1953                    vs1,
1954                    group_regs,
1955                )?;
1956                if !vm && vd == VReg::V0 {
1957                    ::core::hint::cold_path();
1958                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1959                        address: PackedAddress::new(
1960                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1961                        ),
1962                    });
1963                }
1964                let sew = vtype.vsew();
1965                // SAFETY: alignment checked above
1966                unsafe {
1967                    zvexx_muldiv_helpers::execute_muladd_op(
1968                        env,
1969                        vd,
1970                        vs1,
1971                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
1972                        vm,
1973                        sew,
1974                        // vnmsub: vd[i] = -(vs1[i] * vd[i]) + vs2[i]; acc=vd, a=vs1, b=vs2
1975                        |acc, a, b, _| b.wrapping_sub(a.wrapping_mul(acc)),
1976                    );
1977                }
1978            }
1979            Self::VnmsubVx {
1980                vd,
1981                rs1: _,
1982                vs2,
1983                vm,
1984            } => {
1985                if !env.vector_instructions_allowed() {
1986                    ::core::hint::cold_path();
1987                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1988                        address: PackedAddress::new(
1989                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1990                        ),
1991                    });
1992                }
1993                let Some(vtype) = env.vtype() else {
1994                    ::core::hint::cold_path();
1995                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
1996                        address: PackedAddress::new(
1997                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
1998                        ),
1999                    });
2000                };
2001                let group_regs = vtype.vlmul().register_count();
2002                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2003                    program_counter,
2004                    vd,
2005                    group_regs,
2006                )?;
2007                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2008                    program_counter,
2009                    vs2,
2010                    group_regs,
2011                )?;
2012                if !vm && vd == VReg::V0 {
2013                    ::core::hint::cold_path();
2014                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2015                        address: PackedAddress::new(
2016                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2017                        ),
2018                    });
2019                }
2020                let sew = vtype.vsew();
2021                let scalar = rs1_value.as_i64().cast_unsigned();
2022                // SAFETY: alignment checked above
2023                unsafe {
2024                    zvexx_muldiv_helpers::execute_muladd_scalar_op(
2025                        env,
2026                        vd,
2027                        scalar,
2028                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
2029                        vm,
2030                        sew,
2031                        // vnmsub: vd[i] = -(rs1 * vd[i]) + vs2[i]
2032                        |acc, a, b, _| b.wrapping_sub(a.wrapping_mul(acc)),
2033                    );
2034                }
2035            }
2036            // vwmaccu.vv / vwmaccu.vx - unsigned widening multiply-add
2037            Self::VwmaccuVv { vd, vs1, vs2, vm } => {
2038                if !env.vector_instructions_allowed() {
2039                    ::core::hint::cold_path();
2040                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2041                        address: PackedAddress::new(
2042                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2043                        ),
2044                    });
2045                }
2046                let Some(vtype) = env.vtype() else {
2047                    ::core::hint::cold_path();
2048                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2049                        address: PackedAddress::new(
2050                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2051                        ),
2052                    });
2053                };
2054                // Widening produces a 2*SEW result; an EEW above ELEN is reserved for every
2055                // implementation, so this is not a Zve64x-specific restriction
2056                if !zvexx_muldiv_helpers::widening_eew_supported(vtype.vsew(), Env::ELEN) {
2057                    ::core::hint::cold_path();
2058                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2059                        address: PackedAddress::new(
2060                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2061                        ),
2062                    });
2063                }
2064                let group_regs = vtype.vlmul().register_count();
2065                let dest_group_regs = zvexx_muldiv_helpers::widening_dest_register_count(
2066                    vtype.vlmul(),
2067                )
2068                .ok_or(ExecutionError::IllegalInstruction {
2069                    address: PackedAddress::new(
2070                        program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2071                    ),
2072                })?;
2073                // vd holds the 2*SEW accumulator
2074                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2075                    program_counter,
2076                    vd,
2077                    dest_group_regs,
2078                )?;
2079                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2080                    program_counter,
2081                    vs2,
2082                    group_regs,
2083                )?;
2084                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2085                    program_counter,
2086                    vs1,
2087                    group_regs,
2088                )?;
2089                if !vm && vd == VReg::V0 {
2090                    ::core::hint::cold_path();
2091                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2092                        address: PackedAddress::new(
2093                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2094                        ),
2095                    });
2096                }
2097                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
2098                    program_counter,
2099                    vd,
2100                    vs2,
2101                    dest_group_regs,
2102                    group_regs,
2103                )?;
2104                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
2105                    program_counter,
2106                    vd,
2107                    vs1,
2108                    dest_group_regs,
2109                    group_regs,
2110                )?;
2111                let sew = vtype.vsew();
2112                // SAFETY: alignment and overlap checked above; 2*SEW <= ELEN checked above
2113                unsafe {
2114                    zvexx_muldiv_helpers::execute_widening_muladd_op(
2115                        env,
2116                        vd,
2117                        vs1,
2118                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
2119                        vm,
2120                        sew,
2121                        // vwmaccu: vd[i] = vd[i] + zext(vs1[i]) * zext(vs2[i])
2122                        |acc, a, b, sew| {
2123                            let mask = zvexx_muldiv_helpers::sew_mask(sew);
2124                            acc.wrapping_add((a & mask).wrapping_mul(b & mask))
2125                        },
2126                    );
2127                }
2128            }
2129            Self::VwmaccuVx {
2130                vd,
2131                rs1: _,
2132                vs2,
2133                vm,
2134            } => {
2135                if !env.vector_instructions_allowed() {
2136                    ::core::hint::cold_path();
2137                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2138                        address: PackedAddress::new(
2139                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2140                        ),
2141                    });
2142                }
2143                let Some(vtype) = env.vtype() else {
2144                    ::core::hint::cold_path();
2145                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2146                        address: PackedAddress::new(
2147                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2148                        ),
2149                    });
2150                };
2151                // Widening produces a 2*SEW result; an EEW above ELEN is reserved for every
2152                // implementation, so this is not a Zve64x-specific restriction
2153                if !zvexx_muldiv_helpers::widening_eew_supported(vtype.vsew(), Env::ELEN) {
2154                    ::core::hint::cold_path();
2155                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2156                        address: PackedAddress::new(
2157                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2158                        ),
2159                    });
2160                }
2161                let group_regs = vtype.vlmul().register_count();
2162                let dest_group_regs = zvexx_muldiv_helpers::widening_dest_register_count(
2163                    vtype.vlmul(),
2164                )
2165                .ok_or(ExecutionError::IllegalInstruction {
2166                    address: PackedAddress::new(
2167                        program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2168                    ),
2169                })?;
2170                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2171                    program_counter,
2172                    vd,
2173                    dest_group_regs,
2174                )?;
2175                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2176                    program_counter,
2177                    vs2,
2178                    group_regs,
2179                )?;
2180                if !vm && vd == VReg::V0 {
2181                    ::core::hint::cold_path();
2182                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2183                        address: PackedAddress::new(
2184                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2185                        ),
2186                    });
2187                }
2188                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
2189                    program_counter,
2190                    vd,
2191                    vs2,
2192                    dest_group_regs,
2193                    group_regs,
2194                )?;
2195                let sew = vtype.vsew();
2196                let scalar = rs1_value.as_u64();
2197                // SAFETY: alignment and overlap checked above; 2*SEW <= ELEN checked above
2198                unsafe {
2199                    zvexx_muldiv_helpers::execute_widening_muladd_scalar_op(
2200                        env,
2201                        vd,
2202                        scalar,
2203                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
2204                        vm,
2205                        sew,
2206                        |acc, a, b, sew| {
2207                            let mask = zvexx_muldiv_helpers::sew_mask(sew);
2208                            acc.wrapping_add((a & mask).wrapping_mul(b & mask))
2209                        },
2210                    );
2211                }
2212            }
2213            // vwmacc.vv / vwmacc.vx - signed widening multiply-add
2214            Self::VwmaccVv { vd, vs1, vs2, vm } => {
2215                if !env.vector_instructions_allowed() {
2216                    ::core::hint::cold_path();
2217                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2218                        address: PackedAddress::new(
2219                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2220                        ),
2221                    });
2222                }
2223                let Some(vtype) = env.vtype() else {
2224                    ::core::hint::cold_path();
2225                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2226                        address: PackedAddress::new(
2227                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2228                        ),
2229                    });
2230                };
2231                // Widening produces a 2*SEW result; an EEW above ELEN is reserved for every
2232                // implementation, so this is not a Zve64x-specific restriction
2233                if !zvexx_muldiv_helpers::widening_eew_supported(vtype.vsew(), Env::ELEN) {
2234                    ::core::hint::cold_path();
2235                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2236                        address: PackedAddress::new(
2237                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2238                        ),
2239                    });
2240                }
2241                let group_regs = vtype.vlmul().register_count();
2242                let dest_group_regs = zvexx_muldiv_helpers::widening_dest_register_count(
2243                    vtype.vlmul(),
2244                )
2245                .ok_or(ExecutionError::IllegalInstruction {
2246                    address: PackedAddress::new(
2247                        program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2248                    ),
2249                })?;
2250                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2251                    program_counter,
2252                    vd,
2253                    dest_group_regs,
2254                )?;
2255                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2256                    program_counter,
2257                    vs2,
2258                    group_regs,
2259                )?;
2260                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2261                    program_counter,
2262                    vs1,
2263                    group_regs,
2264                )?;
2265                if !vm && vd == VReg::V0 {
2266                    ::core::hint::cold_path();
2267                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2268                        address: PackedAddress::new(
2269                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2270                        ),
2271                    });
2272                }
2273                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
2274                    program_counter,
2275                    vd,
2276                    vs2,
2277                    dest_group_regs,
2278                    group_regs,
2279                )?;
2280                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
2281                    program_counter,
2282                    vd,
2283                    vs1,
2284                    dest_group_regs,
2285                    group_regs,
2286                )?;
2287                let sew = vtype.vsew();
2288                // SAFETY: alignment and overlap checked above; 2*SEW <= ELEN checked above
2289                unsafe {
2290                    zvexx_muldiv_helpers::execute_widening_muladd_op(
2291                        env,
2292                        vd,
2293                        vs1,
2294                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
2295                        vm,
2296                        sew,
2297                        // vwmacc: vd[i] = vd[i] + sext(vs1[i]) * sext(vs2[i])
2298                        |acc, a, b, sew| {
2299                            let sa = zvexx_muldiv_helpers::sign_extend(a, sew);
2300                            let sb = zvexx_muldiv_helpers::sign_extend(b, sew);
2301                            acc.wrapping_add(sa.cast_unsigned().wrapping_mul(sb.cast_unsigned()))
2302                        },
2303                    );
2304                }
2305            }
2306            Self::VwmaccVx {
2307                vd,
2308                rs1: _,
2309                vs2,
2310                vm,
2311            } => {
2312                if !env.vector_instructions_allowed() {
2313                    ::core::hint::cold_path();
2314                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2315                        address: PackedAddress::new(
2316                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2317                        ),
2318                    });
2319                }
2320                let Some(vtype) = env.vtype() else {
2321                    ::core::hint::cold_path();
2322                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2323                        address: PackedAddress::new(
2324                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2325                        ),
2326                    });
2327                };
2328                // Widening produces a 2*SEW result; an EEW above ELEN is reserved for every
2329                // implementation, so this is not a Zve64x-specific restriction
2330                if !zvexx_muldiv_helpers::widening_eew_supported(vtype.vsew(), Env::ELEN) {
2331                    ::core::hint::cold_path();
2332                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2333                        address: PackedAddress::new(
2334                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2335                        ),
2336                    });
2337                }
2338                let group_regs = vtype.vlmul().register_count();
2339                let dest_group_regs = zvexx_muldiv_helpers::widening_dest_register_count(
2340                    vtype.vlmul(),
2341                )
2342                .ok_or(ExecutionError::IllegalInstruction {
2343                    address: PackedAddress::new(
2344                        program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2345                    ),
2346                })?;
2347                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2348                    program_counter,
2349                    vd,
2350                    dest_group_regs,
2351                )?;
2352                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2353                    program_counter,
2354                    vs2,
2355                    group_regs,
2356                )?;
2357                if !vm && vd == VReg::V0 {
2358                    ::core::hint::cold_path();
2359                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2360                        address: PackedAddress::new(
2361                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2362                        ),
2363                    });
2364                }
2365                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
2366                    program_counter,
2367                    vd,
2368                    vs2,
2369                    dest_group_regs,
2370                    group_regs,
2371                )?;
2372                let sew = vtype.vsew();
2373                let scalar = rs1_value.as_u64();
2374                // SAFETY: alignment and overlap checked above; 2*SEW <= ELEN checked above
2375                unsafe {
2376                    zvexx_muldiv_helpers::execute_widening_muladd_scalar_op(
2377                        env,
2378                        vd,
2379                        scalar,
2380                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
2381                        vm,
2382                        sew,
2383                        |acc, a, b, sew| {
2384                            let sa = zvexx_muldiv_helpers::sign_extend(a, sew);
2385                            let sb = zvexx_muldiv_helpers::sign_extend(b, sew);
2386                            acc.wrapping_add(sa.cast_unsigned().wrapping_mul(sb.cast_unsigned()))
2387                        },
2388                    );
2389                }
2390            }
2391            // vwmaccsu.vv / vwmaccsu.vx - signed×unsigned widening multiply-add
2392            Self::VwmaccsuVv { vd, vs1, vs2, vm } => {
2393                if !env.vector_instructions_allowed() {
2394                    ::core::hint::cold_path();
2395                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2396                        address: PackedAddress::new(
2397                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2398                        ),
2399                    });
2400                }
2401                let Some(vtype) = env.vtype() else {
2402                    ::core::hint::cold_path();
2403                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2404                        address: PackedAddress::new(
2405                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2406                        ),
2407                    });
2408                };
2409                // Widening produces a 2*SEW result; an EEW above ELEN is reserved for every
2410                // implementation, so this is not a Zve64x-specific restriction
2411                if !zvexx_muldiv_helpers::widening_eew_supported(vtype.vsew(), Env::ELEN) {
2412                    ::core::hint::cold_path();
2413                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2414                        address: PackedAddress::new(
2415                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2416                        ),
2417                    });
2418                }
2419                let group_regs = vtype.vlmul().register_count();
2420                let dest_group_regs = zvexx_muldiv_helpers::widening_dest_register_count(
2421                    vtype.vlmul(),
2422                )
2423                .ok_or(ExecutionError::IllegalInstruction {
2424                    address: PackedAddress::new(
2425                        program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2426                    ),
2427                })?;
2428                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2429                    program_counter,
2430                    vd,
2431                    dest_group_regs,
2432                )?;
2433                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2434                    program_counter,
2435                    vs2,
2436                    group_regs,
2437                )?;
2438                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2439                    program_counter,
2440                    vs1,
2441                    group_regs,
2442                )?;
2443                if !vm && vd == VReg::V0 {
2444                    ::core::hint::cold_path();
2445                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2446                        address: PackedAddress::new(
2447                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2448                        ),
2449                    });
2450                }
2451                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
2452                    program_counter,
2453                    vd,
2454                    vs2,
2455                    dest_group_regs,
2456                    group_regs,
2457                )?;
2458                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
2459                    program_counter,
2460                    vd,
2461                    vs1,
2462                    dest_group_regs,
2463                    group_regs,
2464                )?;
2465                let sew = vtype.vsew();
2466                // SAFETY: alignment and overlap checked above; 2*SEW <= ELEN checked above
2467                unsafe {
2468                    zvexx_muldiv_helpers::execute_widening_muladd_op(
2469                        env,
2470                        vd,
2471                        vs1,
2472                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
2473                        vm,
2474                        sew,
2475                        // vwmaccsu: vd[i] = vd[i] + sext(vs1[i]) * zext(vs2[i])
2476                        |acc, a, b, sew| {
2477                            let sa = zvexx_muldiv_helpers::sign_extend(a, sew);
2478                            let ub = b & zvexx_muldiv_helpers::sew_mask(sew);
2479                            acc.wrapping_add(sa.cast_unsigned().wrapping_mul(ub))
2480                        },
2481                    );
2482                }
2483            }
2484            Self::VwmaccsuVx {
2485                vd,
2486                rs1: _,
2487                vs2,
2488                vm,
2489            } => {
2490                if !env.vector_instructions_allowed() {
2491                    ::core::hint::cold_path();
2492                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2493                        address: PackedAddress::new(
2494                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2495                        ),
2496                    });
2497                }
2498                let Some(vtype) = env.vtype() else {
2499                    ::core::hint::cold_path();
2500                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2501                        address: PackedAddress::new(
2502                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2503                        ),
2504                    });
2505                };
2506                // Widening produces a 2*SEW result; an EEW above ELEN is reserved for every
2507                // implementation, so this is not a Zve64x-specific restriction
2508                if !zvexx_muldiv_helpers::widening_eew_supported(vtype.vsew(), Env::ELEN) {
2509                    ::core::hint::cold_path();
2510                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2511                        address: PackedAddress::new(
2512                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2513                        ),
2514                    });
2515                }
2516                let group_regs = vtype.vlmul().register_count();
2517                let dest_group_regs = zvexx_muldiv_helpers::widening_dest_register_count(
2518                    vtype.vlmul(),
2519                )
2520                .ok_or(ExecutionError::IllegalInstruction {
2521                    address: PackedAddress::new(
2522                        program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2523                    ),
2524                })?;
2525                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2526                    program_counter,
2527                    vd,
2528                    dest_group_regs,
2529                )?;
2530                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2531                    program_counter,
2532                    vs2,
2533                    group_regs,
2534                )?;
2535                if !vm && vd == VReg::V0 {
2536                    ::core::hint::cold_path();
2537                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2538                        address: PackedAddress::new(
2539                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2540                        ),
2541                    });
2542                }
2543                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
2544                    program_counter,
2545                    vd,
2546                    vs2,
2547                    dest_group_regs,
2548                    group_regs,
2549                )?;
2550                let sew = vtype.vsew();
2551                // scalar (rs1) is the signed operand; vs2 elements are unsigned
2552                let scalar = rs1_value.as_u64();
2553                // SAFETY: alignment and overlap checked above; 2*SEW <= ELEN checked above
2554                unsafe {
2555                    zvexx_muldiv_helpers::execute_widening_muladd_scalar_op(
2556                        env,
2557                        vd,
2558                        scalar,
2559                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
2560                        vm,
2561                        sew,
2562                        // vwmaccsu.vx: vd[i] = vd[i] + sext(rs1) * zext(vs2[i])
2563                        // Helper passes (acc, scalar_as_a, vs2_as_b, sew): a=rs1 (signed),
2564                        // b=vs2 (unsigned)
2565                        |acc, a, b, sew| {
2566                            let sa = zvexx_muldiv_helpers::sign_extend(a, sew);
2567                            let ub = b & zvexx_muldiv_helpers::sew_mask(sew);
2568                            acc.wrapping_add(sa.cast_unsigned().wrapping_mul(ub))
2569                        },
2570                    );
2571                }
2572            }
2573            // vwmaccus.vx - unsigned×signed widening multiply-add (vx only)
2574            Self::VwmaccusVx {
2575                vd,
2576                rs1: _,
2577                vs2,
2578                vm,
2579            } => {
2580                if !env.vector_instructions_allowed() {
2581                    ::core::hint::cold_path();
2582                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2583                        address: PackedAddress::new(
2584                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2585                        ),
2586                    });
2587                }
2588                let Some(vtype) = env.vtype() else {
2589                    ::core::hint::cold_path();
2590                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2591                        address: PackedAddress::new(
2592                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2593                        ),
2594                    });
2595                };
2596                // Widening produces a 2*SEW result; an EEW above ELEN is reserved for every
2597                // implementation, so this is not a Zve64x-specific restriction
2598                if !zvexx_muldiv_helpers::widening_eew_supported(vtype.vsew(), Env::ELEN) {
2599                    ::core::hint::cold_path();
2600                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2601                        address: PackedAddress::new(
2602                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2603                        ),
2604                    });
2605                }
2606                let group_regs = vtype.vlmul().register_count();
2607                let dest_group_regs = zvexx_muldiv_helpers::widening_dest_register_count(
2608                    vtype.vlmul(),
2609                )
2610                .ok_or(ExecutionError::IllegalInstruction {
2611                    address: PackedAddress::new(
2612                        program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2613                    ),
2614                })?;
2615                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2616                    program_counter,
2617                    vd,
2618                    dest_group_regs,
2619                )?;
2620                zvexx_muldiv_helpers::check_vreg_group_alignment::<Reg, _, _>(
2621                    program_counter,
2622                    vs2,
2623                    group_regs,
2624                )?;
2625                if !vm && vd == VReg::V0 {
2626                    ::core::hint::cold_path();
2627                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
2628                        address: PackedAddress::new(
2629                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
2630                        ),
2631                    });
2632                }
2633                zvexx_muldiv_helpers::check_no_widening_overlap::<Reg, _, _>(
2634                    program_counter,
2635                    vd,
2636                    vs2,
2637                    dest_group_regs,
2638                    group_regs,
2639                )?;
2640                let sew = vtype.vsew();
2641                // scalar (rs1) is the unsigned operand; vs2 elements are signed
2642                let scalar = rs1_value.as_u64();
2643                // SAFETY: alignment and overlap checked above; 2*SEW <= ELEN checked above
2644                unsafe {
2645                    zvexx_muldiv_helpers::execute_widening_muladd_scalar_op(
2646                        env,
2647                        vd,
2648                        scalar,
2649                        zvexx_muldiv_helpers::OpSrc::Vreg(vs2),
2650                        vm,
2651                        sew,
2652                        // vwmaccus.vx: vd[i] = vd[i] + zext(rs1) * sext(vs2[i])
2653                        // Helper passes (acc, scalar_as_a, vs2_as_b, sew): a=rs1 (unsigned),
2654                        // b=vs2 (signed)
2655                        |acc, a, b, sew| {
2656                            let ua = a & zvexx_muldiv_helpers::sew_mask(sew);
2657                            let sb = zvexx_muldiv_helpers::sign_extend(b, sew);
2658                            acc.wrapping_add(sb.cast_unsigned().wrapping_mul(ua))
2659                        },
2660                    );
2661                }
2662            }
2663        }
2664
2665        ExecutionResult::ContinueNoWrite
2666    }
2667}