Skip to main content

softgpu_functional/
memory.rs

1//! Host-backed global memory arena (SoftGPU functional address space).
2
3use crate::error::{FunctionalError, Result};
4use crate::ir::TypeId;
5
6/// Linear global memory. SoftGPU functional "pointers" are byte offsets into this arena.
7#[derive(Debug, Clone)]
8pub struct GlobalArena {
9    bytes: Vec<u8>,
10}
11
12impl GlobalArena {
13    pub fn new(len: usize) -> Self {
14        Self {
15            bytes: vec![0u8; len],
16        }
17    }
18
19    pub fn from_bytes(bytes: Vec<u8>) -> Self {
20        Self { bytes }
21    }
22
23    pub fn len(&self) -> usize {
24        self.bytes.len()
25    }
26
27    pub fn is_empty(&self) -> bool {
28        self.bytes.is_empty()
29    }
30
31    pub fn as_slice(&self) -> &[u8] {
32        &self.bytes
33    }
34
35    pub fn as_mut_slice(&mut self) -> &mut [u8] {
36        &mut self.bytes
37    }
38
39    pub fn fill_i32_ramp(&mut self, start: i32) {
40        let mut v = start;
41        for chunk in self.bytes.chunks_exact_mut(4) {
42            chunk.copy_from_slice(&v.to_le_bytes());
43            v = v.wrapping_add(1);
44        }
45    }
46
47    pub fn read_i32_slice(&self) -> Result<Vec<i32>> {
48        if self.bytes.len() % 4 != 0 {
49            return Err(FunctionalError::Validation {
50                detail: "arena length not multiple of 4".into(),
51            });
52        }
53        Ok(self
54            .bytes
55            .chunks_exact(4)
56            .map(|c| i32::from_le_bytes([c[0], c[1], c[2], c[3]]))
57            .collect())
58    }
59
60    pub fn load(&self, addr: u64, ty: TypeId) -> Result<i64> {
61        let size = ty_size(ty);
62        let start = usize::try_from(addr).map_err(|_| FunctionalError::Bounds {
63            addr,
64            size,
65            arena_len: self.bytes.len(),
66        })?;
67        let end = start.checked_add(size).ok_or(FunctionalError::Bounds {
68            addr,
69            size,
70            arena_len: self.bytes.len(),
71        })?;
72        if end > self.bytes.len() {
73            return Err(FunctionalError::Bounds {
74                addr,
75                size,
76                arena_len: self.bytes.len(),
77            });
78        }
79        let s = &self.bytes[start..end];
80        Ok(match ty {
81            TypeId::I32 => i32::from_le_bytes([s[0], s[1], s[2], s[3]]) as i64,
82            TypeId::U32 => u32::from_le_bytes([s[0], s[1], s[2], s[3]]) as i64,
83            TypeId::U64 => {
84                u64::from_le_bytes([s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]]) as i64
85            }
86        })
87    }
88
89    pub fn store(&mut self, addr: u64, ty: TypeId, value: i64) -> Result<()> {
90        let size = ty_size(ty);
91        let start = usize::try_from(addr).map_err(|_| FunctionalError::Bounds {
92            addr,
93            size,
94            arena_len: self.bytes.len(),
95        })?;
96        let end = start.checked_add(size).ok_or(FunctionalError::Bounds {
97            addr,
98            size,
99            arena_len: self.bytes.len(),
100        })?;
101        if end > self.bytes.len() {
102            return Err(FunctionalError::Bounds {
103                addr,
104                size,
105                arena_len: self.bytes.len(),
106            });
107        }
108        match ty {
109            TypeId::I32 => {
110                self.bytes[start..end].copy_from_slice(&(value as i32).to_le_bytes());
111            }
112            TypeId::U32 => {
113                self.bytes[start..end].copy_from_slice(&(value as u32).to_le_bytes());
114            }
115            TypeId::U64 => {
116                self.bytes[start..end].copy_from_slice(&(value as u64).to_le_bytes());
117            }
118        }
119        Ok(())
120    }
121}
122
123pub fn ty_size(ty: TypeId) -> usize {
124    match ty {
125        TypeId::I32 | TypeId::U32 => 4,
126        TypeId::U64 => 8,
127    }
128}
129
130pub fn kernarg_load(kernarg: &[u8], offset: u32, ty: TypeId) -> Result<i64> {
131    let size = ty_size(ty);
132    let start = offset as usize;
133    let end = start.checked_add(size).ok_or(FunctionalError::Bounds {
134        addr: u64::from(offset),
135        size,
136        arena_len: kernarg.len(),
137    })?;
138    if end > kernarg.len() {
139        return Err(FunctionalError::Bounds {
140            addr: u64::from(offset),
141            size,
142            arena_len: kernarg.len(),
143        });
144    }
145    let s = &kernarg[start..end];
146    Ok(match ty {
147        TypeId::I32 => i32::from_le_bytes([s[0], s[1], s[2], s[3]]) as i64,
148        TypeId::U32 => u32::from_le_bytes([s[0], s[1], s[2], s[3]]) as i64,
149        TypeId::U64 => u64::from_le_bytes([s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]]) as i64,
150    })
151}