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