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, Hart> ExecutableInstructionOperands for ZveXxReductionInstruction<Hart>
21where
22    Reg: Register,
23    Hart: VectorHartConfig<Reg = Reg>,
24{
25}
26
27#[instruction_execution]
28const impl<Reg, Hart, Env> ExecutableInstructionCsr<Env> for ZveXxReductionInstruction<Hart>
29where
30    Reg: Register,
31    Hart: VectorHartConfig<Reg = Reg>,
32{
33}
34
35#[instruction_execution]
36impl<Reg, Hart, Regs, Env, Memory, PC> ExecutableInstruction<Regs, Env, Memory, PC>
37    for ZveXxReductionInstruction<Hart>
38where
39    Reg: Register,
40    Hart: VectorHartConfig<Reg = Reg>,
41    Regs: RegisterFile<Reg>,
42    Env: VectorRegistersExt<Hart = Hart>,
43    Memory: VirtualMemory,
44    PC: ProgramCounter<Reg::Type, Memory>,
45{
46    #[inline(always)]
47    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
48    fn execute(
49        self,
50        Rs1Rs2OperandValues {
51            rs1_value: _,
52            rs2_value: _,
53        }: Rs1Rs2OperandValues<Reg::Type>,
54        _regs: &mut Regs,
55        env: &mut Env,
56        _memory: &mut Memory,
57        program_counter: &PC,
58    ) -> ExecutionResult<Reg> {
59        match self {
60            Self::Vredsum { 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 vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
79                    program_counter,
80                    config,
81                    vs2,
82                    sew.as_eew(),
83                )?;
84                zvexx_reduction_helpers::execute_reduce_op(
85                    env,
86                    vd,
87                    vs2,
88                    vs1,
89                    vm,
90                    sew,
91                    |acc, elem, _sew| acc.wrapping_add(elem),
92                );
93            }
94            Self::Vredand { vd, vs2, vs1, vm } => {
95                if !zvexx_helpers::non_memory_instruction_allowed::<Reg, _>(env) {
96                    ::core::hint::cold_path();
97                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
98                        address: PackedAddress::new(
99                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
100                        ),
101                    });
102                }
103                let Some(config) = env.vector_config() else {
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 sew = config.vtype().vsew();
112                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
113                    program_counter,
114                    config,
115                    vs2,
116                    sew.as_eew(),
117                )?;
118                zvexx_reduction_helpers::execute_reduce_op(
119                    env,
120                    vd,
121                    vs2,
122                    vs1,
123                    vm,
124                    sew,
125                    |acc, elem, _sew| acc & elem,
126                );
127            }
128            Self::Vredor { vd, vs2, vs1, vm } => {
129                if !zvexx_helpers::non_memory_instruction_allowed::<Reg, _>(env) {
130                    ::core::hint::cold_path();
131                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
132                        address: PackedAddress::new(
133                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
134                        ),
135                    });
136                }
137                let Some(config) = env.vector_config() else {
138                    ::core::hint::cold_path();
139                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
140                        address: PackedAddress::new(
141                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
142                        ),
143                    });
144                };
145                let sew = config.vtype().vsew();
146                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
147                    program_counter,
148                    config,
149                    vs2,
150                    sew.as_eew(),
151                )?;
152                zvexx_reduction_helpers::execute_reduce_op(
153                    env,
154                    vd,
155                    vs2,
156                    vs1,
157                    vm,
158                    sew,
159                    |acc, elem, _sew| acc | elem,
160                );
161            }
162            Self::Vredxor { vd, vs2, vs1, vm } => {
163                if !zvexx_helpers::non_memory_instruction_allowed::<Reg, _>(env) {
164                    ::core::hint::cold_path();
165                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
166                        address: PackedAddress::new(
167                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
168                        ),
169                    });
170                }
171                let Some(config) = env.vector_config() else {
172                    ::core::hint::cold_path();
173                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
174                        address: PackedAddress::new(
175                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
176                        ),
177                    });
178                };
179                let sew = config.vtype().vsew();
180                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
181                    program_counter,
182                    config,
183                    vs2,
184                    sew.as_eew(),
185                )?;
186                zvexx_reduction_helpers::execute_reduce_op(
187                    env,
188                    vd,
189                    vs2,
190                    vs1,
191                    vm,
192                    sew,
193                    |acc, elem, _sew| acc ^ elem,
194                );
195            }
196            Self::Vredminu { vd, vs2, vs1, vm } => {
197                if !zvexx_helpers::non_memory_instruction_allowed::<Reg, _>(env) {
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(config) = env.vector_config() 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                let sew = config.vtype().vsew();
214                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
215                    program_counter,
216                    config,
217                    vs2,
218                    sew.as_eew(),
219                )?;
220                zvexx_reduction_helpers::execute_reduce_op(
221                    env,
222                    vd,
223                    vs2,
224                    vs1,
225                    vm,
226                    sew,
227                    |acc, elem, sew| {
228                        let mask = zvexx_arith_helpers::sew_mask(sew);
229                        if elem & mask < acc & mask { elem } else { acc }
230                    },
231                );
232            }
233            Self::Vredmin { vd, vs2, vs1, vm } => {
234                if !zvexx_helpers::non_memory_instruction_allowed::<Reg, _>(env) {
235                    ::core::hint::cold_path();
236                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
237                        address: PackedAddress::new(
238                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
239                        ),
240                    });
241                }
242                let Some(config) = env.vector_config() else {
243                    ::core::hint::cold_path();
244                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
245                        address: PackedAddress::new(
246                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
247                        ),
248                    });
249                };
250                let sew = config.vtype().vsew();
251                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
252                    program_counter,
253                    config,
254                    vs2,
255                    sew.as_eew(),
256                )?;
257                zvexx_reduction_helpers::execute_reduce_op(
258                    env,
259                    vd,
260                    vs2,
261                    vs1,
262                    vm,
263                    sew,
264                    |acc, elem, sew| {
265                        if zvexx_arith_helpers::sign_extend(elem, sew)
266                            < zvexx_arith_helpers::sign_extend(acc, sew)
267                        {
268                            elem
269                        } else {
270                            acc
271                        }
272                    },
273                );
274            }
275            Self::Vredmaxu { vd, vs2, vs1, vm } => {
276                if !zvexx_helpers::non_memory_instruction_allowed::<Reg, _>(env) {
277                    ::core::hint::cold_path();
278                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
279                        address: PackedAddress::new(
280                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
281                        ),
282                    });
283                }
284                let Some(config) = env.vector_config() else {
285                    ::core::hint::cold_path();
286                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
287                        address: PackedAddress::new(
288                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
289                        ),
290                    });
291                };
292                let sew = config.vtype().vsew();
293                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
294                    program_counter,
295                    config,
296                    vs2,
297                    sew.as_eew(),
298                )?;
299                zvexx_reduction_helpers::execute_reduce_op(
300                    env,
301                    vd,
302                    vs2,
303                    vs1,
304                    vm,
305                    sew,
306                    |acc, elem, sew| {
307                        let mask = zvexx_arith_helpers::sew_mask(sew);
308                        if elem & mask > acc & mask { elem } else { acc }
309                    },
310                );
311            }
312            Self::Vredmax { vd, vs2, vs1, vm } => {
313                if !zvexx_helpers::non_memory_instruction_allowed::<Reg, _>(env) {
314                    ::core::hint::cold_path();
315                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
316                        address: PackedAddress::new(
317                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
318                        ),
319                    });
320                }
321                let Some(config) = env.vector_config() else {
322                    ::core::hint::cold_path();
323                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
324                        address: PackedAddress::new(
325                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
326                        ),
327                    });
328                };
329                let sew = config.vtype().vsew();
330                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
331                    program_counter,
332                    config,
333                    vs2,
334                    sew.as_eew(),
335                )?;
336                zvexx_reduction_helpers::execute_reduce_op(
337                    env,
338                    vd,
339                    vs2,
340                    vs1,
341                    vm,
342                    sew,
343                    |acc, elem, sew| {
344                        if zvexx_arith_helpers::sign_extend(elem, sew)
345                            > zvexx_arith_helpers::sign_extend(acc, sew)
346                        {
347                            elem
348                        } else {
349                            acc
350                        }
351                    },
352                );
353            }
354            Self::Vwredsumu { vd, vs2, vs1, vm } => {
355                if !zvexx_helpers::non_memory_instruction_allowed::<Reg, _>(env) {
356                    ::core::hint::cold_path();
357                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
358                        address: PackedAddress::new(
359                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
360                        ),
361                    });
362                }
363                let Some(config) = env.vector_config() else {
364                    ::core::hint::cold_path();
365                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
366                        address: PackedAddress::new(
367                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
368                        ),
369                    });
370                };
371                let Some(widening_sew) =
372                    zvexx_helpers::WideningSew::<Env::Hart>::new(config.vtype().vsew())
373                else {
374                    ::core::hint::cold_path();
375                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
376                        address: PackedAddress::new(
377                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
378                        ),
379                    });
380                };
381                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
382                    program_counter,
383                    config,
384                    vs2,
385                    widening_sew.narrow().as_eew(),
386                )?;
387                // `vs1` is read with EEW=2*SEW and `vs2` with EEW=SEW
388                zvexx_helpers::check_register_outside_group::<Reg, Env, _, _>(
389                    program_counter,
390                    vs2,
391                    vs1,
392                )?;
393                zvexx_reduction_helpers::execute_widening_reduce_op::<false, _, _, _>(
394                    env,
395                    vd,
396                    vs2,
397                    vs1,
398                    vm,
399                    widening_sew,
400                    // Zero-extend vs2 elements then accumulate
401                    |acc, elem, _sew| acc.wrapping_add(elem),
402                );
403            }
404            Self::Vwredsum { vd, vs2, vs1, vm } => {
405                if !zvexx_helpers::non_memory_instruction_allowed::<Reg, _>(env) {
406                    ::core::hint::cold_path();
407                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
408                        address: PackedAddress::new(
409                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
410                        ),
411                    });
412                }
413                let Some(config) = env.vector_config() else {
414                    ::core::hint::cold_path();
415                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
416                        address: PackedAddress::new(
417                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
418                        ),
419                    });
420                };
421                let Some(widening_sew) =
422                    zvexx_helpers::WideningSew::<Env::Hart>::new(config.vtype().vsew())
423                else {
424                    ::core::hint::cold_path();
425                    return ExecutionResult::Err(ExecutionError::IllegalInstruction {
426                        address: PackedAddress::new(
427                            program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
428                        ),
429                    });
430                };
431                let vs2 = zvexx_helpers::vreg_group::<Reg, Env, _, _>(
432                    program_counter,
433                    config,
434                    vs2,
435                    widening_sew.narrow().as_eew(),
436                )?;
437                // `vs1` is read with EEW=2*SEW and `vs2` with EEW=SEW
438                zvexx_helpers::check_register_outside_group::<Reg, Env, _, _>(
439                    program_counter,
440                    vs2,
441                    vs1,
442                )?;
443                zvexx_reduction_helpers::execute_widening_reduce_op::<true, _, _, _>(
444                    env,
445                    vd,
446                    vs2,
447                    vs1,
448                    vm,
449                    widening_sew,
450                    // Sign-extend vs2 elements then accumulate
451                    |acc, elem, _sew| acc.wrapping_add(elem),
452                );
453            }
454        }
455
456        ExecutionResult::ContinueNoWrite
457    }
458}