1use crate::{core::UntypedVal, immeditate::OutOfBoundsConst, Const16, Error};
2use core::marker::PhantomData;
3
4#[derive(Debug)]
6pub struct Sign<T> {
7 is_positive: bool,
9 marker: PhantomData<fn() -> T>,
11}
12
13impl<T> Clone for Sign<T> {
14 fn clone(&self) -> Self {
15 *self
16 }
17}
18
19impl<T> Copy for Sign<T> {}
20
21impl<T> PartialEq for Sign<T> {
22 fn eq(&self, other: &Self) -> bool {
23 self.is_positive == other.is_positive
24 }
25}
26
27impl<T> Eq for Sign<T> {}
28
29impl<T> Sign<T> {
30 fn new(is_positive: bool) -> Self {
32 Self {
33 is_positive,
34 marker: PhantomData,
35 }
36 }
37
38 pub fn pos() -> Self {
40 Self::new(true)
41 }
42
43 pub fn neg() -> Self {
45 Self::new(false)
46 }
47}
48
49macro_rules! impl_sign_for {
50 ( $($ty:ty),* $(,)? ) => {
51 $(
52 impl From<$ty> for Sign<$ty> {
53 fn from(value: $ty) -> Self {
54 Self::new(value.is_sign_positive())
55 }
56 }
57
58 impl From<Sign<$ty>> for $ty {
59 fn from(sign: Sign<$ty>) -> Self {
60 match sign.is_positive {
61 true => 1.0,
62 false => -1.0,
63 }
64 }
65 }
66 )*
67 };
68}
69impl_sign_for!(f32, f64);
70
71#[derive(Debug, Copy, Clone, PartialEq, Eq)]
76pub struct BranchOffset16(i16);
77
78impl From<i16> for BranchOffset16 {
79 fn from(offset: i16) -> Self {
80 Self(offset)
81 }
82}
83
84impl TryFrom<BranchOffset> for BranchOffset16 {
85 type Error = Error;
86
87 fn try_from(offset: BranchOffset) -> Result<Self, Self::Error> {
88 let Ok(offset16) = i16::try_from(offset.to_i32()) else {
89 return Err(Error::BranchOffsetOutOfBounds);
90 };
91 Ok(Self(offset16))
92 }
93}
94
95impl From<BranchOffset16> for BranchOffset {
96 fn from(offset: BranchOffset16) -> Self {
97 Self::from(i32::from(offset.to_i16()))
98 }
99}
100
101impl BranchOffset16 {
102 pub fn is_init(self) -> bool {
104 self.to_i16() != 0
105 }
106
107 pub fn init(&mut self, valid_offset: BranchOffset) -> Result<(), Error> {
118 assert!(valid_offset.is_init());
119 assert!(!self.is_init());
120 let valid_offset16 = Self::try_from(valid_offset)?;
121 *self = valid_offset16;
122 Ok(())
123 }
124
125 pub fn to_i16(self) -> i16 {
127 self.0
128 }
129}
130
131#[derive(Debug, Copy, Clone, PartialEq, Eq)]
136pub struct BranchOffset(i32);
137
138impl From<i32> for BranchOffset {
139 fn from(index: i32) -> Self {
140 Self(index)
141 }
142}
143
144impl BranchOffset {
145 pub fn uninit() -> Self {
147 Self(0)
148 }
149
150 pub fn from_src_to_dst(src: u32, dst: u32) -> Result<Self, Error> {
156 let src = i64::from(src);
157 let dst = i64::from(dst);
158 let Some(offset) = dst.checked_sub(src) else {
159 unreachable!(
161 "offset for forward branches must have `src` be smaller than or equal to `dst`"
162 );
163 };
164 let Ok(offset) = i32::try_from(offset) else {
165 return Err(Error::BranchOffsetOutOfBounds);
166 };
167 Ok(Self(offset))
168 }
169
170 pub fn is_init(self) -> bool {
172 self.to_i32() != 0
173 }
174
175 pub fn init(&mut self, valid_offset: BranchOffset) {
182 assert!(valid_offset.is_init());
183 assert!(!self.is_init());
184 *self = valid_offset;
185 }
186
187 pub fn to_i32(self) -> i32 {
189 self.0
190 }
191}
192
193#[derive(Debug, Copy, Clone, PartialEq, Eq)]
197#[repr(transparent)]
198pub struct BlockFuel(u32);
199
200impl From<u32> for BlockFuel {
201 fn from(value: u32) -> Self {
202 Self(value)
203 }
204}
205
206impl TryFrom<u64> for BlockFuel {
207 type Error = Error;
208
209 fn try_from(index: u64) -> Result<Self, Self::Error> {
210 match u32::try_from(index) {
211 Ok(index) => Ok(Self(index)),
212 Err(_) => Err(Error::BlockFuelOutOfBounds),
213 }
214 }
215}
216
217impl BlockFuel {
218 pub fn bump_by(&mut self, amount: u64) -> Result<(), Error> {
224 let new_amount = self
225 .to_u64()
226 .checked_add(amount)
227 .ok_or(Error::BlockFuelOutOfBounds)?;
228 self.0 = u32::try_from(new_amount).map_err(|_| Error::BlockFuelOutOfBounds)?;
229 Ok(())
230 }
231
232 pub fn to_u64(self) -> u64 {
234 u64::from(self.0)
235 }
236}
237
238macro_rules! for_each_comparator {
239 ($mac:ident) => {
240 $mac! {
241 I32Eq,
242 I32Ne,
243 I32LtS,
244 I32LtU,
245 I32LeS,
246 I32LeU,
247
248 I32And,
249 I32Or,
250 I32Xor,
251 I32AndEqz,
252 I32OrEqz,
253 I32XorEqz,
254
255 I64Eq,
256 I64Ne,
257 I64LtS,
258 I64LtU,
259 I64LeS,
260 I64LeU,
261
262 F32Eq,
263 F32Ne,
264 F32Lt,
265 F32Le,
266
267 F64Eq,
268 F64Ne,
269 F64Lt,
270 F64Le,
271 }
272 };
273}
274
275macro_rules! define_comparator {
276 ( $( $name:ident ),* $(,)? ) => {
277 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
279 #[repr(u32)]
280 pub enum Comparator {
281 $( $name ),*
282 }
283
284 impl TryFrom<u32> for Comparator {
285 type Error = Error;
286
287 fn try_from(value: u32) -> Result<Self, Self::Error> {
288 match value {
289 $(
290 x if x == Self::$name as u32 => Ok(Self::$name),
291 )*
292 _ => Err(Error::ComparatorOutOfBounds),
293 }
294 }
295 }
296
297 impl From<Comparator> for u32 {
298 fn from(cmp: Comparator) -> u32 {
299 cmp as u32
300 }
301 }
302 };
303}
304for_each_comparator!(define_comparator);
305
306#[derive(Debug, Copy, Clone, PartialEq, Eq)]
314pub struct ComparatorAndOffset {
315 pub cmp: Comparator,
317 pub offset: BranchOffset,
319}
320
321impl ComparatorAndOffset {
322 pub fn new(cmp: Comparator, offset: BranchOffset) -> Self {
324 Self { cmp, offset }
325 }
326
327 pub fn from_u64(value: u64) -> Option<Self> {
331 let hi = (value >> 32) as u32;
332 let lo = (value & 0xFFFF_FFFF) as u32;
333 let cmp = Comparator::try_from(hi).ok()?;
334 let offset = BranchOffset::from(lo as i32);
335 Some(Self { cmp, offset })
336 }
337
338 pub fn from_untyped(value: UntypedVal) -> Option<Self> {
342 Self::from_u64(u64::from(value))
343 }
344
345 pub fn as_u64(&self) -> u64 {
347 let hi = self.cmp as u64;
348 let lo = self.offset.to_i32() as u64;
349 (hi << 32) | lo
350 }
351}
352
353impl From<ComparatorAndOffset> for UntypedVal {
354 fn from(params: ComparatorAndOffset) -> Self {
355 Self::from(params.as_u64())
356 }
357}
358
359#[derive(Debug, Copy, Clone, PartialEq, Eq)]
361pub struct ShiftAmount<T> {
362 value: Const16<T>,
364}
365
366pub trait IntoShiftAmount: Sized {
368 fn into_shift_amount(self) -> Option<ShiftAmount<Self>>;
370}
371
372macro_rules! impl_shift_amount {
373 ( $( ($ty:ty, $bits:literal) ),* $(,)? ) => {
374 $(
375 impl IntoShiftAmount for $ty {
376 fn into_shift_amount(self) -> Option<ShiftAmount<Self>> {
377 <ShiftAmount<$ty>>::new(self)
378 }
379 }
380
381 impl ShiftAmount<$ty> {
382 pub fn new(value: $ty) -> Option<Self> {
386 let value = (value % $bits) as i16;
387 if value == 0 {
388 return None
389 }
390 Some(Self { value: Const16::from(value) })
391 }
392 }
393
394 impl From<ShiftAmount<$ty>> for $ty {
395 fn from(shamt: ShiftAmount<$ty>) -> Self {
396 shamt.value.into()
397 }
398 }
399 )*
400 };
401}
402impl_shift_amount! {
403 (i32, 32),
404 (i64, 64),
405}
406
407#[derive(Debug, Copy, Clone, PartialEq, Eq)]
409#[repr(transparent)]
410pub struct Offset64(u64);
411
412#[derive(Debug, Copy, Clone, PartialEq, Eq)]
414#[repr(transparent)]
415pub struct Offset64Hi(pub(crate) u32);
416
417#[derive(Debug, Copy, Clone, PartialEq, Eq)]
419#[repr(transparent)]
420pub struct Offset64Lo(pub(crate) u32);
421
422impl Offset64 {
423 pub fn split(offset: u64) -> (Offset64Hi, Offset64Lo) {
425 let offset_lo = (offset & 0xFFFF_FFFF) as u32;
426 let offset_hi = (offset >> 32) as u32;
427 (Offset64Hi(offset_hi), Offset64Lo(offset_lo))
428 }
429
430 pub fn combine(hi: Offset64Hi, lo: Offset64Lo) -> Self {
432 let hi = hi.0 as u64;
433 let lo = lo.0 as u64;
434 Self((hi << 32) | lo)
435 }
436}
437
438#[test]
439fn test_offset64_split_combine() {
440 let test_values = [
441 0,
442 1,
443 1 << 1,
444 u64::MAX,
445 u64::MAX - 1,
446 42,
447 77,
448 u64::MAX >> 1,
449 0xFFFF_FFFF_0000_0000,
450 0x0000_0000_FFFF_FFFF,
451 0xF0F0_F0F0_0F0F_0F0F,
452 ];
453 for value in test_values {
454 let (hi, lo) = Offset64::split(value);
455 let combined = u64::from(Offset64::combine(hi, lo));
456 assert_eq!(combined, value);
457 }
458}
459
460impl From<u64> for Offset64 {
461 fn from(offset: u64) -> Self {
462 Self(offset)
463 }
464}
465
466impl From<Offset64> for u64 {
467 fn from(offset: Offset64) -> Self {
468 offset.0
469 }
470}
471
472#[derive(Debug, Copy, Clone, PartialEq, Eq)]
474#[repr(transparent)]
475pub struct Offset16(Const16<u64>);
476
477impl TryFrom<u64> for Offset16 {
478 type Error = OutOfBoundsConst;
479
480 fn try_from(address: u64) -> Result<Self, Self::Error> {
481 <Const16<u64>>::try_from(address).map(Self)
482 }
483}
484
485impl From<Offset16> for Offset64 {
486 fn from(offset: Offset16) -> Self {
487 Offset64(u64::from(offset.0))
488 }
489}
490
491#[derive(Debug, Copy, Clone, PartialEq, Eq)]
493#[repr(transparent)]
494pub struct Address(u64);
495
496impl TryFrom<u64> for Address {
497 type Error = OutOfBoundsConst;
498
499 fn try_from(address: u64) -> Result<Self, OutOfBoundsConst> {
500 if usize::try_from(address).is_err() {
501 return Err(OutOfBoundsConst);
502 };
503 Ok(Self(address))
504 }
505}
506
507impl From<Address> for usize {
508 fn from(address: Address) -> Self {
509 debug_assert!(usize::try_from(address.0).is_ok());
512 address.0 as usize
513 }
514}
515
516impl From<Address> for u64 {
517 fn from(address: Address) -> Self {
518 address.0
519 }
520}
521
522#[derive(Debug, Copy, Clone, PartialEq, Eq)]
524#[repr(transparent)]
525pub struct Address32(u32);
526
527impl TryFrom<Address> for Address32 {
528 type Error = OutOfBoundsConst;
529
530 fn try_from(address: Address) -> Result<Self, OutOfBoundsConst> {
531 let Ok(address) = u32::try_from(u64::from(address)) else {
532 return Err(OutOfBoundsConst);
533 };
534 Ok(Self(address))
535 }
536}
537
538impl From<Address32> for usize {
539 fn from(address: Address32) -> Self {
540 debug_assert!(usize::try_from(address.0).is_ok());
543 address.0 as usize
544 }
545}