Skip to main content

ab_riscv_interpreter/zvbb/zvkb/
zvkb_helpers.rs

1//! Opaque helpers for Zvkb 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::arith::zvexx_arith_helpers::{read_element_u64, sew_mask, write_element_u64};
6use crate::v::zvexx::load::zvexx_load_helpers::mask_bit;
7use ab_riscv_primitives::prelude::*;
8
9/// Execute element-wise and-not over `vstart..vl`, writing SEW-wide results into `vd`.
10///
11/// For each active element i: `vd[i] = ~src[i] & vs2[i]`.
12///
13/// When `vm=true` all elements are active. When `vm=false` the mask register `v0` gates each
14/// element; masked-off elements are left undisturbed (undisturbed policy).
15///
16/// # Safety
17/// - `vd.to_bits() % group_regs == 0` and `vd.to_bits() + group_regs <= 32`
18/// - `vs2.to_bits() % group_regs == 0` and `vs2.to_bits() + group_regs <= 32`
19/// - `src` register (if `Vreg`) satisfies the same alignment as `vs2`
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_vandn<Reg, Env>(
25    env: &mut Env,
26    vd: VReg,
27    vs2: VReg,
28    src: OpSrc,
29    sew: Vsew,
30    vm: bool,
31) where
32    Reg: Register,
33    Env: VectorRegistersExt<Reg>,
34    [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
35{
36    let vl = env.vl();
37    let vstart = env.vstart();
38    for i in vstart.range_to(vl) {
39        if !vm && !mask_bit(env.read_vregs().get(VReg::V0), i) {
40            continue;
41        }
42        // SAFETY: `vs2 % group_regs == 0` and `vs2 + group_regs <= 32` (caller precondition);
43        // `i < vl <= group_regs * elems_per_reg`, so
44        // `vs2 + i / elems_per_reg < vs2 + group_regs <= 32`
45        let a = unsafe { read_element_u64(env.read_vregs(), vs2, i, sew) };
46        let b = match src {
47            OpSrc::Vreg(vs1_base) => {
48                // SAFETY: caller verified that the vs1 register group satisfies the same alignment
49                // constraint as vs2; the index argument is identical, so the same bound holds
50                unsafe { read_element_u64(env.read_vregs(), vs1_base, i, sew) }
51            }
52            OpSrc::Scalar(val) => val,
53        };
54        // `a` is zero-extended to SEW bits by `read_element_u64`; `!b` may have high bits set, but
55        // AND with `a` (whose upper bits are zero) zeros them out naturally
56        let result = !b & a;
57        // SAFETY: `vd % group_regs == 0` and `vd + group_regs <= 32` (caller precondition);
58        // `i < vl <= group_regs * elems_per_reg`, so
59        // `vd + i / elems_per_reg < vd + group_regs <= 32`
60        unsafe {
61            write_element_u64(env.write_vregs(), vd, i, sew, result);
62        }
63    }
64    env.mark_vs_dirty();
65    env.reset_vstart();
66}
67
68/// Execute element-wise bit-reversal within bytes over `vstart..vl`, writing results into `vd`.
69///
70/// For each active element i: the bits within each byte of `vs2[i]` are reversed. The byte order
71/// within the element is preserved; only the bit order within each byte changes.
72///
73/// When `vm=false`, masked-off elements are left undisturbed.
74///
75/// # Safety
76/// Same register-group constraints as [`execute_vandn`], minus the `src` constraint.
77#[inline(always)]
78#[doc(hidden)]
79#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
80pub unsafe fn execute_vbrev8<Reg, Env>(env: &mut Env, vd: VReg, vs2: VReg, sew: Vsew, vm: bool)
81where
82    Reg: Register,
83    Env: VectorRegistersExt<Reg>,
84    [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
85{
86    let vl = env.vl();
87    let vstart = env.vstart();
88    let sew_bytes = u32::from(sew.bytes_width());
89    for i in vstart.range_to(vl) {
90        if !vm && !mask_bit(env.read_vregs().get(VReg::V0), i) {
91            continue;
92        }
93        // SAFETY: `vs2 % group_regs == 0` and `vs2 + group_regs <= 32`; `i < vl`
94        let elem = unsafe { read_element_u64(env.read_vregs(), vs2, i, sew) };
95        // Decompose into bytes (LE = index 0 is least-significant), reverse bits within each active
96        // byte, then reassemble; bytes beyond sew_bytes are already zero because `read_element_u64`
97        // zero-extends to u64
98        let mut bytes = elem.to_le_bytes();
99        for byte in &mut bytes[..sew_bytes as usize] {
100            *byte = byte.reverse_bits();
101        }
102        let result = u64::from_le_bytes(bytes);
103        // SAFETY: `vd % group_regs == 0` and `vd + group_regs <= 32`; `i < vl`
104        unsafe {
105            write_element_u64(env.write_vregs(), vd, i, sew, result);
106        }
107    }
108    env.mark_vs_dirty();
109    env.reset_vstart();
110}
111
112/// Execute element-wise byte reversal over `vstart..vl`, writing results into `vd`.
113///
114/// For each active element i: the bytes within `vs2[i]` are reversed.
115///
116/// When `vm=false`, masked-off elements are left undisturbed.
117///
118/// # Safety
119/// Same register-group constraints as [`execute_vandn`], minus the `src` constraint.
120#[inline(always)]
121#[doc(hidden)]
122#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
123pub unsafe fn execute_vrev8<Reg, Env>(env: &mut Env, vd: VReg, vs2: VReg, sew: Vsew, vm: bool)
124where
125    Reg: Register,
126    Env: VectorRegistersExt<Reg>,
127    [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
128{
129    let vl = env.vl();
130    let vstart = env.vstart();
131    let sew_bytes = u32::from(sew.bytes_width());
132    for i in vstart.range_to(vl) {
133        if !vm && !mask_bit(env.read_vregs().get(VReg::V0), i) {
134            continue;
135        }
136        // SAFETY: `vs2 % group_regs == 0` and `vs2 + group_regs <= 32`; `i < vl`
137        let elem = unsafe { read_element_u64(env.read_vregs(), vs2, i, sew) };
138        // Reverse the byte slice covering exactly the SEW-wide element; bytes beyond sew_bytes are
139        // zero (from zero-extension) and are left untouched
140        let mut bytes = elem.to_le_bytes();
141        bytes[..sew_bytes as usize].reverse();
142        let result = u64::from_le_bytes(bytes);
143        // SAFETY: `vd % group_regs == 0` and `vd + group_regs <= 32`; `i < vl`
144        unsafe {
145            write_element_u64(env.write_vregs(), vd, i, sew, result);
146        }
147    }
148    env.mark_vs_dirty();
149    env.reset_vstart();
150}
151
152/// Execute element-wise rotate-left over `vstart..vl`, writing SEW-wide results into `vd`.
153///
154/// For each active element i: `vd[i] = rotate_left(vs2[i], src[i] % SEW)`.
155///
156/// When `vm=false`, masked-off elements are left undisturbed.
157///
158/// # Safety
159/// Same register-group constraints as [`execute_vandn`].
160#[inline(always)]
161#[doc(hidden)]
162#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
163pub unsafe fn execute_vrol<Reg, Env>(
164    env: &mut Env,
165    vd: VReg,
166    vs2: VReg,
167    src: OpSrc,
168    sew: Vsew,
169    vm: bool,
170) where
171    Reg: Register,
172    Env: VectorRegistersExt<Reg>,
173    [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
174{
175    let vl = env.vl();
176    let vstart = env.vstart();
177    let sew_bits = u64::from(sew.bits_width());
178    let mask = sew_mask(sew);
179    for i in vstart.range_to(vl) {
180        if !vm && !mask_bit(env.read_vregs().get(VReg::V0), i) {
181            continue;
182        }
183        // SAFETY: `vs2 % group_regs == 0` and `vs2 + group_regs <= 32`; `i < vl`
184        let a = unsafe { read_element_u64(env.read_vregs(), vs2, i, sew) };
185        let amount = match src {
186            OpSrc::Vreg(vs1_base) => {
187                // SAFETY: same alignment constraint as vs2; same index bound
188                unsafe { read_element_u64(env.read_vregs(), vs1_base, i, sew) }
189            }
190            OpSrc::Scalar(val) => val,
191        };
192        // `shift < sew_bits`, so `a << shift` never shifts by >= 64 and is safe.
193        // When shift == 0, `sew_bits - shift` == sew_bits; `unbounded_shr` defines
194        // shifts >= bit-width as 0, which is correct: a zero rotation contributes no low bits.
195        let shift = (amount % sew_bits) as u32;
196        let hi = (a << shift) & mask;
197        let lo = a.unbounded_shr(sew_bits as u32 - shift);
198        let result = hi | lo;
199        // SAFETY: `vd % group_regs == 0` and `vd + group_regs <= 32`; `i < vl`
200        unsafe {
201            write_element_u64(env.write_vregs(), vd, i, sew, result);
202        }
203    }
204    env.mark_vs_dirty();
205    env.reset_vstart();
206}
207
208/// Execute element-wise rotate-right over `vstart..vl`, writing SEW-wide results into `vd`.
209///
210/// For each active element i: `vd[i] = rotate_right(vs2[i], src[i] % SEW)`.
211///
212/// Pass `vm=true` for `vror.vi` (bit[25] is consumed as imm[5]; no mask bit exists).
213///
214/// When `vm=false`, masked-off elements are left undisturbed.
215///
216/// # Safety
217/// Same register-group constraints as [`execute_vandn`].
218#[inline(always)]
219#[doc(hidden)]
220#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
221pub unsafe fn execute_vror<Reg, Env>(
222    env: &mut Env,
223    vd: VReg,
224    vs2: VReg,
225    src: OpSrc,
226    sew: Vsew,
227    vm: bool,
228) where
229    Reg: Register,
230    Env: VectorRegistersExt<Reg>,
231    [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
232{
233    let vl = env.vl();
234    let vstart = env.vstart();
235    let sew_bits = u64::from(sew.bits_width());
236    let mask = sew_mask(sew);
237    for i in vstart.range_to(vl) {
238        if !vm && !mask_bit(env.read_vregs().get(VReg::V0), i) {
239            continue;
240        }
241        // SAFETY: `vs2 % group_regs == 0` and `vs2 + group_regs <= 32`; `i < vl`
242        let a = unsafe { read_element_u64(env.read_vregs(), vs2, i, sew) };
243        let amount = match src {
244            OpSrc::Vreg(vs1_base) => {
245                // SAFETY: same alignment constraint as vs2; same index bound
246                unsafe { read_element_u64(env.read_vregs(), vs1_base, i, sew) }
247            }
248            OpSrc::Scalar(val) => val,
249        };
250        // `shift < sew_bits`, so `a >> shift` never shifts by >= 64 and is safe.
251        // When shift == 0, `sew_bits - shift` == sew_bits; `unbounded_shl` defines
252        // shifts >= bit-width as 0, which is correct: a zero rotation contributes no high bits.
253        let shift = (amount % sew_bits) as u32;
254        let lo = a >> shift;
255        let hi = a.unbounded_shl(sew_bits as u32 - shift) & mask;
256        let result = lo | hi;
257        // SAFETY: `vd % group_regs == 0` and `vd + group_regs <= 32`; `i < vl`
258        unsafe {
259            write_element_u64(env.write_vregs(), vd, i, sew, result);
260        }
261    }
262    env.mark_vs_dirty();
263    env.reset_vstart();
264}