1use crate::state::MemoryError;
36use cranelift_codegen::data_value::DataValue;
37use cranelift_codegen::ir::{types, Type};
38
39#[derive(Debug, Copy, Clone, PartialEq)]
40pub enum AddressSize {
41 _32,
42 _64,
43}
44
45impl AddressSize {
46 pub fn bits(&self) -> u64 {
47 match self {
48 AddressSize::_64 => 64,
49 AddressSize::_32 => 32,
50 }
51 }
52}
53
54impl TryFrom<Type> for AddressSize {
55 type Error = MemoryError;
56
57 fn try_from(ty: Type) -> Result<Self, Self::Error> {
58 match ty {
59 types::I64 => Ok(AddressSize::_64),
60 types::I32 => Ok(AddressSize::_32),
61 _ => Err(MemoryError::InvalidAddressType(ty)),
62 }
63 }
64}
65
66#[derive(Debug, Copy, Clone, PartialEq)]
68pub enum AddressRegion {
69 Stack,
70 Function,
71 Table,
72 GlobalValue,
73}
74
75impl AddressRegion {
76 pub fn decode(bits: u64) -> Self {
77 assert!(bits < 4);
78 match bits {
79 0 => AddressRegion::Stack,
80 1 => AddressRegion::Function,
81 2 => AddressRegion::Table,
82 3 => AddressRegion::GlobalValue,
83 _ => unreachable!(),
84 }
85 }
86
87 pub fn encode(self) -> u64 {
88 match self {
89 AddressRegion::Stack => 0,
90 AddressRegion::Function => 1,
91 AddressRegion::Table => 2,
92 AddressRegion::GlobalValue => 3,
93 }
94 }
95}
96
97#[derive(Debug, Clone, PartialEq)]
98pub struct Address {
99 pub size: AddressSize,
100 pub region: AddressRegion,
101 pub entry: u64,
102 pub offset: u64,
103}
104
105impl Address {
106 pub fn from_parts(
107 size: AddressSize,
108 region: AddressRegion,
109 entry: u64,
110 offset: u64,
111 ) -> Result<Self, MemoryError> {
112 let entry_bits = Address::entry_bits(size, region);
113 let offset_bits = Address::offset_bits(size, region);
114
115 let max_entries = (1 << entry_bits) - 1;
116 let max_offset = (1 << offset_bits) - 1;
117
118 if entry > max_entries {
119 return Err(MemoryError::InvalidEntry {
120 entry,
121 max: max_entries,
122 });
123 }
124
125 if offset > max_offset {
126 return Err(MemoryError::InvalidOffset {
127 offset,
128 max: max_offset,
129 });
130 }
131
132 Ok(Address {
133 size,
134 region,
135 entry,
136 offset,
137 })
138 }
139
140 fn entry_bits(size: AddressSize, region: AddressRegion) -> u64 {
141 match (size, region) {
142 (_, AddressRegion::Stack) => 0,
144
145 (_, AddressRegion::Function) => 1,
148
149 (AddressSize::_32, AddressRegion::Table) => 5,
150 (AddressSize::_32, AddressRegion::GlobalValue) => 6,
151
152 (AddressSize::_64, AddressRegion::Table) => 10,
153 (AddressSize::_64, AddressRegion::GlobalValue) => 12,
154 }
155 }
156
157 fn offset_bits(size: AddressSize, region: AddressRegion) -> u64 {
158 let region_bits = 2;
159 let entry_bits = Address::entry_bits(size, region);
160 size.bits() - entry_bits - region_bits
161 }
162}
163
164impl TryFrom<Address> for DataValue {
165 type Error = MemoryError;
166
167 fn try_from(addr: Address) -> Result<Self, Self::Error> {
168 let entry_bits = Address::entry_bits(addr.size, addr.region);
169 let offset_bits = Address::offset_bits(addr.size, addr.region);
170
171 let entry = addr.entry << offset_bits;
172 let region = addr.region.encode() << (entry_bits + offset_bits);
173
174 let value = region | entry | addr.offset;
175 Ok(match addr.size {
176 AddressSize::_32 => DataValue::I32(value as u32 as i32),
177 AddressSize::_64 => DataValue::I64(value as i64),
178 })
179 }
180}
181
182impl TryFrom<DataValue> for Address {
183 type Error = MemoryError;
184
185 fn try_from(value: DataValue) -> Result<Self, Self::Error> {
186 let addr = match value {
187 DataValue::I32(v) => v as u32 as u64,
188 DataValue::I64(v) => v as u64,
189 _ => {
190 return Err(MemoryError::InvalidAddress(value));
191 }
192 };
193
194 let size = match value {
195 DataValue::I32(_) => AddressSize::_32,
196 DataValue::I64(_) => AddressSize::_64,
197 _ => unreachable!(),
198 };
199
200 let region = AddressRegion::decode(addr >> (size.bits() - 2));
201
202 let entry_bits = Address::entry_bits(size, region);
203 let offset_bits = Address::offset_bits(size, region);
204
205 let entry = (addr >> offset_bits) & ((1 << entry_bits) - 1);
206 let offset = addr & ((1 << offset_bits) - 1);
207
208 Address::from_parts(size, region, entry, offset)
209 }
210}
211
212impl TryFrom<u64> for Address {
213 type Error = MemoryError;
214
215 fn try_from(value: u64) -> Result<Self, Self::Error> {
216 let dv = if value > u32::MAX as u64 {
217 DataValue::I64(value as i64)
218 } else {
219 DataValue::I32(value as i32)
220 };
221
222 Address::try_from(dv)
223 }
224}
225
226#[derive(Debug, Clone, PartialEq)]
227pub enum AddressFunctionEntry {
228 UserFunction = 0,
229 LibCall,
230}
231
232impl From<u64> for AddressFunctionEntry {
233 fn from(bits: u64) -> Self {
234 match bits {
235 0 => AddressFunctionEntry::UserFunction,
236 1 => AddressFunctionEntry::LibCall,
237 _ => unreachable!(),
238 }
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn address_region_roundtrip_encode_decode() {
248 let all_regions = [
249 AddressRegion::Stack,
250 AddressRegion::Function,
251 AddressRegion::Table,
252 AddressRegion::GlobalValue,
253 ];
254
255 for region in all_regions {
256 assert_eq!(AddressRegion::decode(region.encode()), region);
257 }
258 }
259
260 #[test]
261 fn address_roundtrip() {
262 let test_addresses = [
263 (AddressSize::_32, AddressRegion::Stack, 0, 0),
264 (AddressSize::_32, AddressRegion::Stack, 0, 1),
265 (AddressSize::_32, AddressRegion::Stack, 0, 1024),
266 (AddressSize::_32, AddressRegion::Stack, 0, 0x3FFF_FFFF),
267 (AddressSize::_32, AddressRegion::Function, 0, 0),
268 (AddressSize::_32, AddressRegion::Function, 1, 1),
269 (AddressSize::_32, AddressRegion::Function, 0, 1024),
270 (AddressSize::_32, AddressRegion::Function, 1, 0x0FFF_FFFF),
271 (AddressSize::_32, AddressRegion::Table, 0, 0),
272 (AddressSize::_32, AddressRegion::Table, 1, 1),
273 (AddressSize::_32, AddressRegion::Table, 31, 0x1FF_FFFF),
274 (AddressSize::_32, AddressRegion::GlobalValue, 0, 0),
275 (AddressSize::_32, AddressRegion::GlobalValue, 1, 1),
276 (AddressSize::_32, AddressRegion::GlobalValue, 63, 0xFF_FFFF),
277 (AddressSize::_64, AddressRegion::Stack, 0, 0),
278 (AddressSize::_64, AddressRegion::Stack, 0, 1),
279 (
280 AddressSize::_64,
281 AddressRegion::Stack,
282 0,
283 0x3FFFFFFF_FFFFFFFF,
284 ),
285 (AddressSize::_64, AddressRegion::Function, 0, 0),
286 (AddressSize::_64, AddressRegion::Function, 1, 1),
287 (AddressSize::_64, AddressRegion::Function, 0, 1024),
288 (AddressSize::_64, AddressRegion::Function, 1, 0x0FFF_FFFF),
289 (AddressSize::_64, AddressRegion::Table, 0, 0),
290 (AddressSize::_64, AddressRegion::Table, 1, 1),
291 (AddressSize::_64, AddressRegion::Table, 31, 0x1FF_FFFF),
292 (AddressSize::_64, AddressRegion::GlobalValue, 0, 0),
293 (AddressSize::_64, AddressRegion::GlobalValue, 1, 1),
294 (AddressSize::_64, AddressRegion::GlobalValue, 63, 0xFF_FFFF),
295 ];
296
297 for (size, region, entry, offset) in test_addresses {
298 let original = Address {
299 size,
300 region,
301 entry,
302 offset,
303 };
304
305 let dv: DataValue = original.clone().try_into().unwrap();
306 let addr = dv.try_into().unwrap();
307
308 assert_eq!(original, addr);
309 }
310 }
311}