zingen/wasm/
abi.rs

1//! WASM ABI
2
3use smallvec::{smallvec, SmallVec};
4use wasmparser::{BlockType, ValType};
5
6/// The alignment mask for 32 bytes (32 - 1).
7const ALIGNMENT_MASK: usize = 31;
8
9/// WASM type size for the stack representation of EVM.
10pub trait Type {
11    /// Alignment the size of this type to bytes32 for the
12    /// stack representation of EVM.
13    ///
14    /// See <https://sites.google.com/site/theoryofoperatingsystems/labs/malloc/align8>
15    fn align(&self) -> usize {
16        (self.size() + ALIGNMENT_MASK) & !ALIGNMENT_MASK
17    }
18
19    /// Size in bytes.
20    fn size(&self) -> usize;
21}
22
23impl Type for &ValType {
24    fn size(&self) -> usize {
25        match self {
26            ValType::I32 | ValType::F32 => 4,
27            ValType::I64 | ValType::F64 => 8,
28            // TODO: align number implementations to 256 bits (issue #20)
29            _ => unimplemented!("unknown unsupported type {self}"),
30        }
31    }
32}
33
34impl Type for ValType {
35    fn size(&self) -> usize {
36        (&self).size()
37    }
38}
39
40impl Type for &[ValType] {
41    fn size(&self) -> usize {
42        self.as_ref().iter().map(|t| t.align()).sum::<usize>()
43    }
44}
45
46impl Type for usize {
47    fn align(&self) -> usize {
48        (*self + ALIGNMENT_MASK) & !ALIGNMENT_MASK
49    }
50
51    fn size(&self) -> usize {
52        self.to_le_bytes().len()
53    }
54}
55
56/// Get the offset of this type in the lowest
57/// significant bytes.
58pub trait ToLSBytes {
59    /// Output buffer
60    type Output: AsRef<[u8]>;
61
62    /// Convert self to the lowest significant bytes.
63    fn to_ls_bytes(&self) -> Self::Output;
64}
65
66macro_rules! offset {
67    ($number:ident, $size:expr) => {
68        impl ToLSBytes for $number {
69            type Output = SmallVec<[u8; $size]>;
70
71            fn to_ls_bytes(&self) -> Self::Output {
72                if *self == 0 {
73                    return smallvec![0];
74                }
75
76                self.to_le_bytes()
77                    .into_iter()
78                    .rev()
79                    .skip_while(|b| *b == 0)
80                    .collect::<Vec<_>>()
81                    .into()
82            }
83        }
84    };
85    ($(($number:ident, $size:expr)),+) => {
86        $(offset!($number, $size);)+
87    };
88}
89
90offset! {
91    (usize, 8),
92    (u64, 8),
93    (i64, 8),
94    (i32, 4),
95    (u32, 4),
96    (u16, 2),
97    (u8, 1)
98}
99
100impl ToLSBytes for BlockType {
101    type Output = SmallVec<[u8; 8]>;
102
103    fn to_ls_bytes(&self) -> Self::Output {
104        match self {
105            BlockType::Empty => Default::default(),
106            BlockType::Type(val) => val.size().to_ls_bytes(),
107            BlockType::FuncType(val) => Self::Output::from_slice(&val.to_ls_bytes()),
108        }
109    }
110}
111
112impl ToLSBytes for &ValType {
113    type Output = SmallVec<[u8; 8]>;
114
115    fn to_ls_bytes(&self) -> Self::Output {
116        self.align().to_ls_bytes()
117    }
118}
119
120impl ToLSBytes for &[ValType] {
121    type Output = SmallVec<[u8; 8]>;
122
123    fn to_ls_bytes(&self) -> Self::Output {
124        self.as_ref()
125            .iter()
126            .map(|t| t.align())
127            .sum::<usize>()
128            .to_ls_bytes()
129    }
130}
131
132#[test]
133fn test_usize_to_ls_bytes() {
134    assert_eq!(363usize.to_ls_bytes().to_vec(), vec![0x01, 0x6b]);
135    assert_eq!(255usize.to_ls_bytes().to_vec(), vec![0xff]);
136}