zingen/wasm/
abi.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! WASM ABI

use smallvec::{smallvec, SmallVec};
use wasmparser::{BlockType, ValType};

/// The alignment mask for 32 bytes (32 - 1).
const ALIGNMENT_MASK: usize = 31;

/// WASM type size for the stack representation of EVM.
pub trait Type {
    /// Alignment the size of this type to bytes32 for the
    /// stack representation of EVM.
    ///
    /// See <https://sites.google.com/site/theoryofoperatingsystems/labs/malloc/align8>
    fn align(&self) -> usize {
        (self.size() + ALIGNMENT_MASK) & !ALIGNMENT_MASK
    }

    /// Size in bytes.
    fn size(&self) -> usize;
}

impl Type for &ValType {
    fn size(&self) -> usize {
        match self {
            ValType::I32 | ValType::F32 => 4,
            ValType::I64 | ValType::F64 => 8,
            // TODO: align number implementations to 256 bits (issue #20)
            _ => unimplemented!("unknown unsupported type {self}"),
        }
    }
}

impl Type for ValType {
    fn size(&self) -> usize {
        (&self).size()
    }
}

impl Type for &[ValType] {
    fn size(&self) -> usize {
        self.as_ref().iter().map(|t| t.align()).sum::<usize>()
    }
}

impl Type for usize {
    fn align(&self) -> usize {
        (*self + ALIGNMENT_MASK) & !ALIGNMENT_MASK
    }

    fn size(&self) -> usize {
        self.to_le_bytes().len()
    }
}

/// Get the offset of this type in the lowest
/// significant bytes.
pub trait ToLSBytes {
    /// Output buffer
    type Output: AsRef<[u8]>;

    /// Convert self to the lowest significant bytes.
    fn to_ls_bytes(&self) -> Self::Output;
}

macro_rules! offset {
    ($number:ident, $size:expr) => {
        impl ToLSBytes for $number {
            type Output = SmallVec<[u8; $size]>;

            fn to_ls_bytes(&self) -> Self::Output {
                if *self == 0 {
                    return smallvec![0];
                }

                self.to_le_bytes()
                    .into_iter()
                    .rev()
                    .skip_while(|b| *b == 0)
                    .collect::<Vec<_>>()
                    .into()
            }
        }
    };
    ($(($number:ident, $size:expr)),+) => {
        $(offset!($number, $size);)+
    };
}

offset! {
    (usize, 8),
    (u64, 8),
    (i64, 8),
    (i32, 4),
    (u32, 4),
    (u16, 2),
    (u8, 1)
}

impl ToLSBytes for BlockType {
    type Output = SmallVec<[u8; 8]>;

    fn to_ls_bytes(&self) -> Self::Output {
        match self {
            BlockType::Empty => Default::default(),
            BlockType::Type(val) => val.size().to_ls_bytes(),
            BlockType::FuncType(val) => Self::Output::from_slice(&val.to_ls_bytes()),
        }
    }
}

impl ToLSBytes for &ValType {
    type Output = SmallVec<[u8; 8]>;

    fn to_ls_bytes(&self) -> Self::Output {
        self.align().to_ls_bytes()
    }
}

impl ToLSBytes for &[ValType] {
    type Output = SmallVec<[u8; 8]>;

    fn to_ls_bytes(&self) -> Self::Output {
        self.as_ref()
            .iter()
            .map(|t| t.align())
            .sum::<usize>()
            .to_ls_bytes()
    }
}

#[test]
fn test_usize_to_ls_bytes() {
    assert_eq!(363usize.to_ls_bytes().to_vec(), vec![0x01, 0x6b]);
    assert_eq!(255usize.to_ls_bytes().to_vec(), vec![0xff]);
}