Skip to main content

ab_riscv_interpreter/v/zvexx/reduction/
zvexx_reduction_helpers.rs

1//! Opaque helpers for ZveXx extension
2use crate::v::vector_registers::{VRegGroup, VectorRegistersExt};
3use crate::v::zvexx::arith::zvexx_arith_helpers::sign_extend;
4use crate::v::zvexx::load::zvexx_load_helpers::{mask_bit, snapshot_mask};
5use crate::v::zvexx::zvexx_helpers::WideningSew;
6use ab_riscv_primitives::prelude::*;
7use core::hint::cold_path;
8
9/// Execute a single-width integer reduction.
10///
11/// `vd` and `vs1` are single registers regardless of `LMUL`, element 0 of which holds the scalar
12/// operand and result.
13#[inline(always)]
14#[doc(hidden)]
15#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
16pub fn execute_reduce_op<Reg, Env, F>(
17    env: &mut Env,
18    vd: VReg,
19    vs2: VRegGroup<Env::Hart>,
20    vs1: VReg,
21    vm: bool,
22    sew: Vsew,
23    op: F,
24) where
25    Reg: Register,
26    Env: VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
27    F: Fn(u64, u64, Vsew) -> u64,
28{
29    // Spec ยง5.4: when vstart >= vl, no element of vd is updated. For reductions this means
30    // vl == 0 (since caller has verified vstart == 0). In that case we must not write vd and
31    // must not mark vs dirty.
32    let vl = vs2.vl();
33    if vl.get() == Vl::ZERO {
34        cold_path();
35        return;
36    }
37    let init = env
38        .read_vregs()
39        .read_first(vs1, sew.as_eew())
40        .expect("Element width never exceeds `ELEN`, which never exceeds `VLEN`; qed");
41    let mask_buf = snapshot_mask(env.read_vregs(), vm);
42    let mut acc = init;
43    for i in vl.indices() {
44        if !mask_bit(&mask_buf, i) {
45            continue;
46        }
47        let elem = env
48            .read_vregs()
49            .read(vs2, i)
50            .expect("`i < vl` of `vs2`; qed");
51        acc = op(acc, elem, sew);
52    }
53    env.write_vregs()
54        .write_first(vd, sew.as_eew(), acc)
55        .expect("Element width never exceeds `ELEN`, which never exceeds `VLEN`; qed");
56    env.mark_vs_dirty();
57}
58
59/// Execute a widening integer sum reduction.
60///
61/// `vd` and `vs1` are single registers regardless of `LMUL`, element 0 of which holds the scalar
62/// operand and result.
63#[inline(always)]
64#[doc(hidden)]
65#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
66pub fn execute_widening_reduce_op<const SIGN_EXTEND_SRC: bool, Reg, Env, F>(
67    env: &mut Env,
68    vd: VReg,
69    vs2: VRegGroup<Env::Hart>,
70    vs1: VReg,
71    vm: bool,
72    sew: WideningSew<Env::Hart>,
73    op: F,
74) where
75    Reg: Register,
76    Env: VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
77    F: Fn(u64, u64, Vsew) -> u64,
78{
79    let wide_sew = sew.wide();
80    let sew = sew.narrow();
81    let vl = vs2.vl();
82    if vl.get() == Vl::ZERO {
83        cold_path();
84        return;
85    }
86    let init = env
87        .read_vregs()
88        .read_first(vs1, wide_sew.as_eew())
89        .expect("Element width never exceeds `ELEN`, which never exceeds `VLEN`; qed");
90    let mask_buf = snapshot_mask(env.read_vregs(), vm);
91    let mut acc = init;
92    for i in vl.indices() {
93        if !mask_bit(&mask_buf, i) {
94            continue;
95        }
96        let raw = env
97            .read_vregs()
98            .read(vs2, i)
99            .expect("`i < vl` of `vs2`; qed");
100        let elem = if SIGN_EXTEND_SRC {
101            sign_extend(raw, sew).cast_unsigned()
102        } else {
103            raw
104        };
105        acc = op(acc, elem, wide_sew);
106    }
107    env.write_vregs()
108        .write_first(vd, wide_sew.as_eew(), acc)
109        .expect("Element width never exceeds `ELEN`, which never exceeds `VLEN`; qed");
110    env.mark_vs_dirty();
111}