Skip to main content

ab_riscv_interpreter/zvbb/zvkb/
zvkb_helpers.rs

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