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