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