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<Memory>(
51 memory: &Memory,
52 address: u64,
53 dst: &mut [u8],
54) -> Result<(), VirtualMemoryError>
55where
56 Memory: VirtualMemory,
57{
58 let Ok(len) = u32::try_from(dst.len()) else {
59 cold_path();
60 return Err(VirtualMemoryError::OutOfBoundsRead { address });
61 };
62 let Some(src) = memory.read_slice(address, len)?.get(..dst.len()) else {
64 cold_path();
65 return Err(VirtualMemoryError::OutOfBoundsRead { address });
66 };
67 for (dst, src) in dst.iter_mut().zip(src) {
70 *dst = *src;
71 }
72 Ok(())
73}
74
75#[inline(always)]
78#[doc(hidden)]
79#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
80pub fn read_bytes<Reg, Memory>(
81 memory: &Memory,
82 address: u64,
83 dst: &mut [u8],
84) -> Result<(), VirtualMemoryError>
85where
86 Reg: Register,
87 Memory: VirtualMemory,
88{
89 let before_wrap = bytes_before_wrap::<Reg>(address, dst.len());
90 let Some((head, tail)) = dst.split_at_mut_checked(before_wrap) else {
91 cold_path();
92 return Err(VirtualMemoryError::OutOfBoundsRead { address });
93 };
94 read_exact(memory, address, head)?;
95 if !tail.is_empty() {
96 cold_path();
97 read_exact(memory, 0, tail)?;
98 }
99 Ok(())
100}
101
102#[inline(always)]
107#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
108pub(crate) fn mask_bit(mask: &[u8], i: u16) -> bool {
109 mask.get(usize::from(i / u8::BITS as u16))
110 .is_some_and(|b| (b >> (i % u8::BITS as u16)) & 1 != 0)
111}
112
113#[inline(always)]
126#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
127pub(in super::super) fn snapshot_mask<Hart>(
128 vregs: &VectorRegisterFile<Hart>,
129 vm: bool,
130) -> [u8; VLENB_USIZE::<Hart>]
131where
132 Hart: VectorHartConfig,
133{
134 if vm {
135 [0xffu8; _]
137 } else {
138 *vregs.get(VReg::V0)
139 }
140}
141
142#[inline(always)]
145#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
146fn read_mem_element<Reg, Memory>(
147 memory: &Memory,
148 addr: u64,
149 eew: Eew,
150) -> Result<[u8; const { usize::from(Eew::MAX_BYTES) }], VirtualMemoryError>
151where
152 Reg: Register,
153 Memory: VirtualMemory,
154{
155 let mut out = [0; _];
156 let element = out
157 .get_mut(..usize::from(eew.bytes_width()))
158 .expect("Element width never exceeds `Eew::MAX_BYTES`; qed");
159 if let Err(err) = read_bytes::<Reg, _>(memory, addr, element) {
160 cold_path();
161 return Err(err);
162 }
163 Ok(out)
164}
165
166#[inline(always)]
178#[doc(hidden)]
179#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
180pub fn execute_unit_stride_load<Reg, Env, Memory>(
181 env: &mut Env,
182 memory: &Memory,
183 vd: VRegSegmentGroup<Env::Hart>,
184 vm: bool,
185 base: u64,
186 fault_only_first: Option<VectorConfig<Env::Hart>>,
187) -> Result<(), ExecutionError<Reg::Type>>
188where
189 Reg: Register,
190 Env: VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
191 Memory: VirtualMemory,
192{
193 let vl = vd.first().vl();
194 let vstart = env.vstart();
195 let eew = vd.first().eew();
196 let elem_bytes = eew.bytes_width();
197 let nf = vd.nf();
198
199 if vm
204 && nf.fields_per_segment() == 1
205 && let Some(count) = u32::from(vl.get()).checked_sub(u32::from(u16::from(vstart)))
206 && count > 0
207 {
208 let first = u16::from(vstart);
209 let addr = effective_address::<Reg>(base, u64::from(first) * u64::from(elem_bytes));
210 let len = count * u32::from(elem_bytes);
212 if let Ok(len_usize) = usize::try_from(len)
214 && !access_wraps::<Reg>(addr, len_usize)
215 && let Ok(bytes) = memory.read_slice(addr, len)
216 && let Some(dst) = env
217 .write_vregs()
218 .elements_bytes_mut(vd.first(), first, count)
219 && bytes.len() >= dst.len()
220 {
221 for (dst, src) in dst.iter_mut().zip(bytes) {
224 *dst = *src;
225 }
226 env.mark_vs_dirty();
227 env.reset_vstart();
228 return Ok(());
229 }
230 cold_path();
231 }
232
233 let segment_stride = u64::from(nf.fields_per_segment()) * u64::from(elem_bytes);
234
235 let mask_buf = snapshot_mask(env.read_vregs(), vm);
236
237 for element in vd.elements(u16::from(vstart)) {
238 let i = element.index();
239 if !vm && !mask_bit(&mask_buf, i) {
240 continue;
241 }
242
243 let elem_base = effective_address::<Reg>(base, u64::from(i) * segment_stride);
244
245 let mut field_buf = [0u64; const { usize::from(Nf::MAX.fields_per_segment()) }];
253
254 for (f, field) in (0..nf.fields_per_segment()).zip(&mut field_buf) {
256 let addr = effective_address::<Reg>(elem_base, u64::from(f * elem_bytes));
257 match read_mem_element::<Reg, _>(memory, addr, eew) {
258 Ok(data) => {
259 *field = u64::from_le_bytes(data);
260 }
261 Err(mem_err) => {
262 cold_path();
263 if let Some(config) = fault_only_first
264 && i > 0
265 {
266 env.set_vector_config(Some(config.with_vl_at_most(Vl::from(i))));
267 env.mark_vs_dirty();
268 env.reset_vstart();
269 return Ok(());
270 }
271 if i > u16::from(vstart) {
272 env.mark_vs_dirty();
274 env.set_vstart(Vstart::from(i));
276 }
277 return Err(ExecutionError::from(mem_err));
278 }
279 }
280 }
281
282 element.write_fields(env.write_vregs(), &field_buf);
284 }
285
286 env.mark_vs_dirty();
287 env.reset_vstart();
288 Ok(())
289}
290
291#[inline(always)]
296#[doc(hidden)]
297#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
298pub fn execute_strided_load<Reg, Env, Memory>(
299 env: &mut Env,
300 memory: &Memory,
301 vd: VRegSegmentGroup<Env::Hart>,
302 vm: bool,
303 base: u64,
304 stride: i64,
305) -> Result<(), ExecutionError<Reg::Type>>
306where
307 Reg: Register,
308 Env: VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
309 Memory: VirtualMemory,
310{
311 let vstart = env.vstart();
312 let eew = vd.first().eew();
313 let elem_bytes = eew.bytes_width();
314
315 let mask_buf = snapshot_mask(env.read_vregs(), vm);
316
317 for element in vd.elements(u16::from(vstart)) {
318 let i = element.index();
319 if !vm && !mask_bit(&mask_buf, i) {
320 continue;
321 }
322
323 let elem_base =
324 effective_address::<Reg>(base, i64::from(i).wrapping_mul(stride).cast_unsigned());
325
326 for field in element.fields() {
327 let f = field.index();
328 let addr = effective_address::<Reg>(elem_base, u64::from(f * elem_bytes));
329 let data = match read_mem_element::<Reg, _>(memory, addr, eew) {
330 Ok(data) => data,
331 Err(mem_err) => {
332 cold_path();
333 if f > 0 || i > u16::from(vstart) {
334 env.mark_vs_dirty();
335 env.set_vstart(Vstart::from(i));
336 }
337 return Err(ExecutionError::from(mem_err));
338 }
339 };
340 field.write(env.write_vregs(), u64::from_le_bytes(data));
341 }
342 }
343
344 env.mark_vs_dirty();
345 env.reset_vstart();
346 Ok(())
347}
348
349#[inline(always)]
358#[doc(hidden)]
359#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
360pub fn execute_indexed_load<Reg, Env, Memory>(
361 env: &mut Env,
362 memory: &Memory,
363 vd: VRegSegmentGroup<Env::Hart>,
364 vs2: VRegGroup<Env::Hart>,
365 vm: bool,
366 base: u64,
367) -> Result<(), ExecutionError<Reg::Type>>
368where
369 Reg: Register,
370 Env: VectorRegistersExt<Hart: HartConfig<Reg = Reg>>,
371 Memory: VirtualMemory,
372{
373 let Some(indexed) = vd.with_index(vs2) else {
374 cold_path();
375 return Ok(());
376 };
377 let vstart = env.vstart();
378 let data_eew = vd.first().eew();
379 let data_elem_bytes = data_eew.bytes_width();
380
381 let mask_buf = snapshot_mask(env.read_vregs(), vm);
382
383 for indexed in indexed.elements(u16::from(vstart)) {
384 let element = indexed.element();
385 let i = element.index();
386 if !vm && !mask_bit(&mask_buf, i) {
387 continue;
388 }
389
390 let offset = indexed.read_index(env.read_vregs());
391 let elem_addr = effective_address::<Reg>(base, offset);
392
393 for field in element.fields() {
394 let f = field.index();
395 let addr =
396 effective_address::<Reg>(elem_addr, u64::from(f) * u64::from(data_elem_bytes));
397 let data = match read_mem_element::<Reg, _>(memory, addr, data_eew) {
398 Ok(data) => data,
399 Err(mem_err) => {
400 cold_path();
401 if f > 0 || i > u16::from(vstart) {
402 env.mark_vs_dirty();
403 env.set_vstart(Vstart::from(i));
404 }
405 return Err(ExecutionError::from(mem_err));
406 }
407 };
408 field.write(env.write_vregs(), u64::from_le_bytes(data));
409 }
410 }
411
412 env.mark_vs_dirty();
413 env.reset_vstart();
414 Ok(())
415}