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