ab_riscv_interpreter/v/zvexx/load/
zvexx_load_helpers.rs1#[cfg(test)]
4mod tests;
5
6use crate::v::vector_config::VectorConfig;
7use crate::v::vector_registers::{VLENB_USIZE, VectorRegisterFile, VectorRegistersExt};
8pub use crate::v::vector_registers::{VRegGroup, VRegSegmentGroup};
9use crate::{ExecutionError, VirtualMemory, VirtualMemoryError};
10use ab_riscv_primitives::prelude::*;
11use core::hint::cold_path;
12
13#[inline(always)]
16#[doc(hidden)]
17#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
18pub fn effective_address<Reg>(base: u64, offset: u64) -> u64
19where
20 Reg: Register,
21{
22 Reg::Type::truncate_from_u64(base.wrapping_add(offset)).as_u64()
23}
24
25#[inline(always)]
28#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
29pub(crate) fn bytes_before_wrap<Reg>(address: u64, len: usize) -> usize
30where
31 Reg: Register,
32{
33 let remaining = (1u128 << Reg::XLEN).saturating_sub(u128::from(address));
34 usize::try_from(remaining).map_or(len, |remaining| remaining.min(len))
35}
36
37#[inline(always)]
39#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
40pub(crate) fn access_wraps<Reg>(address: u64, len: usize) -> bool
41where
42 Reg: Register,
43{
44 bytes_before_wrap::<Reg>(address, len) < len
45}
46
47#[inline(always)]
49#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
50fn read_exact(
51 memory: &impl VirtualMemory,
52 address: u64,
53 dst: &mut [u8],
54) -> Result<(), VirtualMemoryError> {
55 let Ok(len) = u32::try_from(dst.len()) else {
56 cold_path();
57 return Err(VirtualMemoryError::OutOfBoundsRead { address });
58 };
59 let Some(src) = memory.read_slice(address, len)?.get(..dst.len()) else {
61 cold_path();
62 return Err(VirtualMemoryError::OutOfBoundsRead { address });
63 };
64 for (dst, src) in dst.iter_mut().zip(src) {
67 *dst = *src;
68 }
69 Ok(())
70}
71
72#[inline(always)]
75#[doc(hidden)]
76#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
77pub fn read_bytes<Reg, Memory>(
78 memory: &Memory,
79 address: u64,
80 dst: &mut [u8],
81) -> Result<(), VirtualMemoryError>
82where
83 Reg: Register,
84 Memory: VirtualMemory,
85{
86 let before_wrap = bytes_before_wrap::<Reg>(address, dst.len());
87 let Some((head, tail)) = dst.split_at_mut_checked(before_wrap) else {
88 cold_path();
89 return Err(VirtualMemoryError::OutOfBoundsRead { address });
90 };
91 read_exact(memory, address, head)?;
92 if !tail.is_empty() {
93 cold_path();
94 read_exact(memory, 0, tail)?;
95 }
96 Ok(())
97}
98
99#[inline(always)]
104#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
105pub(crate) fn mask_bit(mask: &[u8], i: u16) -> bool {
106 mask.get(usize::from(i / u8::BITS as u16))
107 .is_some_and(|b| (b >> (i % u8::BITS as u16)) & 1 != 0)
108}
109
110#[inline(always)]
123#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
124pub(in super::super) fn snapshot_mask<Hart>(
125 vregs: &VectorRegisterFile<Hart>,
126 vm: bool,
127) -> [u8; VLENB_USIZE::<Hart>]
128where
129 Hart: VectorHartConfig,
130{
131 if vm {
132 [0xffu8; _]
134 } else {
135 *vregs.get(VReg::V0)
136 }
137}
138
139#[inline(always)]
142#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
143fn read_mem_element<Reg>(
144 memory: &impl VirtualMemory,
145 addr: u64,
146 eew: Eew,
147) -> Result<[u8; const { usize::from(Eew::MAX_BYTES) }], VirtualMemoryError>
148where
149 Reg: Register,
150{
151 let mut out = [0; _];
152 let element = out
153 .get_mut(..usize::from(eew.bytes_width()))
154 .expect("Element width never exceeds `Eew::MAX_BYTES`; qed");
155 if let Err(err) = read_bytes::<Reg, _>(memory, addr, element) {
156 cold_path();
157 return Err(err);
158 }
159 Ok(out)
160}
161
162#[inline(always)]
174#[doc(hidden)]
175#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
176pub fn execute_unit_stride_load<Reg, Env, Memory>(
177 env: &mut Env,
178 memory: &Memory,
179 vd: VRegSegmentGroup<Env::Hart>,
180 vm: bool,
181 base: u64,
182 fault_only_first: Option<VectorConfig<Env::Hart>>,
183) -> Result<(), ExecutionError<Reg::Type>>
184where
185 Reg: Register,
186 Env: VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
187 Memory: VirtualMemory,
188{
189 let vl = vd.first().vl();
190 let vstart = env.vstart();
191 let eew = vd.first().eew();
192 let elem_bytes = eew.bytes_width();
193 let nf = vd.nf();
194
195 if vm
200 && nf.fields_per_segment() == 1
201 && let Some(count) = u32::from(vl.get()).checked_sub(u32::from(u16::from(vstart)))
202 && count > 0
203 {
204 let first = u16::from(vstart);
205 let addr = effective_address::<Reg>(base, u64::from(first) * u64::from(elem_bytes));
206 let len = count * u32::from(elem_bytes);
208 if let Ok(len_usize) = usize::try_from(len)
210 && !access_wraps::<Reg>(addr, len_usize)
211 && let Ok(bytes) = memory.read_slice(addr, len)
212 && let Some(dst) = env
213 .write_vregs()
214 .elements_bytes_mut(vd.first(), first, count)
215 && bytes.len() >= dst.len()
216 {
217 for (dst, src) in dst.iter_mut().zip(bytes) {
220 *dst = *src;
221 }
222 env.mark_vs_dirty();
223 env.reset_vstart();
224 return Ok(());
225 }
226 cold_path();
227 }
228
229 let segment_stride = u64::from(nf.fields_per_segment()) * u64::from(elem_bytes);
230
231 let mask_buf = snapshot_mask(env.read_vregs(), vm);
232
233 for element in vd.elements(u16::from(vstart)) {
234 let i = element.index();
235 if !vm && !mask_bit(&mask_buf, i) {
236 continue;
237 }
238
239 let elem_base = effective_address::<Reg>(base, u64::from(i) * segment_stride);
240
241 let mut field_buf = [0u64; const { usize::from(Nf::MAX.fields_per_segment()) }];
249
250 for (f, field) in (0..nf.fields_per_segment()).zip(&mut field_buf) {
252 let addr = effective_address::<Reg>(elem_base, u64::from(f * elem_bytes));
253 match read_mem_element::<Reg>(memory, addr, eew) {
254 Ok(data) => {
255 *field = u64::from_le_bytes(data);
256 }
257 Err(mem_err) => {
258 cold_path();
259 if let Some(config) = fault_only_first
260 && i > 0
261 {
262 env.set_vector_config(Some(config.with_vl_at_most(Vl::from(i))));
263 env.mark_vs_dirty();
264 env.reset_vstart();
265 return Ok(());
266 }
267 if i > u16::from(vstart) {
268 env.mark_vs_dirty();
270 env.set_vstart(Vstart::from(i));
272 }
273 return Err(ExecutionError::from(mem_err));
274 }
275 }
276 }
277
278 element.write_fields(env.write_vregs(), &field_buf);
280 }
281
282 env.mark_vs_dirty();
283 env.reset_vstart();
284 Ok(())
285}
286
287#[inline(always)]
292#[doc(hidden)]
293#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
294pub fn execute_strided_load<Reg, Env, Memory>(
295 env: &mut Env,
296 memory: &Memory,
297 vd: VRegSegmentGroup<Env::Hart>,
298 vm: bool,
299 base: u64,
300 stride: i64,
301) -> Result<(), ExecutionError<Reg::Type>>
302where
303 Reg: Register,
304 Env: VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
305 Memory: VirtualMemory,
306{
307 let vstart = env.vstart();
308 let eew = vd.first().eew();
309 let elem_bytes = eew.bytes_width();
310
311 let mask_buf = snapshot_mask(env.read_vregs(), vm);
312
313 for element in vd.elements(u16::from(vstart)) {
314 let i = element.index();
315 if !vm && !mask_bit(&mask_buf, i) {
316 continue;
317 }
318
319 let elem_base =
320 effective_address::<Reg>(base, i64::from(i).wrapping_mul(stride).cast_unsigned());
321
322 for field in element.fields() {
323 let f = field.index();
324 let addr = effective_address::<Reg>(elem_base, u64::from(f * elem_bytes));
325 let data = match read_mem_element::<Reg>(memory, addr, eew) {
326 Ok(data) => data,
327 Err(mem_err) => {
328 cold_path();
329 if f > 0 || i > u16::from(vstart) {
330 env.mark_vs_dirty();
331 env.set_vstart(Vstart::from(i));
332 }
333 return Err(ExecutionError::from(mem_err));
334 }
335 };
336 field.write(env.write_vregs(), u64::from_le_bytes(data));
337 }
338 }
339
340 env.mark_vs_dirty();
341 env.reset_vstart();
342 Ok(())
343}
344
345#[inline(always)]
354#[doc(hidden)]
355#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
356pub fn execute_indexed_load<Reg, Env, Memory>(
357 env: &mut Env,
358 memory: &Memory,
359 vd: VRegSegmentGroup<Env::Hart>,
360 vs2: VRegGroup<Env::Hart>,
361 vm: bool,
362 base: u64,
363) -> Result<(), ExecutionError<Reg::Type>>
364where
365 Reg: Register,
366 Env: VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
367 Memory: VirtualMemory,
368{
369 let Some(indexed) = vd.with_index(vs2) else {
370 cold_path();
371 return Ok(());
372 };
373 let vstart = env.vstart();
374 let data_eew = vd.first().eew();
375 let data_elem_bytes = data_eew.bytes_width();
376
377 let mask_buf = snapshot_mask(env.read_vregs(), vm);
378
379 for indexed in indexed.elements(u16::from(vstart)) {
380 let element = indexed.element();
381 let i = element.index();
382 if !vm && !mask_bit(&mask_buf, i) {
383 continue;
384 }
385
386 let offset = indexed.read_index(env.read_vregs());
387 let elem_addr = effective_address::<Reg>(base, offset);
388
389 for field in element.fields() {
390 let f = field.index();
391 let addr =
392 effective_address::<Reg>(elem_addr, u64::from(f) * u64::from(data_elem_bytes));
393 let data = match read_mem_element::<Reg>(memory, addr, data_eew) {
394 Ok(data) => data,
395 Err(mem_err) => {
396 cold_path();
397 if f > 0 || i > u16::from(vstart) {
398 env.mark_vs_dirty();
399 env.set_vstart(Vstart::from(i));
400 }
401 return Err(ExecutionError::from(mem_err));
402 }
403 };
404 field.write(env.write_vregs(), u64::from_le_bytes(data));
405 }
406 }
407
408 env.mark_vs_dirty();
409 env.reset_vstart();
410 Ok(())
411}