zingen/visitor/
mod.rs

1//! This module is the central place for machine code emission.
2//!
3//! It defines an implementation of wasmparser's Visitor trait for
4//! `Function`; which defines a visitor per op-code, which validates
5//! and dispatches to the corresponding machine code emitter.
6
7use crate::{Function, Result};
8use paste::paste;
9use tracing::trace;
10use wasmparser::{for_each_operator, BlockType, BrTable, Ieee32, Ieee64, MemArg, VisitOperator};
11
12mod call;
13mod control;
14mod local;
15mod log;
16
17/// A macro to define unsupported WebAssembly operators.
18///
19/// This macro calls itself recursively;
20/// 1. It no-ops when matching a supported operator.
21/// 2. Defines the visitor function and panics when
22///    matching an unsupported operator.
23macro_rules! impl_visit_operator {
24    ( @mvp $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => {
25        impl_visit_operator!($($rest)*);
26    };
27    ( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => {
28        fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output {
29            trace!("{}", stringify!($op));
30            Ok(())
31        }
32
33        impl_visit_operator!($($rest)*);
34    };
35    () => {};
36}
37
38/// Implement arithmetic operators for types.
39macro_rules! map_wasm_operators {
40    (@basic $ty:tt, $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
41        paste! {
42            fn [< visit_ $ty _ $wasm >](&mut self $(,$arg: $argty),*) -> Self::Output {
43                trace!("{}.{}", stringify!($ty), stringify!($evm));
44
45                let before = self.masm.buffer().len();
46                self.masm.[< _ $evm >]()?;
47
48                let instr = self.masm.buffer()[before..].to_vec();
49                self.backtrace.push(instr);
50
51                Ok(())
52            }
53        }
54    };
55    (@integer32 $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
56        map_wasm_operators!(@basic i32, $wasm, $evm $($arg: $argty),*);
57    };
58    (@integer64 $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
59        map_wasm_operators!(@basic i64, $wasm, $evm $($arg: $argty),*);
60    };
61    (@integer $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
62        map_wasm_operators!(@integer32 $wasm, $evm $($arg: $argty),*);
63        map_wasm_operators!(@integer64 $wasm, $evm $($arg: $argty),*);
64    };
65    (@xdr $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
66        paste!{
67            map_wasm_operators!(@integer [< $wasm _s >], $evm $($arg: $argty),*);
68            map_wasm_operators!(@integer [< $wasm _u >], $evm $($arg: $argty),*);
69        }
70    };
71    (@float32 $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
72        map_wasm_operators!(@basic f32, $wasm, $evm $($arg: $argty),*);
73    };
74    (@float64 $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
75        map_wasm_operators!(@basic f64, $wasm, $evm $($arg: $argty),*);
76    };
77    (@float $wasm:tt, $evm:tt $($arg:ident: $argty:ty),*) => {
78        map_wasm_operators!(@float32 $wasm, $evm $($arg: $argty),*);
79        map_wasm_operators!(@float64 $wasm, $evm $($arg: $argty),*);
80    };
81    (@integer_and_float $op:tt $($arg:ident: $argty:ty),*) => {
82        map_wasm_operators!(@integer $op, $op);
83        map_wasm_operators!(@float $op, $op);
84    };
85    (@field ($($field:ident).*) ($op:tt -> $evm:tt) $($arg:tt: $argty:ty),* ) => {
86        paste! {
87            fn [< visit_ $op >](&mut self, $($arg: $argty),*) -> Self::Output {
88                let mut log = stringify!($op).to_string();
89                log = log.replace('_', ".");
90
91                $(
92                    let fmt = &format!(" {:?}", $arg);
93                    if fmt != " Empty" {
94                        log.push_str(&fmt);
95                    }
96                )*
97
98                trace!("{}", log);
99
100                let before = self.masm.buffer().len();
101                self.$($field.)*[< _ $evm >]($($arg),*)?;
102
103                let instr = self.masm.buffer()[before..].to_vec();
104                self.backtrace.push(instr);
105                Ok(())
106            }
107        }
108    };
109    (
110        all: [$($all:tt),+],
111        xdr: [$($xdr:tt),+],
112        integer: [$($integer:tt),+],
113        integer_and_float: [$($op:tt),+],
114        float: [$($float:tt),+],
115        map: {
116            integer: [$($map_int_wasm:tt => $map_int_evm:tt),+],
117        },
118        mem: {
119            all: [$($mem:tt),+],
120            integer: [$($mem_integer:tt),+],
121            integer64: [$($mem_integer64:tt),+],
122            signed: [$($mem_signed:tt),+],
123            signed64: [$($mem_signed64:tt),+],
124            signed_and_float: [$($mem_signed_and_float:tt),+],
125        },
126        masm: {
127            $( $masm:tt $(: { $($marg:ident: $margty:ty),+ })? ),+
128        },
129        global: {
130            $( $global:tt $(: { $($garg:ident: $gargty:ty),+ })? ),+
131        }
132    ) => {
133        paste! {
134            $(map_wasm_operators!(@integer_and_float $op);)+
135
136            $(
137                map_wasm_operators!(@integer [< $all _s >], [< s $all >]);
138                map_wasm_operators!(@integer [< $all _u >], $all);
139                map_wasm_operators!(@float $all, $all);
140            )+
141
142            $(map_wasm_operators!(@integer $integer, $integer);)+
143            $(map_wasm_operators!(@xdr $xdr, $xdr);)+
144            $(map_wasm_operators!(@float $float, $float);)+
145
146            $(
147                map_wasm_operators!(@integer [< $map_int_wasm _s >], [< s $map_int_evm >]);
148                map_wasm_operators!(@integer [< $map_int_wasm _u >], $map_int_evm);
149            )+
150
151            $(
152                map_wasm_operators!(@integer $mem, $mem _arg: MemArg);
153                map_wasm_operators!(@float $mem, $mem _arg: MemArg);
154            )+
155
156
157            $(
158                map_wasm_operators!(@xdr $mem_integer, $mem_integer _arg: MemArg);
159            )+
160
161            $(
162                map_wasm_operators!(@integer64 [< $mem_integer64 _s >], $mem_integer64 _arg: MemArg );
163                map_wasm_operators!(@integer64 [< $mem_integer64 _u >], $mem_integer64 _arg: MemArg );
164            )+
165
166
167            $(
168                map_wasm_operators!(@integer $mem_signed, $mem_signed _arg: MemArg);
169            )+
170
171            $(
172                map_wasm_operators!(@integer $mem_signed_and_float, $mem_signed_and_float _arg: MemArg);
173                map_wasm_operators!(@float $mem_signed_and_float, $mem_signed_and_float _arg: MemArg);
174            )+
175
176            $(
177                map_wasm_operators!(@integer64 $mem_signed64, $mem_signed64 _arg: MemArg);
178            )+
179
180            $(
181                map_wasm_operators!(@field (masm) ($masm -> $masm) $( $($marg: $margty),+ )?);
182            )+
183
184            $(
185                map_wasm_operators!(@field () ($global -> $global) $( $($garg: $gargty),+ )?);
186            )+
187        }
188    };
189}
190
191impl VisitOperator<'_> for Function {
192    type Output = Result<()>;
193
194    for_each_operator!(impl_visit_operator);
195
196    map_wasm_operators! {
197        all: [div, lt, gt, ge, le],
198        xdr: [shr, trunc_f32, trunc_f64],
199        integer: [and, clz, ctz, eqz, or, popcnt, rotl, rotr, shl, xor],
200        integer_and_float: [add, sub, mul, eq, ne],
201        float: [
202            abs, ceil, copysign, floor, max, min, nearest, neg, sqrt,
203            convert_i32_s, convert_i32_u, convert_i64_s, convert_i64_u,
204            trunc
205        ],
206        map: {
207            integer: [rem => mod],
208        },
209        mem: {
210            all: [load],
211            integer: [load8, load16],
212            integer64: [load32],
213            signed: [store8, store16],
214            signed64: [store32],
215            signed_and_float: [store],
216        },
217        masm: {
218            drop,
219            memory_grow: {
220                mem: u32,
221                mem_byte: u8
222            },
223            memory_size: {
224                mem: u32,
225                mem_byte: u8
226            },
227            i32_const: {
228                value: i32
229            },
230            i64_const: {
231                value: i64
232            },
233            f32_const: {
234                value: Ieee32
235            },
236            f64_const: {
237                value: Ieee64
238            },
239            i32_wrap_i64,
240            i64_extend_i32_s,
241            i64_extend_i32_u,
242            f32_demote_f64,
243            f64_promote_f32,
244            i32_reinterpret_f32,
245            i64_reinterpret_f64,
246            f32_reinterpret_i32,
247            f64_reinterpret_i64
248        },
249        global: {
250            else, select, end, nop, unreachable,
251            if: {
252                blockty: BlockType
253            },
254            block: {
255                blockty: BlockType
256            },
257            loop: {
258                blockty: BlockType
259            },
260            br: {
261                relative_depth: u32
262            },
263            br_if: {
264                relative_depth: u32
265            },
266            br_table: {
267                table: BrTable<'_>
268            },
269            local_get: {
270                local_index: u32
271            },
272            local_set: {
273                local_index: u32
274            },
275            local_tee: {
276                local_index: u32
277            },
278            global_get: {
279                global_index: u32
280            },
281            global_set: {
282                global_index: u32
283            },
284            call: {
285                func_index: u32
286            },
287            call_indirect: {
288                type_index: u32,
289                table_index: u32,
290                table_byte: u8
291            }
292        }
293    }
294
295    // Custom implementation for the return instruction
296    fn visit_return(&mut self) -> Self::Output {
297        trace!("return");
298
299        let before = self.masm.buffer().len();
300
301        // for early returns in a function, emit return code with value 1 (true)
302        if self.is_main || self.abi.is_some() {
303            tracing::trace!("early return from main function");
304            self.masm.emit_return_value(&[1])?;
305        } else {
306            tracing::trace!("early return from call");
307            self.masm.call_return(self.ty.results())?;
308        }
309
310        let instr = self.masm.buffer()[before..].to_vec();
311        self.backtrace.push(instr);
312
313        Ok(())
314    }
315}