Skip to main content

ab_riscv_interpreter/v/zvexx/
muldiv.rs

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