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