Skip to main content

wasmi/engine/translator/
utils.rs

1use crate::{
2    core::{Typed, TypedVal, UntypedVal},
3    ir::{Const16, Op, Sign},
4    Error,
5    ExternRef,
6    Func,
7    Ref,
8    ValType,
9};
10use core::num::NonZero;
11
12impl Typed for ExternRef {
13    const TY: ValType = ValType::ExternRef;
14}
15
16macro_rules! impl_typed_for {
17    ( $( $ty:ty as $ident:ident ),* $(,)? ) => {
18        $(
19            impl Typed for $ty {
20                const TY: ValType = crate::ValType::$ident;
21            }
22
23            impl From<TypedVal> for $ty {
24                fn from(typed_value: TypedVal) -> Self {
25                    // # Note
26                    //
27                    // We only use a `debug_assert` here instead of a proper `assert`
28                    // since the whole translation process assumes that Wasm validation
29                    // was already performed and thus type checking does not necessarily
30                    // need to happen redundantly outside of debug builds.
31                    debug_assert!(matches!(typed_value.ty(), <$ty as Typed>::TY));
32                    Self::from(typed_value.untyped())
33                }
34            }
35        )*
36    };
37}
38impl_typed_for! {
39    Ref<Func> as FuncRef,
40    Ref<ExternRef> as ExternRef,
41}
42
43/// A WebAssembly integer. Either `i32` or `i64`.
44///
45/// # Note
46///
47/// This trait provides some utility methods useful for translation.
48pub trait WasmInteger:
49    Copy
50    + Eq
51    + Typed
52    + From<TypedVal>
53    + Into<TypedVal>
54    + From<UntypedVal>
55    + Into<UntypedVal>
56    + TryInto<Const16<Self>>
57{
58    /// The non-zero type of the [`WasmInteger`].
59    type NonZero: Copy + Into<Self> + TryInto<Const16<Self::NonZero>> + Into<UntypedVal>;
60
61    /// Returns `self` as [`Self::NonZero`] if possible.
62    ///
63    /// Returns `None` if `self` is zero.
64    fn non_zero(self) -> Option<Self::NonZero>;
65
66    /// Returns `true` if `self` is equal to zero (0).
67    fn is_zero(self) -> bool;
68
69    /// Returns the wrapped negated `self`.
70    fn wrapping_neg(self) -> Self;
71}
72
73macro_rules! impl_wasm_integer {
74    ($($ty:ty),*) => {
75        $(
76            impl WasmInteger for $ty {
77                type NonZero = NonZero<Self>;
78
79                fn non_zero(self) -> Option<Self::NonZero> {
80                    Self::NonZero::new(self)
81                }
82
83                fn is_zero(self) -> bool {
84                    self == 0
85                }
86
87                fn wrapping_neg(self) -> Self {
88                    Self::wrapping_neg(self)
89                }
90            }
91        )*
92    };
93}
94impl_wasm_integer!(i32, u32, i64, u64);
95
96/// A WebAssembly float. Either `f32` or `f64`.
97///
98/// # Note
99///
100/// This trait provides some utility methods useful for translation.
101pub trait WasmFloat: Typed + Copy + Into<TypedVal> + From<TypedVal> {
102    /// Returns the [`Sign`] of `self`.
103    fn sign(self) -> Sign<Self>;
104}
105
106impl WasmFloat for f32 {
107    fn sign(self) -> Sign<Self> {
108        Sign::from(self)
109    }
110}
111
112impl WasmFloat for f64 {
113    fn sign(self) -> Sign<Self> {
114        Sign::from(self)
115    }
116}
117
118/// Implemented by integer types to wrap them to another (smaller) integer type.
119pub trait Wrap<T> {
120    /// Wraps `self` into a value of type `T`.
121    fn wrap(self) -> T;
122}
123
124impl<T> Wrap<T> for T {
125    #[inline]
126    fn wrap(self) -> T {
127        self
128    }
129}
130
131macro_rules! impl_wrap_for {
132    ( $($from_ty:ty => $to_ty:ty),* $(,)? ) => {
133        $(
134            impl Wrap<$to_ty> for $from_ty {
135                #[inline]
136                fn wrap(self) -> $to_ty { self as _ }
137            }
138        )*
139    };
140}
141impl_wrap_for! {
142    // signed
143    i16 => i8,
144    i32 => i8,
145    i32 => i16,
146    i64 => i8,
147    i64 => i16,
148    i64 => i32,
149    // unsigned
150    u16 => u8,
151    u32 => u8,
152    u32 => u16,
153    u64 => u8,
154    u64 => u16,
155    u64 => u32,
156}
157
158/// Extension trait to bump the consumed fuel of [`Op::ConsumeFuel`].
159pub trait BumpFuelConsumption {
160    /// Increases the fuel consumption of the [`Op::ConsumeFuel`] instruction by `delta`.
161    ///
162    /// # Error
163    ///
164    /// - If `self` is not a [`Op::ConsumeFuel`] instruction.
165    /// - If the new fuel consumption overflows the internal `u64` value.
166    fn bump_fuel_consumption(&mut self, delta: u64) -> Result<(), Error>;
167}
168
169impl BumpFuelConsumption for Op {
170    fn bump_fuel_consumption(&mut self, delta: u64) -> Result<(), Error> {
171        match self {
172            Self::ConsumeFuel { block_fuel } => block_fuel.bump_by(delta).map_err(Error::from),
173            instr => panic!("expected `Op::ConsumeFuel` but found: {instr:?}"),
174        }
175    }
176}
177
178/// Extension trait to query if an [`Op`] is a parameter.
179pub trait IsInstructionParameter {
180    /// Returns `true` if `self` is a parameter to an [`Op`].
181    fn is_instruction_parameter(&self) -> bool;
182}
183
184impl IsInstructionParameter for Op {
185    #[rustfmt::skip]
186    fn is_instruction_parameter(&self) -> bool {
187        matches!(self,
188            | Self::TableIndex { .. }
189            | Self::MemoryIndex { .. }
190            | Self::DataIndex { .. }
191            | Self::ElemIndex { .. }
192            | Self::Const32 { .. }
193            | Self::I64Const32 { .. }
194            | Self::F64Const32 { .. }
195            | Self::BranchTableTarget { .. }
196            | Self::Imm16AndImm32 { .. }
197            | Self::SlotAndImm32 { .. }
198            | Self::SlotSpan { .. }
199            | Self::Slot { .. }
200            | Self::Slot2 { .. }
201            | Self::Slot3 { .. }
202            | Self::SlotList { .. }
203            | Self::CallIndirectParams { .. }
204            | Self::CallIndirectParamsImm16 { .. }
205        )
206    }
207}
208
209/// A reference to an encoded [`Op`].
210#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
211pub struct Instr(u32);
212
213impl From<u32> for Instr {
214    fn from(index: u32) -> Self {
215        Self(index)
216    }
217}
218
219impl From<Instr> for u32 {
220    fn from(instr: Instr) -> Self {
221        instr.0
222    }
223}
224
225impl Instr {
226    /// Creates an [`Instr`] from the given `usize` value.
227    ///
228    /// # Note
229    ///
230    /// This intentionally is an API intended for test purposes only.
231    ///
232    /// # Panics
233    ///
234    /// If the `value` exceeds limitations for [`Instr`].
235    pub fn from_usize(value: usize) -> Self {
236        let Ok(index) = u32::try_from(value) else {
237            panic!("out of bounds index {value} for `Instr`")
238        };
239        Self(index)
240    }
241
242    /// Returns an `usize` representation of the instruction index.
243    pub fn into_usize(self) -> usize {
244        match usize::try_from(self.0) {
245            Ok(index) => index,
246            Err(error) => {
247                panic!("out of bound index {} for `Instr`: {error}", self.0)
248            }
249        }
250    }
251
252    /// Returns the absolute distance between `self` and `other`.
253    ///
254    /// - Returns `0` if `self == other`.
255    /// - Returns `1` if `self` is adjacent to `other` in the sequence of instructions.
256    /// - etc..
257    pub fn distance(self, other: Self) -> u32 {
258        self.0.abs_diff(other.0)
259    }
260}