Skip to main content

ab_riscv_interpreter/zvbb/
zvbb_helpers.rs

1//! Opaque helpers for Zvbb extension
2
3use crate::v::vector_registers::VectorRegistersExt;
4pub use crate::v::zvexx::arith::zvexx_arith_helpers::{OpSrc, check_vreg_group_alignment};
5use crate::v::zvexx::load::zvexx_load_helpers::mask_bit;
6use ab_riscv_primitives::prelude::*;
7
8/// Execute element-wise full bit-reversal over `vstart..vl`, writing SEW-wide results into `vd`.
9///
10/// For each active element i: all bits within `vs2[i]` are reversed end-to-end
11/// (bit 0 <-> bit SEW-1). This differs from `vbrev8`, which reverses bits within each byte while
12/// preserving byte order; `vbrev` also inverts the byte order as a side effect of reversing the
13/// whole element.
14///
15/// When `vm=false`, masked-off elements are left undisturbed.
16///
17/// # Safety
18/// - `vd.to_bits() % group_regs == 0` and `vd.to_bits() + group_regs <= 32`
19/// - `vs2.to_bits() % group_regs == 0` and `vs2.to_bits() + group_regs <= 32`
20/// - `vl <= group_regs * VLEN.bytes() / sew_bytes`
21#[inline(always)]
22#[doc(hidden)]
23#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
24pub unsafe fn execute_vbrev<Reg, Env>(env: &mut Env, vd: VReg, vs2: VReg, sew: Vsew, vm: bool)
25where
26    Reg: Register,
27    Env: VectorRegistersExt<Reg>,
28    [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
29{
30    let vl = env.vl();
31    let vstart = env.vstart();
32    for i in vstart.range_to(vl) {
33        if !vm && !mask_bit(env.read_vregs().get(VReg::V0), i) {
34            continue;
35        }
36        // SAFETY: `vs2 % group_regs == 0` and `vs2 + group_regs <= 32`; `i < vl`
37        let elem = unsafe { env.read_vregs().read_element(vs2, i, sew) };
38        // `elem` is zero-extended from SEW bits to u64; reverse_bits() on the primitive type
39        // of exactly SEW width naturally handles the upper zero bits from zero-extension
40        let result = match sew {
41            Vsew::E8 => u64::from((elem as u8).reverse_bits()),
42            Vsew::E16 => u64::from((elem as u16).reverse_bits()),
43            Vsew::E32 => u64::from((elem as u32).reverse_bits()),
44            Vsew::E64 => elem.reverse_bits(),
45        };
46        // SAFETY: `vd % group_regs == 0` and `vd + group_regs <= 32`; `i < vl`
47        unsafe {
48            env.write_vregs().write_element(vd, i, sew, result);
49        }
50    }
51    env.mark_vs_dirty();
52    env.reset_vstart();
53}
54
55/// Execute element-wise count-leading-zeros over `vstart..vl`, writing SEW-wide results into `vd`.
56///
57/// For each active element i: `vd[i] = clz(vs2[i])`, counting within the SEW-wide field. An
58/// all-zero element produces SEW, not 64.
59///
60/// When `vm=false`, masked-off elements are left undisturbed.
61///
62/// # Safety
63/// Same register-group constraints as [`execute_vbrev`].
64#[inline(always)]
65#[doc(hidden)]
66#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
67pub unsafe fn execute_vclz<Reg, Env>(env: &mut Env, vd: VReg, vs2: VReg, sew: Vsew, vm: bool)
68where
69    Reg: Register,
70    Env: VectorRegistersExt<Reg>,
71    [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
72{
73    let vl = env.vl();
74    let vstart = env.vstart();
75    let sew_bits = u32::from(sew.bits_width());
76    for i in vstart.range_to(vl) {
77        if !vm && !mask_bit(env.read_vregs().get(VReg::V0), i) {
78            continue;
79        }
80        // SAFETY: `vs2 % group_regs == 0` and `vs2 + group_regs <= 32`; `i < vl`
81        let elem = unsafe { env.read_vregs().read_element(vs2, i, sew) };
82        // `elem` is zero-extended from SEW bits to u64; `leading_zeros()` on a u64 therefore counts
83        // the extra (64 - SEW) upper zero bits introduced by zero-extension. Subtracting them gives
84        // the count within the SEW-wide field.
85        let clz = elem.leading_zeros() - (64 - sew_bits);
86        // SAFETY: `vd % group_regs == 0` and `vd + group_regs <= 32`; `i < vl`
87        unsafe {
88            env.write_vregs().write_element(vd, i, sew, u64::from(clz));
89        }
90    }
91    env.mark_vs_dirty();
92    env.reset_vstart();
93}
94
95/// Execute element-wise count-trailing-zeros over `vstart..vl`, writing SEW-wide results into `vd`.
96///
97/// For each active element i: `vd[i] = ctz(vs2[i])`, counting within the SEW-wide field. An
98/// all-zero element produces SEW, not 64.
99///
100/// When `vm=false`, masked-off elements are left undisturbed.
101///
102/// # Safety
103/// Same register-group constraints as [`execute_vbrev`].
104#[inline(always)]
105#[doc(hidden)]
106#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
107pub unsafe fn execute_vctz<Reg, Env>(env: &mut Env, vd: VReg, vs2: VReg, sew: Vsew, vm: bool)
108where
109    Reg: Register,
110    Env: VectorRegistersExt<Reg>,
111    [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
112{
113    let vl = env.vl();
114    let vstart = env.vstart();
115    let sew_bits = u32::from(sew.bits_width());
116    for i in vstart.range_to(vl) {
117        if !vm && !mask_bit(env.read_vregs().get(VReg::V0), i) {
118            continue;
119        }
120        // SAFETY: `vs2 % group_regs == 0` and `vs2 + group_regs <= 32`; `i < vl`
121        let elem = unsafe { env.read_vregs().read_element(vs2, i, sew) };
122        // For non-zero `elem`, `trailing_zeros()` on the zero-extended u64 value is correct: the
123        // upper zero bits do not affect the trailing count. For zero, `trailing_zeros()` returns
124        // 64, but the spec result is SEW; cap at `sew_bits` handles both cases.
125        let ctz = elem.trailing_zeros().min(sew_bits);
126        // SAFETY: `vd % group_regs == 0` and `vd + group_regs <= 32`; `i < vl`
127        unsafe {
128            env.write_vregs().write_element(vd, i, sew, u64::from(ctz));
129        }
130    }
131    env.mark_vs_dirty();
132    env.reset_vstart();
133}
134
135/// Execute element-wise population count over `vstart..vl`, writing SEW-wide results into `vd`.
136///
137/// For each active element i: `vd[i] = popcount(vs2[i])`, in range `[0, SEW]`.
138///
139/// When `vm=false`, masked-off elements are left undisturbed.
140///
141/// # Safety
142/// Same register-group constraints as [`execute_vbrev`].
143#[inline(always)]
144#[doc(hidden)]
145#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
146pub unsafe fn execute_vcpop<Reg, Env>(env: &mut Env, vd: VReg, vs2: VReg, sew: Vsew, vm: bool)
147where
148    Reg: Register,
149    Env: VectorRegistersExt<Reg>,
150    [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
151{
152    let vl = env.vl();
153    let vstart = env.vstart();
154    for i in vstart.range_to(vl) {
155        if !vm && !mask_bit(env.read_vregs().get(VReg::V0), i) {
156            continue;
157        }
158        // SAFETY: `vs2 % group_regs == 0` and `vs2 + group_regs <= 32`; `i < vl`
159        let elem = unsafe { env.read_vregs().read_element(vs2, i, sew) };
160        // `elem` is zero-extended from SEW bits; upper bits are already zero, so `count_ones()`
161        // directly gives the population count within the SEW-wide field
162        let cpop = elem.count_ones();
163        // SAFETY: `vd % group_regs == 0` and `vd + group_regs <= 32`; `i < vl`
164        unsafe {
165            env.write_vregs().write_element(vd, i, sew, u64::from(cpop));
166        }
167    }
168    env.mark_vs_dirty();
169    env.reset_vstart();
170}
171
172/// Execute element-wise widening shift-left-logical over `vstart..vl`, writing 2*SEW-wide
173/// results into `vd`.
174///
175/// For each active element i: `vd[i] = zero_extend_to_2SEW(vs2[i]) << (src[i] % (2*SEW))`.
176/// The source operand width is SEW; the destination element width is `double_sew` (2*SEW).
177///
178/// The caller must ensure SEW <= E32 (i.e., `sew.double_width()` is `Some`); passing SEW=E64 is a
179/// programming error that would produce a result wider than u64.
180///
181/// When `vm=false`, masked-off destination elements are left undisturbed.
182///
183/// # Safety
184/// - `vd` register group satisfies alignment for EMUL = 2*LMUL: `vd.to_bits() % dest_group_regs ==
185///   0` and `vd.to_bits() + dest_group_regs <= 32`
186/// - `vs2` register group satisfies alignment for LMUL
187/// - `src` register (if `Vreg`) satisfies the same alignment as `vs2`
188/// - `vl <= dest_group_regs * VLEN.bytes() / double_sew_bytes`
189#[inline(always)]
190#[doc(hidden)]
191#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
192pub unsafe fn execute_vwsll<Reg, Env>(
193    env: &mut Env,
194    vd: VReg,
195    vs2: VReg,
196    src: OpSrc,
197    sew: Vsew,
198    double_sew: Vsew,
199    vm: bool,
200) where
201    Reg: Register,
202    Env: VectorRegistersExt<Reg>,
203    [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
204{
205    let vl = env.vl();
206    let vstart = env.vstart();
207    // `double_sew_bits` is always a power of two (16, 32, or 64); `& (bits - 1)` is equivalent to
208    // `% bits` and avoids a division
209    let double_sew_bits = u64::from(double_sew.bits_width());
210    for i in vstart.range_to(vl) {
211        if !vm && !mask_bit(env.read_vregs().get(VReg::V0), i) {
212            continue;
213        }
214        // SAFETY: `vs2 % group_regs == 0` and `vs2 + group_regs <= 32`; `i < vl`
215        let a = unsafe { env.read_vregs().read_element(vs2, i, sew) };
216        let amount = match src {
217            OpSrc::Vreg(vs1_base) => {
218                // SAFETY: same alignment constraint as vs2; same index bound
219                unsafe { env.read_vregs().read_element(vs1_base, i, sew) }
220            }
221            OpSrc::Scalar(val) => val,
222        };
223        let shift = (amount % double_sew_bits) as u32;
224        // `a` is zero-extended from SEW bits; `shift < double_sew_bits <= 64`, so this never shifts
225        // by >= 64. The caller guarantees SEW <= E32, hence `double_sew_bits <= 64`.
226        let result = a << shift;
227        // SAFETY: `vd % dest_group_regs == 0` and `vd + dest_group_regs <= 32`; `i < vl`;
228        // `write_element_u64` with `double_sew` writes exactly 2*SEW bits of `result`
229        unsafe {
230            env.write_vregs().write_element(vd, i, double_sew, result);
231        }
232    }
233    env.mark_vs_dirty();
234    env.reset_vstart();
235}