Skip to main content

ab_riscv_interpreter/v/zvexx/
reduction.rs

1//! ZveXx integer reduction instructions
2
3#[cfg(test)]
4mod tests;
5pub mod zvexx_reduction_helpers;
6
7use crate::v::vector_registers::VectorRegistersExt;
8use crate::v::zvexx::arith::zvexx_arith_helpers;
9use crate::v::zvexx::zvexx_helpers;
10use crate::{
11    ExecutableInstruction, ExecutableInstructionCsr, ExecutableInstructionOperands, ExecutionError,
12    ExecutionResult, FetchInstructionResult, InstructionFetcher, OpaqueThreadedExecutionResult,
13    PackedAddress, ProgramCounter, RegisterFile, Rs1Rs2OperandValues, Rs1Rs2Operands,
14    ThreadedExecutableInstruction, ThreadedExecutionResult, VirtualMemory,
15};
16use ab_riscv_macros::instruction_execution;
17use ab_riscv_primitives::prelude::*;
18
19#[instruction_execution]
20const impl<Reg> ExecutableInstructionOperands for ZveXxReductionInstruction<Reg> where Reg: Register {}
21
22#[instruction_execution]
23const impl<Reg, Env> ExecutableInstructionCsr<Env> for ZveXxReductionInstruction<Reg> where
24    Reg: Register
25{
26}
27
28#[instruction_execution]
29impl<Reg, Regs, Env, Memory, PC> ExecutableInstruction<Regs, Env, Memory, PC>
30    for ZveXxReductionInstruction<Reg>
31where
32    Reg: Register,
33    Regs: RegisterFile<Reg>,
34    Env: VectorRegistersExt<Reg>,
35    [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
36    Memory: VirtualMemory,
37    PC: ProgramCounter<Reg::Type, Memory>,
38{
39    #[inline(always)]
40    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
41    fn execute(
42        self,
43        Rs1Rs2OperandValues {
44            rs1_value: _,
45            rs2_value: _,
46        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
47        _regs: &mut Regs,
48        env: &mut Env,
49        _memory: &mut Memory,
50        program_counter: &mut PC,
51    ) -> ExecutionResult<Self::Reg> {
52        match self {
53            Self::Vredsum { vd, vs2, vs1, vm } => {
54                if !env.vector_instructions_allowed() {
55                    ::core::hint::cold_path();
56                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
57                        address: PackedAddress::new(
58                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
59                        ),
60                    });
61                }
62                let Some(vtype) = env.vtype() else {
63                    ::core::hint::cold_path();
64                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
65                        address: PackedAddress::new(
66                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
67                        ),
68                    });
69                };
70                // Spec ยง14: reductions with vstart > 0 are reserved; raise illegal instruction
71                if env.vstart() != Vstart::ZERO {
72                    ::core::hint::cold_path();
73                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
74                        address: PackedAddress::new(
75                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
76                        ),
77                    });
78                }
79                let group_regs = vtype.vlmul().register_count();
80                zvexx_arith_helpers::check_vreg_group_alignment::<Reg, _, _>(
81                    program_counter,
82                    vs2,
83                    group_regs,
84                )?;
85                let sew = vtype.vsew();
86                let vl = env.vl();
87                // SAFETY: `vs2` alignment checked; `vstart == 0` checked;
88                // `vs1` and `vd` are single-register scalar operands
89                unsafe {
90                    zvexx_reduction_helpers::execute_reduce_op(
91                        env,
92                        vd,
93                        vs2,
94                        vs1,
95                        vm,
96                        vl,
97                        sew,
98                        |acc, elem, _sew| acc.wrapping_add(elem),
99                    );
100                }
101            }
102            Self::Vredand { vd, vs2, vs1, vm } => {
103                if !env.vector_instructions_allowed() {
104                    ::core::hint::cold_path();
105                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
106                        address: PackedAddress::new(
107                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
108                        ),
109                    });
110                }
111                let Some(vtype) = env.vtype() else {
112                    ::core::hint::cold_path();
113                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
114                        address: PackedAddress::new(
115                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
116                        ),
117                    });
118                };
119                if env.vstart() != Vstart::ZERO {
120                    ::core::hint::cold_path();
121                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
122                        address: PackedAddress::new(
123                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
124                        ),
125                    });
126                }
127                let group_regs = vtype.vlmul().register_count();
128                zvexx_arith_helpers::check_vreg_group_alignment::<Reg, _, _>(
129                    program_counter,
130                    vs2,
131                    group_regs,
132                )?;
133                let sew = vtype.vsew();
134                let vl = env.vl();
135                // SAFETY: see `Vredsum`
136                unsafe {
137                    zvexx_reduction_helpers::execute_reduce_op(
138                        env,
139                        vd,
140                        vs2,
141                        vs1,
142                        vm,
143                        vl,
144                        sew,
145                        |acc, elem, _sew| acc & elem,
146                    );
147                }
148            }
149            Self::Vredor { vd, vs2, vs1, vm } => {
150                if !env.vector_instructions_allowed() {
151                    ::core::hint::cold_path();
152                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
153                        address: PackedAddress::new(
154                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
155                        ),
156                    });
157                }
158                let Some(vtype) = env.vtype() else {
159                    ::core::hint::cold_path();
160                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
161                        address: PackedAddress::new(
162                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
163                        ),
164                    });
165                };
166                if env.vstart() != Vstart::ZERO {
167                    ::core::hint::cold_path();
168                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
169                        address: PackedAddress::new(
170                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
171                        ),
172                    });
173                }
174                let group_regs = vtype.vlmul().register_count();
175                zvexx_arith_helpers::check_vreg_group_alignment::<Reg, _, _>(
176                    program_counter,
177                    vs2,
178                    group_regs,
179                )?;
180                let sew = vtype.vsew();
181                let vl = env.vl();
182                // SAFETY: see `Vredsum`
183                unsafe {
184                    zvexx_reduction_helpers::execute_reduce_op(
185                        env,
186                        vd,
187                        vs2,
188                        vs1,
189                        vm,
190                        vl,
191                        sew,
192                        |acc, elem, _sew| acc | elem,
193                    );
194                }
195            }
196            Self::Vredxor { vd, vs2, vs1, vm } => {
197                if !env.vector_instructions_allowed() {
198                    ::core::hint::cold_path();
199                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
200                        address: PackedAddress::new(
201                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
202                        ),
203                    });
204                }
205                let Some(vtype) = env.vtype() else {
206                    ::core::hint::cold_path();
207                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
208                        address: PackedAddress::new(
209                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
210                        ),
211                    });
212                };
213                if env.vstart() != Vstart::ZERO {
214                    ::core::hint::cold_path();
215                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
216                        address: PackedAddress::new(
217                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
218                        ),
219                    });
220                }
221                let group_regs = vtype.vlmul().register_count();
222                zvexx_arith_helpers::check_vreg_group_alignment::<Reg, _, _>(
223                    program_counter,
224                    vs2,
225                    group_regs,
226                )?;
227                let sew = vtype.vsew();
228                let vl = env.vl();
229                // SAFETY: see `Vredsum`
230                unsafe {
231                    zvexx_reduction_helpers::execute_reduce_op(
232                        env,
233                        vd,
234                        vs2,
235                        vs1,
236                        vm,
237                        vl,
238                        sew,
239                        |acc, elem, _sew| acc ^ elem,
240                    );
241                }
242            }
243            Self::Vredminu { vd, vs2, vs1, vm } => {
244                if !env.vector_instructions_allowed() {
245                    ::core::hint::cold_path();
246                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
247                        address: PackedAddress::new(
248                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
249                        ),
250                    });
251                }
252                let Some(vtype) = env.vtype() else {
253                    ::core::hint::cold_path();
254                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
255                        address: PackedAddress::new(
256                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
257                        ),
258                    });
259                };
260                if env.vstart() != Vstart::ZERO {
261                    ::core::hint::cold_path();
262                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
263                        address: PackedAddress::new(
264                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
265                        ),
266                    });
267                }
268                let group_regs = vtype.vlmul().register_count();
269                zvexx_arith_helpers::check_vreg_group_alignment::<Reg, _, _>(
270                    program_counter,
271                    vs2,
272                    group_regs,
273                )?;
274                let sew = vtype.vsew();
275                let vl = env.vl();
276                // SAFETY: see `Vredsum`
277                unsafe {
278                    zvexx_reduction_helpers::execute_reduce_op(
279                        env,
280                        vd,
281                        vs2,
282                        vs1,
283                        vm,
284                        vl,
285                        sew,
286                        |acc, elem, sew| {
287                            let mask = zvexx_arith_helpers::sew_mask(sew);
288                            if elem & mask < acc & mask { elem } else { acc }
289                        },
290                    );
291                }
292            }
293            Self::Vredmin { vd, vs2, vs1, vm } => {
294                if !env.vector_instructions_allowed() {
295                    ::core::hint::cold_path();
296                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
297                        address: PackedAddress::new(
298                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
299                        ),
300                    });
301                }
302                let Some(vtype) = env.vtype() else {
303                    ::core::hint::cold_path();
304                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
305                        address: PackedAddress::new(
306                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
307                        ),
308                    });
309                };
310                if env.vstart() != Vstart::ZERO {
311                    ::core::hint::cold_path();
312                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
313                        address: PackedAddress::new(
314                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
315                        ),
316                    });
317                }
318                let group_regs = vtype.vlmul().register_count();
319                zvexx_arith_helpers::check_vreg_group_alignment::<Reg, _, _>(
320                    program_counter,
321                    vs2,
322                    group_regs,
323                )?;
324                let sew = vtype.vsew();
325                let vl = env.vl();
326                // SAFETY: see `Vredsum`
327                unsafe {
328                    zvexx_reduction_helpers::execute_reduce_op(
329                        env,
330                        vd,
331                        vs2,
332                        vs1,
333                        vm,
334                        vl,
335                        sew,
336                        |acc, elem, sew| {
337                            if zvexx_arith_helpers::sign_extend(elem, sew)
338                                < zvexx_arith_helpers::sign_extend(acc, sew)
339                            {
340                                elem
341                            } else {
342                                acc
343                            }
344                        },
345                    );
346                }
347            }
348            Self::Vredmaxu { vd, vs2, vs1, vm } => {
349                if !env.vector_instructions_allowed() {
350                    ::core::hint::cold_path();
351                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
352                        address: PackedAddress::new(
353                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
354                        ),
355                    });
356                }
357                let Some(vtype) = env.vtype() else {
358                    ::core::hint::cold_path();
359                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
360                        address: PackedAddress::new(
361                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
362                        ),
363                    });
364                };
365                if env.vstart() != Vstart::ZERO {
366                    ::core::hint::cold_path();
367                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
368                        address: PackedAddress::new(
369                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
370                        ),
371                    });
372                }
373                let group_regs = vtype.vlmul().register_count();
374                zvexx_arith_helpers::check_vreg_group_alignment::<Reg, _, _>(
375                    program_counter,
376                    vs2,
377                    group_regs,
378                )?;
379                let sew = vtype.vsew();
380                let vl = env.vl();
381                // SAFETY: see `Vredsum`
382                unsafe {
383                    zvexx_reduction_helpers::execute_reduce_op(
384                        env,
385                        vd,
386                        vs2,
387                        vs1,
388                        vm,
389                        vl,
390                        sew,
391                        |acc, elem, sew| {
392                            let mask = zvexx_arith_helpers::sew_mask(sew);
393                            if elem & mask > acc & mask { elem } else { acc }
394                        },
395                    );
396                }
397            }
398            Self::Vredmax { vd, vs2, vs1, vm } => {
399                if !env.vector_instructions_allowed() {
400                    ::core::hint::cold_path();
401                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
402                        address: PackedAddress::new(
403                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
404                        ),
405                    });
406                }
407                let Some(vtype) = env.vtype() else {
408                    ::core::hint::cold_path();
409                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
410                        address: PackedAddress::new(
411                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
412                        ),
413                    });
414                };
415                if env.vstart() != Vstart::ZERO {
416                    ::core::hint::cold_path();
417                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
418                        address: PackedAddress::new(
419                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
420                        ),
421                    });
422                }
423                let group_regs = vtype.vlmul().register_count();
424                zvexx_arith_helpers::check_vreg_group_alignment::<Reg, _, _>(
425                    program_counter,
426                    vs2,
427                    group_regs,
428                )?;
429                let sew = vtype.vsew();
430                let vl = env.vl();
431                // SAFETY: see `Vredsum`
432                unsafe {
433                    zvexx_reduction_helpers::execute_reduce_op(
434                        env,
435                        vd,
436                        vs2,
437                        vs1,
438                        vm,
439                        vl,
440                        sew,
441                        |acc, elem, sew| {
442                            if zvexx_arith_helpers::sign_extend(elem, sew)
443                                > zvexx_arith_helpers::sign_extend(acc, sew)
444                            {
445                                elem
446                            } else {
447                                acc
448                            }
449                        },
450                    );
451                }
452            }
453            Self::Vwredsumu { vd, vs2, vs1, vm } => {
454                if !env.vector_instructions_allowed() {
455                    ::core::hint::cold_path();
456                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
457                        address: PackedAddress::new(
458                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
459                        ),
460                    });
461                }
462                let Some(vtype) = env.vtype() else {
463                    ::core::hint::cold_path();
464                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
465                        address: PackedAddress::new(
466                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
467                        ),
468                    });
469                };
470                if env.vstart() != Vstart::ZERO {
471                    ::core::hint::cold_path();
472                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
473                        address: PackedAddress::new(
474                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
475                        ),
476                    });
477                }
478                // Widening: 2*SEW must fit in ELEN
479                if u32::from(vtype.vsew().bits_width()) * 2 > u32::from(Env::ELEN) {
480                    ::core::hint::cold_path();
481                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
482                        address: PackedAddress::new(
483                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
484                        ),
485                    });
486                }
487                let group_regs = vtype.vlmul().register_count();
488                zvexx_arith_helpers::check_vreg_group_alignment::<Reg, _, _>(
489                    program_counter,
490                    vs2,
491                    group_regs,
492                )?;
493                let sew = vtype.vsew();
494                let vl = env.vl();
495                // SAFETY: `vs2` alignment checked; widening SEW constraint checked above;
496                // `vstart == 0` checked; `vd` and `vs1` are single-register 2*SEW scalar operands
497                unsafe {
498                    zvexx_reduction_helpers::execute_widening_reduce_op::<false, _, _, _>(
499                        env,
500                        vd,
501                        vs2,
502                        vs1,
503                        vm,
504                        vl,
505                        sew,
506                        // Zero-extend vs2 elements then accumulate
507                        |acc, elem, _sew| acc.wrapping_add(elem),
508                    );
509                }
510            }
511            Self::Vwredsum { vd, vs2, vs1, vm } => {
512                if !env.vector_instructions_allowed() {
513                    ::core::hint::cold_path();
514                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
515                        address: PackedAddress::new(
516                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
517                        ),
518                    });
519                }
520                let Some(vtype) = env.vtype() else {
521                    ::core::hint::cold_path();
522                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
523                        address: PackedAddress::new(
524                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
525                        ),
526                    });
527                };
528                if env.vstart() != Vstart::ZERO {
529                    ::core::hint::cold_path();
530                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
531                        address: PackedAddress::new(
532                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
533                        ),
534                    });
535                }
536                if u32::from(vtype.vsew().bits_width()) * 2 > u32::from(Env::ELEN) {
537                    ::core::hint::cold_path();
538                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
539                        address: PackedAddress::new(
540                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
541                        ),
542                    });
543                }
544                let group_regs = vtype.vlmul().register_count();
545                zvexx_arith_helpers::check_vreg_group_alignment::<Reg, _, _>(
546                    program_counter,
547                    vs2,
548                    group_regs,
549                )?;
550                let sew = vtype.vsew();
551                let vl = env.vl();
552                // SAFETY: see `Vwredsumu`
553                unsafe {
554                    zvexx_reduction_helpers::execute_widening_reduce_op::<true, _, _, _>(
555                        env,
556                        vd,
557                        vs2,
558                        vs1,
559                        vm,
560                        vl,
561                        sew,
562                        // Sign-extend vs2 elements then accumulate
563                        |acc, elem, _sew| acc.wrapping_add(elem),
564                    );
565                }
566            }
567        }
568
569        ExecutionResult::ContinueNoWrite
570    }
571}