ab_riscv_interpreter/v/zvexx/arith/
zvexx_arith_helpers.rs1use crate::v::vector_config::BoundedVl;
4use crate::v::vector_registers::{VRegGroup, VectorRegisterFile, VectorRegistersExt};
5use crate::v::zvexx::load::zvexx_load_helpers::{mask_bit, snapshot_mask};
6use crate::v::zvexx::zvexx_helpers::INSTRUCTION_SIZE;
7use crate::{ExecutionError, PackedAddress, ProgramCounter};
8use ab_riscv_primitives::prelude::*;
9use core::hint::cold_path;
10
11const SEW_EEW<const SEW: Vsew>: Eew = SEW.as_eew();
13
14#[inline(always)]
29#[doc(hidden)]
30#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
31pub fn check_mask_dest_overlap<Reg, Env, Memory, PC>(
32 program_counter: &PC,
33 vd: VReg,
34 src: VRegGroup<Env::Hart>,
35) -> Result<(), ExecutionError<Reg::Type>>
36where
37 Reg: Register,
38 Env: VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
39 PC: ProgramCounter<Reg::Type, Memory>,
40{
41 if src.contains(vd) && vd != src.base() {
42 cold_path();
43 return Err(ExecutionError::IllegalInstruction {
44 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
45 });
46 }
47 Ok(())
48}
49
50#[inline(always)]
58#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
59pub(in super::super) fn write_mask_bit<Hart>(
60 vregs: &mut VectorRegisterFile<Hart>,
61 vd: VReg,
62 elem_i: u16,
63 result: bool,
64) -> Option<()>
65where
66 Hart: VectorHartConfig,
67{
68 let byte_idx = usize::from(elem_i / u8::BITS as u16);
69 let bit_idx = elem_i % u8::BITS as u16;
70 let byte = vregs.get_mut(vd).get_mut(byte_idx)?;
71 if result {
72 *byte |= 1 << bit_idx;
73 } else {
74 *byte &= !(1 << bit_idx);
75 }
76 Some(())
77}
78
79#[derive(Debug)]
81#[doc(hidden)]
82pub enum OpSrc<V> {
83 Vreg(V),
85 Scalar(u64),
87}
88
89impl<Hart> OpSrc<VRegGroup<Hart>>
90where
91 Hart: VectorHartConfig,
92{
93 #[inline(always)]
96 #[doc(hidden)]
97 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
98 pub fn with_same_vl(self, vl: BoundedVl<Hart>) -> Option<SameVlOpSrc<Hart>> {
99 match self {
100 Self::Vreg(group) => Some(SameVlOpSrc {
101 vreg: Some(group.with_same_vl(vl)?),
102 scalar: 0,
103 }),
104 Self::Scalar(scalar) => Some(SameVlOpSrc { vreg: None, scalar }),
105 }
106 }
107}
108
109#[derive(Debug, Clone, Copy)]
114#[doc(hidden)]
115pub struct SameVlOpSrc<Hart>
116where
117 Hart: VectorHartConfig,
118{
119 pub vreg: Option<VRegGroup<Hart>>,
121 pub scalar: u64,
123}
124
125#[inline(always)]
133#[doc(hidden)]
134#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
135pub fn execute_arith_op<Reg, Env, F>(
136 env: &mut Env,
137 vd: VRegGroup<Env::Hart>,
138 vs2: VRegGroup<Env::Hart>,
139 src: OpSrc<VRegGroup<Env::Hart>>,
140 vm: bool,
141 sew: Vsew,
142 op: F,
143) where
144 Reg: Register,
145 Env: VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
146 F: Fn(u64, u64, Vsew) -> u64,
147{
148 match sew {
151 Vsew::E8 => {
152 execute_arith_op_const::<{ Vsew::E8 }, _, _, _>(env, vd, vs2, src, vm, op);
153 }
154 Vsew::E16 => {
155 execute_arith_op_const::<{ Vsew::E16 }, _, _, _>(env, vd, vs2, src, vm, op);
156 }
157 Vsew::E32 => {
158 execute_arith_op_const::<{ Vsew::E32 }, _, _, _>(env, vd, vs2, src, vm, op);
159 }
160 Vsew::E64 => {
161 execute_arith_op_const::<{ Vsew::E64 }, _, _, _>(env, vd, vs2, src, vm, op);
162 }
163 }
164}
165
166#[inline(always)]
168#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
169fn execute_arith_op_const<const SEW: Vsew, Reg, Env, F>(
170 env: &mut Env,
171 vd: VRegGroup<Env::Hart>,
172 vs2: VRegGroup<Env::Hart>,
173 src: OpSrc<VRegGroup<Env::Hart>>,
174 vm: bool,
175 op: F,
176) where
177 Reg: Register,
178 Env: VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
179 F: Fn(u64, u64, Vsew) -> u64,
180{
181 let vl = vd.vl();
182 let src_eew_matches = match src {
183 OpSrc::Vreg(vs1) => vs1.eew() == SEW_EEW::<SEW>,
184 OpSrc::Scalar(_) => true,
185 };
186 if vd.eew() != SEW_EEW::<SEW> || vs2.eew() != SEW_EEW::<SEW> || !src_eew_matches {
187 cold_path();
188 return;
189 }
190 let (Some(vs2), Some(src)) = (vs2.with_same_vl(vl), src.with_same_vl(vl)) else {
191 cold_path();
192 return;
193 };
194
195 let vregs = env.write_vregs();
196
197 for i in vl.indices() {
198 if !vm && !mask_bit(vregs.get(VReg::V0), i) {
201 continue;
202 }
203
204 let a = vregs
205 .read_const::<{ SEW_EEW::<SEW> }>(vs2, i)
206 .expect("`vs2` has element width `SEW` and `vl` checked above; qed");
207
208 let b = match src.vreg {
209 Some(vs1) => vregs
210 .read_const::<{ SEW_EEW::<SEW> }>(vs1, i)
211 .expect("`vs1` has element width `SEW` and `vl` checked above; qed"),
212 None => src.scalar,
213 };
214
215 let result = op(a, b, SEW);
216
217 vregs
218 .write_const::<{ SEW_EEW::<SEW> }>(vd, i, result)
219 .expect("`vd` has element width `SEW` and `vl` checked above; qed");
220 }
221
222 env.mark_vs_dirty();
223}
224
225#[inline(always)]
236#[doc(hidden)]
237#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
238pub fn execute_compare_op<Reg, Env, F>(
239 env: &mut Env,
240 vd: VReg,
241 vs2: VRegGroup<Env::Hart>,
242 src: OpSrc<VRegGroup<Env::Hart>>,
243 vm: bool,
244 sew: Vsew,
245 op: F,
246) where
247 Reg: Register,
248 Env: VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
249 F: Fn(u64, u64, Vsew) -> bool,
250{
251 let vl = vs2.vl();
252 let Some(src) = src.with_same_vl(vl) else {
253 cold_path();
254 return;
255 };
256
257 let mask_buf = snapshot_mask(env.read_vregs(), vm);
258
259 for i in vl.indices() {
260 if !mask_bit(&mask_buf, i) {
263 continue;
264 }
265
266 let a = env
267 .read_vregs()
268 .read(vs2, i)
269 .expect("`i < vl` of `vs2`; qed");
270
271 let b = match src.vreg {
272 Some(vs1) => env
273 .read_vregs()
274 .read(vs1, i)
275 .expect("`vs1` has the same `vl` as `vs2`, checked above; qed"),
276 None => src.scalar,
277 };
278
279 let result = op(a, b, sew);
280
281 write_mask_bit(env.write_vregs(), vd, i, result)
282 .expect("`i < vl <= VLEN`, so the mask bit is within `vd`; qed");
283 }
284
285 env.mark_vs_dirty();
286}
287
288#[inline(always)]
290#[doc(hidden)]
291#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
292pub fn sign_extend(val: u64, sew: Vsew) -> i64 {
293 let shift = u64::BITS - u32::from(sew.bits_width());
294 (val.cast_signed() << shift) >> shift
295}
296
297#[inline(always)]
302#[doc(hidden)]
303#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
304pub fn sew_mask(sew: Vsew) -> u64 {
305 if u32::from(sew.bits_width()) == u64::BITS {
306 u64::MAX
307 } else {
308 (1u64 << sew.bits_width()) - 1
309 }
310}