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 I32Nand,
252 I32Nor,
253 I32Xnor,
254
255 I64Eq,
256 I64Ne,
257 I64LtS,
258 I64LtU,
259 I64LeS,
260 I64LeU,
261
262 I64And,
263 I64Or,
264 I64Xor,
265 I64Nand,
266 I64Nor,
267 I64Xnor,
268
269 F32Eq,
270 F32Ne,
271 F32Lt,
272 F32Le,
273 F32NotLt,
274 F32NotLe,
275
276 F64Eq,
277 F64Ne,
278 F64Lt,
279 F64Le,
280 F64NotLt,
281 F64NotLe,
282 }
283 };
284}
285
286macro_rules! define_comparator {
287 ( $( $name:ident ),* $(,)? ) => {
288 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
290 #[repr(u32)]
291 pub enum Comparator {
292 $( $name ),*
293 }
294
295 impl TryFrom<u32> for Comparator {
296 type Error = Error;
297
298 fn try_from(value: u32) -> Result<Self, Self::Error> {
299 match value {
300 $(
301 x if x == Self::$name as u32 => Ok(Self::$name),
302 )*
303 _ => Err(Error::ComparatorOutOfBounds),
304 }
305 }
306 }
307
308 impl From<Comparator> for u32 {
309 fn from(cmp: Comparator) -> u32 {
310 cmp as u32
311 }
312 }
313 };
314}
315for_each_comparator!(define_comparator);
316
317#[derive(Debug, Copy, Clone, PartialEq, Eq)]
325pub struct ComparatorAndOffset {
326 pub cmp: Comparator,
328 pub offset: BranchOffset,
330}
331
332impl ComparatorAndOffset {
333 pub fn new(cmp: Comparator, offset: BranchOffset) -> Self {
335 Self { cmp, offset }
336 }
337
338 pub fn from_u64(value: u64) -> Option<Self> {
342 let hi = (value >> 32) as u32;
343 let lo = (value & 0xFFFF_FFFF) as u32;
344 let cmp = Comparator::try_from(hi).ok()?;
345 let offset = BranchOffset::from(lo as i32);
346 Some(Self { cmp, offset })
347 }
348
349 pub fn as_u64(&self) -> u64 {
351 let hi = self.cmp as u64;
352 let lo = self.offset.to_i32() as u64;
353 (hi << 32) | lo
354 }
355}
356
357impl From<ComparatorAndOffset> for UntypedVal {
358 fn from(params: ComparatorAndOffset) -> Self {
359 Self::from(params.as_u64())
360 }
361}
362
363#[derive(Debug, Copy, Clone, PartialEq, Eq)]
365pub struct ShiftAmount<T> {
366 value: Const16<T>,
368}
369
370macro_rules! impl_from_shift_amount_for {
371 ( $($ty:ty),* $(,)? ) => {
372 $(
373 impl From<ShiftAmount<$ty>> for $ty {
374 fn from(shamt: ShiftAmount<$ty>) -> $ty {
375 shamt.value.into()
376 }
377 }
378 )*
379 };
380}
381impl_from_shift_amount_for!(i32, i64, u32);
382
383pub trait IntoShiftAmount: Sized {
385 type Output;
386 type Input;
387
388 fn into_shift_amount(input: Self::Input) -> Option<Self::Output>;
390}
391
392macro_rules! impl_shift_amount {
393 ( $( ($ty:ty, $bits:literal, $ty16:ty, $shamt:ty) ),* $(,)? ) => {
394 $(
395 impl IntoShiftAmount for $ty {
396 type Output = ShiftAmount<$shamt>;
397 type Input = $shamt;
398
399 fn into_shift_amount(input: Self::Input) -> Option<Self::Output> {
400 let value = (input % $bits) as $ty16;
401 if value == 0 {
402 return None
403 }
404 Some(ShiftAmount { value: Const16::from(value) })
405 }
406 }
407 )*
408 };
409}
410impl_shift_amount! {
411 (i32, 32, i16, i32),
413 (i64, 64, i16, i64),
414
415 (u8 , 8, u16, u32),
417 (u16, 16, u16, u32),
418 (u32, 32, u16, u32),
419 (u64, 64, u16, u32),
420}
421
422#[derive(Debug, Copy, Clone, PartialEq, Eq)]
424#[repr(transparent)]
425pub struct Offset64(u64);
426
427#[derive(Debug, Copy, Clone, PartialEq, Eq)]
429#[repr(transparent)]
430pub struct Offset64Hi(pub(crate) u32);
431
432#[derive(Debug, Copy, Clone, PartialEq, Eq)]
434#[repr(transparent)]
435pub struct Offset64Lo(pub(crate) u32);
436
437impl Offset64 {
438 pub fn split(offset: u64) -> (Offset64Hi, Offset64Lo) {
440 let offset_lo = (offset & 0xFFFF_FFFF) as u32;
441 let offset_hi = (offset >> 32) as u32;
442 (Offset64Hi(offset_hi), Offset64Lo(offset_lo))
443 }
444
445 pub fn combine(hi: Offset64Hi, lo: Offset64Lo) -> Self {
447 let hi = hi.0 as u64;
448 let lo = lo.0 as u64;
449 Self((hi << 32) | lo)
450 }
451}
452
453#[test]
454fn test_offset64_split_combine() {
455 let test_values = [
456 0,
457 1,
458 1 << 1,
459 u64::MAX,
460 u64::MAX - 1,
461 42,
462 77,
463 u64::MAX >> 1,
464 0xFFFF_FFFF_0000_0000,
465 0x0000_0000_FFFF_FFFF,
466 0xF0F0_F0F0_0F0F_0F0F,
467 ];
468 for value in test_values {
469 let (hi, lo) = Offset64::split(value);
470 let combined = u64::from(Offset64::combine(hi, lo));
471 assert_eq!(combined, value);
472 }
473}
474
475impl From<u64> for Offset64 {
476 fn from(offset: u64) -> Self {
477 Self(offset)
478 }
479}
480
481impl From<Offset64> for u64 {
482 fn from(offset: Offset64) -> Self {
483 offset.0
484 }
485}
486
487#[derive(Debug, Copy, Clone, PartialEq, Eq)]
489#[repr(transparent)]
490pub struct Offset8(u8);
491
492impl TryFrom<u64> for Offset8 {
493 type Error = OutOfBoundsConst;
494
495 fn try_from(address: u64) -> Result<Self, Self::Error> {
496 u8::try_from(address)
497 .map(Self)
498 .map_err(|_| OutOfBoundsConst)
499 }
500}
501
502impl From<Offset8> for Offset64 {
503 fn from(offset: Offset8) -> Self {
504 Offset64(u64::from(offset.0))
505 }
506}
507
508#[derive(Debug, Copy, Clone, PartialEq, Eq)]
510#[repr(transparent)]
511pub struct Offset16(Const16<u64>);
512
513impl TryFrom<u64> for Offset16 {
514 type Error = OutOfBoundsConst;
515
516 fn try_from(address: u64) -> Result<Self, Self::Error> {
517 <Const16<u64>>::try_from(address).map(Self)
518 }
519}
520
521impl From<Offset16> for Offset64 {
522 fn from(offset: Offset16) -> Self {
523 Offset64(u64::from(offset.0))
524 }
525}
526
527#[derive(Debug, Copy, Clone, PartialEq, Eq)]
529#[repr(transparent)]
530pub struct Address(u64);
531
532impl TryFrom<u64> for Address {
533 type Error = OutOfBoundsConst;
534
535 fn try_from(address: u64) -> Result<Self, OutOfBoundsConst> {
536 if usize::try_from(address).is_err() {
537 return Err(OutOfBoundsConst);
538 };
539 Ok(Self(address))
540 }
541}
542
543impl From<Address> for usize {
544 fn from(address: Address) -> Self {
545 debug_assert!(usize::try_from(address.0).is_ok());
548 address.0 as usize
549 }
550}
551
552impl From<Address> for u64 {
553 fn from(address: Address) -> Self {
554 address.0
555 }
556}
557
558#[derive(Debug, Copy, Clone, PartialEq, Eq)]
560#[repr(transparent)]
561pub struct Address32(u32);
562
563impl TryFrom<Address> for Address32 {
564 type Error = OutOfBoundsConst;
565
566 fn try_from(address: Address) -> Result<Self, OutOfBoundsConst> {
567 let Ok(address) = u32::try_from(u64::from(address)) else {
568 return Err(OutOfBoundsConst);
569 };
570 Ok(Self(address))
571 }
572}
573
574impl From<Address32> for usize {
575 fn from(address: Address32) -> Self {
576 debug_assert!(usize::try_from(address.0).is_ok());
579 address.0 as usize
580 }
581}